Color picker gui

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Color picker gui

Post by Ovan »

plop everyone I developpe a small, minimalist and cute color picker pane to replace the internal irrlicht IGUIColorSelectDialog that is really poor
for once I share my code so:

Image Image

Code: Select all

#ifndef __C_GUI_COLOR_PICKER_HEADER__
#define __C_GUI_COLOR_PICKER_HEADER__
 
#include <irrlicht/IGUIElement.h>
#include <irrlicht/S3DVertex.h>
 
 
/**
 * Copyright (C) <2014> <Jehan-antoine vayssade>
 * Ovan/Magun contact on irrlicht-fr.org or ovan@sleek-think.ovh
 * 
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
**/
 
namespace irr
{
    namespace gui
    {
        class IGUIButton;
        class IGUIStaticText;
        class IGUIScrollBar;
 
        class CGUIColorPicker : public IGUIElement
        {
            public:
                CGUIColorPicker(IGUIEnvironment *environment, IGUIElement *parent, s32 id = 0) noexcept;
                ~CGUIColorPicker() noexcept;
 
                virtual void setRelativePosition(const core::recti &r);
 
                virtual bool OnEvent(const SEvent&) noexcept;
                virtual void updateAbsolutePosition();
 
                virtual void setPickedColor(const video::SColor&) noexcept;
                virtual const video::SColor& getPickedColor() const noexcept;
 
                virtual void setBackgroundColor(const video::SColor&) noexcept;
                virtual const video::SColor& getBackgroundColor() const noexcept;
 
                virtual void draw();
            protected:
                bool isGradient, isColor, isInside;
                virtual void recalculatePickedColor() noexcept;
                virtual void createAlphaTexture() noexcept;
                virtual void createGradientTexture() noexcept;
            protected:
                IGUIButton      *close;
                IGUIScrollBar   *scroll;
                video::ITexture *img[2];
            protected:
                video::SColor    pickcolor, color;
                video::SColor    background, white, black, alpha;
                core::recti      box, pickbox, gradient;
                core::vector2di  pickpos;
                int              colorpos;
        };
    }
}
 
#endif

Code: Select all

#include "CGUIColorPicker.h"
 
#include <irrlicht/IGUIEnvironment.h>
#include <irrlicht/IGUIButton.h>
#include <irrlicht/IGUIScrollBar.h>
#include <irrlicht/IVideoDriver.h>
#include <irrlicht/irrMath.h>
#include <irrlicht/SColor.h>
 
namespace irr
{
    inline core::vector3df RGBftoHSV(const video::SColorf &rgb)
    {
        core::vector3df hsv;
 
        f32 M = core::max_(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
        f32 m = core::min_(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
        f32 C = M - m;
 
        if(C == 0)
            hsv.X = 0;
        else if(M <= rgb.getRed())
            hsv.X =((rgb.getGreen() - rgb.getBlue()) / C);
        else if(M <= rgb.getGreen())
            hsv.X =((rgb.getBlue() - rgb.getRed()) / C) + 2;
        else if(M <= rgb.getBlue())
            hsv.X =((rgb.getRed() - rgb.getGreen()) / C) + 4;
 
        hsv.X *= 60;
        if(hsv.X < 0)
            hsv.X += 360;
 
        hsv.Y = M;
 
        if(hsv.Y == 0) hsv.Z = 0;
        else hsv.Z = C / hsv.Y;
 
        return hsv;
    }
    namespace gui
    {
        CGUIColorPicker::CGUIColorPicker(IGUIEnvironment *environment, IGUIElement *parent, s32 id) noexcept
            : IGUIElement(EGUIET_COLOR_SELECT_DIALOG , environment, parent, id, {200, 200, 310, 360}),
              background{64, 255, 255, 255}, white{255, 255, 255, 255}, black{255, 0, 0, 0},
              colorpos(0), isGradient(false), isColor(false)
        {
            close = Environment->addButton({5, 140, 85, 156}, this, 0, L"take this color");
 
            scroll = Environment->addScrollBar(true, {5, 125, 85, 135}, this);
            scroll->setMin(0);
            scroll->setMax(255);
            scroll->setPos(255);
 
            createAlphaTexture();
            createGradientTexture();
 
            setPickedColor({255, 64, 64, 128});
            updateAbsolutePosition();
        }
        CGUIColorPicker::~CGUIColorPicker() noexcept
        {
            close->drop();
            scroll->drop();
            img[0]->drop();
            img[1]->drop();
        }
        void CGUIColorPicker::createAlphaTexture() noexcept
        {
            img[0] = Environment->getVideoDriver()->addTexture({16, 16}, "alpha", video::ECF_A8R8G8B8);
            u32 *tmp = (u32*) img[0]->lock();
 
            video::SColor color;
 
            #define square(colorstart, sx, sy, sz, sw)                         \
                color = colorstart;                                            \
                for(int y=sy; y<sw; ++y)                                       \
                    for(int x=sx; x<sz; ++x)                                   \
                        color.getData(&tmp[x + y*16], video::ECF_A8R8G8B8);    \

            square(video::SColor(255, 153, 153, 153), 0, 0,  8,  8);
            square(video::SColor(255, 153, 153, 153), 8, 8, 16, 16);
            square(video::SColor(255, 102, 102, 102), 8, 0, 16,  8);
            square(video::SColor(255, 102, 102, 102), 0, 8,  8, 16);
 
            img[0]->unlock();
        }
        void CGUIColorPicker::createGradientTexture() noexcept
        {
            img[1] = Environment->getVideoDriver()->addTexture({15, 151}, "gradient", video::ECF_A8R8G8B8);
            u32 *tmp = (u32*) img[1]->lock();
 
            video::SColor from;
            video::SColor to;
 
            #define interpolate(colorstart, colorend, start, end)              \
                from = colorstart;                                             \
                to = colorend;                                                 \
                                                                               \
                for(int y=start; y<end; ++y)                                   \
                {                                                              \
                    video::SColor c = to.getInterpolated(from, (y-start)/25.f);\
                    for(int x=0; x<15; ++x)                                    \
                        c.getData(&tmp[x + y*15], video::ECF_A8R8G8B8);        \
                }
 
            interpolate(video::SColor(255, 255, 0, 0),   video::SColor(255, 255, 0, 255),   0,  25);
            interpolate(video::SColor(255, 255, 0, 255), video::SColor(255, 0, 0, 255),    25,  50);
 
            interpolate(video::SColor(255, 0, 0, 255), video::SColor(255, 0, 255, 255),  50,  75);
            interpolate(video::SColor(255, 0, 255, 255), video::SColor(255, 0, 255, 0),  75, 100);
 
            interpolate(video::SColor(255, 0, 255, 0), video::SColor(255, 255, 255, 0), 100, 125);
            interpolate(video::SColor(255, 255, 255, 0), video::SColor(255, 255, 0, 0), 125, 151);
 
            img[1]->unlock();
        }
        void CGUIColorPicker::setRelativePosition(const core::recti &r)
        {
            RelativeRect.UpperLeftCorner = r.UpperLeftCorner;
            RelativeRect.LowerRightCorner.X = r.UpperLeftCorner.X + 110;
            RelativeRect.LowerRightCorner.Y = r.UpperLeftCorner.X + 160;
        }
        bool CGUIColorPicker::OnEvent(const SEvent &event) noexcept
        {
            if(event.EventType == EET_MOUSE_INPUT_EVENT)
            {
                core::vector2di pos(event.MouseInput.X, event.MouseInput.Y);
 
                if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
                {
                    isGradient = gradient.isPointInside(pos);
                    isColor = box.isPointInside(pos);
                    isInside = AbsoluteRect.isPointInside(pos);
                }
 
                if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
                {
                    // does not work since 1.7 (i think)
                    // probably need CGUIModalScreen if this module is add to the engine
                    //if(!AbsoluteRect.isPointInside(pos) && !isInside)
                    //{
                    //    SEvent event;
                    //    event.EventType = EET_GUI_EVENT;
                    //    event.GUIEvent.Caller = this;
                    //    event.GUIEvent.Element = 0;
                    //    event.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
                    //    Parent->OnEvent(event);
                    //    remove();
                    //    drop();
                    //}
                    isGradient = isColor = false;
                }
 
                if(isGradient)
                {
                    if(pos.X < gradient.UpperLeftCorner.X)
                        pos.X = gradient.UpperLeftCorner.X;
                    if(pos.Y < gradient.UpperLeftCorner.Y)
                        pos.Y = gradient.UpperLeftCorner.Y;
 
                    if(pos.X > gradient.LowerRightCorner.X)
                        pos.X = gradient.LowerRightCorner.X;
                    if(pos.Y > gradient.LowerRightCorner.Y)
                        pos.Y = gradient.LowerRightCorner.Y;
 
                    colorpos = pos.Y - gradient.UpperLeftCorner.Y;
                    u32 *tmp =(u32*)img[1]->lock();
                    color.set(tmp[colorpos*img[1]->getOriginalSize().Width]);
                    img[1]->unlock();
                    recalculatePickedColor();
                }
 
                if(isColor)
                {
                    if(pos.X < box.UpperLeftCorner.X)
                        pos.X = box.UpperLeftCorner.X;
                    if(pos.Y < box.UpperLeftCorner.Y)
                        pos.Y = box.UpperLeftCorner.Y;
 
                    if(pos.X > box.LowerRightCorner.X)
                        pos.X = box.LowerRightCorner.X;
                    if(pos.Y > box.LowerRightCorner.Y)
                        pos.Y = box.LowerRightCorner.Y;
 
                    pickpos.X = pos.X - box.UpperLeftCorner.X;
                    pickpos.Y = pos.Y - box.UpperLeftCorner.Y;
                    recalculatePickedColor();
                }
 
                if(isGradient || isColor)
                    return true;
            }
 
            if(event.EventType == EET_GUI_EVENT)
            {
                switch(event.GUIEvent.EventType)
                {
                    case EGET_BUTTON_CLICKED:
                        SEvent event;
                        event.EventType = EET_GUI_EVENT;
                        event.GUIEvent.Caller = this;
                        event.GUIEvent.Element = 0;
                        event.GUIEvent.EventType = EGET_FILE_SELECTED;
                        Parent->OnEvent(event);
                    break;
                    case EGET_SCROLL_BAR_CHANGED:
                        recalculatePickedColor();
                        return true;
                    break;
                }
            }
 
            return IGUIElement::OnEvent(event);
        }
        void CGUIColorPicker::recalculatePickedColor() noexcept
        {
            video::SColor hcolor = color.getInterpolated(white, pickpos.X/80.f);
 
            pickcolor = black.getInterpolated(hcolor, pickpos.Y/80.f);
            pickcolor.setAlpha(scroll->getPos());
 
            alpha = color;
            alpha.setAlpha(0);
        }
        void CGUIColorPicker::setPickedColor(const video::SColor &c) noexcept
        {
            pickcolor = c;
 
            core::vector3df hsv = RGBftoHSV({
                c.getRed()/255.f,
                c.getGreen()/255.f,
                c.getBlue()/255.f,
                c.getAlpha()/255.f
            });
 
            colorpos = 150-hsv.X/360.f*150.f;
            pickpos.X = hsv.Y*80.f;
            pickpos.Y = 80-(hsv.Z)*80.f;
 
            u32 *tmp =(u32*)img[1]->lock();
            color.set(tmp[colorpos*img[1]->getOriginalSize().Width]);
            img[1]->unlock();
 
            alpha = color;
            alpha.setAlpha(0);
        }
        const video::SColor& CGUIColorPicker::getPickedColor() const noexcept
        {
            return pickcolor;
        }
        void CGUIColorPicker::setBackgroundColor(const video::SColor &b) noexcept
        {
            background = b;
        }
        const video::SColor& CGUIColorPicker::getBackgroundColor() const noexcept
        {
            return background;
        }
        void CGUIColorPicker::updateAbsolutePosition()
        {
            IGUIElement::updateAbsolutePosition();
 
            box.UpperLeftCorner = AbsoluteRect.UpperLeftCorner;
            box.LowerRightCorner = AbsoluteRect.UpperLeftCorner;
            box.UpperLeftCorner.X += 5;
            box.UpperLeftCorner.Y += 5;
            box.LowerRightCorner.X += 85;
            box.LowerRightCorner.Y += 85;
 
            gradient.UpperLeftCorner = AbsoluteRect.UpperLeftCorner;
            gradient.LowerRightCorner = AbsoluteRect.UpperLeftCorner;
            gradient.UpperLeftCorner.X += 90;
            gradient.UpperLeftCorner.Y += 5;
            gradient.LowerRightCorner.X += 105;
            gradient.LowerRightCorner.Y += 155;
 
            pickbox.UpperLeftCorner = AbsoluteRect.UpperLeftCorner;
            pickbox.LowerRightCorner = AbsoluteRect.UpperLeftCorner;
            pickbox.UpperLeftCorner.X += 5;
            pickbox.UpperLeftCorner.Y += 90;
            pickbox.LowerRightCorner.X += 85;
            pickbox.LowerRightCorner.Y += 120;
        }
        void CGUIColorPicker::draw()
        {
            Environment->getSkin()->draw3DSunkenPane(
                this, background,
                false, true,
                AbsoluteRect,
                &AbsoluteClippingRect
            );
 
            IGUIElement::draw();
 
            Environment->getVideoDriver()->draw2DImage(img[1], {
                AbsoluteRect.UpperLeftCorner.X+90,
                AbsoluteRect.UpperLeftCorner.Y+5
            });
 
            // 2 draw because the interpolation in the diagonal is not well rendered
            Environment->getVideoDriver()->draw2DRectangle(black, box, &AbsoluteClippingRect);
            Environment->getVideoDriver()->draw2DRectangle(box, white, color, alpha, alpha, &AbsoluteClippingRect);
 
            {
                const core::vector2di start =  {AbsoluteRect.UpperLeftCorner.X+90,  AbsoluteRect.UpperLeftCorner.Y+5+colorpos};
                const core::vector2di end =    {AbsoluteRect.UpperLeftCorner.X+105,  AbsoluteRect.UpperLeftCorner.Y+5+colorpos};
                const core::vector2di hstart = {box.UpperLeftCorner.X,  box.UpperLeftCorner.Y+pickpos.Y};
                const core::vector2di hend =   {box.LowerRightCorner.X, box.UpperLeftCorner.Y+pickpos.Y};
                const core::vector2di vstart = {box.UpperLeftCorner.X+pickpos.X, box.UpperLeftCorner.Y};
                const core::vector2di vend =   {box.UpperLeftCorner.X+pickpos.X, box.LowerRightCorner.Y};
 
                Environment->getVideoDriver()->draw2DLine({ start.X,    start.Y-1}, { end.X,    end.Y-1}, white);
                Environment->getVideoDriver()->draw2DLine({ start.X,    start.Y+1}, { end.X,    end.Y+1}, white);
                Environment->getVideoDriver()->draw2DLine({hstart.X,   hstart.Y-1}, {hend.X,   hend.Y-1}, white);
                Environment->getVideoDriver()->draw2DLine({hstart.X,   hstart.Y+1}, {hend.X,   hend.Y+1}, white);
                Environment->getVideoDriver()->draw2DLine({vstart.X-1,   vstart.Y}, {vend.X-1, vend.Y  }, white);
                Environment->getVideoDriver()->draw2DLine({vstart.X+1,   vstart.Y}, {vend.X+1, vend.Y  }, white);
 
                Environment->getVideoDriver()->draw2DLine(start,   end, black);
                Environment->getVideoDriver()->draw2DLine(hstart, hend, black);
                Environment->getVideoDriver()->draw2DLine(vstart, vend, black);
            }
 
            Environment->getVideoDriver()->draw2DImage(img[0], pickbox, pickbox);
            Environment->getVideoDriver()->draw2DRectangle(
                pickcolor, pickbox,
                &AbsoluteClippingRect
            );
        }
    }
}
in accordance with that of irrlicht EGET_FILE_SELECTED is return "take this color" is clicked

my original post at http://irrlicht-fr.org/viewtopic.php?pid=11560 ;)
Last edited by Ovan on Tue Dec 23, 2014 10:44 pm, edited 2 times in total.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: Color picker gui

Post by Seven »

Awesome!
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Color picker gui

Post by CuteAlien »

Hi, could you consider putting this code under the zlib-license? The current color-picker in the engine never worked, so I would be glad if we had a replacement. I haven't tested this yet, but looks nice from screenshots, so maybe we could replace the one in the engine.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Re: Color picker gui

Post by Ovan »

yes off course (I hoped this)
I edit my previous message to add the licence on the header (it's good?)
can need some minor rewrite i use brace-initialization of c++11 feature
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Color picker gui

Post by CuteAlien »

Thanks! I suppose header is good. Although in Irrlicht we tend to just mention that the license is beside the sources, I hope that's fine. I will also have to do some minor rewrites before adding it (Irrlicht style slightly different and maybe we can replace the define by some function or even have one for that already). But that's minor stuff. I'll try to find some time to add it in the new year (probably no time before that).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Re: Color picker gui

Post by Ovan »

Cool :)
you can add CGUIModalScreen or something similar when is created by IGUIEnvironment ?
make it as "popup" element if is add as modal, would be useful
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Color picker gui

Post by CuteAlien »

Yeah, old one also had a flag for that.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Color picker gui

Post by hendu »

OT: Who named the function getData, when it clearly *puts* data... :P
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Re: Color picker gui

Post by Ovan »

http://irrlicht.sourceforge.net/docu/cl ... f28be96bc9
no it does not put data it really get data from color to a ptr memory and here this memory is the texture at coord [x, y]

same if you write

irr::u32 i;
color.getData(&i, ...);
Last edited by Ovan on Tue Dec 23, 2014 3:12 pm, edited 1 time in total.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Color picker gui

Post by hendu »

No, it doesn't get anything. It writes the color value into the data pointer. Getting would mean "get the color data *from* this pointer".
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Color picker gui

Post by CuteAlien »

It's correct. Getter functions in classes are about getting the data from the class. The data pointer is the target. It doesn't matter if the target is an lvalue or a reference or a pointer into which to write the result - it's always a getter function. This way the name tells you some information which is not as easy visible on first view when looking at the function declaration.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Color picker gui

Post by hendu »

I still think it's poorly named. getDataInto would be far more understandable if putData is against the irr style. Has it been in a release yet?
ent1ty
Competition winner
Posts: 1106
Joined: Sun Nov 08, 2009 11:09 am

Re: Color picker gui

Post by ent1ty »

Looks like the RGBftoHSV function is from my snippet here. If it makes into the engine, acknowledgement would be nice :P
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps

Step back! I have a void pointer, and I'm not afraid to use it!
Ovan
Posts: 70
Joined: Thu Dec 18, 2008 12:41 am
Contact:

Re: Color picker gui

Post by Ovan »

yeah sorry to not mention your code, I forget and thanks ;)

and no hendu, this name is clearly the right name it's the same thing if you write
void getSquare(int x, int *result) { *result = x*x; }
void getSquare(int x, int &result) { result = x*x;}
int getSquare(int x) { return x*x; }
all 3 function do the same thing, (in old C style "getSquare(int x, int *result)" is highly used)
and in this case for SColor a ptr is needed because the size of the returned value can change (due to E_COLOR_FORMAT)

getter function mean you get from the object (here is color) not from the parameter, the parameter is clearly the returned value
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Color picker gui

Post by hendu »

Logically a C style function would be "int getSquare(int)", returning it. Having a random result pointer without a different name would be confusing.

Like in this case, a reader may read it as "get data *into* this SColor from this pointer" like I did. It is not clear without changing the name, because we have 99% of getFoo functions that return the value.

edit:

Its pair, setData, has the same confusion. It can be read either way, set data from this SColor to the pointer, or from the pointer to this SColor. setDataFrom and getDataInto would clear both up.
Post Reply