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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-09-25
0007  * Description : a tool to convert RAW file to DNG - Digital Negative creation.
0008  *
0009  * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010-2011 by Jens Mueller <tschenser at gmx dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "dngwriter_p.h"
0017 
0018 // Local includes
0019 
0020 #include "dngwriterhost.h"
0021 
0022 namespace Digikam
0023 {
0024 
0025 int DNGWriter::Private::createNegative(AutoPtr<dng_negative>& negative,
0026                                        DRawInfo* const identify)
0027 {
0028     qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: DNG Negative structure creation";
0029 
0030     negative->SetDefaultScale(dng_urational(outputWidth,  activeWidth),
0031                               dng_urational(outputHeight, activeHeight));
0032 
0033     if (bayerPattern != Private::LinearRaw)
0034     {
0035         qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Bayer Pattern Linear Raw";
0036         negative->SetDefaultCropOrigin(8, 8);
0037         negative->SetDefaultCropSize(activeWidth - 16, activeHeight - 16);
0038     }
0039     else
0040     {
0041         qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Bayer Pattern Type:" << bayerPattern;
0042         negative->SetDefaultCropOrigin(0, 0);
0043         negative->SetDefaultCropSize(activeWidth, activeHeight);
0044     }
0045 
0046     negative->SetActiveArea(activeArea);
0047     negative->SetModelName(identify->model.toLatin1().constData());
0048     negative->SetLocalName(QString::fromUtf8("%1 %2").arg(identify->make, identify->model).toLatin1().constData());
0049     negative->SetOriginalRawFileName(inputInfo.fileName().toLatin1().constData());
0050     negative->SetColorChannels(identify->rawColors);
0051 
0052     ColorKeyCode colorCodes[4] =
0053     {
0054         colorKeyMaxEnum,
0055         colorKeyMaxEnum,
0056         colorKeyMaxEnum,
0057         colorKeyMaxEnum
0058     };
0059 
0060     for (int i = 0 ; i < qMax(4, identify->colorKeys.length()) ; ++i)
0061     {
0062         if      (identify->colorKeys[i] == QLatin1Char('R'))
0063         {
0064             colorCodes[i] = colorKeyRed;
0065         }
0066         else if (identify->colorKeys[i] == QLatin1Char('G'))
0067         {
0068             colorCodes[i] = colorKeyGreen;
0069         }
0070         else if (identify->colorKeys[i] == QLatin1Char('B'))
0071         {
0072             colorCodes[i] = colorKeyBlue;
0073         }
0074         else if (identify->colorKeys[i] == QLatin1Char('C'))
0075         {
0076             colorCodes[i] = colorKeyCyan;
0077         }
0078         else if (identify->colorKeys[i] == QLatin1Char('M'))
0079         {
0080             colorCodes[i] = colorKeyMagenta;
0081         }
0082         else if (identify->colorKeys[i] == QLatin1Char('Y'))
0083         {
0084             colorCodes[i] = colorKeyYellow;
0085         }
0086     }
0087 
0088     negative->SetColorKeys(colorCodes[0], colorCodes[1], colorCodes[2], colorCodes[3]);
0089 
0090     switch (bayerPattern)
0091     {
0092         case Private::Standard:
0093         {
0094             // Standard bayer mosaicing. All work fine there.
0095             // Bayer CCD mask: https://en.wikipedia.org/wiki/Bayer_filter
0096 
0097             negative->SetBayerMosaic(filter);
0098             break;
0099         }
0100 
0101         case Private::Fuji:
0102         {
0103             // TODO: Fuji is special case. Need to setup different bayer rules here.
0104             // It do not work in all settings. Need indeep investiguations.
0105             // Fuji superCCD: https://en.wikipedia.org/wiki/Super_CCD
0106 
0107             negative->SetFujiMosaic(filter);
0108             break;
0109         }
0110 
0111         case Private::Fuji6x6:
0112         {
0113             // TODO: Fuji is special case. Need to setup different bayer rules here.
0114             // It do not work in all settings. Need indeep investiguations.
0115 
0116             negative->SetFujiMosaic6x6(filter);
0117             break;
0118         }
0119 
0120         case Private::FourColor:
0121         {
0122             negative->SetQuadMosaic(filter);
0123             break;
0124         }
0125 
0126         default:
0127         {
0128             break;
0129         }
0130     }
0131 
0132     qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter:" << dngBayerPatternToString(bayerPattern);
0133 
0134     negative->SetWhiteLevel(identify->whitePoint, 0);
0135     negative->SetWhiteLevel(identify->whitePoint, 1);
0136     negative->SetWhiteLevel(identify->whitePoint, 2);
0137     negative->SetWhiteLevel(identify->whitePoint, 3);
0138 
0139     const dng_mosaic_info* const mosaicinfo = negative->GetMosaicInfo();
0140 
0141     if ((mosaicinfo                  != nullptr) &&
0142         (mosaicinfo->fCFAPatternSize == dng_point(2, 2)))
0143     {
0144         negative->SetQuadBlacks(identify->blackPoint + identify->blackPointCh[0],
0145                                 identify->blackPoint + identify->blackPointCh[1],
0146                                 identify->blackPoint + identify->blackPointCh[2],
0147                                 identify->blackPoint + identify->blackPointCh[3]);
0148     }
0149     else
0150     {
0151         negative->SetBlackLevel(identify->blackPoint, 0);
0152     }
0153 
0154     negative->SetBaselineExposure(0.0);
0155     negative->SetBaselineNoise(1.0);
0156     negative->SetBaselineSharpness(1.0);
0157 
0158     dng_orientation orientation;
0159 
0160     switch (identify->orientation)
0161     {
0162         case DRawInfo::ORIENTATION_180:
0163         {
0164             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic orientation: rotate 180";
0165             orientation = dng_orientation::Rotate180();
0166             break;
0167         }
0168 
0169         case DRawInfo::ORIENTATION_Mirror90CCW:
0170         {
0171             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic orientation: mirror 90CCW";
0172             orientation = dng_orientation::Mirror90CCW();
0173             break;
0174         }
0175 
0176         case DRawInfo::ORIENTATION_90CCW:
0177         {
0178             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic orientation: rotate 90CCW";
0179             orientation = dng_orientation::Rotate90CCW();
0180             break;
0181         }
0182 
0183         case DRawInfo::ORIENTATION_90CW:
0184         {
0185             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic orientation: rotate 90CW";
0186             orientation = dng_orientation::Rotate90CW();
0187             break;
0188         }
0189 
0190         default:   // ORIENTATION_NONE
0191         {
0192             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic orientation: no rotation";
0193             orientation = dng_orientation::Normal();
0194             break;
0195         }
0196     }
0197 
0198     negative->SetBaseOrientation(orientation);
0199     negative->SetAntiAliasStrength(dng_urational(100, 100));
0200     negative->SetLinearResponseLimit(1.0);
0201     negative->SetShadowScale( dng_urational(1, 1) );
0202     negative->SetAnalogBalance(dng_vector_3(1.0, 1.0, 1.0));
0203 
0204     // -------------------------------------------------------------------------------
0205 
0206     AutoPtr<dng_camera_profile> prof(new dng_camera_profile);
0207     prof->SetName(QString::fromUtf8("%1 %2").arg(identify->make, identify->model).toLatin1().constData());
0208 
0209     // Set Camera->XYZ Color matrix as profile.
0210 
0211     dng_matrix matrix;
0212 
0213     switch (identify->rawColors)
0214     {
0215         case 3:
0216         {
0217             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic Raw color components: 3";
0218 
0219             dng_matrix_3by3 camXYZ;
0220             camXYZ[0][0] = identify->cameraXYZMatrix[0][0];
0221             camXYZ[0][1] = identify->cameraXYZMatrix[0][1];
0222             camXYZ[0][2] = identify->cameraXYZMatrix[0][2];
0223             camXYZ[1][0] = identify->cameraXYZMatrix[1][0];
0224             camXYZ[1][1] = identify->cameraXYZMatrix[1][1];
0225             camXYZ[1][2] = identify->cameraXYZMatrix[1][2];
0226             camXYZ[2][0] = identify->cameraXYZMatrix[2][0];
0227             camXYZ[2][1] = identify->cameraXYZMatrix[2][1];
0228             camXYZ[2][2] = identify->cameraXYZMatrix[2][2];
0229 
0230             if (camXYZ.MaxEntry() == 0.0)
0231             {
0232                 qCCritical(DIGIKAM_GENERAL_LOG) << "DNGWriter: camera XYZ Matrix is null : camera not supported";
0233                 return FILE_NOT_SUPPORTED;
0234             }
0235 
0236             matrix = camXYZ;
0237 
0238             break;
0239         }
0240 
0241         case 4:
0242         {
0243             qCDebug(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic Raw color components: 3";
0244 
0245             dng_matrix_4by3 camXYZ;
0246             camXYZ[0][0] = identify->cameraXYZMatrix[0][0];
0247             camXYZ[0][1] = identify->cameraXYZMatrix[0][1];
0248             camXYZ[0][2] = identify->cameraXYZMatrix[0][2];
0249             camXYZ[1][0] = identify->cameraXYZMatrix[1][0];
0250             camXYZ[1][1] = identify->cameraXYZMatrix[1][1];
0251             camXYZ[1][2] = identify->cameraXYZMatrix[1][2];
0252             camXYZ[2][0] = identify->cameraXYZMatrix[2][0];
0253             camXYZ[2][1] = identify->cameraXYZMatrix[2][1];
0254             camXYZ[2][2] = identify->cameraXYZMatrix[2][2];
0255             camXYZ[3][0] = identify->cameraXYZMatrix[3][0];
0256             camXYZ[3][1] = identify->cameraXYZMatrix[3][1];
0257             camXYZ[3][2] = identify->cameraXYZMatrix[3][2];
0258 
0259             if (camXYZ.MaxEntry() == 0.0)
0260             {
0261                 qCCritical(DIGIKAM_GENERAL_LOG) << "DNGWriter: camera XYZ Matrix is null : camera not supported";
0262                 return FILE_NOT_SUPPORTED;
0263             }
0264 
0265             matrix = camXYZ;
0266 
0267             break;
0268         }
0269 
0270         default:
0271         {
0272             qCCritical(DIGIKAM_GENERAL_LOG) << "DNGWriter: Mosaic Raw color components not handled:" << identify->rawColors;
0273         }
0274     }
0275 
0276     prof->SetColorMatrix1((dng_matrix) matrix);
0277     prof->SetCalibrationIlluminant1(lsD65);
0278     negative->AddProfile(prof);
0279 
0280     dng_vector camNeutral(identify->rawColors);
0281 
0282     for (int i = 0 ; i < identify->rawColors ; ++i)
0283     {
0284         camNeutral[i] = 1.0 / identify->cameraMult[i];
0285     }
0286 
0287     negative->SetCameraNeutral(camNeutral);
0288 
0289     if (cancel)
0290     {
0291         return PROCESS_CANCELED;
0292     }
0293 
0294     return PROCESS_CONTINUE;
0295 }
0296 
0297 } // namespace Digikam