Merge pull request #3862 from mapnik/apply_color_blind_filter-fix
Fix apply_color_blind_filter to use correct color-space and avoid NAN…
This commit is contained in:
commit
ff56c86446
3 changed files with 41 additions and 60 deletions
|
@ -28,7 +28,6 @@
|
||||||
#include <mapnik/image_filter_types.hpp>
|
#include <mapnik/image_filter_types.hpp>
|
||||||
#include <mapnik/image_util.hpp>
|
#include <mapnik/image_util.hpp>
|
||||||
#include <mapnik/util/hsl.hpp>
|
#include <mapnik/util/hsl.hpp>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#include <mapnik/warning_ignore.hpp>
|
#include <mapnik/warning_ignore.hpp>
|
||||||
#include <boost/gil/gil_all.hpp>
|
#include <boost/gil/gil_all.hpp>
|
||||||
|
@ -687,36 +686,37 @@ void apply_color_blind_filter(Src & src, ColorBlindFilter const& op)
|
||||||
rgba8_view_t src_view = rgba8_view(src);
|
rgba8_view_t src_view = rgba8_view(src);
|
||||||
bool premultiplied = src.get_premultiplied();
|
bool premultiplied = src.get_premultiplied();
|
||||||
|
|
||||||
|
static constexpr double gamma = 2.2;
|
||||||
|
static constexpr double inv_gamma = 1.0/gamma;
|
||||||
|
|
||||||
for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
|
for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
|
||||||
{
|
{
|
||||||
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
|
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
|
||||||
for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
|
for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
|
||||||
{
|
{
|
||||||
// formula taken from boost/gil/color_convert.hpp:rgb_to_luminance
|
|
||||||
uint8_t & r = get_color(src_it[x], red_t());
|
uint8_t & r = get_color(src_it[x], red_t());
|
||||||
uint8_t & g = get_color(src_it[x], green_t());
|
uint8_t & g = get_color(src_it[x], green_t());
|
||||||
uint8_t & b = get_color(src_it[x], blue_t());
|
uint8_t & b = get_color(src_it[x], blue_t());
|
||||||
uint8_t & a = get_color(src_it[x], alpha_t());
|
uint8_t & a = get_color(src_it[x], alpha_t());
|
||||||
double dr = static_cast<double>(r)/255.0;
|
|
||||||
double dg = static_cast<double>(g)/255.0;
|
|
||||||
double db = static_cast<double>(b)/255.0;
|
|
||||||
double da = static_cast<double>(a)/255.0;
|
|
||||||
// demultiply
|
// demultiply
|
||||||
if (da <= 0.0)
|
if (a == 0)
|
||||||
{
|
{
|
||||||
r = g = b = 0;
|
r = g = b = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (premultiplied)
|
else if (premultiplied)
|
||||||
{
|
{
|
||||||
dr /= da;
|
std::uint32_t cr = (r * 255) / a;
|
||||||
dg /= da;
|
std::uint32_t cg = (g * 255) / a;
|
||||||
db /= da;
|
std::uint32_t cb = (b * 255) / a;
|
||||||
|
r = static_cast<uint8_t>((cr > 255) ? 255 : cr);
|
||||||
|
g = static_cast<uint8_t>((cg > 255) ? 255 : cg);
|
||||||
|
b = static_cast<uint8_t>((cb > 255) ? 255 : cb);
|
||||||
}
|
}
|
||||||
// Convert source color into XYZ color space
|
// Convert source color into XYZ color space
|
||||||
double pow_r = std::pow(dr, 2.2);
|
double pow_r = std::pow(r, gamma);
|
||||||
double pow_g = std::pow(dg, 2.2);
|
double pow_g = std::pow(g, gamma);
|
||||||
double pow_b = std::pow(db, 2.2);
|
double pow_b = std::pow(b, gamma);
|
||||||
double X = (0.412424 * pow_r) + (0.357579 * pow_g) + (0.180464 * pow_b);
|
double X = (0.412424 * pow_r) + (0.357579 * pow_g) + (0.180464 * pow_b);
|
||||||
double Y = (0.212656 * pow_r) + (0.715158 * pow_g) + (0.0721856 * pow_b);
|
double Y = (0.212656 * pow_r) + (0.715158 * pow_g) + (0.0721856 * pow_b);
|
||||||
double Z = (0.0193324 * pow_r) + (0.119193 * pow_g) + (0.950444 * pow_b);
|
double Z = (0.0193324 * pow_r) + (0.119193 * pow_g) + (0.950444 * pow_b);
|
||||||
|
@ -733,10 +733,6 @@ void apply_color_blind_filter(Src & src, ColorBlindFilter const& op)
|
||||||
if (std::abs(m_div2) < (std::numeric_limits<double>::epsilon())) continue;
|
if (std::abs(m_div2) < (std::numeric_limits<double>::epsilon())) continue;
|
||||||
double deviate_x = (op.yint - yint) / (m - op.m);
|
double deviate_x = (op.yint - yint) / (m - op.m);
|
||||||
double deviate_y = (m * deviate_x) + yint;
|
double deviate_y = (m * deviate_x) + yint;
|
||||||
if (std::abs(deviate_y) < (std::numeric_limits<double>::epsilon()))
|
|
||||||
{
|
|
||||||
deviate_y = std::numeric_limits<double>::epsilon() * 2.0;
|
|
||||||
}
|
|
||||||
// Compute the simulated color's XYZ coords
|
// Compute the simulated color's XYZ coords
|
||||||
X = deviate_x * Y / deviate_y;
|
X = deviate_x * Y / deviate_y;
|
||||||
Z = (1.0 - (deviate_x + deviate_y)) * Y / deviate_y;
|
Z = (1.0 - (deviate_x + deviate_y)) * Y / deviate_y;
|
||||||
|
@ -746,25 +742,14 @@ void apply_color_blind_filter(Src & src, ColorBlindFilter const& op)
|
||||||
// Difference between simulated color and neutral grey
|
// Difference between simulated color and neutral grey
|
||||||
double diff_X = neutral_X - X;
|
double diff_X = neutral_X - X;
|
||||||
double diff_Z = neutral_Z - Z;
|
double diff_Z = neutral_Z - Z;
|
||||||
double diff_r = diff_X * 3.24071 + diff_Z * -0.498571; // XYZ->RGB (sRGB:D65)
|
// XYZ->RGB (sRGB:D65)
|
||||||
double diff_g = diff_X * -0.969258 + diff_Z * 0.0415557;
|
double diff_r = diff_X * 3.2407100 + diff_Z *-0.4985710;
|
||||||
double diff_b = diff_X * 0.0556352 + diff_Z * 1.05707;
|
double diff_g = diff_X *-0.9692580 + diff_Z * 0.0415557;
|
||||||
if (std::abs(diff_r) < (std::numeric_limits<double>::epsilon()))
|
double diff_b = diff_X * 0.0556352 + diff_Z * 1.0570700;
|
||||||
{
|
// XYZ->RGB (sRGB:D65)
|
||||||
diff_r = std::numeric_limits<double>::epsilon() * 2.0;
|
double dr = X * 3.2407100 + Y *-1.537260 + Z *-0.4985710;
|
||||||
}
|
double dg = X *-0.9692580 + Y * 1.875990 + Z * 0.0415557;
|
||||||
if (std::abs(diff_g) < (std::numeric_limits<double>::epsilon()))
|
double db = X * 0.0556352 + Y *-0.203996 + Z * 1.0570700;
|
||||||
{
|
|
||||||
diff_g = std::numeric_limits<double>::epsilon() * 2.0;
|
|
||||||
}
|
|
||||||
if (std::abs(diff_b) < (std::numeric_limits<double>::epsilon()))
|
|
||||||
{
|
|
||||||
diff_b = std::numeric_limits<double>::epsilon() * 2.0;
|
|
||||||
}
|
|
||||||
// Convert to RGB color space
|
|
||||||
dr = X * 3.24071 + Y * -1.53726 + Z * -0.498571; // XYZ->RGB (sRGB:D65)
|
|
||||||
dg = X * -0.969258 + Y * 1.87599 + Z * 0.0415557;
|
|
||||||
db = X * 0.0556352 + Y * -0.203996 + Z * 1.05707;
|
|
||||||
// Compensate simulated color towards a neutral fit in RGB space
|
// Compensate simulated color towards a neutral fit in RGB space
|
||||||
double fit_r = ((dr < 0.0 ? 0.0 : 1.0) - dr) / diff_r;
|
double fit_r = ((dr < 0.0 ? 0.0 : 1.0) - dr) / diff_r;
|
||||||
double fit_g = ((dg < 0.0 ? 0.0 : 1.0) - dg) / diff_g;
|
double fit_g = ((dg < 0.0 ? 0.0 : 1.0) - dg) / diff_g;
|
||||||
|
@ -774,27 +759,24 @@ void apply_color_blind_filter(Src & src, ColorBlindFilter const& op)
|
||||||
);
|
);
|
||||||
adjust = std::max((fit_b > 1.0 || fit_b < 0.0) ? 0.0 : fit_b, adjust);
|
adjust = std::max((fit_b > 1.0 || fit_b < 0.0) ? 0.0 : fit_b, adjust);
|
||||||
// Shift proportional to the greatest shift
|
// Shift proportional to the greatest shift
|
||||||
dr = dr + (adjust * diff_r);
|
dr += adjust * diff_r;
|
||||||
dg = dg + (adjust * diff_g);
|
dg += adjust * diff_g;
|
||||||
db = db + (adjust * diff_b);
|
db += adjust * diff_b;
|
||||||
// Apply gamma correction
|
// Apply gamma correction
|
||||||
dr = std::pow(dr, 1.0 / 2.2);
|
dr = std::pow(dr, inv_gamma);
|
||||||
dg = std::pow(dg, 1.0 / 2.2);
|
dg = std::pow(dg, inv_gamma);
|
||||||
db = std::pow(db, 1.0 / 2.2);
|
db = std::pow(db, inv_gamma);
|
||||||
// premultiply
|
|
||||||
dr *= da;
|
|
||||||
dg *= da;
|
|
||||||
db *= da;
|
|
||||||
// Clamp values
|
// Clamp values
|
||||||
if(dr < 0.0) dr = 0.0;
|
if(dr < 0.0 || std::isnan(dr)) dr = 0.0;
|
||||||
if(dr > 1.0) dr = 1.0;
|
if(dr > 255.0) dr = 255.0;
|
||||||
if(dg < 0.0) dg = 0.0;
|
if(dg < 0.0 || std::isnan(dg)) dg = 0.0;
|
||||||
if(dg > 1.0) dg = 1.0;
|
if(dg > 255.0) dg = 255.0;
|
||||||
if(db < 0.0) db = 0.0;
|
if(db < 0.0 || std::isnan(db)) db = 0.0;
|
||||||
if(db > 1.0) db = 1.0;
|
if(db > 255.0) db = 255.0;
|
||||||
r = static_cast<uint8_t>(dr * 255.0);
|
// premultiply
|
||||||
g = static_cast<uint8_t>(dg * 255.0);
|
r = (static_cast<uint8_t>(dr) * a + 255) >> 8;
|
||||||
b = static_cast<uint8_t>(db * 255.0);
|
g = (static_cast<uint8_t>(dg) * a + 255) >> 8;
|
||||||
|
b = (static_cast<uint8_t>(db) * a + 255) >> 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set as premultiplied
|
// set as premultiplied
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d975838e0f6fa75cb3b3574b43ae205979508f67
|
Subproject commit a270d939f1b2b978203b9a91a3cdb4eff9f9f826
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
// mapnik
|
// mapnik
|
||||||
|
@ -482,7 +481,7 @@ SECTION("test color-blind-protanope") {
|
||||||
|
|
||||||
CHECK(im(0,0) == 0xff9a4a00);
|
CHECK(im(0,0) == 0xff9a4a00);
|
||||||
CHECK(im(0,1) == 0xff006e7c);
|
CHECK(im(0,1) == 0xff006e7c);
|
||||||
CHECK(im(1,0) == 0xffd9f6ff);
|
CHECK(im(1,0) == 0xff00f7ff);
|
||||||
CHECK(im(1,1) == 0xff1d7e8e);
|
CHECK(im(1,1) == 0xff1d7e8e);
|
||||||
|
|
||||||
} // END SECTION
|
} // END SECTION
|
||||||
|
@ -499,7 +498,7 @@ SECTION("test color-blind-deuteranope") {
|
||||||
|
|
||||||
CHECK(im(0,0) == 0xff824f00);
|
CHECK(im(0,0) == 0xff824f00);
|
||||||
CHECK(im(0,1) == 0xff1c688b);
|
CHECK(im(0,1) == 0xff1c688b);
|
||||||
CHECK(im(1,0) == 0xffe9f5ff);
|
CHECK(im(1,0) == 0xff27e9ff);
|
||||||
CHECK(im(1,1) == 0xff0077a0);
|
CHECK(im(1,1) == 0xff0077a0);
|
||||||
|
|
||||||
} // END SECTION
|
} // END SECTION
|
||||||
|
@ -516,7 +515,7 @@ SECTION("test color-blind-tritanope") {
|
||||||
|
|
||||||
CHECK(im(0,0) == 0xff595500);
|
CHECK(im(0,0) == 0xff595500);
|
||||||
CHECK(im(0,1) == 0xff80763a);
|
CHECK(im(0,1) == 0xff80763a);
|
||||||
CHECK(im(1,0) == 0xfff8f3ff);
|
CHECK(im(1,0) == 0xfffeecff);
|
||||||
CHECK(im(1,1) == 0xff0017fd);
|
CHECK(im(1,1) == 0xff0017fd);
|
||||||
|
|
||||||
} // END SECTION
|
} // END SECTION
|
||||||
|
|
Loading…
Reference in a new issue