diff --git a/rtengine/cache.h b/rtengine/cache.h new file mode 100644 index 000000000..2e53aab2a --- /dev/null +++ b/rtengine/cache.h @@ -0,0 +1,236 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2016 Flössie + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "../rtgui/threadutils.h" + +namespace rtengine +{ + +namespace cache_helper +{ + + // See http://stackoverflow.com/a/20790050 + template + struct has_hash + : std::false_type + { + }; + + template + struct has_hash()(std::declval()), void())> + : std::true_type + { + }; + +} + +template +class Cache +{ +public: + class Hook + { + public: + virtual ~Hook() + { + } + virtual void onDiscard(const K& key, const V& value) = 0; + virtual void onDisplace(const K& key, const V& value) = 0; + virtual void onRemove(const K& key, const V& value) = 0; + virtual void onDestroy() = 0; + }; + + Cache(unsigned long _size, Hook* _hook = 0) : + store_size(_size), + hook(_hook) + { + } + + ~Cache() + { + if (hook) { + resize(0); + hook->onDestroy(); + } + } + + bool get(const K& key, V& value) const + { + mutex.lock(); + const StoreConstIterator store_it = store.find(key); + const bool present = store_it != store.end(); + if (present) { + lru_list.splice( + lru_list.begin(), + lru_list, + store_it->second.lru_list_it + ); + value = store_it->second.value; + } + mutex.unlock(); + + return present; + } + + bool set(const K& key, const V& value) + { + return set(key, value, Mode::UNCOND); + } + + bool replace(const K& key, const V& value) + { + return set(key, value, Mode::KNOWN); + } + + bool insert(const K& key, const V& value) + { + return set(key, value, Mode::UNKNOWN); + } + + bool remove(const K& key) + { + mutex.lock(); + const StoreIterator store_it = store.find(key); + const bool present = store_it != store.end(); + if (present) { + remove(store_it); + } + mutex.unlock(); + + return present; + } + + void resize(unsigned long size) + { + mutex.lock(); + while (lru_list.size() > size) { + discard(); + } + store_size = size; + mutex.unlock(); + } + + void clear() + { + mutex.lock(); + if (hook) { + for (const auto& entry : store) { + hook->onRemove(entry.first, entry.second.value); + } + } + lru_list.clear(); + store.clear(); + mutex.unlock(); + } + +private: + struct Value; + + using Store = typename std::conditional< + cache_helper::has_hash::value, + std::unordered_map, + std::map + >::type; + using StoreIterator = typename Store::iterator; + using StoreConstIterator = typename Store::const_iterator; + + typedef std::list LruList; + using LruListIterator = typename LruList::iterator; + + struct Value { + V value; + LruListIterator lru_list_it; + }; + + enum class Mode { + UNCOND, + KNOWN, + UNKNOWN + }; + + void discard() + { + const StoreIterator store_it = lru_list.back(); + if (hook) { + hook->onDiscard(store_it->first, store_it->second.value); + } + store.erase(store_it); + lru_list.pop_back(); + } + + bool set(const K& key, const V& value, Mode mode) + { + mutex.lock(); + const StoreIterator store_it = store.find(key); + const bool is_new_key = store_it == store.end(); + if (is_new_key) { + if (mode == Mode::UNCOND || mode == Mode::UNKNOWN) { + if (lru_list.size() >= store_size) { + discard(); + } + lru_list.push_front(store.end()); + const Value v = { + value, + lru_list.begin() + }; + lru_list.front() = store.emplace(key, v).first; + } + } else { + if (mode == Mode::UNCOND || mode == Mode::KNOWN) { + if (hook) { + hook->onDisplace(key, store_it->second.value); + } + lru_list.splice( + lru_list.begin(), + lru_list, + store_it->second.lru_list_it + ); + store_it->second.value = value; + } + } + mutex.unlock(); + + return is_new_key; + } + + void remove(const StoreIterator& store_it) + { + if (hook) { + hook->onRemove(store_it->first, store_it->second.value); + } + lru_list.erase(store_it->second.lru_list_it); + store.erase(store_it); + } + + unsigned long store_size; + Hook* const hook; + mutable MyMutex mutex; + Store store; + mutable LruList lru_list; +}; + +} diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc index a0ea5afb4..533d1e89d 100644 --- a/rtengine/clutstore.cc +++ b/rtengine/clutstore.cc @@ -3,440 +3,233 @@ #include "stdimagesource.h" #include "../rtgui/options.h" -rtengine::CLUTStore clutStore; - -using namespace rtengine; - -const float MAXVAL8 = 255.; - -CLUTStore::CLUTStore() +namespace { -} -CLUT* CLUTStore::getClut( const Glib::ustring& filename ) +std::unique_ptr loadFile( + const Glib::ustring& filename, + const Glib::ustring& working_color_space, + unsigned int& clut_level +) { - CLUT *result = 0; - m_mutex.lock(); - Cluts::iterator cluts_it = m_cluts.find(filename); + std::unique_ptr result; - if (cluts_it == m_cluts.end()) { - if (m_cluts.size() >= options.clutCacheSize) { - // Evict a "random" entry from cache - Cluts::iterator victim_it = m_cluts.begin(); + rtengine::StdImageSource img_src; - if (--victim_it->second.first == -1) { - delete victim_it->second.second; - m_cluts.erase(victim_it); - } - } - - cluts_it = m_cluts.insert(std::make_pair(filename, std::make_pair(0, new HaldCLUT))).first; - cluts_it->second.second->load( filename ); - } - - if (cluts_it->second.second->isValid()) { - result = cluts_it->second.second; - ++cluts_it->second.first; - } else { - delete cluts_it->second.second; - m_cluts.erase(cluts_it); - } - - m_mutex.unlock(); - - return result; -} - -void CLUTStore::releaseClut( const CLUT* clut ) -{ - m_mutex.lock(); - - for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end(); ++cluts_it) { - if (cluts_it->second.second == clut) { - if (--cluts_it->second.first == -1) { - delete cluts_it->second.second; - m_cluts.erase(cluts_it); - } - - break; - } - } - - m_mutex.unlock(); -} - -void CLUTStore::clearCache() -{ - m_mutex.lock(); - - for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end();) { - if (--cluts_it->second.first == -1) { - delete cluts_it->second.second; - Cluts::iterator tmp = cluts_it; - ++cluts_it; - m_cluts.erase(tmp); - } else { - ++cluts_it; - } - } - - m_mutex.unlock(); -} - -void rtengine::splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName ) -{ - filename = Glib::path_get_basename( filename ); - name = filename; - //remove dirs - size_t lastSlashPos = filename.find_last_of( "/" ); - - if ( lastSlashPos == Glib::ustring::npos ) { - lastSlashPos = filename.find_last_of( "\\" ); - } - - size_t lastDotPos = filename.find_last_of( '.' ); - - if ( lastDotPos != Glib::ustring::npos ) { - name = filename.substr( 0, lastDotPos ); - extension = filename.substr( lastDotPos + 1, Glib::ustring::npos ); - } - - profileName = "sRGB"; // sRGB by default - static std::vector workingProfiles = rtengine::getWorkingProfiles(); - - for ( std::vector::iterator it = workingProfiles.begin(); it != workingProfiles.end(); ++it ) { - Glib::ustring ¤tProfile = *it; - - if ( std::search( name.rbegin(), name.rend(), currentProfile.rbegin(), currentProfile.rend() ) == name.rbegin() ) { - profileName = currentProfile; - name = name.substr( 0, name.size() - currentProfile.size() ); - break; - } - } -} - -//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -HaldCLUT::HaldCLUT() - : m_clutImage( 0 ), - m_level (0), - m_profile( "sRGB" ) -{ -} - -HaldCLUT::~HaldCLUT() -{ - if ( m_clutImage ) { - m_clutImage->free(); - m_clutImage = 0; - } -} - -void HaldCLUT::load( Glib::ustring filename ) -{ - m_clutImage = loadFile( filename, "", m_level ); - Glib::ustring name, ext; - splitClutFilename( filename, name, ext, m_profile ); - - if ( m_clutImage ) { - m_filename = filename; - } -} - -Glib::ustring HaldCLUT::profile() const -{ - return m_profile; -} - -Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingColorSpace, int &outLevel ) -{ - Imagefloat *result = 0; - StdImageSource imgSrc; - - if ( !Glib::file_test( filename, Glib::FILE_TEST_EXISTS ) || imgSrc.load(filename) ) { + if (!Glib::file_test(filename, Glib::FILE_TEST_EXISTS) || img_src.load(filename)) { return result; } int fw, fh; - imgSrc.getFullSize (fw, fh, TR_NONE); + img_src.getFullSize(fw, fh, TR_NONE); bool valid = false; - //test on Hald format, copypasted from http://www.quelsolaar.com/technology/clut.html - if ( fw == fh ) { - outLevel = 1; - - for(; outLevel * outLevel * outLevel < fw; outLevel++); - - if( !( outLevel * outLevel * outLevel > fw ) ) { + if (fw == fh) { + unsigned int level = 1; + while (level * level * level < fw) { + ++level; + } + if (level * level * level == fw && level > 1) { + clut_level = level; valid = true; } } - if ( valid ) { - ColorTemp currWB = imgSrc.getWB(); - Imagefloat* baseImg = new Imagefloat (fw, fh); - PreviewProps pp (0, 0, fw, fh, 1); + if (valid) { + rtengine::ColorTemp curr_wb = img_src.getWB(); + result = std::unique_ptr(new rtengine::Imagefloat(fw, fh)); + const PreviewProps pp(0, 0, fw, fh, 1); - procparams::ColorManagementParams icm; - icm.working = workingColorSpace; + rtengine::procparams::ColorManagementParams icm; + icm.working = working_color_space; - imgSrc.getImage (currWB, TR_NONE, baseImg, pp, procparams::ToneCurveParams(), icm, procparams::RAWParams()); + img_src.getImage(curr_wb, TR_NONE, result.get(), pp, rtengine::procparams::ToneCurveParams(), icm, rtengine::procparams::RAWParams()); - if ( !workingColorSpace.empty() ) { - imgSrc.convertColorSpace(baseImg, icm, currWB); + if (!working_color_space.empty()) { + img_src.convertColorSpace(result.get(), icm, curr_wb); } - - result = baseImg; } return result; } -void HaldCLUT::loadClut( Imagefloat *img, RawClut &outClut ) +inline void posToXy(unsigned int pos, unsigned int width, unsigned int (&x)[2], unsigned int (&y)[2]) { - img->normalizeFloatTo1(); - int y_size = img->getH(); - int x_size = img->getW(); - outClut.resize( x_size * y_size * 3 ); - int clutIdx = 0; + x[0] = pos % width; + y[0] = pos / width; + x[1] = (pos + 1) % width; + y[1] = (pos + 1) / width; +} - //int level = m_level * m_level; (unused) - for(int y = 0; y < y_size; y++) { - for(int x = 0; x < x_size; x++) { - outClut[ clutIdx * 3 ] = img->r( y, x ) * MAXVAL8; - outClut[ clutIdx * 3 + 1 ] = img->g( y, x ) * MAXVAL8; - outClut[ clutIdx * 3 + 2 ] = img->b( y, x ) * MAXVAL8; +} - ++clutIdx; +void rtengine::CLUT::splitClutFilename( + const Glib::ustring& filename, + Glib::ustring& name, + Glib::ustring& extension, + Glib::ustring& profile_name +) +{ + Glib::ustring basename = Glib::path_get_basename(filename); + + Glib::ustring::size_type last_slash_pos = basename.rfind('/'); + if (last_slash_pos == Glib::ustring::npos) { + last_slash_pos = basename.rfind('\\'); + } + + const Glib::ustring::size_type last_dot_pos = basename.rfind('.'); + + if (last_dot_pos != Glib::ustring::npos) { + name.assign(basename, 0, last_dot_pos); + extension.assign(basename, last_dot_pos + 1, Glib::ustring::npos); + } else { + name = basename; + } + + profile_name = "sRGB"; + + for (const auto& working_profile : rtengine::getWorkingProfiles()) { + if ( std::search( name.rbegin(), name.rend(), working_profile.rbegin(), working_profile.rend() ) == name.rbegin() ) { + profile_name = working_profile; + name.erase(name.size() - working_profile.size()); + break; } } } -Imagefloat* HaldCLUT::generateIdentImage( int level ) +rtengine::HaldCLUT::HaldCLUT() : + clut_level(0), + clut_profile("sRGB") { - int imageWidth = level * level * level; - Imagefloat *resultImg = new Imagefloat( imageWidth, imageWidth ); +} - int cubeSideSize = level * level; - float step = MAXVALF / (cubeSideSize - 1); - int pos = 0; +rtengine::HaldCLUT::~HaldCLUT() +{ +} - for( int b = 0; b < cubeSideSize; ++b ) { - for ( int g = 0; g < cubeSideSize; ++g ) { - for ( int r = 0; r < cubeSideSize; ++r ) { - int x = pos / imageWidth; - int y = pos % imageWidth; - resultImg->r( x, y ) = step * r; - resultImg->g( x, y ) = step * g; - resultImg->b( x, y ) = step * b; - ++pos; - } +bool rtengine::HaldCLUT::load(const Glib::ustring& filename) +{ + clut_image = loadFile(filename, "", clut_level); + Glib::ustring name, ext; + splitClutFilename(filename, name, ext, clut_profile); + + if (clut_image) { + clut_filename = filename; + return true; + } + + return false; +} + +rtengine::HaldCLUT::operator bool() const +{ + return static_cast(clut_image); +} + +Glib::ustring rtengine::HaldCLUT::getFilename() const +{ + return clut_filename; +} + +Glib::ustring rtengine::HaldCLUT::getProfile() const +{ + return clut_profile; +} + +void rtengine::HaldCLUT::getRGB(float r, float g, float b, float& out_r, float& out_g, float& out_b) const +{ + const unsigned int level = clut_level * clut_level; + + const float flevel_minus_one = static_cast(level - 1) / 65535.0f; + const float flevel_minus_two = static_cast(level - 2); + + const unsigned int red = std::min(flevel_minus_two, r * flevel_minus_one); + const unsigned int green = std::min(flevel_minus_two, g * flevel_minus_one); + const unsigned int blue = std::min(flevel_minus_two, b * flevel_minus_one); + + r = r * flevel_minus_one - red; + g = g * flevel_minus_one - green; + b = b * flevel_minus_one - blue; + + const unsigned int level_square = level * level; + + const unsigned int color = red + green * level + blue * level_square; + + unsigned int x[2]; + unsigned int y[2]; + posToXy(color, clut_image->getWidth(), x, y); + + float tmp1[4] __attribute__((aligned(16))); + tmp1[0] = clut_image->r(y[0], x[0]) * (1 - r) + clut_image->r(y[1], x[1]) * r; + tmp1[1] = clut_image->g(y[0], x[0]) * (1 - r) + clut_image->g(y[1], x[1]) * r; + tmp1[2] = clut_image->b(y[0], x[0]) * (1 - r) + clut_image->b(y[1], x[1]) * r; + + posToXy(color + level, clut_image->getWidth(), x, y); + + float tmp2[4] __attribute__((aligned(16))); + tmp2[0] = clut_image->r(y[0], x[0]) * (1 - r) + clut_image->r(y[1], x[1]) * r; + tmp2[1] = clut_image->g(y[0], x[0]) * (1 - r) + clut_image->g(y[1], x[1]) * r; + tmp2[2] = clut_image->b(y[0], x[0]) * (1 - r) + clut_image->b(y[1], x[1]) * r; + + float out[4] __attribute__((aligned(16))); + out[0] = tmp1[0] * (1 - g) + tmp2[0] * g; + out[1] = tmp1[1] * (1 - g) + tmp2[1] * g; + out[2] = tmp1[2] * (1 - g) + tmp2[2] * g; + + posToXy(color + level_square, clut_image->getWidth(), x, y); + + tmp1[0] = clut_image->r(y[0], x[0]) * (1 - r) + clut_image->r(y[1], x[1]) * r; + tmp1[1] = clut_image->g(y[0], x[0]) * (1 - r) + clut_image->g(y[1], x[1]) * r; + tmp1[2] = clut_image->b(y[0], x[0]) * (1 - r) + clut_image->b(y[1], x[1]) * r; + + posToXy(color + level + level_square, clut_image->getWidth(), x, y); + + tmp2[0] = clut_image->r(y[0], x[0]) * (1 - r) + clut_image->r(y[1], x[1]) * r; + tmp2[1] = clut_image->g(y[0], x[0]) * (1 - r) + clut_image->g(y[1], x[1]) * r; + tmp2[2] = clut_image->b(y[0], x[0]) * (1 - r) + clut_image->b(y[1], x[1]) * r; + + tmp1[0] = tmp1[0] * (1 - g) + tmp2[0] * g; + tmp1[1] = tmp1[1] * (1 - g) + tmp2[1] * g; + tmp1[2] = tmp1[2] * (1 - g) + tmp2[2] * g; + + out_r = out[0] * (1 - b) + tmp1[0] * b; + out_g = out[1] * (1 - b) + tmp1[1] * b; + out_b = out[2] * (1 - b) + tmp1[2] * b; +} + +rtengine::CLUTStore& rtengine::CLUTStore::getInstance() +{ + static CLUTStore instance; + return instance; +} + +std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ustring& filename) +{ + std::shared_ptr result; + + if (!cache.get(filename, result)) { + std::unique_ptr clut(new rtengine::HaldCLUT); + if (clut->load(filename)) { + result = std::move(clut); + cache.insert(filename, result); } } - return resultImg; + return result; } - -bool HaldCLUT::isValid() const +void rtengine::CLUTStore::releaseClut(const std::shared_ptr& clut) { - return m_clutImage != 0; + cache.remove(clut->getFilename()); } -void HaldCLUT::getRGB( float rr, float gg, float bb, float &outR, float &outG, float &outB ) const +void rtengine::CLUTStore::clearCache() { - rr /= MAXVALF; - gg /= MAXVALF; - bb /= MAXVALF; - correct( *m_clutImage, m_level, rr, gg, bb, outR, outG, outB ); + cache.clear(); } -inline float valF( unsigned char val ) +rtengine::CLUTStore::CLUTStore() : + cache(options.clutCacheSize) { - return float( val ) / MAXVAL8; -} - -// copypasted from http://www.quelsolaar.com/technology/clut.html -void HaldCLUT::correct( const HaldCLUT::RawClut& clut, int level, float rr, float gg, float bb, float &outR, float &outG, float &outB ) -{ - int color, red, green, blue, i, j; - float tmp[6], r, g, b; - level = level * level; - - red = rr * (float)(level - 1); - - if(red > level - 2) { - red = (float)level - 2; - } - - if(red < 0) { - red = 0; - } - - green = gg * (float)(level - 1); - - if(green > level - 2) { - green = (float)level - 2; - } - - if(green < 0) { - green = 0; - } - - blue = bb * (float)(level - 1); - - if(blue > level - 2) { - blue = (float)level - 2; - } - - if(blue < 0) { - blue = 0; - } - - r = rr * (float)(level - 1) - red; - g = gg * (float)(level - 1) - green; - b = bb * (float)(level - 1) - blue; - - color = red + green * level + blue * level * level; - - i = color * 3; - j = (color + 1) * 3; - - tmp[0] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[1] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[2] = valF( clut[i] ) * (1 - r) + valF( clut[j] ) * r; - - i = (color + level) * 3; - j = (color + level + 1) * 3; - - tmp[3] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[4] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[5] = valF( clut[i] ) * (1 - r) + valF( clut[j] ) * r; - - outR = tmp[0] * (1 - g) + tmp[3] * g; - outG = tmp[1] * (1 - g) + tmp[4] * g; - outB = tmp[2] * (1 - g) + tmp[5] * g; - - i = (color + level * level) * 3; - j = (color + level * level + 1) * 3; - - tmp[0] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[1] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[2] = valF( clut[i] ) * (1 - r) + valF( clut[j] ) * r; - - i = (color + level + level * level) * 3; - j = (color + level + level * level + 1) * 3; - - tmp[3] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[4] = valF( clut[i++] ) * (1 - r) + valF( clut[j++] ) * r; - tmp[5] = valF( clut[i] ) * (1 - r) + valF( clut[j] ) * r; - - tmp[0] = tmp[0] * (1 - g) + tmp[3] * g; - tmp[1] = tmp[1] * (1 - g) + tmp[4] * g; - tmp[2] = tmp[2] * (1 - g) + tmp[5] * g; - - outR = outR * (1 - b) + tmp[0] * b; - outG = outG * (1 - b) + tmp[1] * b; - outB = outB * (1 - b) + tmp[2] * b; -} - -inline void pos2xy( int pos, int imageSideSize, int &outX, int &outY ) -{ - outX = pos / imageSideSize; - outY = pos % imageSideSize; -} - -void HaldCLUT::correct( Imagefloat &clutImage, int level, float rr, float gg, float bb, float &outR, float &outG, float &outB ) -{ - int color, red, green, blue, i, j; - float tmp[6], r, g, b; - level = level * level; - int imageSideSize = clutImage.getW(); - - red = rr * (float)(level - 1); - - if(red > level - 2) { - red = (float)level - 2; - } - - if(red < 0) { - red = 0; - } - - green = gg * (float)(level - 1); - - if(green > level - 2) { - green = (float)level - 2; - } - - if(green < 0) { - green = 0; - } - - blue = bb * (float)(level - 1); - - if(blue > level - 2) { - blue = (float)level - 2; - } - - if(blue < 0) { - blue = 0; - } - - r = rr * (float)(level - 1) - red; - g = gg * (float)(level - 1) - green; - b = bb * (float)(level - 1) - blue; - - color = red + green * level + blue * level * level; - - - i = color; - j = color + 1; - int xi, yi, xj, yj; - pos2xy( i, imageSideSize, xi, yi ); - pos2xy( j, imageSideSize, xj, yj ); - - tmp[0] = clutImage.r( xi, yi ) * (1 - r) + clutImage.r( xj, yj ) * r; - tmp[1] = clutImage.g( xi, yi ) * (1 - r) + clutImage.g( xj, yj ) * r; - tmp[2] = clutImage.b( xi, yi ) * (1 - r) + clutImage.b( xj, yj ) * r; - - i = color + level; - j = color + level + 1; - pos2xy( i, imageSideSize, xi, yi ); - pos2xy( j, imageSideSize, xj, yj ); - - tmp[3] = clutImage.r( xi, yi ) * (1 - r) + clutImage.r( xj, yj ) * r; - tmp[4] = clutImage.g( xi, yi ) * (1 - r) + clutImage.g( xj, yj ) * r; - tmp[5] = clutImage.b( xi, yi ) * (1 - r) + clutImage.b( xj, yj ) * r; - - outR = tmp[0] * (1 - g) + tmp[3] * g; - outG = tmp[1] * (1 - g) + tmp[4] * g; - outB = tmp[2] * (1 - g) + tmp[5] * g; - - i = color + level * level; - j = color + level * level + 1; - pos2xy( i, imageSideSize, xi, yi ); - pos2xy( j, imageSideSize, xj, yj ); - - tmp[0] = clutImage.r( xi, yi ) * (1 - r) + clutImage.r( xj, yj ) * r; - tmp[1] = clutImage.g( xi, yi ) * (1 - r) + clutImage.g( xj, yj ) * r; - tmp[2] = clutImage.b( xi, yi ) * (1 - r) + clutImage.b( xj, yj ) * r; - - i = color + level + level * level; - j = color + level + level * level + 1; - pos2xy( i, imageSideSize, xi, yi ); - pos2xy( j, imageSideSize, xj, yj ); - - tmp[3] = clutImage.r( xi, yi ) * (1 - r) + clutImage.r( xj, yj ) * r; - tmp[4] = clutImage.g( xi, yi ) * (1 - r) + clutImage.g( xj, yj ) * r; - tmp[5] = clutImage.b( xi, yi ) * (1 - r) + clutImage.b( xj, yj ) * r; - - tmp[0] = tmp[0] * (1 - g) + tmp[3] * g; - tmp[1] = tmp[1] * (1 - g) + tmp[4] * g; - tmp[2] = tmp[2] * (1 - g) + tmp[5] * g; - - outR = outR * (1 - b) + tmp[0] * b; - outG = outG * (1 - b) + tmp[1] * b; - outB = outB * (1 - b) + tmp[2] * b; } diff --git a/rtengine/clutstore.h b/rtengine/clutstore.h index de080b737..ce8fd9627 100644 --- a/rtengine/clutstore.h +++ b/rtengine/clutstore.h @@ -1,107 +1,78 @@ -#ifndef CLUT_STORE_INCLUDED -#define CLUT_STORE_INCLUDED +#pragma once + +#include #include -#include "../rtgui/threadutils.h" + #include "imagefloat.h" -#include -#include +#include "cache.h" namespace rtengine { -// simple CLUT interface class CLUT { public: - virtual void getRGB( float r, float g, float b, float &outR, float &outG, float &outB ) const = 0; - virtual Glib::ustring profile() const = 0; -protected: - virtual ~CLUT() {}; + CLUT() = default; + CLUT(const CLUT& other) = delete; + CLUT& operator =(const CLUT& other) = delete; + virtual ~CLUT() = default; + + virtual explicit operator bool() const = 0; + + virtual Glib::ustring getFilename() const = 0; + virtual Glib::ustring getProfile() const = 0; + + virtual void getRGB(float r, float g, float b, float& out_r, float& out_g, float& out_b) const = 0; + + static void splitClutFilename( + const Glib::ustring& filename, + Glib::ustring& name, + Glib::ustring& extension, + Glib::ustring& profile_name + ); }; -class HaldCLUT : public CLUT +class HaldCLUT + : public CLUT { public: HaldCLUT(); ~HaldCLUT(); - void load( Glib::ustring filename ); - bool isValid() const; - void getRGB( float r, float g, float b, float &outR, float &outG, float &outB ) const; - Glib::ustring profile() const; + bool load(const Glib::ustring& filename); - typedef std::vector RawClut; // using 8 bit for reduce memory usage - static void correct( const RawClut&, int level, float r, float g, float b, float &outR, float &outG, float &outB ); - static void correct( Imagefloat &clutImage, int level, float rr, float gg, float bb, float &outR, float &outG, float &outB ); - static Imagefloat* generateIdentImage( int level ); - static Imagefloat* loadFile( Glib::ustring filename, Glib::ustring workingColorSpace, int &outLevel ); + explicit operator bool() const; + + Glib::ustring getFilename() const; + Glib::ustring getProfile() const; + + void getRGB(float r, float g, float b, float& out_r, float& out_g, float& out_b) const; private: - - void loadClut( Imagefloat *img, RawClut &outClut ); - - Imagefloat *m_clutImage; - int m_level; - Glib::ustring m_filename; - Glib::ustring m_profile; + std::unique_ptr clut_image; + unsigned int clut_level; + Glib::ustring clut_filename; + Glib::ustring clut_profile; }; -// CLUT cache class CLUTStore { public: - CLUTStore(); + static CLUTStore& getInstance(); - CLUT* getClut( const Glib::ustring& filename ); - void releaseClut( const CLUT* clut ); + CLUTStore(const CLUTStore& other) = delete; + CLUTStore& operator =(const CLUTStore& other) = delete; + + std::shared_ptr getClut(const Glib::ustring& filename); + void releaseClut(const std::shared_ptr& clut); void clearCache(); private: - typedef std::map > Cluts; + CLUTStore(); - Cluts m_cluts; - MyMutex m_mutex; + Cache> cache; }; -void splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName ); - -}; //namespace rtengine - -extern rtengine::CLUTStore clutStore; - -namespace rtengine -{ - -//support class for automate call of clutStore.releaseClut() -class ClutPtr -{ -public: - ClutPtr() : m_point( 0 ) {} - explicit ClutPtr(CLUT *p) : m_point( p ) {} - ~ClutPtr() - { - clutStore.releaseClut( m_point ); - } - const CLUT* operator-> () const - { - return m_point; - } - operator bool() const - { - return m_point != 0; - } - void set( CLUT *p ) - { - m_point = p; - } - -private: - ClutPtr& operator=(ClutPtr const& cp ); - CLUT *m_point; -}; - -}; //namespace rtengine - -#endif +} diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 989d9c6a7..2732c567d 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -3205,21 +3205,21 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } - ClutPtr colorLUT; + std::shared_ptr colorLUT; bool clutAndWorkingProfilesAreSame = false; TMatrix work2xyz, xyz2clut, clut2xyz, xyz2work; if ( params->filmSimulation.enabled && !params->filmSimulation.clutFilename.empty() ) { - colorLUT.set( clutStore.getClut( params->filmSimulation.clutFilename ) ); + colorLUT = CLUTStore::getInstance().getClut( params->filmSimulation.clutFilename ); if ( colorLUT ) { - clutAndWorkingProfilesAreSame = colorLUT->profile() == params->icm.working; + clutAndWorkingProfilesAreSame = colorLUT->getProfile() == params->icm.working; if ( !clutAndWorkingProfilesAreSame ) { work2xyz = iccStore->workingSpaceMatrix( params->icm.working ); - xyz2clut = iccStore->workingSpaceInverseMatrix( colorLUT->profile() ); + xyz2clut = iccStore->workingSpaceInverseMatrix( colorLUT->getProfile() ); xyz2work = iccStore->workingSpaceInverseMatrix( params->icm.working ); - clut2xyz = iccStore->workingSpaceMatrix( colorLUT->profile() ); + clut2xyz = iccStore->workingSpaceMatrix( colorLUT->getProfile() ); } } } @@ -4337,6 +4337,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer //Film Simulations if ( colorLUT ) { + MyTime start, stop; + start.set(); for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { float &sourceR = rtemp[ti * TS + tj]; @@ -4375,6 +4377,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } + stop.set(); + printf("Film simulation took %dus.\n", stop.etime(start)); } diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index c03addb42..bda0f84b0 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -857,7 +857,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p // if clut was used and size of clut cache == 1 we free the memory used by the clutstore (default clut cache size = 1 for 32 bit OS) if ( params.filmSimulation.enabled && !params.filmSimulation.clutFilename.empty() && options.clutCacheSize == 1) { - clutStore.clearCache(); + CLUTStore::getInstance().clearCache(); } // freeing up some memory diff --git a/rtgui/filmsimulation.cc b/rtgui/filmsimulation.cc index 07f85df94..3ee1f4742 100644 --- a/rtgui/filmsimulation.cc +++ b/rtgui/filmsimulation.cc @@ -72,7 +72,7 @@ void FilmSimulation::onClutSelected() if ( getEnabled() && !currentClutFilename.empty() && listener && currentClutFilename != m_oldClutFilename ) { Glib::ustring clutName, dummy; - splitClutFilename( currentClutFilename, clutName, dummy, dummy ); + CLUT::splitClutFilename( currentClutFilename, clutName, dummy, dummy ); listener->panelChanged( EvFilmSimulationFilename, clutName ); m_oldClutFilename = currentClutFilename; @@ -132,7 +132,7 @@ void FilmSimulation::read( const rtengine::procparams::ProcParams* pp, const Par if ( !get_inconsistent() && !pp->filmSimulation.enabled ) { if (options.clutCacheSize == 1) { - clutStore.clearCache(); + CLUTStore::getInstance().clearCache(); } } @@ -279,7 +279,7 @@ int ClutComboBox::parseDir (const Glib::ustring& path) for (const auto& entry : entries) { Glib::ustring name, extension, profileName; - splitClutFilename (entry, name, extension, profileName); + CLUT::splitClutFilename (entry, name, extension, profileName); extension = extension.casefold (); if (extension.compare ("tif") != 0 && extension.compare ("png") != 0) {