File indexing completed on 2025-01-19 03:55:11

0001 /*****************************************************************************/
0002 // Copyright 2007-2019 Adobe Systems Incorporated
0003 // All Rights Reserved.
0004 //
0005 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
0006 // accordance with the terms of the Adobe license agreement accompanying it.
0007 /*****************************************************************************/
0008 
0009 #include "dng_preview.h"
0010 
0011 #include "dng_assertions.h"
0012 #include "dng_image.h"
0013 #include "dng_image_writer.h"
0014 #include "dng_memory.h"
0015 #include "dng_stream.h"
0016 #include "dng_tag_codes.h"
0017 #include "dng_tag_values.h"
0018 
0019 /*****************************************************************************/
0020 
0021 class dng_preview_tag_set: public dng_basic_tag_set
0022     {
0023 
0024     private:
0025 
0026         tag_string fApplicationNameTag;
0027 
0028         tag_string fApplicationVersionTag;
0029 
0030         tag_string fSettingsNameTag;
0031 
0032         dng_fingerprint fSettingsDigest;
0033 
0034         tag_uint8_ptr fSettingsDigestTag;
0035 
0036         tag_uint32 fColorSpaceTag;
0037 
0038         tag_string fDateTimeTag;
0039 
0040         tag_real64 fRawToPreviewGainTag;
0041 
0042         tag_uint32 fCacheVersionTag;
0043 
0044     public:
0045 
0046         dng_preview_tag_set (dng_tiff_directory &directory,
0047                              const dng_preview &preview,
0048                              const dng_ifd &ifd);
0049 
0050         virtual ~dng_preview_tag_set ();
0051 
0052     };
0053 
0054 /*****************************************************************************/
0055 
0056 dng_preview_tag_set::dng_preview_tag_set (dng_tiff_directory &directory,
0057                                           const dng_preview &preview,
0058                                           const dng_ifd &ifd)
0059 
0060     :   dng_basic_tag_set (directory, ifd)
0061 
0062     ,   fApplicationNameTag (tcPreviewApplicationName,
0063                              preview.fInfo.fApplicationName,
0064                              false)
0065 
0066     ,   fApplicationVersionTag (tcPreviewApplicationVersion,
0067                                 preview.fInfo.fApplicationVersion,
0068                                 false)
0069 
0070     ,   fSettingsNameTag (tcPreviewSettingsName,
0071                           preview.fInfo.fSettingsName,
0072                           false)
0073 
0074     ,   fSettingsDigest (preview.fInfo.fSettingsDigest)
0075 
0076     ,   fSettingsDigestTag (tcPreviewSettingsDigest,
0077                             fSettingsDigest.data,
0078                             16)
0079 
0080     ,   fColorSpaceTag (tcPreviewColorSpace,
0081                         preview.fInfo.fColorSpace)
0082 
0083     ,   fDateTimeTag (tcPreviewDateTime,
0084                       preview.fInfo.fDateTime,
0085                       true)
0086 
0087     ,   fRawToPreviewGainTag (tcRawToPreviewGain,
0088                               preview.fInfo.fRawToPreviewGain)
0089 
0090     ,   fCacheVersionTag (tcCacheVersion,
0091                           preview.fInfo.fCacheVersion)
0092 
0093     {
0094 
0095     if (preview.fInfo.fApplicationName.NotEmpty ())
0096         {
0097 
0098         directory.Add (&fApplicationNameTag);
0099 
0100         }
0101 
0102     if (preview.fInfo.fApplicationVersion.NotEmpty ())
0103         {
0104 
0105         directory.Add (&fApplicationVersionTag);
0106 
0107         }
0108 
0109     if (preview.fInfo.fSettingsName.NotEmpty ())
0110         {
0111 
0112         directory.Add (&fSettingsNameTag);
0113 
0114         }
0115 
0116     if (preview.fInfo.fSettingsDigest.IsValid ())
0117         {
0118 
0119         directory.Add (&fSettingsDigestTag);
0120 
0121         }
0122 
0123     if (preview.fInfo.fColorSpace != previewColorSpace_MaxEnum)
0124         {
0125 
0126         directory.Add (&fColorSpaceTag);
0127 
0128         }
0129 
0130     if (preview.fInfo.fDateTime.NotEmpty ())
0131         {
0132 
0133         directory.Add (&fDateTimeTag);
0134 
0135         }
0136 
0137     if (preview.fInfo.fRawToPreviewGain != 1.0)
0138         {
0139 
0140         directory.Add (&fRawToPreviewGainTag);
0141 
0142         }
0143 
0144     if (preview.fInfo.fCacheVersion != 0)
0145         {
0146 
0147         directory.Add (&fCacheVersionTag);
0148 
0149         }
0150 
0151     }
0152 
0153 /*****************************************************************************/
0154 
0155 dng_preview_tag_set::~dng_preview_tag_set ()
0156     {
0157 
0158     }
0159 
0160 /*****************************************************************************/
0161 
0162 dng_preview::dng_preview ()
0163 
0164     :   fInfo ()
0165 
0166     {
0167 
0168     }
0169 
0170 /*****************************************************************************/
0171 
0172 dng_preview::~dng_preview ()
0173     {
0174 
0175     }
0176 
0177 /*****************************************************************************/
0178 
0179 dng_image_preview::dng_image_preview ()
0180 
0181     :   fImage ()
0182     ,   fIFD   ()
0183 
0184     {
0185 
0186     }
0187 
0188 /*****************************************************************************/
0189 
0190 dng_image_preview::~dng_image_preview ()
0191     {
0192 
0193     }
0194 
0195 /*****************************************************************************/
0196 
0197 dng_basic_tag_set * dng_image_preview::AddTagSet (dng_tiff_directory &directory) const
0198     {
0199 
0200     fIFD.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage
0201                                             : sfAltPreviewImage;
0202 
0203     fIFD.fImageWidth  = fImage->Width  ();
0204     fIFD.fImageLength = fImage->Height ();
0205 
0206     fIFD.fSamplesPerPixel = fImage->Planes ();
0207 
0208     fIFD.fPhotometricInterpretation = fIFD.fSamplesPerPixel == 1 ? piBlackIsZero
0209                                                                  : piRGB;
0210 
0211     fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
0212 
0213     for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++)
0214         {
0215         fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0];
0216         }
0217 
0218     fIFD.SetSingleStrip ();
0219 
0220     return new dng_preview_tag_set (directory, *this, fIFD);
0221 
0222     }
0223 
0224 /*****************************************************************************/
0225 
0226 void dng_image_preview::WriteData (dng_host &host,
0227                                    dng_image_writer &writer,
0228                                    dng_basic_tag_set &basic,
0229                                    dng_stream &stream) const
0230     {
0231 
0232     writer.WriteImage (host,
0233                        fIFD,
0234                        basic,
0235                        stream,
0236                        *fImage.Get ());
0237 
0238     }
0239 
0240 /*****************************************************************************/
0241 
0242 class dng_jpeg_preview_tag_set: public dng_preview_tag_set
0243     {
0244 
0245     private:
0246 
0247         dng_urational fCoefficientsData [3];
0248 
0249         tag_urational_ptr fCoefficientsTag;
0250 
0251         uint16 fSubSamplingData [2];
0252 
0253         tag_uint16_ptr fSubSamplingTag;
0254 
0255         tag_uint16 fPositioningTag;
0256 
0257         dng_urational fReferenceData [6];
0258 
0259         tag_urational_ptr fReferenceTag;
0260 
0261     public:
0262 
0263         dng_jpeg_preview_tag_set (dng_tiff_directory &directory,
0264                                   const dng_jpeg_preview &preview,
0265                                   const dng_ifd &ifd);
0266 
0267         virtual ~dng_jpeg_preview_tag_set ();
0268 
0269     };
0270 
0271 /******************************************************************************/
0272 
0273 dng_jpeg_preview_tag_set::dng_jpeg_preview_tag_set (dng_tiff_directory &directory,
0274                                                     const dng_jpeg_preview &preview,
0275                                                     const dng_ifd &ifd)
0276 
0277     :   dng_preview_tag_set (directory, preview, ifd)
0278 
0279     ,   fCoefficientsTag (tcYCbCrCoefficients, fCoefficientsData, 3)
0280 
0281     ,   fSubSamplingTag (tcYCbCrSubSampling, fSubSamplingData, 2)
0282 
0283     ,   fPositioningTag (tcYCbCrPositioning, preview.fYCbCrPositioning)
0284 
0285     ,   fReferenceTag (tcReferenceBlackWhite, fReferenceData, 6)
0286 
0287     {
0288 
0289     if (preview.fPhotometricInterpretation == piYCbCr)
0290         {
0291 
0292         fCoefficientsData [0] = dng_urational (299, 1000);
0293         fCoefficientsData [1] = dng_urational (587, 1000);
0294         fCoefficientsData [2] = dng_urational (114, 1000);
0295 
0296         directory.Add (&fCoefficientsTag);
0297 
0298         fSubSamplingData [0] = (uint16) preview.fYCbCrSubSampling.h;
0299         fSubSamplingData [1] = (uint16) preview.fYCbCrSubSampling.v;
0300 
0301         directory.Add (&fSubSamplingTag);
0302 
0303         directory.Add (&fPositioningTag);
0304 
0305         fReferenceData [0] = dng_urational (  0, 1);
0306         fReferenceData [1] = dng_urational (255, 1);
0307         fReferenceData [2] = dng_urational (128, 1);
0308         fReferenceData [3] = dng_urational (255, 1);
0309         fReferenceData [4] = dng_urational (128, 1);
0310         fReferenceData [5] = dng_urational (255, 1);
0311 
0312         directory.Add (&fReferenceTag);
0313 
0314         }
0315 
0316     }
0317 
0318 /*****************************************************************************/
0319 
0320 dng_jpeg_preview_tag_set::~dng_jpeg_preview_tag_set ()
0321     {
0322 
0323     }
0324 
0325 /*****************************************************************************/
0326 
0327 dng_jpeg_preview::dng_jpeg_preview ()
0328 
0329     :   fPreviewSize               ()
0330     ,   fPhotometricInterpretation (piYCbCr)
0331     ,   fYCbCrSubSampling          (1, 1)
0332     ,   fYCbCrPositioning          (2)
0333     ,   fCompressedData            ()
0334 
0335     {
0336 
0337     }
0338 
0339 /*****************************************************************************/
0340 
0341 dng_jpeg_preview::~dng_jpeg_preview ()
0342     {
0343 
0344     }
0345 
0346 /*****************************************************************************/
0347 
0348 dng_basic_tag_set * dng_jpeg_preview::AddTagSet (dng_tiff_directory &directory) const
0349     {
0350 
0351     dng_ifd ifd;
0352 
0353     ifd.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage
0354                                            : sfAltPreviewImage;
0355 
0356     ifd.fImageWidth  = fPreviewSize.h;
0357     ifd.fImageLength = fPreviewSize.v;
0358 
0359     ifd.fPhotometricInterpretation = fPhotometricInterpretation;
0360 
0361     ifd.fBitsPerSample [0] = 8;
0362     ifd.fBitsPerSample [1] = 8;
0363     ifd.fBitsPerSample [2] = 8;
0364 
0365     ifd.fSamplesPerPixel = (fPhotometricInterpretation == piBlackIsZero ? 1 : 3);
0366 
0367     ifd.fCompression = ccJPEG;
0368     ifd.fPredictor   = cpNullPredictor;
0369 
0370     ifd.SetSingleStrip ();
0371 
0372     return new dng_jpeg_preview_tag_set (directory, *this, ifd);
0373 
0374     }
0375 
0376 /*****************************************************************************/
0377 
0378 void dng_jpeg_preview::WriteData (dng_host & /* host */,
0379                                   dng_image_writer & /* writer */,
0380                                   dng_basic_tag_set &basic,
0381                                   dng_stream &stream) const
0382     {
0383 
0384     basic.SetTileOffset (0, (uint32) stream.Position ());
0385 
0386     basic.SetTileByteCount (0, fCompressedData->LogicalSize ());
0387 
0388     stream.Put (fCompressedData->Buffer      (),
0389                 fCompressedData->LogicalSize ());
0390 
0391     if (fCompressedData->LogicalSize () & 1)
0392         {
0393         stream.Put_uint8 (0);
0394         }
0395 
0396     }
0397 
0398 /*****************************************************************************/
0399 
0400 void dng_jpeg_preview::SpoolAdobeThumbnail (dng_stream &stream) const
0401     {
0402 
0403     DNG_ASSERT (fCompressedData.Get (),
0404                 "SpoolAdobeThumbnail: no data");
0405 
0406     DNG_ASSERT (fPhotometricInterpretation == piYCbCr,
0407                 "SpoolAdobeThumbnail: Non-YCbCr");
0408 
0409     uint32 compressedSize = fCompressedData->LogicalSize ();
0410 
0411     stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M'));
0412     stream.Put_uint16 (1036);
0413     stream.Put_uint16 (0);
0414 
0415     stream.Put_uint32 (compressedSize + 28);
0416 
0417     uint32 widthBytes = (fPreviewSize.h * 24 + 31) / 32 * 4;
0418 
0419     stream.Put_uint32 (1);
0420     stream.Put_uint32 (fPreviewSize.h);
0421     stream.Put_uint32 (fPreviewSize.v);
0422     stream.Put_uint32 (widthBytes);
0423     stream.Put_uint32 (widthBytes * fPreviewSize.v);
0424     stream.Put_uint32 (compressedSize);
0425     stream.Put_uint16 (24);
0426     stream.Put_uint16 (1);
0427 
0428     stream.Put (fCompressedData->Buffer (),
0429                 compressedSize);
0430 
0431     if (compressedSize & 1)
0432         {
0433         stream.Put_uint8 (0);
0434         }
0435 
0436     }
0437 
0438 /*****************************************************************************/
0439 
0440 class dng_raw_preview_tag_set: public dng_preview_tag_set
0441     {
0442 
0443     private:
0444 
0445         tag_data_ptr fOpcodeList2Tag;
0446 
0447         tag_uint32_ptr fWhiteLevelTag;
0448 
0449         uint32 fWhiteLevelData [kMaxColorPlanes];
0450 
0451         tag_urational_ptr fBlackLevelTag;
0452 
0453         dng_urational fBlackLevelData [kMaxColorPlanes];
0454 
0455     public:
0456 
0457         dng_raw_preview_tag_set (dng_tiff_directory &directory,
0458                                  const dng_raw_preview &preview,
0459                                  const dng_ifd &ifd);
0460 
0461         virtual ~dng_raw_preview_tag_set ();
0462 
0463     };
0464 
0465 /*****************************************************************************/
0466 
0467 dng_raw_preview_tag_set::dng_raw_preview_tag_set (dng_tiff_directory &directory,
0468                                                   const dng_raw_preview &preview,
0469                                                   const dng_ifd &ifd)
0470 
0471     :   dng_preview_tag_set (directory, preview, ifd)
0472 
0473     ,   fOpcodeList2Tag (tcOpcodeList2,
0474                          ttUndefined,
0475                          0,
0476                          NULL)
0477 
0478     ,   fWhiteLevelTag (tcWhiteLevel,
0479                         fWhiteLevelData,
0480                         preview.fImage->Planes ())
0481 
0482     ,   fBlackLevelTag (tcBlackLevel,
0483                         fBlackLevelData,
0484                         preview.fImage->Planes ())
0485 
0486     {
0487 
0488     if (preview.fOpcodeList2Data.Get ())
0489         {
0490 
0491         fOpcodeList2Tag.SetData  (preview.fOpcodeList2Data->Buffer      ());
0492         fOpcodeList2Tag.SetCount (preview.fOpcodeList2Data->LogicalSize ());
0493 
0494         directory.Add (&fOpcodeList2Tag);
0495 
0496         }
0497 
0498     if (preview.fImage->PixelType () == ttFloat)
0499         {
0500 
0501         for (uint32 j = 0; j < kMaxColorPlanes; j++)
0502             {
0503             fWhiteLevelData [j] = 32768;
0504             }
0505 
0506         directory.Add (&fWhiteLevelTag);
0507 
0508         }
0509 
0510     else
0511         {
0512 
0513         bool nonZeroBlack = false;
0514 
0515         for (uint32 j = 0; j < preview.fImage->Planes (); j++)
0516             {
0517 
0518             fBlackLevelData [j].Set_real64 (preview.fBlackLevel [j], 1);
0519 
0520             nonZeroBlack = nonZeroBlack || (preview.fBlackLevel [j] != 0.0);
0521 
0522             }
0523 
0524         if (nonZeroBlack)
0525             {
0526 
0527             directory.Add (&fBlackLevelTag);
0528 
0529             }
0530 
0531         }
0532 
0533     }
0534 
0535 /*****************************************************************************/
0536 
0537 dng_raw_preview_tag_set::~dng_raw_preview_tag_set ()
0538     {
0539 
0540     }
0541 
0542 /*****************************************************************************/
0543 
0544 dng_raw_preview::dng_raw_preview ()
0545 
0546     :   fImage              ()
0547     ,   fOpcodeList2Data    ()
0548     ,   fCompressionQuality (-1)
0549     ,   fIFD                ()
0550 
0551     {
0552 
0553     for (uint32 n = 0; n < kMaxSamplesPerPixel; n++)
0554         {
0555         fBlackLevel [n] = 0.0;
0556         }
0557 
0558     }
0559 
0560 /*****************************************************************************/
0561 
0562 dng_raw_preview::~dng_raw_preview ()
0563     {
0564 
0565     }
0566 
0567 /*****************************************************************************/
0568 
0569 dng_basic_tag_set * dng_raw_preview::AddTagSet (dng_tiff_directory &directory) const
0570     {
0571 
0572     fIFD.fNewSubFileType = sfPreviewImage;
0573 
0574     fIFD.fImageWidth  = fImage->Width  ();
0575     fIFD.fImageLength = fImage->Height ();
0576 
0577     fIFD.fSamplesPerPixel = fImage->Planes ();
0578 
0579     fIFD.fPhotometricInterpretation = piLinearRaw;
0580 
0581     if (fImage->PixelType () == ttFloat)
0582         {
0583 
0584         fIFD.fCompression = ccDeflate;
0585 
0586         fIFD.fCompressionQuality = fCompressionQuality;
0587 
0588         fIFD.fPredictor = cpFloatingPoint;
0589 
0590         for (uint32 j = 0; j < fIFD.fSamplesPerPixel; j++)
0591             {
0592             fIFD.fBitsPerSample [j] = 16;
0593             fIFD.fSampleFormat  [j] = sfFloatingPoint;
0594             }
0595 
0596         fIFD.FindTileSize (512 * 1024);
0597 
0598         }
0599 
0600     else
0601         {
0602 
0603         fIFD.fCompression = ccLossyJPEG;
0604 
0605         fIFD.fCompressionQuality = fCompressionQuality;
0606 
0607         fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
0608 
0609         for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++)
0610             {
0611             fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0];
0612             }
0613 
0614         fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel);
0615 
0616         }
0617 
0618     return new dng_raw_preview_tag_set (directory, *this, fIFD);
0619 
0620     }
0621 
0622 /*****************************************************************************/
0623 
0624 void dng_raw_preview::WriteData (dng_host &host,
0625                                  dng_image_writer &writer,
0626                                  dng_basic_tag_set &basic,
0627                                  dng_stream &stream) const
0628     {
0629 
0630     writer.WriteImage (host,
0631                        fIFD,
0632                        basic,
0633                        stream,
0634                        *fImage.Get ());
0635 
0636     }
0637 
0638 /*****************************************************************************/
0639 
0640 dng_mask_preview::dng_mask_preview ()
0641 
0642     :   fImage              ()
0643     ,   fCompressionQuality (-1)
0644     ,   fIFD                ()
0645 
0646     {
0647 
0648     }
0649 
0650 /*****************************************************************************/
0651 
0652 dng_mask_preview::~dng_mask_preview ()
0653     {
0654 
0655     }
0656 
0657 /*****************************************************************************/
0658 
0659 dng_basic_tag_set * dng_mask_preview::AddTagSet (dng_tiff_directory &directory) const
0660     {
0661 
0662     fIFD.fNewSubFileType = sfPreviewMask;
0663 
0664     fIFD.fImageWidth  = fImage->Width  ();
0665     fIFD.fImageLength = fImage->Height ();
0666 
0667     fIFD.fSamplesPerPixel = 1;
0668 
0669     fIFD.fPhotometricInterpretation = piTransparencyMask;
0670 
0671     fIFD.fCompression = ccDeflate;
0672     fIFD.fPredictor   = cpHorizontalDifference;
0673 
0674     fIFD.fCompressionQuality = fCompressionQuality;
0675 
0676     fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
0677 
0678     fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel);
0679 
0680     return new dng_basic_tag_set (directory, fIFD);
0681 
0682     }
0683 
0684 /*****************************************************************************/
0685 
0686 void dng_mask_preview::WriteData (dng_host &host,
0687                                   dng_image_writer &writer,
0688                                   dng_basic_tag_set &basic,
0689                                   dng_stream &stream) const
0690     {
0691 
0692     writer.WriteImage (host,
0693                        fIFD,
0694                        basic,
0695                        stream,
0696                        *fImage.Get ());
0697 
0698     }
0699 
0700 /*****************************************************************************/
0701 
0702 dng_depth_preview::dng_depth_preview ()
0703 
0704     :    fImage              ()
0705     ,    fCompressionQuality (-1)
0706     ,    fFullResolution     (false)
0707     ,    fIFD                ()
0708 
0709     {
0710 
0711     }
0712 
0713 /*****************************************************************************/
0714 
0715 dng_depth_preview::~dng_depth_preview ()
0716     {
0717 
0718     }
0719 
0720 /*****************************************************************************/
0721 
0722 dng_basic_tag_set * dng_depth_preview::AddTagSet (dng_tiff_directory &directory) const
0723     {
0724 
0725     fIFD.fNewSubFileType = fFullResolution ? sfDepthMap
0726                                            : sfPreviewDepthMap;
0727 
0728     fIFD.fImageWidth  = fImage->Width  ();
0729     fIFD.fImageLength = fImage->Height ();
0730 
0731     fIFD.fSamplesPerPixel = 1;
0732 
0733     fIFD.fPhotometricInterpretation = piDepth;
0734 
0735     fIFD.fCompression = ccDeflate;
0736     fIFD.fPredictor   = cpHorizontalDifference;
0737 
0738     fIFD.fCompressionQuality = fCompressionQuality;
0739 
0740     fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8;
0741 
0742     fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel);
0743 
0744     return new dng_basic_tag_set (directory, fIFD);
0745 
0746     }
0747 
0748 /*****************************************************************************/
0749 
0750 void dng_depth_preview::WriteData (dng_host &host,
0751                                    dng_image_writer &writer,
0752                                    dng_basic_tag_set &basic,
0753                                    dng_stream &stream) const
0754     {
0755 
0756     writer.WriteImage (host,
0757                        fIFD,
0758                        basic,
0759                        stream,
0760                        *fImage.Get ());
0761 
0762     }
0763 
0764 /*****************************************************************************/
0765 
0766 dng_preview_list::dng_preview_list ()
0767 
0768     :   fCount (0)
0769 
0770     {
0771 
0772     }
0773 
0774 /*****************************************************************************/
0775 
0776 dng_preview_list::~dng_preview_list ()
0777     {
0778 
0779     }
0780 
0781 /*****************************************************************************/
0782 
0783 void dng_preview_list::Append (AutoPtr<dng_preview> &preview)
0784     {
0785 
0786     if (preview.Get ())
0787         {
0788 
0789         DNG_ASSERT (fCount < kMaxDNGPreviews, "DNG preview list overflow");
0790 
0791         if (fCount < kMaxDNGPreviews)
0792             {
0793 
0794             fPreview [fCount++] . Reset (preview.Release ());
0795 
0796             }
0797 
0798         }
0799 
0800     }
0801 
0802 /*****************************************************************************/