涂鸦 opengl简单应用1

先看看结果对比,这是涂抹的一个效果


   


 本文是基于同事培训给出的讲解。有点不太好讲,我给出关键代码 做下解释,若有不对的地方,请指正。


涂抹经历初始化的代码

if (CCNode::init()) {
        brushRadius = radius;
        
        locationsArray_ = CCArray::createWithCapacity(0);
        locationsArray_->retain();
        
    
        CCSprite *sprite = CCSprite::create(pszSpriteFileName);
        sprite->setBlendFunc((ccBlendFunc){GL_ONE,GL_ZERO});
        sprite->setAnchorPoint(ccp(0, 0));
        
        CCRenderTexture *render = CCRenderTexture::create(sprite->getTextureRect().size.width, sprite->getTextureRect().size.height);
        render->begin();
        sprite->visit();
        render->end();
        
        CCAssert((dynamic_cast(dynamic_cast(render->getSprite())->getTexture())->getPixelFormat() == 0), "only RGBA8888 can be saved as image");
        
        paintspriteSize = dynamic_cast(render->getSprite())->getTexture()->getContentSizeInPixels();
        
        int tx = paintspriteSize.width;
        int ty = paintspriteSize.height;
        

        int bytesPerRow			= 4 * tx;
        myDataLength			= bytesPerRow * ty;
        
        //buffer,根据已有像素信息初始化
        buffer_	= (GLubyte *)malloc(sizeof(GLubyte) * myDataLength);
        if( ! (buffer_) ) {
            CCLOG("cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
            free(buffer_);
        }
        render->begin();
        glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer_);
        render->end();
        
        //bufferCurrent,所有像素初始化值为0
        bufferCurrent_ = (GLubyte *)malloc(sizeof(GLubyte) * myDataLength);
        if( ! (bufferCurrent_) ) {
            CCLOG("cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
            free(bufferCurrent_);
        }
        
        int x,y;
        for(y = 0; y < ty; y++) {
            for(x = 0; x < tx; x++) {
                //RGBA
                bufferCurrent_[(y * 4 * tx + ((x * 4)+0))] = 0;
                bufferCurrent_[(y * 4 * tx + ((x * 4)+1))] = 0;
                bufferCurrent_[(y * 4 * tx + ((x * 4)+2))] = 0;
                bufferCurrent_[(y * 4 * tx + ((x * 4)+3))] = 0;
                
//                CCLog("(y * 4 * tx + ((x * 4)+0)=%d",(y * 4 * tx + ((x * 4)+0)));
            }
        }
        
        
        CCTexture2D *texture = new CCTexture2D;
        texture->initWithData(bufferCurrent_, kCCTexture2DPixelFormat_RGBA8888, tx, ty, CCSizeMake(tx, ty));
        texture->autorelease();
        sprite_ = CCSprite::createWithTexture(texture);
        sprite_->setFlipY(true);
        this->addChild(sprite_);
        
        
        return true;
    }


(1)这堆代码前半部分其实就是得到涂抹精灵的所有像素信息,存储在 buffer_ 中,数据类型为Opengl类型Glubyte , 存储的RGBA的值,它只是一个头指针而已,

 (2)后面从bufferCurrent_开始就是为创建一个指针,该指针指向一个和涂抹精灵一样大小的数据块。初始化RGBA(0,0,0,0),然后添加到界面上,这时候是看不到东西的

void glReadPixels( GLint x, GLint y, GLsizei width,GLsizei height, GLenum format, GLenum type, GLvoid *pixels) ;

前面几个参数指定读取的位置尺寸以及格式, 最后一个参数用来返回结果, 所以像素数据自然是读到pixels中。

2.每一帧调用代码

void PaintSprite::update(float dt)
{
    CCObject* str = NULL;
    CCARRAY_FOREACH(locationsArray_, str)
    {
        this->paintingWithLocation(CCPointFromString(dynamic_cast(str)->getCString()));
    
    }
    locationsArray_->removeAllObjects();

}

ccobject的方法,每一帧都会调用,当然你必须是live状态。


3.涂抹实现代码

void PaintSprite::paintingWithLocation(cocos2d::CCPoint location)
{
    if (sprite_) {
        location = sprite_->convertToNodeSpace(location);
    }
    
    //CCLog("%f--%f",location.x,location.y);
    
    int x,y;
    int tx = paintspriteSize.width;
    int ty = paintspriteSize.height;
    
    //图片高度为tx,ty,但是在数组中从0开始,因此上限为tx-1,ty-1;
    if (location.x < 0 || location.x > tx - 1 || location.y < 0 || location.y >ty - 1) {
        return;
    }
    //边界处理
    int yStart = location.y - brushRadius;
    if (yStart < 0) {
        yStart = 0;
    }
    int yEnd = location.y + brushRadius;
    if (yEnd >= ty) {
        yEnd = ty-1;
    }
    int xStart = location.x - brushRadius;
    if (xStart < 0) {
        xStart = 0;
    }
    int xEnd = location.x + brushRadius;
    if (xEnd >= tx) {
        xEnd = tx-1;
    }
    //将涂抹的点确定为一个圆(如果不做限制将会是长方形)
    for(y = yStart; y < yEnd; y+=1) {
        // just want the last byte (alpha) for each pixel
		for(x = xStart; x < xEnd; x+=1) {
        
            if (ccpDistance(ccp(x, y), location) <= brushRadius) {
                bufferCurrent_[(y * 4 * tx + ((x * 4)+0))] = buffer_[(y * 4 * tx + ((x * 4)+0))];
                bufferCurrent_[(y * 4 * tx + ((x * 4)+1))] = buffer_[(y * 4 * tx + ((x * 4)+1))];
                bufferCurrent_[(y * 4 * tx + ((x * 4)+2))] = buffer_[(y * 4 * tx + ((x * 4)+2))];
                bufferCurrent_[(y * 4 * tx + ((x * 4)+3))] = buffer_[(y * 4 * tx + ((x * 4)+3))];
            }
		}
	}
    
    /*更换图片*/
    CCTexture2D *texture = new CCTexture2D;
    texture->initWithData(bufferCurrent_, kCCTexture2DPixelFormat_RGBA8888, tx, ty, CCSizeMake(tx, ty));
    texture->autorelease();
    CCSpriteFrame *spriteFrame = new CCSpriteFrame;
    spriteFrame->initWithTexture(texture, CCRectMake(0, 0, sprite_->getContentSize().width, sprite_->getContentSize().height));
    spriteFrame->autorelease();
    sprite_->setDisplayFrame(spriteFrame);
    /*更换图片*/

}

(1)像素值赋值是最为关键的, 用户点击了屏幕, 程序获取到这个点,从buffer_中得到该点的像素值 ,并赋给bufferCyrrent_

 (2)更换图片较好理解,一般我们通过 setDisplayFrame()方法更换贴图。


整个过程其实就在用户触摸屏幕的时候,不断更新bufferCyrrent_,可以叫它缓冲帧,而更新的内容则来自 涂抹精灵本身图片的所有像素buffer中。然后更新贴图实现涂抹功能。这种涂抹方式相对较低,下一节会一种更好的方式。


你可能感兴趣的:(cocos2d-x,涂鸦,cocos2d,opengl,涂抹)