Colouring the image with multiple color

I need to complete the image with filling colour. When we touch on any part of image like any leaf then clicked leaf should be field by colour. is it possible to achieve this using cocos2d-x CPP.

I would put the picture into Photoshop, cut each element into its own image and then use the setColor() function for whichever image is touched. But I am definitely interested to hear if someone else knows how to do it programmatically.

there is a way to do it programmatically.

But what @grimfate suggested is better way in many reason I think.

Let’s think about the shirt.
The shirt image is devided by broomstick and arm.
User need to touch 3 points on shirt to fill same color by Flood fill algorithm.

Does Cocos have a way to colour an image pixel-by-pixel efficiently?

Hi,
I need help for flood fill class for cocos2d-x in c++.
And i’m using Xcode for development.

Thanks

Hi,

I need to do the same thing… & i also know about flood fill algorithm, But i am not getting pixels…
Basically how to setPixel() & getPixel out of Texture & color it.

To do the Flood Fill:
Create a Texture2D from an Image, and:
Create the setPixel and getPixel functions for this Image:
Working directly with the raw data (this is ultra-fast and efficient).

HI,

I Understood & implemented this. But i am unable to get the exact result. Can someone point out the mistake i am doing.
Here is the code & i am also attaching the classes & Resources folder.

// on “init” you need to initialize your instance
bool HelloWorld::init()
{Archive.zip (533.0 KB)

    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    registerTouch();
    
    auto bg = Sprite::create("SCN_bg.png");
    bg->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
    this->addChild(bg);
    
    std::string filename = "SCN_Clear_apple.png";
    img = new Image();
    img->initWithImageFile(filename);
    
    CCLOG("%s loaded. Size: %dx%d. Bits per pixel: %d", filename.c_str(), img->getWidth(), img->getHeight(), img->getBitPerPixel());
    
    if(img->hasAlpha())
        rgbProfile = 4;
    else
        rgbProfile = 3;
    
    //data = new unsigned char[img->getDataLen() * rgbProfile];
    data = img->getData(); //pixel data
    
    width = img->getWidth();
    height = img->getHeight();
    
    log("Data Length %zd ", img->getDataLen());
    
    //memset(data + img->getDataLen() / 4, 255, img->getDataLen() / 4); //white rectangle
    //setColor4B(83, 109, Color4B::RED, data); //single red pixel
    
    texture = new Texture2D();
    texture->initWithImage(img);
    
    sprite = Sprite::createWithTexture(texture);

    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    addChild(sprite);
    
    setColor4B(85, 109, Color4B::GREEN, data); //single green pixel
    //we have to update texture
    //texture->updateWithData(data, 0, 0, img->getWidth(), img->getHeight()); //here's how to update whole data
    texture->updateWithData(data, 85, 109, 1, 1); //you can also update only part of it to increase performance
    
    return true;
}

void HelloWorld::setColor4B(int x, int y, Color4B color, unsigned char* data)
{
    
            int index = (x + y * img->getWidth()) << 2; //the same as * 4
            data[index] = color.r;
            data[index + 1] = color.g;
            data[index + 2] = color.b;
            data[index + 3] = color.a;
    
//    for(int i = 0; i < width; ++i)
//    {
//        for(int j = 0; j < height; ++j)
//        {
//
//            
//            int index = (i + j * width) << 2; //the same as * 4
//            data[index] = color.r;
//            data[index + 1] = color.g;
//            data[index + 2] = color.b;
//            data[index + 3] = color.a;
//        }
//    }
}

void HelloWorld::FloodFillBorder(int cx, int cy, cocos2d::Color4B fillColor, cocos2d::Color4B borderColor)
{
    std::queue<Point> pixelPoints;
    
    int noOfPixels = img->getDataLen();
    
    std::vector<bool> vCheckedPixels(noOfPixels, false);
    
    pixelPoints.push(Point(cx, cy));
    
    while(pixelPoints.size() > 0)
    {
        Vec2 current = pixelPoints.back();
        pixelPoints.pop();
        
        for(int i = current.x; i < width; ++i)
        {
            int index = (i + (int)current.y * width) << 2;
            unsigned char * pixel = &data[index];
            // You can see/change pixels' RGBA value(0-255) here !
            Color4B tempValues; //  pixel value at this position
//            tempValues.r = *pixel;
//            tempValues.g = *(pixel + 1);
//            tempValues.b = *(pixel + 2);
//            tempValues.a = *(pixel + 3);
            
            tempValues.r = data[index];
            tempValues.g = data[index + 1];
            tempValues.b = data[index + 2];
            tempValues.a = data[index + 3];
            
            if( pixel == nullptr || tempValues == borderColor )
                break;
            
            data[index] = fillColor.r;
            data[index + 1] = fillColor.g;
            data[index + 2] = fillColor.b;
            data[index + 2] = fillColor.a;
            
            vCheckedPixels[i + ((int)current.y * width)] = true;
            
            texture->updateWithData(data, current.x, current.y, 1, 1);
            
            if(current.y + 1 < height)
            {
                int topIndex =  (i + ((int)current.y * width + width)) << 2;
                //unsigned char topPixel = data[topIndex];
                Color4B topPixelColor;
                topPixelColor.r = data[topIndex];
                topPixelColor.g = data[topIndex + 1];
                topPixelColor.b = data[topIndex + 2];
                topPixelColor.a = data[topIndex + 3];
                
                if(vCheckedPixels[i + ((int)current.y * width) + width] == false && topPixelColor != borderColor)
                {
                    pixelPoints.push(Point(i, current.y + 1));
                    //pixelPoints.push(Point(i, topIndex));
                }
            }
            if(current.y - 1 >= 0)
            {
                int bottomIndex =  (i + ((int)current.y * width - width)) << 2;
                //unsigned char topPixel = data[topIndex];
                Color4B bottomPixelColor;
                bottomPixelColor.r = data[bottomIndex];
                bottomPixelColor.g = data[bottomIndex + 1];
                bottomPixelColor.b = data[bottomIndex + 2];
                bottomPixelColor.a = data[bottomIndex + 3];
                
                if(vCheckedPixels[i + ((int)current.y * width) - width] == false && bottomPixelColor != borderColor)
                {
                    pixelPoints.push(Point(i, current.y + 1));
                    //pixelPoints.push(Point(i, bottomIndex));
                }
            }
        }
        
        for(int i = current.x - 1; i >= 0; --i)
        {
            int index = (i + ((int)current.y * width)) << 2;
            unsigned char * pixel = &data[index];
            // You can see/change pixels' RGBA value(0-255) here !
            Color4B tempValues; //  pixel value at this position
            tempValues.r = data[index];
            tempValues.g = data[index + 1];
            tempValues.b = data[index + 2];
            tempValues.a = data[index + 3];
            
            if( pixel == nullptr || tempValues == borderColor )
                break;
            
            data[index] = fillColor.r;
            data[index + 1] = fillColor.g;
            data[index + 2] = fillColor.b;
            data[index + 3] = fillColor.a;
            
            texture->updateWithData(data, current.x, current.y, 1, 1);
            
            vCheckedPixels[i + ((int)current.y * width)] = true;
            
            if(current.y + 1 < height)
            {
                int topIndex =  (i + ((int)current.y * width) + width) << 2;
                //unsigned char topPixel = data[topIndex];
                Color4B topPixelColor;
                topPixelColor.r = data[topIndex];
                topPixelColor.g = data[topIndex + 1];
                topPixelColor.b = data[topIndex + 2];
                topPixelColor.b = data[topIndex + 3];
                
                if(vCheckedPixels[i + ((int)current.y * width) + width] == false && topPixelColor != borderColor)
                {
                    pixelPoints.push(Point(i, current.y + 1));
                    //pixelPoints.push(Point(i, topIndex));
                }
            }
            if(current.y - 1 >= 0)
            {
                int bottomIndex =  (i + ((int)current.y * width - width)) << 2;
                //unsigned char topPixel = data[topIndex];
                Color4B bottomPixelColor;
                bottomPixelColor.r = data[bottomIndex];
                bottomPixelColor.g = data[bottomIndex + 1];
                bottomPixelColor.b = data[bottomIndex + 2];
                bottomPixelColor.a = data[bottomIndex + 3];
                
                if(vCheckedPixels[i + ((int)current.y * width) - width] == false && bottomPixelColor != borderColor)
                {
                    pixelPoints.push(Point(i, current.y + 1));
                    //pixelPoints.push(Point(i, bottomIndex));
                }
            }
        }
        
        //texture->updateWithData(data, current.x, current.y, 1, 1);
    }
    
    //texture->updateWithData(data, 0, 0, width, height);
}

void HelloWorld::registerTouch()
{
    auto touchListener = EventListenerTouchOneByOne::create();
    touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);
    
    auto keyListener = EventListenerKeyboard::create();
    keyListener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this);
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(keyListener, this);
}

bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event)
{
    auto touchLocation = touch->getLocation();
    touchLocation = this->convertToNodeSpace(touchLocation);
    
    if(sprite->getBoundingBox().containsPoint(touchLocation))
    {
        int actualTexCoordinateX;
        int actualTexCoordinateY;
        
        int paddingX, paddingY;
        
        paddingX = sprite->getPosition().x - width/2;
        paddingY = sprite->getPosition().y - height/2;
        
        actualTexCoordinateX = touchLocation.x - paddingX;
        actualTexCoordinateY = touchLocation.y - paddingY;
        
        FloodFillBorder(actualTexCoordinateX, actualTexCoordinateY, Color4B::GREEN, Color4B::BLACK);
    }
    
    return true;
    
}
1 Like