/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)mlib_ImageColorRGB2HSL_Fp.c	9.2	07/10/09 SMI"

/*
 * FUNCTIONS
 *      mlib_ImageColorRGB2HSL_Fp - converts image from RGB to HSV
 *
 * SYNOPSIS
 *      mlib_status mlib_ImageColorRGB2HSL_Fp(mlib_image       *dst,
 *                                            const mlib_image *src)
 *
 * ARGUMENTS
 *      dst  pointer to an dst image
 *      src  pointer to an src image
 *
 *      R, G,B, S,L are in range [0., 1.]
 *      H          is in range [0., 1.)
 *
 * MLIB_FLOAT case notes:
 *      if R==G==B then H = S=0.0f;
 *
 * MLIB_DOUBLE case notes:
 *      if R==G==B then H = S=0.0;
 *
 * RESTRICTION
 *      src and dst must be the same type and the same number
 *      of channels.
 *      They must be 3-channel images.
 *      They can be in MLIB_FLOAT or MLIB_DOUBLE data type.
 *
 * DESCRIPTION
 *      The center of the source image is mapped to the center of the
 *      destination image.
 *
 *      P = min(R, G,B)
 *      V = max(R, G,B)
 *
 *      L = (V + P) / 2
 *
 *      S = | (V - P) / (V + P)      if L <= 0.5
 *          | (V - P) / (2 - V - P)  if L <= 0.5
 *
 *          | (5 + (V-B)/(V-P))/6  if R==V and G==P
 *          | (1 + (V-G)/(V-P))/6  if R==V and B==P
 *      H = | (1 + (V-R)/(V-P))/6  if G==V and B==P
 *          | (3 + (V-B)/(V-P))/6  if G==V and R==P
 *          | (3 + (V-G)/(V-P))/6  if B==V and R==P
 *          | (5 + (V-R)/(V-P))/6  if B==V and G==P
 *
 */

#include <mlib_image.h>
#include <mlib_ImageCheck.h>

/* *********************************************************** */

#if ! defined(__MEDIALIB_OLD_NAMES)
#if defined(__SUNPRO_C)

#pragma weak mlib_ImageColorRGB2HSL_Fp = __mlib_ImageColorRGB2HSL_Fp

#elif defined(__GNUC__)	/* defined(__SUNPRO_C) */
__typeof__(__mlib_ImageColorRGB2HSL_Fp) mlib_ImageColorRGB2HSL_Fp
    __attribute__((weak, alias("__mlib_ImageColorRGB2HSL_Fp")));

#else /* defined(__SUNPRO_C) */

#error  "unknown platform"

#endif /* defined(__SUNPRO_C) */
#endif /* ! defined(__MEDIALIB_OLD_NAMES) */

/* *********************************************************** */

static void mlib_ImageColorRGB2HSL_Fp_F32(
    mlib_image *dst,
    const mlib_image *src);

static void mlib_ImageColorRGB2HSL_Fp_D64(
    mlib_image *dst,
    const mlib_image *src);

/* *********************************************************** */

#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#pragma optimize("", off)
#endif /* defined(_MSC_VER) || defined(__INTEL_COMPILER) */

mlib_status
__mlib_ImageColorRGB2HSL_Fp(
    mlib_image *dst,
    const mlib_image *src)
{
/*  check for obvious errors  */
	MLIB_IMAGE_CHECK(src);
	MLIB_IMAGE_CHECK(dst);
	MLIB_IMAGE_TYPE_EQUAL(dst, src);
	MLIB_IMAGE_CHAN_EQUAL(dst, src);
	MLIB_IMAGE_HAVE_CHAN(dst, 3);

	switch (mlib_ImageGetType(dst)) {
	case MLIB_FLOAT:
		mlib_ImageColorRGB2HSL_Fp_F32(dst, src);
		break;
	case MLIB_DOUBLE:
		mlib_ImageColorRGB2HSL_Fp_D64(dst, src);
		break;
/*  discard any other data types  */
	default:
		return (MLIB_FAILURE);
	}

	return (MLIB_SUCCESS);
}

/* *********************************************************** */

#define	dw	sw
#define	dh	sh

#define	PREPAREVARS(data_type)                                     \
	mlib_s32 sstride =                                         \
	mlib_ImageGetStride(src) / sizeof (data_type);             \
	    mlib_s32 dstride =                                     \
		mlib_ImageGetStride(dst) / sizeof (data_type);     \
	    mlib_s32 ws_ = mlib_ImageGetWidth(src);                \
	    mlib_s32 hs_ = mlib_ImageGetHeight(src);               \
	    mlib_s32 wd_ = mlib_ImageGetWidth(dst);                \
	    mlib_s32 hd_ = mlib_ImageGetHeight(dst);               \
	    mlib_s32 sw = (ws_ < wd_) ? ws_ : wd_;                 \
	    mlib_s32 sh = (hs_ < hd_) ? hs_ : hd_;                 \
	    mlib_s32 dx = ((mlib_s32)ws_ - (mlib_s32)wd_) >> 1;    \
	    mlib_s32 dy = ((mlib_s32)hs_ - (mlib_s32)hd_) >> 1;    \
	    mlib_s32 dxs = ((dx > 0) ? dx : 0);                    \
	    mlib_s32 dxd = ((dx > 0) ? 0 : -dx);                   \
	    mlib_s32 dys = ((dy > 0) ? dy : 0);                    \
	    mlib_s32 dyd = ((dy > 0) ? 0 : -dy);                   \
	    data_type *sdata =                                     \
		(data_type *) mlib_ImageGetData(src) + dxs * 3 +   \
	    sstride * dys;                                         \
	    data_type *ddata =                                     \
		(data_type *) mlib_ImageGetData(dst) + dxd * 3 +   \
	    dstride * dyd;                                         \
	    data_type *ps = sdata;                                 \
	    data_type *pd = ddata;                                 \
	    mlib_s32 i, j

/* *********************************************************** */

void
mlib_ImageColorRGB2HSL_Fp_F32(
    mlib_image *dst,
    const mlib_image *src)
{
	PREPAREVARS(mlib_f32);

	for (j = 0; j < dh; ++j) {
#ifdef __SUNPRO_C
#pragma pipeloop(0)
#endif /* __SUNPRO_C */
		for (i = 0; i < dw; i++) {
			mlib_f32 r = ps[i * 3 + 0];
			mlib_f32 g = ps[i * 3 + 1];
			mlib_f32 b = ps[i * 3 + 2];
			mlib_f32 v, minim, h, s;
			mlib_f32 delta, vdiv, sum;
			mlib_f32 rdelta;
			mlib_f32 shft = 2.0f / 3.0f;
			mlib_f32 dmin = r - g;

			v = minim = r;

			if (r > g)
				minim = g;

			if (r <= g)
				v = g;

			if (minim > b)
				minim = b;

			if (b > v)
				v = b;

			if (v == minim) {
				pd[i * 3 + 2] = v;
				pd[i * 3 + 1] = 0.0f;
				pd[i * 3 + 0] = 0.0f;
				continue;
			}

			if (r == v) {
				shft = 1.0f;
				dmin = g - b;
			} else if (g == v) {
				shft = 1.0f / 3.0f;
				dmin = b - r;
			}

			sum = v + minim;

			vdiv = sum;

			if (sum > 1.0f)
				vdiv = 2.0f - v - minim;
			pd[i * 3 + 2] = sum * 0.5f;

			delta = v - minim;
			rdelta = 1.0f / delta;
			rdelta *= (1.0f / 6.0f);

			s = delta / vdiv;
			h = shft + dmin * rdelta;
			h = h - (mlib_s32)h;

			pd[i * 3 + 1] = s;
			pd[i * 3 + 0] = h;
		}

		ps += sstride;
		pd += dstride;
	}
}

/* *********************************************************** */

void
mlib_ImageColorRGB2HSL_Fp_D64(
    mlib_image *dst,
    const mlib_image *src)
{
	PREPAREVARS(mlib_d64);

	for (j = 0; j < dh; ++j) {
#ifdef __SUNPRO_C
#pragma pipeloop(0)
#endif /* __SUNPRO_C */
		for (i = 0; i < dw; i++) {
			mlib_d64 r = ps[i * 3 + 0];
			mlib_d64 g = ps[i * 3 + 1];
			mlib_d64 b = ps[i * 3 + 2];
			mlib_d64 v, minim, h, s;
			mlib_d64 delta, vdiv, sum;
			mlib_d64 rdelta;
			mlib_d64 shft = 2.0 / 3.0;
			mlib_d64 dmin = r - g;

			v = minim = r;

			if (r > g)
				minim = g;

			if (r <= g)
				v = g;

			if (minim > b)
				minim = b;

			if (b > v)
				v = b;

			if (v == minim) {
				pd[i * 3 + 2] = v;
				pd[i * 3 + 1] = 0.0;
				pd[i * 3 + 0] = 0.0;
				continue;
			}

			if (r == v) {
				shft = 1.0f;
				dmin = g - b;
			} else if (g == v) {
				shft = 1.0 / 3.0;
				dmin = b - r;
			}

			sum = v + minim;

			vdiv = sum;

			if (sum > 1.0)
				vdiv = 2.0 - v - minim;
			pd[i * 3 + 2] = sum * 0.5;

			delta = (v - minim);
			rdelta = 1.0 / delta;
			rdelta *= (1.0 / 6.0);

			vdiv = 1.0 / vdiv;
			s = delta * vdiv;
			h = shft + dmin * rdelta;
			h = h - (mlib_s32)h;

			pd[i * 3 + 1] = s;
			pd[i * 3 + 0] = h;
		}

		ps += sstride;
		pd += dstride;
	}
}

/* *********************************************************** */

#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#pragma optimize("", on)
#endif /* defined(_MSC_VER) || defined(__INTEL_COMPILER) */
