| #pragma once |
| |
| #ifndef T_PIXELUTILS_INCLUDED |
| #define T_PIXELUTILS_INCLUDED |
| |
| #include "tpixel.h" |
| #include "tpixelgr.h" |
| |
| #undef DVAPI |
| #undef DVVAR |
| #ifdef TCOLOR_EXPORTS |
| #define DVAPI DV_EXPORT_API |
| #define DVVAR DV_EXPORT_VAR |
| #else |
| #define DVAPI DV_IMPORT_API |
| #define DVVAR DV_IMPORT_VAR |
| #endif |
| |
| |
| |
| |
| |
| |
| |
| |
| template <class T> |
| inline T blend(const T &a, const T &b, double t) { |
| return T(troundp((1 - t) * a.r + t * b.r), troundp((1 - t) * a.g + t * b.g), |
| troundp((1 - t) * a.b + t * b.b), troundp((1 - t) * a.m + t * b.m)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <class T> |
| inline T blend(const T &a, const T &b, int num, int den) { |
| return T((int)(((den - num) * a.r + num * b.r) / den), |
| (int)(((den - num) * a.g + num * b.g) / den), |
| (int)(((den - num) * a.b + num * b.b) / den), |
| (int)(((den - num) * a.m + num * b.m) / den)); |
| } |
| |
| template <class T> |
| inline T antialias(const T &a, int num) { |
| return T((int)((num * a.r) / 255), (int)((num * a.g) / 255), |
| (int)((num * a.b) / 255), (int)((num * a.m) / 255)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <class T, class Q> |
| DVAPI inline T overPixT(const T &bot, const T &top) { |
| UINT max = T::maxChannelValue; |
| |
| if (top.m == max) return top; |
| |
| if (top.m == 0) return bot; |
| |
| TUINT32 r = top.r + bot.r * (max - top.m) / max; |
| TUINT32 g = top.g + bot.g * (max - top.m) / max; |
| TUINT32 b = top.b + bot.b * (max - top.m) / max; |
| return T((r < max) ? (Q)r : (Q)max, (g < max) ? (Q)g : (Q)max, |
| (b < max) ? (Q)b : (Q)max, |
| (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); |
| } |
| |
| |
| template <class T, class S, class Q> |
| DVAPI inline T overPixGRT(const T &bot, const S &top) { |
| UINT max = T::maxChannelValue; |
| |
| if (top.value == max) return T(top.value, top.value, top.value, top.value); |
| |
| if (top.value == 0) return bot; |
| |
| double aux = (max - top.value) / max; |
| TUINT32 r = (TUINT32)(top.value + bot.r * aux); |
| TUINT32 g = (TUINT32)(top.value + bot.g * aux); |
| TUINT32 b = (TUINT32)(top.value + bot.b * aux); |
| return T((r < max) ? (Q)r : (Q)max, (g < max) ? (Q)g : (Q)max, |
| (b < max) ? (Q)b : (Q)max, |
| (bot.m == max) ? max : (TUINT32)(max - (max - bot.m) * aux)); |
| } |
| |
| |
| |
| |
| template <class T, class Q> |
| |
| DVAPI inline T quickOverPixT(const T &bot, const T &top) { |
| UINT max = T::maxChannelValue; |
| |
| TUINT32 r = top.r + bot.r * (max - top.m) / max; |
| TUINT32 g = top.g + bot.g * (max - top.m) / max; |
| TUINT32 b = top.b + bot.b * (max - top.m) / max; |
| return T((r < max) ? (Q)r : (Q)max, (g < max) ? (Q)g : (Q)max, |
| (b < max) ? (Q)b : (Q)max, |
| (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); |
| } |
| |
| |
| |
| template <class T, class Q> |
| |
| DVAPI inline T quickOverPixPremultT(const T &bot, const T &top) { |
| UINT max = T::maxChannelValue; |
| |
| TUINT32 r = (top.r * top.m + bot.r * (max - top.m)) / max; |
| TUINT32 g = (top.g * top.m + bot.g * (max - top.m)) / max; |
| TUINT32 b = (top.b * top.m + bot.b * (max - top.m)) / max; |
| return T((r < max) ? (Q)r : (Q)max, (g < max) ? (Q)g : (Q)max, |
| (b < max) ? (Q)b : (Q)max, |
| (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max); |
| } |
| |
| |
| |
| template <class T, class Q> |
| DVAPI inline T quickOverPixDarkenBlendedT(const T &bot, const T &top) { |
| struct locals { |
| static inline double comp(const double ch_a, const double ch_b, |
| const double alpha) { |
| return clamp(ch_b + ch_a * (1.0 - alpha)); |
| } |
| static inline double darken_ch(const double dn, const double dn_a, |
| const double up, const double up_a) { |
| return (up / up_a < dn / dn_a) ? comp(dn, up, up_a) : comp(up, dn, dn_a); |
| } |
| static inline double clamp(double val) { |
| return (val < 0.0) ? 0.0 : (val > 1.0) ? 1.0 : val; |
| } |
| }; |
| |
| if (bot.m == 0) return top; |
| |
| if (top.m == T::maxChannelValue && bot.m == T::maxChannelValue) { |
| TUINT32 r = (top.r < bot.r) ? top.r : bot.r; |
| TUINT32 g = (top.g < bot.g) ? top.g : bot.g; |
| TUINT32 b = (top.b < bot.b) ? top.b : bot.b; |
| return T((Q)r, (Q)g, (Q)b, T::maxChannelValue); |
| } |
| |
| double maxi = static_cast<double>(T::maxChannelValue); |
| |
| double upr = static_cast<double>(top.r) / maxi; |
| double upg = static_cast<double>(top.g) / maxi; |
| double upb = static_cast<double>(top.b) / maxi; |
| double upa = static_cast<double>(top.m) / maxi; |
| double dnr = static_cast<double>(bot.r) / maxi; |
| double dng = static_cast<double>(bot.g) / maxi; |
| double dnb = static_cast<double>(bot.b) / maxi; |
| double dna = static_cast<double>(bot.m) / maxi; |
| dnr = locals::darken_ch(dnr, dna, upr, upa); |
| dng = locals::darken_ch(dng, dna, upg, upa); |
| dnb = locals::darken_ch(dnb, dna, upb, upa); |
| dna = locals::comp(dna, upa, upa); |
| T out; |
| out.r = static_cast<Q>(dnr * (maxi + 0.999999)); |
| out.g = static_cast<Q>(dng * (maxi + 0.999999)); |
| out.b = static_cast<Q>(dnb * (maxi + 0.999999)); |
| out.m = static_cast<Q>(dna * (maxi + 0.999999)); |
| return out; |
| } |
| |
| |
| template <class T, class S, class Q> |
| DVAPI inline T quickOverPixGRT(const T &bot, const S &top) { |
| UINT max = T::maxChannelValue; |
| |
| double aux = (max - top.value) / max; |
| TUINT32 r = (TUINT32)(top.value + bot.r * aux); |
| TUINT32 g = (TUINT32)(top.value + bot.g * aux); |
| TUINT32 b = (TUINT32)(top.value + bot.b * aux); |
| return T((r < max) ? (Q)r : (Q)max, (g < max) ? (Q)g : (Q)max, |
| (b < max) ? (Q)b : (Q)max, |
| (bot.m == max) ? max : (TUINT32)(max - (max - bot.m) * aux)); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 overPix(const TPixel32 &bot, const TPixelGR8 &top) { |
| return overPixGRT<TPixel32, TPixelGR8, UCHAR>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel64 overPix(const TPixel64 &bot, const TPixelGR16 &top) { |
| return overPixGRT<TPixel64, TPixelGR16, USHORT>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 overPix(const TPixel32 &bot, const TPixel32 &top) { |
| return overPixT<TPixel32, UCHAR>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel64 overPix(const TPixel64 &bot, const TPixel64 &top) { |
| return overPixT<TPixel64, USHORT>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 quickOverPix(const TPixel32 &bot, const TPixelGR8 &top) { |
| return quickOverPixGRT<TPixel32, TPixelGR8, UCHAR>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel64 quickOverPix(const TPixel64 &bot, const TPixelGR16 &top) { |
| return quickOverPixGRT<TPixel64, TPixelGR16, USHORT>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 quickOverPix(const TPixel32 &bot, const TPixel32 &top) { |
| return quickOverPixT<TPixel32, UCHAR>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 quickOverPixPremult(const TPixel32 &bot, |
| const TPixel32 &top) { |
| return quickOverPixPremultT<TPixel32, UCHAR>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel64 quickOverPix(const TPixel64 &bot, const TPixel64 &top) { |
| return quickOverPixT<TPixel64, USHORT>(bot, top); |
| } |
| |
| |
| |
| DVAPI inline TPixel32 quickOverPixDarkenBlended(const TPixel32 &bot, |
| const TPixel32 &top) { |
| return quickOverPixDarkenBlendedT<TPixel32, UCHAR>(bot, top); |
| } |
| |
| |
| |
| template <class T, class Q> |
| DVAPI inline void overPix(T &outPix, const T &bot, const T &top) { |
| UINT max = T::maxChannelValue; |
| |
| if (top.m == max) |
| outPix = top; |
| else if (top.m == 0) |
| outPix = bot; |
| else { |
| TUINT32 r = top.r + bot.r * (max - top.m) / max; |
| TUINT32 g = top.g + bot.g * (max - top.m) / max; |
| TUINT32 b = top.b + bot.b * (max - top.m) / max; |
| outPix.r = (r < max) ? (Q)r : (Q)max, outPix.g = (g < max) ? (Q)g : (Q)max, |
| outPix.b = (b < max) ? (Q)b : (Q)max, |
| outPix.m = (bot.m == max) ? max : max - (max - bot.m) * (max - top.m) / max; |
| } |
| } |
| |
| |
| |
| DVAPI inline TPixel32 overPixOnWhite(const TPixel32 &top) { |
| UINT max = TPixel32::maxChannelValue; |
| |
| if (top.m == max) |
| return top; |
| else if (top.m == 0) |
| return TPixel32::White; |
| else |
| return TPixel32(top.r + max - top.m, top.g + max - top.m, |
| top.b + max - top.m, max); |
| } |
| |
| DVAPI inline TPixel32 overPixOnBlack(const TPixel32 &top) { |
| UINT max = TPixel32::maxChannelValue; |
| |
| if (top.m == max) return top; |
| |
| if (top.m == 0) return TPixel32::Black; |
| |
| return TPixel32(top.r, top.g, top.b, max); |
| } |
| |
| |
| |
| |
| |
| |
| DVAPI inline TPixelGR8 over(const TPixelGR8 &bot, const TPixelGR8 &top) { |
| return TPixelGR8(std::min(bot.value, top.value)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| DVAPI inline void premult(TPixel32 &pix) { |
| const int MAGICFAC = (257U * 256U + 1U); |
| UINT fac = MAGICFAC * pix.m; |
| |
| pix.r = (UINT)(pix.r * fac + (1U << 23)) >> 24; |
| pix.g = (UINT)(pix.g * fac + (1U << 23)) >> 24; |
| pix.b = (UINT)(pix.b * fac + (1U << 23)) >> 24; |
| } |
| |
| DVAPI inline void premult(TPixel64 &pix) { |
| pix.r = pix.r * pix.m / 65535.0; |
| pix.g = pix.g * pix.m / 65535.0; |
| pix.b = pix.b * pix.m / 65535.0; |
| } |
| |
| DVAPI inline void depremult(TPixel32 &pix) { |
| float fac = 255.0f / pix.m; |
| pix.r = std::min(pix.r * fac, 255.0f); |
| pix.g = std::min(pix.g * fac, 255.0f); |
| pix.b = std::min(pix.b * fac, 255.0f); |
| } |
| |
| DVAPI inline void depremult(TPixel64 &pix) { |
| double fac = 65535.0 / pix.m; |
| pix.r = std::min(pix.r * fac, 65535.0); |
| pix.g = std::min(pix.g * fac, 65535.0); |
| pix.b = std::min(pix.b * fac, 65535.0); |
| } |
| |
| |
| |
| template <typename Chan> |
| const double *premultiplyTable(); |
| |
| template <typename Chan> |
| const double *depremultiplyTable(); |
| |
| |
| |
| DVAPI inline TPixel32 premultiply(const TPixel32 &pix) { |
| const int MAGICFAC = (257U * 256U + 1U); |
| UINT fac = MAGICFAC * pix.m; |
| |
| return TPixel32(((UINT)(pix.r * fac + (1U << 23)) >> 24), |
| ((UINT)(pix.g * fac + (1U << 23)) >> 24), |
| ((UINT)(pix.b * fac + (1U << 23)) >> 24), pix.m); |
| } |
| |
| DVAPI inline TPixel64 premultiply(const TPixel64 &pix) { |
| return TPixel64(pix.r * pix.m / 65535.0, pix.g * pix.m / 65535.0, |
| pix.b * pix.m / 65535.0, pix.m); |
| } |
| |
| DVAPI inline TPixel32 depremultiply(const TPixel32 &pix) { |
| return TPixel32(pix.r * 255.0 / pix.m, pix.g * 255.0 / pix.m, |
| pix.b * 255.0 / pix.m, pix.m); |
| } |
| |
| DVAPI inline TPixel64 depremultiply(const TPixel64 &pix) { |
| return TPixel64(pix.r * 65535.0 / pix.m, pix.g * 65535.0 / pix.m, |
| pix.b * 65535.0 / pix.m, pix.m); |
| } |
| |
| |
| |
| |
| DVAPI void hsv2rgb(TPixel32 &dstRgb, int srcHsv[3], int maxHsv = 255); |
| |
| |
| |
| |
| |
| |
| |
| DVAPI void HSV2RGB(double hue, double sat, double value, double *red, |
| double *green, double *blue); |
| |
| |
| |
| DVAPI void rgb2hsv(int dstHsv[3], const TPixel32 &srcRgb, int maxHsv = 255); |
| |
| DVAPI void RGB2HSV(double r, double g, double b, double *h, double *s, |
| double *v); |
| |
| |
| |
| |
| |
| |
| |
| |
| DVAPI void HLS2RGB(double h, double l, double s, double *r, double *g, |
| double *b); |
| |
| |
| |
| |
| |
| |
| |
| |
| DVAPI void rgb2hls(double r, double g, double b, double *h, double *l, |
| double *s); |
| |
| DVAPI TPixel32 toPixel32(const TPixel64 &); |
| DVAPI TPixel32 toPixel32(const TPixelD &); |
| DVAPI TPixel32 toPixel32(const TPixelGR8 &); |
| |
| DVAPI TPixel64 toPixel64(const TPixel32 &); |
| DVAPI TPixel64 toPixel64(const TPixelD &); |
| DVAPI TPixel64 toPixel64(const TPixelGR8 &); |
| |
| DVAPI TPixelD toPixelD(const TPixel32 &); |
| DVAPI TPixelD toPixelD(const TPixel64 &); |
| DVAPI TPixelD toPixelD(const TPixelGR8 &); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <class T> |
| class PixelConverter { |
| public: |
| inline static T from(const TPixel32 &pix); |
| inline static T from(const TPixel64 &pix); |
| inline static T from(const TPixelD &pix); |
| inline static T from(const TPixelGR8 &pix); |
| }; |
| |
| template <> |
| class PixelConverter<TPixel32> { |
| public: |
| inline static TPixel32 from(const TPixel32 &pix) { return pix; } |
| inline static TPixel32 from(const TPixel64 &pix) { return toPixel32(pix); } |
| inline static TPixel32 from(const TPixelD &pix) { return toPixel32(pix); } |
| inline static TPixel32 from(const TPixelGR8 &pix) { return toPixel32(pix); } |
| }; |
| |
| template <> |
| class PixelConverter<TPixel64> { |
| public: |
| inline static TPixel64 from(const TPixel32 &pix) { return toPixel64(pix); } |
| inline static TPixel64 from(const TPixel64 &pix) { return pix; } |
| inline static TPixel64 from(const TPixelD &pix) { return toPixel64(pix); } |
| inline static TPixel64 from(const TPixelGR8 &pix) { return toPixel64(pix); } |
| }; |
| |
| template <> |
| class PixelConverter<TPixelD> { |
| public: |
| inline static TPixelD from(const TPixel32 &pix) { return toPixelD(pix); } |
| inline static TPixelD from(const TPixel64 &pix) { return toPixelD(pix); } |
| inline static TPixelD from(const TPixelD &pix) { return pix; } |
| inline static TPixelD from(const TPixelGR8 &pix) { return toPixelD(pix); } |
| }; |
| |
| |
| |
| template <class T> |
| void add(T &pixout, const T &pixin, double v) { |
| TINT32 r, g, b, m; |
| r = pixout.r + tround(pixin.r * v); |
| g = pixout.g + tround(pixin.g * v); |
| b = pixout.b + tround(pixin.b * v); |
| m = pixout.m + tround(pixin.m * v); |
| pixout.r = tcrop<TINT32>(r, 0, T::maxChannelValue); |
| pixout.g = tcrop<TINT32>(g, 0, T::maxChannelValue); |
| pixout.b = tcrop<TINT32>(b, 0, T::maxChannelValue); |
| pixout.m = tcrop<TINT32>(m, 0, T::maxChannelValue); |
| } |
| |
| |
| |
| template <class T> |
| void sub(T &pixout, const T &pixin, double v) { |
| TINT32 r, g, b, m; |
| r = pixout.r - (pixin.r * v); |
| g = pixout.g - (pixin.g * v); |
| b = pixout.b - (pixin.b * v); |
| m = pixout.m - (pixin.m * v); |
| pixout.r = tcrop<TINT32>(r, 0, T::maxChannelValue); |
| pixout.g = tcrop<TINT32>(g, 0, T::maxChannelValue); |
| pixout.b = tcrop<TINT32>(b, 0, T::maxChannelValue); |
| pixout.m = tcrop<TINT32>(m, 0, T::maxChannelValue); |
| } |
| |
| |
| |
| |
| |
| |
| template <class T> |
| void mult(T &pixout, const T &pixin, double v) { |
| double r, g, b, m; |
| r = pixin.r + v; |
| g = pixin.g + v; |
| b = pixin.b + v; |
| m = pixin.m + v; |
| pixout.r = |
| (r < 0) ? 0 : ((r < T::maxChannelValue) |
| ? troundp(r * (pixout.r / (double)T::maxChannelValue)) |
| : pixout.r); |
| pixout.g = |
| (g < 0) ? 0 : ((g < T::maxChannelValue) |
| ? troundp(g * (pixout.g / (double)T::maxChannelValue)) |
| : pixout.g); |
| pixout.b = |
| (b < 0) ? 0 : ((b < T::maxChannelValue) |
| ? troundp(b * (pixout.b / (double)T::maxChannelValue)) |
| : pixout.b); |
| pixout.m = |
| (m < 0) ? 0 : ((m < T::maxChannelValue) |
| ? troundp(m * (pixout.m / (double)T::maxChannelValue)) |
| : pixout.m); |
| } |
| |
| |
| |
| |
| |
| template <class T> |
| void lighten(T &pixout, const T &pixin, double v) { |
| pixout.r = pixin.r > pixout.r ? pixin.r : pixout.r; |
| pixout.g = pixin.g > pixout.g ? pixin.g : pixout.g; |
| pixout.b = pixin.b > pixout.b ? pixin.b : pixout.b; |
| pixout.m = pixin.m > pixout.m ? pixin.m : pixout.m; |
| } |
| |
| |
| |
| |
| |
| template <class T> |
| void darken(T &pixout, const T &pixin, double v) { |
| pixout.r = pixin.r < pixout.r ? pixin.r : pixout.r; |
| pixout.g = pixin.g < pixout.g ? pixin.g : pixout.g; |
| pixout.b = pixin.b < pixout.b ? pixin.b : pixout.b; |
| pixout.m = pixin.m < pixout.m ? pixin.m : pixout.m; |
| } |
| |
| #endif |