diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index e500ad37c..3bc6e0bdc 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-2018 TooWaBoo - Version 2.85 + Version 2.86 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -113,6 +113,10 @@ window > box { dialog { background-color: @bg-grey; border-radius: 0; + -GtkDialog-button-spacing: 0; + -GtkDialog-content-area-spacing: 0; + -GtkDialog-content-area-border: 0; + -GtkDialog-action-area-border: 0; } dialog > box { padding: 0.666666666666666666em; @@ -336,9 +340,13 @@ fontchooser scrolledwindow, /*** end ***************************************************************************************/ /*** Load - Save dialog ************************************************************************/ +filechooser { +margin-bottom: 0.25em; +} -filechooser box > box box > button { +filechooser box > box box > button { margin-top: 0.5em; +margin-right: 0; } filechooser image { @@ -465,7 +473,7 @@ separator, background-color: transparent; } grid separator.horizontal, box separator.horizontal { - margin: 0.166666666666666666em 0; + margin: 0.333333333333333333em 0.166666666666666666em; padding: 0; } grid separator.vertical, box separator.vertical { @@ -537,12 +545,12 @@ menu separator { padding: 0; } -.scrollableToolbar separator:not(.dummy) { +.scrollableToolbar separator.vertical { background-color: shade(@bg-light-grey,.75); margin: 0.166666666666666666em; } -#MyExpander separator { +#MyExpander separator.horizontal { background-color: @view-grid-border; margin: 0.333333333333333333em 0.166666666666666666em; } @@ -944,7 +952,8 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #PrefNotebook > header { margin: -0.666666666666666666em -0.666666666666666666em 0.333333333333333333em; } -#PrefNotebook > header tab label { +#PrefNotebook > header tab label, +#AboutNotebook > header tab label { padding-top: 0.25em; padding-bottom: 0.25em; } @@ -1409,8 +1418,12 @@ combobox entry + button:not(.dummy) { border-bottom-left-radius: 0; border-left: none; } + #PlacesPaned button.combo { - margin: 0 0 calc(0.416666666666666666em - 8px) 0; + margin: 0; +} +#PlacesPaned combobox { + margin-bottom: calc(0.416666666666666666em - 8px); } /* Reset button */ @@ -1435,14 +1448,15 @@ colorchooser colorswatch#add-color-button:first-child { } /* Save, Cancel, OK ... buttons */ -.dialog-action-area button { +dialog .dialog-action-area button { min-height: 2.166666666666666666em; - margin-top: 1em; + margin: 0.5em 0 0 0.333333333333333333em; padding: 0; } messagedialog .dialog-action-area button { min-height: 1.833333333333333333em; - margin: -12px 0.5em 0.5em 0.5em; + margin: -12px 0.5em 0.5em; + padding: 0; } messagedialog .dialog-action-area button:not(:only-child):nth-child(1) { margin-right: 0.25em; @@ -1789,7 +1803,7 @@ radio:disabled { radiobutton label, checkbutton label { - margin: 0 0 0 0.416666666666666666em; + margin: 0 0.416666666666666666em; padding: 0; } diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index c2ee72f18..6dfe14a14 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -131,6 +131,7 @@ set(RTENGINESOURCEFILES guidedfilter.cc ipdehaze.cc iplabregions.cc + lj92.c ) if(LENSFUN_HAS_LOAD_DIRECTORY) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 035dab2b2..c9bc8ccad 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -23,6 +23,10 @@ /*RT*/#define LOCALTIME /*RT*/#define DJGPP /*RT*/#include "jpeg.h" +/*RT*/#include "lj92.h" +/*RT*/#ifdef _OPENMP +/*RT*/#include +/*RT*/#endif #include #include @@ -1124,6 +1128,64 @@ void CLASS ljpeg_idct (struct jhead *jh) FORC(64) jh->idct[c] = CLIP(((float *)work[2])[c]+0.5); } +void CLASS lossless_dnglj92_load_raw() +{ + BENCHFUN + + tiff_bps = 16; + + int save = ifp->pos; + uint16_t *lincurve = !strncmp(make,"Blackmagic",10) ? curve : nullptr; + tile_width = tile_length < INT_MAX ? tile_width : raw_width; + size_t tileCount = raw_width / tile_width; + + size_t dataOffset[tileCount]; + if(tile_length < INT_MAX) { + for (size_t t = 0; t < tileCount; ++t) { + dataOffset[t] = get4(); + } + } else { + dataOffset[0] = ifp->pos; + } + uint8_t *data = (uint8_t*)malloc(ifp->size); + fseek(ifp, 0, SEEK_SET); + // read whole file + int data_length = ifp->size; + fread(data, 1, data_length, ifp); + lj92 lj; + int newwidth, newheight, newbps; + lj92_open(&lj, &data[dataOffset[0]], data_length, &newwidth, &newheight, &newbps); + lj92_close(lj); + if (newwidth * newheight * tileCount != raw_width * raw_height) { + // not a lj92 file + fseek(ifp, save, SEEK_SET); + free(data); + lossless_dng_load_raw(); + return; + } + +#ifdef _OPENMP + #pragma omp parallel for num_threads(std::min(tileCount, omp_get_max_threads())) +#endif + for (size_t t = 0; t < tileCount; ++t) { + size_t tcol = t * tile_width; + lj92 lj; + int newwidth, newheight, newbps; + lj92_open(&lj, &data[dataOffset[t]], data_length, &newwidth, &newheight, &newbps); + + uint16_t *target = (uint16_t*)malloc(newwidth * newheight * sizeof *target); + lj92_decode(lj, target, tile_width, 0, lincurve, 0x1000); + for (int y = 0; y < height; ++y) { + for(int x = 0; x < tile_width; ++x) { + RAW(y, x + tcol) = target[y * tile_width + x]; + } + } + lj92_close(lj); + free(target); + } + free(data); +} + void CLASS lossless_dng_load_raw() { unsigned save, trow=0, tcol=0, jwide, jrow, jcol, row, col, i, j; @@ -9280,7 +9342,7 @@ void CLASS identify() switch (tiff_compress) { case 0: case 1: load_raw = &CLASS packed_dng_load_raw; break; - case 7: load_raw = &CLASS lossless_dng_load_raw; break; + case 7: load_raw = (!strncmp(make,"Blackmagic",10) || !strncmp(make,"Canon",5)) ? &CLASS lossless_dnglj92_load_raw : &CLASS lossless_dng_load_raw; break; case 8: load_raw = &CLASS deflate_dng_load_raw; break; case 34892: load_raw = &CLASS lossy_dng_load_raw; break; default: load_raw = 0; diff --git a/rtengine/dcraw.h b/rtengine/dcraw.h index 9c6ac4aec..f9591b640 100644 --- a/rtengine/dcraw.h +++ b/rtengine/dcraw.h @@ -290,6 +290,7 @@ void ljpeg_idct (struct jhead *jh); void canon_sraw_load_raw(); void adobe_copy_pixel (unsigned row, unsigned col, ushort **rp); void lossless_dng_load_raw(); +void lossless_dnglj92_load_raw(); void packed_dng_load_raw(); void deflate_dng_load_raw(); void init_fuji_compr(struct fuji_compressed_params* info); diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index 2a9f68bbc..f27e1fd62 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -161,6 +161,13 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* model = "Unknown"; } + if (model == "Unknown") { + tag = newFrameRootDir->findTag("UniqueCameraModel"); + if (tag) { + model = tag->valueToString(); + } + } + tag = newFrameRootDir->findTagUpward("Orientation"); if (tag) { diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index f26521696..d905bab29 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1079,6 +1079,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) hListener->histogramChanged(histRed, histGreen, histBlue, histLuma, histToneCurve, histLCurve, histCCurve, /*histCLurve, histLLCurve,*/ histLCAM, histCCAM, histRedRaw, histGreenRaw, histBlueRaw, histChroma, histLRETI); } } + if (orig_prev != oprevi) { + delete oprevi; + oprevi = nullptr; + } + } diff --git a/rtengine/lj92.c b/rtengine/lj92.c new file mode 100644 index 000000000..cfdae6bf9 --- /dev/null +++ b/rtengine/lj92.c @@ -0,0 +1,696 @@ +/* +lj92.c +(c) Andrew Baldwin 2014 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include + +#include "lj92.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +//#define SLOW_HUFF +//#define DEBUG + +typedef struct _ljp { + u8* data; + u8* dataend; + int datalen; + int scanstart; + int ix; + int x; // Width + int y; // Height + int bits; // Bit depth + int writelen; // Write rows this long + int skiplen; // Skip this many values after each row + u16* linearize; // Linearization table + int linlen; + int sssshist[16]; + + // Huffman table - only one supported, and probably needed +#ifdef SLOW_HUFF + int* maxcode; + int* mincode; + int* valptr; + u8* huffval; + int* huffsize; + int* huffcode; +#else + u16* hufflut; + int huffbits; +#endif + // Parse state + int cnt; + u32 b; + u16* image; + u16* rowcache; + u16* outrow[2]; +} ljp; + +static int find(ljp* self) { + int ix = self->ix; + u8* data = self->data; + while (data[ix] != 0xFF && ix<(self->datalen-1)) { + ix += 1; + } + ix += 2; + if (ix>=self->datalen) return -1; + self->ix = ix; + return data[ix-1]; +} + +#define BEH(ptr) ((((int)(*&ptr))<<8)|(*(&ptr+1))) + +static int parseHuff(ljp* self) { + int ret = LJ92_ERROR_CORRUPT; + u8* huffhead = &self->data[self->ix]; // xstruct.unpack('>HB16B',self.data[self.ix:self.ix+19]) + u8* bits = &huffhead[2]; + bits[0] = 0; // Because table starts from 1 + int hufflen = BEH(huffhead[0]); + if ((self->ix + hufflen) >= self->datalen) return ret; +#ifdef SLOW_HUFF + u8* huffval = calloc(hufflen - 19,sizeof(u8)); + if (huffval == NULL) return LJ92_ERROR_NO_MEMORY; + self->huffval = huffval; + for (int hix=0;hix<(hufflen-19);hix++) { + huffval[hix] = self->data[self->ix+19+hix]; +#ifdef DEBUG + printf("huffval[%d]=%d\n",hix,huffval[hix]); +#endif + } + self->ix += hufflen; + // Generate huffman table + int k = 0; + int i = 1; + int j = 1; + int huffsize_needed = 1; + // First calculate how long huffsize needs to be + while (i<=16) { + while (j<=bits[i]) { + huffsize_needed++; + k = k+1; + j = j+1; + } + i = i+1; + j = 1; + } + // Now allocate and do it + int* huffsize = calloc(huffsize_needed,sizeof(int)); + if (huffsize == NULL) return LJ92_ERROR_NO_MEMORY; + self->huffsize = huffsize; + k = 0; + i = 1; + j = 1; + // First calculate how long huffsize needs to be + int hsix = 0; + while (i<=16) { + while (j<=bits[i]) { + huffsize[hsix++] = i; + k = k+1; + j = j+1; + } + i = i+1; + j = 1; + } + huffsize[hsix++] = 0; + + // Calculate the size of huffcode array + int huffcode_needed = 0; + k = 0; + int code = 0; + int si = huffsize[0]; + while (1) { + while (huffsize[k] == si) { + huffcode_needed++; + code = code+1; + k = k+1; + } + if (huffsize[k] == 0) + break; + while (huffsize[k] != si) { + code = code << 1; + si = si + 1; + } + } + // Now fill it + int* huffcode = calloc(huffcode_needed,sizeof(int)); + if (huffcode == NULL) return LJ92_ERROR_NO_MEMORY; + self->huffcode = huffcode; + int hcix = 0; + k = 0; + code = 0; + si = huffsize[0]; + while (1) { + while (huffsize[k] == si) { + huffcode[hcix++] = code; + code = code+1; + k = k+1; + } + if (huffsize[k] == 0) + break; + while (huffsize[k] != si) { + code = code << 1; + si = si + 1; + } + } + + i = 0; + j = 0; + + int* maxcode = calloc(17,sizeof(int)); + if (maxcode == NULL) return LJ92_ERROR_NO_MEMORY; + self->maxcode = maxcode; + int* mincode = calloc(17,sizeof(int)); + if (mincode == NULL) return LJ92_ERROR_NO_MEMORY; + self->mincode = mincode; + int* valptr = calloc(17,sizeof(int)); + if (valptr == NULL) return LJ92_ERROR_NO_MEMORY; + self->valptr = valptr; + + while (1) { + while (1) { + i++; + if (i>16) + break; + if (bits[i]!=0) + break; + maxcode[i] = -1; + } + if (i>16) + break; + valptr[i] = j; + mincode[i] = huffcode[j]; + j = j+bits[i]-1; + maxcode[i] = huffcode[j]; + j++; + } + free(huffsize); + self->huffsize = NULL; + free(huffcode); + self->huffcode = NULL; + ret = LJ92_ERROR_NONE; +#else + /* Calculate huffman direct lut */ + // How many bits in the table - find highest entry + u8* huffvals = &self->data[self->ix+19]; + int maxbits = 16; + while (maxbits>0) { + if (bits[maxbits]) break; + maxbits--; + } + self->huffbits = maxbits; + /* Now fill the lut */ + u16* hufflut = (u16*)malloc((1<hufflut = hufflut; + int i = 0; + int hv = 0; + int rv = 0; + int vl = 0; // i + int hcode; + int bitsused = 1; +#ifdef DEBUG + printf("%04x:%x:%d:%x\n",i,huffvals[hv],bitsused,1<<(maxbits-bitsused)); +#endif + while (i<1<maxbits) { + break; // Done. Should never get here! + } + if (vl >= bits[bitsused]) { + bitsused++; + vl = 0; + continue; + } + if (rv == 1 << (maxbits-bitsused)) { + rv = 0; + vl++; + hv++; +#ifdef DEBUG + printf("%04x:%x:%d:%x\n",i,huffvals[hv],bitsused,1<<(maxbits-bitsused)); +#endif + continue; + } + hcode = huffvals[hv]; + hufflut[i] = hcode<<8 | bitsused; + //printf("%d %d %d\n",i,bitsused,hcode); + i++; + rv++; + } + ret = LJ92_ERROR_NONE; +#endif + return ret; +} + +static int parseSof3(ljp* self) { + if (self->ix+6 >= self->datalen) return LJ92_ERROR_CORRUPT; + self->y = BEH(self->data[self->ix+3]); + self->x = BEH(self->data[self->ix+5]); + self->bits = self->data[self->ix+2]; + self->ix += BEH(self->data[self->ix]); + return LJ92_ERROR_NONE; +} + +static int parseBlock(ljp* self,int marker) { + self->ix += BEH(self->data[self->ix]); + if (self->ix >= self->datalen) return LJ92_ERROR_CORRUPT; + return LJ92_ERROR_NONE; +} + +#ifdef SLOW_HUFF +static int nextbit(ljp* self) { + u32 b = self->b; + if (self->cnt == 0) { + u8* data = &self->data[self->ix]; + u32 next = *data++; + b = next; + if (next == 0xff) { + data++; + self->ix++; + } + self->ix++; + self->cnt = 8; + } + int bit = b >> 7; + self->cnt--; + self->b = (b << 1)&0xFF; + return bit; +} + +static int decode(ljp* self) { + int i = 1; + int code = nextbit(self); + while (code > self->maxcode[i]) { + i++; + code = (code << 1) + nextbit(self); + } + int j = self->valptr[i]; + j = j + code - self->mincode[i]; + int value = self->huffval[j]; + return value; +} + +static int receive(ljp* self,int ssss) { + int i = 0; + int v = 0; + while (i != ssss) { + i++; + v = (v<<1) + nextbit(self); + } + return v; +} + +static int extend(ljp* self,int v,int t) { + int vt = 1<<(t-1); + if (v < vt) { + vt = (-1 << t) + 1; + v = v + vt; + } + return v; +} +#endif + +inline static int nextdiff(ljp* self, int Px) { +#ifdef SLOW_HUFF + int t = decode(self); + int diff = receive(self,t); + diff = extend(self,diff,t); + //printf("%d %d %d %x\n",Px+diff,Px,diff,t);//,index,usedbits); +#else + u32 b = self->b; + int cnt = self->cnt; + int huffbits = self->huffbits; + int ix = self->ix; + int next; + while (cnt < huffbits) { + next = *(u16*)&self->data[ix]; + int one = next&0xFF; + int two = next>>8; + b = (b<<16)|(one<<8)|two; + cnt += 16; + ix += 2; + if (one==0xFF) { + //printf("%x %x %x %x %d\n",one,two,b,b>>8,cnt); + b >>= 8; + cnt -= 8; + } else if (two==0xFF) ix++; + } + int index = b >> (cnt - huffbits); + u16 ssssused = self->hufflut[index]; + int usedbits = ssssused&0xFF; + int t = ssssused>>8; + self->sssshist[t]++; + cnt -= usedbits; + int keepbitsmask = (1 << cnt)-1; + b &= keepbitsmask; + while (cnt < t) { + next = *(u16*)&self->data[ix]; + int one = next&0xFF; + int two = next>>8; + b = (b<<16)|(one<<8)|two; + cnt += 16; + ix += 2; + if (one==0xFF) { + b >>= 8; + cnt -= 8; + } else if (two==0xFF) ix++; + } + cnt -= t; + int diff = b >> cnt; + int vt = 1<<(t-1); + if (diff < vt) { + vt = (-1 << t) + 1; + diff += vt; + } + keepbitsmask = (1 << cnt)-1; + self->b = b & keepbitsmask; + self->cnt = cnt; + self->ix = ix; + //printf("%d %d\n",t,diff); + //printf("%d %d %d %x %x %d\n",Px+diff,Px,diff,t,index,usedbits); +#ifdef DEBUG +#endif +#endif + return diff; +} + +static int parsePred6(ljp* self) { + int ret = LJ92_ERROR_CORRUPT; + self->ix = self->scanstart; + //int compcount = self->data[self->ix+2]; + self->ix += BEH(self->data[self->ix]); + self->cnt = 0; + self->b = 0; + int write = self->writelen; + // Now need to decode huffman coded values + int c = 0; + int pixels = self->y * self->x; + u16* out = self->image; + u16* temprow; + u16* thisrow = self->outrow[0]; + u16* lastrow = self->outrow[1]; + + // First pixel predicted from base value + int diff; + int Px; + int col = 0; + int row = 0; + int left = 0; + int linear; + + // First pixel + diff = nextdiff(self,0); + Px = 1 << (self->bits-1); + left = Px + diff; + if (self->linearize) + linear = self->linearize[left]; + else + linear = left; + thisrow[col++] = left; + out[c++] = linear; + if (self->ix >= self->datalen) return ret; + --write; + int rowcount = self->x-1; + while (rowcount--) { + diff = nextdiff(self,0); + Px = left; + left = Px + diff; + if (self->linearize) + linear = self->linearize[left]; + else + linear = left; + thisrow[col++] = left; + out[c++] = linear; + //printf("%d %d %d %d %x\n",col-1,diff,left,thisrow[col-1],&thisrow[col-1]); + if (self->ix >= self->datalen) return ret; + if (--write==0) { + out += self->skiplen; + write = self->writelen; + } + } + temprow = lastrow; + lastrow = thisrow; + thisrow = temprow; + row++; + //printf("%x %x\n",thisrow,lastrow); + while (clinearize) { + if (left>self->linlen) return LJ92_ERROR_CORRUPT; + linear = self->linearize[left]; + } else + linear = left; + thisrow[col++] = left; + //printf("%d %d %d %d\n",col,diff,left,lastrow[col]); + out[c++] = linear; + if (self->ix >= self->datalen) break; + rowcount = self->x-1; + if (--write==0) { + out += self->skiplen; + write = self->writelen; + } + while (rowcount--) { + diff = nextdiff(self,0); + Px = lastrow[col] + ((left - lastrow[col-1])>>1); + left = Px + diff; + //printf("%d %d %d %d %d %x\n",col,diff,left,lastrow[col],lastrow[col-1],&lastrow[col]); + if (self->linearize) { + if (left>self->linlen) return LJ92_ERROR_CORRUPT; + linear = self->linearize[left]; + } else + linear = left; + thisrow[col++] = left; + out[c++] = linear; + if (--write==0) { + out += self->skiplen; + write = self->writelen; + } + } + temprow = lastrow; + lastrow = thisrow; + thisrow = temprow; + if (self->ix >= self->datalen) break; + } + if (c >= pixels) ret = LJ92_ERROR_NONE; + return ret; +} + +static int parseScan(ljp* self) { + int ret = LJ92_ERROR_CORRUPT; + memset(self->sssshist,0,sizeof(self->sssshist)); + self->ix = self->scanstart; + int compcount = self->data[self->ix+2]; + int pred = self->data[self->ix+3+2*compcount]; + if (pred<0 || pred>7) return ret; + if (pred==6) return parsePred6(self); // Fast path + self->ix += BEH(self->data[self->ix]); + self->cnt = 0; + self->b = 0; + int write = self->writelen; + // Now need to decode huffman coded values + int c = 0; + int pixels = self->y * self->x; + u16* out = self->image; + u16* thisrow = self->outrow[0]; + u16* lastrow = self->outrow[1]; + + // First pixel predicted from base value + int diff; + int Px; + int col = 0; + int row = 0; + int left = 0; + while (cbits-1); + } else if (row==0) { + Px = left; + } else if (col==0) { + Px = lastrow[col]; // Use value above for first pixel in row + } else { + switch (pred) { + case 0: + Px = 0; break; // No prediction... should not be used + case 1: + Px = left; break; + case 2: + Px = lastrow[col]; break; + case 3: + Px = lastrow[col-1];break; + case 4: + Px = left + lastrow[col] - lastrow[col-1];break; + case 5: + Px = left + ((lastrow[col] - lastrow[col-1])>>1);break; + case 6: + Px = lastrow[col] + ((left - lastrow[col-1])>>1);break; + case 7: + Px = (left + lastrow[col])>>1;break; + } + } + diff = nextdiff(self,Px); + left = Px + diff; + //printf("%d %d %d\n",c,diff,left); + int linear; + if (self->linearize) { + if (left>self->linlen) return LJ92_ERROR_CORRUPT; + linear = self->linearize[left]; + } else + linear = left; + thisrow[col] = left; + out[c++] = linear; + if (++col==self->x) { + col = 0; + row++; + u16* temprow = lastrow; + lastrow = thisrow; + thisrow = temprow; + } + if (--write==0) { + out += self->skiplen; + write = self->writelen; + } + if (self->ix >= self->datalen+2) break; + } + if (c >= pixels) ret = LJ92_ERROR_NONE; + /*for (int h=0;h<17;h++) { + printf("ssss:%d=%d (%f)\n",h,self->sssshist[h],(float)self->sssshist[h]/(float)(pixels)); + }*/ + return ret; +} + +static int parseImage(ljp* self) { + int ret = LJ92_ERROR_NONE; + while (1) { + int nextMarker = find(self); + if (nextMarker == 0xc4) + ret = parseHuff(self); + else if (nextMarker == 0xc3) + ret = parseSof3(self); + else if (nextMarker == 0xfe)// Comment + ret = parseBlock(self,nextMarker); + else if (nextMarker == 0xd9) // End of image + break; + else if (nextMarker == 0xda) { + self->scanstart = self->ix; + ret = LJ92_ERROR_NONE; + break; + } else if (nextMarker == -1) { + ret = LJ92_ERROR_CORRUPT; + break; + } else + ret = parseBlock(self,nextMarker); + if (ret != LJ92_ERROR_NONE) break; + } + return ret; +} + +static int findSoI(ljp* self) { + int ret = LJ92_ERROR_CORRUPT; + if (find(self)==0xd8) + ret = parseImage(self); + return ret; +} + +static void free_memory(ljp* self) { +#ifdef SLOW_HUFF + free(self->maxcode); + self->maxcode = NULL; + free(self->mincode); + self->mincode = NULL; + free(self->valptr); + self->valptr = NULL; + free(self->huffval); + self->huffval = NULL; + free(self->huffsize); + self->huffsize = NULL; + free(self->huffcode); + self->huffcode = NULL; +#else + free(self->hufflut); + self->hufflut = NULL; +#endif + free(self->rowcache); + self->rowcache = NULL; +} + +int lj92_open(lj92* lj, + uint8_t* data, int datalen, + int* width,int* height, int* bitdepth) { + ljp* self = (ljp*)calloc(sizeof(ljp),1); + if (self==NULL) return LJ92_ERROR_NO_MEMORY; + + self->data = (u8*)data; + self->dataend = self->data + datalen; + self->datalen = datalen; + + int ret = findSoI(self); + + if (ret == LJ92_ERROR_NONE) { + u16* rowcache = (u16*)calloc(self->x * 2,sizeof(u16)); + if (rowcache == NULL) ret = LJ92_ERROR_NO_MEMORY; + else { + self->rowcache = rowcache; + self->outrow[0] = rowcache; + self->outrow[1] = &rowcache[self->x]; + } + } + + if (ret != LJ92_ERROR_NONE) { // Failed, clean up + *lj = NULL; + free_memory(self); + free(self); + } else { + *width = self->x; + *height = self->y; + *bitdepth = self->bits; + *lj = self; + } + return ret; +} + +int lj92_decode(lj92 lj, + uint16_t* target,int writeLength, int skipLength, + uint16_t* linearize,int linearizeLength) { + int ret = LJ92_ERROR_NONE; + ljp* self = lj; + if (self == NULL) return LJ92_ERROR_BAD_HANDLE; + self->image = target; + self->writelen = writeLength; + self->skiplen = skipLength; + self->linearize = linearize; + self->linlen = linearizeLength; + ret = parseScan(self); + return ret; +} + +void lj92_close(lj92 lj) { + ljp* self = lj; + if (self != NULL) + free_memory(self); + free(self); +} diff --git a/rtengine/lj92.h b/rtengine/lj92.h new file mode 100644 index 000000000..bc8bf7604 --- /dev/null +++ b/rtengine/lj92.h @@ -0,0 +1,68 @@ +/* +lj92.h +(c) Andrew Baldwin 2014 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef LJ92_H +#define LJ92_H + +#ifdef __cplusplus +extern "C" +{ +#endif +enum LJ92_ERRORS { + LJ92_ERROR_NONE = 0, + LJ92_ERROR_CORRUPT = -1, + LJ92_ERROR_NO_MEMORY = -2, + LJ92_ERROR_BAD_HANDLE = -3, + LJ92_ERROR_TOO_WIDE = -4, +}; + +typedef struct _ljp* lj92; + +/* Parse a lossless JPEG (1992) structure returning + * - a handle that can be used to decode the data + * - width/height/bitdepth of the data + * Returns status code. + * If status == LJ92_ERROR_NONE, handle must be closed with lj92_close + */ +int lj92_open(lj92* lj, // Return handle here + uint8_t* data,int datalen, // The encoded data + int* width,int* height,int* bitdepth); // Width, height and bitdepth + +/* Release a decoder object */ +void lj92_close(lj92 lj); + +/* + * Decode previously opened lossless JPEG (1992) into a 2D tile of memory + * Starting at target, write writeLength 16bit values, then skip 16bit skipLength value before writing again + * If linearize is not NULL, use table at linearize to convert data values from output value to target value + * Data is only correct if LJ92_ERROR_NONE is returned + */ +int lj92_decode(lj92 lj, + uint16_t* target, int writeLength, int skipLength, // The image is written to target as a tile + uint16_t* linearize, int linearizeLength); // If not null, linearize the data using this table + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/rtgui/dirbrowser.cc b/rtgui/dirbrowser.cc index 501296860..9ed8455fe 100644 --- a/rtgui/dirbrowser.cc +++ b/rtgui/dirbrowser.cc @@ -64,7 +64,7 @@ std::vector listSubDirs (const Glib::RefPtr& dir, bool } catch (const Glib::Exception& exception) { if (options.rtSettings.verbose) { - std::cerr << exception.what () << std::endl; + std::cerr << exception.what().c_str() << std::endl; } } diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 74037b088..8b9b75c8e 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -1208,7 +1208,7 @@ Gtk::Widget* Preferences::getFileBrowserPanel() startupdir = Gtk::manage(new Gtk::Entry()); Gtk::Button* sdselect = Gtk::manage(new Gtk::Button()); - sdselect->set_image (*Gtk::manage (new RTImage ("folder-open.png"))); + sdselect->set_image (*Gtk::manage (new RTImage ("folder-open-small.png"))); Gtk::RadioButton::Group opts = sdcurrent->get_group(); sdlast->set_group(opts); @@ -1277,20 +1277,26 @@ Gtk::Widget* Preferences::getFileBrowserPanel() Gtk::Frame* frmnu = Gtk::manage(new Gtk::Frame(M("PREFERENCES_MENUOPTIONS"))); + + Gtk::Grid* menuGrid = Gtk::manage(new Gtk::Grid()); + menuGrid->get_style_context()->add_class("grid-spacing"); + setExpandAlignProperties(menuGrid, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + ckbmenuGroupRank = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPRANK"))); + setExpandAlignProperties(ckbmenuGroupRank, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); ckbmenuGroupLabel = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPLABEL"))); ckbmenuGroupFileOperations = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPFILEOPERATIONS"))); + setExpandAlignProperties(ckbmenuGroupFileOperations, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); ckbmenuGroupProfileOperations = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPPROFILEOPERATIONS"))); ckbmenuGroupExtProg = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPEXTPROGS"))); - Gtk::VBox* vbmnu = Gtk::manage(new Gtk::VBox()); + + menuGrid->attach (*ckbmenuGroupRank, 0, 0, 1, 1); + menuGrid->attach (*ckbmenuGroupLabel, 1, 0, 1, 1); + menuGrid->attach (*ckbmenuGroupFileOperations, 0, 1, 1, 1); + menuGrid->attach (*ckbmenuGroupProfileOperations, 1, 1, 1, 1); + menuGrid->attach (*ckbmenuGroupExtProg, 0, 2, 2, 1); - vbmnu->pack_start(*ckbmenuGroupRank, Gtk::PACK_SHRINK, 0); - vbmnu->pack_start(*ckbmenuGroupLabel, Gtk::PACK_SHRINK, 0); - vbmnu->pack_start(*ckbmenuGroupFileOperations, Gtk::PACK_SHRINK, 0); - vbmnu->pack_start(*ckbmenuGroupProfileOperations, Gtk::PACK_SHRINK, 0); - vbmnu->pack_start(*ckbmenuGroupExtProg, Gtk::PACK_SHRINK, 0); - - frmnu->add(*vbmnu); + frmnu->add (*menuGrid); Gtk::Frame* fre = Gtk::manage(new Gtk::Frame(M("PREFERENCES_PARSEDEXT"))); @@ -1362,7 +1368,8 @@ Gtk::Widget* Preferences::getFileBrowserPanel() // Separation is needed so that a button is not accidentally clicked when one wanted // to click a spinbox. Ideally, the separation wouldn't require attaching a widget, but how? - Gtk::Label *separator = Gtk::manage (new Gtk::Label()); + Gtk::HSeparator *cacheSeparator = Gtk::manage (new Gtk::HSeparator()); + cacheSeparator->get_style_context()->add_class("grid-row-separator"); Gtk::Label* clearThumbsLbl = Gtk::manage (new Gtk::Label(M("PREFERENCES_CACHECLEAR_ALLBUTPROFILES"))); setExpandAlignProperties(clearThumbsLbl, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); @@ -1380,7 +1387,7 @@ Gtk::Widget* Preferences::getFileBrowserPanel() cacheGrid->attach (*maxThumbHeightSB, 1, 0, 1, 1); cacheGrid->attach (*maxCacheEntriesLbl, 0, 1, 1, 1); cacheGrid->attach (*maxCacheEntriesSB, 1, 1, 1, 1); - cacheGrid->attach (*separator, 0, 2, 2, 1); + cacheGrid->attach (*cacheSeparator, 0, 2, 2, 1); cacheGrid->attach (*clearThumbsLbl, 0, 3, 1, 1); cacheGrid->attach (*clearThumbsBtn, 1, 3, 1, 1); if (moptions.saveParamsCache) {