Blob Blame Raw


// STPic.h: interface for the CSTPic class.
//
//////////////////////////////////////////////////////////////////////

#include <assert.h>

#ifndef STPIC_H
#define STPIC_H

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/****** SASA Picture Class Template **********************

Currently there are two types of this object
ST_RGBM		- CSTPic<UC_PIXEL>	- UCHAR r,g,b,m channels
ST_RGBM64	- CSTPic<US_PIXEL>	- USHORT r,g,b,m channels

The CMAP RASTER pictures have to be converted to ST_RGBM or 
ST_RGBM64, but the CMAP information can be used with the help 
of m_raster.  

m_lX,m_lY - the length of the picture in X,Y direction
m_pic - the buffer of the picture
m_ras - stores the pointer to the original RASTER picture 
   
************************************************************/

#include "toonz4.6/udit.h"
#include "toonz4.6/raster.h"
#include "toonz4.6/pixel.h"
#include "SDef.h"
#include "SError.h"

#include "timagecache.h"
#include "trasterimage.h"

#define ISINPIC(x, y) (m_pic && x >= 0 && x < m_lX && y >= 0 && y < m_lY)

typedef enum {
	ST_NIL,   // EMTY
	ST_RGBM,  // UC_PIXEL
	ST_RGBM64 // US_PIXEL
} ST_TYPE;

//! Old 4.6 picture class template (sandor fxs). There is a significant modification
//! to be considered starting from Toonz 6.1 - the allocated raster is now managed
//! by the image cache. Basically, when constructing one such object, the image is locked
//! in the Toonz cache - and you must remember to unlock and relock it along inactivity periods.
template <class P>
class CSTPic
{
	string m_cacheId;
	TRasterImageP m_picP;

public:
	int m_lX, m_lY;
	P *m_pic;
	const RASTER *m_ras;

	CSTPic(void) : m_cacheId(TImageCache::instance()->getUniqueId()),
				   m_lX(0), m_lY(0), m_pic(0), m_ras(0) {}

	void nullPic()
	{
		//if ( m_pic ) { delete [] m_pic; m_pic=0; }
		unlock();
		TImageCache::instance()->remove(m_cacheId);
	}

	//!Retrieves the raster image from the cache to work with it.
	void lock()
	{
		m_picP = TImageCache::instance()->get(m_cacheId, true);
		m_pic = (P *)m_picP->getRaster()->getRawData();
	}

	//!Release the raster image for inactivity periods.
	void unlock()
	{
		m_picP = 0;
		m_pic = 0;
	}

	CSTPic(const int lX, const int lY) : //throw(SMemAllocError) :
										 m_cacheId(TImageCache::instance()->getUniqueId()),
										 m_lX(lX), m_lY(lY),
										 m_pic(0), m_ras(0)
	{
		try {
			initPic();
		} catch (SMemAllocError) {
			null();
			throw;
		}
	}

	CSTPic(const CSTPic<P> &sp) : //throw(SMemAllocError) :
								  m_lX(sp.m_lX),
								  m_lY(sp.m_lY),
								  m_pic(0), m_ras(sp.m_ras)
	{
		try {
			initPic();
			copyPic(sp);
		} catch (SMemAllocError) {
			null();
			throw;
		}
	}

	~CSTPic(void)
	{
		null();
	}

	//!Allocates the specified raster and surrenders it to the image cache.
	//!\b NOTE: The raster being initialized is LOCKED after this call.
	//!You must remember to UNLOCK it when no more used.
	void initPic() //throw(SMemAllocError)
	{
		nullPic();
		if (m_lX > 0 && m_lY > 0) {
			//m_pic=new P[m_lX*m_lY];
			TRasterGR8P ras(m_lX * m_lY * sizeof(P), 1);
			if (!ras)
				throw SMemAllocError("in initPic");
			TImageCache::instance()->add(m_cacheId, TRasterImageP(ras));
			lock();
		} else {
			char s[200];
			sprintf(s, "in initPic lXY=(%d,%d)\n", m_lX, m_lY);
			throw SMemAllocError(s);
		}
	}

	void null()
	{
		nullPic();
		m_lX = m_lY = 0;
		m_ras = 0;
	}

	// Draws the border of the CSTPic
	void drawRect(const P &ip)
	{
		SRECT rect = {0, 0, m_lX - 1, m_lY - 1};
		drawRect(rect, ip);
	}

	//------------------- The following need to be *LOCKED* before the call ------------------

	// Currently there are two types of STPic
	// ST_RGBM   -	UCHAR r,g,b,m channels
	// ST_RGBM64 -	USHORT r,g,b,m channels
	ST_TYPE getType() const
	{
		if (!m_pic)
			return ST_NIL;
		if (sizeof(m_pic->r) == sizeof(UCHAR))
			return ST_RGBM;
		if (sizeof(m_pic->r) == sizeof(USHORT))
			return ST_RGBM64;
		return ST_NIL;
	}

	// Copies the 'sp' CSTPic into 'm_pic'

	void copyPic(const CSTPic<P> &sp)
	{
		P p;
		for (int y = 0; y < m_lY && y < sp.m_lY; y++)
			for (int x = 0; x < m_lX && x < sp.m_lX; x++) {
				sp.getPixel(x, y, p);
				setRGBM(x, y, p);
			}
	}

	// Copies the 'r' rectangle of 'sp' CSTPic into the 'p' position
	// of 'm_pic'
	void copyPic(const CSTPic<P> &sp, const SRECT &r, const SPOINT &p)
	{
		P ip;
		int xs, ys, xd, yd;
		for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
			for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
				sp.getPixel(xs, ys, ip);
				setRGBM(xd, yd, ip);
			}
	}

	// Draws a rectangle into CSTPic
	void drawRect(const SRECT &rect, const P &ip)
	{
		for (int x = rect.x0; x <= rect.x1; x++) {
			setRGBM(x, rect.y0, ip);
			setRGBM(x, rect.y1, ip);
		}
		for (int y = rect.y0; y <= rect.y1; y++) {
			setRGBM(rect.x0, y, ip);
			setRGBM(rect.x1, y, ip);
		}
	}

	void erease()
	{
		P p = {0, 0, 0, 0};
		fill(p);
	}

	void fill(const P &ip)
	{
		for (int y = 0; y < m_lY; y++)
			for (int x = 0; x < m_lX; x++)
				setRGBM(x, y, ip);
	}

	// Checks whether (x,y) is a proper coordinate
	inline bool isInPic(const int x, const int y) const
	{
		return (m_pic && x >= 0 && x < m_lX && y >= 0 && y < m_lY);
	}

	// Sets the color of the CSTPic pixel
	void setRGB(const int x, const int y, const P &ip)
	{
		if (ISINPIC(x, y)) {
			P *p = m_pic + y * m_lX + x;
			p->r = ip.r;
			p->g = ip.g;
			p->b = ip.b;
		}
	}

	void setRGBM(const int x, const int y, const P &ip)
	{
		if (ISINPIC(x, y)) {
			P *p = m_pic + y * m_lX + x;
			p->r = ip.r;
			p->g = ip.g;
			p->b = ip.b;
			p->m = ip.m;
		}
	}

	// Sets the color of the CSTPic pixel
	void setRGB(const int x, const int y, const I_PIXEL &ip)
	{
		if (ISINPIC(x, y)) {
			P *p = m_pic + y * m_lX + x;
			if (getType() == ST_RGBM) {
				p->r = (UCHAR)I_CUT_0_255(ip.r);
				p->g = (UCHAR)I_CUT_0_255(ip.g);
				p->b = (UCHAR)I_CUT_0_255(ip.b);
			} else if (getType() == ST_RGBM64) {
				p->r = (USHORT)I_CUT_0_65535(ip.r);
				p->g = (USHORT)I_CUT_0_65535(ip.g);
				p->b = (USHORT)I_CUT_0_65535(ip.b);
			}
		}
	}

	void setRGBM(const int x, const int y, const I_PIXEL &ip)
	{
		if (ISINPIC(x, y)) {
			P *p = m_pic + y * m_lX + x;
			if (getType() == ST_RGBM) {
				p->r = (UCHAR)I_CUT_0_255(ip.r);
				p->g = (UCHAR)I_CUT_0_255(ip.g);
				p->b = (UCHAR)I_CUT_0_255(ip.b);
				p->m = (UCHAR)I_CUT_0_255(ip.m);
			} else if (getType() == ST_RGBM64) {
				p->r = I_CUT_0_65535(ip.r);
				p->g = I_CUT_0_65535(ip.g);
				p->b = I_CUT_0_65535(ip.b);
				p->m = I_CUT_0_65535(ip.m);
			}
		}
	}

	// Gets the color of the CSTPic pixel
	void getPixel(const int x, const int y, P &ip) const
	{
		if (ISINPIC(x, y)) {
			const P *p = m_pic + y * m_lX + x;
			ASSIGN_PIXEL(&ip, p);
			return;
		}
		ip.r = ip.g = ip.b = ip.m = 0;
	}

	void getPixel(const int x, const int y, I_PIXEL &ip) const
	{
		if (ISINPIC(x, y)) {
			const P *p = m_pic + y * m_lX + x;
			ip.r = (int)p->r;
			ip.g = (int)p->g;
			ip.b = (int)p->b;
			ip.m = (int)p->m;
			return;
		}
		ip.r = ip.g = ip.b = ip.m = 0;
	}

	// Gets the color of a RASTER pixel
	// RASTER operation always gets I_PIXEL color and I_PIXEL is casted
	// to the right type
	void getRasterPixel(const RASTER *ras, const int x, const int y,
						I_PIXEL &ip) const
	{
		if (x >= 0 && x < ras->lx && y >= 0 && y < ras->ly && ras->buffer) {
			LPIXEL *pL;
			LPIXEL pLL;
			SPIXEL *pS;
			UD44_CMAPINDEX32 *ci32;
			//UD44_PIXEL32* pen;
			UD44_PIXEL32 *col;
			switch (ras->type) {
			case (RAS_RGBM):
				pL = (LPIXEL *)(ras->buffer) + y * ras->wrap + x;
				ip.r = (int)pL->r;
				ip.g = (int)pL->g;
				ip.b = (int)pL->b;
				ip.m = (int)pL->m;
				break;
			case (RAS_RGBM64):
				pS = (SPIXEL *)(ras->buffer) + y * ras->wrap + x;
				ip.r = (int)pS->r;
				ip.g = (int)pS->g;
				ip.b = (int)pS->b;
				ip.m = (int)pS->m;
				break;
			case (RAS_CM32):
				ci32 = (UD44_CMAPINDEX32 *)(ras->buffer) + y * ras->wrap + x;
				col = (UD44_PIXEL32 *)(ras->cmap.buffer);
				PIX_CM32_MAP_TO_RGBM(*ci32, col, pLL)
				ip.r = (int)pLL.r;
				ip.g = (int)pLL.g;
				ip.b = (int)pLL.b;
				ip.m = (int)pLL.m;
				break;
			}
		} else
			ip.r = ip.g = ip.b = ip.m = 0;
	}

	void getRasterPixel(const int x, const int y,
						I_PIXEL &ip) const
	{
		if (m_ras)
			getRasterPixel(m_ras, x, y, ip);
		else
			ip.r = ip.g = ip.b = ip.m = 0;
	}

	// Sets the color of a RASTER pixel
	// RASTER operation always gets I_PIXEL color and I_PIXEL is casted
	// to the right type

	void setRasterPixel(RASTER *ras, const int x, const int y,
						const I_PIXEL &ip) const
	{
		if (x >= 0 && x < ras->lx && y >= 0 && y < ras->ly && ras->buffer) {
			LPIXEL *pL;
			SPIXEL *pS;
			switch (ras->type) {
			case (RAS_RGBM):
				pL = (LPIXEL *)(ras->buffer) + y * ras->wrap + x;
				pL->r = (UCHAR)ip.r;
				pL->g = (UCHAR)ip.g;
				pL->b = (UCHAR)ip.b;
				pL->m = (UCHAR)ip.m;
				break;
			case (RAS_RGBM64):
				pS = (SPIXEL *)(ras->buffer) + y * ras->wrap + x;
				pS->r = (USHORT)ip.r;
				pS->g = (USHORT)ip.g;
				pS->b = (USHORT)ip.b;
				pS->m = (USHORT)ip.m;
				break;
			}
		}
	}

	bool copy_raster(const RASTER *ir, RASTER *out_r,
					 const int xBeg, const int yBeg,
					 const int xEnd, const int yEnd,
					 const int ox, const int oy)

	{
		if ((ir->lx <= 0) || (ir->ly <= 0) || (out_r->lx <= 0) || (out_r->ly <= 0))
			return false;
		if (ir->buffer == NULL || out_r->buffer == NULL)
			return false;
		if (out_r->type == RAS_CM32)
			return false;
		if (ir->type == RAS_CM32 &&
			(ir->cmap.buffer == NULL))
			return false;

		for (int y = yBeg, yy = oy; y <= yEnd; y++, yy++)
			for (int x = xBeg, xx = ox; x <= xEnd; x++, xx++) {
				I_PIXEL ip;
				getRasterPixel(ir, x, y, ip);
				if (xx >= 0 && yy >= 0 && xx < out_r->lx && yy < out_r->ly) {
					LPIXEL *pL;
					SPIXEL *pS;
					if (out_r->type == RAS_RGBM &&
						(ir->type == RAS_RGBM || ir->type == RAS_CM32)) {
						pL = (LPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
						pL->r = (UCHAR)ip.r;
						pL->g = (UCHAR)ip.g;
						pL->b = (UCHAR)ip.b;
						pL->m = (UCHAR)ip.m;
					} else if (out_r->type == RAS_RGBM &&
							   ir->type == RAS_RGBM64) {
						pL = (LPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
						pL->r = PIX_BYTE_FROM_USHORT((USHORT)ip.r);
						pL->g = PIX_BYTE_FROM_USHORT((USHORT)ip.g);
						pL->b = PIX_BYTE_FROM_USHORT((USHORT)ip.b);
						pL->m = PIX_BYTE_FROM_USHORT((USHORT)ip.m);
					} else if (out_r->type == RAS_RGBM64 &&
							   (ir->type == RAS_RGBM || ir->type == RAS_CM32)) {
						pS = (SPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
						pS->r = PIX_USHORT_FROM_BYTE((UCHAR)ip.r);
						pS->g = PIX_USHORT_FROM_BYTE((UCHAR)ip.g);
						pS->b = PIX_USHORT_FROM_BYTE((UCHAR)ip.b);
						pS->m = PIX_USHORT_FROM_BYTE((UCHAR)ip.m);
					} else if (out_r->type == RAS_RGBM64 &&
							   ir->type == RAS_RGBM64) {
						pS = (SPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
						pS->r = (USHORT)ip.r;
						pS->g = (USHORT)ip.g;
						pS->b = (USHORT)ip.b;
						pS->m = (USHORT)ip.m;
					}
				}
			}
		return true;
	}

	// Generates and reads the CSTPic using RASTER.
	// \b NOTE: This LOCKS the raster being read. You must remember to unlock it afterwards
	// when it is needed no more.
	virtual void read(const RASTER *ras) //throw(SMemAllocError)
	{
		try {
			null();
			if (((ras->type == RAS_RGBM || ras->type == RAS_RGBM64) &&
				 ras->buffer && ras->lx > 0 && ras->ly > 0) ||
				(ras->type == RAS_CM32 && ras->buffer &&
				 ras->cmap.buffer && ras->lx > 0 && ras->ly > 0)) {
				m_lX = ras->lx;
				m_lY = ras->ly;
				m_ras = ras;
				initPic();
				lock();
				ST_TYPE type = getType();
				P *p = m_pic;
				I_PIXEL ip;
				memset(&ip, 0, sizeof(I_PIXEL));
				for (int y = 0; y < m_lY; y++)
					for (int x = 0; x < m_lX; x++, p++) {
						getRasterPixel(ras, x, y, ip);
						switch (type) {
						case (ST_RGBM):
							if (ras->type == RAS_RGBM64) {
								// RGBM have to be 'scaled' from USHORT to UCHAR
								p->r = PIX_BYTE_FROM_USHORT((USHORT)ip.r);
								p->g = PIX_BYTE_FROM_USHORT((USHORT)ip.g);
								p->b = PIX_BYTE_FROM_USHORT((USHORT)ip.b);
								p->m = PIX_BYTE_FROM_USHORT((USHORT)ip.m);
							} else {
								// RGBM are UCHAR in the raster
								p->r = (UCHAR)ip.r;
								p->g = (UCHAR)ip.g;
								p->b = (UCHAR)ip.b;
								p->m = (UCHAR)ip.m;
							}
							break;
						case (ST_RGBM64):
							if (ras->type == RAS_RGBM64) {
								// RGBM are USHORT in the raster
								p->r = ip.r;
								p->g = ip.g;
								p->b = ip.b;
								p->m = ip.m;
							} else {
								// RGBM have to be 'scaled' from UCHAR to USHORT
								p->r = PIX_USHORT_FROM_BYTE((UCHAR)ip.r);
								p->g = PIX_USHORT_FROM_BYTE((UCHAR)ip.g);
								p->b = PIX_USHORT_FROM_BYTE((UCHAR)ip.b);
								p->m = PIX_USHORT_FROM_BYTE((UCHAR)ip.m);
							}
							break;
						}
					}
			}
		} catch (SMemAllocError) {
			null();
			throw;
		}
	}

	const CSTPic<P> &operator=(const CSTPic<P> &sp) // throw(SMemAllocError)
	{
		try {
			null();
			m_lX = sp.m_lX;
			m_lY = sp.m_lY;
			m_ras = sp.m_ras;
			initPic();
			copyPic(sp);
		} catch (SMemAllocError) {
			null();
			throw;
		}

		return (*this);
	}

	// Writes the CSTPic into the RASTER
	virtual void write(RASTER *ras) const //throw(SWriteRasterError)
	{
		if ((ras->type == RAS_RGBM || ras->type == RAS_RGBM64) &&
			ras->lx > 0 && ras->ly > 0 && ras->buffer) {
			int x, y;
			I_PIXEL ip;
			const P *p;
			for (y = 0; y < m_lY && y < ras->ly; y++)
				for (x = 0; x < m_lX && x < ras->lx; x++) {
					p = m_pic + y * m_lX + x;
					ip.r = (int)p->r;
					ip.g = (int)p->g;
					ip.b = (int)p->b;
					ip.m = (int)p->m;
					setRasterPixel(ras, x, y, ip);
				}
		} else
			throw SWriteRasterError("(bad Raster type)");
	}

	// Writes the 'r' rectangle of CSTPic into the 'p' position in RASTER
	virtual void write(RASTER *ras, const SRECT &r, const SPOINT &p) const
	//throw(SWriteRasterError)
	{
		if (ras->type == RAS_RGBM || ras->type == RAS_RGBM64) {
			int xs, ys, xd, yd;
			P pp;
			I_PIXEL ip;
			for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
				for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
					getPixel(xs, ys, pp);
					ip.r = (int)pp.r;
					ip.g = (int)pp.g;
					ip.b = (int)pp.b;
					ip.m = (int)pp.m;
					setRasterPixel(ras, xd, yd, ip);
				}
		} else
			throw SWriteRasterError("(bad Raster type)");
	}

	virtual void writeOutBorder(const RASTER *rasin, const int border, RASTER *ras, const SRECT &r, const SPOINT &p) const
	//throw(SWriteRasterError)
	{
		assert(rasin->type == RAS_CM32);
		UD44_PIXEL32 *col = (UD44_PIXEL32 *)(rasin->cmap.buffer);

		if (ras->type == RAS_RGBM || ras->type == RAS_RGBM64) {
			int xs, ys, xd, yd;

			I_PIXEL ip;
			for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
				for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
					int x = xd - border;
					int y = yd - border;
					if (x >= 0 && y >= 0 && x < rasin->lx && y < rasin->ly) {
						UD44_CMAPINDEX32 pixel = *(((UD44_CMAPINDEX32 *)rasin->buffer) + y * rasin->wrap + x);
						//int tone = (pix&0xff);
						//int paint = ((pix>>8)&0xfff);
						if ((pixel & 0xff) == 0 || ((pixel >> 8) & 0xfff) != 0) {
							LPIXEL pLL;
							PIX_CM32_MAP_TO_RGBM(pixel, col, pLL)
							ip.r = (int)pLL.r;
							ip.g = (int)pLL.g;
							ip.b = (int)pLL.b;
							ip.m = (int)pLL.m;
							setRasterPixel(ras, xd, yd, ip);
							continue;
						}
					}

					P pp;
					getPixel(xs, ys, pp);
					ip.r = (int)pp.r;
					ip.g = (int)pp.g;
					ip.b = (int)pp.b;
					ip.m = (int)pp.m;
					setRasterPixel(ras, xd, yd, ip);
				}
		} else
			throw SWriteRasterError("(bad Raster type)");
	}

	void convertToCurrentType(P &d, const I_PIXEL &s) const
	{
		if (getType() == ST_RGBM) {
			d.r = (UCHAR)s.r;
			d.g = (UCHAR)s.g;
			d.b = (UCHAR)s.b;
			d.m = (UCHAR)s.m;
		}
		if (getType() == ST_RGBM64) {
			d.r = (USHORT)s.r;
			d.g = (USHORT)s.g;
			d.b = (USHORT)s.b;
			d.m = (USHORT)s.m;
		}
	}

	bool isSameColor(const P *a, const P *b) const
	{
		if (a->r == b->r && a->g == b->g && a->b == b->b)
			return true;
		return false;
	}

	void colorNoise(const I_PIXEL &cc1, const I_PIXEL &cc2, const double d)
	{
		P c1, c2;

		if (d <= 0)
			return;

		convertToCurrentType(c1, cc1);
		convertToCurrentType(c2, cc2);
		int xy = m_lX * m_lY;
		P *pPic = m_pic;
		for (int i = 0; i < xy; i++, pPic++)
			if (isSameColor(&c1, pPic)) {
				double q = (rand() % 101) / 100.0;
				q = d * q;
				q = q > 1.0 ? 1.0 : q;
				double r = (1.0 - q) * (double)c1.r + q * (double)c2.r;
				double g = (1.0 - q) * (double)c1.g + q * (double)c2.g;
				double b = (1.0 - q) * (double)c1.b + q * (double)c2.b;
				r = D_CUT_0_255(r);
				g = D_CUT_0_255(g);
				b = D_CUT_0_255(b);
				pPic->r = UC_ROUND(r);
				pPic->g = UC_ROUND(g);
				pPic->b = UC_ROUND(b);
			}
	}

	/*
void hlsNoise(const double d) 
{	int xy=m_lX*m_lY;
	P* p=m_pic;
	for( int i=0; i<xy; i++,p++ )
		if ( p->m>0 ) {
			double h,l,s,q;
			rgb2hls(p->r,p->g,p->b,&h,&l,&s);
			q=1.0-d*(double)((rand()%201)-100)/100.0;
			l*=q;
			hls2rgb(h,l,s,&(p->r),&(p->g),&(p->b));
		}		
}
*/

	void rgb2hls(UCHAR r, UCHAR g, UCHAR b,
				 double *h, double *l, double *s)
	{
		double ma, mi, delta, sum;
		double rf, gf, bf;

		rf = (double)r / 255.0;
		gf = (double)g / 255.0;
		bf = (double)b / 255.0;

		ma = rf > gf ? rf : gf;
		ma = ma > bf ? ma : bf;
		mi = rf < gf ? rf : gf;
		mi = mi < bf ? mi : bf;
		sum = ma + mi;
		delta = ma - mi;
		*l = sum / 2.0;
		if (fabs(delta) < 0.000001) {
			*s = 0.0;
			*h = UNDEFINED;
		} else {
			*s = *l <= 0.5 ? delta / sum : delta / (2.0 - sum);
			*h = fabs((rf - ma)) < 0.000001 ? (gf - bf) / delta : (fabs((gf - ma)) < 0.000001 ? 2.0 + (bf - rf) / delta : 4.0 + (rf - gf) / delta);
			*h *= 60;
			*h = *h < 0.0 ? *h + 360.0 : *h;
		}
	}

	double fromHue(double n1, double n2, double hue)
	{
		double v, h;

		h = hue > 360.0 ? hue - 360.0 : hue;
		h = h < 0.0 ? h + 360.0 : h;

		if (h < 60.0) {
			v = n1 + (n2 - n1) * h / 60.0;
		} else if (h < 180.0) {
			v = n2;
		} else if (h < 240.0) {
			v = n1 + (n2 - n1) * (240.0 - h) / 60.0;
		} else
			v = n1;
		return (v);
	}

	void hls2rgb(double h, double l, double s,
				 UCHAR *r, UCHAR *g, UCHAR *b)
	{
		double rf, gf, bf;
		double m1, m2;

		m2 = l <= 0.5 ? l * (1.0 + s) : l + s - l * s;
		m1 = 2 * l - m2;
		if (fabs(s - 0.0) < 0.000001) {
			if (fabs(h - UNDEFINED) < 0.000001) {
				rf = gf = bf = l * 255.0;
			} else
				rf = gf = bf = 0.0;
		} else {
			rf = fromHue(m1, m2, h + 120.0) * 255.0;
			gf = fromHue(m1, m2, h) * 255.0;
			bf = fromHue(m1, m2, h - 120.0) * 255.0;
		}
		rf = D_CUT_0_255(rf);
		gf = D_CUT_0_255(gf);
		bf = D_CUT_0_255(bf);
		*r = UC_ROUND(rf);
		*g = UC_ROUND(gf);
		*b = UC_ROUND(bf);
	}
};
#endif // !defined(AFX_STPIC_H__BABE9488_F054_11D5_B927_0040F674BE6A__INCLUDED_)