2013-06-06 12:02:54 +08:00
|
|
|
//
|
2013-06-20 14:15:53 +08:00
|
|
|
// Texture2DMutable.cpp
|
2013-06-06 12:02:54 +08:00
|
|
|
// Ported to C++ by Dmitry Matyukhin
|
|
|
|
//
|
2013-06-20 14:15:53 +08:00
|
|
|
// MutableTexture.m
|
2013-06-06 12:02:54 +08:00
|
|
|
// Created by Lam Hoang Pham.
|
|
|
|
// Improved by Manuel Martinez-Almeida.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#include "CCTexture2DMutable.h"
|
|
|
|
|
|
|
|
|
|
|
|
using namespace cocos2d;
|
|
|
|
|
|
|
|
#if CC_MUTABLE_TEXTURE_SAVE_ORIGINAL_DATA
|
2013-06-20 14:15:53 +08:00
|
|
|
void* Texture2DMutable::getOriginalTexData() {
|
2013-06-06 12:02:54 +08:00
|
|
|
return originalData_;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void* Texture2DMutable::getTexData() {
|
2013-06-06 12:02:54 +08:00
|
|
|
return data_;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void Texture2DMutable::setTexData(void *var) {
|
2013-06-06 12:02:54 +08:00
|
|
|
data_ = var;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void Texture2DMutable::releaseData(void* data)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
//Don't free the data
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void* Texture2DMutable::keepData(void* data, unsigned int lenght)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
void *newData = malloc(lenght);
|
|
|
|
memmove(newData, data, lenght);
|
|
|
|
return newData;
|
|
|
|
}
|
|
|
|
|
2013-07-25 19:52:44 +08:00
|
|
|
bool Texture2DMutable::initWithImageFile(const char *imageFile, cocos2d::Texture2D::PixelFormat pixelFormat, unsigned int pixelsWide, unsigned int pixelsHigh, const cocos2d::Size& contentSize)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-20 14:15:53 +08:00
|
|
|
image_ = new cocos2d::Image();
|
2013-06-06 12:02:54 +08:00
|
|
|
image_->initWithImageFile(imageFile);
|
|
|
|
|
|
|
|
|
|
|
|
return initWithData(image_->getData(), pixelFormat, pixelsWide, pixelsHigh, contentSize);
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
bool Texture2DMutable::initWithImageFile(const char *imageFile)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-20 14:15:53 +08:00
|
|
|
image_ = new cocos2d::Image();
|
2013-06-06 12:02:54 +08:00
|
|
|
image_->initWithImageFile(imageFile);
|
|
|
|
|
|
|
|
bool hasAlpha = image_->hasAlpha();
|
2013-07-12 14:30:26 +08:00
|
|
|
Size imageSize = Size((float)(image_->getWidth()), (float)(image_->getHeight()));
|
2013-06-06 12:02:54 +08:00
|
|
|
size_t bpp = image_->getBitsPerComponent();
|
2013-07-25 19:52:44 +08:00
|
|
|
cocos2d::Texture2D::PixelFormat pixelFormat;
|
2013-06-06 12:02:54 +08:00
|
|
|
|
|
|
|
// compute pixel format
|
|
|
|
if(hasAlpha)
|
|
|
|
{
|
2013-07-26 04:36:19 +08:00
|
|
|
pixelFormat = Texture2D::PixelFormat::DEFAULT;
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (bpp >= 8)
|
|
|
|
{
|
2013-07-26 04:36:19 +08:00
|
|
|
pixelFormat = Texture2D::PixelFormat::RGB888;
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-07-26 04:36:19 +08:00
|
|
|
pixelFormat = Texture2D::PixelFormat::RGB565;
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return initWithData(image_->getData(), pixelFormat, imageSize.width, imageSize.height, imageSize);
|
|
|
|
}
|
|
|
|
|
2013-07-25 19:52:44 +08:00
|
|
|
bool Texture2DMutable::initWithData(const void* data, Texture2D::PixelFormat pixelFormat, unsigned int width, unsigned int height, const Size& size)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-20 14:15:53 +08:00
|
|
|
if(!Texture2D::initWithData(data, pixelFormat, width, height, size)) {
|
2013-06-06 12:02:54 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pixelFormat) {
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::RGBA8888: bytesPerPixel_ = 4; break;
|
|
|
|
case Texture2D::PixelFormat::A8: bytesPerPixel_ = 1; break;
|
|
|
|
case Texture2D::PixelFormat::RGBA4444:
|
|
|
|
case Texture2D::PixelFormat::RGB565:
|
|
|
|
case Texture2D::PixelFormat::RGB5A1:
|
2013-06-06 12:02:54 +08:00
|
|
|
bytesPerPixel_ = 2;
|
|
|
|
break;
|
|
|
|
default:break;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_ = (void*) data;
|
|
|
|
|
|
|
|
#if CC_MUTABLE_TEXTURE_SAVE_ORIGINAL_DATA
|
|
|
|
unsigned int max = width * height * bytesPerPixel_;
|
|
|
|
originalData_ = malloc(max);
|
|
|
|
memcpy(originalData_, data_, max);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-05 16:49:22 +08:00
|
|
|
Color4B Texture2DMutable::pixelAt(const Point& pt)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
|
2013-07-07 21:08:14 +08:00
|
|
|
Color4B c(0, 0, 0, 0);
|
2013-06-06 12:02:54 +08:00
|
|
|
if(!data_) return c;
|
|
|
|
if(pt.x < 0 || pt.y < 0) return c;
|
2013-06-15 14:03:30 +08:00
|
|
|
if(pt.x >= _contentSize.width || pt.y >= _contentSize.height) return c;
|
2013-06-06 12:02:54 +08:00
|
|
|
|
|
|
|
//! modified, texture origin point is left top, cocos2d origin point is left bottom
|
|
|
|
//! unsigned int x = pt.x, y = pt.y
|
2013-06-15 14:03:30 +08:00
|
|
|
unsigned int x = pt.x, y = _pixelsHigh - pt.y;
|
2013-06-06 12:02:54 +08:00
|
|
|
|
2013-07-26 04:36:19 +08:00
|
|
|
if(_pixelFormat == Texture2D::PixelFormat::RGBA8888){
|
2013-06-06 12:02:54 +08:00
|
|
|
unsigned int *pixel = (unsigned int *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
c.r = *pixel & 0xff;
|
|
|
|
c.g = (*pixel >> 8) & 0xff;
|
|
|
|
c.b = (*pixel >> 16) & 0xff;
|
|
|
|
c.a = (*pixel >> 24) & 0xff;
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGBA4444){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
c.a = ((*pixel & 0xf) << 4) | (*pixel & 0xf);
|
|
|
|
c.b = (((*pixel >> 4) & 0xf) << 4) | ((*pixel >> 4) & 0xf);
|
|
|
|
c.g = (((*pixel >> 8) & 0xf) << 4) | ((*pixel >> 8) & 0xf);
|
|
|
|
c.r = (((*pixel >> 12) & 0xf) << 4) | ((*pixel >> 12) & 0xf);
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGB5A1){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
c.r = ((*pixel >> 11) & 0x1f)<<3;
|
|
|
|
c.g = ((*pixel >> 6) & 0x1f)<<3;
|
|
|
|
c.b = ((*pixel >> 1) & 0x1f)<<3;
|
|
|
|
c.a = (*pixel & 0x1)*255;
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGB565){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
c.b = (*pixel & 0x1f)<<3;
|
|
|
|
c.g = ((*pixel >> 5) & 0x3f)<<2;
|
|
|
|
c.r = ((*pixel >> 11) & 0x1f)<<3;
|
|
|
|
c.a = 255;
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::A8){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLubyte *pixel = (GLubyte *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
c.a = pixel[(y * _pixelsWide) + x];
|
2013-06-06 12:02:54 +08:00
|
|
|
// Default white
|
|
|
|
c.r = 255;
|
|
|
|
c.g = 255;
|
|
|
|
c.b = 255;
|
|
|
|
}
|
|
|
|
|
2013-07-24 06:20:22 +08:00
|
|
|
//log("color : %i, %i, %i, %i", c.r, c.g, c.b, c.a);
|
2013-06-06 12:02:54 +08:00
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2013-07-05 16:49:22 +08:00
|
|
|
bool Texture2DMutable::setPixelAt(const Point& pt, Color4B c)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
if(!data_)return false;
|
|
|
|
if(pt.x < 0 || pt.y < 0) return false;
|
2013-06-15 14:03:30 +08:00
|
|
|
if(pt.x >= _contentSize.width || pt.y >= _contentSize.height) return false;
|
2013-06-06 12:02:54 +08:00
|
|
|
unsigned int x = pt.x, y = pt.y;
|
|
|
|
|
|
|
|
dirty_ = true;
|
|
|
|
|
|
|
|
// Shifted bit placement based on little-endian, let's make this more
|
|
|
|
// portable =/
|
|
|
|
|
2013-07-26 04:36:19 +08:00
|
|
|
if(_pixelFormat == Texture2D::PixelFormat::RGBA8888){
|
2013-06-06 12:02:54 +08:00
|
|
|
unsigned int *pixel = (unsigned int *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel[(y * _pixelsWide) + x] = (c.a << 24) | (c.b << 16) | (c.g << 8) | c.r;
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGBA4444){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
*pixel = ((c.r >> 4) << 12) | ((c.g >> 4) << 8) | ((c.b >> 4) << 4) | (c.a >> 4);
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGB5A1){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
*pixel = ((c.r >> 3) << 11) | ((c.g >> 3) << 6) | ((c.b >> 3) << 1) | (c.a > 0);
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::RGB565){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLushort *pixel = (GLushort *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel = pixel + (y * _pixelsWide) + x;
|
2013-06-06 12:02:54 +08:00
|
|
|
*pixel = ((c.r >> 3) << 11) | ((c.g >> 2) << 5) | (c.b >> 3);
|
2013-07-26 04:36:19 +08:00
|
|
|
} else if(_pixelFormat == Texture2D::PixelFormat::A8){
|
2013-06-06 12:02:54 +08:00
|
|
|
GLubyte *pixel = (GLubyte *)data_;
|
2013-06-15 14:03:30 +08:00
|
|
|
pixel[(y * _pixelsWide) + x] = c.a;
|
2013-06-06 12:02:54 +08:00
|
|
|
} else {
|
|
|
|
dirty_ = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-05 16:49:22 +08:00
|
|
|
void Texture2DMutable::fill(Color4B p)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-15 14:03:30 +08:00
|
|
|
for(int r = 0; r < _contentSize.height; ++r)
|
|
|
|
for(int c = 0; c < _contentSize.width; ++c)
|
2013-07-12 14:30:26 +08:00
|
|
|
this->setPixelAt(Point(c, r), p);
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
Texture2D* Texture2DMutable::copyMutable(bool isMutable )
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-20 14:15:53 +08:00
|
|
|
Texture2D* co;
|
2013-06-06 12:02:54 +08:00
|
|
|
if(isMutable)
|
|
|
|
{
|
2013-06-15 14:03:30 +08:00
|
|
|
int mem = _pixelsWide*_pixelsHigh*bytesPerPixel_;
|
2013-06-06 12:02:54 +08:00
|
|
|
void *newData = malloc(mem);
|
|
|
|
memcpy(newData, data_, mem);
|
2013-06-20 14:15:53 +08:00
|
|
|
co = new Texture2DMutable();
|
2013-06-15 14:03:30 +08:00
|
|
|
if (!co->initWithData(newData, _pixelFormat, _pixelsWide, _pixelsHigh, _contentSize)) {
|
2013-06-06 12:02:54 +08:00
|
|
|
delete co;
|
|
|
|
co = NULL;
|
|
|
|
}
|
|
|
|
}else {
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
co = new Texture2D();
|
2013-06-15 14:03:30 +08:00
|
|
|
if (!co->initWithData(data_, _pixelFormat, _pixelsWide, _pixelsHigh, _contentSize)) {
|
2013-06-06 12:02:54 +08:00
|
|
|
delete co;
|
|
|
|
co = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return co;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
Texture2DMutable* Texture2DMutable::copy()
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-20 14:15:53 +08:00
|
|
|
return (Texture2DMutable*)this->copyMutable( true );
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void Texture2DMutable::copy(Texture2DMutable* textureToCopy, const Point& offset)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-06-15 14:03:30 +08:00
|
|
|
for(int r = 0; r < _contentSize.height;++r){
|
|
|
|
for(int c = 0; c < _contentSize.width; ++c){
|
2013-07-12 14:30:26 +08:00
|
|
|
setPixelAt(Point(c + offset.x, r + offset.y), textureToCopy->pixelAt(Point(c, r)));
|
2013-06-06 12:02:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void Texture2DMutable::restore()
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
#if CC_MUTABLE_TEXTURE_SAVE_ORIGINAL_DATA
|
2013-06-15 14:03:30 +08:00
|
|
|
memcpy(data_, originalData_, bytesPerPixel_*_pixelsWide*_pixelsHigh);
|
2013-06-06 12:02:54 +08:00
|
|
|
this->apply();
|
|
|
|
#else
|
2013-06-20 14:15:53 +08:00
|
|
|
//You should set CC_MUTABLE_TEXTURE_SAVE_ORIGINAL_DATA 1 in Texture2DMutable.h
|
2013-07-20 13:01:27 +08:00
|
|
|
CCASSERT(false, "Exception: MutableTexture.restore was disabled by the user.");
|
2013-06-06 12:02:54 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void Texture2DMutable::apply()
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
if(!data_) return;
|
|
|
|
|
2013-06-15 14:03:30 +08:00
|
|
|
glBindTexture(GL_TEXTURE_2D, _name);
|
2013-06-06 12:02:54 +08:00
|
|
|
|
2013-06-15 14:03:30 +08:00
|
|
|
switch(_pixelFormat)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::RGBA8888:
|
2013-06-15 14:03:30 +08:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pixelsWide, _pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_);
|
2013-06-06 12:02:54 +08:00
|
|
|
break;
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::RGBA4444:
|
2013-06-15 14:03:30 +08:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pixelsWide, _pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data_);
|
2013-06-06 12:02:54 +08:00
|
|
|
break;
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::RGB5A1:
|
2013-06-15 14:03:30 +08:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pixelsWide, _pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data_);
|
2013-06-06 12:02:54 +08:00
|
|
|
break;
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::RGB565:
|
2013-06-15 14:03:30 +08:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _pixelsWide, _pixelsHigh, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data_);
|
2013-06-06 12:02:54 +08:00
|
|
|
break;
|
2013-07-26 04:36:19 +08:00
|
|
|
case Texture2D::PixelFormat::A8:
|
2013-06-15 14:03:30 +08:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, _pixelsWide, _pixelsHigh, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data_);
|
2013-06-06 12:02:54 +08:00
|
|
|
break;
|
|
|
|
default:
|
2013-07-20 13:01:27 +08:00
|
|
|
CCASSERT(false, "NSInternalInconsistencyException");
|
2013-06-06 12:02:54 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
dirty_ = false;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
void *Texture2DMutable::getData()
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
return data_;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
Texture2DMutable::Texture2DMutable(void)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
image_ = NULL;
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:15:53 +08:00
|
|
|
Texture2DMutable::~Texture2DMutable(void)
|
2013-06-06 12:02:54 +08:00
|
|
|
{
|
|
|
|
CCLOGINFO("cocos2d: deallocing %p", this);
|
|
|
|
|
|
|
|
CC_SAFE_DELETE(image_);
|
|
|
|
|
|
|
|
free(data_);
|
|
|
|
#if CC_MUTABLE_TEXTURE_SAVE_ORIGINAL_DATA
|
|
|
|
free(originalData_);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|