/* * RGB <--> HSV conversion in integer arithmetics, to be used on Windows. * Copyright (c) 2013 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "hsv.h" #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN3(a,b,c) MIN((a), MIN((b), (c))) #define MAX3(a,b,c) MAX((a), MAX((b), (c))) #define RGBGetRValue(rgb) (rgb & 0x000000FF) #define RGBGetGValue(rgb) ((rgb >> 8) & 0x000000FF) #define RGBGetBValue(rgb) ((rgb >> 16) & 0x000000FF) #define ToRGBColor(r, g, b) ((b << 16) | (g << 8) | (r)) void rgb2hsv(unsigned int rgb, hsv_t* hsv) { int r = RGBGetRValue(rgb); int g = RGBGetGValue(rgb); int b = RGBGetBValue(rgb); int m = MIN3(r, g, b); int M = MAX3(r, g, b); int delta = M - m; if (delta == 0) { /* Achromatic case (i.e. grayscale) */ hsv->hue = -1; /* undefined */ hsv->saturation = 0; } else { int h; if (r == M) h = ((g - b) * 60 * HUE_DEGREE) / delta; else if (g == M) h = ((b - r) * 60 * HUE_DEGREE) / delta + 120 * HUE_DEGREE; else /*if(b == M)*/ h = ((r - g) * 60 * HUE_DEGREE) / delta + 240 * HUE_DEGREE; if (h < 0) h += 360 * HUE_DEGREE; hsv->hue = h; /* The constatnt 8 is tuned to statistically cause as little * tolerated mismatches as possible in RGB -> HSV -> RGB conversion. * (See the unit test at the bottom of this file.) */ hsv->saturation = (256 * delta - 8) / M; } hsv->value = M; } unsigned int hsv2rgb(hsv_t* hsv) { unsigned char r = 0; unsigned char g = 0; unsigned char b = 0; if (hsv->saturation == 0) { r = g = b = hsv->value; } else { int h = (hsv->hue)%360; int s = hsv->saturation; int v = hsv->value; int i = h / (60 * HUE_DEGREE); int p = (256 * v - s*v) / 256; if (i & 1) { int q = (256 * 60 * HUE_DEGREE*v - h*s*v + 60 * HUE_DEGREE*s*v*i) / (256 * 60 * HUE_DEGREE); switch (i) { case 1: r = q; g = v; b = p; break; case 3: r = p; g = q; b = v; break; case 5: r = v; g = p; b = q; break; } } else { int t = (256 * 60 * HUE_DEGREE*v + h*s*v - 60 * HUE_DEGREE*s*v*(i + 1)) / (256 * 60 * HUE_DEGREE); switch (i) { case 0: r = v; g = t; b = p; break; case 2: r = p; g = v; b = t; break; case 4: r = t; g = p; b = v; break; } } } return ToRGBColor(r, g, b); } #ifdef TEST #include #define DIFF(a,b) ((a) >= (b) ? (a) - (b) : (b) - (a)) int main(int argc, char** argv) { int r0, g0, b0; hsv_t hsv; int r1, g1, b1; int ok = 0, fuzzy = 0, ko = 0, total; for (r0 = 0; r0 < 256; r0++) { for (g0 = 0; g0 < 256; g0++) { for (b0 = 0; b0 < 256; b0++) { COLORREF tmp; rgb2hsv(RGB(r0, g0, b0), &hsv); tmp = hsv2rgb(&hsv); r1 = GetRValue(tmp); g1 = GetGValue(tmp); b1 = GetBValue(tmp); if (r0 == r1 && b0 == b1 && g0 == g1) ok++; else if (DIFF(r0, r1) <= 1 && DIFF(b0, b1) <= 1 && DIFF(g0, g1) <= 1) fuzzy++; else ko++; } } } total = ok + fuzzy + ko; printf("Test results:\n"); printf(" * exact matches: %8d (%5.2f%%)\n", ok, (100.0 * ok) / total); printf(" * fuzzy matches: %8d (%5.2f%%)\n", fuzzy, (100.0 * fuzzy) / total); printf(" * mismatches: %8d (%5.2f%%)\n", ko, (100.0 * ko) / total); return (ko == 0 ? 0 : 1); } #endif /* TEST */