lib: Added a raw data API (#23)

* raw data API

* add missing type arg

* tests: better stuff
This commit is contained in:
Vaxry 2024-04-06 21:16:55 +01:00 committed by GitHub
parent 981b661782
commit 7561459770
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 337 additions and 47 deletions

View File

@ -65,12 +65,17 @@ target_link_libraries(hyprcursor-util PkgConfig::deps hyprcursor)
# tests # tests
add_custom_target(tests) add_custom_target(tests)
add_executable(hyprcursor_test "tests/test.cpp") add_executable(hyprcursor_test1 "tests/full_rendering.cpp")
target_link_libraries(hyprcursor_test PRIVATE hyprcursor) target_link_libraries(hyprcursor_test1 PRIVATE hyprcursor)
add_test(NAME "Test libhyprcursor in C++" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test) add_test(NAME "Test libhyprcursor in C++ (full rendering)" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test1)
add_dependencies(tests hyprcursor_test) add_dependencies(tests hyprcursor_test1)
add_executable(hyprcursor_test_c "tests/test.c") add_executable(hyprcursor_test2 "tests/only_metadata.cpp")
target_link_libraries(hyprcursor_test2 PRIVATE hyprcursor)
add_test(NAME "Test libhyprcursor in C++ (only metadata)" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test2)
add_dependencies(tests hyprcursor_test2)
add_executable(hyprcursor_test_c "tests/c_test.c")
target_link_libraries(hyprcursor_test_c PRIVATE hyprcursor) target_link_libraries(hyprcursor_test_c PRIVATE hyprcursor)
add_test(NAME "Test libhyprcursor in C" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test_c) add_test(NAME "Test libhyprcursor in C" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test_c)
add_dependencies(tests hyprcursor_test_c) add_dependencies(tests hyprcursor_test_c)

View File

@ -15,7 +15,7 @@ enum eOperation {
OPERATION_EXTRACT = 1, OPERATION_EXTRACT = 1,
}; };
eResizeAlgo explicitResizeAlgo = RESIZE_INVALID; eHyprcursorResizeAlgo explicitResizeAlgo = HC_RESIZE_INVALID;
struct XCursorConfigEntry { struct XCursorConfigEntry {
int size = 0, hotspotX = 0, hotspotY = 0, delay = 0; int size = 0, hotspotX = 0, hotspotY = 0, delay = 0;
@ -329,7 +329,7 @@ static std::optional<std::string> extractXTheme(const std::string& xpath_, const
} }
// write a meta.hl // write a meta.hl
std::string metaString = std::format("resize_algorithm = {}\n", explicitResizeAlgo == RESIZE_INVALID ? "none" : algoToString(explicitResizeAlgo)); std::string metaString = std::format("resize_algorithm = {}\n", explicitResizeAlgo == HC_RESIZE_INVALID ? "none" : algoToString(explicitResizeAlgo));
// find hotspot from first entry // find hotspot from first entry
metaString += metaString +=

View File

@ -102,4 +102,21 @@ CAPI void hyprcursor_style_done(struct hyprcursor_manager_t* manager, struct hyp
*/ */
CAPI void hyprcursor_register_logging_function(struct hyprcursor_manager_t* manager, PHYPRCURSORLOGFUNC fn); CAPI void hyprcursor_register_logging_function(struct hyprcursor_manager_t* manager, PHYPRCURSORLOGFUNC fn);
/*!
\since 0.1.6
Returns the raw image data of a cursor shape, not rendered at all, alongside the metadata.
The object needs to be freed instantly after using, see hyprcursor_raw_shape_data_free()
*/
CAPI hyprcursor_cursor_raw_shape_data* hyprcursor_get_raw_shape_data(struct hyprcursor_manager_t* manager, char* shape);
/*!
\since 0.1.6
See hyprcursor_get_raw_shape_data.
Frees the returned object.
*/
CAPI void hyprcursor_raw_shape_data_free(hyprcursor_cursor_raw_shape_data* data);
#endif #endif

View File

@ -2,6 +2,7 @@
#include <vector> #include <vector>
#include <stdlib.h> #include <stdlib.h>
#include <string>
#include "shared.h" #include "shared.h"
@ -28,6 +29,24 @@ namespace Hyprcursor {
std::vector<SCursorImageData> images; std::vector<SCursorImageData> images;
}; };
/*!
C++ structs for hyprcursor_cursor_raw_shape_image and hyprcursor_cursor_raw_shape_data
*/
struct SCursorRawShapeImage {
std::vector<unsigned char> data;
int size = 0;
int delay = 200;
};
struct SCursorRawShapeData {
std::vector<SCursorRawShapeImage> images;
float hotspotX = 0;
float hotspotY = 0;
std::string overridenBy = "";
eHyprcursorResizeAlgo resizeAlgo = HC_RESIZE_NONE;
eHyprcursorDataType type = HC_DATA_PNG;
};
/*! /*!
Basic Hyprcursor manager. Basic Hyprcursor manager.
@ -93,11 +112,47 @@ namespace Hyprcursor {
return data; return data;
} }
/*!
\since 0.1.6
Returns the raw image data of a cursor shape, not rendered at all, alongside the metadata.
*/
SCursorRawShapeData getRawShapeData(const char* shape_) {
auto CDATA = getRawShapeDataC(shape_);
if (CDATA->overridenBy) {
SCursorRawShapeData d{.overridenBy = CDATA->overridenBy};
free(CDATA->overridenBy);
delete CDATA;
return d;
}
SCursorRawShapeData data{.hotspotX = CDATA->hotspotX, .hotspotY = CDATA->hotspotY, .overridenBy = "", .resizeAlgo = CDATA->resizeAlgo, .type = CDATA->type};
for (size_t i = 0; i < CDATA->len; ++i) {
SCursorRawShapeImageC* cimage = &CDATA->images[i];
SCursorRawShapeImage& img = data.images.emplace_back();
img.size = cimage->size;
img.delay = cimage->delay;
img.data = std::vector<unsigned char>{(unsigned char*)cimage->data, (unsigned char*)cimage->data + (std::size_t)cimage->len};
}
delete[] CDATA->images;
delete CDATA;
return data;
}
/*! /*!
Prefer getShape, this is for C compat. Prefer getShape, this is for C compat.
*/ */
SCursorImageData** getShapesC(int& outSize, const char* shape_, const SCursorStyleInfo& info); SCursorImageData** getShapesC(int& outSize, const char* shape_, const SCursorStyleInfo& info);
/*!
Prefer getShapeData, this is for C compat.
*/
SCursorRawShapeDataC* getRawShapeDataC(const char* shape_);
/*! /*!
Marks a certain style as done, allowing it to be potentially freed Marks a certain style as done, allowing it to be potentially freed
*/ */

View File

@ -25,6 +25,39 @@ enum eHyprcursorLogLevel {
HC_LOG_CRITICAL, HC_LOG_CRITICAL,
}; };
enum eHyprcursorDataType {
HC_DATA_PNG = 0,
HC_DATA_SVG,
};
enum eHyprcursorResizeAlgo {
HC_RESIZE_INVALID = 0,
HC_RESIZE_NONE,
HC_RESIZE_BILINEAR,
HC_RESIZE_NEAREST,
};
struct SCursorRawShapeImageC {
void* data;
unsigned long int len;
int size;
int delay;
};
typedef struct SCursorRawShapeImageC hyprcursor_cursor_raw_shape_image;
struct SCursorRawShapeDataC {
struct SCursorRawShapeImageC* images;
unsigned long int len;
float hotspotX;
float hotspotY;
char* overridenBy;
enum eHyprcursorResizeAlgo resizeAlgo;
enum eHyprcursorDataType type;
};
typedef struct SCursorRawShapeDataC hyprcursor_cursor_raw_shape_data;
/* /*
msg is owned by the caller and will be freed afterwards. msg is owned by the caller and will be freed afterwards.
*/ */

View File

@ -294,7 +294,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap
break; break;
// if we get here, means loadThemeStyle wasn't called most likely. If resize algo is specified, this is an error. // if we get here, means loadThemeStyle wasn't called most likely. If resize algo is specified, this is an error.
if (shape->resizeAlgo != RESIZE_NONE) { if (shape->resizeAlgo != HC_RESIZE_NONE) {
Debug::log(HC_LOG_ERR, logFn, "getSurfaceFor didn't match a size?"); Debug::log(HC_LOG_ERR, logFn, "getSurfaceFor didn't match a size?");
return nullptr; return nullptr;
} }
@ -348,11 +348,59 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap
return data; return data;
} }
SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) {
if (!shape_) {
Debug::log(HC_LOG_ERR, logFn, "getShapeDataC: shape of nullptr is invalid");
return nullptr;
}
const std::string SHAPE = shape_;
SCursorRawShapeDataC* data = new SCursorRawShapeDataC;
std::vector<SLoadedCursorImage*> resultingImages;
for (auto& shape : impl->theme.shapes) {
// if it's overridden just return the override
if (const auto IT = std::find(shape->overrides.begin(), shape->overrides.end(), SHAPE); IT != shape->overrides.end()) {
data->overridenBy = strdup(IT->c_str());
return data;
}
if (shape->directory != SHAPE)
continue;
if (!impl->loadedShapes.contains(shape.get()))
continue; // ??
// found it
for (auto& i : impl->loadedShapes[shape.get()].images) {
resultingImages.push_back(i.get());
}
data->hotspotX = shape->hotspotX;
data->hotspotY = shape->hotspotY;
data->type = shape->shapeType == SHAPE_PNG ? HC_DATA_PNG : HC_DATA_SVG;
break;
}
data->len = resultingImages.size();
data->images = new SCursorRawShapeImageC[data->len];
for (size_t i = 0; i < data->len; ++i) {
data->images[i].data = resultingImages[i]->data;
data->images[i].len = resultingImages[i]->dataLen;
data->images[i].size = resultingImages[i]->side;
data->images[i].delay = resultingImages[i]->delay;
}
return data;
}
bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
Debug::log(HC_LOG_INFO, logFn, "loadThemeStyle: loading for size {}", info.size); Debug::log(HC_LOG_INFO, logFn, "loadThemeStyle: loading for size {}", info.size);
for (auto& shape : impl->theme.shapes) { for (auto& shape : impl->theme.shapes) {
if (shape->resizeAlgo == RESIZE_NONE && shape->shapeType != SHAPE_SVG) { if (shape->resizeAlgo == HC_RESIZE_NONE && shape->shapeType != SHAPE_SVG) {
// don't resample NONE style cursors // don't resample NONE style cursors
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: ignoring {}", shape->directory); Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: ignoring {}", shape->directory);
continue; continue;
@ -415,7 +463,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
const auto PCAIRO = cairo_create(newImage->cairoSurface); const auto PCAIRO = cairo_create(newImage->cairoSurface);
cairo_set_antialias(PCAIRO, shape->resizeAlgo == RESIZE_BILINEAR ? CAIRO_ANTIALIAS_GOOD : CAIRO_ANTIALIAS_NONE); cairo_set_antialias(PCAIRO, shape->resizeAlgo == HC_RESIZE_BILINEAR ? CAIRO_ANTIALIAS_GOOD : CAIRO_ANTIALIAS_NONE);
cairo_save(PCAIRO); cairo_save(PCAIRO);
cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR); cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR);
@ -426,7 +474,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
cairo_pattern_set_extend(PTN, CAIRO_EXTEND_NONE); cairo_pattern_set_extend(PTN, CAIRO_EXTEND_NONE);
const float scale = info.size / (float)f->side; const float scale = info.size / (float)f->side;
cairo_scale(PCAIRO, scale, scale); cairo_scale(PCAIRO, scale, scale);
cairo_pattern_set_filter(PTN, shape->resizeAlgo == RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST); cairo_pattern_set_filter(PTN, shape->resizeAlgo == HC_RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST);
cairo_set_source(PCAIRO, PTN); cairo_set_source(PCAIRO, PTN);
cairo_rectangle(PCAIRO, 0, 0, info.size, info.size); cairo_rectangle(PCAIRO, 0, 0, info.size, info.size);
@ -487,7 +535,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) { void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) {
for (auto& shape : impl->theme.shapes) { for (auto& shape : impl->theme.shapes) {
if (shape->resizeAlgo == RESIZE_NONE && shape->shapeType != SHAPE_SVG) if (shape->resizeAlgo == HC_RESIZE_NONE && shape->shapeType != SHAPE_SVG)
continue; continue;
std::erase_if(impl->loadedShapes[shape.get()].images, [info, &shape](const auto& e) { std::erase_if(impl->loadedShapes[shape.get()].images, [info, &shape](const auto& e) {
@ -520,6 +568,9 @@ PNG reading
static cairo_status_t readPNG(void* data, unsigned char* output, unsigned int len) { static cairo_status_t readPNG(void* data, unsigned char* output, unsigned int len) {
const auto DATA = (SLoadedCursorImage*)data; const auto DATA = (SLoadedCursorImage*)data;
if (DATA->readNeedle >= DATA->dataLen)
return CAIRO_STATUS_READ_ERROR;
if (!DATA->data) if (!DATA->data)
return CAIRO_STATUS_READ_ERROR; return CAIRO_STATUS_READ_ERROR;
@ -528,11 +579,6 @@ static cairo_status_t readPNG(void* data, unsigned char* output, unsigned int le
std::memcpy(output, (uint8_t*)DATA->data + DATA->readNeedle, toRead); std::memcpy(output, (uint8_t*)DATA->data + DATA->readNeedle, toRead);
DATA->readNeedle += toRead; DATA->readNeedle += toRead;
if (DATA->readNeedle >= DATA->dataLen) {
delete[] (char*)DATA->data;
DATA->data = nullptr;
}
return CAIRO_STATUS_SUCCESS; return CAIRO_STATUS_SUCCESS;
} }

View File

@ -56,3 +56,19 @@ void hyprcursor_register_logging_function(struct hyprcursor_manager_t* manager,
const auto MGR = (CHyprcursorManager*)manager; const auto MGR = (CHyprcursorManager*)manager;
MGR->registerLoggingFunction(fn); MGR->registerLoggingFunction(fn);
} }
CAPI hyprcursor_cursor_raw_shape_data* hyprcursor_get_raw_shape_data(struct hyprcursor_manager_t* manager, char* shape) {
const auto MGR = (CHyprcursorManager*)manager;
return MGR->getRawShapeDataC(shape);
}
CAPI void hyprcursor_raw_shape_data_free(hyprcursor_cursor_raw_shape_data* data) {
if (data->overridenBy) {
free(data->overridenBy);
delete data;
return;
}
delete[] data->images;
delete data;
}

View File

@ -18,7 +18,7 @@ struct SLoadedCursorImage {
// read stuff // read stuff
size_t readNeedle = 0; size_t readNeedle = 0;
void* data = nullptr; void* data = nullptr; // raw png / svg data, not image data
size_t dataLen = 0; size_t dataLen = 0;
bool isSVG = false; // if true, data is just a string of chars bool isSVG = false; // if true, data is just a string of chars

View File

@ -2,13 +2,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <hyprcursor/shared.h>
enum eResizeAlgo {
RESIZE_INVALID = 0,
RESIZE_NONE,
RESIZE_BILINEAR,
RESIZE_NEAREST,
};
enum eShapeType { enum eShapeType {
SHAPE_INVALID = 0, SHAPE_INVALID = 0,
@ -16,19 +10,19 @@ enum eShapeType {
SHAPE_SVG, SHAPE_SVG,
}; };
inline eResizeAlgo stringToAlgo(const std::string& s) { inline eHyprcursorResizeAlgo stringToAlgo(const std::string& s) {
if (s == "none") if (s == "none")
return RESIZE_NONE; return HC_RESIZE_NONE;
if (s == "nearest") if (s == "nearest")
return RESIZE_NEAREST; return HC_RESIZE_NEAREST;
return RESIZE_BILINEAR; return HC_RESIZE_BILINEAR;
} }
inline std::string algoToString(const eResizeAlgo a) { inline std::string algoToString(const eHyprcursorResizeAlgo a) {
switch (a) { switch (a) {
case RESIZE_BILINEAR: return "bilinear"; case HC_RESIZE_BILINEAR: return "bilinear";
case RESIZE_NEAREST: return "nearest"; case HC_RESIZE_NEAREST: return "nearest";
case RESIZE_NONE: return "none"; case HC_RESIZE_NONE: return "none";
default: return "none"; default: return "none";
} }
@ -44,7 +38,7 @@ struct SCursorImage {
struct SCursorShape { struct SCursorShape {
std::string directory; std::string directory;
float hotspotX = 0, hotspotY = 0; float hotspotX = 0, hotspotY = 0;
eResizeAlgo resizeAlgo = RESIZE_NEAREST; eHyprcursorResizeAlgo resizeAlgo = HC_RESIZE_NEAREST;
std::vector<SCursorImage> images; std::vector<SCursorImage> images;
std::vector<std::string> overrides; std::vector<std::string> overrides;
eShapeType shapeType = SHAPE_INVALID; eShapeType shapeType = SHAPE_INVALID;

View File

@ -1,3 +1,10 @@
/*
hyprlang-test in C.
Renders a cursor shape to /tmp at 48px
For better explanations, see the cpp tests.
*/
#include <hyprcursor/hyprcursor.h> #include <hyprcursor/hyprcursor.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -6,11 +13,6 @@ void logFunction(enum eHyprcursorLogLevel level, char* message) {
printf("[hc] %s\n", message); printf("[hc] %s\n", message);
} }
/*
hyprlang-test in C.
Renders a cursor shape to /tmp at 48px
*/
int main(int argc, char** argv) { int main(int argc, char** argv) {
struct hyprcursor_manager_t* mgr = hyprcursor_manager_create_with_logger(NULL, logFunction); struct hyprcursor_manager_t* mgr = hyprcursor_manager_create_with_logger(NULL, logFunction);
@ -24,6 +26,21 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
hyprcursor_cursor_raw_shape_data* shapeData = hyprcursor_get_raw_shape_data(mgr, "left_ptr");
if (!shapeData || shapeData->len <= 0) {
printf("failed querying left_ptr\n");
return 1;
}
printf("left_ptr images: %d\n", shapeData->len);
for (size_t i = 0; i < shapeData->len; ++i) {
printf("left_ptr image size: %d\n", shapeData->images[i].len);
}
hyprcursor_raw_shape_data_free(shapeData);
shapeData = NULL;
struct hyprcursor_cursor_style_info info = {.size = 48}; struct hyprcursor_cursor_style_info info = {.size = 48};
if (!hyprcursor_load_theme_style(mgr, info)) { if (!hyprcursor_load_theme_style(mgr, info)) {
printf("load failed\n"); printf("load failed\n");

View File

@ -1,3 +1,11 @@
/*
full_rendering.cpp
This example shows probably what you want to do.
Hyprcursor will render a left_ptr shape at 48x48px to a file called /tmp/arrow.png
*/
#include <iostream> #include <iostream>
#include <hyprcursor/hyprcursor.hpp> #include <hyprcursor/hyprcursor.hpp>
@ -5,29 +13,43 @@ void logFunction(enum eHyprcursorLogLevel level, char* message) {
std::cout << "[hc] " << message << "\n"; std::cout << "[hc] " << message << "\n";
} }
/*
hyprlang-test in C++.
Renders a cursor shape to /tmp at 48px
*/
int main(int argc, char** argv) { int main(int argc, char** argv) {
/*
Create a manager. You can optionally pass a logger function.
*/
Hyprcursor::CHyprcursorManager mgr(nullptr, logFunction); Hyprcursor::CHyprcursorManager mgr(nullptr, logFunction);
/*
Manager could be invalid if no themes were found, or
a specified theme was invalid.
*/
if (!mgr.valid()) { if (!mgr.valid()) {
std::cout << "mgr is invalid\n"; std::cout << "mgr is invalid\n";
return 1; return 1;
} }
/*
Style describes what pixel size you want your cursor
images to be.
Remember to free styles once you're done with them
(e.g. the user requested to change the cursor size to something else)
*/
Hyprcursor::SCursorStyleInfo style{.size = 48}; Hyprcursor::SCursorStyleInfo style{.size = 48};
// preload size 48 for testing
if (!mgr.loadThemeStyle(style)) { if (!mgr.loadThemeStyle(style)) {
std::cout << "failed loading style\n"; std::cout << "failed loading style\n";
return 1; return 1;
} }
// get cursor for left_ptr /*
Get a shape. This will return the data about available image(s),
their delay, hotspot, etc.
*/
const auto SHAPEDATA = mgr.getShape("left_ptr", style); const auto SHAPEDATA = mgr.getShape("left_ptr", style);
/*
If the size doesn't exist, images will be empty.
*/
if (SHAPEDATA.images.empty()) { if (SHAPEDATA.images.empty()) {
std::cout << "no images\n"; std::cout << "no images\n";
return 1; return 1;
@ -35,11 +57,16 @@ int main(int argc, char** argv) {
std::cout << "hyprcursor returned " << SHAPEDATA.images.size() << " images\n"; std::cout << "hyprcursor returned " << SHAPEDATA.images.size() << " images\n";
// save to disk /*
Save to disk with cairo
*/
const auto RET = cairo_surface_write_to_png(SHAPEDATA.images[0].surface, "/tmp/arrow.png"); const auto RET = cairo_surface_write_to_png(SHAPEDATA.images[0].surface, "/tmp/arrow.png");
std::cout << "Cairo returned for write: " << RET << "\n"; std::cout << "Cairo returned for write: " << RET << "\n";
/*
As mentioned before, clean up by releasing the style.
*/
mgr.cursorSurfaceStyleDone(style); mgr.cursorSurfaceStyleDone(style);
if (RET) if (RET)

80
tests/only_metadata.cpp Normal file
View File

@ -0,0 +1,80 @@
/*
only_metadata.cpp
This is a mode in which you probably do NOT want to operate,
but major DEs might want their own renderer for
cursor shapes.
Prefer full_rendering.cpp for consistency and simplicity.
*/
#include <iostream>
#include <hyprcursor/hyprcursor.hpp>
void logFunction(enum eHyprcursorLogLevel level, char* message) {
std::cout << "[hc] " << message << "\n";
}
int main(int argc, char** argv) {
/*
Create a manager. You can optionally pass a logger function.
*/
Hyprcursor::CHyprcursorManager mgr(nullptr, logFunction);
/*
Manager could be invalid if no themes were found, or
a specified theme was invalid.
*/
if (!mgr.valid()) {
std::cout << "mgr is invalid\n";
return 1;
}
/*
If you are planning on using your own renderer,
you do not want to load in any styles, as those
are rendered once you make your call.
Instead, let's request left_ptr's metadata
*/
auto RAWDATA = mgr.getRawShapeData("left_ptr");
/*
if images are empty, check overridenBy
*/
if (RAWDATA.images.empty()) {
/*
if overridenBy is empty, the current theme doesn't have this shape.
*/
if (RAWDATA.overridenBy.empty())
return false;
/*
load what it's overriden by.
*/
RAWDATA = mgr.getRawShapeData(RAWDATA.overridenBy.c_str());
}
/*
If we still have no images, the theme seems broken.
*/
if (RAWDATA.images.empty()) {
std::cout << "failed querying left_ptr\n";
return 1;
}
/*
You can query the images (animation frames)
or their properties.
Every image has .data and .type for you to handle.
*/
std::cout << "left_ptr images: " << RAWDATA.images.size() << "\n";
for (auto& i : RAWDATA.images)
std::cout << "left_ptr data size: " << i.data.size() << "\n";
return 0;
}