PuzzleD

D Interface to libpuzzle
git clone git://xatko.vsos.ethz.ch/PuzzleD.git
Log | Files | Refs

commit 1d11a26ceb1458791883218a2889d0e9e802900e
parent d53a6a17ee1c271f6c492e4bb7a49f91732aff07
Author: Dominik Schmidt <das1993@hotmail.com>
Date:   Thu, 27 Aug 2015 12:33:25 +0200

Add DDoc & Unittests

Diffstat:
puzzle.d | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 189 insertions(+), 0 deletions(-)

diff --git a/puzzle.d b/puzzle.d @@ -1,5 +1,13 @@ module puzzle; +version(unittest){ + static immutable string testimage="iVBORw0KGgoAAAANSUhEUgAAAE4AAAASAQMAAADPKHrSAAAABlBMVEX///8AAABVwtN+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wgbCSsvSqRkugAAAGxJREFUCNdjYEAAGyYDMH2AgUGCSQDMdAAyGTCZDQwSbLL9yRL//jkrANVKHDPk6OlxEwBpcxHkkJCAMwUE3H9AmQYGbkATlEDMhAQwE6htxqFDTgIM9s+3tz549+/f5wcg01kQLkJi8jHgAQA3KhrFG5DorwAAAABJRU5ErkJggg=="; + ubyte[] testimagedat(){ + import std.base64; + return cast(ubyte[])Base64.decode(testimage); + } +} + extern(C){ struct PuzzleContext { uint puzzle_max_width; @@ -99,8 +107,22 @@ class PuzzleException : Exception{ } } +/** + * PuzzleContext wrapper + * + * Has to be inititialized with `Puzzle.initialize();` + * and frees the context on destruction. + * + * Be careful, when you stack-allocate it, the object gets + * rendered unusable once the scope has been left. + * + */ struct Puzzle{ PuzzleContext ctx; + /** + * Initializes the Puzzle-context. + * + */ void initialize() out{ assert(ctx.valid()); @@ -108,23 +130,57 @@ struct Puzzle{ body{ puzzle_init_context(&ctx); } + + ///Frees the PuzzleContext (If there is one) ~this(){ if(ctx.valid){ puzzle_free_context(&ctx); } } + /** + * Returns a newly allocated DVec-Structure + * + * $(DDOC_SEE_ALSO struct DVec) + * + */ DVec dvec(){ return DVec(&ctx); } + + /** + * Returns a newly allocated CVec-Structure + * + * $(DDOC_SEE_ALSO struct CVec) + * + */ CVec cvec(){ return CVec(&ctx); } } +/// +unittest{ + Puzzle p; + assert(!p.ctx.valid); + p.initialize(); + assert(p.ctx.valid); + + DVec d=p.dvec(); + assert(d.ctx.valid); + + DVec c=p.dvec(); + assert(c.ctx.valid); +} +/** + * Wraps the PuzzleDVec-structure + * + */ struct DVec{ PuzzleDvec vec; PuzzleContext *ctx; @disable this(); + + ///Initializes the DVec this(PuzzleContext *ctx) in{ assert(ctx.valid(), "Context invalid"); @@ -133,30 +189,61 @@ struct DVec{ this.ctx=ctx; puzzle_init_dvec(ctx,&vec); } + + ///Frees the DVec (If it has been allocated by the library); ~this(){ if(ctx!=null){ puzzle_free_dvec(ctx,&vec); } } + + /** + * Generates the vector from a file. + * Params: + * f = File to read the data from. + * Throws: + * PuzzleException if the puzzle-function fails to load the image + */ void load(string f){ if(puzzle_fill_dvec_from_file(ctx, &vec, toStringz(f))!=0){ throw new PuzzleException("Couldn't read from file"); } } + + /** + * Generates the vector from memory + * Params: + * mem = The memory of an Image + * Throws: + * PuzzleException if the memory couldn't be read. + */ void load(void mem[]){ if(puzzle_fill_dvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){ throw new PuzzleException("Couldn't read from memory"); } } + /** + * Get the CVec derived from this DVector. + * Returns: + * The corresponding(and comparable) CVector to this DVector + */ CVec cvec(){ CVec cvec=CVec(ctx); puzzle_fill_cvec_from_dvec(ctx,&cvec.vec,&vec); return cvec; } + + /** + * Get the length of the vector + * Returns: + * The length of the vector + */ @safe @nogc @property size_t length(){ return vec.sizeof_vec; } } + +/// struct CVec{ PuzzleCvec vec; PuzzleContext *ctx; @@ -165,14 +252,37 @@ struct CVec{ this(c.ctx); puzzle_uncompress_cvec(ctx, &c.vec, &vec); } + /** + * Generates a Vector by hand. + * + * This can be used to save a Vector and reload it again. + * + * Params: + * a = The array of data to fill the vector with. + */ @nogc pure nothrow this(byte[] a){ this(cast(char*)a.ptr, a.length); } + ///ditto @nogc pure nothrow this(char *a, size_t length){ vec.vec=a; vec.sizeof_vec=length; } + /// + unittest{ + Puzzle p; + p.initialize(); + CVec c=p.cvec(); + + c.load(testimagedat()); + assert(c.length>0); + + CVec c2=CVec(c.vec.vec,c.length); + assert(std.math.approxEqual(c.compare(c2),0)); + } + + ///Initializes a Vector this(PuzzleContext *ctx) in{ assert(ctx.valid(), "Context invalid"); @@ -181,49 +291,110 @@ struct CVec{ this.ctx=ctx; puzzle_init_cvec(ctx,&vec); } + ///Frees the vector(If it was allocated by the library) ~this(){ if(ctx!=null){ puzzle_free_cvec(ctx,&vec); } } + + /** + * Generates the vector from a file. + * Params: + * f = File to read the data from. + * Throws: + * PuzzleException if the puzzle-function fails to load the image + */ void load(string f){ if(puzzle_fill_cvec_from_file(ctx, &vec, toStringz(f))!=0){ throw new PuzzleException("Couldn't read from file"); } } + + /** + * Generates the vector from memory + * Params: + * mem = The memory of an Image + * Throws: + * PuzzleException if the memory couldn't be read. + */ void load(void mem[]){ if(puzzle_fill_cvec_from_mem(ctx, &vec, cast(void*)mem, mem.length)!=0){ throw new PuzzleException("Couldn't read from memory"); } } + + /** + * Compare two CVectors. + * Params: + * b = The second vector to compare to + * hasText = Wether one of the images had any text in it. + * Returns: + * The distance of both vectors. + */ double compare(in ref CVec b, bool hasText=false){ return puzzle_vector_normalized_distance(ctx,&vec,&b.vec,(hasText) ? 1 : 0); } + + + /** + * Compresses a CVector + * Returns: + * A compressed CVector + */ CVec_Compressed compress(){ return CVec_Compressed(this); } + + /** + * Get the length of the vector + * Returns: + * The length of the vector + */ @nogc @safe @property size_t length(){ return vec.sizeof_vec; } } + +/// +unittest{ + Puzzle p; + p.initialize(); + CVec c=p.cvec(); + c.load(testimagedat()); + + assert(std.math.approxEqual(c.compare(c),0)); +} + +///A compressed CVector struct CVec_Compressed{ PuzzleCompressedCvec vec; PuzzleContext *ctx; @disable this(); + + ///Generates a compressed CVector corresponding to c this(ref CVec c){ this(c.ctx); puzzle_compress_cvec(ctx, &vec, &c.vec); } + /** + * Generates a Vector by hand. + * + * Params: + * a = The array of data to fill the vector with. + */ this(ubyte[] a){ this(a.ptr,a.length); } + ///ditto this(ubyte* a, size_t length){ vec.vec=a; vec.sizeof_compressed_vec=length; } + ///Allocates a new Compressed CVector in the library this(PuzzleContext *ctx) in{ assert(ctx.valid(), "Context invalid"); @@ -232,15 +403,33 @@ struct CVec_Compressed{ this.ctx=ctx; puzzle_init_compressed_cvec(ctx,&vec); } + + ///Frees the Vector, if it was allocated by the library ~this(){ if(ctx!=null){ puzzle_free_compressed_cvec(ctx,&vec); } } + + ///Returns the corresponding uncompressed CVector CVec uncompress(){ return CVec(this); } + + /// @safe @nogc @property size_t length(){ return vec.sizeof_compressed_vec; } } + +/// +unittest{ + Puzzle p; + p.initialize(); + CVec c=p.cvec(); + c.load(testimagedat()); + CVec_Compressed cc=c.compress(); + CVec c2=cc.uncompress(); + + assert(std.math.approxEqual(c.compare(c2),0)); +}