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")); + for (DrawingLine line : mLines) + { + 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(); + } +}