Skip to content

Commit

Permalink
Sprite and TextureCache from ax::Data (#1967) [skip ci]
Browse files Browse the repository at this point in the history
* Add Sprite and TextureCache way to use image from Data

* add Sprite cpp tests

* Apply suggestions from code review

---------

Co-authored-by: halx99 <[email protected]>
  • Loading branch information
AlexandreK38 and halx99 authored Jun 6, 2024
1 parent ec8cdf2 commit 79aabfd
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 0 deletions.
37 changes: 37 additions & 0 deletions core/2d/Sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ Sprite* Sprite::create(std::string_view filename, const Rect& rect)
return nullptr;
}

Sprite* Sprite::create(const Data& imageData, std::string_view key)
{
Sprite *sprite = new Sprite();
if (sprite->initWithImageData(imageData, key))
{
sprite->autorelease();
return sprite;
}
AX_SAFE_DELETE(sprite);
return nullptr;
}

Sprite* Sprite::createWithSpriteFrame(SpriteFrame* spriteFrame)
{
Sprite* sprite = new Sprite();
Expand Down Expand Up @@ -304,6 +316,31 @@ bool Sprite::initWithTexture(Texture2D* texture, const Rect& rect, bool rotated)
return result;
}

bool Sprite::initWithImageData(const Data& imageData, std::string_view key)
{
if (imageData.isNull() || key.empty())
{
AXLOG("Call Sprite::initWithImageData empty data or blank key.");
return false;
}

//_fileName = filename;

Texture2D *texture = _director->getTextureCache()->addImage(imageData, key);

if (texture)
{
Rect rect = Rect::ZERO;
rect.size = texture->getContentSize();
return initWithTexture(texture, rect);
}

// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return false;
}

Sprite::Sprite()
{
#if AX_SPRITE_DEBUG_DRAW
Expand Down
23 changes: 23 additions & 0 deletions core/2d/Sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ class AX_DLL Sprite : public Node, public TextureProtocol
*/
static Sprite* create(std::string_view filename, const Rect& rect);

/**
* Creates a sprite with an image data and an image key.
*
* @param imageData A Data of the image fil.
* @param key The unique key for the image in the cache.
* @return An autoreleased sprite object.
*/
static Sprite* create(const ax::Data& imageData, std::string_view key);

/**
* Creates a sprite with a Texture2D object.
*
Expand Down Expand Up @@ -618,6 +627,20 @@ class AX_DLL Sprite : public Node, public TextureProtocol
* @lua init
*/
virtual bool initWithFile(std::string_view filename, const Rect& rect);

/**
* Initializes a sprite with an image data, and a key for the cache.
*
* This method will load the image data to Texture2D,
* then use Texture2D to create a sprite.
* After initialization, the offset will be (0,0).
*
* @param imageData The image data
* @param key The key for cache.
* @return True if the sprite is initialized properly, false otherwise.
* @lua init
*/
virtual bool initWithImageData(const ax::Data& imageData, std::string_view key);

virtual void setVertexLayout();

Expand Down
52 changes: 52 additions & 0 deletions core/renderer/TextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,58 @@ Texture2D* TextureCache::addImage(Image* image, std::string_view key, PixelForma
return texture;
}

Texture2D* TextureCache::addImage(const Data& imageData, std::string_view key)
{
AXASSERT(!imageData.isNull() && !key.empty(), "TextureCache: imageData MUST not be empty and key not empty");

Texture2D * texture = nullptr;

do
{
auto it = _textures.find(key);
if (it != _textures.end()) {
texture = it->second;
break;
}

Image* image = new Image();
AX_BREAK_IF(nullptr == image);

bool bRet = image->initWithImageData(imageData.getBytes(), imageData.getSize());
AX_BREAK_IF(!bRet);

texture = new Texture2D();

if (texture)
{
if (texture->initWithImage(image))
{

#if CC_ENABLE_CACHE_TEXTURE_DATA
VolatileTextureMgr::addImage(texture, image);
#endif
_textures.emplace(key, texture);
}
else
{
AX_SAFE_RELEASE(texture);
texture = nullptr;
AXLOG("axmol: initWithImage failed!");
}
}
else
{
AXLOG("axmol: Allocating memory for Texture2D failed!");
}

AX_SAFE_RELEASE(image);

} while (0);


return texture;
}

bool TextureCache::reloadTexture(std::string_view fileName)
{
Texture2D* texture = nullptr;
Expand Down
9 changes: 9 additions & 0 deletions core/renderer/TextureCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ class AX_DLL TextureCache : public Object
Texture2D* addImage(Image* image, std::string_view key);
Texture2D* addImage(Image* image, std::string_view key, PixelFormat format);

/** Returns a Texture2D object given an Image.
* If the image was not previously loaded, it will create a new Texture2D object and it will return it.
* Otherwise it will return a reference of a previously loaded image.
* @param imageData The Data of the image to use
* @param key The "key" parameter will be used as the "key" for the cache.
* If "key" is nil, then a new texture will be created each time.
*/
Texture2D* addImage(const Data& imageData, std::string_view key);

/** Returns an already created texture. Returns nil if the texture doesn't exist.
@param key It's the related/absolute path of the file image.
@since v0.99.5
Expand Down
84 changes: 84 additions & 0 deletions tests/cpp-tests/Source/SpriteTest/SpriteTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ SpriteTests::SpriteTests()
ADD_TEST_CASE(SpriteSlice9Test9);
ADD_TEST_CASE(SpriteSlice9Test10);
ADD_TEST_CASE(Issue17119);
ADD_TEST_CASE(SpriteWithImageDataTest1);
ADD_TEST_CASE(SpriteWithImageDataTest2);
ADD_TEST_CASE(SpriteWithImageDataTest3);
};

//------------------------------------------------------------------
Expand Down Expand Up @@ -5891,3 +5894,84 @@ void Issue17119::update(float dt)
_s4->setFlippedX(!flipped);
}
}

//------------------------------------------------------------------
//
// SpriteWithImageDataTest1
//
//------------------------------------------------------------------

SpriteWithImageDataTest1::SpriteWithImageDataTest1()
{
Size s = Director::getInstance()->getVisibleSize();

ax::Data imageData = FileUtils::getInstance()->getDataFromFile("Images/grossini.png");

Sprite* sprite = Sprite::create(imageData, "sprite_image_key_test_1");
AXASSERT(sprite != nullptr, "Sprite with image data and key failed");
addChild(sprite);
sprite->setPosition(s.width / 2 - s.width / 3, s.height / 2);

Sprite* sprite2 = Sprite::create(imageData, "sprite_image_key_test_1");
AXASSERT(sprite2->getTexture() == sprite->getTexture(), "Sprite with image data same key failed");
addChild(sprite2);
sprite2->setPosition(s.width / 2 + s.width / 3, s.height / 2);
}

std::string SpriteWithImageDataTest1::title() const
{
return "Sprite with Image Data 1";
}

std::string SpriteWithImageDataTest1::subtitle() const
{
return "data given";
}

//------------------------------------------------------------------
//
// SpriteWithImageDataTest2
//
//------------------------------------------------------------------

SpriteWithImageDataTest2::SpriteWithImageDataTest2()
{
ax::Data emptyImageData;

Sprite* sprite = Sprite::create(emptyImageData, "sprite_image_key_test_2");
AXASSERT(sprite == nullptr, "Sprite with empty image data failed");
}

std::string SpriteWithImageDataTest2::title() const
{
return "Sprite with Image Data 2";
}

std::string SpriteWithImageDataTest2::subtitle() const
{
return "no sprite due to data empty";
}

//------------------------------------------------------------------
//
// SpriteWithImageDataTest3
//
//------------------------------------------------------------------

SpriteWithImageDataTest3::SpriteWithImageDataTest3()
{
ax::Data imageData = FileUtils::getInstance()->getDataFromFile("Images/grossini.png");

Sprite* sprite = Sprite::create(imageData, "");
AXASSERT(sprite == nullptr, "Sprite with empty image key failed");
}

std::string SpriteWithImageDataTest3::title() const
{
return "Sprite with Image Data 3";
}

std::string SpriteWithImageDataTest3::subtitle() const
{
return "no sprite due to empty key";
}
27 changes: 27 additions & 0 deletions tests/cpp-tests/Source/SpriteTest/SpriteTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,4 +946,31 @@ class Issue17119 : public SpriteTestDemo
ax::Sprite* _s4;
};

class SpriteWithImageDataTest1 : public SpriteTestDemo
{
public:
CREATE_FUNC(SpriteWithImageDataTest1);
SpriteWithImageDataTest1();
virtual std::string title() const override;
virtual std::string subtitle() const override;
};

class SpriteWithImageDataTest2 : public SpriteTestDemo
{
public:
CREATE_FUNC(SpriteWithImageDataTest2);
SpriteWithImageDataTest2();
virtual std::string title() const override;
virtual std::string subtitle() const override;
};

class SpriteWithImageDataTest3 : public SpriteTestDemo
{
public:
CREATE_FUNC(SpriteWithImageDataTest3);
SpriteWithImageDataTest3();
virtual std::string title() const override;
virtual std::string subtitle() const override;
};

#endif

0 comments on commit 79aabfd

Please sign in to comment.