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
88bool gPrintAsserts   = true;
89bool gBreakOnAsserts = true;
90
91/*****************************************************************************/
92
93void 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
148void 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
170uint32 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
209real64 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
302real64 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
329bool gDNGShowTimers = true;
330
331dng_timer::dng_timer (const char *message)
332
333	:	fMessage   (message             )
334	,	fStartTime (TickTimeInSeconds ())
335
336	{
337
338	}
339
340/*****************************************************************************/
341
342dng_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
356real64 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
381real64 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
392dng_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
419const dng_dither & dng_dither::Get ()
420	{
421
422	static dng_dither dither;
423
424	return dither;
425
426	}
427
428/*****************************************************************************/
429
430void 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
526class 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
564dng_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
580void 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
738void 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