diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 1daaa9119..f502e61cc 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -48,6 +48,7 @@ #47 19.11.2017 HDR-Dynamikkompression (TooWaBoo) RT 5.3 #48 13.12.2017 Erweiterung (TooWaBoo) RT 5.3 #49 21.12.2017 Lokaler Kontrast (TooWaBoo) RT 5.3 +#50 07.01.2018 Crop Settings (TooWaBoo) RT 5.3 ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen @@ -572,9 +573,9 @@ HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle HISTORY_MSG_283;(Farbanpassungen)\nIntensität HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch -HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode +HISTORY_MSG_285;(Rauschreduzierung)\nMedianfilter - Methode HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen +HISTORY_MSG_287;(Rauschreduzierung)\nMedianfilter - Iterationen HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche HISTORY_MSG_290;(Sensor-Matrix)\nSchwarzpunkt - Rot @@ -1151,7 +1152,7 @@ PREFERENCES_REMEMBERZOOMPAN;Zoom und Bildposition merken PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Öffnen eines neuen Bildes mit den Zoom- und Positionswerten\ndes vorangegangenen Bildes.\n\nFunktioniert nur unter folgenden Bedingungen:\nEin-Reitermodus aktiv\n“Demosaikmethode für 100%-Ansicht“ muss auf “Wie im Bild-\nverarbeitungsprofil vorgegeben“ eingestellt sein. PREFERENCES_RGBDTL_LABEL;Maximale Anzahl Threads für Rauschreduzierung PREFERENCES_RGBDTL_TOOLTIP;Die Rauschreduzierung benötigt mindestens 128MB RAM für ein 10 Megapixel-Bild oder 512MB für ein 40 Megapixel-Bild, und zusätzlich 128MB RAM pro Thread. Je mehr Threads parallel ablaufen, desto schneller ist die Berechnung. Bei Einstellung "0" werden so viele Threads wie möglich benutzt. -PREFERENCES_SAVE_TP_OPEN_NOW;Werkzeugstatus jetzt sichern +PREFERENCES_SAVE_TP_OPEN_NOW;Werkzeugstatus jetzt speichern PREFERENCES_SELECTFONT;Schriftart PREFERENCES_SELECTFONT_COLPICKER;Schriftart für die Farbwähler PREFERENCES_SELECTLANG;Sprache @@ -1513,7 +1514,7 @@ TP_DIRPYRDENOISE_LUMINANCE_CURVE;Luminanzkurve TP_DIRPYRDENOISE_LUMINANCE_DETAIL;Luminanzdetails TP_DIRPYRDENOISE_LUMINANCE_FRAME;Luminanz TP_DIRPYRDENOISE_LUMINANCE_SMOOTHING;Luminanz -TP_DIRPYRDENOISE_MAIN_COLORSPACE;Methode +TP_DIRPYRDENOISE_MAIN_COLORSPACE;Farbraum TP_DIRPYRDENOISE_MAIN_COLORSPACE_LAB;L*a*b* TP_DIRPYRDENOISE_MAIN_COLORSPACE_LABEL;Rauschreduzierung TP_DIRPYRDENOISE_MAIN_COLORSPACE_RGB;RGB @@ -1524,7 +1525,7 @@ TP_DIRPYRDENOISE_MAIN_MODE;Qualität TP_DIRPYRDENOISE_MAIN_MODE_AGGRESSIVE;Hoch TP_DIRPYRDENOISE_MAIN_MODE_CONSERVATIVE;Standard TP_DIRPYRDENOISE_MAIN_MODE_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. -TP_DIRPYRDENOISE_MEDIAN_METHOD;Medianmethode +TP_DIRPYRDENOISE_MEDIAN_METHOD;Methode TP_DIRPYRDENOISE_MEDIAN_METHOD_CHROMINANCE;Nur Farbe TP_DIRPYRDENOISE_MEDIAN_METHOD_LAB;L*a*b* TP_DIRPYRDENOISE_MEDIAN_METHOD_LABEL;Medianfilter @@ -1532,7 +1533,7 @@ TP_DIRPYRDENOISE_MEDIAN_METHOD_LUMINANCE;Nur Luminanz TP_DIRPYRDENOISE_MEDIAN_METHOD_RGB;RGB TP_DIRPYRDENOISE_MEDIAN_METHOD_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MEDIAN_METHOD_WEIGHTED;Gewichtet L* (wenig) + a*b* (normal) -TP_DIRPYRDENOISE_MEDIAN_PASSES;Medianiterationen +TP_DIRPYRDENOISE_MEDIAN_PASSES;Iterationen TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Manchmal führt ein kleines 3×3-Fenster mit\nmehreren Iterationen zu besseren Ergebnissen\nals ein 7×7-Fenster mit nur einer Iteration. TP_DIRPYRDENOISE_MEDIAN_TYPE;Mediantyp TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -2226,25 +2227,25 @@ ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! -!BATCHQUEUE_STARTSTOPHINT;Start or stop processing the images in the queue.\n\nShortcut: Ctrl+s -!GENERAL_SLIDER;Slider -!HISTORY_MSG_173;NR - Detail recovery -!HISTORY_MSG_203;NR - Color space -!HISTORY_MSG_256;NR - Median - Type -!HISTORY_MSG_297;NR - Mode -!HISTORY_MSG_METADATA_MODE;Metadata copy mode -!PREFERENCES_CROP;Crop editing -!PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop area -!PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop -!PREFERENCES_CROP_GUIDES_FRAME;Frame -!PREFERENCES_CROP_GUIDES_FULL;Original -!PREFERENCES_CROP_GUIDES_NONE;None -!PREFERENCES_EDITORCMDLINE;Custom command line -!TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Increase (multiply) the value of all chrominance sliders.\nThis curve lets you adjust the strength of chromatic noise reduction as a function of chromaticity, for instance to increase the action in areas of low saturation and to decrease it in those of high saturation. -!TP_DIRPYRDENOISE_LABEL;Noise Reduction -!TP_METADATA_EDIT;Apply modifications -!TP_METADATA_MODE;Metadata copy mode -!TP_METADATA_STRIP;Strip all metadata -!TP_METADATA_TUNNEL;Copy unchanged -!TP_RAW_PIXELSHIFTONEGREEN;Use one green instead of average -!TP_RAW_PIXELSHIFTONEGREEN_TOOLTIP;Use one green instead of averaging two greens for regions without motion. +BATCHQUEUE_STARTSTOPHINT;Startet / Stoppt die Verarbeitung\nder Warteschlange.\n\nTaste: Strg + s +GENERAL_SLIDER;Regler +HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails +HISTORY_MSG_203;(Rauschreduzierung)\nFarbraum +HISTORY_MSG_256;(Rauschreduzierung)\nMedianfilter - Mediantyp +HISTORY_MSG_297;(Rauschreduzierung)\nQualität +HISTORY_MSG_METADATA_MODE;(Metadaten)\nKopiermodus +PREFERENCES_CROP;Einstellung des Ausschnittswerkzeuges +PREFERENCES_CROP_AUTO_FIT;Automatischer Zoom des Ausschnitts +PREFERENCES_CROP_GUIDES;Hilfslinien anzeigen wenn Ausschnitt nicht verändert wird +PREFERENCES_CROP_GUIDES_FRAME;Rahmen +PREFERENCES_CROP_GUIDES_FULL;Vorgabe des Ausschnittswerkzeuges +PREFERENCES_CROP_GUIDES_NONE;Keine +PREFERENCES_EDITORCMDLINE;Benutzerdefinierte Befehlszeile +TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Erhöht / Reduziert die Intensität der\nChrominanz-Rauschreduzierung in\nAbhängigkeit der Farbsättigung. +TP_DIRPYRDENOISE_LABEL;Rauschreduzierung +TP_METADATA_EDIT;Veränderte Daten +TP_METADATA_MODE;Kopiermodus +TP_METADATA_STRIP;Keine +TP_METADATA_TUNNEL;Unveränderte Daten +TP_RAW_PIXELSHIFTONEGREEN;Benutze ein Grün +TP_RAW_PIXELSHIFTONEGREEN_TOOLTIP;Benutze ein Grün anstelle des\nDurchschnitts beider Grüns für\nBereiche ohne Bewegung. diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 1e196bff8..af7fb00af 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.60 + Version 2.61 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -213,10 +213,6 @@ dialog frame > label { padding-left: 0.91667em; } -#BatchQueueButtons { - margin-top: 0.66667em; -} - frame > label { margin: 0; padding: 0.5em 0; @@ -637,6 +633,11 @@ scale:disabled trough { background-image: none; } +#BatchQueueButtonsMainContainer scale:disabled slider, +#BatchQueueButtonsMainContainer scale:disabled trough { + background-color: shade(@bg-light-grey,.85); +} + /*** end ***************************************************************************************/ /*** Progressbar *******************************************************************************/ @@ -1225,6 +1226,51 @@ popover.background modelbutton:hover { } /** end ****************************************************************************************/ +/*** Switch ***********************************************************************************/ +switch { + min-height: 2.16667em; + min-width: 11em; + margin: 0; + padding: 0; + border-radius: 0.2em; + background-image: none; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; + background-color: @bg-scale-entry; + margin-bottom: 0.5em; +} + +switch slider { + border: 0.08334em solid @bg-entry-border; + background-image: linear-gradient(to bottom, shade (@accent-color2,1.15), shade (@accent-color2,.85)); + border: 0.08334em solid @bg-entry-border; + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); + border-radius: 0.2em 0 0 0.2em; +} +switch:checked slider{ + border-radius: 0 0.2em 0.2em 0; +} + +switch:hover slider { + background-image: linear-gradient(to bottom, shade (@accent-color2,1.20), shade (@accent-color2,.90)); +} + +switch:checked { + background-color: rgb(140,0,20); + color: @headline-big; +} + +switch:disabled:not(:checked) { + box-shadow: none; + background-image: none; + background-color: shade (@bg-light-grey, .85); +} +switch:disabled slider { + background-image: linear-gradient(to bottom, rgba(125,125,125,.4), rgba(60,60,60,.4)); + background-color: shade (@bg-light-grey, .85); +} +/** end ****************************************************************************************/ + /*** Buttons ***********************************************************************************/ button { min-height: 2.16667em; @@ -1355,6 +1401,7 @@ button.MiddleH { /**/ /* Button base format for Toolbox and dialogs */ +#ToolPanelNotebook > stack > box > box > combobox .combo, dialog button, #MyExpander button, #BatchQueueButtonsMainContainer button { @@ -1371,7 +1418,7 @@ combobox .combo, dialog combobox .combo, #ToolPanelNotebook combobox .combo, #BatchQueueButtonsMainContainer combobox .combo { - padding: 0 0.208334em; + padding: 0 0.26667em; } /**/ @@ -1386,6 +1433,7 @@ dialog combobox .combo, #MyExpander combobox + label */ { margin-left: 0.16667em; } + #MyExpander label + * > button:not(.flat).Left, #MyExpander label + combobox:not(:first-child):not(:only-child), #MyExpander label + button:not(.flat):not(spinbutton) { @@ -1455,13 +1503,6 @@ messagedialog .dialog-action-area button:not(:only-child):nth-child(2) { } /**/ -/* Queue */ -#BatchQueueButtons button { - min-height: 2.16667em; - min-width: 10em; -} -/**/ - /* View & Filechooser Buttons */ dialog .view button, window .view button { diff --git a/rtengine/alignedbuffer.h b/rtengine/alignedbuffer.h index 560f0884f..27a376c57 100644 --- a/rtengine/alignedbuffer.h +++ b/rtengine/alignedbuffer.h @@ -18,6 +18,8 @@ */ #ifndef _ALIGNEDBUFFER_ #define _ALIGNEDBUFFER_ + +#include #include #include diff --git a/rtengine/cJSON.c b/rtengine/cJSON.c index 8e9cdcccf..263a45113 100644 --- a/rtengine/cJSON.c +++ b/rtengine/cJSON.c @@ -38,7 +38,8 @@ const char *cJSON_GetErrorPtr(void) {return ep;} static int cJSON_strcasecmp(const char *s1,const char *s2) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + if (!s1) return (s1==s2)?0:1; + if (!s2) return 1; for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } @@ -107,7 +108,7 @@ static const char *parse_number(cJSON *item,const char *num) } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - + item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; @@ -156,12 +157,12 @@ static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; if (*str!='\"') {ep=str;return 0;} /* not a string! */ - + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ if (!out) return 0; - + ptr=str+1;ptr2=out; while (*ptr!='\"' && *ptr) { @@ -190,7 +191,7 @@ static const char *parse_string(cJSON *item,const char *str) } len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - + #if defined( __GNUC__ ) && __GNUC__ >= 7// silence warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" @@ -224,10 +225,10 @@ static const char *parse_string(cJSON *item,const char *str) static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0;unsigned char token; - + if (!str) return cJSON_strdup(""); ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - + out=(char*)cJSON_malloc(len+3); if (!out) return 0; @@ -360,7 +361,7 @@ static char *print_array(cJSON *item,int depth,int fmt) char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; - + /* How many entries in the array? */ while (child) numentries++,child=child->next; /* Explicitly handle numentries==0 */ @@ -383,7 +384,7 @@ static char *print_array(cJSON *item,int depth,int fmt) if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } - + /* If we didn't fail, try to malloc the output string */ if (!fail) out=(char*)cJSON_malloc(len); /* If that fails, we fail. */ @@ -396,7 +397,7 @@ static char *print_array(cJSON *item,int depth,int fmt) cJSON_free(entries); return 0; } - + /* Compose the output array. */ *out='['; ptr=out+1;*ptr=0; @@ -408,7 +409,7 @@ static char *print_array(cJSON *item,int depth,int fmt) } cJSON_free(entries); *ptr++=']';*ptr++=0; - return out; + return out; } /* Build an object from the text. */ @@ -416,11 +417,11 @@ static const char *parse_object(cJSON *item,const char *value) { cJSON *child; if (*value!='{') {ep=value;return 0;} /* not an object! */ - + item->type=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; /* empty array. */ - + item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); @@ -429,7 +430,7 @@ static const char *parse_object(cJSON *item,const char *value) if (*value!=':') {ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; - + while (*value==',') { cJSON *new_item; @@ -442,7 +443,7 @@ static const char *parse_object(cJSON *item,const char *value) value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; } - + if (*value=='}') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } @@ -483,7 +484,7 @@ static char *print_object(cJSON *item,int depth,int fmt) if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } - + /* Try to allocate the output string */ if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; @@ -495,7 +496,7 @@ static char *print_object(cJSON *item,int depth,int fmt) cJSON_free(names);cJSON_free(entries); return 0; } - + /* Compose the output: */ *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0; for (i=0;ichild;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) { + cJSON *c=array->child; + while (c && which>0) c=c->next,which--; + if (!c) return 0; + if (c->prev) c->prev->next=c->next; + if (c->next) c->next->prev=c->prev; + if (c==array->child) array->child=c->next;c->prev=c->next=0; + return c; +} void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} @@ -602,4 +611,4 @@ void cJSON_Minify(char *json) else *into++=*json++; // All other characters. } *into=0; // and null-terminate. -} \ No newline at end of file +} diff --git a/rtengine/color.cc b/rtengine/color.cc index dbabb217b..706d0f36d 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -1786,7 +1786,7 @@ void Color::Lab2XYZ(vfloat L, vfloat a, vfloat b, vfloat &x, vfloat &y, vfloat & void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, const float wp[3][3], int width) { -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat maxvalfv = F2V(MAXVALF); vfloat c116v = F2V(116.f); vfloat c5242d88v = F2V(5242.88f); @@ -1794,7 +1794,7 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, vfloat c200v = F2V(200.f); #endif int i = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for(;i < width - 3; i+=4) { const vfloat rv = LVFU(R[i]); const vfloat gv = LVFU(G[i]); diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 75891c589..eee4f34c0 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -3851,7 +3851,7 @@ float CLASS foveon_avg (short *pix, int range[2], float cfilt) short * CLASS foveon_make_curve (double max, double mul, double filt) { short *curve; - unsigned i, size; + size_t i, size; double x; if (!filt) filt = 0.8; diff --git a/rtengine/image16.cc b/rtengine/image16.cc index 426fc289c..1c023d181 100644 --- a/rtengine/image16.cc +++ b/rtengine/image16.cc @@ -330,50 +330,50 @@ Image16::tofloat() return imgfloat; } -// Parallized transformation; create transform with cmsFLAGS_NOCACHE! -void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) -{ - // LittleCMS cannot parallelize planar Lab float images - // so build temporary buffers to allow multi processor execution -#ifdef _OPENMP - #pragma omp parallel -#endif - { - AlignedBuffer bufferLab(width * 3); - AlignedBuffer bufferRGB(width * 3); +// // Parallized transformation; create transform with cmsFLAGS_NOCACHE! +// void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) +// { +// // LittleCMS cannot parallelize planar Lab float images +// // so build temporary buffers to allow multi processor execution +// #ifdef _OPENMP +// #pragma omp parallel +// #endif +// { +// AlignedBuffer bufferLab(width * 3); +// AlignedBuffer bufferRGB(width * 3); -#ifdef _OPENMP - #pragma omp for schedule(static) -#endif +// #ifdef _OPENMP +// #pragma omp for schedule(static) +// #endif - for (int y = cy; y < cy + height; y++) - { - unsigned short *pRGB, *pR, *pG, *pB; - float *pLab, *pL, *pa, *pb; +// for (int y = cy; y < cy + height; y++) +// { +// unsigned short *pRGB, *pR, *pG, *pB; +// float *pLab, *pL, *pa, *pb; - pLab= bufferLab.data; - pL = labImage.L[y] + cx; - pa = labImage.a[y] + cx; - pb = labImage.b[y] + cx; +// pLab= bufferLab.data; +// pL = labImage.L[y] + cx; +// pa = labImage.a[y] + cx; +// pb = labImage.b[y] + cx; - for (int x = 0; x < width; x++) { - *(pLab++) = *(pL++) / 327.68f; - *(pLab++) = *(pa++) / 327.68f; - *(pLab++) = *(pb++) / 327.68f; - } +// for (int x = 0; x < width; x++) { +// *(pLab++) = *(pL++) / 327.68f; +// *(pLab++) = *(pa++) / 327.68f; +// *(pLab++) = *(pb++) / 327.68f; +// } - cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); +// cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); - pRGB = bufferRGB.data; - pR = r(y - cy); - pG = g(y - cy); - pB = b(y - cy); +// pRGB = bufferRGB.data; +// pR = r(y - cy); +// pG = g(y - cy); +// pB = b(y - cy); - for (int x = 0; x < width; x++) { - *(pR++) = *(pRGB++); - *(pG++) = *(pRGB++); - *(pB++) = *(pRGB++); - } - } // End of parallelization - } -} +// for (int x = 0; x < width; x++) { +// *(pR++) = *(pRGB++); +// *(pG++) = *(pRGB++); +// *(pB++) = *(pRGB++); +// } +// } // End of parallelization +// } +// } diff --git a/rtengine/image16.h b/rtengine/image16.h index 4d74dfbc4..ee402d556 100644 --- a/rtengine/image16.h +++ b/rtengine/image16.h @@ -96,7 +96,7 @@ public: delete this; } - void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); + /* void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); */ }; } diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index d5b17ed80..bfeb85534 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -154,11 +154,25 @@ void Imagefloat::getScanline (int row, unsigned char* buffer, int bps) int ix = 0; float* sbuffer = (float*) buffer; + // agriggio -- assume the image is normalized to [0, 65535] for (int i = 0; i < width; i++) { + sbuffer[ix++] = r(row, i) / 65535.f; + sbuffer[ix++] = g(row, i) / 65535.f; + sbuffer[ix++] = b(row, i) / 65535.f; + } + } else if (bps == 16) { + unsigned short *sbuffer = (unsigned short *)buffer; + for (int i = 0, ix = 0; i < width; i++) { sbuffer[ix++] = r(row, i); sbuffer[ix++] = g(row, i); sbuffer[ix++] = b(row, i); } + } else if (bps == 8) { + for (int i = 0, ix = 0; i < width; i++) { + buffer[ix++] = rtengine::uint16ToUint8Rounded(r(row, i)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(g(row, i)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(b(row, i)); + } } } @@ -516,3 +530,51 @@ void Imagefloat::ExecCMSTransform(cmsHTRANSFORM hTransform) } // End of parallelization } } + +// Parallized transformation; create transform with cmsFLAGS_NOCACHE! +void Imagefloat::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) +{ + // LittleCMS cannot parallelize planar Lab float images + // so build temporary buffers to allow multi processor execution +#ifdef _OPENMP + #pragma omp parallel +#endif + { + AlignedBuffer bufferLab(width * 3); + AlignedBuffer bufferRGB(width * 3); + +#ifdef _OPENMP + #pragma omp for schedule(static) +#endif + + for (int y = cy; y < cy + height; y++) + { + float *pRGB, *pR, *pG, *pB; + float *pLab, *pL, *pa, *pb; + + pLab= bufferLab.data; + pL = labImage.L[y] + cx; + pa = labImage.a[y] + cx; + pb = labImage.b[y] + cx; + + for (int x = 0; x < width; x++) { + *(pLab++) = *(pL++) / 327.68f; + *(pLab++) = *(pa++) / 327.68f; + *(pLab++) = *(pb++) / 327.68f; + } + + cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); + + pRGB = bufferRGB.data; + pR = r(y - cy); + pG = g(y - cy); + pB = b(y - cy); + + for (int x = 0; x < width; x++) { + *(pR++) = *(pRGB++); + *(pG++) = *(pRGB++); + *(pB++) = *(pRGB++); + } + } // End of parallelization + } +} diff --git a/rtengine/imagefloat.h b/rtengine/imagefloat.h index 7348588df..753406d25 100644 --- a/rtengine/imagefloat.h +++ b/rtengine/imagefloat.h @@ -106,6 +106,7 @@ public: void calcCroppedHistogram(const ProcParams ¶ms, float scale, LUTu & hist); void ExecCMSTransform(cmsHTRANSFORM hTransform); + void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); }; } diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index a8fe9d0da..88274cdb4 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -75,15 +75,6 @@ FILE* g_fopen_withBinaryAndLock(const Glib::ustring& fname) return f; } -Glib::ustring to_utf8 (const std::string& str) -{ - try { - return Glib::locale_to_utf8 (str); - } catch (Glib::Error&) { - return Glib::convert_with_fallback (str, "UTF-8", "ISO-8859-1", "?"); - } -} - } Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.", "Error while reading header.", "File reading error", "Image format not supported."}; @@ -136,13 +127,19 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr iptc = iptc_data_new (); + const unsigned char utf8Esc[] = {0x1B, '%', 'G'}; + IptcDataSet * ds = iptc_dataset_new (); + iptc_dataset_set_tag (ds, IPTC_RECORD_OBJECT_ENV, IPTC_TAG_CHARACTER_SET); + iptc_dataset_set_data (ds, utf8Esc, 3, IPTC_DONT_VALIDATE); + iptc_data_add_dataset (iptc, ds); + iptc_dataset_unref (ds); + for (rtengine::procparams::IPTCPairs::const_iterator i = iptcc.begin(); i != iptcc.end(); ++i) { if (i->first == "Keywords" && !(i->second.empty())) { for (unsigned int j = 0; j < i->second.size(); j++) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS); - std::string loc = to_utf8(i->second.at(j)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast(64), loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast(64), i->second.at(j).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -152,8 +149,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr for (unsigned int j = 0; j < i->second.size(); j++) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY); - std::string loc = to_utf8(i->second.at(j)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast(32), loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast(32), i->second.at(j).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -165,8 +161,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr if (i->first == strTags[j].field && !(i->second.empty())) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, strTags[j].tag); - std::string loc = to_utf8(i->second.at(0)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(strTags[j].size, loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(0).c_str(), min(strTags[j].size, i->second.at(0).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -1250,13 +1245,13 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) int bytes = 0; if (!error && (bytes = iptc_jpeg_ps3_save_iptc (nullptr, 0, iptcdata, size, buffer, 65532)) < 0) { - if (iptcdata) { - iptc_data_free_buf (iptc, iptcdata); - } - error = true; } + if (iptcdata) { + iptc_data_free_buf (iptc, iptcdata); + } + if (!error) { jpeg_write_marker(&cinfo, JPEG_APP0 + 13, buffer, bytes); } @@ -1341,189 +1336,198 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) int lineWidth = width * 3 * bps / 8; unsigned char* linebuffer = new unsigned char[lineWidth]; -// TODO the following needs to be looked into - do we really need two ways to write a Tiff file ? - if (exifRoot && uncompressed) { - FILE *file = g_fopen_withBinaryAndLock (fname); + // little hack to get libTiff to use proper byte order (see TIFFClienOpen()): + const char *mode = !exifRoot ? "w" : (exifRoot->getOrder() == rtexif::INTEL ? "wl" : "wb"); +#ifdef WIN32 + FILE *file = g_fopen_withBinaryAndLock (fname); + int fileno = _fileno(file); + int osfileno = _get_osfhandle(fileno); + TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode); +#else + TIFF* out = TIFFOpen(fname.c_str(), mode); + int fileno = TIFFFileno (out); +#endif - if (!file) { - delete [] linebuffer; - return IMIO_CANNOTWRITEFILE; + if (!out) { + delete [] linebuffer; + return IMIO_CANNOTWRITEFILE; + } + + if (pl) { + pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); + pl->setProgress (0.0); + } + + if (exifRoot) { + rtexif::TagDirectory* cl = (const_cast (exifRoot))->clone (nullptr); + + // ------------------ remove some unknown top level tags which produce warnings when opening a tiff (might be useless) ----------------- + + rtexif::Tag *removeTag = cl->getTag (0x9003); + + if (removeTag) { + removeTag->setKeep (false); } - if (pl) { - pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); - pl->setProgress (0.0); + removeTag = cl->getTag (0x9211); + + if (removeTag) { + removeTag->setKeep (false); } - // buffer for the exif and iptc - unsigned int bufferSize; - unsigned char* buffer = nullptr; // buffer will be allocated in createTIFFHeader - unsigned char* iptcdata = nullptr; - unsigned int iptclen = 0; + // ------------------ Apply list of change ----------------- - if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen) && iptcdata) { + for (auto currExifChange : exifChange) { + cl->applyChange (currExifChange.first, currExifChange.second); + } + + rtexif::Tag *tag = cl->getTag (TIFFTAG_EXIFIFD); + + if (tag && tag->isDirectory()) { + rtexif::TagDirectory *exif = tag->getDirectory(); + + if (exif) { + int exif_size = exif->calculateSize(); + unsigned char *buffer = new unsigned char[exif_size + 8]; + // TIFFOpen writes out the header and sets file pointer at position 8 + + exif->write (8, buffer); + + write (fileno, buffer + 8, exif_size); + + delete [] buffer; + // let libtiff know that scanlines or any other following stuff should go + // at a different offset: + TIFFSetWriteOffset (out, exif_size + 8); + TIFFSetField (out, TIFFTAG_EXIFIFD, 8); + } + } + + //TODO Even though we are saving EXIF IFD - MakerNote still comes out screwed. + + if ((tag = cl->getTag (TIFFTAG_MODEL)) != nullptr) { + TIFFSetField (out, TIFFTAG_MODEL, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_MAKE)) != nullptr) { + TIFFSetField (out, TIFFTAG_MAKE, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_DATETIME)) != nullptr) { + TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_ARTIST)) != nullptr) { + TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_COPYRIGHT)) != nullptr) { + TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue()); + } + + delete cl; + } + + unsigned char* iptcdata = nullptr; + unsigned int iptclen = 0; + + if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen)) { + if (iptcdata) { iptc_data_free_buf (iptc, iptcdata); iptcdata = nullptr; } + } - int size = rtexif::ExifManager::createTIFFHeader (exifRoot, exifChange, width, height, bps, profileData, profileLength, (char*)iptcdata, iptclen, buffer, bufferSize); - - if (iptcdata) { - iptc_data_free_buf (iptc, iptcdata); + if (iptcdata) { + rtexif::Tag* iptcTag = new rtexif::Tag (nullptr, rtexif::lookupAttrib (rtexif::ifdAttribs, "IPTCData")); + iptcTag->initLongArray((char*)iptcdata, iptclen); +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::MOTOROLA; +#else + bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::INTEL; +#endif + if (needsReverse) { + unsigned char *ptr = iptcTag->getValue(); + for (int a = 0; a < iptcTag->getCount(); ++a) { + unsigned char cc; + cc = ptr[3]; + ptr[3] = ptr[0]; + ptr[0] = cc; + cc = ptr[2]; + ptr[2] = ptr[1]; + ptr[1] = cc; + ptr += 4; + } } + TIFFSetField (out, TIFFTAG_RICHTIFFIPTC, iptcTag->getCount(), (long*)iptcTag->getValue()); + iptc_data_free_buf (iptc, iptcdata); + } - // The maximum lenght is strangely not the same than for the JPEG file... - // Which maximum length is the good one ? - if (size > 0 && size <= static_cast(bufferSize)) { - fwrite (buffer, size, 1, file); - } + TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION); + TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField (out, TIFFTAG_IMAGELENGTH, height); + TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField (out, TIFFTAG_ROWSPERSTRIP, height); + TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, bps); + TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE); + TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); + + if (!uncompressed) { + TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); + } + + if (profileData) { + TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData); + } #if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - bool needsReverse = bps == 16 && exifRoot->getOrder() == rtexif::MOTOROLA; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; #else - bool needsReverse = bps == 16 && exifRoot->getOrder() == rtexif::INTEL; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; #endif - for (int i = 0; i < height; i++) { - getScanline (i, linebuffer, bps); + for (int row = 0; row < height; row++) { + getScanline (row, linebuffer, bps); - if (needsReverse) + if (needsReverse) { + if (bps == 16) { for (int i = 0; i < lineWidth; i += 2) { char c = linebuffer[i]; linebuffer[i] = linebuffer[i + 1]; linebuffer[i + 1] = c; } - - fwrite (linebuffer, lineWidth, 1, file); - - if (pl && !(i % 100)) { - pl->setProgress ((double)(i + 1) / height); + } else { + for (int i = 0; i < lineWidth; i += 4) { + std::swap(linebuffer[i], linebuffer[i+3]); + std::swap(linebuffer[i+1], linebuffer[i+2]); + } } } - if(buffer) { - delete [] buffer; - } - - if (ferror(file)) { - writeOk = false; - } - - fclose (file); - } else { - // little hack to get libTiff to use proper byte order (see TIFFClienOpen()): - const char *mode = !exifRoot ? "w" : (exifRoot->getOrder() == rtexif::INTEL ? "wl" : "wb"); -#ifdef WIN32 - FILE *file = g_fopen_withBinaryAndLock (fname); - int fileno = _fileno(file); - int osfileno = _get_osfhandle(fileno); - TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode); -#else - TIFF* out = TIFFOpen(fname.c_str(), mode); - int fileno = TIFFFileno (out); -#endif - - if (!out) { + if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) { + TIFFClose (out); delete [] linebuffer; return IMIO_CANNOTWRITEFILE; } - if (pl) { - pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); - pl->setProgress (0.0); + if (pl && !(row % 100)) { + pl->setProgress ((double)(row + 1) / height); } - - if (exifRoot) { - rtexif::Tag *tag = exifRoot->getTag (TIFFTAG_EXIFIFD); - - if (tag && tag->isDirectory()) { - rtexif::TagDirectory *exif = tag->getDirectory(); - - if (exif) { - int exif_size = exif->calculateSize(); - unsigned char *buffer = new unsigned char[exif_size + 8]; - // TIFFOpen writes out the header and sets file pointer at position 8 - - exif->write (8, buffer); - - write (fileno, buffer + 8, exif_size); - - delete [] buffer; - // let libtiff know that scanlines or any other following stuff should go - // at a different offset: - TIFFSetWriteOffset (out, exif_size + 8); - TIFFSetField (out, TIFFTAG_EXIFIFD, 8); - } - } - -//TODO Even though we are saving EXIF IFD - MakerNote still comes out screwed. - - if ((tag = exifRoot->getTag (TIFFTAG_MODEL)) != nullptr) { - TIFFSetField (out, TIFFTAG_MODEL, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_MAKE)) != nullptr) { - TIFFSetField (out, TIFFTAG_MAKE, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_DATETIME)) != nullptr) { - TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_ARTIST)) != nullptr) { - TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_COPYRIGHT)) != nullptr) { - TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue()); - } - - } - - TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION); - TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField (out, TIFFTAG_IMAGELENGTH, height); - TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, 3); - TIFFSetField (out, TIFFTAG_ROWSPERSTRIP, height); - TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, bps); - TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); - TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); - TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE); - - if (!uncompressed) { - TIFFSetField (out, TIFFTAG_PREDICTOR, PREDICTOR_NONE); - } - - if (profileData) { - TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData); - } - - for (int row = 0; row < height; row++) { - getScanline (row, linebuffer, bps); - - if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) { - TIFFClose (out); - delete [] linebuffer; - return IMIO_CANNOTWRITEFILE; - } - - if (pl && !(row % 100)) { - pl->setProgress ((double)(row + 1) / height); - } - } - - if (TIFFFlush(out) != 1) { - writeOk = false; - } - - TIFFClose (out); -#ifdef WIN32 - fclose (file); -#endif } + if (TIFFFlush(out) != 1) { + writeOk = false; + } + + TIFFClose (out); +#ifdef WIN32 + fclose (file); +#endif + delete [] linebuffer; if (pl) { diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 6403612d6..97e356f18 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1274,21 +1274,18 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool } } - Image16* im16 = im->to16(); - delete im; - int imw, imh; double tmpScale = ipf.resizeScale (¶ms, fW, fH, imw, imh); if (tmpScale != 1.0) { - Image16* tempImage = new Image16 (imw, imh); - ipf.resize (im16, tempImage, tmpScale); - delete im16; - im16 = tempImage; + Imagefloat* tempImage = new Imagefloat (imw, imh); + ipf.resize (im, tempImage, tmpScale); + delete im; + im = tempImage; } - im16->saveTIFF (fname, 16, true); - delete im16; + im->saveTIFF (fname, 16, true); + delete im; if (plistener) { plistener->setProgressState (false); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 052f13d08..00c349323 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -54,7 +54,7 @@ using namespace rtengine; // begin of helper function for rgbProc() void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) { -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat cr = F2V(0.299f); vfloat cg = F2V(0.587f); vfloat cb = F2V(0.114f); @@ -62,7 +62,7 @@ void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); @@ -95,14 +95,14 @@ void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize, float exp_scale, float comp, float hlrange) { -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat threev = F2V(3.f); vfloat maxvalfv = F2V(MAXVALF); #endif for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); @@ -159,7 +159,7 @@ void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, // this is a hack to avoid the blue=>black bug (Issue 2141) for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); vfloat gv = LVF(gtemp[ti * tileSize + tj]); @@ -2170,7 +2170,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw const float pow1 = pow_F ( 1.64f - pow_F ( 0.29f, n ), 0.73f ); float nj, nbbj, ncbj, czj, awj, flj; Ciecam02::initcam2float (gamu, yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); +#ifdef __SSE2__ const float reccmcz = 1.f / (c2 * czj); +#endif const float pow1n = pow_F ( 1.64f - pow_F ( 0.29f, nj ), 0.73f ); const float epsil = 0.0001f; @@ -2735,7 +2737,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, J, C, h, xw2, yw2, zw2, - f2, c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); + c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x, y, z; x = xx * 655.35f; y = yy * 655.35f; @@ -2746,10 +2748,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // gamut control in Lab mode; I must study how to do with cIECAM only if (gamu == 1) { - float HH, Lprov1, Chprov1; + float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; - HH = xatan2f (bb, aa); float2 sincosval; if (Chprov1 == 0.0f) { @@ -3079,7 +3080,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, ncie->J_p[i][j], ncie_C_p, ncie->h_p[i][j], xw2, yw2, zw2, - f2, c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); + c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x = (float)xx * 655.35f; float y = (float)yy * 655.35f; float z = (float)zz * 655.35f; @@ -3762,7 +3763,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } else { for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { //brightness/contrast STVF(rtemp[ti * TS + tj], tonecurve(LVF(rtemp[ti * TS + tj]))); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 1adc2fc5f..c1b175ab6 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -239,9 +239,9 @@ public: void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const FramesMetaData *metadata, int rawRotationDeg, bool fullImage); float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh); void lab2monitorRgb (LabImage* lab, Image8* image); - void resize (Image16* src, Image16* dst, float dScale); + void resize (Imagefloat* src, Imagefloat* dst, float dScale); void Lanczos (const LabImage* src, LabImage* dst, float scale); - void Lanczos (const Image16* src, Image16* dst, float scale); + void Lanczos (const Imagefloat* src, Imagefloat* dst, float scale); void deconvsharpening (float** luminance, float** buffer, int W, int H, const SharpeningParams &sharpenParam); void MLsharpen (LabImage* lab);// Manuel's clarity / sharpening @@ -349,7 +349,7 @@ public: void colorToningLabGrid(LabImage *lab); Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm); - Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); + Imagefloat* lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); // CieImage *ciec; bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index abef0a878..ba2fe6ffb 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -262,7 +262,7 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, * If a custom gamma profile can be created, divide by 327.68, convert to xyz and apply the custom gamma transform * otherwise divide by 327.68, convert to xyz and apply the sRGB transform, before converting with gamma2curve */ -Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga) +Imagefloat* ImProcFunctions::lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga) { if (cx < 0) { @@ -281,7 +281,7 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch = lab->H - cy; } - Image16* image = new Image16 (cw, ch); + Imagefloat* image = new Imagefloat (cw, ch); cmsHPROFILE oprof = nullptr; if (ga) { @@ -300,11 +300,12 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int } lcmsMutex->lock (); cmsHPROFILE iprof = cmsCreateLab4Profile(nullptr); - cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, oprof, TYPE_RGB_16, icm.outputIntent, flags); + cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, oprof, TYPE_RGB_FLT, icm.outputIntent, flags); lcmsMutex->unlock (); image->ExecCMSTransform(hTransform, *lab, cx, cy); cmsDeleteTransform(hTransform); + image->normalizeFloatTo65535(); } else { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) @@ -329,9 +330,9 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int Color::xyz2srgb(x_, y_, z_, R, G, B); - image->r(i - cy, j - cx) = (int)Color::gamma2curve[CLIP(R)]; - image->g(i - cy, j - cx) = (int)Color::gamma2curve[CLIP(G)]; - image->b(i - cy, j - cx) = (int)Color::gamma2curve[CLIP(B)]; + image->r(i - cy, j - cx) = Color::gamma2curve[CLIP(R)]; + image->g(i - cy, j - cx) = Color::gamma2curve[CLIP(G)]; + image->b(i - cy, j - cx) = Color::gamma2curve[CLIP(B)]; } } } diff --git a/rtengine/ipresize.cc b/rtengine/ipresize.cc index 644e180c7..1a48e5c43 100644 --- a/rtengine/ipresize.cc +++ b/rtengine/ipresize.cc @@ -46,7 +46,7 @@ static inline float Lanc (float x, float a) } } -void ImProcFunctions::Lanczos (const Image16* src, Image16* dst, float scale) +void ImProcFunctions::Lanczos (const Imagefloat* src, Imagefloat* dst, float scale) { const float delta = 1.0f / scale; @@ -159,9 +159,9 @@ void ImProcFunctions::Lanczos (const Image16* src, Image16* dst, float scale) b += wh[k] * lb[jj]; } - dst->r (i, j) = CLIP (static_cast (r)); - dst->g (i, j) = CLIP (static_cast (g)); - dst->b (i, j) = CLIP (static_cast (b)); + dst->r (i, j) = CLIP (r);//static_cast (r)); + dst->g (i, j) = CLIP (g);//static_cast (g)); + dst->b (i, j) = CLIP (b);//static_cast (b)); } } @@ -396,7 +396,7 @@ float ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, in return (float)dScale; } -void ImProcFunctions::resize (Image16* src, Image16* dst, float dScale) +void ImProcFunctions::resize (Imagefloat* src, Imagefloat* dst, float dScale) { #ifdef PROFILE time_t t1 = clock(); diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 4b8ccf71a..80041e706 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1478,7 +1478,7 @@ bool EPDParams::operator !=(const EPDParams& other) const FattalToneMappingParams::FattalToneMappingParams() : enabled(false), threshold(0), - amount(1) + amount(30) { } diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 4bdfe6f06..787e2e63f 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -25,6 +25,8 @@ constexpr double RT_NAN = std::numeric_limits::quiet_NaN(); constexpr float RT_PI_F = RT_PI; constexpr float RT_PI_F_2 = RT_PI_2; constexpr float RT_PI_F_180 = RT_PI_180; +constexpr float RT_1_PI_F = RT_1_PI; +constexpr float RT_2_PI_F = RT_2_PI; constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN(); diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 8e24b7bb9..873cfd9bc 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -534,7 +534,7 @@ public: * @param errorCode is the error code if an error occured (e.g. the input image could not be loaded etc.) * @param pl is an optional ProgressListener if you want to keep track of the progress * @return the resulting image, with the output profile applied, exif and iptc data set. You have to save it or you can access the pixel data directly. */ -IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = nullptr, bool flush = false); +IImagefloat* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = nullptr, bool flush = false); /** This class is used to control the batch processing. The class implementing this interface will be called when the full processing of an * image is ready and the next job to process is needed. */ @@ -545,7 +545,7 @@ public: * there is no jobs left. * @param img is the result of the last ProcessingJob * @return the next ProcessingJob to process */ - virtual ProcessingJob* imageReady (IImage16* img) = 0; + virtual ProcessingJob* imageReady (IImagefloat* img) = 0; virtual void error (Glib::ustring message) = 0; }; /** This function performs all the image processinf steps corresponding to the given ProcessingJob. It runs in the background, thus it returns immediately, diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 9f79153f7..e74c32a35 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -65,7 +65,7 @@ public: { } - Image16 *operator()() + Imagefloat *operator()() { if (!job->fast) { return normal_pipeline(); @@ -75,7 +75,7 @@ public: } private: - Image16 *normal_pipeline() + Imagefloat *normal_pipeline() { if (!stage_init()) { return nullptr; @@ -86,7 +86,7 @@ private: return stage_finish(); } - Image16 *fast_pipeline() + Imagefloat *fast_pipeline() { if (!job->pparams.resize.enabled) { return normal_pipeline(); @@ -832,7 +832,7 @@ private: } } - Image16 *stage_finish() + Imagefloat *stage_finish() { procparams::ProcParams& params = job->pparams; //ImProcFunctions ipf (¶ms, true); @@ -1228,7 +1228,7 @@ private: } } - Image16* readyImg = nullptr; + Imagefloat* readyImg = nullptr; cmsHPROFILE jprof = nullptr; bool customGamma = false; bool useLCMS = false; @@ -1238,7 +1238,7 @@ private: GammaValues ga; // if(params.blackwhite.enabled) params.toneCurve.hrenabled=false; - readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm, &ga); + readyImg = ipf.lab2rgbOut (labView, cx, cy, cw, ch, params.icm, &ga); customGamma = true; //or selected Free gamma @@ -1252,7 +1252,7 @@ private: // if Default gamma mode: we use the profile selected in the "Output profile" combobox; // gamma come from the selected profile, otherwise it comes from "Free gamma" tool - readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm); + readyImg = ipf.lab2rgbOut (labView, cx, cy, cw, ch, params.icm); if (settings->verbose) { printf ("Output profile_: \"%s\"\n", params.icm.output.c_str()); @@ -1282,7 +1282,7 @@ private: } if (tmpScale != 1.0 && params.resize.method == "Nearest") { // resize rgb data (gamma applied) - Image16* tempImage = new Image16 (imw, imh); + Imagefloat* tempImage = new Imagefloat (imw, imh); ipf.resize (readyImg, tempImage, tmpScale); delete readyImg; readyImg = tempImage; @@ -1568,7 +1568,7 @@ private: } // namespace -IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool flush) +IImagefloat* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool flush) { ImageProcessor proc (pjob, errorCode, pl, flush); return proc(); @@ -1581,7 +1581,7 @@ void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl) while (currentJob) { int errorCode; - IImage16* img = processImage (currentJob, errorCode, bpl, true); + IImagefloat* img = processImage (currentJob, errorCode, bpl, true); if (errorCode) { bpl->error (M ("MAIN_MSG_CANNOTLOAD")); diff --git a/rtengine/sleef.c b/rtengine/sleef.c index 17dfccc0f..f03c9f1b3 100644 --- a/rtengine/sleef.c +++ b/rtengine/sleef.c @@ -923,9 +923,8 @@ __inline float mulsignf(float x, float y) { return intBitsToFloat(floatToRawIntBits(x) ^ (floatToRawIntBits(y) & (1 << 31))); } -__inline float signf(float d) { return mulsignf(1, d); } +__inline float signf(float d) { return copysign(1, d); } __inline float mlaf(float x, float y, float z) { return x * y + z; } -__inline float xrintf(float x) { return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f); } __inline int xisnanf(float x) { return x != x; } __inline int xisinff(float x) { return x == rtengine::RT_INFINITY_F || x == -rtengine::RT_INFINITY_F; } @@ -984,7 +983,7 @@ __inline float xsinf(float d) { int q; float u, s; - q = (int)xrintf(d * (float)rtengine::RT_1_PI); + q = rint(d * rtengine::RT_1_PI_F); d = mlaf(q, -PI4_Af*4, d); d = mlaf(q, -PI4_Bf*4, d); @@ -1009,7 +1008,7 @@ __inline float xcosf(float d) { int q; float u, s; - q = 1 + 2*(int)xrintf(d * (float)rtengine::RT_1_PI - 0.5f); + q = 1 + 2*rint(d * rtengine::RT_1_PI_F - 0.5f); d = mlaf(q, -PI4_Af*2, d); d = mlaf(q, -PI4_Bf*2, d); @@ -1035,7 +1034,7 @@ __inline float2 xsincosf(float d) { float u, s, t; float2 r; - q = (int)rint(d * ((float)(2 * rtengine::RT_1_PI))); + q = rint(d * rtengine::RT_2_PI_F); s = d; @@ -1076,7 +1075,7 @@ __inline float xtanf(float d) { int q; float u, s, x; - q = (int)xrintf(d * (float)(2 * rtengine::RT_1_PI)); + q = rint(d * (float)(2 * rtengine::RT_1_PI)); x = d; @@ -1202,7 +1201,7 @@ __inline float xlogf(float d) { __inline float xexpf(float d) { if(d<=-104.0f) return 0.0f; - int q = (int)xrintf(d * R_LN2f); + int q = rint(d * R_LN2f); float s, u; s = mlaf(q, -L2Uf, d); diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index 645451905..7ef490807 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1121,15 +1121,37 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) const float min_luminance = 1.f; TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working); -#ifdef _OPENMP - #pragma omp parallel for if (multiThread) -#endif + float max_Y = 0.f; + int max_x = 0, max_y = 0; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif +{ + float max_YThr = 0.f; + int max_xThr = 0, max_yThr = 0; +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) nowait +#endif for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { Yr (x, y) = std::max (luminance (rgb->r (y, x), rgb->g (y, x), rgb->b (y, x), ws), min_luminance); // clip really black pixels + if (Yr(x, y) > max_YThr) { + max_YThr = Yr(x, y); + max_xThr = x; + max_yThr = y; + } } } +#ifdef _OPENMP +#pragma omp critical +#endif + if (max_YThr > max_Y) { + max_Y = max_YThr; + max_x = max_xThr; + max_y = max_yThr; + } +} // median filter on the deep shadows, to avoid boosting noise // because w2 >= w and h2 >= h, we can use the L buffer as temporary buffer for Median_Denoise() @@ -1170,9 +1192,11 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); + + const float scale = 65535.f / std::max(L(max_x * wr + 1, max_y * hr + 1), epsilon) * (65535.f / Yr(max_x, max_y)); #ifdef _OPENMP - #pragma omp parallel for if(multiThread) + #pragma omp parallel for schedule(dynamic,16) if(multiThread) #endif for (int y = 0; y < h; y++) { int yy = y * hr + 1; @@ -1181,7 +1205,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) int xx = x * wr + 1; float Y = Yr (x, y); - float l = std::max (L (xx, yy), epsilon) * (65535.f / Y); + float l = std::max (L (xx, yy), epsilon) * (scale / Y); rgb->r (y, x) = std::max (rgb->r (y, x), 0.f) * l; rgb->g (y, x) = std::max (rgb->g (y, x), 0.f) * l; rgb->b (y, x) = std::max (rgb->b (y, x), 0.f) * l; diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index a90ec18b5..6cb1e7ff8 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -739,11 +739,12 @@ void TagDirectory::applyChange (std::string name, Glib::ustring value) } else { const TagAttrib* attrib = nullptr; - for (int i = 0; attribs[i].ignore != -1; i++) + for (int i = 0; attribs[i].ignore != -1; i++) { if (!strcmp (attribs[i].name, fseg.c_str())) { attrib = &attribs[i]; break; } + } if (attrib) { Tag* nt = new Tag (this, attrib); @@ -1663,15 +1664,11 @@ void Tag::toString (char* buffer, int ofs) const return; } - size_t maxcount = 4; - - if (count < 4) { - maxcount = count; - } + size_t maxcount = rtengine::min(count, 10); strcpy (buffer, ""); - for (ssize_t i = 0; i < std::min(maxcount, valuesize - ofs); i++) { + for (ssize_t i = 0; i < rtengine::min(maxcount, valuesize - ofs); i++) { if (i > 0) { strcat (buffer, ", "); } @@ -1931,22 +1928,48 @@ void Tag::initInt (int data, TagType t, int cnt) setInt (data, 0, t); } +void Tag::swapByteOrder2(char *buffer, int count) +{ + char* ptr = buffer; + for (int i = 0; i < count; i+=2) { + unsigned char c = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = c; + ptr += 2; + } +} void Tag::initUserComment (const Glib::ustring &text) { + const bool useBOM = false; // set it to true if you want to output BOM in UCS-2/UTF-8 UserComments ; this could be turned to an options entry type = UNDEFINED; if (text.is_ascii()) { - count = 8 + strlen (text.c_str()); - valuesize = count; + valuesize = count = 8 + strlen (text.c_str()); value = new unsigned char[valuesize]; - memcpy((char*)value, "ASCII\0\0\0", 8); - memcpy((char*)value + 8, text.c_str(), valuesize - 8); + memcpy(value, "ASCII\0\0\0", 8); + memcpy(value + 8, text.c_str(), valuesize - 8); } else { - wchar_t *commentStr = (wchar_t*)g_utf8_to_utf16 (text.c_str(), -1, NULL, NULL, NULL); - count = 8 + wcslen(commentStr)*2; - valuesize = count; - value = (unsigned char*)new char[valuesize]; - memcpy((char*)value, "UNICODE\0", 8); - memcpy((char*)value + 8, (char*)commentStr, valuesize - 8); + wchar_t *commentStr = (wchar_t*)g_utf8_to_utf16 (text.c_str(), -1, nullptr, nullptr, nullptr); + size_t wcStrSize = wcslen(commentStr); + valuesize = count = wcStrSize * 2 + 8 + (useBOM ? 2 : 0); + value = new unsigned char[valuesize]; + memcpy(value, "UNICODE\0", 8); + + if (useBOM) { + if (getOrder() == INTEL) { //Little Endian + value[8] = 0xFF; + value[9] = 0xFE; + } else { + value[8] = 0xFE; + value[9] = 0xFF; + } + } + + // Swapping byte order to match the Exif's byte order + if (getOrder() != HOSTORDER) { + swapByteOrder2((char*)commentStr, wcStrSize * 2); + } + + memcpy(value + 8 + (useBOM ? 2 : 0), (char*)commentStr, wcStrSize * 2); g_free(commentStr); } } @@ -3197,120 +3220,6 @@ int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::pro return size + 6; } -int ExifManager::createTIFFHeader (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, int bps, const char* profiledata, int profilelen, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize) -{ - -// write tiff header - int offs = 0; - ByteOrder order = HOSTORDER; - - if (root) { - order = root->getOrder (); - } - - TagDirectory* cl; - - if (root) { - cl = (const_cast (root))->clone (nullptr); - // remove some unknown top level tags which produce warnings when opening a tiff - Tag *removeTag = cl->getTag (0x9003); - - if (removeTag) { - removeTag->setKeep (false); - } - - removeTag = cl->getTag (0x9211); - - if (removeTag) { - removeTag->setKeep (false); - } - } else { - cl = new TagDirectory (nullptr, ifdAttribs, HOSTORDER); - } - -// add tiff strip data - int rps = 8; - int strips = ceil ((double)H / rps); - cl->replaceTag (new Tag (cl, lookupAttrib (ifdAttribs, "RowsPerStrip"), rps, LONG)); - Tag* stripBC = new Tag (cl, lookupAttrib (ifdAttribs, "StripByteCounts")); - stripBC->initInt (0, LONG, strips); - cl->replaceTag (stripBC); - Tag* stripOffs = new Tag (cl, lookupAttrib (ifdAttribs, "StripOffsets")); - stripOffs->initInt (0, LONG, strips); - cl->replaceTag (stripOffs); - - for (int i = 0; i < strips - 1; i++) { - stripBC->setInt (rps * W * 3 * bps / 8, i * 4); - } - - int remaining = (H - rps * floor ((double)H / rps)) * W * 3 * bps / 8; - - if (remaining) { - stripBC->setInt (remaining, (strips - 1) * 4); - } else { - stripBC->setInt (rps * W * 3 * bps / 8, (strips - 1) * 4); - } - - if (profiledata) { - Tag* icc = new Tag (cl, lookupAttrib (ifdAttribs, "ICCProfile")); - icc->initUndefArray (profiledata, profilelen); - cl->replaceTag (icc); - } - - if (iptcdata) { - Tag* iptc = new Tag (cl, lookupAttrib (ifdAttribs, "IPTCData")); - iptc->initLongArray (iptcdata, iptclen); - cl->replaceTag (iptc); - } - -// apply list of changes - for (rtengine::procparams::ExifPairs::const_iterator i = changeList.begin(); i != changeList.end(); ++i) { - cl->applyChange (i->first, i->second); - } - - // append default properties - const std::vector defTags = getDefaultTIFFTags (cl); - - defTags[0]->setInt (W, 0, LONG); - defTags[1]->setInt (H, 0, LONG); - defTags[8]->initInt (0, SHORT, 3); - - for (int i = 0; i < 3; i++) { - defTags[8]->setInt (bps, i * 2, SHORT); - } - - for (int i = defTags.size() - 1; i >= 0; i--) { - Tag* defTag = defTags[i]; - cl->replaceTag (defTag->clone (cl)); - delete defTag; - } - -// calculate strip offsets - int size = cl->calculateSize (); - int byps = bps / 8; - - for (int i = 0; i < strips; i++) { - stripOffs->setInt (size + 8 + i * rps * W * 3 * byps, i * 4); - } - - cl->sort (); - bufferSize = cl->calculateSize() + 8; - buffer = new unsigned char[bufferSize]; // this has to be deleted in caller - sset2 ((unsigned short)order, buffer + offs, order); - offs += 2; - sset2 (42, buffer + offs, order); - offs += 2; - sset4 (8, buffer + offs, order); - - int endOffs = cl->write (8, buffer); - -// cl->printAll(); - delete cl; - - return endOffs; -} - - int ExifManager::createPNGMarker(const TagDirectory* root, const rtengine::procparams::ExifPairs &changeList, int W, int H, int bps, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize) { // write tiff header diff --git a/rtexif/rtexif.h b/rtexif/rtexif.h index 822407389..33419fe71 100644 --- a/rtexif/rtexif.h +++ b/rtexif/rtexif.h @@ -236,6 +236,8 @@ public: void initLongArray (const char* data, int len); void initRational (int num, int den); + static void swapByteOrder2 (char *buffer, int count); + // get basic tag properties int getID () const { diff --git a/rtexif/stdattribs.cc b/rtexif/stdattribs.cc index a7e3fe00f..ca19b7f1c 100644 --- a/rtexif/stdattribs.cc +++ b/rtexif/stdattribs.cc @@ -452,12 +452,109 @@ public: } count = std::min (count, 65535); // limit to 65535 chars to avoid crashes in case of corrupted metadata - char *buffer = new char[count - 7]; + char *buffer = new char[count - 6]; // include 2 ending null chars for UCS-2 string (possibly) + char *value = (char*)t->getValue(); - if (!memcmp ((char*)t->getValue(), "ASCII\0\0\0", 8)) { - strncpy (buffer, (char*)t->getValue() + 8, count - 8); + if (!memcmp(value, "ASCII\0\0\0", 8)) { + memcpy(buffer, value + 8, count - 8); buffer[count - 8] = '\0'; + } else if (!memcmp(value, "UNICODE\0", 8)) { + memcpy(buffer, value + 8, count - 8); + buffer[count - 7] = buffer[count - 8] = '\0'; + Glib::ustring tmp1(buffer); + + + bool hasBOM = false; + enum ByteOrder bo = UNKNOWN; + if (count % 2 || (count >= 11 && (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF))) { + // odd string length can only be UTF-8, don't change anything + std::string retVal (buffer + 3); + delete [] buffer; + return retVal; + } else if (count >= 10) { + if (buffer[0] == 0xFF && buffer[1] == 0xFE) { + bo = INTEL; // little endian + hasBOM = true; + } else if (buffer[0] == 0xFE && buffer[1] == 0xFF) { + bo = MOTOROLA; // big endian + hasBOM = true; + } + } + if (bo == UNKNOWN) { + // auto-detecting byte order; we still don't know if it's UCS-2 or UTF-8 + int a = 0, b = 0, c = 0, d = 0; + for (int j = 8; j < count; j++) { + char cc = value[j]; + if (!(j%2)) { + // counting zeros for first byte + if (!cc) { + ++a; + } + } else { + // counting zeros for second byte + if (!cc) { + ++b; + } + } + if (!(cc & 0x80) || ((cc & 0xC0) == 0xC0) || ((cc & 0xC0) == 0x80)) { + ++c; + } + if ((cc & 0xC0) == 0x80) { + ++d; + } + } + if (c == (count - 8) && d) { + // this is an UTF-8 string + std::string retVal (buffer); + delete [] buffer; + return retVal; + } + if ((a || b) && a != b) { + bo = a > b ? MOTOROLA : INTEL; + } + } + if (bo == UNKNOWN) { + // assuming platform's byte order +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + bo = INTEL; +#else + bo = MOTOROLA; +#endif + } + + // now swapping if necessary + if (!hasBOM && bo != HOSTORDER) { + if (t->getOrder() != HOSTORDER) { + Tag::swapByteOrder2(buffer, count - 8); + } + } + + glong written; + char* utf8Str = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, &written, nullptr); + delete [] buffer; + buffer = new char[written + 1]; + memcpy(buffer, utf8Str, written); + buffer[written] = 0; + } else if (!memcmp(value, "\0\0\0\0\0\0\0\0", 8)) { + // local charset string, whatever it is + memcpy(buffer, value + 8, count - 8); + buffer[count - 7] = buffer[count - 8] = '\0'; + + gsize written = 0; + char *utf8Str = g_locale_to_utf8(buffer, count - 8, nullptr, &written, nullptr); + if (utf8Str && written) { + delete [] buffer; + size_t length = strlen(utf8Str); + buffer = new char[length + 1]; + strcpy(buffer, utf8Str); + } else { + buffer[0] = 0; + } + if (utf8Str) { + g_free(utf8Str); + } } else { + // JIS: unsupported buffer[0] = 0; } @@ -467,11 +564,8 @@ public: } virtual void fromString (Tag* t, const std::string& value) { - char *buffer = new char[t->getCount()]; - memcpy (buffer, "ASCII\0\0\0", 8); - strcpy (buffer + 8, value.c_str()); - t->fromString (buffer, value.size() + 9); - delete [] buffer; + Glib::ustring tmpStr(value); + t->userCommentFromString (tmpStr); } }; UserCommentInterpreter userCommentInterpreter; @@ -809,10 +903,10 @@ const TagAttrib ifdAttribs[] = { {0, AC_WRITE, 0, nullptr, 0x828e, AUTO, "CFAPattern", &cfaInterpreter}, {0, AC_WRITE, 0, kodakIfdAttribs, 0x8290, AUTO, "KodakIFD", &stdInterpreter}, {0, AC_WRITE, 1, nullptr, 0x8298, AUTO, "Copyright", &stdInterpreter}, + {0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter}, {0, AC_DONTWRITE, 0, nullptr, 0x8606, AUTO, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format {0, AC_WRITE, 0, exifAttribs, 0x8769, AUTO, "Exif", &stdInterpreter}, {0, AC_SYSTEM, 0, nullptr, 0x8773, AUTO, "ICCProfile", &stdInterpreter}, - {0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter}, {0, AC_WRITE, 0, gpsAttribs, 0x8825, AUTO, "GPSInfo", &stdInterpreter}, {0, AC_WRITE, 0, nullptr, 0x9003, AUTO, "DateTimeOriginal", &stdInterpreter}, {0, AC_WRITE, 0, nullptr, 0x9004, AUTO, "DateTimeDigitized", &stdInterpreter}, diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 6b04ed0ae..2f3505a08 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -373,7 +373,7 @@ Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring &filenam { timeval tv; gettimeofday(&tv, nullptr); - char mseconds[4]; + char mseconds[11]; sprintf(mseconds, "%d", (int)(tv.tv_usec / 1000)); time_t rawtime; struct tm *timeinfo; @@ -578,7 +578,7 @@ void BatchQueue::startProcessing () } } -rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) +rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImagefloat* img) { // save image img diff --git a/rtgui/batchqueue.h b/rtgui/batchqueue.h index 68373838e..0d91542b6 100644 --- a/rtgui/batchqueue.h +++ b/rtgui/batchqueue.h @@ -62,7 +62,7 @@ public: return (!fd.empty()); } - rtengine::ProcessingJob* imageReady (rtengine::IImage16* img); + rtengine::ProcessingJob* imageReady (rtengine::IImagefloat* img); void error (Glib::ustring msg); void setProgress (double p); void rightClicked (ThumbBrowserEntryBase* entry); diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index e9964073a..6e13cd2a5 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -1772,9 +1772,9 @@ void EditorPanel::procParamsChanged (Thumbnail* thm, int whoChangedIt) } } -bool EditorPanel::idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) +bool EditorPanel::idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) { - rtengine::IImage16* img = pc->returnValue(); + rtengine::IImagefloat* img = pc->returnValue(); delete pc; if ( img ) { @@ -1785,13 +1785,13 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, Gl img->setSaveProgressListener (parent->getProgressListener()); if (sf.format == "tif") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsTIFF), fname, sf.tiffBits, sf.tiffUncompressed), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fname, sf.tiffBits, sf.tiffUncompressed), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else if (sf.format == "png") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsPNG), fname, sf.pngBits), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsPNG), fname, sf.pngBits), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else if (sf.format == "jpg") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else { delete ld; @@ -1812,7 +1812,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, Gl return false; } -bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) +bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) { img->free (); @@ -1937,7 +1937,7 @@ void EditorPanel::saveAsPressed () ipc->getParams (&pparams); rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); - ProgressConnector *ld = new ProgressConnector(); + ProgressConnector *ld = new ProgressConnector(); ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_saveImage ), ld, fnameOut, sf, pparams)); saveimgas->set_sensitive (false); @@ -1981,7 +1981,7 @@ void EditorPanel::sendToGimpPressed () rtengine::procparams::ProcParams pparams; ipc->getParams (&pparams); rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); - ProgressConnector *ld = new ProgressConnector(); + ProgressConnector *ld = new ProgressConnector(); ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_sendToGimp ), ld, openThm->getFileName() )); saveimgas->set_sensitive (false); @@ -1996,7 +1996,7 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm rtengine::ProcessingJob *job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); // save immediately - rtengine::IImage16 *img = rtengine::processImage (job, err, nullptr, false); + rtengine::IImagefloat *img = rtengine::processImage (job, err, nullptr, false); int err = 0; @@ -2042,10 +2042,10 @@ void EditorPanel::histogramProfile_toggled() colorMgmtToolBar->updateHistogram(); } -bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname) +bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname) { - rtengine::IImage16* img = pc->returnValue(); + rtengine::IImagefloat* img = pc->returnValue(); delete pc; if (img) { @@ -2077,7 +2077,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, ProgressConnector *ld = new ProgressConnector(); img->setSaveProgressListener (parent->getProgressListener()); - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsTIFF), fileName, sf.tiffBits, sf.tiffUncompressed), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fileName, sf.tiffBits, sf.tiffUncompressed), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_sentToGimp), ld, img, fileName)); } else { Glib::ustring msg_ = Glib::ustring (" Error during image processing\n"); @@ -2090,7 +2090,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, return false; } -bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring filename) +bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring filename) { img->free (); int errore = pc->returnValue(); diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index 0249e77ca..7876d18e1 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -146,10 +146,10 @@ private: void close (); BatchQueueEntry* createBatchQueueEntry (); - bool idle_imageSaved (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); - bool idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); - bool idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname); - bool idle_sentToGimp (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring filename); + bool idle_imageSaved (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); + bool idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); + bool idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname); + bool idle_sentToGimp (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring filename); void histogramProfile_toggled (); diff --git a/rtgui/fattaltonemap.cc b/rtgui/fattaltonemap.cc index 10dba51cf..a6f4c5190 100644 --- a/rtgui/fattaltonemap.cc +++ b/rtgui/fattaltonemap.cc @@ -26,10 +26,7 @@ using namespace rtengine::procparams; FattalToneMapping::FattalToneMapping(): FoldableToolPanel(this, "fattal", M("TP_TM_FATTAL_LABEL"), true, true) { - -// setEnabledTooltipMarkup(M("TP_EPD_TOOLTIP")); - - amount = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_AMOUNT"), 1., 100., 1., 0.0)); + amount = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_AMOUNT"), 1., 100., 30., 0.0)); threshold = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_THRESHOLD"), -100., 100., 1., 0.0)); amount->setAdjusterListener(this); diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc index 870f90940..fdfee64ab 100644 --- a/rtgui/main-cli.cc +++ b/rtgui/main-cli.cc @@ -822,7 +822,7 @@ int processLineParams ( int argc, char **argv ) } // Process image - rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, nullptr); + rtengine::IImagefloat* resultImage = rtengine::processImage (job, errorCode, nullptr); if ( !resultImage ) { errors++; diff --git a/rtgui/saveformatpanel.cc b/rtgui/saveformatpanel.cc index 13e687595..fef05de23 100644 --- a/rtgui/saveformatpanel.cc +++ b/rtgui/saveformatpanel.cc @@ -40,14 +40,16 @@ SaveFormatPanel::SaveFormatPanel () : listener (nullptr) format->append ("JPEG (8 bit)"); format->append ("TIFF (8 bit)"); format->append ("TIFF (16 bit)"); + format->append ("TIFF (32 bit float)"); format->append ("PNG (8 bit)"); format->append ("PNG (16 bit)"); fstr[0] = "jpg"; fstr[1] = "tif"; fstr[2] = "tif"; - fstr[3] = "png"; + fstr[3] = "tif"; fstr[4] = "png"; + fstr[5] = "png"; hb1->attach (*flab, 0, 0, 1, 1); hb1->attach (*format, 1, 0, 1, 1); @@ -121,8 +123,10 @@ void SaveFormatPanel::init (SaveFormat &sf) if (sf.format == "jpg") { format->set_active (0); } else if (sf.format == "png" && sf.pngBits == 16) { - format->set_active (4); + format->set_active (5); } else if (sf.format == "png" && sf.pngBits == 8) { + format->set_active (4); + } else if (sf.format == "tif" && sf.tiffBits == 32) { format->set_active (3); } else if (sf.format == "tif" && sf.tiffBits == 16) { format->set_active (2); @@ -146,7 +150,7 @@ SaveFormat SaveFormatPanel::getFormat () int sel = format->get_active_row_number(); sf.format = fstr[sel]; - if (sel == 4) { + if (sel == 5) { sf.pngBits = 16; } else { sf.pngBits = 8; @@ -154,6 +158,8 @@ SaveFormat SaveFormatPanel::getFormat () if (sel == 2) { sf.tiffBits = 16; + } else if (sel == 3) { + sf.tiffBits = 32; } else { sf.tiffBits = 8; } diff --git a/rtgui/saveformatpanel.h b/rtgui/saveformatpanel.h index 76ae7055d..8dc493051 100644 --- a/rtgui/saveformatpanel.h +++ b/rtgui/saveformatpanel.h @@ -44,7 +44,7 @@ protected: Gtk::Grid* jpegOpts; Gtk::Label* jpegSubSampLabel; FormatChangeListener* listener; - Glib::ustring fstr[5]; + Glib::ustring fstr[6]; Gtk::CheckButton* savesPP;