diff --git a/build.xml b/build.xml
index 9a0b638..31ba080 100644
--- a/build.xml
+++ b/build.xml
@@ -13,7 +13,6 @@
-
@@ -41,7 +40,7 @@
-
+
diff --git a/fetch_dependencies.sh b/fetch_dependencies.sh
index 9d745fa..ea6b6bc 100755
--- a/fetch_dependencies.sh
+++ b/fetch_dependencies.sh
@@ -52,7 +52,10 @@ do
use_jackson=$1
;;
use_zstd)
- use_zstd=1
+ use_zstd=$1
+ ;;
+ use_artgraphic)
+ use_artgraphic=$1
;;
*)
parsemetaarg "$1"
@@ -73,18 +76,19 @@ then
$metarun mkdir libs
fi
-libversion_artgaphic=0.2.0
-lib_artgaphic=artgaphics-${libversion_artgaphic}.jar
-if [[ ! -e libs/$lib_artgaphic ]]
+if [[ -n $user_artgraphic ]]
then
- lib_artgaphic_source_file=sharedrawweb/dist/lib/$lib_artgaphic
- if [[ -f $lib_artgaphic_source_file ]]
+ if [[ ! -e libs/$lib_artgaphic ]]
then
- # ../ since libs is one level below current project
- $metarun ln -s ../$lib_artgaphic_source_file libs/
- else
- log_error "Missing $lib_artgaphic_source_file"
- log_info "It is required to build sharedrawweb project first"
+ lib_artgaphic_source_file=sharedrawweb/dist/lib/$lib_artgaphic
+ if [[ -f $lib_artgaphic_source_file ]]
+ then
+ # ../ since libs is one level below current project
+ $metarun ln -s ../$lib_artgaphic_source_file libs/
+ else
+ log_error "Missing $lib_artgaphic_source_file"
+ log_info "It is required to build sharedrawweb project first"
+ fi
fi
fi
diff --git a/java/org/artisanlogiciel/compression/Main.java b/java/org/artisanlogiciel/compression/Main.java
new file mode 100644
index 0000000..62bb04e
--- /dev/null
+++ b/java/org/artisanlogiciel/compression/Main.java
@@ -0,0 +1,92 @@
+package org.artisanlogiciel.compression;
+
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.artisanlogiciel.graphics.SvgWriter;
+import org.artisanlogiciel.graphics.Drawing;
+
+/**
+ * Very simple utility to convert from .imc to svg.
+ *
+ * TODO : could be more clever and not load full image into memory to save it afterwards ( for very small embedded systems ).
+ */
+
+public class Main
+{
+
+ Drawing mLocalImage;
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ if ( args.length > 1 )
+ {
+ m.loadExpanded(args[0]);
+ try {
+ File out = new File(args[1]);
+ if (out.exists())
+ {
+ System.err.println("File already exists" + out + " Not overriding it");
+
+ }
+ else
+ {
+ //m.saveSvg(new DataOutputStream(new FileOutputStream(out)));
+ FileOutputStream fout = new FileOutputStream(out);
+ m.saveSvg(fout);
+ fout.flush();
+ fout.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ System.out.println("Very simple utility to convert from .imc to svg.");
+ System.out.println("first argument is compressed file, second is svg output file (should not already exist)");
+ }
+
+ }
+
+ public void saveSvg(OutputStream s)
+ throws IOException
+ {
+ SvgWriter writer = new SvgWriter(mLocalImage.getLines());
+ writer.writeTo(s);
+ }
+
+ public void saveKompressed( String ref) {
+ try {
+ FileOutputStream fi = new FileOutputStream( ref);
+ mLocalImage.saveLinesKompressed( new DataOutputStream( fi));
+ }
+ catch( java.io.FileNotFoundException fnfe)
+ {
+ fnfe.printStackTrace(System.err);
+ }
+ catch ( java.io.IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ }
+
+ public void loadExpanded( String ref) {
+ try {
+ FileInputStream fi = new FileInputStream( ref);
+ mLocalImage = new Drawing();
+ mLocalImage.loadLinesExpanded( new DataInputStream( fi));
+ }
+ catch( java.io.FileNotFoundException fnfe) {
+ fnfe.printStackTrace(System.err);
+ }
+ catch ( java.io.IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ }
+
+}
diff --git a/java/org/artisanlogiciel/compression/graphics/BitFieldReader.java b/java/org/artisanlogiciel/compression/graphics/BitFieldReader.java
new file mode 100644
index 0000000..e6bdc6b
--- /dev/null
+++ b/java/org/artisanlogiciel/compression/graphics/BitFieldReader.java
@@ -0,0 +1,146 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package org.artisanlogiciel.compression.graphics;
+
+/*
+ simple class to read bitfields
+ @see BitFieldWriter
+ @author Philippe Lhardy
+*/
+
+import java.io.*;
+
+class BitFieldReader {
+
+ // size of nextWord
+ final int dataSize = 32;
+
+ /* offset of next bit to be read in current word
+ should ensure bitOffset < dataSize
+ */
+ private int bitOffset = 0;
+
+ /* word used waiting to be fully read
+ dataSize is bit size of currentWord
+ currentWord is part of word read that begins at bitOffset.
+ */
+ private int currentWord = 0;
+
+ private DataInputStream inputStream;
+
+ /* value returned is an int between 0 and 2^bits-1 */
+ public int read( int bits) throws java.io.IOException {
+ int field = 0;
+ int head = 0;
+
+ // a new word is needed
+ if ( bitOffset == 0 ) {
+ nextWord();
+ }
+ // if more bits needed that word currently used.
+ if ( bits + bitOffset > dataSize) {
+ // warning recursive call ( not terminal )
+ int bitsize = dataSize - bitOffset;
+ head = read( dataSize - bitOffset);
+ // current word had entirely been read, need a new one
+ // don't do that... nextWord() will be done by next read...
+ field = read( bits - bitsize);
+ // reconstruct all
+ // more significant bits in first word, least in last
+ field = field | ( head << (bits - bitsize));
+ }
+ else {
+ // terminal part do the job
+ if ( bits == dataSize) {
+ // special case to keep sign
+ field = currentWord;
+ currentWord = 0;
+ bitOffset = 0;
+ }
+ else {
+ field = ( currentWord >> ( dataSize - bits) )
+ & ( 0x7fffffff >> ( dataSize - 1 - bits) );
+ // bits are read then remove them from currentWord
+ currentWord <<= bits;
+ bitOffset = ( bitOffset + bits ) % dataSize;
+ /*
+ System.out.println( "size " + Integer.toString(bits)
+ + " value " + Integer.toString(field)
+ + " offset " + Integer.toString(bitOffset)
+ + " currentWord " + Integer.toString( currentWord)
+ );
+ */
+ }
+ }
+
+
+ return field;
+ }
+
+
+ private int getInt( byte[] pBytes)
+ {
+ long field = 0;
+ field = pBytes[0]<<24 | pBytes[1]<<16 | pBytes[2]<<8 | pBytes[3];
+ return (int) field;
+ }
+
+
+ /**
+ * doesn't work correctly as readInt
+ */
+ private int readInt()
+ throws IOException
+ {
+ byte bytes[]=new byte[4];
+ int nr=0;
+ int index = 0;
+ /* read at least 4 bytes */
+ while ( ( nr != -1) && ( index < 4 ) )
+ {
+ nr = inputStream.read( bytes, index, 4 - index);
+ index +=nr;
+ }
+ return getInt( bytes);
+ }
+
+
+ private void nextWord() throws java.io.IOException {
+ // read next word
+ // currentWord = readInt();
+ currentWord= inputStream.readInt();
+ bitOffset = 0;
+ }
+
+ /*
+ go to next entire word.
+ */
+ public void padToWord() throws java.io.IOException {
+ if ( bitOffset != 0) {
+ nextWord();
+ }
+ }
+
+ /* set InputStream */
+ public void setInputStream( InputStream input) {
+ inputStream = new DataInputStream(input);
+ }
+
+}
diff --git a/java/org/artisanlogiciel/compression/graphics/BitFieldWriter.java b/java/org/artisanlogiciel/compression/graphics/BitFieldWriter.java
new file mode 100644
index 0000000..cf93c62
--- /dev/null
+++ b/java/org/artisanlogiciel/compression/graphics/BitFieldWriter.java
@@ -0,0 +1,125 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package org.artisanlogiciel.compression.graphics;
+
+/*
+ simple class to write bitfields
+ @see BitFieldReader
+ @author Philippe Lhardy
+*/
+
+import java.io.*;
+
+class BitFieldWriter {
+
+ // size of nextWord
+ final int dataSize = 32;
+
+ /* bitOffset is number of bits already used in word
+ should ensure bitOffset < dataSize
+ */
+ private int bitOffset;
+
+ /* word used waiting to be filled to be written
+ dataSize is bit size of nextWord
+ */
+ private int nextWord;
+
+ private DataOutputStream outputStream;
+
+ public void write( int field, int bits) throws java.io.IOException {
+ // if more bits needed that word currently used.
+ if ( bits + bitOffset > dataSize) {
+ // warning recursive call ( not terminal )
+ // fill and write current word
+ int bitsize = dataSize - bitOffset;
+ int head;
+ // most significant bits first
+ // suppress at left least significant bits
+ head = field >> (bits - bitsize);
+ write( head, dataSize - bitOffset);
+ // no need of newWord() previous write already done it
+ // least significant bits last
+ field = field & ( 0x7FFFFFFF >> ( bitsize - 1));
+ write( field, bits - bitsize);
+ }
+ else {
+ if ( bits == dataSize ) {
+ // special case to keep sign
+ nextWord = field;
+ bitOffset = 0;
+ }
+ else {
+ // keep only meaning part of field
+ field = field & ( 0x7FFFFFFF >> ( dataSize - 1 - bits));
+ nextWord = nextWord | ( field << ( dataSize - bits - bitOffset));
+ bitOffset = ( bitOffset + bits ) % dataSize;
+ }
+ // a new word is needed
+ if ( bitOffset == 0 ) {
+ newWord();
+ }
+ }
+ /*
+ System.out.println( "nextWord " + Integer.toString( nextWord) + " size "
+ + Integer.toString( bits) + " field " + Integer.toString( field));
+ */
+ }
+
+ /**
+ * seems to works correctly as DataOutputStream.writeInt()
+ */
+ private void writeInt( int i)
+ throws IOException
+ {
+ long j = i;
+ byte bytes[]=new byte[4];
+ bytes[0] = (byte) ((j & 0xFF000000) >> 24);
+ bytes[1] = (byte) ((j & 0x00FF0000) >> 16);
+ bytes[2] = (byte) ((j & 0x0000FF00) >> 8);
+ bytes[3] = (byte) (j & 0x000000FF);
+ outputStream.write( bytes, 0, 4);
+ }
+
+ private void newWord() throws java.io.IOException {
+ // do really write current word;
+ //writeInt( nextWord);
+ outputStream.writeInt(nextWord);
+ // reset nextWord
+ bitOffset = 0;
+ nextWord = 0;
+ }
+
+ /*
+ write all current datas if any
+ to have a clean word ready.
+ */
+ public void padToWord() throws java.io.IOException {
+ if ( bitOffset != 0) {
+ newWord();
+ }
+ }
+
+ /* set OutputStream */
+ public void setOutputStream( OutputStream output) {
+ outputStream = new DataOutputStream(output);
+ }
+
+}
diff --git a/java/org/artisanlogiciel/compression/graphics/DrawLineExpander.java b/java/org/artisanlogiciel/compression/graphics/DrawLineExpander.java
new file mode 100644
index 0000000..84c8529
--- /dev/null
+++ b/java/org/artisanlogiciel/compression/graphics/DrawLineExpander.java
@@ -0,0 +1,149 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+/*
+ DrawingLineExpander
+ decompress a draw line from a stream
+
+ main algorithm is to code only delta displacement in littlest possible size.
+*/
+package org.artisanlogiciel.compression.graphics;
+
+import java.awt.Point;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+public class DrawLineExpander {
+
+ ArrayList expandedLines;
+ int currentSize;
+ int scode[] = {3,6,14,30,64};
+ final static int SCODE_MAX = 4;
+ private BitFieldReader fieldReader = null;
+
+ public DrawLineExpander() {
+ expandedLines = new ArrayList<>();
+ fieldReader = new BitFieldReader();
+ }
+
+ /* create a List of points */
+ public ArrayList expand( InputStream input ) throws java.io.IOException {
+ fieldReader.setInputStream( input);
+ int point_count = fieldReader.read( 32);
+ // System.out.println( point_count);
+ expandedLines.add( readAbs( 64));
+ for ( int i = 1; i < point_count; i ++ ) {
+ Point point = readRel();
+ if ( currentSize == 64 ) {
+ // absolute
+ expandedLines.add( point);
+ }
+ else {
+ // relative
+ Point previous = (Point) expandedLines.get(expandedLines.size()-1);
+ point.x = point.x + previous.x;
+ point.y = point.y + previous.y;
+ expandedLines.add( point);
+ }
+ }
+ return expandedLines;
+ }
+
+ /*
+ read point with size encoding depending on first bits
+ */
+ private Point readRel() throws java.io.IOException {
+
+ int index;
+
+ /* 11 : next, 10 : previous ; 0* : same
+ 6 -> 14->30
+ ^ |
+ | v
+ 3 <- 64
+ */
+ // find index of current size
+ for ( index = 0; index < SCODE_MAX; index ++) {
+ if ( scode[index] == currentSize ) break;
+ }
+
+ // read size modifier until it is good one
+ while ( fieldReader.read(1) != 0 ) {
+ index = index + ( ( fieldReader.read(1) == 0 ) ? -1 : 1);
+ // handle circularity
+ if ( index > SCODE_MAX ) {
+ index = 0;
+ }
+ if ( index < 0 ) {
+ index = SCODE_MAX;
+ }
+ }
+ return readAbs( scode[index]);
+ }
+
+ /* write given Point with size */
+ Point readAbs( int size) throws java.io.IOException {
+ int codeval;
+ int center, max;
+
+ currentSize = size;
+
+ Point point = new Point();
+ if ( size > 32 ) {
+ point.x = fieldReader.read(32);
+ point.y = fieldReader.read(32);
+ }
+ else {
+ boolean nocenter=false;
+ codeval = fieldReader.read( size);
+ // System.out.println( codeval);
+ /*
+ for coding/decoding scheme see
+ org.artisanlogiciel.graphics.DrawLineKompressor.java
+ */
+ if ( size == 3 ) {
+ max = 3;
+ center = 1;
+ nocenter=true;
+ }
+ else {
+ max = ( 1 << ( size / 2 ) );
+ center = (max / 2) - 1;
+ }
+ // central hole impossible but we don't really care
+ if ( nocenter )
+ {
+
+ if ( codeval > ( center * max ) ) {
+ codeval ++;
+ }
+ if ( codeval >= ( max * max )) {
+ System.out.println(
+ "!!! reserved value for decompression should not occur !!!");
+ }
+ }
+ point.x = ( codeval % max) - center;
+ point.y = ( codeval / max) - center;
+ // System.out.println( point.toString());
+ }
+ return point;
+ }
+
+}
+
diff --git a/java/org/artisanlogiciel/compression/graphics/DrawLineKompressor.java b/java/org/artisanlogiciel/compression/graphics/DrawLineKompressor.java
new file mode 100644
index 0000000..a435537
--- /dev/null
+++ b/java/org/artisanlogiciel/compression/graphics/DrawLineKompressor.java
@@ -0,0 +1,256 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+/*
+ DrawingLineKompressor
+ compress a draw line over a stream
+
+ main algorithm is to code only delta displacement in littlest possible size.
+*/
+package org.artisanlogiciel.compression.graphics;
+
+import java.awt.Point;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+public class DrawLineKompressor {
+
+ ArrayList fromLines;
+ int previousSize;
+ int scode[] = {3,6,14,30,64};
+ final static int SCODE_MAX = 4;
+ private BitFieldWriter fieldWriter = null;
+
+ public DrawLineKompressor( ArrayList lines) {
+ fromLines = lines;
+ fieldWriter = new BitFieldWriter();
+ }
+
+ public void kompress( OutputStream output ) throws java.io.IOException {
+ fieldWriter.setOutputStream( output);
+ int skipsame=0;
+
+ if ( ( fromLines != null) && ( fromLines.size() > 0)) {
+ Point topoint = null;
+ for ( int i = 1; i < fromLines.size(); i++) {
+ Point frompoint = (Point) fromLines.get( i);
+ if ( ( topoint != null ) && ( topoint.x == frompoint.x ) && ( topoint.y == frompoint.y ) )
+ {
+ skipsame ++;
+ }
+ topoint=frompoint;
+ }
+ if ( skipsame > 0 )
+ {
+ System.out.println("[WARNING] lines contains " + skipsame + " duplicates points");
+ }
+ }
+
+ if ( ( fromLines != null) && ( fromLines.size() > 0)) {
+ Point frompoint = (Point) fromLines.get(0);
+ Point point, topoint;
+ topoint = new Point();
+ int center, size;
+ fieldWriter.write( fromLines.size() - skipsame, 32);
+ writeAbs( frompoint, 64);
+ for ( int i = 1; i < fromLines.size(); i++) {
+ point = (Point) fromLines.get( i);
+ topoint.x = point.x - frompoint.x;
+ topoint.y = point.y - frompoint.y;
+ // find wich size to use...
+ if (( topoint.x == 0 ) && ( topoint.y == 0 ))
+ {
+ // ARGH THIS IS INVALID !
+ System.out.println("[ERROR] (0,0) point is invalid for compression !!!");
+ continue;
+ }
+ if ( (Math.abs( topoint.x) < 2) && (Math.abs(topoint.y) < 2)) {
+ writeRel( topoint, 3);
+ }
+ else {
+ size = 64;
+ for ( int index = 1; index < ( SCODE_MAX - 1 ); index ++) {
+ /* center = ( ( 1 << ( scode[index] / 2 ) ) - 1 ) / 2;
+ if ( (Math.abs(topoint.x) <= center)
+ && (Math.abs(topoint.y) <= center))
+ {
+ size = scode[index];
+ }
+ */
+ // center is biased there are more + than - , ex for 6bits :(x,y) within ( -3,4 ) x (-3,4)
+ center = 1 << ( ( scode[index] / 2 ) - 1 );
+ if ( ( topoint.x <= center ) && ( topoint.x > -center )
+ && ( topoint.y <= center ) && ( topoint.y > -center ) )
+ {
+ size = scode[index];
+ // always take the smaller size.
+ break;
+ }
+ }
+ if ( size < 32 ) {
+ writeRel( topoint, size);
+ }
+ else {
+ // warning absolute
+ writeRel( point, 64);
+ }
+ }
+ // new frompoint is previous point
+ frompoint = point;
+ }
+ fieldWriter.padToWord();
+ }
+ }
+
+ /*
+ compute bits of size
+ */
+ void writeRel( Point point, int size) throws java.io.IOException {
+
+ /*
+ System.out.println( "point " + point.toString());
+ System.out.println( "size " + Integer.toString( size));
+ */
+
+ /* 11 : next, 10 : previous ; 0* : same
+ 6 -> 14
+ ^ |
+ | v
+ 3 <- 64
+ */
+ if ( size != previousSize) {
+ int cindex, pindex;
+ // find index of size
+ for ( cindex = 0; cindex < SCODE_MAX; cindex ++) {
+ if ( scode[cindex] == size ) break;
+ }
+ // find index of previous size
+ for ( pindex = 0; pindex < SCODE_MAX; pindex ++) {
+ if ( scode[pindex] == previousSize ) break;
+ }
+
+ if ( pindex < cindex ) {
+ /*
+ if way with = is better than - then use = next
+ else use - previous
+ 0--<--pindex===>==cindex---<---SCODE_MAX
+ */
+ if ( ((SCODE_MAX - cindex) + pindex ) > ( pindex - cindex ) ) {
+ // ( pindex - cindex ) times next
+ for ( ; pindex < cindex; pindex ++) {
+ fieldWriter.write( 3,2);
+ }
+ }
+ else {
+ // ((SCODE_MAX -cindex) + pindex ) fois previous
+ for ( pindex = SCODE_MAX - cindex + pindex; pindex > 0; pindex --) {
+ fieldWriter.write( 2,2);
+ }
+ }
+ }
+ else {
+ /*
+ if way with = is better than - then use = previous
+ else use - next
+ 0-->--cindex===<==pindex--->---SCODE_MAX
+ */
+ if ( ((SCODE_MAX - pindex) + cindex ) > cindex - pindex ) {
+ // ( cindex - pindex ) fois previous
+ for ( ; cindex < pindex; pindex --) {
+ fieldWriter.write( 2,2);
+ }
+ }
+ else {
+ // ((SCODE_MAX - pindex) + cindex ) fois next
+ for ( pindex = SCODE_MAX - pindex + cindex; pindex > 0; pindex --)
+ {
+ fieldWriter.write( 3,2);
+ }
+ }
+ }
+ }
+ // this is good size.
+ fieldWriter.write( 0,1);
+ writeAbs( point, size);
+ }
+
+ /* write given Point with size */
+ void writeAbs( Point point, int size) throws java.io.IOException {
+ int codeval;
+ int center, max;
+
+ previousSize = size;
+
+ if ( size > 32 ) {
+ fieldWriter.write( point.x, 32);
+ fieldWriter.write( point.y, 32);
+ }
+ else {
+ boolean nocenter = false;
+ /* size == 3 example :
+ X - >
+ Y 0 1 2
+ | 3 . 4-
+ v 5 6 7
+ specific application of general case bellow.
+ */
+
+ /* X ->
+ Y 0
+ | 0 center max
+ v max
+
+ 0 1 2 ...center.... (max - 1)
+ max 2 * max - 1
+ . (0,0) center * ( max + 1)
+ (max-1)*max -1 (max ^ 2) - 1
+2 */
+ if ( size == 3 ) {
+ max = 3;
+ center = 1;
+ nocenter = true;
+ }
+ else {
+ max = ( 1 << ( size / 2 ) );
+ // biased center ( -center +1, center )
+ center = (max / 2) - 1;
+ }
+ /*
+ System.out.println( "size" + Integer.toString( size));
+ System.out.println( "max, center " + Integer.toString( max) + " " + Integer.toString( center));
+ System.out.println( "x, y " + Integer.toString( point.x) + " " + Integer.toString( point.y));
+ */
+ codeval = point.x + center + (( point.y + center ) * max );
+ // central hole impossible BUT we should not really care ( in fact whole center square is impossible since covered by encoding with lower number of bits ) , avoid it only for size 3.
+ if (nocenter) {
+ if ( codeval == ( center * max ) + 1 ) {
+ System.out.println("!!! warning (0,0) point is invalid for compression !!!");
+ }
+ if ( codeval > (center * max) ){
+ codeval --;
+ }
+ }
+ // System.out.println( "code " + Integer.toString( codeval));
+ fieldWriter.write( codeval, size);
+ }
+ }
+
+
+}
+
diff --git a/java/org/artisanlogiciel/graphics/DrawException.java b/java/org/artisanlogiciel/graphics/DrawException.java
new file mode 100644
index 0000000..968d78a
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/DrawException.java
@@ -0,0 +1,28 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package org.artisanlogiciel.graphics;
+
+import java.io.Serializable;
+
+public class DrawException extends Exception implements Serializable {
+ public DrawException() {
+ super();
+ }
+}
diff --git a/java/org/artisanlogiciel/graphics/Drawing.java b/java/org/artisanlogiciel/graphics/Drawing.java
new file mode 100644
index 0000000..aba2003
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/Drawing.java
@@ -0,0 +1,106 @@
+/**
+ Drawing
+
+ contains a set of DrawingLine to create a Drawing
+ - feature of load / save
+*/
+package org.artisanlogiciel.graphics;
+
+import java.awt.Graphics;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.ArrayList;
+
+import org.artisanlogiciel.graphics.Importer;
+import org.artisanlogiciel.graphics.ResetException;
+import org.artisanlogiciel.graphics.NoMoreLineException;
+
+public class Drawing
+{
+
+ private ArrayList lines;
+
+ public Drawing()
+ {
+ reset();
+ }
+
+ public void reset() {
+ lines = new ArrayList(10);
+ }
+
+ public void addLine( DrawingLine line) {
+ lines.add( line);
+ }
+
+ public void saveLines( DataOutputStream destination) throws
+ java.io.IOException {
+ destination.writeInt( lines.size());
+ for ( DrawingLine line : lines )
+ {
+ line.save( destination);
+ }
+ }
+
+ public void loadLines( DataInputStream source) throws
+ java.io.IOException {
+ int nb_lines = source.readInt();
+ DrawingLine line;
+ for ( int i = 0; i < nb_lines; i++ ) {
+ line = new DrawingLine();
+ line.load( source);
+ lines.add( line);
+ }
+ }
+
+ public void importImage( Importer importer) throws
+ java.io.IOException
+ {
+ importer.importInto(this);
+ }
+
+ public void saveLinesKompressed( DataOutputStream destination) throws
+ java.io.IOException {
+ destination.writeInt( lines.size());
+ for ( DrawingLine line : lines )
+ {
+ line.saveKompressed( destination);
+ }
+ }
+
+ public void loadLinesExpanded( DataInputStream source) throws
+ java.io.IOException {
+ // DrawLineExpander expander = new DrawLineExpander();
+ int nb_lines = source.readInt();
+ DrawingLine line;
+ for ( int i = 0; i < nb_lines; i++ ) {
+ line = new DrawingLine();
+ line.loadExpanded( source);
+ lines.add( line);
+ }
+ }
+
+ public int length() {
+ return lines.size();
+ }
+
+ public DrawingLine getLine( int line) throws ResetException, NoMoreLineException {
+ if ( line == lines.size() ) {
+ throw new NoMoreLineException();
+ }
+ if ( line > lines.size() ) {
+ throw new ResetException();
+ }
+ return lines.get( line);
+ }
+
+ public ArrayList getLines() {
+ return new ArrayList(lines);
+ }
+
+ /** Not a copy, use with care */
+ public ArrayList getInternLines() {
+ return lines;
+ }
+
+}
diff --git a/java/org/artisanlogiciel/graphics/DrawingLine.java b/java/org/artisanlogiciel/graphics/DrawingLine.java
new file mode 100644
index 0000000..916b523
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/DrawingLine.java
@@ -0,0 +1,96 @@
+/**
+ DrawingLine
+ keep track of list of Points constituting a line
+*/
+package org.artisanlogiciel.graphics;
+
+import java.awt.Point;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.artisanlogiciel.compression.graphics.DrawLineExpander;
+import org.artisanlogiciel.compression.graphics.DrawLineKompressor;
+
+public class DrawingLine implements Cloneable, Serializable
+{
+
+ ArrayList lines;
+
+ public DrawingLine() {
+ lines = new ArrayList<>();
+ }
+
+ public void addPoint( Point point) {
+ lines.add( point);
+ }
+
+ public void setPoints( ArrayList points) {
+ lines = points;
+ }
+
+ public Object clone() throws java.lang.CloneNotSupportedException {
+ return super.clone();
+ }
+
+ private void writePoint( Point p, DataOutputStream destination) throws
+ java.io.IOException {
+ destination.writeInt( p.x);
+ destination.writeInt( p.y);
+ }
+
+ private void readPoint( DataInputStream source) throws java.io.IOException {
+ Point point = new Point( source.readInt(), source.readInt());
+ lines.add( point);
+ }
+
+ public void save( DataOutputStream destination) throws
+ java.io.IOException {
+ destination.writeInt( lines.size());
+ for (int i=0; i < lines.size(); i++) {
+ writePoint( (Point) lines.get(i), destination);
+ }
+ }
+
+ public void load( DataInputStream source) throws
+ java.io.IOException {
+ int point_number = source.readInt();
+ for (int i=0; i < point_number; i++) {
+ readPoint( source);
+ }
+ }
+
+ /**
+ * send over the stream a compressed version of this line
+ */
+ public void saveKompressed( OutputStream destination) throws
+ java.io.IOException {
+ DrawLineKompressor kompressor = new DrawLineKompressor( lines);
+ kompressor.kompress( destination);
+ }
+
+ /**
+ * load form the stream a compressed version of this line
+ */
+ public void loadExpanded( InputStream source) throws
+ java.io.IOException {
+ DrawLineExpander expander = new DrawLineExpander();
+ lines = expander.expand( source);
+ }
+
+ public ArrayList getPoints() {
+
+ return new ArrayList(lines);
+ }
+
+ public ArrayList internalGetPoints() {
+
+ return lines;
+ }
+
+
+}
+
diff --git a/java/org/artisanlogiciel/graphics/IMAImporter.java b/java/org/artisanlogiciel/graphics/IMAImporter.java
new file mode 100644
index 0000000..f3b6cb4
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/IMAImporter.java
@@ -0,0 +1,94 @@
+package org.artisanlogiciel.graphics;
+
+import java.awt.Point;
+import java.io.DataInputStream;
+
+/**
+.IMA format is an old proprietary format for laser show program
+it contain a list of point the laser beam should goe through,
+x=255 has a special meaning to hide (255,254) ( send beam into the box) or show it (255,255): let is flow outside.
+*/
+public class IMAImporter implements Importer
+{
+
+ final DataInputStream mStream;
+ boolean mBeanOn = false;
+ int mIndex =0;
+ boolean mDebug = false;
+
+ public IMAImporter(DataInputStream inputStream)
+ {
+ mStream=inputStream;
+ }
+
+ public void setDebug(boolean pDebug)
+ {
+ mDebug=pDebug;
+ }
+
+ @Override
+ public void importInto(Drawing drawing)
+ {
+ int x,y;
+ DrawingLine line = null;
+ mBeanOn = false;
+ try {
+ // little endian unsigned short
+ int pointCount = mStream.readUnsignedByte();
+ pointCount = ( 256 * mStream.readUnsignedByte() ) + pointCount;
+ if ( mDebug )
+ {
+ System.out.println("point count :" + pointCount);
+ }
+ for (int j = 0 ; j < pointCount; j+=2)
+ {
+ mIndex=j;
+ x = mStream.readUnsignedByte();
+ y = mStream.readUnsignedByte();
+ // special beam on/off
+ if ( ( x == 0xff ) && ( y >= 0xfe ) )
+ {
+ mBeanOn = ( y == 0xff);
+ if ( mDebug )
+ {
+ System.out.println("beam change at " + mIndex + " " + mBeanOn);
+ }
+ }
+ else
+ {
+ y = 255-y;
+ if ( mDebug )
+ {
+ System.out.println("point at " + mIndex + " " + mBeanOn + " x " + x + " y " + y);
+ }
+ if ( mBeanOn )
+ {
+ if (line == null )
+ {
+ line = new DrawingLine();
+ }
+ line.addPoint(new Point(x,y));
+ }
+ else
+ {
+ if ( line != null )
+ {
+ drawing.addLine(line);
+ line = null;
+ }
+ }
+ }
+ }
+ if ( line != null )
+ {
+ drawing.addLine(line);
+ line = null;
+ }
+ }
+ catch( Exception any)
+ {
+ System.err.println(" error at index " + mIndex);
+ any.printStackTrace(System.err);
+ }
+ }
+}
diff --git a/java/org/artisanlogiciel/graphics/IMAWriter.java b/java/org/artisanlogiciel/graphics/IMAWriter.java
new file mode 100644
index 0000000..d1b0d04
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/IMAWriter.java
@@ -0,0 +1,143 @@
+package org.artisanlogiciel.graphics;
+
+import java.awt.Point;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Write a DrawLine (using its internal Vector representation) into IMA format
+ * this is with loss ( ima is a 255x255 point resolution )
+ *
+ * @author philippe
+ *
+ */
+public class IMAWriter {
+
+ private final ArrayList mLines;
+
+ private int mByteCount = 0;
+ // need to compute bounding box and normalize to 255x255
+ private Point mOrig = new Point(100000,100000);
+ private Point mMax = new Point(-100000,-100000);
+ double ratioX = .1;
+ double ratioY = .1;
+
+ public IMAWriter(ArrayList pLines)
+ {
+ mLines = pLines;
+ setup();
+ }
+
+ private void updateOrigMax(Point p)
+ {
+ double ox = mOrig.getX(), oy = mOrig.getY(), mx = mMax.getX(), my = mMax.getY();
+
+ if (p.getX() < ox )
+ {
+ ox = p.getX();
+ }
+ if (p.getX() > mx)
+ {
+ mx = p.getX();
+ }
+ if (p.getY() < oy )
+ {
+ oy = p.getY();
+ }
+ if (p.getY() > my)
+ {
+ my = p.getY();
+ }
+ mOrig.setLocation(ox,oy);
+ mMax.setLocation(mx,my);
+
+ }
+
+ private void setup()
+ {
+ int count = 0;
+ for (DrawingLine line : mLines)
+ {
+ for (Point p : line.getPoints())
+ {
+ updateOrigMax(p);
+ count+=2;
+ }
+ // beam on + off
+ count+=4;
+ }
+ mByteCount=count;
+ if ( mMax.getX() != mOrig.getX())
+ {
+ ratioX = 255 / (mMax.getX() - mOrig.getX());
+ }
+ else
+ {
+ ratioX = 1.;
+ }
+ if ( mMax.getY() != mOrig.getY())
+ {
+ ratioY = 255 / (mMax.getY() - mOrig.getY());
+ }
+ else
+ {
+ ratioY = 1.;
+ }
+
+ }
+
+ public void writeTo(DataOutputStream pData)
+ throws IOException
+ {
+ int wrote = 0;
+ // writeheader : number of points ...
+ // little endian unsigned short.
+ pData.writeByte(mByteCount%256);
+ pData.writeByte(mByteCount/256);
+ for (DrawingLine line : mLines)
+ {
+ // write ima line
+ wrote += writeLine(pData, line.getPoints());
+ }
+
+ }
+
+ void writeBeamOnOff(DataOutputStream pData,boolean pOn)
+ throws IOException
+ {
+ pData.writeByte(0xff);
+ pData.writeByte(pOn ? 0xff : 0xfe);
+ }
+
+ void writePoint(DataOutputStream pData,Point pPoint)
+ throws IOException
+ {
+ int x = (int) ((pPoint.getX()-mOrig.getX()) * ratioX);
+ int y = 255 - ((int) ((pPoint.getY()-mOrig.getY()) * ratioY));
+ if ( x == 0xff )
+ {
+ x=0xfe;
+ }
+ pData.writeByte(x);
+ pData.writeByte(y);
+ }
+
+ int writeLine(DataOutputStream pData,ArrayList pPoints)
+ throws IOException
+ {
+ int count = 0;
+
+ for (Point p : pPoints)
+ {
+ writePoint(pData,p);
+ if ( count == 0 )
+ {
+ writeBeamOnOff(pData,true);
+ }
+ count++;
+ }
+ writeBeamOnOff(pData,false);
+ return 2*(count +2);
+ }
+}
diff --git a/java/org/artisanlogiciel/graphics/Importer.java b/java/org/artisanlogiciel/graphics/Importer.java
new file mode 100644
index 0000000..8e12ed2
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/Importer.java
@@ -0,0 +1,8 @@
+package org.artisanlogiciel.graphics;
+
+public interface Importer {
+
+ void importInto(Drawing drawing);
+ void setDebug(boolean pDebug);
+
+}
diff --git a/java/org/artisanlogiciel/graphics/NoMoreLineException.java b/java/org/artisanlogiciel/graphics/NoMoreLineException.java
new file mode 100644
index 0000000..3e8a921
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/NoMoreLineException.java
@@ -0,0 +1,31 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package org.artisanlogiciel.graphics;
+
+import java.io.Serializable;
+
+public class NoMoreLineException
+ extends DrawException
+ implements Serializable
+{
+ public NoMoreLineException() {
+ super();
+ }
+}
diff --git a/java/org/artisanlogiciel/graphics/ResetException.java b/java/org/artisanlogiciel/graphics/ResetException.java
new file mode 100644
index 0000000..3777416
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/ResetException.java
@@ -0,0 +1,28 @@
+/*
+ This file is part of ShareDrawWeb.
+
+ ShareDrawWeb is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ ShareDrawWeb is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ShareDrawWeb; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package org.artisanlogiciel.graphics;
+
+public class ResetException
+ extends DrawException
+{
+ public ResetException() {
+ super();
+ }
+}
diff --git a/java/org/artisanlogiciel/graphics/SvgWriter.java b/java/org/artisanlogiciel/graphics/SvgWriter.java
new file mode 100644
index 0000000..5666afe
--- /dev/null
+++ b/java/org/artisanlogiciel/graphics/SvgWriter.java
@@ -0,0 +1,65 @@
+package org.artisanlogiciel.graphics;
+
+import java.awt.Point;
+//import java.io.DataOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Write a DrawLine (using its internal Vector representation) into svg
+ *
+ * @author philippe
+ *
+ */
+public class SvgWriter {
+
+ private final ArrayList mLines;
+
+ public SvgWriter(ArrayList pLines)
+ {
+ mLines = pLines;
+ }
+
+ public void writeTo(OutputStream pData)
+ throws IOException
+ {
+ pData.write("".getBytes("UTF8"));
+ pData.write("".getBytes("UTF8"));
+ }
+
+ String getSvgPathString(ArrayList pPoints)
+ {
+ StringBuilder sb = new StringBuilder(pPoints.size() * 10);
+ Point second = null;
+ Point previous = null;
+ for ( Point p : pPoints)
+ {
+ if ( previous != null )
+ {
+ if ( second == null )
+ {
+ second = p;
+ // 'l' a line ( 'c' woudl be a curve )
+ sb.append(" l ");
+ }
+ sb.append( "" + (p.getX() - previous.getX()) + "," + (p.getY() - previous.getY()) );
+ previous = p;
+ }
+ else
+ {
+ sb.append("m " + p.getX() + "," + p.getY());
+ previous = p;
+ }
+ }
+ return sb.toString();
+ }
+}