Clean up clutstore.* and add LRU cache

This commit adds a true LRU cache to `rtengine` which is used in the new
`CLUTStore` class. The code in `clutstore.*` was cleaned up with C++11
features and small optimizations taken from my `clutbench` project.
The `CLUTStore` class was converted to a true singleton.
This commit is contained in:
Flössie
2016-04-16 22:20:18 +02:00
parent e61e488346
commit e495093b18
6 changed files with 476 additions and 472 deletions

View File

@@ -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<rtengine::Imagefloat> 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<rtengine::Imagefloat> 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<Glib::ustring> workingProfiles = rtengine::getWorkingProfiles();
for ( std::vector<Glib::ustring>::iterator it = workingProfiles.begin(); it != workingProfiles.end(); ++it ) {
Glib::ustring &currentProfile = *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<rtengine::Imagefloat>(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<bool>(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<float>(level - 1) / 65535.0f;
const float flevel_minus_two = static_cast<float>(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::CLUT> rtengine::CLUTStore::getClut(const Glib::ustring& filename)
{
std::shared_ptr<rtengine::CLUT> result;
if (!cache.get(filename, result)) {
std::unique_ptr<rtengine::HaldCLUT> 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<rtengine::CLUT>& 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;
}