1 /*****************************************************************************/ 2 // Copyright 2006-2012 Adobe Systems Incorporated 3 // All Rights Reserved. 4 // 5 // NOTICE: Adobe permits you to use, modify, and distribute this file in 6 // accordance with the terms of the Adobe license agreement accompanying it. 7 /*****************************************************************************/ 8 9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_utils.cpp#3 $ */ 10 /* $DateTime: 2012/08/12 15:38:38 $ */ 11 /* $Change: 842799 $ */ 12 /* $Author: tknoll $ */ 13 14 /*****************************************************************************/ 15 16 #include "dng_utils.h" 17 18 #include "dng_area_task.h" 19 #include "dng_assertions.h" 20 #include "dng_bottlenecks.h" 21 #include "dng_exceptions.h" 22 #include "dng_host.h" 23 #include "dng_image.h" 24 #include "dng_flags.h" 25 #include "dng_point.h" 26 #include "dng_rect.h" 27 #include "dng_safe_arithmetic.h" 28 #include "dng_tag_types.h" 29 #include "dng_tile_iterator.h" 30 31 #if qMacOS 32 #include <TargetConditionals.h> 33 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 34 #include <MobileCoreServices/MobileCoreServices.h> 35 #else 36 #include <CoreServices/CoreServices.h> 37 #endif // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 38 #endif // qMacOS 39 40 #if qiPhone || qMacOS 41 // these provide timers 42 #include <mach/mach.h> 43 #include <mach/mach_time.h> 44 #endif 45 46 #if qWinOS 47 #include <windows.h> 48 #else 49 #include <sys/time.h> 50 #include <stdarg.h> // for va_start/va_end 51 #endif 52 53 /*****************************************************************************/ 54 55 #if qDNGDebug 56 57 /*****************************************************************************/ 58 59 #if qMacOS 60 #define DNG_DEBUG_BREAK __asm__ volatile ("int3") 61 #elif qWinOS 62 #if qDNG64Bit 63 // no inline assembly on Win 64-bit, so use DebugBreak 64 #define DNG_DEBUG_BREAK DebugBreak() 65 #else 66 #define DNG_DEBUG_BREAK __asm__ volatile ("int3") 67 #endif 68 #elif qiPhone 69 // simulator is running on Intel 70 #if qiPhoneSimulator 71 #define DNG_DEBUG_BREAK __asm__ volatile ("int3") 72 #else 73 // The debugger doesn't restore program counter after this is called. 74 // Caller must move program counter past line to continue. 75 // As of iOS5/xCode 4.2, recovery may not be possible. 76 #define DNG_DEBUG_BREAK __asm__ volatile ("bkpt 1") 77 #endif 78 #elif qAndroid 79 #define DNG_DEBUG_BREAK __asm__ volatile ("bkpt 1") 80 #elif qLinux 81 #define DNG_DEBUG_BREAK __asm__ volatile ("int3") 82 #else 83 #define DNG_DEBUG_BREAK 84 #endif 85 86 /*****************************************************************************/ 87 88 bool gPrintAsserts = true; 89 bool gBreakOnAsserts = true; 90 91 /*****************************************************************************/ 92 93 void dng_show_message (const char *s) 94 { 95 96 #if qDNGPrintMessages 97 98 // display the message 99 if (gPrintAsserts) 100 fprintf (stderr, "%s\n", s); 101 102 #elif qiPhone || qAndroid || qLinux 103 104 if (gPrintAsserts) 105 fprintf (stderr, "%s\n", s); 106 107 // iOS doesn't print a message to the console like DebugStr and MessageBox do, so we have to do both 108 // You'll have to advance the program counter manually past this statement 109 if (gBreakOnAsserts) 110 DNG_DEBUG_BREAK; 111 112 #elif qMacOS 113 114 if (gBreakOnAsserts) 115 { 116 // truncate the to 255 chars 117 char ss [256]; 118 119 uint32 len = strlen (s); 120 if (len > 255) 121 len = 255; 122 strncpy (&(ss [1]), s, len ); 123 ss [0] = (unsigned char) len; 124 125 DebugStr ((unsigned char *) ss); 126 } 127 else if (gPrintAsserts) 128 { 129 fprintf (stderr, "%s\n", s); 130 } 131 132 #elif qWinOS 133 134 // display a dialog 135 // This is not thread safe. Multiple message boxes can be launched. 136 // Should also be launched in its own thread so main msg queue isn't thrown off. 137 if (gBreakOnAsserts) 138 MessageBoxA (NULL, (LPSTR) s, NULL, MB_OK); 139 else if (gPrintAsserts) 140 fprintf (stderr, "%s\n", s); 141 142 #endif 143 144 } 145 146 /*****************************************************************************/ 147 148 void dng_show_message_f (const char *fmt, ... ) 149 { 150 151 char buffer [1024]; 152 153 va_list ap; 154 va_start (ap, fmt); 155 156 vsnprintf (buffer, sizeof (buffer), fmt, ap); 157 158 va_end (ap); 159 160 dng_show_message (buffer); 161 162 } 163 164 /*****************************************************************************/ 165 166 #endif 167 168 /*****************************************************************************/ 169 170 uint32 ComputeBufferSize(uint32 pixelType, const dng_point &tileSize, 171 uint32 numPlanes, PaddingType paddingType) 172 173 { 174 175 // Convert tile size to uint32. 176 if (tileSize.h < 0 || tileSize.v < 0) 177 { 178 ThrowMemoryFull("Negative tile size"); 179 } 180 const uint32 tileSizeH = static_cast<uint32>(tileSize.h); 181 const uint32 tileSizeV = static_cast<uint32>(tileSize.v); 182 183 const uint32 pixelSize = TagTypeSize(pixelType); 184 185 // Add padding to width if necessary. 186 uint32 paddedWidth = tileSizeH; 187 if (paddingType == pad16Bytes) 188 { 189 if (!RoundUpForPixelSize(paddedWidth, pixelSize, &paddedWidth)) 190 { 191 ThrowMemoryFull("Arithmetic overflow computing buffer size"); 192 } 193 } 194 195 // Compute buffer size. 196 uint32 bufferSize; 197 if (!SafeUint32Mult(paddedWidth, tileSizeV, &bufferSize) || 198 !SafeUint32Mult(bufferSize, pixelSize, &bufferSize) || 199 !SafeUint32Mult(bufferSize, numPlanes, &bufferSize)) 200 { 201 ThrowMemoryFull("Arithmetic overflow computing buffer size"); 202 } 203 204 return bufferSize; 205 } 206 207 /*****************************************************************************/ 208 209 real64 TickTimeInSeconds () 210 { 211 212 #if qWinOS 213 214 // One might think it prudent to cache the frequency here, however 215 // low-power CPU modes can, and do, change the value returned. 216 // Thus the frequencey needs to be retrieved each time. 217 218 // Note that the frequency changing can cause the return 219 // result to jump backwards, which is why the TickCountInSeconds 220 // (below) also exists. 221 222 // Just plug in laptop when doing timings to minimize this. 223 // QPC/QPH is a slow call compared to rtdsc. 224 225 #if qImagecore 226 227 // You should be plugged-in when measuring. 228 229 static real64 freqMultiplier = 0.0; 230 231 if (freqMultiplier == 0.0) 232 { 233 234 LARGE_INTEGER freq; 235 236 QueryPerformanceFrequency (&freq); 237 238 freqMultiplier = 1.0 / (real64) freq.QuadPart; 239 240 } 241 242 #else 243 244 LARGE_INTEGER freq; 245 246 QueryPerformanceFrequency (&freq); 247 248 real64 freqMultiplier = 1.0 / (real64) freq.QuadPart; 249 250 #endif // qImagecore 251 252 LARGE_INTEGER cycles; 253 254 QueryPerformanceCounter (&cycles); 255 256 return (real64) cycles.QuadPart * freqMultiplier; 257 258 #elif qiPhone || qMacOS 259 260 // this is switching Mac to high performance timer 261 // and this is also the timer for iPhone 262 263 // assume frequency is unchanging, requesting frequency every time call 264 // is too slow. multiple cores, different frequency ? 265 266 static real64 freqMultiplier = 0.0; 267 if (freqMultiplier == 0.0) 268 { 269 mach_timebase_info_data_t freq; 270 mach_timebase_info(&freq); 271 272 // converts from nanos to micros 273 // numer = 125, denom = 3 * 1000 274 freqMultiplier = ((real64)freq.numer / (real64)freq.denom) * 1.0e-9; 275 } 276 277 return mach_absolute_time() * freqMultiplier; 278 279 #elif qAndroid || qLinux 280 281 //this is a fast timer to nanos 282 struct timespec now; 283 clock_gettime(CLOCK_MONOTONIC, &now); 284 return now.tv_sec + (real64)now.tv_nsec * 1.0e-9; 285 286 #else 287 288 // Perhaps a better call exists. (e.g. avoid adjtime effects) 289 290 struct timeval tv; 291 292 gettimeofday (&tv, NULL); 293 294 return tv.tv_sec + (real64)tv.tv_usec * 1.0e-6; 295 296 #endif 297 298 } 299 300 /*****************************************************************************/ 301 302 real64 TickCountInSeconds () 303 { 304 305 #if qWinOS 306 307 return GetTickCount () * (1.0 / 1000.0); 308 309 #elif qMacOS 310 311 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 312 // TODO: Needs implementation. 313 ThrowProgramError ("TickCountInSeconds() not implemented on iOS"); 314 return 0; 315 #else 316 return TickCount () * (1.0 / 60.0); 317 #endif // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 318 319 #else 320 321 return TickTimeInSeconds (); 322 323 #endif 324 325 } 326 327 /*****************************************************************************/ 328 329 bool gDNGShowTimers = true; 330 331 dng_timer::dng_timer (const char *message) 332 333 : fMessage (message ) 334 , fStartTime (TickTimeInSeconds ()) 335 336 { 337 338 } 339 340 /*****************************************************************************/ 341 342 dng_timer::~dng_timer () 343 { 344 345 if (!gDNGShowTimers) 346 return; 347 348 real64 totalTime = TickTimeInSeconds () - fStartTime; 349 350 fprintf (stderr, "%s: %0.3f sec\n", fMessage, totalTime); 351 352 } 353 354 /*****************************************************************************/ 355 356 real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point, 357 const dng_rect_real64 &rect) 358 { 359 360 real64 distSqr = DistanceSquared (point, 361 rect.TL ()); 362 363 distSqr = Max_real64 (distSqr, 364 DistanceSquared (point, 365 rect.BL ())); 366 367 distSqr = Max_real64 (distSqr, 368 DistanceSquared (point, 369 rect.BR ())); 370 371 distSqr = Max_real64 (distSqr, 372 DistanceSquared (point, 373 rect.TR ())); 374 375 return distSqr; 376 377 } 378 379 /*****************************************************************************/ 380 381 real64 MaxDistancePointToRect (const dng_point_real64 &point, 382 const dng_rect_real64 &rect) 383 { 384 385 return sqrt (MaxSquaredDistancePointToRect (point, 386 rect)); 387 388 } 389 390 /*****************************************************************************/ 391 392 dng_dither::dng_dither () 393 394 : fNoiseBuffer () 395 396 { 397 398 const uint32 kSeed = 1; 399 400 fNoiseBuffer.Allocate (kRNGSize2D * sizeof (uint16)); 401 402 uint16 *buffer = fNoiseBuffer.Buffer_uint16 (); 403 404 uint32 seed = kSeed; 405 406 for (uint32 i = 0; i < kRNGSize2D; i++) 407 { 408 409 seed = DNG_Random (seed); 410 411 buffer [i] = (uint16) (seed); 412 413 } 414 415 } 416 417 /******************************************************************************/ 418 419 const dng_dither & dng_dither::Get () 420 { 421 422 static dng_dither dither; 423 424 return dither; 425 426 } 427 428 /*****************************************************************************/ 429 430 void HistogramArea (dng_host & /* host */, 431 const dng_image &image, 432 const dng_rect &area, 433 uint32 *hist, 434 uint32 maxValue, 435 uint32 plane) 436 { 437 438 DNG_ASSERT (image.PixelType () == ttShort, "Unsupported pixel type"); 439 440 DoZeroBytes (hist, (maxValue + 1) * (uint32) sizeof (uint32)); 441 442 dng_rect tile; 443 444 dng_tile_iterator iter (image, area); 445 446 while (iter.GetOneTile (tile)) 447 { 448 449 dng_const_tile_buffer buffer (image, tile); 450 451 const void *sPtr = buffer.ConstPixel (tile.t, 452 tile.l, 453 plane); 454 455 uint32 count0 = 1; 456 uint32 count1 = tile.H (); 457 uint32 count2 = tile.W (); 458 459 int32 step0 = 0; 460 int32 step1 = buffer.fRowStep; 461 int32 step2 = buffer.fColStep; 462 463 OptimizeOrder (sPtr, 464 buffer.fPixelSize, 465 count0, 466 count1, 467 count2, 468 step0, 469 step1, 470 step2); 471 472 DNG_ASSERT (count0 == 1, "OptimizeOrder logic error"); 473 474 const uint16 *s1 = (const uint16 *) sPtr; 475 476 for (uint32 row = 0; row < count1; row++) 477 { 478 479 if (maxValue == 0x0FFFF && step2 == 1) 480 { 481 482 for (uint32 col = 0; col < count2; col++) 483 { 484 485 uint32 x = s1 [col]; 486 487 hist [x] ++; 488 489 } 490 491 } 492 493 else 494 { 495 496 const uint16 *s2 = s1; 497 498 for (uint32 col = 0; col < count2; col++) 499 { 500 501 uint32 x = s2 [0]; 502 503 if (x <= maxValue) 504 { 505 506 hist [x] ++; 507 508 } 509 510 s2 += step2; 511 512 } 513 514 } 515 516 s1 += step1; 517 518 } 519 520 } 521 522 } 523 524 /*****************************************************************************/ 525 526 class dng_limit_float_depth_task: public dng_area_task 527 { 528 529 private: 530 531 const dng_image &fSrcImage; 532 533 dng_image &fDstImage; 534 535 uint32 fBitDepth; 536 537 real32 fScale; 538 539 public: 540 541 dng_limit_float_depth_task (const dng_image &srcImage, 542 dng_image &dstImage, 543 uint32 bitDepth, 544 real32 scale); 545 546 virtual dng_rect RepeatingTile1 () const 547 { 548 return fSrcImage.RepeatingTile (); 549 } 550 551 virtual dng_rect RepeatingTile2 () const 552 { 553 return fDstImage.RepeatingTile (); 554 } 555 556 virtual void Process (uint32 threadIndex, 557 const dng_rect &tile, 558 dng_abort_sniffer *sniffer); 559 560 }; 561 562 /*****************************************************************************/ 563 564 dng_limit_float_depth_task::dng_limit_float_depth_task (const dng_image &srcImage, 565 dng_image &dstImage, 566 uint32 bitDepth, 567 real32 scale) 568 569 : fSrcImage (srcImage) 570 , fDstImage (dstImage) 571 , fBitDepth (bitDepth) 572 , fScale (scale) 573 574 { 575 576 } 577 578 /*****************************************************************************/ 579 580 void dng_limit_float_depth_task::Process (uint32 /* threadIndex */, 581 const dng_rect &tile, 582 dng_abort_sniffer * /* sniffer */) 583 { 584 585 dng_const_tile_buffer srcBuffer (fSrcImage, tile); 586 dng_dirty_tile_buffer dstBuffer (fDstImage, tile); 587 588 uint32 count0 = tile.H (); 589 uint32 count1 = tile.W (); 590 uint32 count2 = fDstImage.Planes (); 591 592 int32 sStep0 = srcBuffer.fRowStep; 593 int32 sStep1 = srcBuffer.fColStep; 594 int32 sStep2 = srcBuffer.fPlaneStep; 595 596 int32 dStep0 = dstBuffer.fRowStep; 597 int32 dStep1 = dstBuffer.fColStep; 598 int32 dStep2 = dstBuffer.fPlaneStep; 599 600 const void *sPtr = srcBuffer.ConstPixel (tile.t, 601 tile.l, 602 0); 603 604 void *dPtr = dstBuffer.DirtyPixel (tile.t, 605 tile.l, 606 0); 607 608 OptimizeOrder (sPtr, 609 dPtr, 610 srcBuffer.fPixelSize, 611 dstBuffer.fPixelSize, 612 count0, 613 count1, 614 count2, 615 sStep0, 616 sStep1, 617 sStep2, 618 dStep0, 619 dStep1, 620 dStep2); 621 622 const real32 *sPtr0 = (const real32 *) sPtr; 623 real32 *dPtr0 = ( real32 *) dPtr; 624 625 real32 scale = fScale; 626 627 bool limit16 = (fBitDepth == 16); 628 bool limit24 = (fBitDepth == 24); 629 630 for (uint32 index0 = 0; index0 < count0; index0++) 631 { 632 633 const real32 *sPtr1 = sPtr0; 634 real32 *dPtr1 = dPtr0; 635 636 for (uint32 index1 = 0; index1 < count1; index1++) 637 { 638 639 // If the scale is a NOP, and the data is packed solid, we can just do memory 640 // copy. 641 642 if (scale == 1.0f && sStep2 == 1 && dStep2 == 1) 643 { 644 645 if (dPtr1 != sPtr1) // srcImage != dstImage 646 { 647 648 memcpy (dPtr1, sPtr1, count2 * (uint32) sizeof (real32)); 649 650 } 651 652 } 653 654 else 655 { 656 657 const real32 *sPtr2 = sPtr1; 658 real32 *dPtr2 = dPtr1; 659 660 for (uint32 index2 = 0; index2 < count2; index2++) 661 { 662 663 real32 x = sPtr2 [0]; 664 665 x *= scale; 666 667 dPtr2 [0] = x; 668 669 sPtr2 += sStep2; 670 dPtr2 += dStep2; 671 672 } 673 674 } 675 676 // The data is now in the destination buffer. 677 678 if (limit16) 679 { 680 681 uint32 *dPtr2 = (uint32 *) dPtr1; 682 683 for (uint32 index2 = 0; index2 < count2; index2++) 684 { 685 686 uint32 x = dPtr2 [0]; 687 688 uint16 y = DNG_FloatToHalf (x); 689 690 x = DNG_HalfToFloat (y); 691 692 dPtr2 [0] = x; 693 694 dPtr2 += dStep2; 695 696 } 697 698 } 699 700 else if (limit24) 701 { 702 703 uint32 *dPtr2 = (uint32 *) dPtr1; 704 705 for (uint32 index2 = 0; index2 < count2; index2++) 706 { 707 708 uint32 x = dPtr2 [0]; 709 710 uint8 temp [3]; 711 712 DNG_FloatToFP24 (x, temp); 713 714 x = DNG_FP24ToFloat (temp); 715 716 dPtr2 [0] = x; 717 718 dPtr2 += dStep2; 719 720 } 721 722 } 723 724 sPtr1 += sStep1; 725 dPtr1 += dStep1; 726 727 } 728 729 sPtr0 += sStep0; 730 dPtr0 += dStep0; 731 732 } 733 734 } 735 736 /******************************************************************************/ 737 738 void LimitFloatBitDepth (dng_host &host, 739 const dng_image &srcImage, 740 dng_image &dstImage, 741 uint32 bitDepth, 742 real32 scale) 743 { 744 745 DNG_ASSERT (srcImage.PixelType () == ttFloat, "Floating point image expected"); 746 DNG_ASSERT (dstImage.PixelType () == ttFloat, "Floating point image expected"); 747 748 dng_limit_float_depth_task task (srcImage, 749 dstImage, 750 bitDepth, 751 scale); 752 753 host.PerformAreaTask (task, dstImage.Bounds ()); 754 755 } 756 757 /*****************************************************************************/ 758