/*
* Copyright (c) 2005-2007 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include
#include "view.h"
#include /* XXX depends on rg */
#include "hsvpal.h"
#include "primitive.h"
#include "numerical.h"
#include "gui_math.h"
#include
static float cH = 0.0, cS = 0.0, cV = 0.0, cA = 0.0; /* Copy buffer */
#ifdef AG_THREADS
static AG_Mutex CopyLock = AG_MUTEX_INITIALIZER;
#endif
static void RenderPalette(AG_HSVPal *);
AG_HSVPal *
AG_HSVPalNew(void *parent, Uint flags)
{
AG_HSVPal *pal;
pal = Malloc(sizeof(AG_HSVPal));
AG_ObjectInit(pal, &agHSVPalClass);
pal->flags |= flags;
if (flags & AG_HSVPAL_HFILL) { AG_ExpandHoriz(pal); }
if (flags & AG_HSVPAL_VFILL) { AG_ExpandVert(pal); }
AG_ObjectAttach(parent, pal);
return (pal);
}
/* Return the 8-bit representation of the current alpha value. */
static __inline__ Uint8
GetAlpha8(AG_HSVPal *pal)
{
AG_Variable *bAlpha;
Uint8 a = 255;
void *p;
bAlpha = AG_GetVariable(pal, "alpha", &p);
switch (AG_VARIABLE_TYPE(bAlpha)) {
case AG_VARIABLE_FLOAT:
a = (Uint8)((*(float *)p)*255.0);
break;
case AG_VARIABLE_DOUBLE:
a = (Uint8)((*(double *)p)*255.0);
break;
case AG_VARIABLE_INT:
a = (int)((*(int *)p));
break;
case AG_VARIABLE_UINT8:
a = (int)((*(Uint8 *)p));
break;
default:
break;
}
AG_UnlockVariable(bAlpha);
return (a);
}
static __inline__ void
SetAlpha8(AG_HSVPal *pal, Uint8 a)
{
AG_Variable *bAlpha;
void *pAlpha;
bAlpha = AG_GetVariable(pal, "alpha", &pAlpha);
switch (AG_VARIABLE_TYPE(bAlpha)) {
case AG_VARIABLE_FLOAT:
*(float *)pAlpha = (float)(((float)a)/255.0);
break;
case AG_VARIABLE_DOUBLE:
*(double *)pAlpha = (double)(((double)a)/255.0);
break;
case AG_VARIABLE_INT:
*(int *)pAlpha = (int)a;
break;
case AG_VARIABLE_UINT8:
*(Uint8 *)pAlpha = (Uint8)a;
break;
default:
break;
}
AG_UnlockVariable(bAlpha);
}
static __inline__ void
UpdatePixelFromHSVA(AG_HSVPal *pal)
{
Uint8 r, g, b, a;
AG_Variable *bFormat, *bv;
AG_PixelFormat **pFormat;
void *v;
AG_HSV2RGB(AG_GetFloat(pal, "hue"),
AG_GetFloat(pal, "saturation"),
AG_GetFloat(pal, "value"), &r, &g, &b);
a = GetAlpha8(pal);
if ((bv = AG_GetVariable(pal, "RGBv", &v)) != NULL) {
switch (AG_VARIABLE_TYPE(bv)) {
case AG_VARIABLE_FLOAT:
((float *)v)[0] = (float)r/255.0;
((float *)v)[1] = (float)g/255.0;
((float *)v)[2] = (float)b/255.0;
break;
case AG_VARIABLE_DOUBLE:
((double *)v)[0] = (double)r/255.0;
((double *)v)[1] = (double)g/255.0;
((double *)v)[2] = (double)b/255.0;
break;
case AG_VARIABLE_INT:
((int *)v)[0] = (int)r;
((int *)v)[1] = (int)g;
((int *)v)[2] = (int)b;
break;
case AG_VARIABLE_UINT8:
((Uint8 *)v)[0] = r;
((Uint8 *)v)[1] = g;
((Uint8 *)v)[2] = b;
break;
default:
break;
}
AG_UnlockVariable(bv);
}
if ((bv = AG_GetVariable(pal, "RGBAv", &v)) != NULL) {
switch (AG_VARIABLE_TYPE(bv)) {
case AG_VARIABLE_FLOAT:
((float *)v)[0] = (float)r/255.0;
((float *)v)[1] = (float)g/255.0;
((float *)v)[2] = (float)b/255.0;
((float *)v)[3] = (float)a/255.0;
break;
case AG_VARIABLE_DOUBLE:
((double *)v)[0] = (double)r/255.0;
((double *)v)[1] = (double)g/255.0;
((double *)v)[2] = (double)b/255.0;
((double *)v)[3] = (double)a/255.0;
break;
case AG_VARIABLE_INT:
((int *)v)[0] = (int)r;
((int *)v)[1] = (int)g;
((int *)v)[2] = (int)b;
((int *)v)[3] = (int)a;
break;
case AG_VARIABLE_UINT8:
((Uint8 *)v)[0] = r;
((Uint8 *)v)[1] = g;
((Uint8 *)v)[2] = b;
((Uint8 *)v)[3] = a;
break;
default:
break;
}
AG_UnlockVariable(bv);
}
bFormat = AG_GetVariable(pal, "pixel-format", &pFormat);
AG_SetUint32(pal, "pixel", AG_MapRGBA(*pFormat, r,g,b,a));
AG_UnlockVariable(bFormat);
}
static void
UpdateHSVFromPixel(AG_HSVPal *hsv, Uint32 pixel)
{
Uint8 r, g, b, a;
float h, s, v;
AG_Variable *bFormat;
AG_PixelFormat **pFormat;
bFormat = AG_GetVariable(hsv, "pixel-format", &pFormat);
AG_GetRGBA(pixel, *pFormat, &r,&g,&b,&a);
AG_RGB2HSV(r, g, b, &h,&s,&v);
AG_SetFloat(hsv, "hue", h);
AG_SetFloat(hsv, "saturation", s);
AG_SetFloat(hsv, "value", v);
SetAlpha8(hsv, a);
AG_UnlockVariable(bFormat);
}
static void
UpdateHSVFromRGBAv(AG_HSVPal *hsv)
{
AG_Variable *bRGBAv;
void *RGBAv;
Uint8 r, g, b, a;
float h, s, v;
if ((bRGBAv = AG_GetVariable(hsv, "RGBAv", &RGBAv)) == NULL) {
return;
}
switch (AG_VARIABLE_TYPE(bRGBAv)) {
case AG_VARIABLE_FLOAT:
r = (Uint8)(((float *)RGBAv)[0] * 255.0);
g = (Uint8)(((float *)RGBAv)[1] * 255.0);
b = (Uint8)(((float *)RGBAv)[2] * 255.0);
a = (Uint8)(((float *)RGBAv)[3] * 255.0);
break;
case AG_VARIABLE_DOUBLE:
r = (Uint8)(((double *)RGBAv)[0] * 255.0);
g = (Uint8)(((double *)RGBAv)[1] * 255.0);
b = (Uint8)(((double *)RGBAv)[2] * 255.0);
a = (Uint8)(((double *)RGBAv)[3] * 255.0);
break;
case AG_VARIABLE_INT:
r = (Uint8)(((int *)RGBAv)[0]);
g = (Uint8)(((int *)RGBAv)[1]);
b = (Uint8)(((int *)RGBAv)[2]);
a = (Uint8)(((int *)RGBAv)[3]);
break;
case AG_VARIABLE_UINT8:
r = ((Uint8 *)RGBAv)[0];
g = ((Uint8 *)RGBAv)[1];
b = ((Uint8 *)RGBAv)[2];
a = ((Uint8 *)RGBAv)[3];
break;
default:
r = 0;
g = 0;
b = 0;
a = 0;
break;
}
AG_RGB2HSV(r, g, b, &h, &s, &v);
AG_SetFloat(hsv, "hue", h);
AG_SetFloat(hsv, "saturation", s);
AG_SetFloat(hsv, "value", v);
SetAlpha8(hsv, a);
AG_UnlockVariable(bRGBAv);
hsv->flags |= AG_HSVPAL_DIRTY;
}
static void
UpdateHSVFromRGBv(AG_HSVPal *hsv)
{
AG_Variable *bRGBv;
void *RGBv;
Uint8 r, g, b;
float h, s, v;
if ((bRGBv = AG_GetVariable(hsv, "RGBv", &RGBv)) == NULL) {
return;
}
switch (AG_VARIABLE_TYPE(bRGBv)) {
case AG_VARIABLE_FLOAT:
r = (Uint8)(((float *)RGBv)[0] * 255.0);
g = (Uint8)(((float *)RGBv)[1] * 255.0);
b = (Uint8)(((float *)RGBv)[2] * 255.0);
break;
case AG_VARIABLE_DOUBLE:
r = (Uint8)(((double *)RGBv)[0] * 255.0);
g = (Uint8)(((double *)RGBv)[1] * 255.0);
b = (Uint8)(((double *)RGBv)[2] * 255.0);
break;
case AG_VARIABLE_INT:
r = (Uint8)(((int *)RGBv)[0]);
g = (Uint8)(((int *)RGBv)[1]);
b = (Uint8)(((int *)RGBv)[2]);
break;
case AG_VARIABLE_UINT8:
r = ((Uint8 *)RGBv)[0];
g = ((Uint8 *)RGBv)[1];
b = ((Uint8 *)RGBv)[2];
break;
default:
r = 0;
g = 0;
b = 0;
break;
}
AG_RGB2HSV(r, g, b, &h, &s, &v);
AG_SetFloat(hsv, "hue", h);
AG_SetFloat(hsv, "saturation", s);
AG_SetFloat(hsv, "value", v);
AG_UnlockVariable(bRGBv);
hsv->flags |= AG_HSVPAL_DIRTY;
}
static void
UpdateHue(AG_HSVPal *pal, int x, int y)
{
float h;
h = Atan2((float)y, (float)x);
if (h < 0) {
h += (float)(2*AG_PI);
}
AG_SetFloat(pal, "hue", h/(2*AG_PI)*360.0);
UpdatePixelFromHSVA(pal);
AG_PostEvent(NULL, pal, "h-changed", NULL);
pal->flags |= AG_HSVPAL_DIRTY;
}
static void
UpdateSV(AG_HSVPal *pal, int ax, int ay)
{
float s, v;
int x = ax - pal->triangle.x;
int y = ay - pal->triangle.y;
if (x < -y/2) { x = -y/2; }
if (x > y/2) { x = y/2; }
if (y > pal->triangle.h-1) { y = pal->triangle.h-1; }
s = 1.0 - (float)y/(float)pal->triangle.h;
v = 1.0 - (float)(x + y/2)/(float)pal->triangle.h;
if (s < 0.0F) { s = 0.00001F; }
else if (s > 1.0F) { s = 1.0F; }
if (v < 0.0F) { v = 0.0001F; }
else if (v > 1.0F) { v = 1.0F; }
AG_SetFloat(pal, "saturation", s);
AG_SetFloat(pal, "value", v);
UpdatePixelFromHSVA(pal);
AG_PostEvent(NULL, pal, "sv-changed", NULL);
pal->flags |= AG_HSVPAL_DIRTY;
}
static void
UpdateAlpha(AG_HSVPal *pal, int x)
{
AG_Variable *bAlpha;
void *pAlpha;
bAlpha = AG_GetVariable(pal, "alpha", &pAlpha);
switch (AG_VARIABLE_TYPE(bAlpha)) {
case AG_VARIABLE_FLOAT:
*(float *)pAlpha = ((float)x)/((float)pal->rAlpha.w);
if (*(float *)pAlpha > 1.0) {
*(float *)pAlpha = 1.0;
} else if (*(float *)pAlpha < 0.0) {
*(float *)pAlpha = 0.0;
}
break;
case AG_VARIABLE_DOUBLE:
*(double *)pAlpha = ((double)x)/((double)pal->rAlpha.w);
if (*(double *)pAlpha > 1.0) {
*(double *)pAlpha = 1.0;
} else if (*(double *)pAlpha < 0.0) {
*(double *)pAlpha = 0.0;
}
break;
case AG_VARIABLE_INT:
*(int *)pAlpha = x/pal->rAlpha.w;
if (*(int *)pAlpha > 255) {
*(int *)pAlpha = 255;
} else if (*(int *)pAlpha < 0) {
*(int *)pAlpha = 0;
}
break;
case AG_VARIABLE_UINT8:
*(Uint8 *)pAlpha = (Uint8)(x/pal->rAlpha.w);
break;
default:
break;
}
AG_UnlockVariable(bAlpha);
UpdatePixelFromHSVA(pal);
AG_PostEvent(NULL, pal, "a-changed", NULL);
}
static void
CloseMenu(AG_HSVPal *pal)
{
AG_MenuCollapse(pal->menu, pal->menu_item);
AG_ObjectDestroy(pal->menu);
pal->menu = NULL;
pal->menu_item = NULL;
pal->menu_win = NULL;
}
static void
ShowRGBValue(AG_Event *event)
{
AG_HSVPal *pal = AG_PTR(1);
Uint8 r, g, b;
float h, s, v;
AG_ObjectLock(pal);
h = AG_GetFloat(pal, "hue");
s = AG_GetFloat(pal, "saturation");
v = AG_GetFloat(pal, "value");
AG_ObjectUnlock(pal);
AG_HSV2RGB(h, s, v, &r, &g, &b);
AG_TextMsg(AG_MSG_INFO, "%.2f,%.2f,%.2f -> %u,%u,%u", h, s, v, r, g, b);
}
#if 0
static void
EditNumValues(AG_Event *event)
{
AG_HSVPal *pal = AG_PTR(1);
AG_Window *pwin;
AG_Window *win;
AG_Numerical *num;
AG_Variable *b1, *b2;
float v;
if ((pwin = AG_WidgetParentWindow(pal)) == NULL)
return;
if ((win = AG_WindowNewNamed(AG_WINDOW_NOMAXIMIZE, "hsvpal-%p-numedit",
pal)) == NULL) {
return;
}
AG_WindowSetCaption(win, _("Color values"));
AG_WindowSetPosition(win, AG_WINDOW_LOWER_LEFT, 0);
AG_WindowSetCloseAction(win, AG_WINDOW_DETACH);
{
AG_Variable *bAlpha;
void *pAlpha;
num = AG_NumericalNew(win, 0, NULL, _("Hue: "));
AG_NumericalSizeHint(num, "000");
AG_WidgetCopyBinding(num, "value", pal, "hue");
AG_NumericalSetRange(num, 0.0, 359.0);
AG_NumericalSetIncrement(num, 1);
AG_NumericalSetPrecision(num, "f", 0);
num = AG_NumericalNew(win, 0, NULL, _("Saturation: "));
AG_NumericalSizeHint(num, "00.00");
AG_WidgetCopyBinding(num, "value", pal, "saturation");
AG_NumericalSetRange(num, 0.0, 1.0);
AG_NumericalSetIncrement(num, 0.01);
AG_NumericalSetPrecision(num, "f", 2);
num = AG_NumericalNew(win, 0, NULL, _("Value: "));
AG_NumericalSizeHint(num, "00.00");
AG_WidgetCopyBinding(num, "value", pal, "value");
AG_NumericalSetRange(num, 0.0, 1.0);
AG_NumericalSetIncrement(num, 0.01);
AG_NumericalSetPrecision(num, "f", 2);
num = AG_NumericalNew(win, 0, NULL, _("Alpha: "));
AG_NumericalSizeHint(num, "0.000");
AG_WidgetCopyBinding(num, "value", pal, "alpha");
bAlpha = AG_GetVariable(pal, "alpha", &pAlpha);
switch (AG_VARIABLE_TYPE(bAlpha)) {
case AG_VARIABLE_FLOAT:
case AG_VARIABLE_DOUBLE:
AG_NumericalSetRange(num, 0.0, 1.0);
AG_NumericalSetIncrement(num, 0.005);
AG_NumericalSetPrecision(num, "f", 3);
break;
case AG_VARIABLE_INT:
case AG_VARIABLE_UINT:
case AG_VARIABLE_UINT8:
AG_NumericalSetRange(num, 0.0, 255.0);
AG_NumericalSetIncrement(num, 1.0);
AG_NumericalSetPrecision(num, "f", 0);
break;
}
AG_UnlockVariable(bAlpha);
}
AG_WindowAttach(pwin, win);
AG_WindowShow(win);
}
#endif
static void
SetComplementaryColor(AG_Event *event)
{
AG_HSVPal *pal = AG_PTR(1);
float hue;
AG_ObjectLock(pal);
hue = AG_GetFloat(pal, "hue");
AG_SetFloat(pal, "hue", ((int)hue+180) % 359);
UpdatePixelFromHSVA(pal);
pal->flags |= AG_HSVPAL_DIRTY;
AG_PostEvent(NULL, pal, "h-changed", NULL);
AG_PostEvent(NULL, pal, "sv-changed", NULL);
AG_ObjectUnlock(pal);
}
static void
CopyColor(AG_Event *event)
{
AG_HSVPal *pal = AG_PTR(1);
AG_ObjectLock(pal);
AG_MutexLock(&CopyLock);
cH = AG_GetFloat(pal, "hue");
cS = AG_GetFloat(pal, "saturation");
cV = AG_GetFloat(pal, "value");
cA = AG_GetFloat(pal, "alpha");
AG_MutexUnlock(&CopyLock);
AG_ObjectUnlock(pal);
}
static void
PasteColor(AG_Event *event)
{
AG_HSVPal *pal = AG_PTR(1);
AG_ObjectLock(pal);
AG_MutexLock(&CopyLock);
AG_SetFloat(pal, "hue", cH);
AG_SetFloat(pal, "saturation", cS);
AG_SetFloat(pal, "value", cV);
AG_SetFloat(pal, "alpha", cA);
AG_MutexUnlock(&CopyLock);
UpdatePixelFromHSVA(pal);
pal->flags |= AG_HSVPAL_DIRTY;
AG_PostEvent(NULL, pal, "h-changed", NULL);
AG_PostEvent(NULL, pal, "sv-changed", NULL);
AG_ObjectUnlock(pal);
}
static void
OpenMenu(AG_HSVPal *pal)
{
int x, y;
if (pal->menu != NULL)
CloseMenu(pal);
pal->menu = AG_MenuNew(NULL, 0);
pal->menu_item = AG_MenuAddItem(pal->menu, NULL);
{
#if 0
AG_MenuAction(pal->menu_item, _("Edit numerically"), NULL,
EditNumValues, "%p", pal);
#endif
AG_MenuAction(pal->menu_item, _("Copy"), NULL,
CopyColor, "%p", pal);
AG_MenuAction(pal->menu_item, _("Paste"), NULL,
PasteColor, "%p", pal);
AG_MenuSeparator(pal->menu_item);
AG_MenuAction(pal->menu_item, _("Show RGB value"), NULL,
ShowRGBValue, "%p", pal);
AG_MenuAction(pal->menu_item, _("Complementary color"), NULL,
SetComplementaryColor, "%p", pal);
}
pal->menu->itemSel = pal->menu_item;
SDL_GetMouseState(&x, &y);
pal->menu_win = AG_MenuExpand(pal->menu, pal->menu_item, x, y);
}
static void
MouseButtonDown(AG_Event *event)
{
AG_HSVPal *pal = AG_SELF();
int btn = AG_INT(1);
int x = AG_INT(2);
int y = AG_INT(3);
float r;
switch (btn) {
case SDL_BUTTON_LEFT:
if (y > pal->rAlpha.y) {
UpdateAlpha(pal, x);
pal->state = AG_HSVPAL_SEL_A;
} else {
x -= pal->circle.x;
y -= pal->circle.y;
r = Hypot((float)x, (float)y);
if (r > (float)pal->circle.rin) {
UpdateHue(pal, x, y);
pal->state = AG_HSVPAL_SEL_H;
} else {
UpdateSV(pal, AG_INT(2), AG_INT(3));
pal->state = AG_HSVPAL_SEL_SV;
}
}
AG_WidgetFocus(pal);
break;
case SDL_BUTTON_MIDDLE:
case SDL_BUTTON_RIGHT:
OpenMenu(pal);
break;
}
}
static void
MouseButtonUp(AG_Event *event)
{
AG_HSVPal *pal = AG_SELF();
pal->state = AG_HSVPAL_SEL_NONE;
}
static void
MouseMotion(AG_Event *event)
{
AG_HSVPal *pal = AG_SELF();
int x = AG_INT(1);
int y = AG_INT(2);
switch (pal->state) {
case AG_HSVPAL_SEL_NONE:
break;
case AG_HSVPAL_SEL_H:
UpdateHue(pal,
x - pal->circle.x,
y - pal->circle.y);
break;
case AG_HSVPAL_SEL_SV:
UpdateSV(pal, x, y);
break;
case AG_HSVPAL_SEL_A:
UpdateAlpha(pal, x);
break;
}
}
static void
Bound(AG_Event *event)
{
AG_HSVPal *hsv = AG_SELF();
AG_Variable *V = AG_PTR(1);
if (AG_VARIABLE_TYPE(V) == AG_VARIABLE_UINT32 &&
strcmp(V->name, "pixel") == 0) {
#if 0
hsv->flags |= AG_HSVPAL_PIXEL;
#endif
UpdateHSVFromPixel(hsv, *(Uint32 *)V->data.p);
} else if (strcmp(V->name, "RGBAv") == 0) {
UpdateHSVFromRGBAv(hsv);
} else if (strcmp(V->name, "RGBv") == 0) {
UpdateHSVFromRGBv(hsv);
}
}
static void
Init(void *obj)
{
AG_HSVPal *pal = obj;
WIDGET(pal)->flags |= AG_WIDGET_FOCUSABLE;
AG_BindFloat(pal, "hue", &pal->h);
AG_BindFloat(pal, "saturation", &pal->s);
AG_BindFloat(pal, "value", &pal->v);
AG_BindFloat(pal, "alpha", &pal->a);
AG_BindUint32(pal, "pixel", &pal->pixel);
AG_BindPointer(pal, "pixel-format", (void *)&agVideoFmt);
/* AG_BindFloat(pal, "red", &pal->r); */
/* AG_BindFloat(pal, "green", &pal->g); */
/* AG_BindFloat(pal, "blue", &pal->b); */
/* AG_BindFloat(pal, "RGBAv", &pal->rgbav); */
pal->flags = 0;
pal->h = 0.0;
pal->s = 0.0;
pal->v = 0.0;
pal->a = 1.0;
pal->pixel = AG_MapRGBA(agVideoFmt, 0,0,0,255);
pal->circle.spacing = 10;
pal->circle.width = 20;
pal->state = AG_HSVPAL_SEL_NONE;
pal->surface = NULL;
pal->menu = NULL;
pal->menu_item = NULL;
pal->menu_win = NULL;
AG_WidgetMapSurface(pal, NULL);
AG_SetEvent(pal, "window-mousebuttonup", MouseButtonUp, NULL);
AG_SetEvent(pal, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(pal, "window-mousemotion", MouseMotion, NULL);
AG_SetEvent(pal, "bound", Bound, NULL);
}
static void
RenderPalette(AG_HSVPal *pal)
{
float h, cur_h, cur_s, cur_v;
Uint32 pc;
Uint8 r, g, b, a, da;
int x, y, i;
AG_Rect rd;
cur_h = (AG_GetFloat(pal, "hue")/360) * 2*AG_PI;
cur_s = AG_GetFloat(pal, "saturation");
cur_v = AG_GetFloat(pal, "value");
AG_SurfaceLock(pal->surface);
/* Render the circle of hues. */
for (h = 0.0; h < 2*AG_PI; h += pal->circle.dh) {
AG_HSV2RGB((h/(2*AG_PI)*360.0), 1.0, 1.0, &r, &g, &b);
pc = AG_MapRGB(agVideoFmt, r,g,b);
for (i = 0; i < pal->circle.width; i++) {
x = (pal->circle.rout - i)*Cos(h);
y = (pal->circle.rout - i)*Sin(h);
AG_PUT_PIXEL2(pal->surface,
pal->circle.x+x,
pal->circle.y+y,
pc);
}
}
/* Render the triangle of saturation and value. */
for (y = 0; y < pal->triangle.h; y += 2) {
float sat = (float)(pal->triangle.h - y) /
(float)(pal->triangle.h);
for (x = 0; x < y; x++) {
AG_HSV2RGB((cur_h/(2*AG_PI))*360.0, sat,
1.0 - ((float)x/(float)pal->triangle.h),
&r, &g, &b);
pc = AG_MapRGB(agVideoFmt, r,g,b);
AG_PUT_PIXEL2(pal->surface,
pal->triangle.x + x - y/2,
pal->triangle.y + y,
pc);
AG_PUT_PIXEL2(pal->surface,
pal->triangle.x + x - y/2,
pal->triangle.y + y + 1,
pc);
}
}
/* Render the alpha selector. */
/* XXX overblending */
for (y = 8; y < pal->rAlpha.h+16; y+=8) {
for (x = 0; x < pal->rAlpha.w; x+=16) {
rd.w = 8;
rd.h = 8;
rd.x = pal->rAlpha.x+x;
rd.y = pal->rAlpha.y+y;
AG_FillRect(pal->surface, &rd, pal->cTile);
}
y += 8;
for (x = 8; x < pal->rAlpha.w; x+=16) {
rd.w = 8;
rd.h = 8;
rd.x = pal->rAlpha.x+x;
rd.y = pal->rAlpha.y+y;
AG_FillRect(pal->surface, &rd, pal->cTile);
}
}
AG_HSV2RGB((cur_h/(2*AG_PI))*360.0, cur_s, cur_v, &r, &g, &b);
da = MIN(1, pal->surface->w/255);
for (y = pal->rAlpha.y+8; y < pal->surface->h; y++) {
for (x = 0, a = 0; x < pal->surface->w; x++) {
AG_BLEND_RGBA2_CLIPPED(pal->surface, x, y,
r, g, b, a, AG_ALPHA_SRC);
a = x*255/pal->surface->w;
}
}
AG_SurfaceUnlock(pal->surface);
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
r->w = 128;
r->h = 128;
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_HSVPal *pal = obj;
if (a->w < 32 || a->h < 32)
return (-1);
pal->rAlpha.x = 0;
pal->rAlpha.h = 32;
pal->rAlpha.y = a->h - 32;
pal->rAlpha.w = a->w;
pal->circle.rout = MIN(a->w, (a->h - pal->rAlpha.h))/2;
pal->circle.rin = pal->circle.rout - pal->circle.width;
pal->circle.dh = (float)(1.0/(pal->circle.rout*AG_PI));
pal->circle.x = a->w/2;
pal->circle.y = (a->h - pal->rAlpha.h)/2;
pal->triangle.x = a->w/2;
pal->triangle.y = pal->circle.y+pal->circle.width-pal->circle.rout;
pal->triangle.h = pal->circle.rin*Sin((37.0/360.0)*(2*AG_PI)) -
pal->circle.rin*Sin((270.0/360.0)*(2*AG_PI));
pal->selcircle_r = pal->circle.width/2 - 4;
pal->flags |= AG_HSVPAL_DIRTY;
return (0);
}
static void
Draw(void *obj)
{
AG_HSVPal *pal = obj;
float cur_h, cur_s, cur_v;
Uint8 r, g, b, a;
int x, y;
if (WIDGET(pal)->w < 16 || WIDGET(pal)->h < 16)
return;
if (pal->flags & AG_HSVPAL_DIRTY) {
pal->flags &= ~(AG_HSVPAL_DIRTY);
pal->surface = AG_SurfaceVideoRGB(WIDTH(pal), HEIGHT(pal));
if (pal->surface == NULL) {
AG_FatalError(NULL);
}
pal->cTile = AG_MapRGB(pal->surface->format, 140,140,140);
RenderPalette(pal);
AG_WidgetReplaceSurface(pal, 0, pal->surface);
}
cur_h = (AG_GetFloat(pal, "hue") / 360.0) * 2*AG_PI;
cur_s = AG_GetFloat(pal, "saturation");
cur_v = AG_GetFloat(pal, "value");
a = (Uint8)(AG_GetFloat(pal, "alpha")*255);
AG_WidgetBlitFrom(pal, pal, 0, NULL, 0, 0);
/* Indicate the current selection. */
AG_DrawCircle(pal,
pal->circle.x + (pal->circle.rin + pal->circle.width/2)*Cos(cur_h),
pal->circle.y + (pal->circle.rin + pal->circle.width/2)*Sin(cur_h),
pal->selcircle_r,
AG_COLOR(HSVPAL_CIRCLE_COLOR));
/* The rendering routine uses (v = 1 - x/h), so (x = -v*h + h). */
y = (int)((1.0 - cur_s) * (float)pal->triangle.h);
x = (int)(-(cur_v*(float)pal->triangle.h - (float)pal->triangle.h));
if (x < 0) { x = 0; }
if (x > y) { x = y; }
AG_DrawCircle(pal,
pal->triangle.x + x - y/2,
pal->triangle.y + y,
pal->selcircle_r,
AG_COLOR(HSVPAL_CIRCLE_COLOR));
x = a*pal->rAlpha.w/255;
if (x > pal->rAlpha.w-3) { x = pal->rAlpha.w-3; }
/* Draw the color preview. */
AG_HSV2RGB((cur_h*360.0)/(2*AG_PI), cur_s, cur_v, &r, &g, &b);
AG_DrawRectFilled(pal,
AG_RECT(pal->rAlpha.x, pal->rAlpha.y, pal->rAlpha.w, 8),
AG_MapRGB(agVideoFmt, r,g,b));
/* Draw the alpha bar. */
AG_DrawLineV(pal,
pal->rAlpha.x + x,
pal->rAlpha.y + 1,
pal->rAlpha.y + pal->rAlpha.h,
AG_COLOR(HSVPAL_BAR1_COLOR));
AG_DrawLineV(pal,
pal->rAlpha.x + x + 1,
pal->rAlpha.y + 1,
pal->rAlpha.y + pal->rAlpha.h,
AG_COLOR(HSVPAL_BAR2_COLOR));
AG_DrawLineV(pal,
pal->rAlpha.x + x + 2,
pal->rAlpha.y + 1,
pal->rAlpha.y + pal->rAlpha.h,
AG_COLOR(HSVPAL_BAR1_COLOR));
}
AG_WidgetClass agHSVPalClass = {
{
"Agar(Widget:HSVPal)",
sizeof(AG_HSVPal),
{ 0,0 },
Init,
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};