Custom TRC Tone response curve and Illuminant - change Histogram - RGB and Lab values - for output (screen, TIF, JPG..) (#5949)

* Change TRC in the process

* Improve GUI slope

* Add tooltip output profile

* Various change to enable and tooltip

* Chnage rtthumbnail

* Small change tooltip trc

* Another small change tooltip

* Improve GUI - change default TRC BT709 - change tooltip

* Other GUI improvment

* Small changes to BT709 values

* Various change to TRC - add illuminant working profile

* Change labels tooltip TRC illuminant

* Display wp in console if wp provided

* Change tooltip

* Change max wlope

* Init trc + illum + primaries

* Add black point compensation

* Change location abstract

* Fixed bug with rtthumbnail...

* Added tooltip

* Logscale for wslope

* Change tooltip abstract

* Change labels tooltips abstract profile

* Added 6 sliders custom primaries X and Y

* Change GUI custom primaries

* Use custom primaries abstract profiles

* Tooltip with primaries red green blue

* Fixed warning gtk

* Change one tooltip

* Change range custom primaries

* Change GUI default Primaries

* Change one tooltip

* Speedup for trc

* Rendering intent for abstract profile

* Hide intent abstract

* Preserve neutral tones

* Chnage settings preserves neutral tones

* Improve GUI

* Improvment to preserv

* Clean code - speedup TRC when illuminant and primaries default

* Change label

* Change bad default value Blux

* Add forgoten illuminant when selecting primaries

* clean code

* Change labels

* improve workingtrc for LA

* Change default primaries when select default

* Added others working profile to primaries

* Change labels and GUI

* Change tooltip

* CIExy diagram displaying the primaries (interactive) and the white point (#6207)

* Ciexy diagram

* Change to Ciexy graph

* Change graph and defaut coordonates

* Various changes to graph - params

* Disable setListener(ToolPanelListener)

* Add some graduation to graph

* Clean comment code graph

* Change radius 0

* Enable sensitive graph Ciexy

* update Ciexy graph with primaries

* Remove reset button graph

* Change labels and behavior

* First step third spot green

* Second step 3rd spot green

* First try 3 primaries graph Ciexy

* Fixed bad behavior in lab grid

* Fixed another bad behavior graph

* Clean and comment code

* Change default gamma

* More accurate graph Cie xy

* Added white point to Ciexy graph

* Added tooltip Cie xy diagram

* Improvment when illuminant change

* Refine diagram CIE xy - added WP 2000K

* White point D120 + tooltip

* Change label

* Change labels

* Change tooltips

* Improve diagram CIE xy with parabolic

* Other parabolic to improve Cie xy diagram

* Small change GUI

* Added Label CIE xy - change labels

* Change behavior when none - change labels

* Improve gUI and trys to fix LGTM alerts

* CIE xy change set sensitive

* Improve tooltip primaries sliders

* Adapt tooltip to new labels

* Fixed crash and some bad behavior

* First fix bad behavior with some primaries

* Second fixed bad behavior primaries

* Third fixed bad behavior primaries

* Change white point BestRGB

* Change order rgb in history msg Ciexy

* Change tooltip

* Change tooltip

* Improve GUI primaries

* Illuminant 1500K - display in console matrix XYZ-RGB

* Improve GUI illuminant

* Change a tooltip

* Somme changes to GUI and verbose

* Test code for wprim in read

* clean code for wprims

* further cleanups, not tested

* Some cleanups and bugfixes, #5949

* Simplify `std::unique_ptr<>` dereferencing

Also some minor cleanups.

* Some changes suggested by Floessie

* Others change suggested

* Others changes suggested by Floessie

* Forgotten change in procparams.cc

* Added black and white for use with the primaries channel mixer

* Small change behavior GUI - illuminant

* Change pragma omp in iplab2rgb

* Add enums and clean up

* Remove unused code icmpanel.cc

* Fix LGTM alert, #5949

Co-authored-by: Ingo Weyrich <heckflosse67@gmx.de>
Co-authored-by: Flössie <floessie.mail@gmail.com>
This commit is contained in:
Desmis
2021-05-16 06:18:25 +02:00
committed by GitHub
parent 7040aed540
commit 35abd92209
27 changed files with 2751 additions and 309 deletions

View File

@@ -16,18 +16,20 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#include "rtengine.h"
#include "image8.h"
#include "imagefloat.h"
#include "labimage.h"
#include "improcfun.h"
#include <glibmm/ustring.h>
#include "iccstore.h"
#include "iccmatrices.h"
#include "settings.h"
#include "alignedbuffer.h"
#include "color.h"
#include "iccmatrices.h"
#include "iccstore.h"
#include "image8.h"
#include "imagefloat.h"
#include "improcfun.h"
#include "labimage.h"
#include "procparams.h"
#include "rtengine.h"
#include "settings.h"
#include "utils.h"
namespace rtengine
{
@@ -120,6 +122,19 @@ inline void copyAndClamp(const LabImage *src, unsigned char *dst, const double r
} // namespace
float gammalog(float x, float p, float s, float g3, float g4)
{
return x <= g3 ? x * s : (1.f + g4) * xexpf(xlogf(x) / p) - g4;//continuous
}
#ifdef __SSE2__
vfloat gammalog(vfloat x, vfloat p, vfloat s, vfloat g3, vfloat g4)
{
return vself(vmaskf_le(x, g3), x * s, (F2V(1.f) + g4) * xexpf(xlogf(x) / p) - g4);//continuous
}
#endif
// Used in ImProcCoordinator::updatePreviewImage (rtengine/improccoordinator.cc)
// Crop::update (rtengine/dcrop.cc)
// Thumbnail::processImage (rtengine/rtthumbnail.cc)
@@ -373,8 +388,31 @@ Imagefloat* ImProcFunctions::lab2rgbOut(LabImage* lab, int cx, int cy, int cw, i
return image;
}
void ImProcFunctions::preserv(LabImage *nprevl, LabImage *provis, int cw, int ch)
{//avoid too strong in middle values chroma when changing primaries
float pres = 0.01f * params->icm.preser;
float neutral = 2000000000.f;//if a2 + b2 < 200000000 scale 0..100 a and b about : 140 > a & b > -140 decrease effect
float medneutral = 10000000.f;//plein effect 10 > a & b > -10
float aaneu = 1.f / (medneutral - neutral);
float bbneu = - aaneu * neutral;
#ifdef _OPENMP
#pragma omp for schedule(dynamic, 16) nowait
#endif
for (int i = 0; i < ch; ++i)
for (int j = 0; j < cw; ++j) {
float neu = SQR(provis->a[i][j]) + SQR(provis->b[i][j]);
if (neu < medneutral) {//plein effect
nprevl->a[i][j] = intp(pres, provis->a[i][j], nprevl->a[i][j]);
nprevl->b[i][j] = intp(pres, provis->b[i][j], nprevl->b[i][j]);
} else if (neu < neutral) {//decrease effect
float presred = aaneu * neu + bbneu;
nprevl->a[i][j] = intp(pres * presred, provis->a[i][j], nprevl->a[i][j]);
nprevl->b[i][j] = intp(pres * presred, provis->b[i][j], nprevl->b[i][j]);
}
}
}
void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw, int ch, int mul, const Glib::ustring &profile, double gampos, double slpos, cmsHTRANSFORM &transform, bool normalizeIn, bool normalizeOut, bool keepTransForm) const
void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw, int ch, int mul, Glib::ustring &profile, double gampos, double slpos, int &illum, int prim, cmsHTRANSFORM &transform, bool normalizeIn, bool normalizeOut, bool keepTransForm) const
{
const TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile);
@@ -394,6 +432,163 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
}
};
if (profile == "sRGB" || profile == "Adobe RGB" || profile == "ProPhoto" || profile == "WideGamut" || profile == "BruceRGB" || profile == "Beta RGB" || profile == "BestRGB" || profile == "Rec2020" || profile == "ACESp0" || profile == "ACESp1") {
if (settings->verbose) {
printf("Profile=%s\n", profile.c_str());
}
} else {
if (settings->verbose) {
printf("profile not accepted\n");
}
return;
}
if (mul == -5 && gampos == 2.4 && slpos == 12.92310) {//must be change if we change settings RT sRGB
//only in this case we can shortcut..all process..no gamut control..because we reduce...leads to very small differences, but big speedup
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16) if (multiThread)
#endif
for (int i = 0; i < ch; ++i)
for (int j = 0; j < cw; ++j) {
float r = src->r(i, j);
float g = src->g(i, j);
float b = src->b(i, j);
r = (Color::igammatab_srgb[r]) / 65535.f;
g = (Color::igammatab_srgb[g]) / 65535.f;
b = (Color::igammatab_srgb[b]) / 65535.f;
dst->r(i, j) = r;
dst->g(i, j) = g;
dst->b(i, j) = b;
}
return;
}
if (mul == 1 ||(params->icm.wprim == ColorManagementParams::Primaries::DEFAULT && params->icm.will == ColorManagementParams::Illuminant::DEFAULT)) {//shortcut and speedup when no call primaries and illuminant - no gamut control...in this case be carefull
GammaValues g_a; //gamma parameters
double pwr = 1.0 / static_cast<double>(gampos);
Color::calcGamma(pwr, slpos, g_a); // call to calcGamma with selected gamma and slope
#ifdef _OPENMP
# pragma omp parallel for schedule(dynamic,16) if (multiThread)
#endif
for (int y = 0; y < ch; ++y) {
int x = 0;
#ifdef __SSE2__
for (; x < cw - 3; x += 4) {
STVFU(dst->r(y,x), F2V(65536.f) * gammalog(LVFU(src->r(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->g(y,x), F2V(65536.f) * gammalog(LVFU(src->g(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
STVFU(dst->b(y,x), F2V(65536.f) * gammalog(LVFU(src->b(y,x)), F2V(gampos), F2V(slpos), F2V(g_a[3]), F2V(g_a[4])));
}
#endif
for (; x < cw; ++x) {
dst->r(y,x) = 65536.f * gammalog(src->r(y,x), gampos, slpos, g_a[3], g_a[4]);
dst->g(y,x) = 65536.f * gammalog(src->g(y,x), gampos, slpos, g_a[3], g_a[4]);
dst->b(y,x) = 65536.f * gammalog(src->b(y,x), gampos, slpos, g_a[3], g_a[4]);
}
}
return;
}
float redxx = params->icm.redx;
float redyy = params->icm.redy;
float bluxx = params->icm.blux;
float bluyy = params->icm.bluy;
float grexx = params->icm.grex;
float greyy = params->icm.grey;
if (prim == 12) {//convert datas area to xy
float redgraphx = params->icm.labgridcieALow;
float redgraphy = params->icm.labgridcieBLow;
float blugraphx = params->icm.labgridcieAHigh;
float blugraphy = params->icm.labgridcieBHigh;
float gregraphx = params->icm.labgridcieGx;
float gregraphy = params->icm.labgridcieGy;
redxx = 0.55f * (redgraphx + 1.f) - 0.1f;
redxx = rtengine::LIM(redxx, 0.41f, 1.f);//limit values for xy (arbitrary)
redyy = 0.55f * (redgraphy + 1.f) - 0.1f;
redyy = rtengine::LIM(redyy, 0.f, 0.7f);
bluxx = 0.55f * (blugraphx + 1.f) - 0.1f;
bluxx = rtengine::LIM(bluxx, -0.1f, 0.5f);
bluyy = 0.55f * (blugraphy + 1.f) - 0.1f;
bluyy = rtengine::LIM(bluyy, -0.1f, 0.5f);
grexx = 0.55f * (gregraphx + 1.f) - 0.1f;
grexx = rtengine::LIM(grexx, -0.1f, 0.4f);
greyy = 0.55f * (gregraphy + 1.f) - 0.1f;
greyy = rtengine::LIM(greyy, 0.5f, 1.f);
}
switch (ColorManagementParams::Primaries(prim)) {
case ColorManagementParams::Primaries::DEFAULT: {
break;
}
case ColorManagementParams::Primaries::SRGB: {
profile = "sRGB";
break;
}
case ColorManagementParams::Primaries::ADOBE_RGB: {
profile = "Adobe RGB";
break;
}
case ColorManagementParams::Primaries::PRO_PHOTO: {
profile = "ProPhoto";
break;
}
case ColorManagementParams::Primaries::REC2020: {
profile = "Rec2020";
break;
}
case ColorManagementParams::Primaries::ACES_P1: {
profile = "ACESp1";
break;
}
case ColorManagementParams::Primaries::WIDE_GAMUT: {
profile = "WideGamut";
break;
}
case ColorManagementParams::Primaries::ACES_P0: {
profile = "ACESp0";
break;
}
case ColorManagementParams::Primaries::BRUCE_RGB: {
profile = "BruceRGB";
break;
}
case ColorManagementParams::Primaries::BETA_RGB: {
profile = "Beta RGB";
break;
}
case ColorManagementParams::Primaries::BEST_RGB: {
profile = "BestRGB";
break;
}
case ColorManagementParams::Primaries::CUSTOM: {
profile = "Custom";
break;
}
case ColorManagementParams::Primaries::CUSTOM_GRID: {
profile = "Custom";
break;
}
}
if (settings->verbose && prim != 0) {
printf("prim=%i Profile Destination=%s\n", prim, profile.c_str());
}
cmsHTRANSFORM hTransform = nullptr;
if (transform) {
hTransform = transform;
@@ -418,8 +613,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
D60 = 6005 // for ACES AP0 and AP1
};
ColorTemp temp = ColorTemp::D50;
double tempv4 = 5003.;
float p[6]; //primaries
//primaries for 10 working profiles ==> output profiles
@@ -430,6 +624,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.8260;
p[4] = 0.1570;
p[5] = 0.0180;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
} else if (profile == "Adobe RGB") {
p[0] = 0.6400; //Adobe primaries
p[1] = 0.3300;
@@ -437,7 +632,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.7100;
p[4] = 0.1500;
p[5] = 0.0600;
temp = ColorTemp::D65;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
} else if (profile == "sRGB") {
p[0] = 0.6400; // sRGB primaries
p[1] = 0.3300;
@@ -445,7 +641,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.6000;
p[4] = 0.1500;
p[5] = 0.0600;
temp = ColorTemp::D65;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
} else if (profile == "BruceRGB") {
p[0] = 0.6400; // Bruce primaries
p[1] = 0.3300;
@@ -453,14 +650,16 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.6500;
p[4] = 0.1500;
p[5] = 0.0600;
temp = ColorTemp::D65;
} else if (profile == "Beta RGB") {
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
} else if (profile == "Beta RGB") {
p[0] = 0.6888; // Beta primaries
p[1] = 0.3112;
p[2] = 0.1986;
p[3] = 0.7551;
p[4] = 0.1265;
p[5] = 0.0352;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
} else if (profile == "BestRGB") {
p[0] = 0.7347; // Best primaries
p[1] = 0.2653;
@@ -468,6 +667,7 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.7750;
p[4] = 0.1300;
p[5] = 0.0350;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
} else if (profile == "Rec2020") {
p[0] = 0.7080; // Rec2020 primaries
p[1] = 0.2920;
@@ -475,7 +675,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.7970;
p[4] = 0.1310;
p[5] = 0.0460;
temp = ColorTemp::D65;
tempv4 = 6504.;
illum = toUnderlying(ColorManagementParams::Illuminant::D65);
} else if (profile == "ACESp0") {
p[0] = 0.7347; // ACES P0 primaries
p[1] = 0.2653;
@@ -483,7 +684,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 1.0;
p[4] = 0.0001;
p[5] = -0.0770;
temp = ColorTemp::D60;
tempv4 = 6004.;
illum = toUnderlying(ColorManagementParams::Illuminant::D60);
} else if (profile == "ACESp1") {
p[0] = 0.713; // ACES P1 primaries
p[1] = 0.293;
@@ -491,7 +693,8 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.830;
p[4] = 0.128;
p[5] = 0.044;
temp = ColorTemp::D60;
tempv4 = 6004.;
illum = toUnderlying(ColorManagementParams::Illuminant::D60);
} else if (profile == "ProPhoto") {
p[0] = 0.7347; //ProPhoto and default primaries
p[1] = 0.2653;
@@ -499,6 +702,14 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
p[3] = 0.8404;
p[4] = 0.0366;
p[5] = 0.0001;
illum = toUnderlying(ColorManagementParams::Illuminant::D50);
} else if (profile == "Custom") {
p[0] = redxx;
p[1] = redyy;
p[2] = grexx;
p[3] = greyy;
p[4] = bluxx;
p[5] = bluyy;
} else {
p[0] = 0.7347; //default primaries always unused
p[1] = 0.2653;
@@ -528,27 +739,145 @@ void ImProcFunctions::workingtrc(const Imagefloat* src, Imagefloat* dst, int cw,
// 7 parameters for smoother curves
cmsCIExyY xyD;
cmsWhitePointFromTemp(&xyD, (double)temp);
if (profile == "ACESp0") {
xyD = {0.32168, 0.33767, 1.0};//refine white point to avoid differences
Glib::ustring ills = "D50";
switch (ColorManagementParams::Illuminant(illum)) {
case ColorManagementParams::Illuminant::DEFAULT:
case ColorManagementParams::Illuminant::STDA:
case ColorManagementParams::Illuminant::TUNGSTEN_2000K:
case ColorManagementParams::Illuminant::TUNGSTEN_1500K: {
break;
}
case ColorManagementParams::Illuminant::D41: {
tempv4 = 4100.;
ills = "D41";
break;
}
case ColorManagementParams::Illuminant::D50: {
tempv4 = 5003.;
ills = "D50";
break;
}
case ColorManagementParams::Illuminant::D55: {
tempv4 = 5500.;
ills = "D55";
break;
}
case ColorManagementParams::Illuminant::D60: {
tempv4 = 6004.;
ills = "D60";
break;
}
case ColorManagementParams::Illuminant::D65: {
tempv4 = 6504.;
ills = "D65";
break;
}
case ColorManagementParams::Illuminant::D80: {
tempv4 = 8000.;
ills = "D80";
break;
}
case ColorManagementParams::Illuminant::D120: {
tempv4 = 12000.;
ills = "D120";
break;
}
}
cmsWhitePointFromTemp(&xyD, tempv4);
switch (ColorManagementParams::Illuminant(illum)) {
case ColorManagementParams::Illuminant::DEFAULT:
case ColorManagementParams::Illuminant::D55:
case ColorManagementParams::Illuminant::D80: {
break;
}
case ColorManagementParams::Illuminant::D41: {
break;
}
case ColorManagementParams::Illuminant::D50: {
xyD = {0.3457, 0.3585, 1.0}; // near LCMS values but not perfect... it's a compromise!!
break;
}
case ColorManagementParams::Illuminant::D60: {
xyD = {0.32168, 0.33767, 1.0};
break;
}
case ColorManagementParams::Illuminant::D65: {
xyD = {0.312700492, 0.329000939, 1.0};
break;
}
case ColorManagementParams::Illuminant::D120: {
xyD = {0.269669, 0.28078, 1.0};
break;
}
case ColorManagementParams::Illuminant::STDA: {
xyD = {0.447573, 0.407440, 1.0};
ills = "stdA 2875K";
break;
}
case ColorManagementParams::Illuminant::TUNGSTEN_2000K: {
xyD = {0.526591, 0.41331, 1.0};
ills = "Tungsten 2000K";
break;
}
case ColorManagementParams::Illuminant::TUNGSTEN_1500K: {
xyD = {0.585703, 0.393157, 1.0};
ills = "Tungsten 1500K";
break;
}
}
//D41 0.377984 0.381229
//D55 0.332424 0.347426
//D80 0.293755 0.309185
//D75 0.299021 0.314852
cmsToneCurve* GammaTRC[3];
GammaTRC[0] = GammaTRC[1] = GammaTRC[2] = cmsBuildParametricToneCurve(NULL, five, gammaParams);//5 = more smoother than 4
cmsHPROFILE oprofdef = nullptr;
const cmsCIExyYTRIPLE Primaries = {
{p[0], p[1], 1.0}, // red
{p[2], p[3], 1.0}, // green
{p[4], p[5], 1.0} // blue
};
const cmsHPROFILE oprofdef = cmsCreateRGBProfile(&xyD, &Primaries, GammaTRC);
cmsFreeToneCurve(GammaTRC[0]);
oprofdef = cmsCreateRGBProfile(&xyD, &Primaries, GammaTRC);
cmsWriteTag(oprofdef, cmsSigRedTRCTag, GammaTRC[0]);
cmsWriteTag(oprofdef, cmsSigGreenTRCTag, GammaTRC[1]);
cmsWriteTag(oprofdef, cmsSigBlueTRCTag, GammaTRC[2]);
//to read XYZ values and illuminant
if (rtengine::settings->verbose) {
cmsCIEXYZ *redT = static_cast<cmsCIEXYZ*>(cmsReadTag(oprofdef, cmsSigRedMatrixColumnTag));
cmsCIEXYZ *greenT = static_cast<cmsCIEXYZ*>(cmsReadTag(oprofdef, cmsSigGreenMatrixColumnTag));
cmsCIEXYZ *blueT = static_cast<cmsCIEXYZ*>(cmsReadTag(oprofdef, cmsSigBlueMatrixColumnTag));
printf("Illuminant=%s\n", ills.c_str());
printf("rX=%f gX=%f bX=%f\n", redT->X, greenT->X, blueT->X);
printf("rY=%f gY=%f bY=%f\n", redT->Y, greenT->Y, blueT->Y);
printf("rZ=%f gZ=%f bZ=%f\n", redT->Z, greenT->Z, blueT->Z);
}
cmsFreeToneCurve(GammaTRC[0]);
if (oprofdef) {
constexpr cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE;
constexpr cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_GAMUTCHECK;
const cmsHPROFILE iprof = ICCStore::getInstance()->getXYZProfile();
lcmsMutex->lock();
hTransform = cmsCreateTransform(iprof, TYPE_RGB_FLT, oprofdef, TYPE_RGB_FLT, params->icm.outputIntent, flags);
hTransform = cmsCreateTransform(iprof, TYPE_RGB_FLT, oprofdef, TYPE_RGB_FLT, params->icm.aRendIntent, flags);
lcmsMutex->unlock();
}
}