1 /*****************************************************************************/
2 // Copyright 2008-2009 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_misc_opcodes.cpp#1 $ */
10 /* $DateTime: 2012/05/30 13:28:51 $ */
11 /* $Change: 832332 $ */
12 /* $Author: tknoll $ */
13 
14 /*****************************************************************************/
15 
16 #include "dng_misc_opcodes.h"
17 
18 #include "dng_bottlenecks.h"
19 #include "dng_exceptions.h"
20 #include "dng_globals.h"
21 #include "dng_host.h"
22 #include "dng_image.h"
23 #include "dng_rect.h"
24 #include "dng_safe_arithmetic.h"
25 #include "dng_stream.h"
26 #include "dng_tag_values.h"
27 
28 /*****************************************************************************/
29 
dng_opcode_TrimBounds(const dng_rect & bounds)30 dng_opcode_TrimBounds::dng_opcode_TrimBounds (const dng_rect &bounds)
31 
32 	:	dng_opcode (dngOpcode_TrimBounds,
33 					dngVersion_1_3_0_0,
34 					kFlag_None)
35 
36 	,	fBounds (bounds)
37 
38 	{
39 
40 	}
41 
42 /*****************************************************************************/
43 
dng_opcode_TrimBounds(dng_stream & stream)44 dng_opcode_TrimBounds::dng_opcode_TrimBounds (dng_stream &stream)
45 
46 	:	dng_opcode (dngOpcode_TrimBounds,
47 					stream,
48 					"TrimBounds")
49 
50 	,	fBounds ()
51 
52 	{
53 
54 	if (stream.Get_uint32 () != 16)
55 		{
56 		ThrowBadFormat ();
57 		}
58 
59 	fBounds.t = stream.Get_int32 ();
60 	fBounds.l = stream.Get_int32 ();
61 	fBounds.b = stream.Get_int32 ();
62 	fBounds.r = stream.Get_int32 ();
63 
64 	if (fBounds.IsEmpty ())
65 		{
66 		ThrowBadFormat ();
67 		}
68 
69 	#if qDNGValidate
70 
71 	if (gVerbose)
72 		{
73 
74 		printf ("Bounds: t=%d, l=%d, b=%d, r=%d\n",
75 				(int) fBounds.t,
76 				(int) fBounds.l,
77 				(int) fBounds.b,
78 				(int) fBounds.r);
79 
80 		}
81 
82 	#endif
83 
84 	}
85 
86 /*****************************************************************************/
87 
PutData(dng_stream & stream) const88 void dng_opcode_TrimBounds::PutData (dng_stream &stream) const
89 	{
90 
91 	stream.Put_uint32 (16);
92 
93 	stream.Put_int32 (fBounds.t);
94 	stream.Put_int32 (fBounds.l);
95 	stream.Put_int32 (fBounds.b);
96 	stream.Put_int32 (fBounds.r);
97 
98 	}
99 
100 /*****************************************************************************/
101 
Apply(dng_host &,dng_negative &,AutoPtr<dng_image> & image)102 void dng_opcode_TrimBounds::Apply (dng_host & /* host */,
103 								   dng_negative & /* negative */,
104 								   AutoPtr<dng_image> &image)
105 	{
106 
107 	if (fBounds.IsEmpty () || (fBounds & image->Bounds ()) != fBounds)
108 		{
109 		ThrowBadFormat ();
110 		}
111 
112 	image->Trim (fBounds);
113 
114 	}
115 
116 /*****************************************************************************/
117 
GetData(dng_stream & stream)118 void dng_area_spec::GetData (dng_stream &stream)
119 	{
120 
121 	fArea.t = stream.Get_int32 ();
122 	fArea.l = stream.Get_int32 ();
123 	fArea.b = stream.Get_int32 ();
124 	fArea.r = stream.Get_int32 ();
125 
126 	fPlane  = stream.Get_uint32 ();
127 	fPlanes = stream.Get_uint32 ();
128 
129 	fRowPitch = stream.Get_uint32 ();
130 	fColPitch = stream.Get_uint32 ();
131 
132 	if (fPlanes < 1)
133 		{
134 		ThrowBadFormat ();
135 		}
136 
137 	if (fRowPitch < 1 || fColPitch < 1)
138 		{
139 		ThrowBadFormat ();
140 		}
141 
142 	if (fArea.IsEmpty ())
143 		{
144 		if (fRowPitch != 1 || fColPitch != 1)
145 			{
146 			ThrowBadFormat ();
147 			}
148 		}
149 
150 	else
151 		{
152 		int32 width = 0;
153 		int32 height = 0;
154 		if (!SafeInt32Sub (fArea.b, fArea.t, &height) ||
155 			 !SafeInt32Sub (fArea.r, fArea.l, &width) ||
156 			 fRowPitch > static_cast<uint32>(height) ||
157 			 fColPitch > static_cast<uint32>(width))
158 			{
159 			ThrowBadFormat();
160 			}
161 		}
162 
163 	#if qDNGValidate
164 
165 	if (gVerbose)
166 		{
167 
168 		printf ("AreaSpec: t=%d, l=%d, b=%d, r=%d, p=%u:%u, rp=%u, cp=%u\n",
169 				(int) fArea.t,
170 				(int) fArea.l,
171 				(int) fArea.b,
172 				(int) fArea.r,
173 				(unsigned) fPlane,
174 				(unsigned) fPlanes,
175 				(unsigned) fRowPitch,
176 				(unsigned) fColPitch);
177 
178 		}
179 
180 	#endif
181 
182 	}
183 
184 /*****************************************************************************/
185 
PutData(dng_stream & stream) const186 void dng_area_spec::PutData (dng_stream &stream) const
187 	{
188 
189 	stream.Put_int32 (fArea.t);
190 	stream.Put_int32 (fArea.l);
191 	stream.Put_int32 (fArea.b);
192 	stream.Put_int32 (fArea.r);
193 
194 	stream.Put_uint32 (fPlane);
195 	stream.Put_uint32 (fPlanes);
196 
197 	stream.Put_uint32 (fRowPitch);
198 	stream.Put_uint32 (fColPitch);
199 
200 	}
201 
202 /*****************************************************************************/
203 
Overlap(const dng_rect & tile) const204 dng_rect dng_area_spec::Overlap (const dng_rect &tile) const
205 	{
206 
207 	// Special case - if the fArea is empty, then dng_area_spec covers
208 	// the entire image, no matter how large it is.
209 
210 	if (fArea.IsEmpty ())
211 		{
212 		return tile;
213 		}
214 
215 	dng_rect overlap = fArea & tile;
216 
217 	if (overlap.NotEmpty ())
218 		{
219 
220 		overlap.t = fArea.t + ConvertUint32ToInt32(
221 			RoundUpUint32ToMultiple(static_cast<uint32>(overlap.t - fArea.t),
222 									fRowPitch));
223 		overlap.l = fArea.l + ConvertUint32ToInt32(
224 			RoundUpUint32ToMultiple(static_cast<uint32>(overlap.l - fArea.l),
225 									fColPitch));
226 
227 		if (overlap.NotEmpty ())
228 			{
229 
230 			overlap.b = overlap.t + ((overlap.H () - 1) / fRowPitch) * fRowPitch + 1;
231 			overlap.r = overlap.l + ((overlap.W () - 1) / fColPitch) * fColPitch + 1;
232 
233 			return overlap;
234 
235 			}
236 
237 		}
238 
239 	return dng_rect ();
240 
241 	}
242 
243 /*****************************************************************************/
244 
dng_opcode_MapTable(dng_host & host,const dng_area_spec & areaSpec,const uint16 * table,uint32 count)245 dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host,
246 										  const dng_area_spec &areaSpec,
247 										  const uint16 *table,
248 										  uint32 count)
249 
250 	:	dng_inplace_opcode (dngOpcode_MapTable,
251 							dngVersion_1_3_0_0,
252 							kFlag_None)
253 
254 	,	fAreaSpec (areaSpec)
255 	,	fTable    ()
256 	,	fCount    (count)
257 
258 	{
259 
260 	if (count == 0 || count > 0x10000)
261 		{
262 		ThrowProgramError ();
263 		}
264 
265 	fTable.Reset (host.Allocate (0x10000 * sizeof (uint16)));
266 
267 	DoCopyBytes (table,
268 				 fTable->Buffer (),
269 				 count * (uint32) sizeof (uint16));
270 
271 	ReplicateLastEntry ();
272 
273 	}
274 
275 /*****************************************************************************/
276 
dng_opcode_MapTable(dng_host & host,dng_stream & stream)277 dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host,
278 										  dng_stream &stream)
279 
280 	:	dng_inplace_opcode (dngOpcode_MapTable,
281 							stream,
282 							"MapTable")
283 
284 	,	fAreaSpec ()
285 	,	fTable    ()
286 	,	fCount    (0)
287 
288 	{
289 
290 	uint32 dataSize = stream.Get_uint32 ();
291 
292 	fAreaSpec.GetData (stream);
293 
294 	fCount = stream.Get_uint32 ();
295 
296 	uint32 requiredSize = SafeUint32Mult(fCount, 2);
297 	requiredSize = SafeUint32Add(requiredSize, dng_area_spec::kDataSize);
298 	requiredSize = SafeUint32Add(requiredSize, 4);
299 	if (dataSize != requiredSize)
300 		{
301 		ThrowBadFormat ();
302 		}
303 
304 	if (fCount == 0 || fCount > 0x10000)
305 		{
306 		ThrowBadFormat ();
307 		}
308 
309 	fTable.Reset (host.Allocate (0x10000 * sizeof (uint16)));
310 
311 	uint16 *table = fTable->Buffer_uint16 ();
312 
313 	for (uint32 index = 0; index < fCount; index++)
314 		{
315 		table [index] = stream.Get_uint16 ();
316 		}
317 
318 	ReplicateLastEntry ();
319 
320 	#if qDNGValidate
321 
322 	if (gVerbose)
323 		{
324 
325 		printf ("Count: %u\n", (unsigned) fCount);
326 
327 		for (uint32 j = 0; j < fCount && j < gDumpLineLimit; j++)
328 			{
329 			printf ("    Table [%5u] = %5u\n", (unsigned) j, (unsigned) table [j]);
330 			}
331 
332 		if (fCount > gDumpLineLimit)
333 			{
334 			printf ("    ... %u table entries skipped\n", (unsigned) (fCount - gDumpLineLimit));
335 			}
336 
337 		}
338 
339 	#endif
340 
341 	}
342 
343 /*****************************************************************************/
344 
ReplicateLastEntry()345 void dng_opcode_MapTable::ReplicateLastEntry ()
346 	{
347 
348 	uint16 *table = fTable->Buffer_uint16 ();
349 
350 	uint16 lastEntry = table [fCount];
351 
352 	for (uint32 index = fCount; index < 0x10000; index++)
353 		{
354 		table [index] = lastEntry;
355 		}
356 
357 	}
358 
359 /*****************************************************************************/
360 
PutData(dng_stream & stream) const361 void dng_opcode_MapTable::PutData (dng_stream &stream) const
362 	{
363 
364 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + fCount * 2);
365 
366 	fAreaSpec.PutData (stream);
367 
368 	stream.Put_uint32 (fCount);
369 
370 	uint16 *table = fTable->Buffer_uint16 ();
371 
372 	for (uint32 index = 0; index < fCount; index++)
373 		{
374 		stream.Put_uint16 (table [index]);
375 		}
376 
377 	}
378 
379 /*****************************************************************************/
380 
BufferPixelType(uint32)381 uint32 dng_opcode_MapTable::BufferPixelType (uint32 /* imagePixelType */)
382 	{
383 
384 	return ttShort;
385 
386 	}
387 
388 /*****************************************************************************/
389 
ModifiedBounds(const dng_rect & imageBounds)390 dng_rect dng_opcode_MapTable::ModifiedBounds (const dng_rect &imageBounds)
391 	{
392 
393 	return fAreaSpec.Overlap (imageBounds);
394 
395 	}
396 
397 /*****************************************************************************/
398 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)399 void dng_opcode_MapTable::ProcessArea (dng_negative & /* negative */,
400 									   uint32 /* threadIndex */,
401 									   dng_pixel_buffer &buffer,
402 									   const dng_rect &dstArea,
403 									   const dng_rect & /* imageBounds */)
404 	{
405 
406 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
407 
408 	if (overlap.NotEmpty ())
409 		{
410 
411 		for (uint32 plane = fAreaSpec.Plane ();
412 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
413 			 plane < buffer.Planes ();
414 			 plane++)
415 			{
416 
417 			DoMapArea16 (buffer.DirtyPixel_uint16 (overlap.t, overlap.l, plane),
418 						 1,
419 						 (overlap.H () + fAreaSpec.RowPitch () - 1) / fAreaSpec.RowPitch (),
420 						 (overlap.W () + fAreaSpec.ColPitch () - 1) / fAreaSpec.ColPitch (),
421 						 0,
422 						 fAreaSpec.RowPitch () * buffer.RowStep (),
423 						 fAreaSpec.ColPitch (),
424 						 fTable->Buffer_uint16 ());
425 
426 			}
427 
428 		}
429 
430 	}
431 
432 /*****************************************************************************/
433 
dng_opcode_MapPolynomial(const dng_area_spec & areaSpec,uint32 degree,const real64 * coefficient)434 dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (const dng_area_spec &areaSpec,
435 													uint32 degree,
436 													const real64 *coefficient)
437 
438 	:	dng_inplace_opcode (dngOpcode_MapPolynomial,
439 							dngVersion_1_3_0_0,
440 							kFlag_None)
441 
442 	,	fAreaSpec (areaSpec)
443 	,	fDegree   (degree)
444 
445 	{
446 
447 	for (uint32 j = 0; j <= kMaxDegree; j++)
448 		{
449 
450 		if (j <= fDegree)
451 			{
452 			fCoefficient [j] = coefficient [j];
453 			}
454 
455 		else
456 			{
457 			fCoefficient [j] = 0.0;
458 			}
459 
460 		}
461 
462 	// Reduce degree if possible.
463 
464 	while (fDegree > 0 && fCoefficient [fDegree] == 0.0)
465 		{
466 		fDegree--;
467 		}
468 
469 	}
470 
471 /*****************************************************************************/
472 
dng_opcode_MapPolynomial(dng_stream & stream)473 dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (dng_stream &stream)
474 
475 	:	dng_inplace_opcode (dngOpcode_MapPolynomial,
476 							stream,
477 							"MapPolynomial")
478 
479 	,	fAreaSpec ()
480 	,	fDegree   (0)
481 
482 	{
483 
484 	uint32 dataSize = stream.Get_uint32 ();
485 
486 	fAreaSpec.GetData (stream);
487 
488 	fDegree = stream.Get_uint32 ();
489 
490 	if (fDegree > kMaxDegree)
491 		{
492 		ThrowBadFormat ();
493 		}
494 
495 	if (dataSize != dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8)
496 		{
497 		ThrowBadFormat ();
498 		}
499 
500 	for (uint32 j = 0; j <= kMaxDegree; j++)
501 		{
502 
503 		if (j <= fDegree)
504 			{
505 			fCoefficient [j] = stream.Get_real64 ();
506 			}
507 		else
508 			{
509 			fCoefficient [j] = 0.0;
510 			}
511 
512 		}
513 
514 	#if qDNGValidate
515 
516 	if (gVerbose)
517 		{
518 
519 		for (uint32 k = 0; k <= fDegree; k++)
520 			{
521 			printf ("    Coefficient [%u] = %f\n", (unsigned) k, fCoefficient [k]);
522 			}
523 
524 		}
525 
526 	#endif
527 
528 	}
529 
530 /*****************************************************************************/
531 
PutData(dng_stream & stream) const532 void dng_opcode_MapPolynomial::PutData (dng_stream &stream) const
533 	{
534 
535 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8);
536 
537 	fAreaSpec.PutData (stream);
538 
539 	stream.Put_uint32 (fDegree);
540 
541 	for (uint32 j = 0; j <= fDegree; j++)
542 		{
543 		stream.Put_real64 (fCoefficient [j]);
544 		}
545 
546 	}
547 
548 /*****************************************************************************/
549 
BufferPixelType(uint32 imagePixelType)550 uint32 dng_opcode_MapPolynomial::BufferPixelType (uint32 imagePixelType)
551 	{
552 
553 	// If we are operating on the stage 1 image, then we need
554 	// to adjust the coefficients to convert from the image
555 	// values to the 32-bit floating point values that this
556 	// opcode operates on.
557 
558 	// If we are operating on the stage 2 or 3 image, the logical
559 	// range of the image is already 0.0 to 1.0, so we don't
560 	// need to adjust the values.
561 
562 	real64 scale32 = 1.0;
563 
564 	if (Stage () == 1)
565 		{
566 
567 		switch (imagePixelType)
568 			{
569 
570 			case ttFloat:
571 				break;
572 
573 			case ttShort:
574 				{
575 				scale32 = (real64) 0xFFFF;
576 				break;
577 				}
578 
579 			case ttLong:
580 				{
581 				scale32 = (real64) 0xFFFFFFFF;
582 				break;
583 				}
584 
585 			default:
586 				ThrowBadFormat ();
587 
588 			}
589 
590 		}
591 
592 	real64 factor32 = 1.0 / scale32;
593 
594 	for (uint32 j = 0; j <= kMaxDegree; j++)
595 		{
596 
597 		fCoefficient32 [j] = ConvertDoubleToFloat(fCoefficient [j] * factor32);
598 
599 		factor32 *= scale32;
600 
601 		}
602 
603 	return ttFloat;
604 
605 	}
606 
607 /*****************************************************************************/
608 
ModifiedBounds(const dng_rect & imageBounds)609 dng_rect dng_opcode_MapPolynomial::ModifiedBounds (const dng_rect &imageBounds)
610 	{
611 
612 	return fAreaSpec.Overlap (imageBounds);
613 
614 	}
615 
616 /*****************************************************************************/
617 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)618 void dng_opcode_MapPolynomial::ProcessArea (dng_negative & /* negative */,
619 											uint32 /* threadIndex */,
620 											dng_pixel_buffer &buffer,
621 											const dng_rect &dstArea,
622 											const dng_rect & /* imageBounds */)
623 	{
624 
625 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
626 
627 	if (overlap.NotEmpty ())
628 		{
629 
630 		uint32 cols = overlap.W ();
631 
632 		uint32 colPitch = fAreaSpec.ColPitch ();
633 
634 		for (uint32 plane = fAreaSpec.Plane ();
635 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
636 			 plane < buffer.Planes ();
637 			 plane++)
638 			{
639 
640 			for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ())
641 				{
642 
643 				real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane);
644 
645 				switch (fDegree)
646 					{
647 
648 					case 0:
649 						{
650 
651 						real32 y = Pin_real32 (0.0f,
652 											   fCoefficient32 [0],
653 											   1.0f);
654 
655 						for (uint32 col = 0; col < cols; col += colPitch)
656 							{
657 
658 							dPtr [col] = y;
659 
660 							}
661 
662 						break;
663 
664 						}
665 
666 					case 1:
667 						{
668 
669 						real32 c0 = fCoefficient32 [0];
670 						real32 c1 = fCoefficient32 [1];
671 
672 						if (c0 == 0.0f)
673 							{
674 
675 							if (c1 > 0.0f)
676 								{
677 
678 								for (uint32 col = 0; col < cols; col += colPitch)
679 									{
680 
681 									real32 x = dPtr [col];
682 
683 									real32 y = c1 * x;
684 
685 									dPtr [col] = Min_real32 (y, 1.0f);
686 
687 									}
688 
689 								}
690 
691 							else
692 								{
693 
694 								for (uint32 col = 0; col < cols; col += colPitch)
695 									{
696 
697 									dPtr [col] = 0.0f;
698 
699 									}
700 
701 								}
702 
703 							}
704 
705 						else
706 							{
707 
708 							for (uint32 col = 0; col < cols; col += colPitch)
709 								{
710 
711 								real32 x = dPtr [col];
712 
713 								real32 y = c0 +
714 										   c1 * x;
715 
716 								dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
717 
718 								}
719 
720 							}
721 
722 						break;
723 
724 						}
725 
726 					case 2:
727 						{
728 
729 						for (uint32 col = 0; col < cols; col += colPitch)
730 							{
731 
732 							real32 x = dPtr [col];
733 
734 							real32 y =  fCoefficient32 [0] + x *
735 									   (fCoefficient32 [1] + x *
736 									   (fCoefficient32 [2]));
737 
738 							dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
739 
740 							}
741 
742 						break;
743 
744 						}
745 
746 					case 3:
747 						{
748 
749 						for (uint32 col = 0; col < cols; col += colPitch)
750 							{
751 
752 							real32 x = dPtr [col];
753 
754 							real32 y =  fCoefficient32 [0] + x *
755 									   (fCoefficient32 [1] + x *
756 									   (fCoefficient32 [2] + x *
757 									   (fCoefficient32 [3])));
758 
759 							dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
760 
761 							}
762 
763 						break;
764 
765 						}
766 
767 					case 4:
768 						{
769 
770 						for (uint32 col = 0; col < cols; col += colPitch)
771 							{
772 
773 							real32 x = dPtr [col];
774 
775 							real32 y =  fCoefficient32 [0] + x *
776 									   (fCoefficient32 [1] + x *
777 									   (fCoefficient32 [2] + x *
778 									   (fCoefficient32 [3] + x *
779 									   (fCoefficient32 [4]))));
780 
781 							dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
782 
783 							}
784 
785 						break;
786 
787 						}
788 
789 					default:
790 						{
791 
792 						for (uint32 col = 0; col < cols; col += colPitch)
793 							{
794 
795 							real32 x = dPtr [col];
796 
797 							real32 y = fCoefficient32 [0];
798 
799 							real32 xx = x;
800 
801 							for (uint32 j = 1; j <= fDegree; j++)
802 								{
803 
804 								y += fCoefficient32 [j] * xx;
805 
806 								xx *= x;
807 
808 								}
809 
810 							dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
811 
812 							}
813 
814 						}
815 
816 					}
817 
818 				}
819 
820 			}
821 
822 		}
823 
824 	}
825 
826 /*****************************************************************************/
827 
dng_opcode_DeltaPerRow(const dng_area_spec & areaSpec,AutoPtr<dng_memory_block> & table)828 dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (const dng_area_spec &areaSpec,
829 												AutoPtr<dng_memory_block> &table)
830 
831 	:	dng_inplace_opcode (dngOpcode_DeltaPerRow,
832 							dngVersion_1_3_0_0,
833 							kFlag_None)
834 
835 	,	fAreaSpec (areaSpec)
836 	,	fTable    ()
837 	,	fScale    (1.0f)
838 
839 	{
840 
841 	fTable.Reset (table.Release ());
842 
843 	}
844 
845 /*****************************************************************************/
846 
dng_opcode_DeltaPerRow(dng_host & host,dng_stream & stream)847 dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (dng_host &host,
848 												dng_stream &stream)
849 
850 	:	dng_inplace_opcode (dngOpcode_DeltaPerRow,
851 							stream,
852 							"DeltaPerRow")
853 
854 	,	fAreaSpec ()
855 	,	fTable    ()
856 	,	fScale    (1.0f)
857 
858 	{
859 
860 	uint32 dataSize = stream.Get_uint32 ();
861 
862 	fAreaSpec.GetData (stream);
863 
864 	uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().H (),
865 										fAreaSpec.RowPitch ());
866 
867 	if (deltas != stream.Get_uint32 ())
868 		{
869 		ThrowBadFormat ();
870 		}
871 
872 	if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4)
873 		{
874 		ThrowBadFormat ();
875 		}
876 
877 	fTable.Reset (host.Allocate (SafeUint32Mult (deltas,
878 		static_cast<uint32> (sizeof (real32)))));
879 
880 	real32 *table = fTable->Buffer_real32 ();
881 
882 	for (uint32 j = 0; j < deltas; j++)
883 		{
884 		table [j] = stream.Get_real32 ();
885 		}
886 
887 	#if qDNGValidate
888 
889 	if (gVerbose)
890 		{
891 
892 		printf ("Count: %u\n", (unsigned) deltas);
893 
894 		for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++)
895 			{
896 			printf ("    Delta [%u] = %f\n", (unsigned) k, table [k]);
897 			}
898 
899 		if (deltas > gDumpLineLimit)
900 			{
901 			printf ("    ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit));
902 			}
903 
904 		}
905 
906 	#endif
907 
908 	}
909 
910 /*****************************************************************************/
911 
PutData(dng_stream & stream) const912 void dng_opcode_DeltaPerRow::PutData (dng_stream &stream) const
913 	{
914 
915 	uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().H (),
916 										fAreaSpec.RowPitch ());
917 
918 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4);
919 
920 	fAreaSpec.PutData (stream);
921 
922 	stream.Put_uint32 (deltas);
923 
924 	real32 *table = fTable->Buffer_real32 ();
925 
926 	for (uint32 j = 0; j < deltas; j++)
927 		{
928 		stream.Put_real32 (table [j]);
929 		}
930 
931 	}
932 
933 /*****************************************************************************/
934 
BufferPixelType(uint32 imagePixelType)935 uint32 dng_opcode_DeltaPerRow::BufferPixelType (uint32 imagePixelType)
936 	{
937 
938 	real64 scale32 = 1.0;
939 
940 	switch (imagePixelType)
941 		{
942 
943 		case ttFloat:
944 			break;
945 
946 		case ttShort:
947 			{
948 			scale32 = (real64) 0xFFFF;
949 			break;
950 			}
951 
952 		case ttLong:
953 			{
954 			scale32 = (real64) 0xFFFFFFFF;
955 			break;
956 			}
957 
958 		default:
959 			ThrowBadFormat ();
960 
961 		}
962 
963 	fScale = (real32) (1.0 / scale32);
964 
965 	return ttFloat;
966 
967 	}
968 
969 /*****************************************************************************/
970 
ModifiedBounds(const dng_rect & imageBounds)971 dng_rect dng_opcode_DeltaPerRow::ModifiedBounds (const dng_rect &imageBounds)
972 	{
973 
974 	return fAreaSpec.Overlap (imageBounds);
975 
976 	}
977 
978 /*****************************************************************************/
979 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)980 void dng_opcode_DeltaPerRow::ProcessArea (dng_negative & /* negative */,
981 										  uint32 /* threadIndex */,
982 										  dng_pixel_buffer &buffer,
983 										  const dng_rect &dstArea,
984 										  const dng_rect & /* imageBounds */)
985 	{
986 
987 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
988 
989 	if (overlap.NotEmpty ())
990 		{
991 
992 		uint32 cols = overlap.W ();
993 
994 		uint32 colPitch = fAreaSpec.ColPitch ();
995 
996 		for (uint32 plane = fAreaSpec.Plane ();
997 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
998 			 plane < buffer.Planes ();
999 			 plane++)
1000 			{
1001 
1002 			const real32 *table = fTable->Buffer_real32 () +
1003 								  ((overlap.t - fAreaSpec.Area ().t) /
1004 								   fAreaSpec.RowPitch ());
1005 
1006 			for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ())
1007 				{
1008 
1009 				real32 rowDelta = *(table++) * fScale;
1010 
1011 				real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane);
1012 
1013 				for (uint32 col = 0; col < cols; col += colPitch)
1014 					{
1015 
1016 					real32 x = dPtr [col];
1017 
1018 					real32 y = x + rowDelta;
1019 
1020 					dPtr [col] = Pin_real32 (0.0f, y, 1.0f);
1021 
1022 					}
1023 
1024 				}
1025 
1026 			}
1027 
1028 		}
1029 
1030 	}
1031 
1032 /*****************************************************************************/
1033 
dng_opcode_DeltaPerColumn(const dng_area_spec & areaSpec,AutoPtr<dng_memory_block> & table)1034 dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (const dng_area_spec &areaSpec,
1035 												      AutoPtr<dng_memory_block> &table)
1036 
1037 	:	dng_inplace_opcode (dngOpcode_DeltaPerColumn,
1038 							dngVersion_1_3_0_0,
1039 							kFlag_None)
1040 
1041 	,	fAreaSpec (areaSpec)
1042 	,	fTable    ()
1043 	,	fScale    (1.0f)
1044 
1045 	{
1046 
1047 	fTable.Reset (table.Release ());
1048 
1049 	}
1050 
1051 /*****************************************************************************/
1052 
dng_opcode_DeltaPerColumn(dng_host & host,dng_stream & stream)1053 dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (dng_host &host,
1054 												      dng_stream &stream)
1055 
1056 	:	dng_inplace_opcode (dngOpcode_DeltaPerColumn,
1057 							stream,
1058 							"DeltaPerColumn")
1059 
1060 	,	fAreaSpec ()
1061 	,	fTable    ()
1062 	,	fScale    (1.0f)
1063 
1064 	{
1065 
1066 	uint32 dataSize = stream.Get_uint32 ();
1067 
1068 	fAreaSpec.GetData (stream);
1069 
1070 	uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().W (),
1071 										fAreaSpec.ColPitch ());
1072 
1073 	if (deltas != stream.Get_uint32 ())
1074 		{
1075 		ThrowBadFormat ();
1076 		}
1077 
1078 	if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4)
1079 		{
1080 		ThrowBadFormat ();
1081 		}
1082 
1083 	fTable.Reset (host.Allocate (SafeUint32Mult (deltas,
1084 		static_cast<uint32> (sizeof (real32)))));
1085 
1086 	real32 *table = fTable->Buffer_real32 ();
1087 
1088 	for (uint32 j = 0; j < deltas; j++)
1089 		{
1090 		table [j] = stream.Get_real32 ();
1091 		}
1092 
1093 	#if qDNGValidate
1094 
1095 	if (gVerbose)
1096 		{
1097 
1098 		printf ("Count: %u\n", (unsigned) deltas);
1099 
1100 		for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++)
1101 			{
1102 			printf ("    Delta [%u] = %f\n", (unsigned) k, table [k]);
1103 			}
1104 
1105 		if (deltas > gDumpLineLimit)
1106 			{
1107 			printf ("    ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit));
1108 			}
1109 
1110 		}
1111 
1112 	#endif
1113 
1114 	}
1115 
1116 /*****************************************************************************/
1117 
PutData(dng_stream & stream) const1118 void dng_opcode_DeltaPerColumn::PutData (dng_stream &stream) const
1119 	{
1120 
1121 	uint32 deltas = SafeUint32DivideUp (fAreaSpec.Area ().W (),
1122 										fAreaSpec.ColPitch ());
1123 
1124 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4);
1125 
1126 	fAreaSpec.PutData (stream);
1127 
1128 	stream.Put_uint32 (deltas);
1129 
1130 	real32 *table = fTable->Buffer_real32 ();
1131 
1132 	for (uint32 j = 0; j < deltas; j++)
1133 		{
1134 		stream.Put_real32 (table [j]);
1135 		}
1136 
1137 	}
1138 
1139 /*****************************************************************************/
1140 
BufferPixelType(uint32 imagePixelType)1141 uint32 dng_opcode_DeltaPerColumn::BufferPixelType (uint32 imagePixelType)
1142 	{
1143 
1144 	real64 scale32 = 1.0;
1145 
1146 	switch (imagePixelType)
1147 		{
1148 
1149 		case ttFloat:
1150 			break;
1151 
1152 		case ttShort:
1153 			{
1154 			scale32 = (real64) 0xFFFF;
1155 			break;
1156 			}
1157 
1158 		case ttLong:
1159 			{
1160 			scale32 = (real64) 0xFFFFFFFF;
1161 			break;
1162 			}
1163 
1164 		default:
1165 			ThrowBadFormat ();
1166 
1167 		}
1168 
1169 	fScale = (real32) (1.0 / scale32);
1170 
1171 	return ttFloat;
1172 
1173 	}
1174 
1175 /*****************************************************************************/
1176 
ModifiedBounds(const dng_rect & imageBounds)1177 dng_rect dng_opcode_DeltaPerColumn::ModifiedBounds (const dng_rect &imageBounds)
1178 	{
1179 
1180 	return fAreaSpec.Overlap (imageBounds);
1181 
1182 	}
1183 
1184 /*****************************************************************************/
1185 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)1186 void dng_opcode_DeltaPerColumn::ProcessArea (dng_negative & /* negative */,
1187 											 uint32 /* threadIndex */,
1188 											 dng_pixel_buffer &buffer,
1189 											 const dng_rect &dstArea,
1190 											 const dng_rect & /* imageBounds */)
1191 	{
1192 
1193 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
1194 
1195 	if (overlap.NotEmpty ())
1196 		{
1197 
1198 		uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) /
1199 					  fAreaSpec.RowPitch ();
1200 
1201 		int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch ();
1202 
1203 		for (uint32 plane = fAreaSpec.Plane ();
1204 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
1205 			 plane < buffer.Planes ();
1206 			 plane++)
1207 			{
1208 
1209 			const real32 *table = fTable->Buffer_real32 () +
1210 								  ((overlap.l - fAreaSpec.Area ().l) /
1211 								   fAreaSpec.ColPitch ());
1212 
1213 			for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ())
1214 				{
1215 
1216 				real32 colDelta = *(table++) * fScale;
1217 
1218 				real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane);
1219 
1220 				for (uint32 row = 0; row < rows; row++)
1221 					{
1222 
1223 					real32 x = dPtr [0];
1224 
1225 					real32 y = x + colDelta;
1226 
1227 					dPtr [0] = Pin_real32 (0.0f, y, 1.0f);
1228 
1229 					dPtr += rowStep;
1230 
1231 					}
1232 
1233 				}
1234 
1235 			}
1236 
1237 		}
1238 
1239 	}
1240 
1241 /*****************************************************************************/
1242 
dng_opcode_ScalePerRow(const dng_area_spec & areaSpec,AutoPtr<dng_memory_block> & table)1243 dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (const dng_area_spec &areaSpec,
1244 												AutoPtr<dng_memory_block> &table)
1245 
1246 	:	dng_inplace_opcode (dngOpcode_ScalePerRow,
1247 							dngVersion_1_3_0_0,
1248 							kFlag_None)
1249 
1250 	,	fAreaSpec (areaSpec)
1251 	,	fTable    ()
1252 
1253 	{
1254 
1255 	fTable.Reset (table.Release ());
1256 
1257 	}
1258 
1259 /*****************************************************************************/
1260 
dng_opcode_ScalePerRow(dng_host & host,dng_stream & stream)1261 dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (dng_host &host,
1262 												dng_stream &stream)
1263 
1264 	:	dng_inplace_opcode (dngOpcode_ScalePerRow,
1265 							stream,
1266 							"ScalePerRow")
1267 
1268 	,	fAreaSpec ()
1269 	,	fTable    ()
1270 
1271 	{
1272 
1273 	uint32 dataSize = stream.Get_uint32 ();
1274 
1275 	fAreaSpec.GetData (stream);
1276 
1277 	uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().H (),
1278 										fAreaSpec.RowPitch ());
1279 
1280 	if (scales != stream.Get_uint32 ())
1281 		{
1282 		ThrowBadFormat ();
1283 		}
1284 
1285 	if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4)
1286 		{
1287 		ThrowBadFormat ();
1288 		}
1289 
1290 	fTable.Reset (host.Allocate (SafeUint32Mult (scales,
1291 		static_cast<uint32> (sizeof (real32)))));
1292 
1293 	real32 *table = fTable->Buffer_real32 ();
1294 
1295 	for (uint32 j = 0; j < scales; j++)
1296 		{
1297 		table [j] = stream.Get_real32 ();
1298 		}
1299 
1300 	#if qDNGValidate
1301 
1302 	if (gVerbose)
1303 		{
1304 
1305 		printf ("Count: %u\n", (unsigned) scales);
1306 
1307 		for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++)
1308 			{
1309 			printf ("    Scale [%u] = %f\n", (unsigned) k, table [k]);
1310 			}
1311 
1312 		if (scales > gDumpLineLimit)
1313 			{
1314 			printf ("    ... %u scales skipped\n", (unsigned) (scales - gDumpLineLimit));
1315 			}
1316 
1317 		}
1318 
1319 	#endif
1320 
1321 	}
1322 
1323 /*****************************************************************************/
1324 
PutData(dng_stream & stream) const1325 void dng_opcode_ScalePerRow::PutData (dng_stream &stream) const
1326 	{
1327 
1328 	uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().H (),
1329 										fAreaSpec.RowPitch ());
1330 
1331 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4);
1332 
1333 	fAreaSpec.PutData (stream);
1334 
1335 	stream.Put_uint32 (scales);
1336 
1337 	real32 *table = fTable->Buffer_real32 ();
1338 
1339 	for (uint32 j = 0; j < scales; j++)
1340 		{
1341 		stream.Put_real32 (table [j]);
1342 		}
1343 
1344 	}
1345 
1346 /*****************************************************************************/
1347 
BufferPixelType(uint32)1348 uint32 dng_opcode_ScalePerRow::BufferPixelType (uint32 /* imagePixelType */)
1349 	{
1350 
1351 	return ttFloat;
1352 
1353 	}
1354 
1355 /*****************************************************************************/
1356 
ModifiedBounds(const dng_rect & imageBounds)1357 dng_rect dng_opcode_ScalePerRow::ModifiedBounds (const dng_rect &imageBounds)
1358 	{
1359 
1360 	return fAreaSpec.Overlap (imageBounds);
1361 
1362 	}
1363 
1364 /*****************************************************************************/
1365 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)1366 void dng_opcode_ScalePerRow::ProcessArea (dng_negative & /* negative */,
1367 										  uint32 /* threadIndex */,
1368 										  dng_pixel_buffer &buffer,
1369 										  const dng_rect &dstArea,
1370 										  const dng_rect & /* imageBounds */)
1371 	{
1372 
1373 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
1374 
1375 	if (overlap.NotEmpty ())
1376 		{
1377 
1378 		uint32 cols = overlap.W ();
1379 
1380 		uint32 colPitch = fAreaSpec.ColPitch ();
1381 
1382 		for (uint32 plane = fAreaSpec.Plane ();
1383 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
1384 			 plane < buffer.Planes ();
1385 			 plane++)
1386 			{
1387 
1388 			const real32 *table = fTable->Buffer_real32 () +
1389 								  ((overlap.t - fAreaSpec.Area ().t) /
1390 								   fAreaSpec.RowPitch ());
1391 
1392 			for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ())
1393 				{
1394 
1395 				real32 rowScale = *(table++);
1396 
1397 				real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane);
1398 
1399 				for (uint32 col = 0; col < cols; col += colPitch)
1400 					{
1401 
1402 					real32 x = dPtr [col];
1403 
1404 					real32 y = x * rowScale;
1405 
1406 					dPtr [col] = Min_real32 (y, 1.0f);
1407 
1408 					}
1409 
1410 				}
1411 
1412 			}
1413 
1414 		}
1415 
1416 	}
1417 
1418 /*****************************************************************************/
1419 
dng_opcode_ScalePerColumn(const dng_area_spec & areaSpec,AutoPtr<dng_memory_block> & table)1420 dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (const dng_area_spec &areaSpec,
1421 												      AutoPtr<dng_memory_block> &table)
1422 
1423 	:	dng_inplace_opcode (dngOpcode_ScalePerColumn,
1424 							dngVersion_1_3_0_0,
1425 							kFlag_None)
1426 
1427 	,	fAreaSpec (areaSpec)
1428 	,	fTable    ()
1429 
1430 	{
1431 
1432 	fTable.Reset (table.Release ());
1433 
1434 	}
1435 
1436 /*****************************************************************************/
1437 
dng_opcode_ScalePerColumn(dng_host & host,dng_stream & stream)1438 dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (dng_host &host,
1439 												      dng_stream &stream)
1440 
1441 	:	dng_inplace_opcode (dngOpcode_ScalePerColumn,
1442 							stream,
1443 							"ScalePerColumn")
1444 
1445 	,	fAreaSpec ()
1446 	,	fTable    ()
1447 
1448 	{
1449 
1450 	uint32 dataSize = stream.Get_uint32 ();
1451 
1452 	fAreaSpec.GetData (stream);
1453 
1454 	uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().W (),
1455 										fAreaSpec.ColPitch());
1456 
1457 	if (scales != stream.Get_uint32 ())
1458 		{
1459 		ThrowBadFormat ();
1460 		}
1461 
1462 	if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4)
1463 		{
1464 		ThrowBadFormat ();
1465 		}
1466 
1467 	fTable.Reset (host.Allocate (SafeUint32Mult (scales,
1468 		static_cast<uint32> (sizeof (real32)))));
1469 
1470 	real32 *table = fTable->Buffer_real32 ();
1471 
1472 	for (uint32 j = 0; j < scales; j++)
1473 		{
1474 		table [j] = stream.Get_real32 ();
1475 		}
1476 
1477 	#if qDNGValidate
1478 
1479 	if (gVerbose)
1480 		{
1481 
1482 		printf ("Count: %u\n", (unsigned) scales);
1483 
1484 		for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++)
1485 			{
1486 			printf ("    Scale [%u] = %f\n", (unsigned) k, table [k]);
1487 			}
1488 
1489 		if (scales > gDumpLineLimit)
1490 			{
1491 			printf ("    ... %u deltas skipped\n", (unsigned) (scales - gDumpLineLimit));
1492 			}
1493 
1494 		}
1495 
1496 	#endif
1497 
1498 	}
1499 
1500 /*****************************************************************************/
1501 
PutData(dng_stream & stream) const1502 void dng_opcode_ScalePerColumn::PutData (dng_stream &stream) const
1503 	{
1504 
1505 	uint32 scales = SafeUint32DivideUp (fAreaSpec.Area ().W (),
1506 										fAreaSpec.ColPitch ());
1507 
1508 	stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4);
1509 
1510 	fAreaSpec.PutData (stream);
1511 
1512 	stream.Put_uint32 (scales);
1513 
1514 	real32 *table = fTable->Buffer_real32 ();
1515 
1516 	for (uint32 j = 0; j < scales; j++)
1517 		{
1518 		stream.Put_real32 (table [j]);
1519 		}
1520 
1521 	}
1522 
1523 /*****************************************************************************/
1524 
BufferPixelType(uint32)1525 uint32 dng_opcode_ScalePerColumn::BufferPixelType (uint32 /* imagePixelType */)
1526 	{
1527 
1528 	return ttFloat;
1529 
1530 	}
1531 
1532 /*****************************************************************************/
1533 
ModifiedBounds(const dng_rect & imageBounds)1534 dng_rect dng_opcode_ScalePerColumn::ModifiedBounds (const dng_rect &imageBounds)
1535 	{
1536 
1537 	return fAreaSpec.Overlap (imageBounds);
1538 
1539 	}
1540 
1541 /*****************************************************************************/
1542 
ProcessArea(dng_negative &,uint32,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)1543 void dng_opcode_ScalePerColumn::ProcessArea (dng_negative & /* negative */,
1544 											 uint32 /* threadIndex */,
1545 											 dng_pixel_buffer &buffer,
1546 											 const dng_rect &dstArea,
1547 											 const dng_rect & /* imageBounds */)
1548 	{
1549 
1550 	dng_rect overlap = fAreaSpec.Overlap (dstArea);
1551 
1552 	if (overlap.NotEmpty ())
1553 		{
1554 
1555 		uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) /
1556 					  fAreaSpec.RowPitch ();
1557 
1558 		int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch ();
1559 
1560 		for (uint32 plane = fAreaSpec.Plane ();
1561 			 plane < fAreaSpec.Plane () + fAreaSpec.Planes () &&
1562 			 plane < buffer.Planes ();
1563 			 plane++)
1564 			{
1565 
1566 			const real32 *table = fTable->Buffer_real32 () +
1567 								  ((overlap.l - fAreaSpec.Area ().l) /
1568 								   fAreaSpec.ColPitch ());
1569 
1570 			for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ())
1571 				{
1572 
1573 				real32 colScale = *(table++);
1574 
1575 				real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane);
1576 
1577 				for (uint32 row = 0; row < rows; row++)
1578 					{
1579 
1580 					real32 x = dPtr [0];
1581 
1582 					real32 y = x * colScale;
1583 
1584 					dPtr [0] = Min_real32 (y, 1.0f);
1585 
1586 					dPtr += rowStep;
1587 
1588 					}
1589 
1590 				}
1591 
1592 			}
1593 
1594 		}
1595 
1596 	}
1597 
1598 /*****************************************************************************/
1599