1 //     ____   ______ __
2 //    / __ \ / ____// /
3 //   / /_/ // /    / /
4 //  / ____// /___ / /___   PixInsight Class Library
5 // /_/     \____//_____/   PCL 2.1.19
6 // ----------------------------------------------------------------------------
7 // Standard RAW_compat File Format Module Version 1.5.3
8 // ----------------------------------------------------------------------------
9 // RawInstance.cpp - Released 2020-01-14T11:57:23Z
10 // ----------------------------------------------------------------------------
11 // This file is part of the standard RAW_compat PixInsight module.
12 //
13 // Copyright (c) 2003-2020 Pleiades Astrophoto S.L. All Rights Reserved.
14 //
15 // Redistribution and use in both source and binary forms, with or without
16 // modification, is permitted provided that the following conditions are met:
17 //
18 // 1. All redistributions of source code must retain the above copyright
19 //    notice, this list of conditions and the following disclaimer.
20 //
21 // 2. All redistributions in binary form must reproduce the above copyright
22 //    notice, this list of conditions and the following disclaimer in the
23 //    documentation and/or other materials provided with the distribution.
24 //
25 // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
26 //    of their contributors, may be used to endorse or promote products derived
27 //    from this software without specific prior written permission. For written
28 //    permission, please contact info@pixinsight.com.
29 //
30 // 4. All products derived from this software, in any form whatsoever, must
31 //    reproduce the following acknowledgment in the end-user documentation
32 //    and/or other materials provided with the product:
33 //
34 //    "This product is based on software from the PixInsight project, developed
35 //    by Pleiades Astrophoto and its contributors (http://pixinsight.com/)."
36 //
37 //    Alternatively, if that is where third-party acknowledgments normally
38 //    appear, this acknowledgment must be reproduced in the product itself.
39 //
40 // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
41 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
44 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45 // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
46 // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
47 // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
50 // POSSIBILITY OF SUCH DAMAGE.
51 // ----------------------------------------------------------------------------
52 
53 #include <libraw-compat/libraw.h>
54 
55 #include "RawInstance.h"
56 #include "RawFormat.h"
57 #include "RawModule.h"
58 
59 #include <pcl/FastRotation.h>
60 #include <pcl/FITSHeaderKeyword.h>
61 #include <pcl/StandardStatus.h>
62 #include <pcl/TimePoint.h>
63 #include <pcl/Version.h>
64 
65 namespace pcl
66 {
67 
68 // ----------------------------------------------------------------------------
69 
70 class RawReadHints
71 {
72 public:
73 
74    RawPreferences preferences;
75    int            verbosity = RAW_DEFAULT_VERBOSITY;
76 
RawReadHints(const IsoString & hints)77    RawReadHints( const IsoString& hints )
78    {
79       preferences.LoadFromSettings();
80 
81       IsoStringList theHints;
82       hints.Break( theHints, ' ', true/*trim*/ );
83       theHints.Remove( IsoString() );
84       bool preview = false;
85       for ( IsoStringList::const_iterator token = theHints.Begin(); token != theHints.End(); ++token )
86       {
87          if ( *token == "raw" )
88          {
89             preferences.interpolation = RawPreferences::Bilinear;
90             preferences.interpolateAs4Colors = false;
91             preferences.useAutoWhiteBalance = false;
92             preferences.useCameraWhiteBalance = false;
93             preferences.noWhiteBalance = true;
94             preferences.createSuperPixels = false;
95             preferences.outputRawRGB = true;
96             preferences.outputCFA = false;
97             preferences.noBlackPointCorrection = true;
98             preferences.noAutoFlip = true;
99             preferences.noAutoCrop = false;
100             preferences.noClipHighlights = true;
101             preferences.noiseThreshold = 0;
102             preview = false;
103          }
104          else if ( *token == "preview" )
105          {
106             preferences.interpolation = RawPreferences::HalfSize;
107             preferences.interpolateAs4Colors = false;
108             preferences.useAutoWhiteBalance = false;
109             preferences.useCameraWhiteBalance = true;
110             preferences.noWhiteBalance = false;
111             preferences.createSuperPixels = false;
112             preferences.outputRawRGB = false;
113             preferences.outputCFA = false;
114             preferences.noBlackPointCorrection = false;
115             preferences.noAutoFlip = false;
116             preferences.noAutoCrop = false;
117             preferences.noClipHighlights = false;
118             preferences.noiseThreshold = 0;
119             preview = true;
120          }
121          else if ( *token == "rgb" )
122          {
123             preferences.interpolation = RawPreferences::VNG;
124             preferences.interpolateAs4Colors = false;
125             preferences.useAutoWhiteBalance = false;
126             preferences.useCameraWhiteBalance = true;
127             preferences.noWhiteBalance = false;
128             preferences.createSuperPixels = false;
129             preferences.outputRawRGB = false;
130             preferences.outputCFA = false;
131             preferences.noBlackPointCorrection = false;
132             preferences.noAutoFlip = false;
133             preferences.noAutoCrop = false;
134             preferences.noClipHighlights = false;
135             preferences.noiseThreshold = 0;
136             preview = false;
137          }
138 
139          // 'fast' must be interpreted differently if the 'preview' hint has
140          // already been specified --- otherwise we wouldn't be 'fast' at all!
141          else if ( *token == "fast" )
142             preferences.interpolation = preview ? RawPreferences::HalfSize : RawPreferences::Bilinear;
143 
144          else if ( *token == "bilinear" )
145             preferences.interpolation = RawPreferences::Bilinear;
146          else if ( *token == "vng" )
147             preferences.interpolation = RawPreferences::VNG;
148          else if ( *token == "ppg" )
149             preferences.interpolation = RawPreferences::PPG;
150          else if ( *token == "ahd" )
151             preferences.interpolation = RawPreferences::AHD;
152          else if ( *token == "aahd" )
153             preferences.interpolation = RawPreferences::AAHD;
154          else if ( *token == "dht" )
155             preferences.interpolation = RawPreferences::DHT;
156          else if ( *token == "dcb" )
157             preferences.interpolation = RawPreferences::DCB;
158 
159          else if ( *token == "dcb-iterations" )
160          {
161             if ( ++token == theHints.End() )
162                break;
163             int n;
164             if ( token->TryToInt( n ) )
165                preferences.dcbIterations = Range( n, 1, 5 );
166          }
167 
168          else if ( *token == "dcb-refinement" )
169             preferences.dcbRefinement = true;
170          else if ( *token == "no-dcb-refinement" )
171             preferences.dcbRefinement = false;
172 
173          else if ( *token == "half-size" )
174             preferences.interpolation = RawPreferences::HalfSize;
175 
176          else if ( *token == "interpolate-as-4-colors" )
177             preferences.interpolateAs4Colors = true;
178          else if ( *token == "no-interpolate-as-4-colors" )
179             preferences.interpolateAs4Colors = false;
180 
181          else if ( *token == "auto-white-balance" )
182          {
183             preferences.useAutoWhiteBalance = true;
184             preferences.noWhiteBalance = false;
185          }
186          else if ( *token == "no-auto-white-balance" )
187             preferences.useAutoWhiteBalance = false;
188 
189          else if ( *token == "camera-white-balance" )
190          {
191             preferences.useCameraWhiteBalance = true;
192             preferences.noWhiteBalance = false;
193          }
194          else if ( *token == "no-camera-white-balance" )
195             preferences.useCameraWhiteBalance = false;
196 
197          else if ( *token == "no-white-balance" )
198             preferences.noWhiteBalance = true;
199          else if ( *token == "daylight-white-balance" )
200          {
201             preferences.useAutoWhiteBalance = false;
202             preferences.useCameraWhiteBalance = false;
203             preferences.noWhiteBalance = false;
204          }
205 
206          else if ( *token == "super-pixels" )
207          {
208             preferences.createSuperPixels = true;
209             preferences.outputRawRGB = false;
210             preferences.outputCFA = false;
211          }
212          else if ( *token == "no-super-pixels" )
213             preferences.createSuperPixels = false;
214 
215          else if ( *token == "bayer-drizzle" )
216          {
217             preferences.outputRawRGB = true;
218             preferences.outputCFA = false;
219             preferences.createSuperPixels = false;
220          }
221          else if ( *token == "no-bayer-drizzle" )
222             preferences.outputRawRGB = false;
223 
224          else if ( *token == "cfa" )
225          {
226             preferences.outputCFA = true;
227             preferences.outputRawRGB = false;
228             preferences.createSuperPixels = false;
229          }
230          else if ( *token == "no-cfa" )
231             preferences.outputCFA = false;
232 
233          else if ( *token == "auto-flip" )
234             preferences.noAutoFlip = false;
235          else if ( *token == "no-auto-flip" )
236             preferences.noAutoFlip = true;
237 
238          else if ( *token == "auto-crop" )
239             preferences.noAutoCrop = false;
240          else if ( *token == "no-auto-crop" )
241             preferences.noAutoCrop = true;
242 
243          else if ( *token == "black-point-correction" )
244             preferences.noBlackPointCorrection = false;
245          else if ( *token == "no-black-point-correction" )
246             preferences.noBlackPointCorrection = true;
247 
248          else if ( *token == "clip-highlights" )
249             preferences.noClipHighlights = false;
250          else if ( *token == "no-clip-highlights" )
251             preferences.noClipHighlights = true;
252 
253          else if ( *token == "wavelet-noise-threshold" )
254          {
255             if ( ++token == theHints.End() )
256                break;
257             int t;
258             if ( token->TryToInt( t ) )
259                if ( t >= 0 )
260                   preferences.noiseThreshold = t;
261          }
262          else if ( *token == "no-wavelet-noise-reduction" )
263             preferences.noiseThreshold = 0;
264 
265          else if ( *token == "fbdd-noise-reduction" )
266          {
267             if ( ++token == theHints.End() )
268                break;
269             int n;
270             if ( token->TryToInt( n ) )
271                preferences.fbddNoiseReduction = Range( n, 0, 2 );
272          }
273          else if ( *token == "no-fbdd-noise-reduction" )
274             preferences.fbddNoiseReduction = 0;
275 
276          else if ( *token == "verbosity" )
277          {
278             if ( ++token == theHints.End() )
279                break;
280             int n;
281             if ( token->TryToInt( n ) )
282                verbosity = Range( n, 0, 3 );
283          }
284       }
285    }
286 };
287 
288 // ----------------------------------------------------------------------------
289 // ----------------------------------------------------------------------------
290 
291 class RawProgress
292 {
293 public:
294 
RawProgress(RawInstance & instance)295    RawProgress( RawInstance& instance )
296       : m_instance( instance )
297    {
298       m_instance.m_raw->set_progress_handler( Callback, this );
299    }
300 
~RawProgress()301    ~RawProgress()
302    {
303       Complete();
304    }
305 
Complete()306    void Complete()
307    {
308       if ( m_monitor.IsInitialized() )
309          m_monitor.Complete();
310    }
311 
312 private:
313 
314    RawInstance&    m_instance;
315    StatusMonitor   m_monitor;
316    StandardStatus  m_status;
317    LibRaw_progress m_lastStage = LIBRAW_PROGRESS_START;
318    int             m_lastIteration = 0;
319 
Callback(void * data,LibRaw_progress stage,int iteration,int expected)320    static int Callback( void* data, LibRaw_progress stage, int iteration, int expected )
321    {
322 #define P   reinterpret_cast<RawProgress*>( data )
323 
324       try
325       {
326          if ( stage != LIBRAW_PROGRESS_START )
327          {
328             if ( stage != P->m_lastStage )
329             {
330                P->m_lastStage = stage;
331 
332                if ( P->m_monitor.IsInitialized() )
333                {
334                   P->m_monitor.Complete();
335                   P->m_monitor.Clear();
336                }
337 
338                if ( iteration < expected )
339                {
340                   P->m_lastIteration = iteration;
341                   P->m_monitor.SetCallback( &P->m_status );
342                   P->m_monitor.Initialize( LibRaw::strprogress( stage ), expected );
343                   if ( iteration > 0 )
344                      P->m_monitor += iteration;
345                }
346                else
347                   Console().WriteLn( "<end><cbr>" + String( LibRaw::strprogress( stage ) ) );
348             }
349             else
350             {
351                if ( iteration > P->m_lastIteration )
352                {
353                   if ( P->m_monitor.IsInitialized() )
354                      P->m_monitor += iteration - P->m_lastIteration;
355                   P->m_lastIteration = iteration;
356                }
357             }
358          }
359 
360          return 0;
361       }
362       catch ( const ProcessAborted& )
363       {
364          return 1;
365       }
366       catch ( ... )
367       {
368          throw;
369       }
370 
371 #undef P
372    }
373 };
374 
375 // ----------------------------------------------------------------------------
376 // ----------------------------------------------------------------------------
377 
RawInstance(const RawFormat * F)378 RawInstance::RawInstance( const RawFormat* F ) : FileFormatImplementation( F )
379 {
380 }
381 
382 // ----------------------------------------------------------------------------
383 
~RawInstance()384 RawInstance::~RawInstance() noexcept( false )
385 {
386    Close();
387 }
388 
389 // ----------------------------------------------------------------------------
390 
ExposureAsText() const391 String RawInstance::ExposureAsText() const
392 {
393    String expText;
394    if ( m_exposure >= 1 )
395       return String().Format( "%.0fs", m_exposure );
396    if ( m_exposure > 0 )
397       return String().Format( "1/%.0fs", 1.0/m_exposure );
398    return "<unknown>";
399 }
400 
401 // ----------------------------------------------------------------------------
402 
CheckOpenStream(const String & memberFunc) const403 void RawInstance::CheckOpenStream( const String& memberFunc ) const
404 {
405    if ( !IsOpen() )
406       throw Error( "RawInstance::" + memberFunc + "(): Illegal request on a closed stream." );
407 }
408 
409 // ----------------------------------------------------------------------------
410 
CheckLibRawReturnCode(int errorCode)411 void RawInstance::CheckLibRawReturnCode( int errorCode )
412 {
413    if ( errorCode != 0 )
414    {
415       if ( errorCode > 0 )
416          throw Error( ::strerror( errorCode ) );
417       if ( errorCode == LIBRAW_CANCELLED_BY_CALLBACK )
418          throw ProcessAborted();
419       if ( LIBRAW_FATAL_ERROR( errorCode ) )
420          throw Error( String().Format( "LibRaw (%d): ", errorCode ) + libraw_strerror( errorCode ) );
421    }
422 }
423 
424 // ----------------------------------------------------------------------------
425 
EnabledOrDisabled(bool x)426 static String EnabledOrDisabled( bool x )
427 {
428    return x ? "enabled" : "disabled";
429 }
430 
Open(const String & filePath,const IsoString & hints)431 ImageDescriptionArray RawInstance::Open( const String& filePath, const IsoString& hints )
432 {
433 #define sizes     m_raw->imgdata.sizes
434 #define idata     m_raw->imgdata.idata
435 #define color     m_raw->imgdata.color
436 #define thumbnail m_raw->imgdata.thumbnail
437 #define other     m_raw->imgdata.other
438 
439    Close();
440 
441    Exception::EnableConsoleOutput();
442 
443    try
444    {
445       /*
446        * Decode read hints.
447        */
448       {
449          RawReadHints readHints( hints );
450          m_preferences = readHints.preferences;
451          m_verbosity = readHints.verbosity;
452       }
453 
454       /*
455        * Open the file with LibRaw.
456        */
457       m_filePath = filePath;
458 
459       m_raw = new LibRaw;
460 
461       if ( m_verbosity > 1 )
462          m_progress = new RawProgress( *this );
463 
464       IsoString filePath8 =
465 #ifdef __PCL_WINDOWS
466          File::UnixPathToWindows( filePath ).ToMBS();
467 #else
468          filePath.ToUTF8();
469 #endif
470       CheckLibRawReturnCode( m_raw->open_file( filePath8.c_str() ) );
471 
472       /*
473       ByteArray data = File::ReadFile( filePath );
474 
475       CheckLibRawReturnCode( m_raw->open_buffer( data.Begin(), data.Length() ) );
476       //CheckLibRawReturnCode( m_raw->adjust_sizes_info_only() );
477       */
478 
479       bool xtrans = idata.filters == 9;
480       bool foveon = idata.is_foveon;
481       bool raw = (m_preferences.outputCFA || m_preferences.outputRawRGB || m_preferences.createSuperPixels && !xtrans) && !foveon;
482       bool noAutoCrop = raw && m_preferences.noAutoCrop
483                             && m_preferences.noWhiteBalance
484                             && m_preferences.noBlackPointCorrection
485                             && m_preferences.noClipHighlights
486                             && m_preferences.noiseThreshold == 0;
487 
488       /*
489        * Descriptive metadata.
490        */
491       m_description = IsoString( other.desc ).Trimmed().UTF8ToUTF16();
492       m_author = IsoString( other.artist ).Trimmed().UTF8ToUTF16();
493 
494       /*
495        * Camera identification.
496        */
497       m_cameraManufacturer = IsoString( idata.make ).Trimmed();
498       m_cameraModel = IsoString( idata.model ).Trimmed();
499 
500       if ( !foveon )
501       {
502          /*
503           * Get the CFA pattern of the raw image.
504           */
505          if ( xtrans )
506          {
507             const char* x = reinterpret_cast<const char*>( noAutoCrop ? idata.xtrans_abs : idata.xtrans );
508             for ( int i = 0; i < 36; ++i )
509                m_rawCFAPattern << idata.cdesc[int( x[i] )];
510 
511             m_cfaPatternName = "X-Trans";
512          }
513          else
514          {
515             m_rawCFAPattern = IsoString() << idata.cdesc[m_raw->COLOR( 0, 0 )]
516                                           << idata.cdesc[m_raw->COLOR( 0, 1 )]
517                                           << idata.cdesc[m_raw->COLOR( 1, 0 )]
518                                           << idata.cdesc[m_raw->COLOR( 1, 1 )];
519 
520             m_cfaPatternName = "Bayer";
521          }
522 
523          /*
524           * CFA patterns refer to the top-left corner of the *visible* image
525           * region. If we are not cropping invisible areas, rotate CFA patterns
526           * horizontally and/or vertically, as necessary.
527           */
528          if ( noAutoCrop )
529             if ( xtrans )
530             {
531                // 6x6 X-Trans pattern
532                int dy = sizes.top_margin % 6;
533                int dx = sizes.left_margin % 6;
534                char* p = m_rawCFAPattern.Begin();
535 
536                if ( dy )
537                {
538 #ifdef _MSC_VER
539                   char* t = new char[ 6*dy ];
540 #else
541                   char t[ 6*dy ];
542 #endif
543                   memcpy( t, p, 6*dy );
544                   memcpy( p, p + 6*dy, 36 - 6*dy );
545                   memcpy( p + 36 - 6*dy, t, 6*dy );
546 #ifdef _MSC_VER
547                   delete [] t;
548 #endif
549                }
550                if ( dx )
551                {
552 #ifdef _MSC_VER
553                   char* t = new char[ dx ];
554 #else
555                   char t[ dx ];
556 #endif
557                   for ( int i = 0; i < 6; ++i, p += 6 )
558                   {
559                      memcpy( t, p, dx );
560                      memcpy( p, p + dx, 6-dx );
561                      memcpy( p + 6-dx, t, dx );
562                   }
563 #ifdef _MSC_VER
564                   delete [] t;
565 #endif
566                }
567             }
568             else
569             {
570                // 2x2 Bayer pattern
571                if ( sizes.top_margin & 1 )
572                {
573                   Swap( m_rawCFAPattern[0], m_rawCFAPattern[2] );
574                   Swap( m_rawCFAPattern[1], m_rawCFAPattern[3] );
575                }
576                if ( sizes.left_margin & 1 )
577                {
578                   Swap( m_rawCFAPattern[0], m_rawCFAPattern[1] );
579                   Swap( m_rawCFAPattern[2], m_rawCFAPattern[3] );
580                }
581             }
582 
583          /*
584           * Get the CFA pattern of the output image. Rotate it if we are
585           * loading with camera flipping enabled and the original frame has
586           * been rotated by 180, 90 CCW or 90 CW degrees.
587           */
588          m_cfaPattern = m_rawCFAPattern;
589          if ( !m_preferences.noAutoFlip )
590             if ( raw )
591                if ( xtrans )
592                   switch ( sizes.flip )
593                   {
594                   case 0:
595                      break;
596                   case 3:  // 180 degrees
597                      for ( int y0 = 0, y1 = 5; y0 < y1; ++y0, --y1 )
598                         for ( int x0 = 0, x1 = 5; x0 < x1; ++x0, --x1 )
599                         {
600                            Swap( m_cfaPattern[6*y0 + x0], m_cfaPattern[6*y1 + x1] );
601                            Swap( m_cfaPattern[6*y0 + x1], m_cfaPattern[6*y1 + x0] );
602                         }
603                      break;
604                   case 5:  // 90 degrees counter-clockwise
605                      {
606                         IsoString tmp = m_cfaPattern;
607                         for ( int y = 0, t = 0; y < 6; ++y )
608                            for ( int x = 0; x < 6; ++x, ++t )
609                               m_cfaPattern[(5 - x)*6 + y] = tmp[t];
610                      }
611                      break;
612                   case 6:  // 90 degrees clockwise
613                      {
614                         IsoString tmp = m_cfaPattern;
615                         for ( int y = 0, t = 0; y < 6; ++y )
616                            for ( int x = 0; x < 6; ++x, ++t )
617                               m_cfaPattern[x*6 + 5 - y] = tmp[t];
618                      }
619                      break;
620                   }
621                else
622                   switch ( sizes.flip )
623                   {
624                   case 0:
625                      break;
626                   case 3:  // 180 degrees
627                      Swap( m_cfaPattern[0], m_cfaPattern[3] );
628                      Swap( m_cfaPattern[1], m_cfaPattern[2] );
629                      break;
630                   case 5:  // 90 degrees counter-clockwise
631                      Swap( m_cfaPattern[0], m_cfaPattern[2] );
632                      Swap( m_cfaPattern[1], m_cfaPattern[3] );
633                      Swap( m_cfaPattern[0], m_cfaPattern[3] );
634                      break;
635                   case 6:  // 90 degrees clockwise
636                      Swap( m_cfaPattern[0], m_cfaPattern[1] );
637                      Swap( m_cfaPattern[2], m_cfaPattern[3] );
638                      Swap( m_cfaPattern[0], m_cfaPattern[3] );
639                      break;
640                   }
641       }
642 
643       /*
644        * Camera RGB to sRGB conversion matrix.
645        */
646       m_sRGBConversionMatrix = F32Matrix( 3, idata.colors );
647       for ( int i = 0; i < 3; ++i )
648          for ( int j = 0; j < idata.colors; ++j )
649             m_sRGBConversionMatrix[i][j] = color.rgb_cam[i][j];
650 
651       /*
652        * Camera timestamp, exposure in seconds, ISO speed, focal length in mm
653        * and f/d aperture.
654        */
655       if ( other.timestamp != 0 )
656          m_timestamp = TimePoint( other.timestamp );
657       m_exposure = other.shutter;
658       m_isoSpeed = other.iso_speed;
659       m_focalLength = other.focal_len;
660       m_aperture = other.aperture;
661 
662       /*
663        * Check for nonexistent or invalid metadata items. These have caused
664        * problems in the old dcraw times, so checking them here doesn't hurt.
665        */
666       if ( !IsFinite( m_exposure ) )
667          m_exposure = 0;
668       if ( !IsFinite( m_focalLength ) )
669          m_focalLength = 0;
670       if ( !IsFinite( m_aperture ) )
671          m_aperture = 0;
672 
673       /*
674        * Geometry and color space image properties.
675        */
676       ImageInfo i;
677       i.supported        = idata.filters >= 1000 || xtrans || foveon;
678       i.colorSpace       = m_preferences.outputCFA ? ColorSpace::Gray : ColorSpace::RGB;
679       i.numberOfChannels = m_preferences.outputCFA ? 1 : 3;
680 
681       if ( noAutoCrop )
682       {
683          i.width  = sizes.raw_width;
684          i.height = sizes.raw_height;
685       }
686       else
687       {
688          i.width  = sizes.width;
689          i.height = sizes.height;
690          if ( !raw )
691             if ( m_preferences.interpolation == RawPreferences::HalfSize )
692             {
693                i.width  >>= 1;
694                i.height >>= 1;
695             }
696       }
697 
698       if ( !m_preferences.noAutoFlip )
699          if ( sizes.flip == 5 || sizes.flip == 6 ) // rotated by +/-90 deg?
700             Swap( i.width, i.height );
701 
702       /*
703        * Format-independent image properties.
704        */
705       ImageOptions o;
706       o.bitsPerSample      = 16;
707       o.complexSample      = false;
708       o.embedICCProfile    = color.profile != nullptr &&
709                                  reinterpret_cast<const ICCProfile*>( color.profile )->IsProfile();
710       o.embedThumbnail     = thumbnail.tlength > 0 && (thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG || thumbnail.tformat == LIBRAW_THUMBNAIL_BITMAP);
711       o.ieeefpSampleFormat = false;
712       o.xResolution        = 100;
713       o.yResolution        = 100;
714       o.lowerRange         = 0;
715       o.upperRange         = 65535;
716       o.isoSpeed           = m_isoSpeed;
717       o.exposure           = m_exposure;
718       o.aperture           = m_aperture;
719       o.focalLength        = m_focalLength;
720 
721       /*
722        * Be verbose as requested.
723        */
724       if ( m_verbosity > 0 )
725       {
726          m_progress->Complete();
727 
728          Console console;
729          console.WriteLn( "<end><cbr>" );
730          if ( !m_cameraModel.IsEmpty() )
731             console.WriteLn(                     "Camera ........... <raw>" + m_cameraManufacturer + ' ' + m_cameraModel + "</raw>" );
732          if ( m_timestamp.IsValid() )
733             console.WriteLn(                     "Timestamp ........ " + m_timestamp.ToString( 3/*timeItems*/, 0/*precision*/ ) );
734          if ( m_exposure > 0 )
735             console.WriteLn(                     "Exposure ......... " + ExposureAsText() );
736          if ( m_isoSpeed > 0 )
737             console.WriteLn( String().Format(    "ISO speed ........ %d", m_isoSpeed ) );
738          if ( m_focalLength > 0 )
739          {
740             console.WriteLn( String().Format(    "Focal length ..... %.2g mm", m_focalLength ) );
741             if ( m_aperture > 0 )
742                console.WriteLn( String().Format( "Aperture ......... f/%.2g = %.2g mm", m_aperture, m_focalLength/m_aperture ) );
743          }
744          if ( o.embedICCProfile )
745             console.WriteLn(                     "ICC profile ...... <raw>" + reinterpret_cast<const ICCProfile*>( color.profile )->Description() + "</raw>" );
746          if ( !m_author.IsEmpty() )
747             console.WriteLn(                     "Author ........... <raw>" + m_author + "</raw>" );
748          if ( !m_rawCFAPattern.IsEmpty() )
749             console.WriteLn(                     "CFA pattern ...... " + m_cfaPatternName + ' ' + m_rawCFAPattern + ((m_cfaPattern != m_rawCFAPattern) ? " (" + m_cfaPattern + ")" : IsoString()) );
750          console.WriteLn( String().Format(       "Raw dimensions ... w=%d h=%d", sizes.raw_width, sizes.raw_height ) );
751          console.WriteLn( String().Format(       "Image geometry ... x=%d y=%d w=%d h=%d", sizes.left_margin, sizes.top_margin, sizes.width, sizes.height ) );
752          if ( sizes.flip > 0 )
753             console.WriteLn( String().Format(    "Image rotation ... %d deg", (sizes.flip == 5) ? 90 : ((sizes.flip == 6) ? 270 : 180) ) );
754          console.WriteLn( "<end><cbr>" );
755       }
756 
757       if ( m_verbosity > 1 )
758       {
759          Console console;
760          console.WriteLn( "<end><cbr>Raw decoding parameters:" );
761          console.WriteLn(          "Output mode ............... " + m_preferences.OutputModeAsString() );
762          if ( raw )
763          {
764             console.WriteLn(       "Auto crop ................. " + EnabledOrDisabled( !noAutoCrop ) );
765          }
766          else
767          {
768             console.Write(         "Interpolation ............. " );
769             if ( xtrans )
770                console.WriteLn( "X-Trans" );
771             else if ( foveon )
772                console.WriteLn( "Foveon" );
773             else
774             {
775                console.WriteLn( m_preferences.InterpolationAsString() );
776                if ( m_preferences.interpolation == RawPreferences::DCB )
777                {
778                   console.WriteLn(         "DCB iterations ............ " + String( m_preferences.dcbIterations ) );
779                   console.WriteLn(         "DCB refinement ............ " + EnabledOrDisabled( m_preferences.dcbRefinement ) );
780                }
781                console.WriteLn(            "Interpolate as 4 colors ... " + EnabledOrDisabled( m_preferences.interpolateAs4Colors ) );
782             }
783             if ( !xtrans && !foveon )
784                console.WriteLn(            "FBDD noise reduction ...... " + String( m_preferences.fbddNoiseReduction ) );
785          }
786          console.WriteLn(                  "Wavelet noise threshold ... " + String( m_preferences.noiseThreshold ) );
787          console.WriteLn(                  "White balancing ........... " + m_preferences.WhiteBalancingAsString() );
788          console.WriteLn(                  "Black point correction .... " + EnabledOrDisabled( !m_preferences.noBlackPointCorrection  ) );
789          console.WriteLn(                  "Highlights clipping ....... " + EnabledOrDisabled( !m_preferences.noClipHighlights ) );
790          console.WriteLn(                  "Auto rotate ............... " + EnabledOrDisabled( !m_preferences.noAutoFlip ) );
791          console.WriteLn( String().Format( "Output image .............. w=%d h=%d n=%d %s", i.width, i.height, i.numberOfChannels,
792                                                                            (i.colorSpace == ColorSpace::RGB) ? "RGB" : "Gray" ) );
793          console.WriteLn( "<end><cbr>" );
794       }
795 
796       return ImageDescriptionArray() << ImageDescription( i, o );
797    }
798    catch ( ... )
799    {
800       Close();
801       throw;
802    }
803 
804 #undef sizes
805 #undef idata
806 #undef color
807 #undef thumbnail
808 #undef other
809 }
810 
811 // ----------------------------------------------------------------------------
812 
IsOpen() const813 bool RawInstance::IsOpen() const
814 {
815    return !m_raw.IsNull();
816 }
817 
818 // ----------------------------------------------------------------------------
819 
FilePath() const820 String RawInstance::FilePath() const
821 {
822    return m_filePath;
823 }
824 
825 // ----------------------------------------------------------------------------
826 
Close()827 void RawInstance::Close()
828 {
829    m_raw.Reset();
830    m_progress.Reset();
831    m_description = m_author = String();
832    m_cameraManufacturer = m_cameraModel = m_cfaPattern = m_rawCFAPattern = m_cfaPatternName = IsoString();
833    m_sRGBConversionMatrix = F32Matrix();
834    m_timestamp = TimePoint();
835    m_exposure = m_isoSpeed = m_focalLength = m_aperture = 0;
836 }
837 
838 // ----------------------------------------------------------------------------
839 
ImageFormatInfo() const840 String RawInstance::ImageFormatInfo() const
841 {
842    if ( !IsOpen() )
843       return String();
844 
845    return String( "camera=" ) + '\'' + m_cameraManufacturer + ' ' + m_cameraModel + '\'' + ' ' +
846           String( "ISO=" ) + String( m_isoSpeed ) + ' ' +
847           String( "exposure=" ) + ExposureAsText();
848 }
849 
850 // ----------------------------------------------------------------------------
851 
ReadICCProfile()852 ICCProfile RawInstance::ReadICCProfile()
853 {
854    CheckOpenStream( "ReadICCProfile" );
855 
856    if ( m_raw->imgdata.color.profile != nullptr )
857    {
858       const ICCProfile* icc = reinterpret_cast<const ICCProfile*>( m_raw->imgdata.color.profile );
859       if ( icc->IsProfile() )
860          return *icc;
861    }
862    return ICCProfile();
863 }
864 
865 // ----------------------------------------------------------------------------
866 
ReadFITSKeywords()867 FITSKeywordArray RawInstance::ReadFITSKeywords()
868 {
869    if ( !IsOpen() )
870       return FITSKeywordArray();
871 
872    FITSKeywordArray keywords;
873 
874    keywords << FITSHeaderKeyword( "COMMENT",
875                                   IsoString(),
876                                   "Decoded with " + PixInsightVersion::AsString().ToIsoString() )
877             << FITSHeaderKeyword( "COMMENT",
878                                   IsoString(),
879                                   "Decoded with " + Module->ReadableVersion() )
880             << FITSHeaderKeyword( "COMMENT",
881                                   IsoString(),
882                                   IsoString( "Decoded with LibRaw version " ) + LibRaw::version() );
883 
884    if ( !m_cameraModel.IsEmpty() )
885       keywords << FITSHeaderKeyword( "INSTRUME", '\'' + m_cameraManufacturer + ' ' + m_cameraModel + '\'', "Camera model" );
886 
887    if ( m_timestamp.IsValid() )
888       keywords << FITSHeaderKeyword( "DATE-OBS", '\'' + m_timestamp.ToIsoString(
889                               3/*timeItems*/, 0/*precision*/, 0/*tz*/, false/*timeZone*/ ) + '\'', "Camera timestamp" );
890 
891    if ( m_exposure > 0 )
892       keywords.Add( FITSHeaderKeyword( "EXPTIME", IsoString().Format( "%.6f", m_exposure ), "Exposure time in seconds" ) );
893 
894    if ( m_isoSpeed > 0 )
895       keywords.Add( FITSHeaderKeyword( "ISOSPEED", IsoString().Format( "%d", m_isoSpeed ), "ISO speed as specified in ISO 12232" ) );
896 
897    if ( m_focalLength > 0 )
898    {
899       keywords.Add( FITSHeaderKeyword( "FOCALLEN", IsoString().Format( "%.2f", m_focalLength ), "Focal length in mm" ) );
900       if ( m_aperture > 0 )
901          keywords.Add( FITSHeaderKeyword( "APTDIA", IsoString().Format( "%.2f", m_focalLength/m_aperture ), "Aperture diameter in mm" ) );
902    }
903 
904    return keywords;
905 }
906 
907 // ----------------------------------------------------------------------------
908 
ImagePropertyDescriptions()909 PropertyDescriptionArray RawInstance::ImagePropertyDescriptions()
910 {
911    CheckOpenStream( "ImagePropertyDescriptions" );
912 
913    PropertyDescriptionArray descriptions;
914    if ( !m_cfaPattern.IsEmpty() )
915    {
916       descriptions << PropertyDescription( "PCL:CFASourcePattern", VariantType::IsoString );
917       if ( m_rawCFAPattern != m_cfaPattern )
918          descriptions << PropertyDescription( "PCL:RawCFASourcePattern", VariantType::IsoString );
919       descriptions << PropertyDescription( "PCL:CFASourcePatternName", VariantType::IsoString );
920       descriptions << PropertyDescription( "PCL:sRGBConversionMatrix", VariantType::F32Matrix );
921    }
922    if ( !m_cameraModel.IsEmpty() )
923       descriptions << PropertyDescription( "Instrument:Camera:Name", VariantType::String );
924    if ( m_isoSpeed > 0 )
925       descriptions << PropertyDescription( "Instrument:Camera:ISOSpeed", VariantType::Int32 );
926    if ( m_exposure > 0 )
927       descriptions << PropertyDescription( "Instrument:ExposureTime", VariantType::Float32 );
928    if ( m_focalLength > 0 )
929    {
930       descriptions << PropertyDescription( "Instrument:Telescope:FocalLength", VariantType::Float32 );
931       if ( m_aperture > 0 )
932          descriptions << PropertyDescription( "Instrument:Telescope:Aperture", VariantType::Float32 );
933    }
934    if ( !m_description.IsEmpty() )
935       descriptions << PropertyDescription( "Observation:Description", VariantType::String );
936    if ( m_timestamp.IsValid() )
937       descriptions << PropertyDescription( "Observation:Time:Start", VariantType::TimePoint );
938    if ( !m_author.IsEmpty() )
939       descriptions << PropertyDescription( "Observer:Name", VariantType::String );
940 
941    return descriptions;
942 }
943 
944 // ----------------------------------------------------------------------------
945 
ReadImageProperty(const IsoString & property)946 Variant RawInstance::ReadImageProperty( const IsoString& property )
947 {
948    CheckOpenStream( "ReadImageProperty" );
949 
950    if ( property == "PCL:CFASourcePattern" )
951       return m_cfaPattern;
952    if ( property == "PCL:RawCFASourcePattern" )
953       return m_rawCFAPattern;
954    if ( property == "PCL:CFASourcePatternName" )
955       return m_cfaPatternName;
956    if ( property == "PCL:sRGBConversionMatrix" )
957       return m_sRGBConversionMatrix;
958    if ( property == "Instrument:Camera:Name" )
959       return String( m_cameraManufacturer + ' ' + m_cameraModel );
960    if ( property == "Instrument:Camera:ISOSpeed" )
961       return int32( m_isoSpeed );
962    if ( property == "Instrument:ExposureTime" )
963       return m_exposure;
964    if ( property == "Instrument:Telescope:Aperture" )
965       return (m_aperture > 0) ? m_focalLength/m_aperture/1000 : .0F;
966    if ( property == "Instrument:Telescope:FocalLength" )
967       return m_focalLength/1000;
968    if ( property == "Observation:Description" )
969       return m_description;
970    if ( property == "Observation:Time:Start" )
971       return m_timestamp;
972    if ( property == "Observer:Name" )
973       return m_author;
974 
975    return Variant();
976 }
977 
978 // ----------------------------------------------------------------------------
979 // ----------------------------------------------------------------------------
980 
981 /*
982  * Fast color filter array (CFA) pixel sample selection.
983  * Code borrowed from the DrizzleIntegration process.
984  */
985 class CFAIndex
986 {
987 public:
988 
989    typedef GenericMatrix<bool>   cfa_channel_index;
990 
991    CFAIndex() = default;
992    CFAIndex( const CFAIndex& ) = default;
993 
CFAIndex(const IsoString & pattern)994    CFAIndex( const IsoString& pattern )
995    {
996       if ( pattern.IsEmpty() )
997          throw Error( "Empty CFA pattern." );
998       m_size = int( Sqrt( pattern.Length() ) );
999       if ( m_size < 2 )
1000          throw Error( "Invalid CFA pattern '" + pattern + '\'' );
1001       if ( m_size*m_size != pattern.Length() )
1002          throw Error( "Non-square CFA patterns are not supported: '" + pattern + '\'' );
1003       for ( int c = 0; c < 3; ++c )
1004       {
1005          m_index[c] = cfa_channel_index( m_size, m_size );
1006          IsoString::const_iterator p = pattern.Begin();
1007          for ( int i = 0; i < m_size; ++i )
1008             for ( int j = 0; j < m_size; ++j )
1009                m_index[c][i][j] = *p++ == "RGB"[c];
1010       }
1011    }
1012 
1013    operator bool() const
1014    {
1015       return m_size > 0;
1016    }
1017 
operator ()(int chn,int row,int col) const1018    bool operator ()( int chn, int row, int col ) const
1019    {
1020       return m_index[chn][row % m_size][col % m_size];
1021    }
1022 
1023 private:
1024 
1025    int               m_size = 0;
1026    cfa_channel_index m_index[ 3 ]; // RGB
1027 };
1028 
1029 // ----------------------------------------------------------------------------
1030 
1031 class RawImageReader
1032 {
1033 public:
1034 
1035 #define preferences  instance.m_preferences
1036 #define RAW          instance.m_raw
1037 #define idata        instance.m_raw->imgdata.idata
1038 #define params       instance.m_raw->imgdata.params
1039 #define sizes        instance.m_raw->imgdata.sizes
1040 #define color        instance.m_raw->imgdata.color
1041 #define rawdata      instance.m_raw->imgdata.rawdata
1042 
1043    template <class P>
Read(GenericImage<P> & image,RawInstance & instance)1044    static void Read( GenericImage<P>& image, RawInstance& instance )
1045    {
1046       instance.CheckOpenStream( "ReadImage" );
1047 
1048       instance.CheckLibRawReturnCode( RAW->unpack() );
1049 
1050       bool xtrans = idata.filters == 9;
1051       bool foveon = idata.is_foveon;
1052       bool raw = (preferences.outputCFA || preferences.outputRawRGB || preferences.createSuperPixels && !xtrans) && !foveon;
1053 
1054       /*
1055        * Set up dcraw processing parameters.
1056        */
1057 
1058       // Linear 16-bit output
1059       params.output_bps = 16;
1060 
1061       // Output color space: raw
1062       params.output_color = 0;
1063 
1064       // Disable LibRaw's default histogram transformation
1065       params.no_auto_bright = 1;
1066 
1067       // Disable LibRaw's default gamma curve transformation
1068       params.gamm[0] = params.gamm[1] = 1.0;
1069 
1070       if ( preferences.noWhiteBalance )
1071       {
1072          // Disable white balancing
1073          for ( size_type i = 0; i < ItemsInArray( params.user_mul ); ++i )
1074             params.user_mul[i] = 1;
1075          params.use_auto_wb = params.use_camera_wb = 0;
1076          params.use_camera_matrix = 0;
1077       }
1078       else
1079       {
1080          // Automatic white balance
1081          if ( preferences.useAutoWhiteBalance )
1082             params.use_auto_wb = 1;
1083          // Camera white balance
1084          if ( preferences.useCameraWhiteBalance )
1085             params.use_camera_wb = 1;
1086       }
1087 
1088       // Interpolation and related parameters
1089       if ( raw )
1090       {
1091          params.no_interpolation = 1;
1092       }
1093       else if ( !foveon && !xtrans )
1094       {
1095          if ( preferences.interpolation == RawPreferences::HalfSize )
1096             params.half_size = 1;
1097          else
1098          {
1099             int q = 0;
1100             switch ( preferences.interpolation )
1101             {
1102             case RawPreferences::Bilinear: q =  0; break;
1103             default:
1104             case RawPreferences::VNG:      q =  1; break;
1105             case RawPreferences::PPG:      q =  2; break;
1106             case RawPreferences::AHD:      q =  3; break;
1107             case RawPreferences::DCB:      q =  4; break;
1108             case RawPreferences::DHT:      q = 11; break;
1109             case RawPreferences::AAHD:     q = 12; break;
1110             }
1111             params.user_qual = q;
1112 
1113             if ( preferences.interpolation == RawPreferences::DCB )
1114             {
1115                params.dcb_iterations = preferences.dcbIterations;
1116                params.dcb_enhance_fl = preferences.dcbRefinement;
1117             }
1118          }
1119 
1120          if ( preferences.interpolateAs4Colors )
1121             params.four_color_rgb = 1;
1122 
1123          params.fbdd_noiserd = preferences.fbddNoiseReduction;
1124       }
1125 
1126       // No automatic camera flip
1127       if ( raw || preferences.noAutoFlip )
1128          params.user_flip = 0;
1129 
1130       // No black point adjustment
1131       if ( preferences.noBlackPointCorrection )
1132          params.user_black = 0;
1133 
1134       // Do not clip highlights
1135       if ( preferences.noClipHighlights )
1136          params.highlight = 1;
1137 
1138       // Wavelet noise reduction
1139       if ( preferences.noiseThreshold > 0 )
1140          params.threshold = preferences.noiseThreshold;
1141 
1142       /*
1143        * Generate the output image.
1144        */
1145       if ( raw )
1146       {
1147          if ( preferences.noWhiteBalance
1148            && preferences.noBlackPointCorrection
1149            && preferences.noClipHighlights
1150            && preferences.noiseThreshold == 0 )
1151          {
1152             /*
1153              * Output pure raw data
1154              */
1155             const uint16* u = rawdata.raw_image;
1156             if ( u == nullptr )
1157                throw Error( "LibRaw: Null raw image data" );
1158 
1159             int width, height;
1160             if ( preferences.noAutoCrop )
1161             {
1162                width = sizes.raw_width;
1163                height = sizes.raw_height;
1164             }
1165             else
1166             {
1167                width = sizes.width;
1168                height = sizes.height;
1169                u += sizes.top_margin*sizes.raw_width + sizes.left_margin;
1170             }
1171 
1172             if ( preferences.outputCFA )
1173             {
1174                /*
1175                 * Output monochrome CFA pure raw image.
1176                 */
1177                image.AllocateData( width, height, 1, ColorSpace::Gray );
1178                typename GenericImage<P>::sample_iterator i( image );
1179                for ( int y = 0; y < height; ++y )
1180                {
1181                   const uint16* U = u + y*sizes.raw_width;
1182                   for ( int x = 0; x < width; ++x, ++i, ++U )
1183                      *i = P::ToSample( *U );
1184                }
1185             }
1186             else if ( preferences.outputRawRGB )
1187             {
1188                /*
1189                 * Output RGB pure raw image.
1190                 */
1191                image.AllocateData( width, height, 3, ColorSpace::RGB );
1192                image.Zero();
1193                CFAIndex index( instance.m_rawCFAPattern );
1194                typename GenericImage<P>::pixel_iterator i( image );
1195                for ( int y = 0; y < height; ++y )
1196                {
1197                   const uint16* U = u + y*sizes.raw_width;
1198                   for ( int x = 0; x < width; ++x, ++i, ++U )
1199                   {
1200                      typename P::sample v = P::ToSample( *U );
1201                      if ( index( 1, y, x ) )
1202                         i[1] = v;
1203                      else if ( index( 0, y, x ) )
1204                         i[0] = v;
1205                      else if ( index( 2, y, x ) )
1206                         i[2] = v;
1207                   }
1208                }
1209             }
1210             else if ( preferences.createSuperPixels )
1211             {
1212                /*
1213                 * Output RGB pure superpixels image.
1214                 */
1215                CFAIndex index( instance.m_rawCFAPattern );
1216                int Rr = 0, Rc = 0, G1r = -1, G1c = 0, G2r = 0, G2c = 0, Br = 0, Bc = 0;
1217                for ( int i = 0; i < 2; ++i )
1218                   for ( int j = 0; j < 2; ++j )
1219                      if ( index( 1, i, j ) )
1220                      {
1221                         if ( G1r < 0 )
1222                            G1r = i, G1c = j;
1223                         else
1224                            G2r = i, G2c = j;
1225                      }
1226                      else if ( index( 0, i, j ) )
1227                         Rr = i, Rc = j;
1228                      else if ( index( 2, i, j ) )
1229                         Br = i, Bc = j;
1230 
1231                image.AllocateData( width >> 1, height >> 1, 3, ColorSpace::RGB );
1232                typename GenericImage<P>::pixel_iterator i( image );
1233                for ( int y = 0; y < height-1; y += 2 )
1234                {
1235                   const uint16* U[2];
1236                   U[0] = u + y*sizes.raw_width;
1237                   U[1] = U[0] + sizes.raw_width;
1238                   for ( int x = 0; x < width-1; x += 2, ++i, U[0] += 2, U[1] += 2 )
1239                   {
1240                      i[0] = P::ToSample( U[Rr][Rc] );
1241                      i[1] = P::ToSample( double( int( U[G1r][G1c] ) + int( U[G2r][G2c] ) )/2/uint16_max );
1242                      i[2] = P::ToSample( U[Br][Bc] );
1243                   }
1244                }
1245             }
1246             else
1247                throw Error( "RawImageReader::Read(): Internal error: invalid raw output mode" );
1248          }
1249          else
1250          {
1251             /*
1252              * Output preprocessed raw data with one or more of white
1253              * balancing, black level subtraction and highlights clipping
1254              * applied.
1255              */
1256             instance.CheckLibRawReturnCode( RAW->dcraw_process() );
1257             const uint16 (*u)[4] = RAW->imgdata.image;
1258             if ( u == nullptr )
1259                throw Error( "LibRaw: Null preprocessed raw image data" );
1260 
1261             if ( preferences.outputCFA )
1262             {
1263                /*
1264                 * Output monochrome CFA preprocessed raw image.
1265                 */
1266                image.AllocateData( sizes.width, sizes.height, 1, ColorSpace::Gray );
1267                typename GenericImage<P>::sample_iterator i( image );
1268                for ( int y = 0; y < sizes.height; ++y )
1269                {
1270                   const uint16 (*U)[4] = u + y*sizes.width;
1271                   for ( int x = 0; x < sizes.width; ++x, ++i )
1272                      *i = P::ToSample( U[x][RAW->COLOR( y, x )] );
1273                }
1274             }
1275             else if ( preferences.outputRawRGB )
1276             {
1277                /*
1278                 * Output RGB preprocessed raw image.
1279                 */
1280                image.AllocateData( sizes.width, sizes.height, 3, ColorSpace::RGB );
1281                typename GenericImage<P>::pixel_iterator i( image );
1282                for ( int y = 0; y < sizes.height; ++y )
1283                {
1284                   const uint16 (*U)[4] = u + y*sizes.width;
1285                   for ( int x = 0; x < sizes.width; ++x, ++i )
1286                      for ( int c = 0; c < 3; ++c )
1287                         i[c] = P::ToSample( U[x][c] );
1288                }
1289             }
1290             else if ( preferences.createSuperPixels )
1291             {
1292                /*
1293                 * Output RGB preprocessed superpixels image.
1294                 */
1295                image.AllocateData( sizes.width >> 1, sizes.height >> 1, 3, ColorSpace::RGB );
1296                typename GenericImage<P>::pixel_iterator i( image );
1297                for ( int y = 0; y < sizes.height-1; y += 2 )
1298                {
1299                   const uint16 (*U0)[4] = u + y*sizes.width;
1300                   const uint16 (*U1)[4] = U0 + sizes.width;
1301                   for ( int x = 0; x < sizes.width-1; x += 2, ++i )
1302                   {
1303                      i[0] = P::ToSample( double( U0[x][0] + U0[x+1][0] + U1[x][0] + U1[x+1][0] )/uint16_max );
1304                      i[1] = P::ToSample( double( int( U0[x][1] ) + int( U0[x+1][1] )
1305                                                + int( U1[x][1] ) + int( U1[x+1][1] ) )/2/uint16_max );
1306                      i[2] = P::ToSample( double( U0[x][2] + U0[x+1][2] + U1[x][2] + U1[x+1][2] )/uint16_max );
1307                   }
1308                }
1309             }
1310             else
1311                throw Error( "RawImageReader::Read(): Internal error: invalid raw output mode" );
1312          }
1313 
1314          /*
1315           * Apply camera rotation.
1316           */
1317          if ( !preferences.noAutoFlip )
1318             switch ( sizes.flip )
1319             {
1320             case 0:
1321                break;
1322             case 3:
1323                Rotate180() >> image;
1324                break;
1325             case 5:
1326                Rotate90CCW() >> image;
1327                break;
1328             case 6:
1329                Rotate90CW() >> image;
1330                break;
1331             }
1332       }
1333       else
1334       {
1335          /*
1336           * Output demosaiced / postprocessed RGB image.
1337           */
1338          instance.CheckLibRawReturnCode( RAW->dcraw_process() );
1339          int ret;
1340          libraw_processed_image_t* p = RAW->dcraw_make_mem_image( &ret );
1341          if ( p != nullptr )
1342          {
1343             bool valid = p->type == LIBRAW_IMAGE_BITMAP
1344                      && (p->colors == 3 || p->colors == 1)
1345                      && (p->bits == 16 || p->bits == 8);
1346             if ( valid )
1347             {
1348                image.AllocateData( p->width, p->height, p->colors, (p->colors == 3) ? ColorSpace::RGB : ColorSpace::Gray );
1349                if ( p->bits == 16 )
1350                {
1351                   const uint16* u = reinterpret_cast<const uint16*>( p->data );
1352                   for ( typename GenericImage<P>::pixel_iterator i( image ); i; ++i )
1353                      for ( int j = 0; j < p->colors; ++j, ++u )
1354                         i[j] = P::ToSample( *u );
1355                }
1356                else // p->bits == 8
1357                {
1358                   const uint8* u = reinterpret_cast<const uint8*>( p->data );
1359                   for ( typename GenericImage<P>::pixel_iterator i( image ); i; ++i )
1360                      for ( int j = 0; j < p->colors; ++j, ++u )
1361                         i[j] = P::ToSample( *u );
1362                }
1363             }
1364 
1365             LibRaw::dcraw_clear_mem( p );
1366 
1367             if ( !valid )
1368                throw Error( "LibRaw: Invalid postprocessed image format" );
1369          }
1370          else
1371             instance.CheckLibRawReturnCode( ret );
1372       }
1373    }
1374 
1375 #undef preferences
1376 #undef RAW
1377 #undef idata
1378 #undef params
1379 #undef sizes
1380 #undef color
1381 #undef rawdata
1382 };
1383 
1384 // ----------------------------------------------------------------------------
1385 
ReadImage(Image & image)1386 void RawInstance::ReadImage( Image& image )
1387 {
1388    RawImageReader::Read( image, *this );
1389 }
1390 
1391 // ----------------------------------------------------------------------------
1392 
ReadImage(DImage & image)1393 void RawInstance::ReadImage( DImage& image )
1394 {
1395    RawImageReader::Read( image, *this );
1396 }
1397 
1398 // ----------------------------------------------------------------------------
1399 
ReadImage(UInt8Image & image)1400 void RawInstance::ReadImage( UInt8Image& image )
1401 {
1402    RawImageReader::Read( image, *this );
1403 }
1404 
1405 // ----------------------------------------------------------------------------
1406 
ReadImage(UInt16Image & image)1407 void RawInstance::ReadImage( UInt16Image& image )
1408 {
1409    RawImageReader::Read( image, *this );
1410 }
1411 
1412 // ----------------------------------------------------------------------------
1413 
ReadImage(UInt32Image & image)1414 void RawInstance::ReadImage( UInt32Image& image )
1415 {
1416    RawImageReader::Read( image, *this );
1417 }
1418 
1419 // ----------------------------------------------------------------------------
1420 
ReadThumbnail()1421 UInt8Image RawInstance::ReadThumbnail()
1422 {
1423 #define thumbnail m_raw->imgdata.thumbnail
1424 
1425    CheckOpenStream( "ReadImage" );
1426 
1427    UInt8Image T;
1428 
1429    if ( thumbnail.tlength > 0 )
1430    {
1431       if ( thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG )
1432       {
1433          CheckLibRawReturnCode( m_raw->unpack_thumb() );
1434          Bitmap bmp( thumbnail.thumb, thumbnail.tlength, "JPG" );
1435          T.AllocateData( bmp.Width(), bmp.Height(), 3, ColorSpace::RGB );
1436          T.Blend( bmp );
1437       }
1438       else if ( thumbnail.tformat == LIBRAW_THUMBNAIL_BITMAP )
1439       {
1440          CheckLibRawReturnCode( m_raw->unpack_thumb() );
1441          T.AllocateData( thumbnail.twidth, thumbnail.theight, 3, ColorSpace::RGB );
1442          const char* t = thumbnail.thumb;
1443          for ( UInt8Image::pixel_iterator i( T ); i; ++i )
1444          {
1445             i[0] = uint8( *t++ );
1446             i[1] = uint8( *t++ );
1447             i[2] = uint8( *t++ );
1448          }
1449       }
1450    }
1451 
1452    return T;
1453 
1454 #undef thumbnail
1455 }
1456 
1457 // ----------------------------------------------------------------------------
1458 
1459 } // pcl
1460 
1461 // ----------------------------------------------------------------------------
1462 // EOF RawInstance.cpp - Released 2020-01-14T11:57:23Z
1463