mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-16 11:19:18 +01:00
237 lines
7.6 KiB
C++
237 lines
7.6 KiB
C++
/*
|
|
* RenderItemMergeFunction.hpp
|
|
*
|
|
* Created on: Feb 16, 2009
|
|
* Author: struktured
|
|
*/
|
|
|
|
#ifndef RenderItemMergeFunction_HPP_
|
|
#define RenderItemMergeFunction_HPP_
|
|
|
|
#include "Common.hpp"
|
|
#include "Renderable.hpp"
|
|
#include "Waveform.hpp"
|
|
#include <limits>
|
|
#include <functional>
|
|
#include <map>
|
|
|
|
|
|
template <class T>
|
|
inline T interpolate(T a, T b, float ratio)
|
|
{
|
|
return (ratio*a + (1-ratio)*b) * 0.5;
|
|
}
|
|
|
|
template <>
|
|
inline int interpolate(int a, int b, float ratio)
|
|
{
|
|
return (int)(ratio*(float)a + (1-ratio)*(float)b) * 0.5;
|
|
}
|
|
|
|
template <>
|
|
inline bool interpolate(bool a, bool b, float ratio)
|
|
{
|
|
return (ratio >= 0.5) ? a : b;
|
|
}
|
|
|
|
|
|
/// Merges two render items and returns zero if they are virtually equivalent and large values
|
|
/// when they are dissimilar. If two render items cannot be compared, NOT_COMPARABLE_VALUE is returned.
|
|
class RenderItemMergeFunction {
|
|
public:
|
|
virtual RenderItem * operator()(const RenderItem * r1, const RenderItem * r2, double ratio) const = 0;
|
|
virtual TypeIdPair typeIdPair() const = 0;
|
|
};
|
|
|
|
/// A base class to construct render item distance mergeFunctions. Just specify your two concrete
|
|
/// render item types as template parameters and override the computeMerge() function.
|
|
template <class R1, class R2=R1, class R3=R2>
|
|
class RenderItemMerge : public RenderItemMergeFunction {
|
|
|
|
protected:
|
|
/// Override to create your own distance mergeFunction for your specified custom types.
|
|
virtual R3 * computeMerge(const R1 * r1, const R2 * r2, double ratio) const = 0;
|
|
|
|
public:
|
|
|
|
inline virtual R3 * operator()(const RenderItem * r1, const RenderItem * r2, double ratio) const {
|
|
if (supported(r1, r2))
|
|
return computeMerge(dynamic_cast<const R1*>(r1), dynamic_cast<const R2*>(r2), ratio);
|
|
else if (supported(r2,r1))
|
|
return computeMerge(dynamic_cast<const R1*>(r2), dynamic_cast<const R2*>(r1), ratio);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/// Returns true if and only if r1 and r2 are of type R1 and R2 respectively.
|
|
inline bool supported(const RenderItem * r1, const RenderItem * r2) const {
|
|
return typeid(r1) == typeid(const R1 *) && typeid(r2) == typeid(const R2 *);
|
|
}
|
|
|
|
inline TypeIdPair typeIdPair() const {
|
|
return TypeIdPair(typeid(const R1*).name(), typeid(const R2*).name());
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class ShapeMerge : public RenderItemMerge<Shape> {
|
|
|
|
public:
|
|
|
|
ShapeMerge() {}
|
|
virtual ~ShapeMerge() {}
|
|
|
|
protected:
|
|
|
|
virtual inline Shape * computeMerge(const Shape * lhs, const Shape * rhs, double ratio) const {
|
|
|
|
Shape * ret = new Shape();
|
|
Shape & target = *ret;
|
|
|
|
target.x = interpolate(lhs->x, rhs->x, ratio);
|
|
target.y = interpolate(lhs->y, rhs->y, ratio);
|
|
target.a = interpolate(lhs->a, rhs->a, ratio);
|
|
target.a2 = interpolate(lhs->a2, rhs->a2, ratio);
|
|
target.r = interpolate(lhs->r, rhs->r, ratio);
|
|
target.r2 = interpolate(lhs->r2, rhs->r2, ratio);
|
|
target.g = interpolate(lhs->g, rhs->g, ratio);
|
|
target.g2 = interpolate(lhs->g2, rhs->g2, ratio);
|
|
target.b = interpolate(lhs->b, rhs->b, ratio);
|
|
target.b2 = interpolate(lhs->b2, rhs->b2, ratio);
|
|
|
|
target.ang = interpolate(lhs->ang, rhs->ang, ratio);
|
|
target.radius = interpolate(lhs->radius, rhs->radius, ratio);
|
|
|
|
target.tex_ang = interpolate(lhs->tex_ang, rhs->tex_ang, ratio);
|
|
target.tex_zoom = interpolate(lhs->tex_zoom, rhs->tex_zoom, ratio);
|
|
|
|
target.border_a = interpolate(lhs->border_a, rhs->border_a, ratio);
|
|
target.border_r = interpolate(lhs->border_r, rhs->border_r, ratio);
|
|
target.border_g = interpolate(lhs->border_g, rhs->border_g, ratio);
|
|
target.border_b = interpolate(lhs->border_b, rhs->border_b, ratio);
|
|
|
|
target.sides = interpolate(lhs->sides, rhs->sides, ratio);
|
|
|
|
target.additive = interpolate(lhs->additive, rhs->additive, ratio);
|
|
target.textured = interpolate(lhs->textured, rhs->textured, ratio);
|
|
target.thickOutline = interpolate(lhs->thickOutline, rhs->thickOutline, ratio);
|
|
target.enabled = interpolate(lhs->enabled, rhs->enabled, ratio);
|
|
|
|
target.masterAlpha = interpolate(lhs->masterAlpha, rhs->masterAlpha, ratio);
|
|
target.imageUrl = (ratio > 0.5) ? lhs->imageUrl : rhs->imageUrl, ratio;
|
|
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
class BorderMerge : public RenderItemMerge<Border> {
|
|
|
|
public:
|
|
|
|
BorderMerge() {}
|
|
virtual ~BorderMerge() {}
|
|
|
|
protected:
|
|
|
|
virtual inline Border * computeMerge(const Border * lhs, const Border * rhs, double ratio) const
|
|
{
|
|
Border * ret = new Border();
|
|
|
|
Border & target = *ret;
|
|
|
|
target.inner_a = interpolate(lhs->inner_a, rhs->inner_a, ratio);
|
|
target.inner_r = interpolate(lhs->inner_r, rhs->inner_r, ratio);
|
|
target.inner_g = interpolate(lhs->inner_g, rhs->inner_g, ratio);
|
|
target.inner_b = interpolate(lhs->inner_b, rhs->inner_b, ratio);
|
|
target.inner_size = interpolate(lhs->inner_size, rhs->inner_size, ratio);
|
|
|
|
target.outer_a = interpolate(lhs->outer_a, rhs->outer_a, ratio);
|
|
target.outer_r = interpolate(lhs->outer_r, rhs->outer_r, ratio);
|
|
target.outer_g = interpolate(lhs->outer_g, rhs->outer_g, ratio);
|
|
target.outer_b = interpolate(lhs->outer_b, rhs->outer_b, ratio);
|
|
target.outer_size = interpolate(lhs->outer_size, rhs->outer_size, ratio);
|
|
|
|
target.masterAlpha = interpolate(lhs->masterAlpha, rhs->masterAlpha, ratio);
|
|
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
|
|
class WaveformMerge : public RenderItemMerge<Waveform> {
|
|
|
|
public:
|
|
|
|
WaveformMerge() {}
|
|
virtual ~WaveformMerge() {}
|
|
|
|
protected:
|
|
|
|
/// @BUG unimplemented
|
|
virtual inline Waveform * computeMerge(const Waveform * lhs, const Waveform * rhs, double ratio) const
|
|
{
|
|
return 0;
|
|
/*
|
|
Waveform * ret = new Waveform();
|
|
Waveform & target = *ret;
|
|
|
|
target.additive = interpolate(lhs->additive, rhs->additive, ratio);
|
|
target.dots = interpolate(lhs->dots, rhs->dots, ratio);
|
|
target.samples = (rhs->samples > lhs-> samples) ? lhs->samples : rhs->samples;
|
|
target.scaling = interpolate(lhs->scaling, rhs->scaling, ratio);
|
|
target.sep = interpolate(lhs->sep, rhs->sep, ratio);
|
|
target.smoothing = interpolate(lhs->smoothing, rhs->smoothing, ratio);
|
|
target.spectrum = interpolate(lhs->spectrum, rhs->spectrum, ratio);
|
|
target.thick = interpolate(lhs->thick, rhs->thick, ratio);
|
|
target.masterAlpha = interpolate(lhs->masterAlpha, rhs->masterAlpha, ratio);
|
|
|
|
return ret;
|
|
*/
|
|
}
|
|
};
|
|
|
|
|
|
/// Use as the top level merge function. It stores a map of all other
|
|
/// merge functions, using the function that fits best with the
|
|
/// incoming type parameters.
|
|
class MasterRenderItemMerge : public RenderItemMerge<RenderItem> {
|
|
|
|
typedef std::map<TypeIdPair, RenderItemMergeFunction*> MergeFunctionMap;
|
|
public:
|
|
|
|
MasterRenderItemMerge() {}
|
|
virtual ~MasterRenderItemMerge() {}
|
|
|
|
inline void add(RenderItemMergeFunction * fun) {
|
|
_mergeFunctionMap[fun->typeIdPair()] = fun;
|
|
}
|
|
|
|
protected:
|
|
virtual inline RenderItem * computeMerge(const RenderItem * lhs, const RenderItem * rhs, double ratio) const {
|
|
|
|
RenderItemMergeFunction * mergeFunction;
|
|
|
|
TypeIdPair pair(typeid(lhs), typeid(rhs));
|
|
if (_mergeFunctionMap.count(pair)) {
|
|
mergeFunction = _mergeFunctionMap[pair];
|
|
} else if (_mergeFunctionMap.count(pair = TypeIdPair(typeid(rhs), typeid(lhs)))) {
|
|
mergeFunction = _mergeFunctionMap[pair];
|
|
} else {
|
|
mergeFunction = 0;
|
|
}
|
|
|
|
// If specialized mergeFunction exists, use it to get higher granularity
|
|
// of correctness
|
|
if (mergeFunction)
|
|
return (*mergeFunction)(lhs, rhs, ratio);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
mutable MergeFunctionMap _mergeFunctionMap;
|
|
};
|
|
|
|
#endif /* RenderItemMergeFunction_HPP_ */
|