diff --git a/src/SConstruct b/src/SConstruct index 5ad4807..3f7a557 100644 --- a/src/SConstruct +++ b/src/SConstruct @@ -42,6 +42,7 @@ headers = [ 'font.h', 'framebuffer.h', 'group.h', + 'sound.h', 'sprite.h', 'world.h' ] diff --git a/src/animation.c b/src/animation.c index e6a799c..61ea7fe 100644 --- a/src/animation.c +++ b/src/animation.c @@ -56,25 +56,17 @@ static void mipy(float *buffer, int *w, int *h) { *d = (*s0 + *s1)*0.5f; } - -int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **outPixels) { +int imageLoadFromSDL(SDL_Surface *surface, int *outWidth, int *outHeight, unsigned char **outPixels) { *outWidth = 0; *outHeight = 0; *outPixels = NULL; - - // load image - SDL_Surface *surface = IMG_Load(path); - if (!surface) { - fprintf(stderr, "helianthus: cannot load image: %s\n", path); - return FALSE; - } + if (!surface) return FALSE; // convert to RGBA if (surface->format->format != SDL_PIXELFORMAT_RGBA8888) { SDL_PixelFormat *format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA8888); SDL_Surface *converted = SDL_ConvertSurface(surface, format, 0); SDL_FreeFormat(format); - SDL_FreeSurface(surface); surface = converted; } @@ -95,7 +87,6 @@ int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **o } } SDL_UnlockSurface(surface); - SDL_FreeSurface(surface); *outPixels = pixels; *outWidth = w; @@ -103,6 +94,29 @@ int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **o return TRUE; } +int imageLoadFromMemory(const void *data, int size, int *outWidth, int *outHeight, unsigned char **outPixels) { + SDL_RWops *rw = data && size > 0 ? SDL_RWFromMem((void*)data, size) : NULL; + SDL_Surface *surface = rw ? IMG_Load_RW(rw, SDL_TRUE) : NULL; + int res = imageLoadFromSDL(surface, outWidth, outHeight, outPixels); + if (surface) { + SDL_FreeSurface(surface); + } else { + fprintf(stderr, "helianthus: cannot load image from memory\n"); + } + return res; +} + +int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **outPixels) { + SDL_Surface *surface = IMG_Load(path); + int res = imageLoadFromSDL(surface, outWidth, outHeight, outPixels); + if (surface) { + SDL_FreeSurface(surface); + } else { + fprintf(stderr, "helianthus: cannot load image: %s\n", path); + } + return res; +} + int imageSave(const char *path, int width, int height, const void *pixels) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN diff --git a/src/animation.h b/src/animation.h index 158b926..8f9a401 100644 --- a/src/animation.h +++ b/src/animation.h @@ -10,6 +10,7 @@ typedef struct _Animation *Animation; int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **outPixels); +int imageLoadFromMemory(const void *data, int size, int *outWidth, int *outHeight, unsigned char **outPixels); int imageSave(const char *path, int width, int height, const void *pixels); unsigned int imageToGLTexture(int width, int height, const void *pixels, int wrap); unsigned int imageToGLTextureEx(int width, int height, const void *pixels, int horWrap, int vertWrap, int smooth, int mipMap); diff --git a/src/common.c b/src/common.c index dcef0dd..8eff884 100644 --- a/src/common.c +++ b/src/common.c @@ -3,6 +3,7 @@ #include #include "private.h" +#include "drawing.h" int heliInitialized; @@ -260,7 +261,9 @@ double colorGetValue(unsigned int colorCode) { } -unsigned int colorByName(const char *colorName) { +unsigned int colorByName(const char *colorName) + { return colorByNameA(colorName, 1); } +unsigned int colorByNameA(const char *colorName, double a) { unsigned int code = 0; const char *x = colorName; @@ -272,8 +275,9 @@ unsigned int colorByName(const char *colorName) { if (c >= '0' && c <= '9') hex[i] = c - '0'; if (c >= 'a' && c <= 'f') hex[i] = c - 'a' + 10; } - for(int i = 0; i < 8; ++i) + for(int i = 0; i < 6; ++i) code |= ( hex[i] << ((7-i)*4) ); + code |= colorToInt( a*(hex[6]*16 + hex[7])/255.0 ); } else if (isalpha(*x)) { int count = (int)(sizeof(colors)/sizeof(*colors)); @@ -282,13 +286,13 @@ unsigned int colorByName(const char *colorName) { do { if (tolower(*a) != tolower(*b)) break; ++a, ++b; - if (!*a && !*b) return colorByName(colors[i][1]); + if (!*a && !*b) return colorByNameA(colors[i][1], 1); } while (*a && *b); } } else { - double r = 0, g = 0, b = 0, a = 1; - sscanf(x, "%lf %lf %lf %lf", &r, &g, &b, &a); - code = colorByRGBA(r, g, b, a); + double r = 0, g = 0, b = 0, aa = 1; + sscanf(x, "%lf %lf %lf %lf", &r, &g, &b, &aa); + code = colorByRGBA(r, g, b, aa*a); } return code; } diff --git a/src/drawing.h b/src/drawing.h index 37f8e6d..0ff496a 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -84,6 +84,7 @@ void restoreState(); unsigned int colorByName(const char *colorName); +unsigned int colorByNameA(const char *colorName, double a); unsigned int colorByRGB(double r, double g, double b); unsigned int colorByRGBA(double r, double g, double b, double a); unsigned int colorByHSV(double h, double s, double v); diff --git a/src/font.c b/src/font.c index 6cb1d4b..3a3786d 100644 --- a/src/font.c +++ b/src/font.c @@ -87,7 +87,7 @@ static HeliGlyph blankGlyph; static const char blankPath[] = ""; static Font defaultFont = NULL; static Font unicodeFont = NULL; - +static int memFontIndex = 0; static HeliFontMap* createFontMap() { HeliFontMap *map = calloc(1, sizeof(*map)); @@ -246,21 +246,27 @@ static HeliFontInstance* loadFont(const char *path) { ftInitialized = TRUE; } if (ftLibrary) { - if (path[0] && path[0] != 1) { - if (FT_New_Face(ftLibrary, path, 0, &fi->face)) { - fprintf(stderr, "helianthus: cannot load font from file: %s\n", path); - fi->face = NULL; + if (strncmp(path, "helimem:", 8) == 0) { + void *data = NULL; + int size = 0; + fi->face = NULL; + if (sscanf(path, "helimem:%p:%d:", &data, &size) != EOF && data && size > 0) { + FT_Open_Args args = {}; + args.flags = FT_OPEN_MEMORY; + args.memory_base = (FT_Byte*)data; + args.memory_size = (FT_Long)size; + if (FT_Open_Face(ftLibrary, &args, 0, &fi->face)) + fi->face = NULL; } + if (!fi->face) + fprintf(stderr, "helianthus: cannot load font from memory\n"); } else { - FT_Open_Args args = {}; - args.flags = FT_OPEN_MEMORY; - args.memory_base = path[0] ? (FT_Byte*)heliBlobUnicodeFont : (FT_Byte*)heliBlobDefaultFont; - args.memory_size = path[0] ? heliBlobUnicodeFontSize : heliBlobDefaultFontSize; - if (FT_Open_Face(ftLibrary, &args, 0, &fi->face)) { - fprintf(stderr, "helianthus: cannot initialize %s font\n", path[0] ? "unicode" : "default"); + if (FT_New_Face(ftLibrary, path, 0, &fi->face)) { + fprintf(stderr, "helianthus: cannot load font from file: %s\n", path); fi->face = NULL; } } + if (fi->face) { FT_Set_Char_Size(fi->face, 0, FONT_BASE_SIZE*64, 72, 72); fi->height = fi->face->size->metrics.height/64.0; @@ -300,6 +306,20 @@ Font createFont(const char *path) { return f; } + +Font createFontFromMemory(const void *data, int size) { + char buffer[1024] = {}; + sprintf(buffer, "helimem:%p:%d:%d", data, size, ++memFontIndex); + return createFont(buffer); +} + + +Font fontClone(Font font) { + if (!font) return NULL; + return createFont(font->instance ? font->instance->path : NULL); +} + + void fontDestroy(Font f) { heliObjectUnregister(f); HeliDrawingState *ss = heliDrawingGetState(); @@ -337,8 +357,10 @@ TextLayout createTextLayout(const char *text) { HeliDrawingState *drawingState = heliDrawingGetState(); - if (!defaultFont) defaultFont = createFont(NULL); - if (!unicodeFont) unicodeFont = createFont("\1"); + if (!defaultFont) + defaultFont = createFontFromMemory(heliBlobDefaultFont, heliBlobDefaultFontSize); + if (!unicodeFont) + unicodeFont = createFontFromMemory(heliBlobUnicodeFont, heliBlobUnicodeFontSize); HeliFontInstance *fonts[10]; int fontsCount = 0; @@ -364,8 +386,11 @@ TextLayout createTextLayout(const char *text) { HeliLineDesc *line = layout->lines; line->height = fonts[0]->height; layout->chars = calloc(strlen(text) + 1, sizeof(HeliCharDesc)); - if (fonts[0]->path && fonts[0]->path[0] != '\1') - layout->font = createFont(fonts[0]->path); + if ( drawingState->font + && drawingState->font != defaultFont + && drawingState->font != unicodeFont + && drawingState->font->instance == fonts[0] ) + layout->font = fontClone(drawingState->font); // gather glyphs const char *c = text; diff --git a/src/font.h b/src/font.h index 2f5bd1a..ea32e6b 100644 --- a/src/font.h +++ b/src/font.h @@ -23,10 +23,13 @@ typedef enum _VAlign { Font createFont(const char *path); +Font createFontFromMemory(const void *data, int size); void fontDestroy(Font font); void textFont(Font font); void textFontDefault(); +Font fontClone(Font font); + void text(const char *text, double x, double y); void textAlign(HAlign hor, VAlign vert); void textSize(double size); diff --git a/src/helianthus.h b/src/helianthus.h index 8605f3e..7c0464d 100644 --- a/src/helianthus.h +++ b/src/helianthus.h @@ -13,6 +13,7 @@ extern "C" { #include "helianthus/group.h" #include "helianthus/drawing.h" #include "helianthus/font.h" +#include "helianthus/sound.h" #ifdef __cplusplus diff --git a/src/sound.c b/src/sound.c index 1672a79..13bbe9d 100644 --- a/src/sound.c +++ b/src/sound.c @@ -3,7 +3,7 @@ #include #include "private.h" -#include "world.h" +#include "sound.h" #define HELI_SOUND_CHANNELS 64 @@ -33,11 +33,13 @@ static Sound channels[HELI_SOUND_CHANNELS]; static HeliSoundTrashItem *trash; static size_t trashSize; +static int memSoundIndex = 0; static int less(unsigned int a, unsigned int b) { return (b - a) < (1u << 31); } + static void trashAdd(Mix_Chunk *chunk) { if (!chunk) return; @@ -55,6 +57,7 @@ static void trashAdd(Mix_Chunk *chunk) { trash[prevSize].time = time; } + static void trashProcess() { unsigned int time = SDL_GetTicks(); for(int i = 0; i < trashSize; ++i) @@ -62,6 +65,7 @@ static void trashProcess() { { Mix_FreeChunk(trash[i].chunk); trash[i].chunk = NULL; } } + static void trashDestroy() { for(int i = 0; i < trashSize; ++i) if (trash[i].chunk) Mix_FreeChunk(trash[i].chunk); @@ -84,6 +88,7 @@ static void init() { initialized = TRUE; } + static void deinit() { if (!initialized) return; heliArrayDestroy(&cache); @@ -101,13 +106,26 @@ static HeliSoundInstance* load(const char *path) { HeliSoundInstance *s = calloc(1, sizeof(*s)); s->path = heliStringCopy(path); if (ready) { - s->chunk = Mix_LoadWAV(path); - if (!s->chunk) - fprintf(stderr, "helianthus: cannot load sound file: %s\n", path); + if (strncmp(path, "helimem:", 8) == 0) { + void *data = NULL; + int size = 0; + s->chunk = NULL; + if (sscanf(path, "helimem:%p:%d:", &data, &size) != EOF && data && size > 0) { + SDL_RWops *rw = SDL_RWFromMem((void*)data, size); + if (rw) s->chunk = Mix_LoadWAV_RW(rw, SDL_TRUE); + } + if (!s->chunk) + fprintf(stderr, "helianthus: cannot load sound from memory\n"); + } else { + s->chunk = Mix_LoadWAV(path); + if (!s->chunk) + fprintf(stderr, "helianthus: cannot load sound file: %s\n", path); + } } return s; } + static void unload(HeliSoundInstance *s) { assert(!s->refcount); free(s->path); @@ -131,6 +149,20 @@ Sound createSound(const char *path) { return sound; } + +Sound createSoundFromMemory(const void *data, int size) { + char buffer[1024] = {}; + sprintf(buffer, "helimem:%p:%d:%d", data, size, ++memSoundIndex); + return createSound(buffer); +} + + +Sound soundClone(Sound sound) { + if (!sound) return NULL; + return createSound(sound->instance ? sound->instance->path : NULL); +} + + void soundDestroy(Sound sound) { heliObjectUnregister(sound); soundStop(sound); @@ -150,6 +182,7 @@ void soundPlay(Sound sound, int loop) { } } + void soundStop(Sound sound) { for(int i = 0; i < HELI_SOUND_CHANNELS; ++i) { if (channels[i] == sound) { @@ -164,6 +197,7 @@ void soundStop(Sound sound) { void heliSoundUpdate() { trashProcess(); } + void heliSoundFinish() { deinit(); } diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..9345b5f --- /dev/null +++ b/src/sound.h @@ -0,0 +1,19 @@ +#ifndef HELI_SOUND_H +#define HELI_SOUND_H + + +#include "common.h" + + +typedef struct _Sound *Sound; + + +Sound createSound(const char *path); +Sound createSoundFromMem(const void *data, int size); +void soundDestroy(Sound sound); +Sound soundClone(Sound sound); +void soundPlay(Sound sound, int loop); +void soundStop(Sound sound); + + +#endif diff --git a/src/world.c b/src/world.c index c103935..06f24e7 100644 --- a/src/world.c +++ b/src/world.c @@ -126,6 +126,9 @@ int worldGetHeight() void worldSetHeight(int h) { resize(width, h); } +void worldSetSize(int w, int h) + { resize(w, h); } + int worldGetResizable() { return resizable; } void worldSetResizable(int r) { @@ -163,21 +166,12 @@ void worldSetFrameRateEx(double minFrameRate, double maxFrameRate) { } void worldSetFrameRate(double frameRate) { worldSetFrameRateEx(frameRate, frameRate); } +void worldSetVariableFrameRate() + { worldSetFrameRateEx(HELI_MIN_FPS, HELI_MAX_FPS); } double worldGetFrameTime() { return frameTime; } -double worldGetMinFrameRate(); -void worldSetMinFrameRate(double minFrameRate); - -double worldGetMaxFrameRate(); -void worldSetMaxFrameRate(double maxFrameRate); - -void worldSetFrameRate(double frameRate); - -double worldGetFrameTime(); - - int worldGetFrameCount() { return (int)frameCount; } double worldGetSeconds() diff --git a/src/world.h b/src/world.h index 64ffb99..294a5be 100644 --- a/src/world.h +++ b/src/world.h @@ -6,7 +6,6 @@ #include "sprite.h" typedef void (*Callback)(); -typedef struct _Sound *Sound; typedef enum _KeyEvent { KEYEVENT_KEY_DOWN, @@ -20,11 +19,6 @@ typedef enum _KeyEvent { void drawSprites(); -Sound createSound(const char *path); -void soundDestroy(Sound sound); -void soundPlay(Sound sound, int loop); -void soundStop(Sound sound); - int keyDown(const char *code); int keyWentDown(const char *code); int keyWentUp(const char *code); @@ -57,6 +51,8 @@ void worldSetWidth(int width); int worldGetHeight(); void worldSetHeight(int height); +void worldSetSize(int width, int height); + int worldGetResizable(); void worldSetResizable(int resizable); @@ -67,6 +63,7 @@ double worldGetMinFrameRate(); double worldGetMaxFrameRate(); void worldSetFrameRateEx(double minFrameRate, double maxFrameRate); void worldSetFrameRate(double frameRate); +void worldSetVariableFrameRate(); double worldGetFrameTime(); int worldGetFrameCount();