File indexing completed on 2025-01-05 03:56:48

0001 /* -*- C++ -*-
0002  * File: dcraw_emu.cpp
0003  * Copyright 2008-2021 LibRaw LLC (info@libraw.org)
0004  * Created: Sun Mar 23,   2008
0005  *
0006  * LibRaw simple C++ API sample: almost complete dcraw emulator
0007  *
0008 
0009 LibRaw is free software; you can redistribute it and/or modify
0010 it under the terms of the one of two licenses as you choose:
0011 
0012 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
0013    (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
0014 
0015 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
0016    (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
0017 
0018 
0019  */
0020 #ifdef _MSC_VER
0021 // suppress sprintf-related warning. sprintf() is permitted in sample code
0022 #define _CRT_SECURE_NO_WARNINGS
0023 #endif
0024 
0025 #include <stdio.h>
0026 #include <string.h>
0027 #include <stdlib.h>
0028 #include <math.h>
0029 #include <ctype.h>
0030 
0031 #include "libraw/libraw.h"
0032 
0033 #ifndef LIBRAW_WIN32_CALLS
0034 #include <sys/mman.h>
0035 #include <sys/time.h>
0036 #include <unistd.h>
0037 #else
0038 #include <io.h>
0039 #endif
0040 #include <fcntl.h>
0041 #include <sys/stat.h>
0042 
0043 #ifdef LIBRAW_WIN32_CALLS
0044 #define snprintf _snprintf
0045 #include <windows.h>
0046 #else
0047 #define O_BINARY 0
0048 #endif
0049 
0050 #ifdef USE_DNGSDK
0051 #include "dng_host.h"
0052 #include "dng_negative.h"
0053 #include "dng_simple_image.h"
0054 #include "dng_info.h"
0055 #endif
0056 
0057 void usage(const char *prog)
0058 {
0059   printf("dcraw_emu: almost complete dcraw emulator\n");
0060   printf("Usage:  %s [OPTION]... [FILE]...\n", prog);
0061   printf("-c float-num       Set adjust maximum threshold (default 0.75)\n"
0062          "-v        Verbose: print progress messages (repeated -v will add "
0063          "verbosity)\n"
0064          "-w        Use camera white balance, if possible\n"
0065          "-a        Average the whole image for white balance\n"
0066          "-A <x y w h> Average a grey box for white balance\n"
0067          "-r <r g b g> Set custom white balance\n"
0068          "+M/-M     Use/don't use an embedded color matrix\n"
0069          "-C <r b>  Correct chromatic aberration\n"
0070          "-P <file> Fix the dead pixels listed in this file\n"
0071          "-K <file> Subtract dark frame (16-bit raw PGM)\n"
0072          "-k <num>  Set the darkness level\n"
0073          "-S <num>  Set the saturation level\n"
0074          "-R <num>  Set raw processing options to num\n"
0075          "-n <num>  Set threshold for wavelet denoising\n"
0076          "-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)\n"
0077          "-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)\n"
0078          "-o [0-8]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ,ACES,\n"
0079          "          DCI-P3,Rec2020)\n"
0080 #ifndef NO_LCMS
0081          "-o file   Output ICC profile\n"
0082          "-p file   Camera input profile (use \'embed\' for embedded profile)\n"
0083 #endif
0084          "-j        Don't stretch or rotate raw pixels\n"
0085          "-W        Don't automatically brighten the image\n"
0086          "-b <num>  Adjust brightness (default = 1.0)\n"
0087          "-q N      Set the interpolation quality:\n"
0088          "          0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB\n"
0089          "          11 - DHT, 12 - AAHD\n"
0090          "-h        Half-size color image (twice as fast as \"-q 0\")\n"
0091          "-f        Interpolate RGGB as four colors\n"
0092          "-m <num>  Apply a 3x3 median filter to R-G and B-G\n"
0093          "-s [0..N-1] Select one raw image from input file\n"
0094          "-4        Linear 16-bit, same as \"-6 -W -g 1 1\n"
0095          "-6        Write 16-bit output\n"
0096          "-g pow ts Set gamma curve to gamma pow and toe slope ts (default = "
0097          "2.222 4.5)\n"
0098          "-T        Write TIFF instead of PPM\n"
0099          "-G        Use green_matching() filter\n"
0100          "-B <x y w h> use cropbox\n"
0101          "-F        Use FILE I/O instead of streambuf API\n"
0102          "-Z <suf>  Output filename generation rules\n"
0103          "          .suf => append .suf to input name, keeping existing suffix "
0104          "too\n"
0105          "           suf => replace input filename last extension\n"
0106          "          - => output to stdout\n"
0107          "          filename.suf => output to filename.suf\n"
0108          "-timing   Detailed timing report\n"
0109          "-fbdd N   0 - disable FBDD noise reduction (default), 1 - light "
0110          "FBDD, 2 - full\n"
0111          "-dcbi N   Number of extra DCD iterations (default - 0)\n"
0112          "-dcbe     DCB color enhance\n"
0113          "-aexpo <e p> exposure correction\n"
0114          "-apentax4shot enables merge of 4-shot pentax files\n"
0115          "-apentax4shotorder 3102 sets pentax 4-shot alignment order\n"
0116 #ifdef USE_RAWSPEED_BITS
0117          "-arsbits V Set use_rawspeed to V\n"
0118 #endif
0119          "-mmap     Use memory mmaped buffer instead of plain FILE I/O\n"
0120          "-mem     Use memory buffer instead of FILE I/O\n"
0121          "-disars   Do not use RawSpeed library\n"
0122          "-disinterp Do not run interpolation step\n"
0123          "-dsrawrgb1 Disable YCbCr to RGB conversion for sRAW (Cb/Cr "
0124          "interpolation enabled)\n"
0125          "-dsrawrgb2 Disable YCbCr to RGB conversion for sRAW (Cb/Cr "
0126          "interpolation disabled)\n"
0127 #ifdef USE_DNGSDK
0128          "-dngsdk   Use Adobe DNG SDK for DNG decode\n"
0129          "-dngflags N set DNG decoding options to value N\n"
0130 #endif
0131          "-doutputflags N set params.output_flags to N\n"
0132   );
0133   exit(1);
0134 }
0135 
0136 static int verbosity = 0;
0137 int cnt = 0;
0138 int my_progress_callback(void *d, enum LibRaw_progress p, int iteration,
0139                          int expected)
0140 {
0141   char *passed = (char *)(d ? d : "default string"); // data passed to callback
0142                                                      // at set_callback stage
0143 
0144   if (verbosity > 2) // verbosity set by repeat -v switches
0145   {
0146     printf("CB: %s  pass %d of %d (data passed=%s)\n", libraw_strprogress(p),
0147            iteration, expected, passed);
0148   }
0149   else if (iteration == 0) // 1st iteration of each step
0150     printf("Starting %s (expecting %d iterations)\n", libraw_strprogress(p),
0151            expected);
0152   else if (iteration == expected - 1)
0153     printf("%s finished\n", libraw_strprogress(p));
0154 
0155   ///    if(++cnt>10) return 1; // emulate user termination on 10-th callback
0156   ///    call
0157 
0158   return 0; // always return 0 to continue processing
0159 }
0160 
0161 // timer
0162 #ifndef LIBRAW_WIN32_CALLS
0163 static struct timeval start, end;
0164 void timerstart(void) { gettimeofday(&start, NULL); }
0165 void timerprint(const char *msg, const char *filename)
0166 {
0167   gettimeofday(&end, NULL);
0168   float msec = (end.tv_sec - start.tv_sec) * 1000.0f +
0169                (end.tv_usec - start.tv_usec) / 1000.0f;
0170   printf("Timing: %s/%s: %6.3f msec\n", filename, msg, msec);
0171 }
0172 #else
0173 LARGE_INTEGER start;
0174 void timerstart(void) { QueryPerformanceCounter(&start); }
0175 void timerprint(const char *msg, const char *filename)
0176 {
0177   LARGE_INTEGER unit, end;
0178   QueryPerformanceCounter(&end);
0179   QueryPerformanceFrequency(&unit);
0180   float msec = (float)(end.QuadPart - start.QuadPart);
0181   msec /= (float)unit.QuadPart / 1000.0f;
0182   printf("Timing: %s/%s: %6.3f msec\n", filename, msg, msec);
0183 }
0184 
0185 #endif
0186 
0187 struct file_mapping
0188 {
0189     void *map;
0190     INT64 fsize;
0191 #ifdef LIBRAW_WIN32_CALLS
0192     HANDLE fd, fd_map;
0193     file_mapping() : map(0), fsize(0), fd(INVALID_HANDLE_VALUE), fd_map(INVALID_HANDLE_VALUE){}
0194 #else
0195     int  fd;
0196     file_mapping() : map(0), fsize(0), fd(-1){}
0197 #endif
0198 };
0199 
0200 void create_mapping(struct file_mapping& data, const std::string& fn)
0201 {
0202 #ifdef LIBRAW_WIN32_CALLS
0203     std::wstring fpath(fn.begin(), fn.end());
0204     if ((data.fd = CreateFileW(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE) return;
0205     LARGE_INTEGER fs;
0206     if (!GetFileSizeEx(data.fd, &fs)) return;
0207     data.fsize = fs.QuadPart;
0208     if ((data.fd_map = ::CreateFileMapping(data.fd, 0, PAGE_READONLY, fs.HighPart, fs.LowPart, 0)) == INVALID_HANDLE_VALUE) return;
0209     data.map = MapViewOfFile(data.fd_map, FILE_MAP_READ, 0, 0, data.fsize);
0210 #else
0211     struct stat stt;
0212     if ((data.fd = open(fn.c_str(), O_RDONLY)) < 0) return;
0213     if (fstat(data.fd, &stt) != 0) return;
0214     data.fsize = stt.st_size;
0215     data.map = mmap(0, data.fsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, data.fd, 0);
0216     return;
0217 #endif
0218 }
0219 
0220 void close_mapping(struct file_mapping& data)
0221 {
0222 #ifdef LIBRAW_WIN32_CALLS
0223     if (data.map) UnmapViewOfFile(data.map);
0224     if (data.fd_map != INVALID_HANDLE_VALUE) CloseHandle(data.fd_map);
0225     if (data.fd != INVALID_HANDLE_VALUE) CloseHandle(data.fd);
0226     data.map = 0;
0227     data.fsize = 0;
0228     data.fd = data.fd_map = INVALID_HANDLE_VALUE;
0229 #else
0230     if (data.map)
0231         munmap(data.map, data.fsize);
0232     if (data.fd >= 0)
0233         close(data.fd);
0234     data.map = 0;
0235     data.fsize = 0;
0236     data.fd = -1;
0237 #endif
0238 }
0239 
0240 
0241 int main(int argc, char *argv[])
0242 {
0243   if (argc == 1)
0244     usage(argv[0]);
0245 
0246   LibRaw RawProcessor;
0247   int i, arg, c, ret;
0248   char opm, opt, *cp, *sp;
0249   int use_timing = 0, use_mem = 0, use_mmap = 0;
0250   char *outext = NULL;
0251 #ifdef USE_DNGSDK
0252   dng_host *dnghost = NULL;
0253 #endif
0254   struct file_mapping mapping;
0255   void *iobuffer = 0;
0256 #ifdef OUT
0257 #undef OUT
0258 #endif
0259 #define OUT RawProcessor.imgdata.params
0260 #define OUTR RawProcessor.imgdata.rawparams
0261 
0262   argv[argc] = (char *)"";
0263   for (arg = 1; (((opm = argv[arg][0]) - 2) | 2) == '+';)
0264   {
0265     char *optstr = argv[arg];
0266     opt = argv[arg++][1];
0267     if ((cp = strchr(sp = (char *)"cnbrkStqmHABCgU", opt)) != 0)
0268       for (i = 0; i < "111411111144221"[cp - sp] - '0'; i++)
0269         if (!isdigit(argv[arg + i][0]) && !optstr[2])
0270         {
0271           fprintf(stderr, "Non-numeric argument to \"-%c\"\n", opt);
0272           return 1;
0273         }
0274     if (!strchr("ftdeam", opt) && argv[arg - 1][2]) {
0275       fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0276       continue;
0277     }
0278     switch (opt)
0279     {
0280     case 'v':
0281       verbosity++;
0282       break;
0283     case 'G':
0284       OUT.green_matching = 1;
0285       break;
0286     case 'c':
0287       OUT.adjust_maximum_thr = (float)atof(argv[arg++]);
0288       break;
0289     case 'U':
0290       OUT.auto_bright_thr = (float)atof(argv[arg++]);
0291       break;
0292     case 'n':
0293       OUT.threshold = (float)atof(argv[arg++]);
0294       break;
0295     case 'b':
0296       OUT.bright = (float)atof(argv[arg++]);
0297       break;
0298     case 'P':
0299       OUT.bad_pixels = argv[arg++];
0300       break;
0301     case 'K':
0302       OUT.dark_frame = argv[arg++];
0303       break;
0304     case 'r':
0305       for (c = 0; c < 4; c++)
0306         OUT.user_mul[c] = (float)atof(argv[arg++]);
0307       break;
0308     case 'C':
0309       OUT.aber[0] = 1 / atof(argv[arg++]);
0310       OUT.aber[2] = 1 / atof(argv[arg++]);
0311       break;
0312     case 'g':
0313       OUT.gamm[0] = 1 / atof(argv[arg++]);
0314       OUT.gamm[1] = atof(argv[arg++]);
0315       break;
0316     case 'k':
0317       OUT.user_black = atoi(argv[arg++]);
0318       break;
0319     case 'S':
0320       OUT.user_sat = atoi(argv[arg++]);
0321       break;
0322     case 'R':
0323       OUTR.options = atoi(argv[arg++]);
0324       break;
0325     case 't':
0326       if (!strcmp(optstr, "-timing"))
0327         use_timing = 1;
0328       else if (!argv[arg - 1][2])
0329         OUT.user_flip = atoi(argv[arg++]);
0330       else
0331         fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0332       break;
0333     case 'q':
0334       OUT.user_qual = atoi(argv[arg++]);
0335       break;
0336     case 'm':
0337       if (!strcmp(optstr, "-mmap"))
0338         use_mmap = 1;
0339       else
0340           if (!strcmp(optstr, "-mem"))
0341         use_mem = 1;
0342       else
0343       {
0344         if (!argv[arg - 1][2])
0345           OUT.med_passes = atoi(argv[arg++]);
0346         else
0347           fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0348       }
0349       break;
0350     case 'H':
0351       OUT.highlight = atoi(argv[arg++]);
0352       break;
0353     case 's':
0354       OUTR.shot_select = abs(atoi(argv[arg++]));
0355       break;
0356     case 'o':
0357       if (isdigit(argv[arg][0]) && !isdigit(argv[arg][1]))
0358         OUT.output_color = atoi(argv[arg++]);
0359 #ifndef NO_LCMS
0360       else
0361         OUT.output_profile = argv[arg++];
0362       break;
0363     case 'p':
0364       OUT.camera_profile = argv[arg++];
0365 #endif
0366       break;
0367     case 'h':
0368       OUT.half_size = 1;
0369       break;
0370     case 'f':
0371       if (!strcmp(optstr, "-fbdd"))
0372         OUT.fbdd_noiserd = atoi(argv[arg++]);
0373       else
0374       {
0375         if (!argv[arg - 1][2])
0376           OUT.four_color_rgb = 1;
0377         else
0378           fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0379       }
0380       break;
0381     case 'A':
0382       for (c = 0; c < 4; c++)
0383         OUT.greybox[c] = atoi(argv[arg++]);
0384       break;
0385     case 'B':
0386       for (c = 0; c < 4; c++)
0387         OUT.cropbox[c] = atoi(argv[arg++]);
0388       break;
0389     case 'a':
0390       if (!strcmp(optstr, "-aexpo"))
0391       {
0392         OUT.exp_correc = 1;
0393         OUT.exp_shift = (float)atof(argv[arg++]);
0394         OUT.exp_preser = (float)atof(argv[arg++]);
0395       }
0396 #ifdef USE_RAWSPEED_BITS
0397       else if (!strcmp(optstr, "-arsbits"))
0398       {
0399     OUTR.use_rawspeed = atoi(argv[arg++]);
0400       }
0401 #endif
0402       else if (!strcmp(optstr, "-apentax4shot"))
0403       {
0404         OUTR.options |= LIBRAW_RAWOPTIONS_PENTAX_PS_ALLFRAMES;
0405       }
0406       else if (!strcmp(optstr, "-apentax4shotorder"))
0407       {
0408         strncpy(OUTR.p4shot_order, argv[arg++], 5);
0409       }
0410       else if (!argv[arg - 1][2])
0411         OUT.use_auto_wb = 1;
0412       else
0413         fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0414       break;
0415     case 'w':
0416       OUT.use_camera_wb = 1;
0417       break;
0418     case 'M':
0419       OUT.use_camera_matrix = (opm == '+')?3:0;
0420       break;
0421     case 'j':
0422       OUT.use_fuji_rotate = 0;
0423       break;
0424     case 'W':
0425       OUT.no_auto_bright = 1;
0426       break;
0427     case 'T':
0428       OUT.output_tiff = 1;
0429       break;
0430     case '4':
0431       OUT.gamm[0] = OUT.gamm[1] = OUT.no_auto_bright = 1; /* no break here! */
0432     case '6':
0433       OUT.output_bps = 16;
0434       break;
0435     case 'Z':
0436       outext = strdup(argv[arg++]);
0437       break;
0438     case 'd':
0439       if (!strcmp(optstr, "-dcbi"))
0440         OUT.dcb_iterations = atoi(argv[arg++]);
0441       else if (!strcmp(optstr, "-doutputflags"))
0442         OUT.output_flags = atoi(argv[arg++]);
0443       else if (!strcmp(optstr, "-disars"))
0444         OUTR.use_rawspeed = 0;
0445       else if (!strcmp(optstr, "-disinterp"))
0446         OUT.no_interpolation = 1;
0447       else if (!strcmp(optstr, "-dcbe"))
0448         OUT.dcb_enhance_fl = 1;
0449       else if (!strcmp(optstr, "-dsrawrgb1"))
0450       {
0451         OUTR.specials |= LIBRAW_RAWSPECIAL_SRAW_NO_RGB;
0452         OUTR.specials &= ~LIBRAW_RAWSPECIAL_SRAW_NO_INTERPOLATE;
0453       }
0454       else if (!strcmp(optstr, "-dsrawrgb2"))
0455       {
0456         OUTR.specials &= ~LIBRAW_RAWSPECIAL_SRAW_NO_RGB;
0457         OUTR.specials |= LIBRAW_RAWSPECIAL_SRAW_NO_INTERPOLATE;
0458       }
0459 #ifdef USE_DNGSDK
0460       else if (!strcmp(optstr, "-dngsdk"))
0461       {
0462         dnghost = new dng_host;
0463         RawProcessor.set_dng_host(dnghost);
0464       }
0465       else if (!strcmp(optstr, "-dngflags"))
0466       {
0467         OUTR.use_dngsdk = atoi(argv[arg++]);
0468       }
0469 #endif
0470       else
0471         fprintf(stderr, "Unknown option \"%s\".\n", argv[arg - 1]);
0472       break;
0473     default:
0474       fprintf(stderr, "Unknown option \"-%c\".\n", opt);
0475       break;
0476     }
0477   }
0478 #ifndef LIBRAW_WIN32_CALLS
0479   putenv((char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
0480 #else
0481   _putenv(
0482       (char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
0483 #endif
0484 #define P1 RawProcessor.imgdata.idata
0485 #define S RawProcessor.imgdata.sizes
0486 #define C RawProcessor.imgdata.color
0487 #define T RawProcessor.imgdata.thumbnail
0488 #define P2 RawProcessor.imgdata.other
0489 
0490   if (outext && !strcmp(outext, "-"))
0491     use_timing = verbosity = 0;
0492 
0493   if (verbosity > 1)
0494     RawProcessor.set_progress_handler(my_progress_callback,
0495                                       (void *)"Sample data passed");
0496 #ifdef LIBRAW_USE_OPENMP
0497   if (verbosity)
0498     printf("Using %d threads\n", omp_get_max_threads());
0499 #endif
0500 
0501   int done = 0;
0502   int total = argc - arg;
0503   for (; arg < argc; arg++)
0504   {
0505     char outfn[1024];
0506 
0507     if (verbosity)
0508       printf("Processing file %s\n", argv[arg]);
0509 
0510     timerstart();
0511 
0512     if (use_mmap)
0513     {
0514         create_mapping(mapping, argv[arg]);
0515         if (!mapping.map)
0516         {
0517             fprintf(stderr, "Cannot map %s\n", argv[arg]);
0518             close_mapping(mapping);
0519             continue;
0520         }
0521       if ((ret = RawProcessor.open_buffer(mapping.map,mapping.fsize) !=
0522                  LIBRAW_SUCCESS))
0523       {
0524         fprintf(stderr, "Cannot open_buffer %s: %s\n", argv[arg], libraw_strerror(ret));
0525         close_mapping(mapping);
0526         continue; // no recycle b/c open file will recycle itself
0527       }
0528     }
0529     else  if (use_mem)
0530     {
0531       int file = open(argv[arg], O_RDONLY | O_BINARY);
0532       struct stat st;
0533       if (file < 0)
0534       {
0535         fprintf(stderr, "Cannot open %s: %s\n", argv[arg], strerror(errno));
0536         continue;
0537       }
0538       if (fstat(file, &st))
0539       {
0540         fprintf(stderr, "Cannot stat %s: %s\n", argv[arg], strerror(errno));
0541         close(file);
0542         continue;
0543       }
0544       if (!(iobuffer = malloc(st.st_size)))
0545       {
0546         fprintf(stderr, "Cannot allocate %d kbytes for memory buffer\n",
0547                 (int)(st.st_size / 1024));
0548         close(file);
0549         continue;
0550       }
0551       int rd;
0552       if (st.st_size != (rd = read(file, iobuffer, st.st_size)))
0553       {
0554         fprintf(stderr,
0555                 "Cannot read %d bytes instead of  %d to memory buffer\n",
0556                 (int)rd, (int)st.st_size);
0557         close(file);
0558         free(iobuffer);
0559         continue;
0560       }
0561       close(file);
0562       if ((ret = RawProcessor.open_buffer(iobuffer, st.st_size)) !=
0563           LIBRAW_SUCCESS)
0564       {
0565         fprintf(stderr, "Cannot open_buffer %s: %s\n", argv[arg],
0566                 libraw_strerror(ret));
0567         free(iobuffer);
0568         continue; // no recycle b/c open file will recycle itself
0569       }
0570     }
0571     else
0572     {
0573         ret = RawProcessor.open_file(argv[arg]);
0574 
0575       if (ret != LIBRAW_SUCCESS)
0576       {
0577         fprintf(stderr, "Cannot open %s: %s\n", argv[arg],
0578                 libraw_strerror(ret));
0579         continue; // no recycle b/c open_file will recycle itself
0580       }
0581     }
0582 
0583     if (use_timing)
0584       timerprint("LibRaw::open_file()", argv[arg]);
0585 
0586     timerstart();
0587     if ((ret = RawProcessor.unpack()) != LIBRAW_SUCCESS)
0588     {
0589       fprintf(stderr, "Cannot unpack %s: %s\n", argv[arg],
0590               libraw_strerror(ret));
0591       continue;
0592     }
0593 
0594     if (use_timing)
0595       timerprint("LibRaw::unpack()", argv[arg]);
0596 
0597     timerstart();
0598     if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_process()))
0599     {
0600       fprintf(stderr, "Cannot do postprocessing on %s: %s\n", argv[arg],
0601               libraw_strerror(ret));
0602       if (LIBRAW_FATAL_ERROR(ret))
0603         continue;
0604     }
0605     if (use_timing)
0606       timerprint("LibRaw::dcraw_process()", argv[arg]);
0607 
0608     if (!outext)
0609       snprintf(outfn, sizeof(outfn), "%s.%s", argv[arg],
0610                OUT.output_tiff ? "tiff" : (P1.colors > 1 ? "ppm" : "pgm"));
0611     else if (!strcmp(outext, "-"))
0612       snprintf(outfn, sizeof(outfn), "-");
0613     else
0614     {
0615       if (*outext == '.') // append
0616         snprintf(outfn, sizeof(outfn), "%s%s", argv[arg], outext);
0617       else if (strchr(outext, '.') && *outext != '.') // dot is not 1st char
0618         strncpy(outfn, outext, sizeof(outfn));
0619       else
0620       {
0621         strncpy(outfn, argv[arg], sizeof(outfn));
0622         if (strlen(outfn) > 0)
0623         {
0624           char *lastchar = outfn + strlen(outfn); // points to term 0
0625           while (--lastchar > outfn)
0626           {
0627             if (*lastchar == '/' || *lastchar == '\\')
0628               break;
0629             if (*lastchar == '.')
0630             {
0631               *lastchar = 0;
0632               break;
0633             };
0634           }
0635         }
0636         strncat(outfn, ".", sizeof(outfn) - strlen(outfn) - 1);
0637         strncat(outfn, outext, sizeof(outfn) - strlen(outfn) - 1);
0638       }
0639     }
0640 
0641     if (verbosity)
0642     {
0643       printf("Writing file %s\n", outfn);
0644     }
0645 
0646     if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_ppm_tiff_writer(outfn)))
0647       fprintf(stderr, "Cannot write %s: %s\n", outfn, libraw_strerror(ret));
0648     else
0649       done++;
0650 
0651     RawProcessor.recycle(); // just for show this call
0652 
0653     if (use_mmap && mapping.map)
0654         close_mapping(mapping);
0655     else if (use_mem && iobuffer)
0656     {
0657       free(iobuffer);
0658       iobuffer = 0;
0659     }
0660   }
0661 #ifdef USE_DNGSDK
0662   if (dnghost)
0663     delete dnghost;
0664 #endif
0665   if (total == 0)
0666     return 1;
0667   if (done < total)
0668     return 2;
0669   return 0;
0670 }