先看看结果对比,这是涂抹的一个效果
本文是基于同事培训给出的讲解。有点不太好讲,我给出关键代码 做下解释,若有不对的地方,请指正。
涂抹经历初始化的代码
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<CCTexture2D *>(dynamic_cast<CCSprite *>(render->getSprite())->getTexture())->getPixelFormat() == 0), "only RGBA8888 can be saved as image"); paintspriteSize = dynamic_cast<CCSprite *>(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<CCString *>(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()方法更换贴图。