先看看结果对比,这是涂抹的一个效果
本文是基于同事培训给出的讲解。有点不太好讲,我给出关键代码 做下解释,若有不对的地方,请指正。
涂抹经历初始化的代码
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();
}
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);
/*更换图片*/
}
(2)更换图片较好理解,一般我们通过 setDisplayFrame()方法更换贴图。