ccrendertexture saveToFile的时候,么有办法携带图片的alpha信息
主要是在制作将plist解压成小图片的时候,savetofile的时候,无法将小图片的png图片带上,这个主要的原因是 texture的begin和end之间,buffer中的颜色会被进行premultialpha
所以有人单独做了一个补丁,制作继承自ccrendertexutre的文件
cpp:
#include "RenderTextureAlpha.h"
RenderTextureAlpha * RenderTextureAlpha::create(int w, int h, Texture2D::PixelFormat eFormat)
{
RenderTextureAlpha *ret = new (std::nothrow) RenderTextureAlpha();
if(ret && ret->initWithWidthAndHeight(w, h, eFormat))
{
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
bool RenderTextureAlpha::saveToFile(const std::string& fileName, bool isRGBA, std::function<void (RenderTexture*, const std::string&)> callback)
{
std::string basename(fileName);
std::transform(basename.begin(), basename.end(), basename.begin(), ::tolower);
Image::Format format;
if (basename.find(".png") != std::string::npos)
{
format = Image::Format::PNG;
} else if (basename.find(".jpg") != std::string::npos)
{
if (isRGBA) CCLOG("RGBA is not supported for JPG format.");
format = Image::Format::JPG;
}
_saveFileCallback = callback;
std::string fullpath = FileUtils::getInstance()->getWritablePath() + fileName;
_saveToFileCommand.init(_globalZOrder);
_saveToFileCommand.func = CC_CALLBACK_0(RenderTextureAlpha::onSaveToFile, this, fullpath, isRGBA);
Director::getInstance()->getRenderer()->addCommand(&_saveToFileCommand);
return true;
}
void RenderTextureAlpha::onSaveToFile(const std::string& filename, bool isRGBA)
{
Image *image = newImage(true);
if (image)
{
image->saveToFile(filename.c_str(), !isRGBA);
}
if(_saveFileCallback)
{
_saveFileCallback(this, filename);
}
CC_SAFE_DELETE(image);
}
/* get buffer as Image */
Image* RenderTextureAlpha::newImage(bool fliimage)
{
CCASSERT(_pixelFormat == Texture2D::PixelFormat::RGBA8888, "only RGBA8888 can be saved as image");
if (nullptr == _texture)
{
return nullptr;
}
const Size& s = _texture->getContentSizeInPixels();
// to get the image size to save
// if the saving image domain exceeds the buffer texture domain,
// it should be cut
int savedBufferWidth = (int)s.width;
int savedBufferHeight = (int)s.height;
GLubyte *buffer = nullptr;
GLubyte *tempData = nullptr;
Image *image = new (std::nothrow) Image();
do
{
CC_BREAK_IF(! (buffer = new (std::nothrow) GLubyte[savedBufferWidth * savedBufferHeight * 4]));
if(! (tempData = new (std::nothrow) GLubyte[savedBufferWidth * savedBufferHeight * 4]))
{
delete[] buffer;
buffer = nullptr;
break;
}
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_oldFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
// TODO: move this to configration, so we don't check it every time
/* Certain Qualcomm Andreno gpu's will retain data in memory after a frame buffer switch which corrupts the render to the texture. The solution is to clear the frame buffer before rendering to the texture. However, calling glClear has the unintended result of clearing the current texture. Create a temporary texture to overcome this. At the end of RenderTexture::begin(), switch the attached texture to the second one, call glClear, and then switch back to the original texture. This solution is unnecessary for other devices as they don't have the same issue with switching frame buffers.
*/
if (Configuration::getInstance()->checkForGLExtension("GL_QCOM"))
{
// -- bind a temporary texture so we can clear the render buffer without losing our texture
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureCopy->getName(), 0);
CHECK_GL_ERROR_DEBUG();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getName(), 0);
}
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0,0,savedBufferWidth, savedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, tempData);
glBindFramebuffer(GL_FRAMEBUFFER, _oldFBO);
// FIX BEGIN
for (int i = 0; i < savedBufferHeight; ++i)
{
for (int j = 0; j < savedBufferWidth; ++j)
{
GLfloat Rs = (GLfloat)(tempData[(i * savedBufferWidth + j) * 4]) / 255.0;
GLfloat Gs = (GLfloat)(tempData[(i * savedBufferWidth + j) * 4 + 1]) / 255.0;
GLfloat Bs = (GLfloat)(tempData[(i * savedBufferWidth + j) * 4 + 2]) / 255.0;
GLfloat As = (GLfloat)(tempData[(i * savedBufferWidth + j) * 4 + 3]) / 255.0;
const float backgroundAlpha = 0.f;
GLfloat Rd = Rs/As + (backgroundAlpha * (1-As));
GLfloat Gd = Gs/As + (backgroundAlpha * (1-As));
GLfloat Bd = Bs/As + (backgroundAlpha * (1-As));
GLubyte nRd = (GLubyte)(Rd * 255.0);
GLubyte nGd = (GLubyte)(Gd * 255.0);
GLubyte nBd = (GLubyte)(Bd * 255.0);
tempData[(i * savedBufferWidth + j) * 4] = nRd;
tempData[(i * savedBufferWidth + j) * 4 + 1] = nGd;
tempData[(i * savedBufferWidth + j) * 4 + 2] = nBd;
}
}
// FIX END
if ( fliimage ) // -- flip is only required when saving image to file
{
// to get the actual texture data
// #640 the image read from rendertexture is dirty
for (int i = 0; i < savedBufferHeight; ++i)
{
memcpy(&buffer[i * savedBufferWidth * 4],
&tempData[(savedBufferHeight - i - 1) * savedBufferWidth * 4],
savedBufferWidth * 4);
}
image->initWithRawData(buffer, savedBufferWidth * savedBufferHeight * 4, savedBufferWidth, savedBufferHeight, 8);
}
else
{
image->initWithRawData(tempData, savedBufferWidth * savedBufferHeight * 4, savedBufferWidth, savedBufferHeight, 8);
}
} while (0);
CC_SAFE_DELETE_ARRAY(buffer);
CC_SAFE_DELETE_ARRAY(tempData);
return image;
}
h:
#include "cocos2d.h"
USING_NS_CC;
class RenderTextureAlpha : public RenderTexture
{
public:
static RenderTextureAlpha*create(int w, int h, Texture2D::PixelFormat eFormat = Texture2D::PixelFormat::RGBA8888);
bool saveToFile(const std::string& fileName, bool isRGBA = true, std::function<void(RenderTexture*, const std::string&)> callback = nullptr);
void onSaveToFile(const std::string& filename, bool isRGBA);
Image* newImage(bool fliimage);
};

浙公网安备 33010602011771号