/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016  B. Stultiens
 *
 * 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 "hsv2rgb.h"


void hsv2rgb_8b(int16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b)
{
    uint8_t sextant;
    uint8_t bb;
    uint16_t ww;
    uint8_t h_fraction;

    if (!(s)) {
        *(r) = *(g) = *(b) = (v);
        return;
    }

    sextant = h >> 8;
    HSV_SEXTANT_TEST(sextant);          // Optional: Limit hue sextants to defined space

    HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in

    *g = v;     // Top level

    // Perform actual calculations
    /*
     * Bottom level: v * (1.0 - s)
     * --> (v * (255 - s) + error_corr) / 256
     */
    bb = ~s;
    ww = v * bb;
    ww += 1;        // Error correction
    ww += ww >> 8;      // Error correction
    *b = ww >> 8;

    h_fraction = h & 0xff;  // 0...255

    if(!(sextant & 1)) {
        // *r = ...slope_up...;
        /*
         * Slope up: v * (1.0 - s * (1.0 - h))
         * --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256
         */
        ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction));
        ww += ww >> 8;      // Error correction 1
        bb = ww >> 8;
        bb = ~bb;
        ww = v * bb;
        ww += v >> 1;       // Error correction 2
        *r = ww >> 8;
    } else {
        // *r = ...slope_down...;
        /*
         * Slope down: v * (1.0 - s * h)
         * --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256
         */
        ww = s * h_fraction;
        ww += ww >> 8;      // Error correction 1
        bb = ww >> 8;
        bb = ~bb;
        ww = v * bb;
        ww += v >> 1;       // Error correction 2
        *r = ww >> 8;

        /*
         * A perfect match for h_fraction == 0 implies:
         *  *r = (ww >> 8) + (h_fraction ? 0 : 1)
         * However, this is an extra calculation that may not be required.
         */
    }
}
