1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.il.image;
26 
27 import std.exception;
28 
29 import std.algorithm;
30 import std.string;
31 import std.exception;
32 import std.range;
33 import std.traits;
34 import std.conv;
35 
36 import des.math.linear.vector;
37 import des.il.region;
38 import des.util.testsuite;
39 
40 enum ComponentType
41 {
42     RAWBYTE,
43     BYTE,
44     UBYTE,
45 
46     SHORT,
47     USHORT,
48 
49     INT,
50     UINT,
51 
52     FLOAT,
53     NORM_FLOAT,
54 
55     DOUBLE,
56     NORM_DOUBLE
57 }
58 
59 struct PixelType
60 {
61     ComponentType comp = ComponentType.RAWBYTE;
62     size_t channels = 1;
63 
64     invariant() { assert( channels > 0 ); }
65 
66     pure @safe nothrow @nogc
67     {
68         this( ComponentType ict, size_t ch )
69         {
70             comp = ict;
71             channels = ch;
72         }
73 
74         this( size_t ch )
75         {
76             comp = ComponentType.RAWBYTE;
77             channels = ch;
78         }
79 
80         @property
81         {
82             size_t bpp() const { return compSize * channels; }
83 
84             size_t compSize() const
85             {
86                 final switch( comp )
87                 {
88                     case ComponentType.RAWBYTE:
89                     case ComponentType.BYTE:
90                     case ComponentType.UBYTE:
91                         return byte.sizeof;
92 
93                     case ComponentType.SHORT:
94                     case ComponentType.USHORT:
95                         return short.sizeof;
96 
97                     case ComponentType.INT:
98                     case ComponentType.UINT:
99                         return int.sizeof;
100 
101                     case ComponentType.FLOAT:
102                     case ComponentType.NORM_FLOAT:
103                         return float.sizeof;
104 
105                     case ComponentType.DOUBLE:
106                     case ComponentType.NORM_DOUBLE:
107                         return double.sizeof;
108                 }
109             }
110         }
111     }
112 }
113 
114 class ImageException : Exception 
115 { 
116     @safe pure nothrow this( string msg, string file=__FILE__, size_t line=__LINE__ ) 
117     { super( msg, file, line ); } 
118 }
119 
120 struct Image(size_t N) if( N > 0 )
121 {
122     alias Image!N selftype;
123 
124     alias Vector!(N,size_t,"whd"[0..N].spaceSep) imsize_t;
125     alias Vector!(N,size_t,"xyz"[0..N].spaceSep) imcrd_t;
126     alias Vector!(N,ptrdiff_t,"xyz"[0..N].spaceSep) imdiff_t;
127 
128     alias Region!(N,ptrdiff_t) imregion_t;
129 
130     static struct Header
131     {
132         PixelType type;
133         imsize_t size;
134 
135         pure
136         {
137             @safe nothrow @property @nogc
138             {
139                 size_t dataSize() const { return pixelCount * type.bpp; }
140 
141                 size_t pixelCount() const
142                 {
143                     size_t sz = 1;
144                     foreach( v; size.data ) sz *= v;
145                     return sz;
146                 }
147             }
148         }
149     }
150 
151     private Header head;
152     void[] data;
153 
154     invariant() { assert( data.length == head.dataSize ); }
155 
156     pure
157     {
158         this(this) { data = data.dup; }
159 
160         this( in Image!N img )
161         {
162             head = img.head;
163             data = img.data.dup;
164         }
165 
166         immutable this( in Image!N img )
167         {
168             head = img.head;
169             data = img.data.idup;
170         }
171 
172         this( Header hdr, in void[] data=[] )
173         {
174             head = hdr;
175 
176             if( data.length == 0 )
177                 this.data = new void[]( head.dataSize );
178             else
179             {
180                 enforce( data.length == head.dataSize );
181                 this.data = data.dup;
182             }
183         }
184 
185         this( in size_t[N] sz, in PixelType pt, in void[] data=[] )
186         { this( Header(pt,imsize_t(sz)), data ); }
187 
188         this(V)( in V v, in PixelType pt, in void[] data=[] )
189             if( isCompatibleVector!(N,size_t,V) )
190         { this( to!(size_t[N])(v.data), pt, data ); }
191 
192         this(T)( in size_t[N] sz, in T[] data=[] )
193         { this( sz, PixelType( ComponentType.RAWBYTE, T.sizeof ), data ); }
194 
195         this(V,T)( in V v, in T[] data=[] )
196             if( isCompatibleVector!(N,size_t,V) )
197         { this( to!(size_t[N])(v.data), PixelType( ComponentType.RAWBYTE, T.sizeof ), data ); }
198 
199         void clear() { fill( cast(ubyte[])data, ubyte(0) ); }
200 
201         static if( N > 1 )
202         {
203             this( in Image!(N-1) img, size_t dim=N-1 )
204             in { assert( dim < N ); } body
205             {
206                 imsize_t sz;
207                 foreach( i; 0 .. N )
208                     if( i == dim ) sz[i] = 1;
209                     else sz[i] = img.size[i-(i>dim)];
210                 head.size = sz;
211                 head.type = img.type;
212                 data = img.data.dup;
213             }
214         }
215 
216         const @property
217         {
218             auto dup() { return selftype( this ); }
219             auto idup() { return immutable(selftype)( this ); }
220         }
221 
222         immutable(void[]) dump() const
223         { return (cast(void[])[head] ~ data).idup; }
224 
225         static auto load( immutable(void[]) rawdata )
226         {
227             auto head = (cast(Header[])rawdata[0..Header.sizeof])[0];
228             return selftype( head, rawdata[Header.sizeof .. $] );
229         }
230 
231         ref T pixel(T)( in size_t[N] crd... )
232         {
233             checkComponentType!T;
234             return (cast(T[])data)[index(crd)];
235         }
236 
237         ref const(T) pixel(T)( in size_t[N] crd... ) const
238         {
239             checkComponentType!T;
240             return (cast(const(T)[])data)[index(crd)];
241         }
242 
243         ref T pixel(T,V)( in V v )
244             if( isCompatibleVector!(N,size_t,V) )
245         {
246             checkComponentType!T;
247             return (cast(T[])data)[index(to!(size_t[N])(v.data))];
248         }
249 
250         ref const(T) pixel(T,V)( in V v ) const
251             if( isCompatibleVector!(N,size_t,V) )
252         {
253             checkComponentType!T;
254             return (cast(const(T)[])data)[index(to!(size_t[N])(v.data))];
255         }
256 
257         @property T[] mapAs(T)()
258         {
259             checkComponentType!T;
260             return cast(T[])data; 
261         }
262 
263         @property const(T)[] mapAs(T)() const
264         {
265             checkComponentType!T;
266             return cast(const(T)[])data; 
267         }
268 
269         private
270         {
271             private void checkComponentType(T)() const
272             {
273                 enforce( T.sizeof == head.type.bpp,
274                         new ImageException( "access with wrong type" ) );
275             }
276 
277             static size_t indexCalc( in size_t[N] imsize, in size_t[N] crd )
278             in
279             {
280                 enforce( all!"a[0]>a[1]"( zip( imsize.dup, crd.dup ) ), 
281                         new ImageException("range violation") );
282             }
283             body
284             {
285                 size_t ret;
286                 foreach( i; 0 .. N )
287                 {
288                     auto v = reduce!((a,b)=>(a*=b))(1UL,imsize[0..i]);
289                     ret += crd[i] * v;
290                 }
291                 return ret;
292             }
293 
294             size_t index( in size_t[N] crd... ) const
295             { return indexCalc( header.size.data, crd ); }
296 
297             size_t index(V)( in V v ) const
298                 if( isCompatibleVector!(N,size_t,V) )
299             { return indexCalc( header.size.data, to!(size_t[N])(v.data) ); }
300         }
301 
302         @property
303         {
304             auto size() const { return head.size; }
305 
306             auto size(V)( in V sz ) 
307                 if( isCompatibleVector!(N,size_t,V) )
308             in
309             {
310                 enforce( all!"a>=0"(sz.data.dup),
311                         new ImageException( "resize components must be >= 0" ) );
312             }
313             body
314             {
315                 head.size = imsize_t( sz.data );
316                 if( data.length != head.dataSize )
317                     data = new void[]( head.dataSize );
318                 return sz;
319             }
320 
321             auto type() const { return head.type; }
322 
323             auto type( in PixelType tp )
324             {
325                 head.type = tp;
326                 if( data.length != head.dataSize )
327                     data = new void[]( head.dataSize );
328                 return tp;
329             }
330 
331             auto header() const { return head; }
332         }
333 
334         auto copy(T)( in Region!(N,T) r ) const if( isIntegral!T )
335         {
336             auto ret = selftype( imsize_t(r.size).data, this.header.type );
337 
338             auto crop = imregion_t( imsize_t(), size ).overlapLocal( r );
339             auto bpp = type.bpp;
340 
341             auto line_size = crop.size[0];
342 
343             enum fbody = `
344                 auto pp = ind - r.pos;
345                 if( all!"a[0]>=0&&a[0]<a[1]"(zip(pp.data.dup,ret.size.data.dup)) )
346                 {
347                     auto o1 = index(ind);
348                     auto a = o1 * bpp;
349                     auto b = (o1 + line_size) * bpp;
350                     auto o2 = ret.index(pp);
351                     auto c = o2 * bpp;
352                     auto d = (o2 + line_size) * bpp;
353                     ret.data[c..d] = data[a..b];
354                 }
355             `;
356             mixin( indexForeachString( "crop.pos", "crop.lim", "ind", fbody, 0 ) );
357 
358             return ret;
359         }
360 
361         void paste(V)( in V pos, in Image!N im )
362             if( isCompatibleVector!(N,size_t,V) )
363         {
364             enforce( im.type == this.type,
365                 new ImageException( "Image type is not good for paste." ) );
366 
367             auto crop = imregion_t( imregion_t.ptype.init, size ).overlapLocal( imregion_t(pos,im.size) );
368 
369             auto bpp = type.bpp;
370 
371             auto line_size = crop.size[0];
372 
373             enum fbody = `
374                 auto pp = ind - pos;
375                 if( all!"a[0]>=0&&a[0]<a[1]"(zip(pp.data.dup,im.size.data.dup)) )
376                 {
377                     auto o1 = index(ind);
378                     auto a = o1 * bpp;
379                     auto b = (o1 + line_size) * bpp;
380                     auto o2 = im.index(pp);
381                     auto c = o2 * bpp;
382                     auto d = (o2 + line_size) * bpp;
383                     data[a..b] = im.data[c..d];
384                 }
385                 `;
386             mixin( indexForeachString( "crop.pos", "crop.lim", "ind", fbody, 0 ) );
387         }
388 
389         private static string indexForeachString( string start, string end,
390                                   string ind, string fbody, size_t[] without... )
391         {
392             string[] ret;
393 
394             ret ~= format( `Vector!(N,size_t) %s = %s;`, ind, start );
395 
396             foreach( i; 0 .. N )
397             {
398                 if( canFind(without,i) ) continue;
399                 ret ~= format( `for( %2$s[%1$d] = %3$s[%1$d]; %2$s[%1$d] < %4$s[%1$d]; %2$s[%1$d]++ ){`, 
400                                       i, ind, start, end );
401             }
402 
403             ret ~= fbody;
404 
405             foreach( i; 0 .. N )
406             {
407                 if( canFind(without,i) ) continue;
408                 ret ~= "}";
409             }
410 
411             return ret.join("\n");
412         }
413 
414         static if( N > 1 )
415         {
416             @property Image!(N-1) histoConv(size_t K, T)() const if( K < N )
417             {
418                 if( T.sizeof != type.bpp )
419                     throw new ImageException( "type size uncompatible with elem size" );
420                 
421                 Vector!(N-1,size_t) ret_size;
422                 foreach( i; 0 .. N )
423                     if( i != K )
424                         ret_size[i-cast(size_t)(i>K)] = size[i];
425 
426                 auto ret = Image!(N-1)( ret_size, type );
427 
428                 enum fbody = `
429 
430                     Vector!(N-1,size_t) rind;
431                     foreach( i; 0 .. N ) if( i != K )
432                         rind[i-cast(size_t)(i>K)] = ind[i];
433 
434                     for( ind[K] = 0; ind[K] < size[K]; ind[K]++ )
435                         ret.mapAs!(T)[ret.index(rind)] += mapAs!(T)[index(ind)];
436                     `;
437                 mixin( indexForeachString( "Vector!(N,size_t).init", "size", "ind", fbody, K ) );
438 
439                 return ret;
440             }
441         }
442     }
443 }
444 
445 alias Image!1 Image1;
446 alias Image!2 Image2;
447 alias Image!3 Image3;
448 
449 version(unittest)
450 {
451     private struct bvec3
452     {
453         ubyte[3] data;
454         alias data this;
455         this( ubyte[3] d... ) { data = d; }
456     }
457 }
458 
459 unittest
460 {
461     auto a = Image2( [3,3], PixelType( ComponentType.UBYTE, 3 ) );
462     assert( a.data.length != 0 );
463     assert( eq( a.header.size, [3,3] ) );
464     a.pixel!bvec3(0,0) = bvec3(1,2,3);
465     auto b = a;
466     assert( b == a );
467     assert( eq( b.pixel!bvec3(0,0), bvec3(1,2,3) ) );
468     a.pixel!bvec3(1,1) = bvec3(3,4,5);
469     assert( b != a );
470     assert( b == Image2.load( b.dump() ) );
471     assert( b == b.dup );
472     assert( b.data == b.idup.data.dup );
473     a = b;
474     assert( b == a );
475     assert( a == Image2.load( b.dump() ) );
476     assert( a == b.dup );
477     assert( a.data == b.idup.data.dup );
478     auto crd = ivec2(1,2);
479     b.pixel!bvec3(crd) = bvec3(5,6,8);
480     assert( eq( b.pixel!bvec3(1,2), bvec3(5,6,8) ) );
481 
482     b.clear();
483     assert( eq( b.pixel!bvec3(1,2), bvec3(0,0,0) ) );
484 }
485 
486 unittest
487 {
488     auto a = Image2( [3,3], to!(ubyte[])([ 1,2,3,4,5,6,7,8,9 ]) );
489     auto b = Image2(a);
490 
491     assert( a.pixel!ubyte(0,0) == 1 );
492     assert( b.pixel!ubyte(0,0) == 1 );
493     a.pixel!ubyte(0,0) = 2;
494     assert( a.pixel!ubyte(0,0) == 2 );
495     assert( b.pixel!ubyte(0,0) == 1 );
496 
497     auto c = immutable Image2(a);
498     assert( c.pixel!ubyte(0,0) == 2 );
499 }
500 
501 unittest
502 {
503     auto a = Image1( [3], to!(ubyte[])([ 1,2,3 ]) );
504     assert(  mustExcept({ a.pixel!(ubyte)(8) = 0; }) );
505     assert( !mustExcept({ a.pixel!(ubyte)(0) = 0; }) );
506 
507     assert( a.pixel!ubyte(0) == 0 );
508 
509     auto b = Image2(a);
510 
511     assert( b.header.size.w == 3 );
512     assert( b.header.size.h == 1 );
513 
514     assert( b.pixel!ubyte(0,0) == 0 );
515     assert( b.pixel!ubyte(1,0) == 2 );
516     assert( mustExcept({ b.pixel!ubyte(1,1) = 2; }) );
517 
518     auto c = Image2(a,0);
519 
520     assert( c.header.size.w == 1 );
521     assert( c.header.size.h == 3 );
522 
523     assert( c.pixel!ubyte(0,0) == 0 );
524     assert( c.pixel!ubyte(0,1) == 2 );
525     assert( mustExcept({ c.pixel!ubyte(1,1) = 2; }) );
526 
527     c.size = ivec2(2,2);
528 
529     assert( c.size.w == 2 );
530     assert( c.size.h == 2 );
531 }
532 
533 unittest
534 {
535     auto a = Image1( [5], PixelType( ComponentType.FLOAT, 2 ) );
536     a.pixel!vec2(3) = vec2(1,1);
537     a.pixel!vec2(4) = vec2(2,2);
538     auto b = a.copy( Image1.imregion_t(3,2) );
539     assert( b.pixel!vec2(0) == a.pixel!vec2(3) );
540     assert( b.pixel!vec2(1) == a.pixel!vec2(4) );
541 }
542 
543 unittest
544 {
545     auto a = Image2( [3,3], PixelType( ComponentType.FLOAT, 2 ) );
546 
547     assert( a.index(1,2) == 7 );
548     assert( a.index(ivec2(1,2)) == 7 );
549 
550     a.mapAs!(vec2)[a.index(1,2)] = vec2(1,1);
551     assert( a.pixel!vec2(1,2) == vec2(1,1) );
552 
553     a.pixel!vec2(1,2) = vec2(2,2);
554     assert( a.pixel!vec2(1,2) == vec2(2,2) );
555 }
556 
557 unittest
558 {
559     auto a = Image3( [3,3,3], PixelType( ComponentType.FLOAT, 2 ) );
560 
561     assert( a.index(1,2,1) == 16 );
562 
563     a.mapAs!(vec2)[a.index(1,2,1)] = vec2(1,1);
564     assert( a.pixel!vec2(1,2,1) == vec2(1,1) );
565 
566     a.pixel!vec2(1,2,1) = vec2(2,2);
567     assert( a.pixel!vec2(1,2,1) == vec2(2,2) );
568 
569     auto b = a.copy( a.imregion_t(1,2,1,1,1,1) );
570     assert( b.pixel!vec2(0,0,0) == vec2(2,2) );
571 }
572 
573 unittest
574 {
575     pure vec2 sum( in Image2 img )
576     {
577         auto buf = img.mapAs!vec2;
578         return reduce!((a,b)=>(a+=b))(vec2(0,0),buf);
579     }
580 
581     auto a = Image2( [3,3], PixelType( ComponentType.FLOAT, 2 ) );
582 
583     a.pixel!vec2(0,0) = vec2(1,2);
584     a.pixel!vec2(1,2) = vec2(2,2);
585 
586     assert( sum(a) == vec2(3,4) );
587 }
588 
589 unittest
590 {
591     Image2 img;
592     assert( img.data.length == 0 );
593     assert( img.data is null );
594     assert( img.size == Image2.imsize_t(0,0) );
595 
596     img.size = ivec2(3,3);
597     img.type = PixelType( ComponentType.NORM_FLOAT, 3 );
598     img.clear();
599 
600     assert( img.data.length == 27 * float.sizeof );
601     assert( img.type.bpp == 3 * float.sizeof );
602 
603     img.pixel!col3(0,1) = col3( .2,.1,.3 );
604     assert( img.pixel!vec3(0,1) == vec3(.2,.1,.3) );
605 
606     auto di = Image2.load( img.dump() );
607     assert( di.size == img.size );
608     assert( di.type == img.type );
609     assert( di.data == img.data );
610 
611     auto ii = immutable(Image2)( img );
612     assert( ii.size == img.size );
613     assert( ii.type == img.type );
614     assert( ii.data == img.data );
615 
616     assert( ii.pixel!vec3(0,1) == vec3(.2,.1,.3) );
617 
618     auto dii = immutable(Image2).load( ii.dump() );
619     static assert( is( typeof(dii) == Image2 ) );
620     assert( dii.size == img.size );
621     assert( dii.type == img.type );
622     assert( dii.data == img.data );
623 
624     auto dd = ii.dup;
625     static assert( is( typeof(dd) == Image2 ) );
626     assert( dd.size == img.size );
627     assert( dd.type == img.type );
628     assert( dd.data == img.data );
629 
630     auto ddi = ii.idup;
631     static assert( is( typeof(ddi) == immutable(Image2) ) );
632     assert( ddi.size == img.size );
633     assert( ddi.type == img.type );
634     assert( ddi.data == img.data );
635 }
636 
637 unittest 
638 {
639     ubyte[] data = 
640     [
641         2, 1, 3, 5, 2,
642         9, 1, 2, 6, 0,
643         2, 5, 2, 9, 1,
644         8, 3, 6, 3, 0,
645         6, 2, 8, 1, 5 
646     ];
647 
648     ubyte[] datav1 =
649     [
650         0, 0, 0, 0, 0, 0, 0, 
651         0, 2, 1, 3, 5, 2, 0,
652         0, 9, 1, 2, 6, 0, 0,
653         0, 2, 5, 2, 9, 1, 0,
654         0, 8, 3, 6, 3, 0, 0,
655         0, 6, 2, 8, 1, 5, 0,
656         0, 0, 0, 0, 0, 0, 0 
657     ];
658 
659     ubyte[] datav2 = 
660     [
661         1, 2, 6,
662         5, 2, 9,
663         3, 6, 3
664     ];
665 
666     ubyte[] datav3 =
667     [
668         0, 0, 0, 0,
669         0, 2, 1, 3,
670         0, 9, 1, 2,
671         0, 2, 5, 2
672     ];
673 
674     ubyte[] datav4 =
675     [
676         0, 0, 0, 0, 
677         3, 5, 2, 0,
678         2, 6, 0, 0,
679         2, 9, 1, 0
680     ];
681 
682     ubyte[] datav5 =
683     [
684         0, 2, 5, 2,
685         0, 8, 3, 6,
686         0, 6, 2, 8,
687         0, 0, 0, 0
688     ];
689 
690     ubyte[] datav6 =
691     [
692         2, 9, 1, 0,
693         6, 3, 0, 0,
694         8, 1, 5, 0,
695         0, 0, 0, 0 
696     ];
697 
698     auto orig = Image2( [5,5], data );
699     auto im = orig.copy( iRegion2( 0, 0, 5, 5 ) );
700     assert( orig == im );
701     
702     auto imv1 = Image2( ivec2( 7, 7 ), datav1 );
703     assert( orig.copy( iRegion2( -1, -1, 7, 7 ) ) == imv1 );
704 
705     auto imv2 = Image2( [3,3], datav2 );
706     assert( orig.copy( iRegion2( 1, 1, 3, 3 ) ) == imv2 );
707 
708     auto imv3 = Image2( ivec2(4,4), datav3 );
709     assert( orig.copy( iRegion2( -1, -1, 4, 4 ) ) == imv3 );
710 
711     auto imv4 = Image2( ivec2(4,4), datav4 );
712     assert( orig.copy( iRegion2( 2, -1, 4, 4 ) ) == imv4 );
713 
714     auto imv5 = Image2( ivec2(4,4), datav5 );
715     assert( orig.copy( iRegion2( -1, 2, 4, 4 ) ) == imv5 );
716 
717     auto imv6 = Image2( [4,4], datav6 );
718     assert( orig.copy( iRegion2( 2, 2, 4, 4 ) ) == imv6 );
719 }
720 
721 unittest 
722 {
723     ubyte[] data = 
724     [
725         2, 1, 3, 5, 2,
726         9, 1, 2, 6, 0,
727         2, 5, 2, 9, 1,
728         8, 3, 6, 3, 0,
729         6, 2, 8, 1, 5 
730     ];
731 
732     ubyte[] datav1 = 
733     [
734         1, 2, 6, 0, 0, 0, 0,
735         5, 2, 9, 1, 0, 0, 0,
736         3, 6, 3, 0, 0, 0, 0,
737         2, 8, 1, 5, 0, 0, 0,  
738         0, 0, 0, 0, 0, 0, 0,
739         0, 0, 0, 0, 0, 0, 0,
740         0, 0, 0, 0, 0, 0, 0
741     ];
742 
743     ubyte[] datav2 = 
744     [
745         0, 0, 0, 0, 0, 0, 0,
746         0, 2, 1, 3, 5, 2, 0,
747         0, 9, 1, 2, 6, 0, 0,
748         0, 2, 5, 2, 9, 1, 0,  
749         0, 8, 3, 6, 3, 0, 0,
750         0, 6, 2, 8, 1, 5, 0,
751         0, 0, 0, 0, 0, 0, 0
752     ];
753 
754 
755     auto orig = Image2( ivec2( 7, 7 ), PixelType( ComponentType.RAWBYTE, 1 ) );
756     auto im = Image2( ivec2( 5, 5 ), data );
757 
758     auto res = Image2(orig);
759     res.paste( ivec2(-1,-1), im );
760     assert( res.data == datav1 );
761 
762     res = Image2(orig);
763     res.paste( ivec2(1,1), im );
764     assert( res.data == datav2 );
765 }
766 
767 unittest
768 {
769     auto data = 
770     [
771         vec2( 1, 2 ), vec2( 3, 4 ), vec2( 5, 6 ),
772         vec2( 7, 8 ), vec2( 9, 1 ), vec2( 1, 2 ),
773         vec2( 2, 3 ), vec2( 4, 5 ), vec2( 6, 7 )
774     ];
775 
776     auto img = Image2( ivec2(3,3), data );
777 
778     assert( img.size == ivec2(3,3) );
779     assert( img.type.bpp == 2 * float.sizeof );
780 
781     assert( img.pixel!vec2(1,1) == vec2(9,1) );
782     assert( img.type.comp == ComponentType.RAWBYTE );
783 
784     auto imdata = img.mapAs!vec2;
785     assert( data == imdata );
786 
787     img.clear();
788     assert( img.pixel!vec2(1,1) == vec2(0,0) );
789 
790     img.mapAs!(vec2)[] = data[];
791     imdata = img.mapAs!vec2;
792     assert( data == imdata );
793 
794     auto constdata = img.idup.mapAs!vec2;
795     assert( constdata == imdata );
796     assert( is( typeof(constdata) == const(vec2)[] ) );
797 }
798 
799 unittest
800 {
801     assert( mustExcept({ Image2( [3,3], PixelType( ComponentType.UBYTE, 3 ), [ 1, 2, 3 ] ); }) );
802 
803     auto dt = [ vec2(1,0), vec2(0,1) ];
804     assert( mustExcept({ Image2( ivec2(3,3), dt ); }) );
805 
806     auto img = Image2( ivec2(3,3), PixelType( ComponentType.NORM_FLOAT, 3 ) );
807     assert( mustExcept({ auto d = img.mapAs!vec2; }) );
808 
809     assert( !mustExcept({ img.pixel!col3(1,0) = col3(1,1,1); }) );
810     assert(  mustExcept({ img.pixel!vec2(1,0) = vec2(1,1); }) );
811     assert(  mustExcept({ img.pixel!vec3(4,4) = vec3(1,1); }) );
812 }
813 
814 unittest
815 {
816     ubyte[] dt =
817         [
818         0,0,0,0,
819         0,0,0,0,
820         0,0,0,0,
821         0,0,0,0,
822         
823         0,0,0,0,
824         0,1,2,0,
825         0,3,4,0,
826         0,0,0,0,
827 
828         0,0,0,0,
829         0,5,6,0,
830         0,7,8,0,
831         0,0,0,0,
832 
833         0,0,0,0,
834         0,0,0,0,
835         0,0,0,0,
836         0,0,0,0
837         ];
838 
839     ubyte[] cp = 
840         [
841         1,2,1,2,
842         3,4,3,4,
843         1,2,1,2,
844         3,4,3,4,
845 
846         5,6,5,6,
847         7,8,7,8,
848         5,6,5,6,
849         7,8,7,8,
850 
851         1,2,1,2,
852         3,4,3,4,
853         1,2,1,2,
854         3,4,3,4,
855 
856         5,6,5,6,
857         7,8,7,8,
858         5,6,5,6,
859         7,8,7,8,
860         ];
861 
862     ubyte[] rs = 
863         [
864             8,7,
865             6,5,
866             4,3,
867             2,1
868         ];
869 
870     ubyte[] nnd = [ 0,0, 0,0, 0,0, 0,8 ];
871 
872     auto a = Image3( [4,4,4], PixelType( ComponentType.UBYTE,1 ), dt );
873     auto b = Image3( [4,4,4], PixelType( ComponentType.UBYTE,1 ), cp );
874     auto c = Image3( [4,4,4], PixelType( ComponentType.UBYTE,1 ) );
875 
876     auto part = a.copy(iRegion3(ivec3(1,1,1), ivec3(2,2,2)));
877 
878     c.paste( ivec3(0,0,0), part );
879     c.paste( ivec3(0,2,0), part );
880     c.paste( ivec3(2,0,0), part );
881     c.paste( ivec3(2,2,0), part );
882     
883     c.paste( ivec3(0,0,2), part );
884     c.paste( ivec3(0,2,2), part );
885     c.paste( ivec3(2,0,2), part );
886     c.paste( ivec3(2,2,2), part );
887 
888     assert( b == c );
889 
890     auto part2 = b.copy(iRegion3(ivec3(1,1,1), ivec3(2,2,2)));
891     auto rr = Image3( ivec3(2,2,2), PixelType( ComponentType.UBYTE,1 ), rs );
892     assert( rr == part2 );
893 
894     auto nn = rr.copy( iRegion3( ivec3(-1,-1,-1), ivec3(2,2,2) ) );
895     auto nndi = Image3( ivec3(2,2,2), PixelType( ComponentType.UBYTE,1 ), nnd );
896 
897     assert( nn == nndi );
898 }
899 
900 unittest
901 {
902     ubyte[] img_data =
903         [
904             1,2,5,8,
905             4,3,1,1
906         ];
907 
908     ubyte[] hi_x_data = [ 16, 9 ];
909     ubyte[] hi_y_data = [ 5, 5, 6, 9 ];
910 
911     auto img = Image2( [4,2], PixelType( ComponentType.UBYTE, 1 ), img_data );
912     auto hi_x = Image1( [2], PixelType( ComponentType.UBYTE, 1 ), hi_x_data );
913     auto hi_y = Image1( [4], PixelType( ComponentType.UBYTE, 1 ), hi_y_data );
914 
915     assert( img.histoConv!(0,ubyte) == hi_x );
916     assert( img.histoConv!(1,ubyte) == hi_y );
917 }
918 
919 unittest
920 {
921     ubyte[] src_data =
922         [
923         1,2,3,
924         4,5,6,
925         7,8,9
926         ];
927 
928     ubyte[] dst1_data =
929         [
930         0,0,0,
931         0,0,0,
932         0,0,0,
933         1,2,3,
934         4,5,6,
935         7,8,9,
936         0,0,0,
937         0,0,0,
938         0,0,0
939         ];
940 
941     ubyte[] dst2_data =
942         [
943         0,1,0,
944         0,2,0,
945         0,3,0,
946         0,4,0,
947         0,5,0,
948         0,6,0,
949         0,7,0,
950         0,8,0,
951         0,9,0
952         ];
953 
954     auto src = Image2( ivec2(3,3), PixelType( ComponentType.UBYTE, 1 ), src_data );
955     auto dst = Image3( ivec3(3,3,3), PixelType( ComponentType.UBYTE, 1 ) );
956     dst.paste( ivec3(0,0,1), Image3( src ) );
957     assert( dst.data == dst1_data );
958     dst.clear();
959     dst.paste( ivec3(1,0,0), Image3(src,0) );
960     assert( dst.data == dst2_data );
961 }