/* * This file is part of RawTherapee. * * Copyright (c) 2012 Oliver Duis * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include #include "lcp.h" #include "safegtk.h" #include "iccmatrices.h" #include "iccstore.h" #include "rawimagesource.h" #include "improcfun.h" #include "rt_math.h" #ifdef WIN32 #include // for GCC32 #ifndef _WIN32_IE #define _WIN32_IE 0x0600 #endif #include #endif using namespace std; using namespace rtengine; using namespace rtexif; LCPModelCommon::LCPModelCommon() { focLenX=focLenY=-1; imgXCenter=imgYCenter=0.5; x0=y0=fx=fy=meanErr=0; badErr=false; for (int i=0;i<5;i++) param[i]=0; scaleFac=1; } bool LCPModelCommon::empty() const { return param[0]==0 && param[1]==0 && param[2]==0; } void LCPModelCommon::print() const { printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n",focLenX,focLenY,imgXCenter,imgYCenter,scaleFac,meanErr); printf("xy0 %g/%g fxy %g/%g\n",x0,y0,fx,fy); printf("param: %g/%g/%g/%g/%g\n",param[0],param[1],param[2],param[3],param[4]); } // weightened merge two parameters void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) { float facB=1-facA; focLenX = facA * a.focLenX + facB * b.focLenX; focLenY = facA * a.focLenY + facB * b.focLenY; imgXCenter = facA * a.imgXCenter + facB * b.imgXCenter; imgYCenter = facA * a.imgYCenter + facB * b.imgYCenter; scaleFac = facA * a.scaleFac + facB * b.scaleFac; meanErr = facA * a.meanErr + facB * b.meanErr; for (int i=0;i<5;i++) param[i]= facA * a.param[i] + facB * b.param[i]; } void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) { // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter int Dmax=fullWidth; if (fullHeight>fullWidth) Dmax=fullHeight; // correct focLens if (focLenX<0) { // they may not be given // and 35mm may not be given either if (focalLength35mm<1) focalLength35mm = focalLength*sensorFormatFactor; focLenX=focLenY=focalLength / ( 35*focalLength/focalLength35mm); // focLen must be calculated in pixels } if (swapXY) { x0 = (mirrorX ? 1.-imgYCenter : imgYCenter) * fullWidth; y0 = (mirrorY ? 1.-imgXCenter : imgXCenter) * fullHeight; fx = focLenY * Dmax; fy = focLenX * Dmax; } else { x0 = (mirrorX ? 1.-imgXCenter : imgXCenter) * fullWidth; y0 = (mirrorY ? 1.-imgYCenter : imgYCenter) * fullHeight; fx = focLenX * Dmax; fy = focLenY * Dmax; } //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } LCPPersModel::LCPPersModel() { focLen=focDist=aperture=0; } // mode: 0=distortion, 1=vignette, 2=CA bool LCPPersModel::hasModeData(int mode) const { return (mode==0 && !vignette.empty() && !vignette.badErr) || (mode==1 && !base.empty() && !base.badErr) || (mode==2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() && !chromRG.badErr && !chromG.badErr && !chromBG.badErr); } void LCPPersModel::print() const { printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); printf("Base:\n"); base.print(); if (!chromRG.empty()) { printf("ChromRG:\n"); chromRG.print(); } if (!chromG.empty()) { printf("ChromG:\n"); chromG.print(); } if (!chromBG.empty()) { printf("ChromBG:\n"); chromBG.print(); } if (!vignette.empty()) { printf("Vignette:\n"); vignette.print(); } printf("\n"); } // if !vignette then geometric and CA LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) { if (pProf==NULL) return; useCADist=useCADistP; // determine in what the image with the RAW landscape in comparison (calibration target) // in vignetting, the rotation has not taken place yet int rot = 0; if (rawRotationDeg>=0) rot=(coarse.rotate+rawRotationDeg) % 360; swapXY = (rot==90 || rot==270); bool mirrorX = (rot==90 || rot==180); bool mirrorY = (rot==180 || rot==270); //printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); pProf->calcParams(vignette?0:1, focalLength, focusDist, aperture, &mc, NULL, NULL); mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); if (!vignette) { pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); for (int i=0;i<3;i++) chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); } enableCA = !vignette && focusDist>0; } void LCPMapper::correctDistortion(double& x, double& y) const { double xd=(x-mc.x0)/mc.fx, yd=(y-mc.y0)/mc.fy; const float* aDist = mc.param; double rsqr = xd*xd+yd*yd; double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3]; double commonFac = (((aDist[2]*rsqr + aDist[1])*rsqr + aDist[0])*rsqr + 1.) + 2. * (yfac * yd + xfac * xd); double xnew = xd * commonFac + xfac * rsqr; double ynew = yd * commonFac + yfac * rsqr; x = xnew * mc.fx + mc.x0; y = ynew * mc.fy + mc.y0; } void LCPMapper::correctCA(double& x, double& y, int channel) const { if (!enableCA) return; double rsqr, xgreen, ygreen; // First calc the green channel like normal distortion // the other are just deviations from it double xd=(x-chrom[1].x0)/chrom[1].fx, yd=(y-chrom[1].y0)/chrom[1].fy; // Green contains main distortion, just like base if (useCADist) { const float* aDist = chrom[1].param; double rsqr = xd*xd+yd*yd; double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3]; double commonFac = (((aDist[2]*rsqr + aDist[1])*rsqr + aDist[0])*rsqr + 1.) + 2. * (yfac * yd + xfac * xd); xgreen = xd * commonFac + aDist[4] * rsqr; ygreen = yd * commonFac + aDist[3] * rsqr; } else { xgreen=xd; ygreen=yd; } if (channel==1) { // green goes directly x = xgreen * chrom[1].fx + chrom[1].x0; y = ygreen * chrom[1].fy + chrom[1].y0; } else { // others are diffs from green xd=xgreen; yd=ygreen; rsqr=xd*xd+yd*yd; const float* aCA =chrom[channel].param; double xfac=aCA[swapXY?3:4], yfac=aCA[swapXY?4:3]; double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2]*rsqr)) + 2. * (yfac*yd + xfac*xd); x = (chrom[channel].scaleFac * ( xd * commonSum + xfac*rsqr )) * chrom[channel].fx + chrom[channel].x0; y = (chrom[channel].scaleFac * ( yd * commonSum + yfac*rsqr )) * chrom[channel].fy + chrom[channel].y0; } } float LCPMapper::calcVignetteFac(int x, int y) const { // No need for swapXY, since vignette is in RAW and always before rotation double xd=((double)x-mc.x0)/mc.fx, yd=((double)y-mc.y0)/mc.fy; const float* aVig= mc.param; double rsqr = xd*xd+yd*yd; double param0Sqr = aVig[0]*aVig[0]; return 1. + rsqr * (-aVig[0] + rsqr * ((param0Sqr - aVig[1]) - (param0Sqr*aVig[0] - 2.*aVig[0]*aVig[1] + aVig[2]) *rsqr + (param0Sqr*param0Sqr + aVig[1]*aVig[1] + 2.*aVig[0]*aVig[2] - 3.*param0Sqr*aVig[1]) *rsqr*rsqr)); } LCPProfile::LCPProfile(Glib::ustring fname) { const int BufferSize=8192; char buf[BufferSize]; XML_Parser parser = XML_ParserCreate(NULL); if (!parser) throw "Couldn't allocate memory for XML parser"; XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); XML_SetCharacterDataHandler(parser, XmlTextHandler); XML_SetUserData(parser, (void *)this); isFisheye=inCamProfiles=firstLIDone=inPerspect=inAlternateLensID=inAlternateLensNames=false; sensorFormatFactor=1; for (int i=0;ihasModeData(0)) { errVignette+=aPersModel[pm]->vignette.meanErr; vignetteCount++; } if (aPersModel[pm]->hasModeData(1)) { errBase+=aPersModel[pm]->base.meanErr; baseCount++; } if (aPersModel[pm]->hasModeData(2)) { errChrom+=std::max(std::max(aPersModel[pm]->chromRG.meanErr,aPersModel[pm]->chromG.meanErr),aPersModel[pm]->chromBG.meanErr); chromCount++; } } // Only if we have enough frames, filter out errors int filtered=0; if (baseCount+chromCount+vignetteCount>=minFramesLeft) { if (baseCount>0) errBase/=(double)baseCount; if (chromCount>0) errChrom/=(double)chromCount; if (vignetteCount>0) errVignette/=(double)vignetteCount; // Now mark all the bad ones as bad, and hasModeData will return false; for (int pm=0;pmhasModeData(0) && aPersModel[pm]->vignette.meanErr > maxAvgDevFac * errVignette) { aPersModel[pm]->vignette.badErr=true; filtered++; } if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.meanErr > maxAvgDevFac * errBase) { aPersModel[pm]->base.badErr=true; filtered++; } if (aPersModel[pm]->hasModeData(2) && (aPersModel[pm]->chromRG.meanErr > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.meanErr > maxAvgDevFac * errChrom || aPersModel[pm]->chromBG.meanErr > maxAvgDevFac * errChrom)) { aPersModel[pm]->chromRG.badErr=aPersModel[pm]->chromG.badErr=aPersModel[pm]->chromBG.badErr=true; filtered++; } } //printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./(baseCount+chromCount+vignetteCount), maxAvgDevFac, baseCount+chromCount+vignetteCount-filtered); } return filtered; } // mode: 0=vignette, 1=distortion, 2=CA void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const { float euler=exp(1.0); // find the frames with the least distance, focal length wise LCPPersModel *pLow=NULL, *pHigh=NULL; float focalLengthLog=log(focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; float focusDistLog=focusDist>0? log(focusDist)+euler : 0; // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case) for (int pm=0;pmfocLen; if (aPersModel[pm]->hasModeData(mode)) { if (f <= focalLength && (pLow==NULL || f > pLow->focLen || (focusDist==0 && f==pLow->focLen && pLow->focDist>aPersModel[pm]->focDist))) { pLow=aPersModel[pm]; } if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen || (focusDist==0 && f==pHigh->focLen && pHigh->focDistfocDist))) { pHigh=aPersModel[pm]; } } } if (!pLow) pLow=pHigh; else if (!pHigh) pHigh=pLow; else { // Pass 2: We have some, so take the best aperture for vignette and best focus for CA and distortion // there are usually several frame per focal length. In the end pLow will have both flen and apterure/focdis below the target, // and vice versa pHigh float bestFocLenLow=pLow->focLen, bestFocLenHigh=pHigh->focLen; for (int pm=0;pmaperture; // float aperLog=log(aper); float focDist=aPersModel[pm]->focDist; float focDistLog=log(focDist)+euler; double meanErr; if (aPersModel[pm]->hasModeData(mode)) { if (mode==0) { meanErr=aPersModel[pm]->vignette.meanErr; // by aperture (vignette), and max out focus distance // tests showed doing this by log(aperture) is not as advisable if (aPersModel[pm]->focLen==bestFocLenLow && ( (aper==aperture && pLow->vignette.meanErr>meanErr) || (aper>=aperture && aperaperture && pLow->aperture > aperture) || (aper<=aperture && (pLow->aperture>aperture || fabs(aperture-aper)aperture))))) { pLow=aPersModel[pm]; } if (aPersModel[pm]->focLen==bestFocLenHigh && ( (aper==aperture && pHigh->vignette.meanErr>meanErr) || (aper<=aperture && aper>pHigh->aperture && pHigh->aperture < aperture) || (aper>=aperture && (pHigh->apertureaperture))))) { pHigh=aPersModel[pm]; } } else { meanErr = (mode==1 ? aPersModel[pm]->base.meanErr : aPersModel[pm]->chromG.meanErr); if (focusDist>0) { // by focus distance if (aPersModel[pm]->focLen==bestFocLenLow && ( (focDist==focusDist && (mode==1 ? pLow->base.meanErr : pLow->chromG.meanErr)>meanErr) || (focDist>=focusDist && focDistfocDist && pLow->focDist > focusDist) || (focDist<=focusDist && (pLow->focDist>focusDist || fabs(focusDistLog-focDistLog)focDist)+euler)))))) { pLow=aPersModel[pm]; } if (aPersModel[pm]->focLen==bestFocLenHigh && ( (focDist==focusDist && (mode==1 ? pHigh->base.meanErr : pHigh->chromG.meanErr)>meanErr) || (focDist<=focusDist && focDist>pHigh->focDist && pHigh->focDist < focusDist) || (focDist>=focusDist && (pHigh->focDistfocDist)+euler)))))) { pHigh=aPersModel[pm]; } } else { // no focus distance available, just error if (aPersModel[pm]->focLen==bestFocLenLow && (mode==1 ? pLow->base.meanErr : pLow->chromG.meanErr)>meanErr) { pLow=aPersModel[pm]; } if (aPersModel[pm]->focLen==bestFocLenHigh && (mode==1 ? pHigh->base.meanErr : pHigh->chromG.meanErr)>meanErr) { pHigh=aPersModel[pm]; } } } } } } if (pLow!=NULL && pHigh!=NULL) { // average out the factors, linear interpolation in logarithmic scale float facLow=0.5; bool focLenOnSpot=false; // pretty often, since max/min are often as frames in LCP // There is as foclen range, take that as basis if (pLow->focLen < pHigh->focLen) { facLow = (log(pHigh->focLen)-focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen)); } else { focLenOnSpot=pLow->focLen==pHigh->focLen && pLow->focLen==focalLength; } // and average the other factor if available if (mode==0 && pLow->aperture < aperture && pHigh->aperture > aperture) { // Mix in aperture float facAperLow = (pHigh->aperture - aperture) / (pHigh->aperture - pLow->aperture); facLow = focLenOnSpot ? facAperLow : (0.5*facLow + 0.5*facAperLow); } else if (mode!=0 && focusDist>0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) { // focus distance for all else (if focus distance is given) float facDistLow = (log(pHigh->focDist)+euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist)); facLow = focLenOnSpot ? facDistLow : (0.8*facLow + 0.2*facDistLow); } switch (mode) { case 0: // vignette pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); break; case 1: // distortion pCorr1->merge(pLow->base, pHigh->base, facLow); break; case 2: // CA pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); break; } //printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow); } else printf("Error: LCP file contained no %s parameters\n",mode==0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); } void LCPProfile::print() const { printf("=== Profile %s\n", profileName.c_str()); printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n",persModelCount,isRaw,isFisheye,sensorFormatFactor); for (int pm=0;pmprint(); } void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) { LCPProfile *pProf=static_cast(pLCPProfile); bool parseAttr=false; if (*pProf->inInvalidTag) return; // We ignore everything in dirty tag till it's gone // clean up tagname const char* src=strrchr(el,':'); if (src==NULL) src=const_cast(el); else src++; strcpy(pProf->lastTag,src); if (!strcmp("VignetteModelPiecewiseParam",src)) strcpy(pProf->inInvalidTag,src); if (!strcmp("CameraProfiles",src)) pProf->inCamProfiles=true; if (!strcmp("AlternateLensIDs",src)) pProf->inAlternateLensID=true; if (!strcmp("AlternateLensNames",src)) pProf->inAlternateLensNames=true; if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) return; if (!strcmp("li",src)) { pProf->pCurPersModel=new LCPPersModel(); pProf->pCurCommon=&pProf->pCurPersModel->base; // iterated to next tags within persModel return; } if (!strcmp("PerspectiveModel",src)) { pProf->firstLIDone=true; pProf->inPerspect=true; return; } else if (!strcmp("FisheyeModel",src)) { pProf->firstLIDone=true; pProf->inPerspect=true; pProf->isFisheye=true; // just misses third param, and different path, rest is the same return; } else if (!strcmp("Description",src)) parseAttr=true; // Move pointer to general section if (pProf->inPerspect) { if (!strcmp("ChromaticRedGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromRG; parseAttr=true; } else if (!strcmp("ChromaticGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromG; parseAttr=true; } else if (!strcmp("ChromaticBlueGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromBG; parseAttr=true; } else if (!strcmp("VignetteModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->vignette; parseAttr=true; } } // some profiles (espc. Pentax) have a different structure that is attributes based // simulate tags by feeding them in if (parseAttr && attr!=NULL) { for (int i = 0; attr[i]; i += 2) { const char* nameStart=strrchr(attr[i],':'); if (nameStart==NULL) nameStart=const_cast(attr[i]); else nameStart++; strcpy(pProf->lastTag, nameStart); XmlTextHandler(pLCPProfile, attr[i+1], strlen(attr[i+1])); } } } void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len) { LCPProfile *pProf=static_cast(pLCPProfile); if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) return; // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately) bool onlyWhiteSpace=true; int i=0; while (ilastTag; // Common data section if (!pProf->firstLIDone) { // Generic tags are the same for all if (!strcmp("ProfileName",tag)) pProf->profileName=Glib::ustring(raw); else if (!strcmp("Model",tag)) pProf->camera=Glib::ustring(raw); else if (!strcmp("Lens",tag)) pProf->lens=Glib::ustring(raw); else if (!strcmp("CameraPrettyName",tag)) pProf->cameraPrettyName=Glib::ustring(raw); else if (!strcmp("LensPrettyName",tag)) pProf->lensPrettyName=Glib::ustring(raw); else if (!strcmp("CameraRawProfile",tag)) pProf->isRaw=!strcmp("True",raw); } // --- Now all floating points. Must replace local dot characters // WARNING: called by different threads, that may run on different local settings, // so don't use system params if (atof("1,2345")==1.2345) { char* p=raw; while (*p) { if (*p=='.') *p=','; p++; } } if (!pProf->firstLIDone) { if (!strcmp("SensorFormatFactor",tag)) pProf->sensorFormatFactor=atof(raw); } // Perspective model base data if (!strcmp("FocalLength",tag)) pProf->pCurPersModel->focLen=atof(raw); else if (!strcmp("FocusDistance",tag)) { double focDist=atof(raw); pProf->pCurPersModel->focDist=focDist<10000?focDist:10000; } else if (!strcmp("ApertureValue",tag)) pProf->pCurPersModel->aperture=atof(raw); // Section depended if (!strcmp("FocalLengthX",tag)) pProf->pCurCommon->focLenX=atof(raw); else if (!strcmp("FocalLengthY",tag)) pProf->pCurCommon->focLenY=atof(raw); else if (!strcmp("ImageXCenter",tag)) pProf->pCurCommon->imgXCenter=atof(raw); else if (!strcmp("ImageYCenter",tag)) pProf->pCurCommon->imgYCenter=atof(raw); else if (!strcmp("ScaleFactor",tag)) pProf->pCurCommon->scaleFac=atof(raw); else if (!strcmp("ResidualMeanError",tag)) pProf->pCurCommon->meanErr=atof(raw); else if (!strcmp("RadialDistortParam1",tag) || !strcmp("VignetteModelParam1",tag)) pProf->pCurCommon->param[0]=atof(raw); else if (!strcmp("RadialDistortParam2",tag) || !strcmp("VignetteModelParam2",tag)) pProf->pCurCommon->param[1]=atof(raw); else if (!strcmp("RadialDistortParam3",tag) || !strcmp("VignetteModelParam3",tag)) pProf->pCurCommon->param[2]=atof(raw); else if (!strcmp("RadialDistortParam4",tag) || !strcmp("TangentialDistortParam1",tag)) pProf->pCurCommon->param[3]=atof(raw); else if (!strcmp("RadialDistortParam5",tag) || !strcmp("TangentialDistortParam2",tag)) pProf->pCurCommon->param[4]=atof(raw); } void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) { LCPProfile *pProf=static_cast(pLCPProfile); // We ignore everything in dirty tag till it's gone if (*pProf->inInvalidTag) { if (strstr(el,pProf->inInvalidTag)) *pProf->inInvalidTag=0; return; } if (strstr(el,":CameraProfiles")) pProf->inCamProfiles=false; if (strstr(el,":AlternateLensIDs")) pProf->inAlternateLensID=false; if (strstr(el,":AlternateLensNames")) pProf->inAlternateLensNames=false; if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) return; if (strstr(el,":PerspectiveModel") || strstr(el,":FisheyeModel")) pProf->inPerspect=false; else if (strstr(el, ":li")) { pProf->aPersModel[pProf->persModelCount]=pProf->pCurPersModel; pProf->pCurPersModel=NULL; pProf->persModelCount++; } } // Generates as singleton LCPStore* LCPStore::getInstance() { static LCPStore* instance_ = 0; if ( instance_ == 0 ) { static MyMutex smutex_; MyMutex::MyLock lock(smutex_); if ( instance_ == 0 ) { instance_ = new LCPStore(); } } return instance_; } LCPProfile* LCPStore::getProfile (Glib::ustring filename) { if (filename.length()==0 || !isValidLCPFileName(filename)) return NULL; MyMutex::MyLock lock(mtx); std::map::iterator r = profileCache.find (filename); if (r!=profileCache.end()) return r->second; // Add profile (if exists) profileCache[filename]=new LCPProfile(filename); //profileCache[filename]->print(); return profileCache[filename]; } bool LCPStore::isValidLCPFileName(Glib::ustring filename) const { if (!safe_file_test (filename, Glib::FILE_TEST_EXISTS) || safe_file_test (filename, Glib::FILE_TEST_IS_DIR)) return false; size_t pos=filename.find_last_of ('.'); return pos>0 && !filename.casefold().compare (pos, 4, ".lcp"); } Glib::ustring LCPStore::getDefaultCommonDirectory() const { Glib::ustring dir; #ifdef WIN32 WCHAR pathW[MAX_PATH]={0}; char pathA[MAX_PATH]; if (SHGetSpecialFolderPathW(NULL,pathW,CSIDL_COMMON_APPDATA,false)) { char pathA[MAX_PATH]; WideCharToMultiByte(CP_UTF8,0,pathW,-1,pathA,MAX_PATH,0,0); Glib::ustring fullDir=Glib::ustring(pathA)+Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); if (safe_file_test(fullDir, Glib::FILE_TEST_IS_DIR)) dir=fullDir; } #endif // TODO: Add Mac paths here return dir; }