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 }