/* === S Y N F I G ========================================================= */
/*! \file synfig/rendering/software/task/taskpixelgammasw.cpp
** \brief TaskPixelGammaSW
**
** $Id$
**
** \legal
** ......... ... 2016-2018 Ivan Mahonin
**
** This package 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 2 of
** the License, or (at your option) any later version.
**
** This package 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.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <synfig/debug/debugsurface.h>
#include <synfig/general.h>
#include "../../common/task/taskpixelprocessor.h"
#include "tasksw.h"
#endif
using namespace synfig;
using namespace rendering;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
namespace {
class TaskPixelGammaSW: public TaskPixelGamma, public TaskSW
{
public:
typedef etl::handle<TaskPixelGammaSW> Handle;
static Token token;
virtual Token::Handle get_token() const { return token.handle(); }
private:
typedef void Func(ColorReal &dst, const ColorReal &src, const ColorReal &gamma);
struct Params
{
ColorReal *dst;
int dst_stride;
const ColorReal *src;
int src_stride;
int width;
int height;
union {
ColorReal gamma[3];
struct {
ColorReal gamma_r, gamma_g, gamma_b;
};
};
Params():
dst(), dst_stride(),
src(), src_stride(),
width(), height(),
gamma_r(1.0), gamma_g(1.0), gamma_b(1.0)
{ }
Params(
Color *dst,
int dst_stride,
const Color *src,
int src_stride,
int width,
int height,
ColorReal gamma_r,
ColorReal gamma_g,
ColorReal gamma_b
):
dst((ColorReal*)dst), dst_stride(dst_stride),
src((const ColorReal*)src), src_stride(src_stride),
width(width), height(height),
gamma_r(gamma_r), gamma_g(gamma_g), gamma_b(gamma_b)
{ }
};
static inline ColorReal clamp(const ColorReal &x)
{
const ColorReal max = ColorReal(1.0)/real_low_precision<ColorReal>();
return std::max(-max, std::min(max, x));
}
static inline ColorReal clamp_positive(const ColorReal &x)
{
const ColorReal max = ColorReal(1.0)/real_low_precision<ColorReal>();
return std::max(real_low_precision<ColorReal>(), std::min(max, x));
}
static inline void func_none(ColorReal&, const ColorReal&, const ColorReal&) { }
static inline void func_copy(ColorReal &dst, const ColorReal &src, const ColorReal&)
{ dst = src; }
static inline void func_one(ColorReal &dst, const ColorReal &, const ColorReal &)
{ dst = ColorReal(1.0); }
static inline void func_pow(ColorReal &dst, const ColorReal &src, const ColorReal &gamma)
{ dst = clamp(src < 0 ? -pow(-src, gamma) : pow(src, gamma)); }
template<Func fr, Func fg, Func fb>
static void process_rgb(const Params &p) {
if (p.src == p.dst)
{
assert(p.src_stride == p.dst_stride);
int dst_dr = 4*(p.dst_stride - p.width);
int row_size = 4*p.width;
ColorReal *dst = p.dst;
for(ColorReal *dst_end = dst + 4*p.dst_stride*p.height; dst != dst_end; dst += dst_dr)
{
for(ColorReal *dst_row_end = dst + row_size; dst != dst_row_end; dst += 4)
{
fr(dst[0], dst[0], p.gamma_r);
fg(dst[1], dst[1], p.gamma_g);
fb(dst[2], dst[2], p.gamma_b);
}
}
}
else
{
assert(p.src + 4*p.src_stride*p.height <= p.dst || p.dst + 4*p.dst_stride*p.height <= p.src);
int dst_dr = 4*(p.dst_stride - p.width);
int src_dr = 4*(p.src_stride - p.width);
int row_size = 4*p.width;
ColorReal *dst = p.dst;
const ColorReal *src = p.src;
for(ColorReal *dst_end = dst + 4*p.dst_stride*p.height; dst != dst_end; dst += dst_dr, src += src_dr)
{
for(ColorReal *dst_row_end = dst + row_size; dst != dst_row_end; dst += 4, src += 4)
{
fr(dst[0], src[0], p.gamma_r);
fg(dst[1], src[1], p.gamma_g);
fb(dst[2], src[2], p.gamma_b);
dst[3] = src[3];
}
}
}
}
template<Func fr, Func fg>
static void process_rg(const Params &p) {
if ( approximate_equal_lp(p.gamma_b, ColorReal(0.0))) process_rgb<fr, fg, func_one >(p); else
if (!approximate_equal_lp(p.gamma_b, ColorReal(1.0))) process_rgb<fr, fg, func_pow >(p); else
if (p.src == p.dst) process_rgb<fr, fg, func_none>(p); else
process_rgb<fr, fg, func_copy>(p);
}
template<Func fr>
static void process_r(const Params &p) {
if ( approximate_equal_lp(p.gamma_g, ColorReal(0.0))) process_rg<fr, func_one >(p); else
if (!approximate_equal_lp(p.gamma_g, ColorReal(1.0))) process_rg<fr, func_pow >(p); else
if (p.src == p.dst) process_rg<fr, func_none>(p); else
process_rg<fr, func_copy>(p);
}
static void process(const Params &p) {
if ( approximate_equal_lp(p.gamma_r, ColorReal(0.0))) process_r<func_one >(p); else
if (!approximate_equal_lp(p.gamma_r, ColorReal(1.0))) process_r<func_pow >(p); else
if (p.src == p.dst) process_r<func_none>(p); else
process_r<func_copy>(p);
}
public:
virtual bool run(RunParams&) const {
if (!is_valid() || !sub_task() || !sub_task()->is_valid())
return true;
RectInt rd = target_rect;
VectorInt offset = get_offset();
RectInt rs = sub_task()->target_rect + rd.get_min() + offset;
etl::set_intersect(rs, rs, rd);
if (rs.is_valid())
{
LockWrite ldst(this);
if (!ldst) return false;
LockRead lsrc(sub_task());
if (!lsrc) return false;
synfig::Surface &dst = ldst->get_surface();
const synfig::Surface &src = lsrc->get_surface();
process(Params(
&dst[rs.miny][rs.minx],
dst.get_pitch()/sizeof(Color),
&src[rs.miny - rd.miny - offset[1]][rs.minx - rd.minx - offset[0]],
src.get_pitch()/sizeof(Color),
rs.get_width(),
rs.get_height(),
clamp_positive(gamma.get_r()),
clamp_positive(gamma.get_g()),
clamp_positive(gamma.get_b()) ));
}
return true;
}
};
Task::Token TaskPixelGammaSW::token(
DescReal<TaskPixelGammaSW, TaskPixelGamma>("PixelGammaSW") );
} // end of anonimous namespace
/* === E N T R Y P O I N T ================================================= */