summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrik.ramaekers@protonmail.com>2020-02-05 18:58:55 +0100
committerAldrik Ramaekers <aldrik.ramaekers@protonmail.com>2020-02-05 18:58:55 +0100
commit8c2f35bd1f18b62fff609f3a7d77d4e85b706916 (patch)
tree93e504989bd6182b1b381f1c9bd1b46e0732169a /src
parentec901c42d7d5dee13b4c69e4b65fc385d8ffd3a8 (diff)
refactor
Diffstat (limited to 'src')
-rw-r--r--src/array.c204
-rw-r--r--src/array.h34
-rw-r--r--src/asset_definitions.h32
-rw-r--r--src/assets.c312
-rw-r--r--src/assets.h142
-rw-r--r--src/camera.c22
-rw-r--r--src/camera.h19
-rw-r--r--src/command_line.c265
-rw-r--r--src/command_line.h12
-rw-r--r--src/config.h4
-rw-r--r--src/external/LooplessSizeMove.c864
-rw-r--r--src/external/cJSON.c2980
-rw-r--r--src/external/cJSON.h294
-rw-r--r--src/external/stb_image.h7547
-rw-r--r--src/external/stb_truetype.h4882
-rw-r--r--src/external/utf8.h1258
-rw-r--r--src/input.c239
-rw-r--r--src/input.h224
-rw-r--r--src/linux/platform.c1593
-rw-r--r--src/linux/thread.c129
-rw-r--r--src/localization.c148
-rw-r--r--src/localization.h59
-rw-r--r--src/memory.h19
-rw-r--r--src/memory_bucket.c74
-rw-r--r--src/memory_bucket.h26
-rw-r--r--src/mo_edit.c21
-rw-r--r--src/platform.h222
-rw-r--r--src/platform_shared.c244
m---------src/project-base0
-rw-r--r--src/project_base.h81
-rw-r--r--src/render.c465
-rw-r--r--src/render.h61
-rw-r--r--src/settings.c313
-rw-r--r--src/settings.h60
-rw-r--r--src/settings_config.c240
-rw-r--r--src/settings_config.h40
-rw-r--r--src/string_utils.c557
-rw-r--r--src/string_utils.h87
-rw-r--r--src/test.txt1
-rw-r--r--src/thread.h67
-rw-r--r--src/ui.c1544
-rw-r--r--src/ui.h201
-rw-r--r--src/windows/platform.c1268
-rw-r--r--src/windows/thread.c102
44 files changed, 86 insertions, 26870 deletions
diff --git a/src/array.c b/src/array.c
deleted file mode 100644
index 7c0cbeb..0000000
--- a/src/array.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-array array_create(u64 entry_size)
-{
- array new_array;
- new_array.length = 0;
- new_array.reserved_length = 0;
- new_array.entry_size = entry_size;
- new_array.data = 0;
- new_array.reserve_jump = 1;
- new_array.mutex = mutex_create_recursive();
-
- return new_array;
-}
-
-int array_push(array *array, void *data)
-{
- ASSERT(array);
- ASSERT(data);
- ASSERT(array->reserve_jump >= 1);
-
- mutex_lock(&array->mutex);
- array->length++;
-
- if (!array->data)
- {
- array->data = mem_alloc(array->entry_size * array->reserve_jump);
- array->reserved_length = array->reserve_jump;
- }
-
- if (array->reserved_length < array->length)
- {
- array->reserved_length += array->reserve_jump;
- array->data = mem_realloc(array->data, (array->reserved_length*array->entry_size));
- }
-
- memcpy(array->data + ((array->length-1) * array->entry_size),
- data, array->entry_size);
-
- s32 result = array->length -1;
- mutex_unlock(&array->mutex);
- return result;
-}
-
-int array_push_size(array *array, void *data, s32 data_size)
-{
- ASSERT(array);
- ASSERT(data);
- ASSERT(array->reserve_jump >= 1);
-
- mutex_lock(&array->mutex);
- array->length++;
-
- if (!array->data)
- {
- array->data = mem_alloc(array->entry_size * array->reserve_jump);
- array->reserved_length = array->reserve_jump;
- }
-
- if (array->reserved_length < array->length)
- {
- array->reserved_length += array->reserve_jump;
- array->data = mem_realloc(array->data, (array->reserved_length*array->entry_size));
- }
-
- memcpy(array->data + ((array->length-1) * array->entry_size),
- data, data_size);
-
- // fill remaining space with 0
- if (array->entry_size > data_size)
- {
- s32 remaining = array->entry_size - data_size;
- memset(array->data + ((array->length-1) * array->entry_size) + data_size,
- 0, remaining);
- }
-
- s32 result = array->length -1;
- mutex_unlock(&array->mutex);
- return result;
-}
-
-void array_reserve(array *array, u32 reserve_count)
-{
- ASSERT(array);
-
- mutex_lock(&array->mutex);
- u32 reserved_count = array->reserved_length - array->length;
- reserve_count -= reserved_count;
-
- if (reserve_count > 0)
- {
- array->reserved_length += reserve_count;
-
- if (array->data)
- array->data = mem_realloc(array->data, (array->reserved_length*array->entry_size));
- else
- array->data = mem_alloc(array->reserved_length*array->entry_size);
- }
- mutex_unlock(&array->mutex);
-}
-
-void array_remove_at(array *array, u32 at)
-{
- ASSERT(array);
- ASSERT(at >= 0);
- ASSERT(at < array->length);
-
- mutex_lock(&array->mutex);
- if (array->length > 1)
- {
- int offset = at * array->entry_size;
- int size = (array->length - at - 1) * array->entry_size;
- memcpy(array->data + offset,
- array->data + offset + array->entry_size,
- size);
-
- //array->data = realloc(array->data, array->length * array->entry_size);
- }
-
- array->length--;
- mutex_unlock(&array->mutex);
-}
-
-void array_remove(array *array, void *ptr)
-{
- mutex_lock(&array->mutex);
- int offset = ptr - array->data;
- int at = offset / array->entry_size;
- array_remove_at(array, at);
- mutex_unlock(&array->mutex);
-}
-
-void array_remove_by(array *array, void *data)
-{
- ASSERT(array);
-
- mutex_lock(&array->mutex);
- for (int i = 0; i < array->length; i++)
- {
- void *d = array_at(array, i);
- if (memcmp(d, data, array->entry_size) == 0)
- {
- array_remove_at(array, i);
- return;
- }
- }
- mutex_unlock(&array->mutex);
-}
-
-void *array_at(array *array, u32 at)
-{
- mutex_lock(&array->mutex);
- ASSERT(array);
- ASSERT(at >= 0);
- ASSERT(at < array->length);
-
- void *result = array->data + (at * array->entry_size);
- mutex_unlock(&array->mutex);
- return result;
-}
-
-void array_destroy(array *array)
-{
- ASSERT(array);
- mem_free(array->data);
- mutex_destroy(&array->mutex);
-}
-
-void array_swap(array *array, u32 swap1, u32 swap2)
-{
- ASSERT(array);
- ASSERT(swap2 >= 0);
- ASSERT(swap2 < array->length);
- if (swap1 == swap2) return;
-
- void *swap1_at = array_at(array, swap1);
- void *swap2_at = array_at(array, swap2);
-
- mutex_lock(&array->mutex);
- char swap1_buffer[array->entry_size];
- memcpy(swap1_buffer, swap1_at, array->entry_size);
- memcpy(swap1_at, swap2_at, array->entry_size);
- memcpy(swap2_at, swap1_buffer, array->entry_size);
- mutex_unlock(&array->mutex);
-}
-
-array array_copy(array *arr)
-{
- array new_array;
- new_array.length = arr->length;
- new_array.reserved_length = arr->reserved_length;
- new_array.entry_size = arr->entry_size;
- new_array.data = mem_alloc(new_array.entry_size*new_array.reserved_length);
- new_array.mutex = mutex_create();
-
- mutex_lock(&arr->mutex);
- memcpy(new_array.data, arr->data, new_array.entry_size*new_array.reserved_length);
- mutex_unlock(&arr->mutex);
- return new_array;
-} \ No newline at end of file
diff --git a/src/array.h b/src/array.h
deleted file mode 100644
index cf3195c..0000000
--- a/src/array.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_ARRAY
-#define INCLUDE_ARRAY
-
-#define ASSERT(e_) {if(!(e_)){*(int*)0=0;}}
-
-typedef struct t_array
-{
- u32 length;
- u32 reserved_length;
- u64 entry_size;
- u16 reserve_jump;
- void *data;
- mutex mutex;
-} array;
-
-array array_create(u64 entry_size);
-int array_push(array *array, void *data);
-int array_push_size(array *array, void *data, s32 data_size);
-void array_remove_at(array *array, u32 at);
-void array_remove(array *array, void *ptr);
-void array_remove_by(array *array, void *data);
-void *array_at(array *array, u32 at);
-void array_destroy(array *array);
-void array_swap(array *array, u32 swap1, u32 swap2);
-void array_reserve(array *array, u32 reserve_count);
-array array_copy(array *array);
-
-#endif \ No newline at end of file
diff --git a/src/asset_definitions.h b/src/asset_definitions.h
new file mode 100644
index 0000000..628ae38
--- /dev/null
+++ b/src/asset_definitions.h
@@ -0,0 +1,32 @@
+extern u8 _binary____data_imgs_en_png_start[];
+extern u8 _binary____data_imgs_en_png_end[];
+
+extern u8 _binary____data_imgs_nl_png_start[];
+extern u8 _binary____data_imgs_nl_png_end[];
+
+extern u8 _binary____data_imgs_logo_64_png_start[];
+extern u8 _binary____data_imgs_logo_64_png_end[];
+
+extern u8 _binary____data_fonts_mono_ttf_start[];
+extern u8 _binary____data_fonts_mono_ttf_end[];
+
+extern u8 _binary____data_translations_en_English_mo_start[];
+extern u8 _binary____data_translations_en_English_mo_end[];
+
+extern u8 _binary____data_translations_nl_Dutch_mo_start[];
+extern u8 _binary____data_translations_nl_Dutch_mo_end[];
+
+extern u8 _binary____data_imgs_list_png_start[];
+extern u8 _binary____data_imgs_list_png_end[];
+
+extern u8 _binary____data_imgs_delete_png_start[];
+extern u8 _binary____data_imgs_delete_png_end[];
+
+extern u8 _binary____data_imgs_exclaim_png_start[];
+extern u8 _binary____data_imgs_exclaim_png_end[];
+
+extern u8 _binary____data_imgs_add_png_start[];
+extern u8 _binary____data_imgs_add_png_end[];
+
+extern u8 _binary____data_imgs_set_png_start[];
+extern u8 _binary____data_imgs_set_png_end[]; \ No newline at end of file
diff --git a/src/assets.c b/src/assets.c
deleted file mode 100644
index 0ba6192..0000000
--- a/src/assets.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-void assets_create()
-{
- assets asset_collection;
- asset_collection.images = array_create(sizeof(image));
- asset_collection.fonts = array_create(sizeof(font));
-
- array_reserve(&asset_collection.images, ASSET_IMAGE_COUNT);
- array_reserve(&asset_collection.fonts, ASSET_FONT_COUNT);
-
- asset_collection.queue.queue = array_create(sizeof(asset_task));
- asset_collection.post_process_queue = array_create(sizeof(asset_task));
-
- array_reserve(&asset_collection.queue.queue, ASSET_QUEUE_COUNT);
- array_reserve(&asset_collection.post_process_queue, ASSET_QUEUE_COUNT);
-
- asset_mutex = mutex_create();
- asset_collection.valid = true;
- asset_collection.done_loading_assets = false;
-
- global_asset_collection = asset_collection;
-}
-
-inline static bool is_big_endian()
-{
- volatile uint32_t i=0x01234567;
- // return 1 for big endian, 0 for little endian.
- return !((*((uint8_t*)(&i))) == 0x67);
-}
-
-void assets_do_post_process()
-{
- mutex_lock(&asset_mutex);
-
- for (int i = 0; i < global_asset_collection.post_process_queue.length; i++)
- {
- asset_task *task = array_at(&global_asset_collection.post_process_queue, i);
-
- if (task->type == ASSET_IMAGE)
- {
- if (task->image->data && task->valid)
- {
- glGenTextures(1, &task->image->textureID);
- glBindTexture(GL_TEXTURE_2D, task->image->textureID);
-
- s32 flag = is_big_endian() ? GL_UNSIGNED_INT_8_8_8_8 :
- GL_UNSIGNED_INT_8_8_8_8_REV;
-
- glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA8, task->image->width,
- task->image->height, 0, GL_RGBA, flag, task->image->data);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- task->image->loaded = true;
-
- if (!task->image->keep_in_memory)
- stbi_image_free(task->image->data);
- }
- }
- else if (task->type == ASSET_FONT)
- {
- if (task->valid)
- {
- for (s32 i = TEXT_CHARSET_START; i < TEXT_CHARSET_END; i++)
- {
- glyph *g = &task->font->glyphs[i];
-
- glGenTextures(1, &g->textureID);
- glBindTexture(GL_TEXTURE_2D, g->textureID);
-
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, g->width,g->height,
- 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap );
-
- mem_free(g->bitmap);
- }
-
- task->font->loaded = true;
- }
- }
-
- array_remove_at(&global_asset_collection.post_process_queue, i);
- }
-
- mutex_unlock(&asset_mutex);
-}
-
-bool assets_queue_worker_load_image(image *image)
-{
- set_active_directory(binary_path);
-
- image->data = stbi_load_from_memory(image->start_addr,
- image->end_addr - image->start_addr,
- &image->width,
- &image->height,
- &image->channels,
- STBI_rgb_alpha);
-
- return !(image->data == 0);
-}
-
-bool assets_queue_worker_load_font(font *font)
-{
- unsigned char *ttf_buffer = (unsigned char*)font->start_addr;
-
- stbtt_fontinfo info;
- if (!stbtt_InitFont(&info, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)))
- {
- return false;
- }
- float scale = stbtt_ScaleForPixelHeight(&info, font->size);
-
- for (s32 i = TEXT_CHARSET_START; i < TEXT_CHARSET_END; i++)
- {
- s32 w, h, xoff, yoff;
-
- glyph new_glyph;
- new_glyph.bitmap = stbtt_GetCodepointBitmap(&info, 0, scale, i, &w, &h, &xoff, &yoff);
- new_glyph.width = w;
- new_glyph.height = h;
- new_glyph.xoff = xoff;
- new_glyph.yoff = yoff;
-
- if (i == 'M') font->px_h = -yoff;
-
- font->glyphs[i-TEXT_CHARSET_START] = new_glyph;
- }
-
- font->info = info;
- font->scale = scale;
-
- return true;
-}
-
-void *assets_queue_worker()
-{
- while (global_asset_collection.valid && !global_asset_collection.done_loading_assets)
- {
- if (mutex_trylock(&asset_mutex))
- {
- int queue_length = global_asset_collection.queue.queue.length;
- if (!queue_length)
- {
- mutex_unlock(&asset_mutex);
- continue;
- }
-
- asset_task *task = array_at(&global_asset_collection.queue.queue, 0);
- asset_task buf = *task;
- array_remove_at(&global_asset_collection.queue.queue, 0);
- mutex_unlock(&asset_mutex);
-
- // load here
- if (buf.type == ASSET_IMAGE)
- {
- bool result = assets_queue_worker_load_image(buf.image);
- buf.valid = result;
- }
- else if (buf.type == ASSET_FONT)
- {
- bool result = assets_queue_worker_load_font(buf.font);
- buf.valid = result;
- }
-
- mutex_lock(&asset_mutex);
-
- assert(global_asset_collection.post_process_queue.reserved_length >
- global_asset_collection.post_process_queue.length);
-
- array_push(&global_asset_collection.post_process_queue, &buf);
- mutex_unlock(&asset_mutex);
- }
-
- // 3 ms
- thread_sleep(3000);
- }
-
- return 0;
-}
-
-image *assets_load_image(u8 *start_addr, u8 *end_addr, bool keep_in_memory)
-{
- // check if image is already loaded or loading
- for (int i = 0; i < global_asset_collection.images.length; i++)
- {
- image *img_at = array_at(&global_asset_collection.images, i);
-
- if (start_addr == img_at->start_addr)
- {
- // image is already loaded/loading
- img_at->references++;
- return img_at;
- }
- }
-
- image new_image;
- new_image.loaded = false;
- new_image.start_addr = start_addr;
- new_image.end_addr = end_addr;
- new_image.references = 1;
- new_image.keep_in_memory = keep_in_memory;
-
- // NOTE(Aldrik): we should never realloc the image array because pointers will be
- // invalidated.
- assert(global_asset_collection.images.reserved_length > global_asset_collection.images.length);
-
- int index = array_push(&global_asset_collection.images, &new_image);
-
- asset_task task;
- task.type = ASSET_IMAGE;
- task.image = array_at(&global_asset_collection.images, index);
-
- mutex_lock(&asset_mutex);
- array_push(&global_asset_collection.queue.queue, &task);
- mutex_unlock(&asset_mutex);
-
- return task.image;
-}
-
-void assets_destroy_image(image *image_to_destroy)
-{
- if (image_to_destroy->references == 1)
- {
- glBindTexture(GL_TEXTURE_2D, 0);
- glDeleteTextures(1, &image_to_destroy->textureID);
-
- if (image_to_destroy->keep_in_memory)
- stbi_image_free(image_to_destroy->data);
-
- //array_remove(&global_asset_collection.images, image_at);
- }
- else
- {
- image_to_destroy->references--;
- }
-}
-
-font *assets_load_font(u8 *start_addr, u8 *end_addr, s16 size)
-{
- //assert(!(size % 4));
- for (int i = 0; i < global_asset_collection.fonts.length; i++)
- {
- font *font_at = array_at(&global_asset_collection.fonts, i);
-
- if (start_addr == font_at->start_addr && font_at->size == size)
- {
- // font is already loaded/loading
- font_at->references++;
- return font_at;
- }
- }
-
- font new_font;
- new_font.loaded = false;
- new_font.start_addr = start_addr;
- new_font.end_addr = end_addr;
- new_font.size = size;
- new_font.references = 1;
-
- // NOTE(Aldrik): we should never realloc the font array because pointers will be
- // invalidated.
- assert(global_asset_collection.fonts.reserved_length > global_asset_collection.fonts.length);
-
- int index = array_push(&global_asset_collection.fonts, &new_font);
-
- asset_task task;
- task.type = ASSET_FONT;
- task.font = array_at(&global_asset_collection.fonts, index);
-
- mutex_lock(&asset_mutex);
- array_push(&global_asset_collection.queue.queue, &task);
- mutex_unlock(&asset_mutex);
-
- return task.font;
-}
-
-void assets_destroy_font(font *font_to_destroy)
-{
- if (font_to_destroy->references == 1)
- {
- //glBindTexture(GL_TEXTURE_2D, 0);
- //glDeleteTextures(1, font_to_destroy->textureIDs);
- }
- else
- {
- font_to_destroy->references--;
- }
-}
-
-void assets_destroy()
-{
- global_asset_collection.valid = false;
- global_asset_collection.done_loading_assets = false;
-
- array_destroy(&global_asset_collection.images);
- array_destroy(&global_asset_collection.fonts);
-
- array_destroy(&global_asset_collection.queue.queue);
- array_destroy(&global_asset_collection.post_process_queue);
-
- mem_free(binary_path);
-
- mutex_destroy(&asset_mutex);
-}
diff --git a/src/assets.h b/src/assets.h
deleted file mode 100644
index 4e614e9..0000000
--- a/src/assets.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_ASSETS
-#define INCLUDE_ASSETS
-
-#ifndef ASSET_IMAGE_COUNT
-#define ASSET_IMAGE_COUNT 10
-#endif
-
-#ifndef ASSET_FONT_COUNT
-#define ASSET_FONT_COUNT 10
-#endif
-
-#ifndef ASSET_QUEUE_COUNT
-#define ASSET_QUEUE_COUNT 20
-#endif
-
-// binary blobs
-extern u8 _binary____data_imgs_en_png_start[];
-extern u8 _binary____data_imgs_en_png_end[];
-
-extern u8 _binary____data_imgs_nl_png_start[];
-extern u8 _binary____data_imgs_nl_png_end[];
-
-extern u8 _binary____data_imgs_logo_64_png_start[];
-extern u8 _binary____data_imgs_logo_64_png_end[];
-
-extern u8 _binary____data_fonts_mono_ttf_start[];
-extern u8 _binary____data_fonts_mono_ttf_end[];
-
-extern u8 _binary____data_translations_en_English_mo_start[];
-extern u8 _binary____data_translations_en_English_mo_end[];
-
-extern u8 _binary____data_translations_nl_Dutch_mo_start[];
-extern u8 _binary____data_translations_nl_Dutch_mo_end[];
-
-extern u8 _binary____data_imgs_list_png_start[];
-extern u8 _binary____data_imgs_list_png_end[];
-
-extern u8 _binary____data_imgs_delete_png_start[];
-extern u8 _binary____data_imgs_delete_png_end[];
-
-extern u8 _binary____data_imgs_exclaim_png_start[];
-extern u8 _binary____data_imgs_exclaim_png_end[];
-
-extern u8 _binary____data_imgs_add_png_start[];
-extern u8 _binary____data_imgs_add_png_end[];
-
-extern u8 _binary____data_imgs_set_png_start[];
-extern u8 _binary____data_imgs_set_png_end[];
-
-typedef struct t_image {
- u8 *start_addr;
- u8 *end_addr;
- bool loaded;
- bool keep_in_memory;
- s32 width;
- s32 height;
- s32 channels;
- void *data;
- s16 references;
- GLuint textureID;
-} image;
-
-#define TEXT_CHARSET_START 0
-#define TEXT_CHARSET_END 2000
-#define TOTAL_GLYPHS TEXT_CHARSET_END-TEXT_CHARSET_START
-
-typedef struct t_glyph
-{
- s32 width;
- s32 height;
- s32 xoff;
- s32 yoff;
- void *bitmap;
- GLuint textureID;
-} glyph;
-
-typedef struct t_font
-{
- u8 *start_addr;
- u8 *end_addr;
- bool loaded;
- s16 references;
- s16 size;
- s32 px_h;
- float32 scale;
- stbtt_fontinfo info;
- glyph glyphs[TOTAL_GLYPHS];
-} font;
-
-typedef enum t_asset_task_type
-{
- ASSET_IMAGE,
- ASSET_FONT,
-} asset_task_type;
-
-typedef struct t_asset_task
-{
- s8 type;
- bool valid;
- union {
- image *image;
- font *font;
- };
-} asset_task;
-
-typedef struct t_asset_queue {
- array queue;
-} asset_queue;
-
-typedef struct t_assets {
- array images;
- array fonts;
- asset_queue queue;
- array post_process_queue;
- bool valid;
- bool done_loading_assets;
-} assets;
-
-char *binary_path;
-
-mutex asset_mutex;
-assets global_asset_collection;
-
-void assets_create();
-void assets_destroy();
-
-void assets_do_post_process();
-void *assets_queue_worker();
-
-image *assets_load_image(u8 *start_addr, u8 *end_addr, bool keep_in_memory);
-void assets_destroy_image(image *image);
-
-font *assets_load_font(u8 *start_addr, u8 *end_addr, s16 size);
-void assets_destroy_font(font *font);
-
-#endif \ No newline at end of file
diff --git a/src/camera.c b/src/camera.c
deleted file mode 100644
index 5bde9f9..0000000
--- a/src/camera.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-void camera_apply_transformations(platform_window *window, camera *camera)
-{
- s32 x = (window->width/2)+(camera->x);
- s32 y = (window->height/2)+(camera->y);
- glTranslatef(x, y, 0.0f);
- glRotatef(camera->rotation, 0.0f, 0.0f, 1.0f);
- glTranslatef(-x, -y, 0.0f);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- glOrtho(camera->x, window->width+camera->x,
- window->height+camera->y, camera->y, -100, 100);
-
- glMatrixMode(GL_MODELVIEW);
-} \ No newline at end of file
diff --git a/src/camera.h b/src/camera.h
deleted file mode 100644
index c6e4093..0000000
--- a/src/camera.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_CAMERA
-#define INCLUDE_CAMERA
-
-typedef struct t_camera
-{
- float32 x;
- float32 y;
- float32 rotation;
-} camera;
-
-void camera_apply_transformations(platform_window *window, camera *camera);
-
-#endif \ No newline at end of file
diff --git a/src/command_line.c b/src/command_line.c
deleted file mode 100644
index b1718f9..0000000
--- a/src/command_line.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#if 0
-void find_text_in_files(search_result *search_result);
-s32 prepare_search_directory_path(char *path, s32 len);
-search_result *create_empty_search_result();
-void do_search();
-bool export_results(search_result *result);
-
-static void print_license_message()
-{
- time_t t = time(0);
- struct tm *now = localtime(&t);
-
- printf("BSD 2-Clause License\n"
- "\n"
- "Copyright (c) %d, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com\n"
- "All rights reserved.\n"
- "\n"
- "Redistribution and use in source and binary forms, with or without\n"
- "modification, are permitted provided that the following conditions are met:\n"
- "\n"
- "1. Redistributions of source code must retain the above copyright notice, this\n"
- "list of conditions and the following disclaimer.\n"
- "\n"
- "2. Redistributions in binary form must reproduce the above copyright notice,\n"
- "this list of conditions and the following disclaimer in the documentation\n"
- "and/or other materials provided with the distribution.\n"
- "\n"
- "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
- "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
- "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"
- "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n"
- "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
- "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
- "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n"
- "CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
- "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
- "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n", now->tm_year+1900);
-
- printf("The following parts of the Software are separately licensed:\n"
- "- The Liberation font family is licensed under the SIL Open Font License" "(version 2 onwards)\n\n");
-}
-
-static void print_help_message()
-{
-#define explain_required_argument(c,e) printf(" %-18s%-18s%s\n", c, "REQUIRED", e);
-#define explain_other_argument(c,e) printf(" %-18s%-18s%s\n", c, " ", e);
-#define explain_optional_argument(c,d,e) printf(" %-18s%-18s%s\n", c, d, e);
-#define DEFAULT(d) "default=\""d"\""
-
- printf("Usage: text-search [OPTION] ... [OPTION] ...\n");
- printf("Example: text-search "
- "--directory \"/home/john/Documents\" --text \"homework\" --recursive 0\n");
- printf("Text-search, search for files and text within files.\n");
- printf("Matches will be printed to console in format: [path]:[line]:[filter]\n\n");
-
- printf("Available arguments:\n");
- explain_required_argument("--directory", "The directory to search");
- explain_optional_argument("--text", DEFAULT("*"), "The text to search for within files, supports wildcards '*' and '?'");
- explain_optional_argument("--filter", DEFAULT("*"), "Used to filter on specific files, supports wildcards '*' and '?'");
- explain_optional_argument("--recursive", DEFAULT("1"), "Recursively search through directories");
- explain_optional_argument("--max-file-size", DEFAULT("0"), "The maximum size, in kb, a file will be searched through for matching text. 0 for no limit");
- explain_optional_argument("--threads", DEFAULT("10"), "The number of threads used for searching, minimum of 1 thread.");
- explain_optional_argument("--locale", DEFAULT("en"), "The language errors will be reported in. Available locales are: 'en', 'nl'");
- explain_optional_argument("--export", DEFAULT(""), "Export the results to a file in json format");
-
- printf("\nOther arguments:\n");
- explain_other_argument("--help", "Display this help message");
- explain_other_argument("--license", "Display the license");
-}
-
-static bool is_valid_argument(char *arg)
-{
- if (string_equals(arg, "--directory")) return true;
- if (string_equals(arg, "--text")) return true;
- if (string_equals(arg, "--filter")) return true;
- if (string_equals(arg, "--recursive")) return true;
- if (string_equals(arg, "--max-file-size")) return true;
- if (string_equals(arg, "--threads")) return true;
- if (string_equals(arg, "--locale")) return true;
- if (string_equals(arg, "--export")) return true;
-
- return false;
-}
-#endif
-void handle_command_line_arguments(int argc, char **argv)
-{
-#if 0
- load_available_localizations();
- set_locale("en");
-
- s32 current_arg_index = 1;
- bool is_help_request = string_equals(argv[current_arg_index], "--help");
- bool is_license_request = string_equals(argv[current_arg_index], "--license");
-
- if (is_help_request)
- {
- print_help_message();
- return;
- }
- else if (is_license_request)
- {
- print_license_message();
- return;
- }
-
- char directory[MAX_INPUT_LENGTH];
- string_copyn(directory, "", MAX_INPUT_LENGTH);
-
- char text[MAX_INPUT_LENGTH];
- string_copyn(text, "*", MAX_INPUT_LENGTH);
-
- char filter[MAX_INPUT_LENGTH];
- string_copyn(filter, "*", MAX_INPUT_LENGTH);
-
- bool recursive = true;
- s32 max_file_size = 0;
- s32 threads = 10;
-
- char locale[MAX_INPUT_LENGTH];
- string_copyn(locale, "en", MAX_INPUT_LENGTH);
-
- char export_path[MAX_INPUT_LENGTH];
- string_copyn(export_path, "", MAX_INPUT_LENGTH);
-
- bool expect_argument_name = true;
- for (s32 i = current_arg_index; i < argc; i++)
- {
- if (expect_argument_name && !is_valid_argument(argv[i]))
- {
- printf("%s: %s\n", localize("invalid_argument"), argv[i]);
- }
-
- if (!expect_argument_name)
- {
- if (string_equals(argv[i-1], "--directory"))
- {
- string_copyn(directory, argv[i], MAX_INPUT_LENGTH);
- }
- if (string_equals(argv[i-1], "--text"))
- {
- string_copyn(text, argv[i], MAX_INPUT_LENGTH);
- }
- if (string_equals(argv[i-1], "--filter"))
- {
- string_copyn(filter, argv[i], MAX_INPUT_LENGTH);
- }
- if (string_equals(argv[i-1], "--recursive"))
- {
- recursive = string_to_u32(argv[i]);
- }
- if (string_equals(argv[i-1], "--max-file-size"))
- {
- max_file_size = string_to_u32(argv[i]);
- }
- if (string_equals(argv[i-1], "--threads"))
- {
- threads = string_to_u32(argv[i]);
- }
- if (string_equals(argv[i-1], "--locale"))
- {
- string_copyn(locale, argv[i], MAX_INPUT_LENGTH);
- }
- if (string_equals(argv[i-1], "--export"))
- {
- string_copyn(export_path, argv[i], MAX_INPUT_LENGTH);
- }
- }
-
- expect_argument_name = !expect_argument_name;
- }
-
- // input validation
- if (!set_locale(locale))
- {
- printf(localize("warning_locale_not_available"), locale);
- printf("\n");
- }
-
- if (string_equals(directory, ""))
- {
- printf("%s", localize("error_directory_not_specified"));
- printf("\n");
- return;
- }
-
- if (!platform_directory_exists(directory))
- {
- printf(localize("error_directory_not_found"), directory);
- printf("\n");
- return;
- }
-
- if (string_equals(text, ""))
- {
- printf("%s", localize("error_text_argument_empty"));
- printf("\n");
- return;
- }
-
- if (string_equals(filter, ""))
- {
- printf("%s", localize("error_filter_argument_empty"));
- printf("\n");
- return;
- }
-
- if (threads < 1)
- {
- printf("%s", localize("error_threads_too_low"));
- printf("\n");
- return;
- }
-
- if (!string_equals(export_path, ""))
- {
- char dir_buffer[MAX_INPUT_LENGTH];
- get_directory_from_path(dir_buffer, export_path);
-
- if (!platform_directory_exists(dir_buffer))
- {
- printf(localize("error_invalid_export_path"), dir_buffer);
- printf("\n");
- return;
- }
- }
-
- search_result *result = create_empty_search_result();
- string_copyn(result->directory_to_search, directory, MAX_INPUT_LENGTH);
- string_copyn(result->file_filter, filter, MAX_INPUT_LENGTH);
- string_copyn(result->text_to_find, text, MAX_INPUT_LENGTH);
- string_copyn(result->export_path, export_path, MAX_INPUT_LENGTH);
- result->max_thread_count = threads;
- result->max_file_size = max_file_size;
- result->is_recursive = recursive;
- result->is_command_line_search = true;
-
-
- // begin search (code below is equal to code in text_search.c)
- result->walking_file_system = true;
- result->done_finding_matches = false;
-
- result->search_result_source_dir_len = strlen(result->directory_to_search);
- result->search_result_source_dir_len = prepare_search_directory_path(result->directory_to_search,
- result->search_result_source_dir_len);
- result->start_time = platform_get_time(TIME_FULL, TIME_US);
-
- platform_list_files(&result->files, result->directory_to_search, result->file_filter, result->is_recursive, &result->mem_bucket,
- &result->cancel_search,
- &result->done_finding_files);
- find_text_in_files(result);
-
- while(!result->threads_closed) { thread_sleep(1000); }
-
- if (!string_equals(export_path, ""))
- {
- export_results(result);
- }
-#endif
-} \ No newline at end of file
diff --git a/src/command_line.h b/src/command_line.h
deleted file mode 100644
index 74daa8d..0000000
--- a/src/command_line.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_COMMAND_LINE
-#define INCLUDE_COMMAND_LINE
-
-void handle_command_line_arguments(int argc, char **argv);
-
-#endif \ No newline at end of file
diff --git a/src/config.h b/src/config.h
index 51b6563..4bd9e8b 100644
--- a/src/config.h
+++ b/src/config.h
@@ -19,4 +19,8 @@
#define UNSAVED_CHANGES_COLOR rgb(255, 102, 102)
#define MISSING_TRANSLATION_COLOR rgb(255, 179, 102)
+#define ASSET_IMAGE_COUNT 10
+#define ASSET_FONT_COUNT 10
+#define ASSET_QUEUE_COUNT 20
+
#endif \ No newline at end of file
diff --git a/src/external/LooplessSizeMove.c b/src/external/LooplessSizeMove.c
deleted file mode 100644
index 1843cb9..0000000
--- a/src/external/LooplessSizeMove.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- LooplessSizeMove.c
- Implements functions for modal-less window resizing and movement in Windows
-
- Author: Nathaniel J Fries
-
- The author asserts no copyright, this work is released into the public domain.
-*/
-
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#ifdef TIME_LOOP
-#include <stdio.h>
-#endif /* TIME_LOOP */
-
-/* fills the MINMAXINFO structure pointed to by the second argument with
- default MIMAX values, then sends WM_GETMINMAXINFO to allow the
- user's Window Procedure to modify it.
-*/
-void GetMinMaxInfo(HWND hwnd, PMINMAXINFO info);
-/* begins the loopless resize/move process */
-LRESULT PrepareSizeMove(HWND hwnd, WPARAM action, DWORD dwPos);
-/* stops the loopless resize/move process.
- if cancel is TRUE, restores window size and position to
- what they were before resizing/moving.
-*/
-void StopSizing(BOOL cancel);
-/* see LooplessSizeMove.h */
-LRESULT CALLBACK LSMProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-BOOLEAN SizingCheck(const MSG *lpmsg);
-
-#define GetWindowLongAW(hwnd, lp)\
-(IsWindowUnicode(hwnd)) ? \
-(GetWindowLongW(hwnd, lp)) : \
-(GetWindowLongA(hwnd, lp))
-#define SendMessageAW(hwnd, msg, wParam, lParam)\
-(IsWindowUnicode(hwnd)) ? \
-(SendMessageW(hwnd, msg, wParam, lParam)) : \
-(SendMessageA(hwnd, msg, wParam, lParam))
-#define PostMessageAW(hwnd, msg, wParam, lParam)\
-(IsWindowUnicode(hwnd)) ? \
-(PostMessageW(hwnd, msg, wParam, lParam)) : \
-(PostMessageA(hwnd, msg, wParam, lParam))
-#define DefWindowProcAW(hwnd, msg, wParam, lParam)\
-(IsWindowUnicode(hwnd)) ? \
-(DefWindowProcW(hwnd, msg, wParam, lParam)) : \
-(DefWindowProcA(hwnd, msg, wParam, lParam))
-
-#define RECTWIDTH(r) ((r).right - (r).left)
-#define RECTHEIGHT(r) ((r).bottom - (r).top)
-
-#define LSM_LEFT 0x01
-#define LSM_TOP 0x02
-#define LSM_RIGHT 0x04
-#define LSM_BOTTOM 0x08
-#define LSM_CAPTION 0x00
-#define LSM_NOGRAB 0xF0
-
-#define LSM_SHAKE_MINTIME 20 /* minimum time between bothering */
-#define LSM_SHAKE_MAXTIME 1000 /* maximum time between movements for "shake" effect */
-#define LSM_SHAKE_STATE_MAXIMIZED 1
-#define LSM_SNAP_HELPER_CLASS "LSM_SNAP_HELPER"
-/*
- information required to reverse a "shake" action.
-*/
-typedef struct _SHAKERESTORENODE
-{
- HWND hwnd;
- WINDOWPLACEMENT place;
- struct _SHAKERESTORENODE *next;
-} SHAKERESTORENODE, *PSHAKERESTORENODE;
-typedef struct
-{
- DWORD dwPrevTime;
- WORD wDir;
- WORD wCount;
- SHAKERESTORENODE *restoreList;
-} SHAKEDATA;
-typedef BOOL (*SHAKEFOREACHFN)(PSHAKERESTORENODE, LPARAM);
-
-typedef struct
-{
- HWND helperWindow;
- WORD isSnapped;
- WORD snapType; /* LSM_TOP, LSM_LEFT, LSM_RIGHT */
- RECT rcWork;
- RECT rcRestore;
-} SNAPDATA;
-
-/* holds all data that needs to be held on to for resizing */
-typedef struct
-{
- HWND hwnd;
- MINMAXINFO minmax;
- RECT rcWin;
- RECT rcOrig;
- POINT ptCapture;
- LONG grab;
- SHAKEDATA shake;
- SNAPDATA snap;
-} SIZEMOVEDATA;
-
-DWORD dwSizeMoveTlsIndex = 0;
-#define LSMTlsCheck() if(dwSizeMoveTlsIndex == 0){ dwSizeMoveTlsIndex = TlsAlloc(); }
-#define LSMSet(data) TlsSetValue(dwSizeMoveTlsIndex, data)
-#define LSMGet() TlsGetValue(dwSizeMoveTlsIndex)
-
-void GetMinMaxInfo(HWND hwnd, PMINMAXINFO info)
-{
- RECT rc;
- LONG style = GetWindowLongAW(hwnd, GWL_STYLE);
- LONG altStyle = ((style & WS_CAPTION) == WS_CAPTION)?
- (style & ~WS_BORDER):(style);
-
- /* calculate the default values in case WindowProc does not respond */
- GetClientRect(GetParent(hwnd), &rc);
- AdjustWindowRectEx(&rc, altStyle, ((style & WS_POPUP) && GetMenu(hwnd)),
- GetWindowLongAW(hwnd, GWL_EXSTYLE));
- info->ptMaxPosition.x = rc.left;
- info->ptMaxPosition.y = rc.top;
- info->ptMaxSize.x = rc.right - rc.left;
- info->ptMaxSize.y = rc.bottom - rc.top;
- if(style & WS_CAPTION)
- {
- info->ptMinTrackSize.x = GetSystemMetrics(SM_CXMINTRACK);
- info->ptMaxTrackSize.y = GetSystemMetrics(SM_CYMINTRACK);
- }
- else
- {
- /* why not zero? this is what ReactOS and presumably Wine do,
- and they're the experts at replicating Windows UI behavior */
- info->ptMinTrackSize.x = info->ptMaxPosition.x * -2;
- info->ptMaxTrackSize.y = info->ptMaxPosition.y * -2;
- }
- info->ptMaxTrackSize.x = GetSystemMetrics(SM_CXMAXTRACK);
- info->ptMaxTrackSize.y = GetSystemMetrics(SM_CYMAXTRACK);
-
- /* ask Window proc to make any changes */
- SendMessageAW(hwnd, WM_GETMINMAXINFO, 0, (LPARAM)info);
-}
-
-BOOL ForEachShakeNodeFree(PSHAKERESTORENODE node, LPARAM ignore)
-{
- LocalFree(node);
- return TRUE;
-}
-BOOL ForEachShakeNode(SHAKEDATA *pShake, SHAKEFOREACHFN fn, LPARAM lParam)
-{
- SHAKERESTORENODE *next, *curr;
- BOOL res = TRUE;
- if(pShake->restoreList)
- {
- curr = pShake->restoreList;
- while(res && curr)
- {
- next = curr->next;
- fn(curr, lParam);
- curr = next;
- }
- }
- return res;
-}
-
-void SnapCleanup(SIZEMOVEDATA *sizemove)
-{
- ShowWindow(sizemove->snap.helperWindow, SW_HIDE);
- ZeroMemory(&sizemove->snap.rcWork, sizeof(RECT));
-}
-
-LRESULT WINAPI SnapHelperWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch(msg)
- {
- case WM_PAINT:
- {
- SIZEMOVEDATA *sizemove = LSMGet();
- HDC hDC = GetDC(hwnd);
- HPEN hPen = CreatePen(PS_INSIDEFRAME, 1, GetSysColor(COLOR_HIGHLIGHT));
- LOGBRUSH blog = { BS_HOLLOW, 0, 0 };
- HBRUSH hBrush = CreateBrushIndirect(&blog);
- if(!sizemove || !hDC || !hPen || !hBrush)
- break;
-
- SelectObject(hDC, hPen);
- SelectObject(hDC, hBrush);
-
- Rectangle(hDC, 0, 0, RECTWIDTH(sizemove->snap.rcWork), RECTHEIGHT(sizemove->snap.rcWork));
-
- DeleteObject(hBrush);
- DeleteObject(hPen);
- break;
- }
- default:
- {
- break;
- }
- }
- return DefWindowProcA(hwnd, msg, wParam, lParam);
-}
-
-LRESULT PrepareSizeMove(HWND hwnd, WPARAM action, DWORD dwPos)
-{
- WINDOWINFO winfo;
- SIZEMOVEDATA *sm;
- RECT rcClipCursor;
-
- winfo.cbSize = sizeof(WINDOWINFO);
- /* most likely not a valid window */
- if(GetWindowInfo(hwnd, &winfo) == FALSE)
- return 0;
- /* can't move or resize an invisible window */
- if(!IsWindowVisible(hwnd))
- return 0;
- /* can't resize a window without the resizing border */
- if((action & 0xfff0) == SC_MOVE && !(winfo.dwStyle & WS_SIZEBOX))
- return 0;
-
- /*
- if another window on this thread has capture,
- it might be using this too...
- tell it to clean up before setting the tls value
- */
- ReleaseCapture();
-
- if(!(sm = LSMGet()))
- {
- WNDCLASSA wndcls;
- HINSTANCE hInstance = GetModuleHandleA(NULL);
- sm = LocalAlloc(0, sizeof(SIZEMOVEDATA));
- if(!sm)
- {
- /* error */
- return 1;
- }
- if(!LSMSet(sm))
- {
- LocalFree(sm);
- return 2;
- }
- /* prevent potential crashes and bad initialization bugs */
- ZeroMemory(sm, sizeof(SIZEMOVEDATA));
-
- if(!GetClassInfoA(hInstance, LSM_SNAP_HELPER_CLASS, &wndcls))
- {
- ZeroMemory(&wndcls, sizeof(WNDCLASSA));
- wndcls.style = CS_SAVEBITS;
- wndcls.hbrBackground = GetSysColorBrush(COLOR_HOTLIGHT);
- wndcls.lpszClassName = LSM_SNAP_HELPER_CLASS;
- wndcls.lpfnWndProc = SnapHelperWinProc;
- wndcls.hInstance = hInstance;
- RegisterClassA(&wndcls);
- }
- /* WS_EX_LAYERED: window is not fully opaque
- WS_EX_TRANSPARENT: mouse events pass through layered window
- ES_EX_NOACTIVATE: window cannot be activated by user (no loss of mouse capture or keyboard focus)
- */
- sm->snap.helperWindow = CreateWindowExA(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE,
- LSM_SNAP_HELPER_CLASS, "", WS_POPUP /* no borders */, 0, 0, 100, 100, NULL, NULL, hInstance, NULL);
- if(sm->snap.helperWindow)
- {
- SetLayeredWindowAttributes(sm->snap.helperWindow, 0, 75, LWA_ALPHA);
- }
- }
- else if(sm->hwnd != hwnd)
- {
- /* forget shake data, consistent with Windows 7 behavior */
- ForEachShakeNode(&sm->shake, ForEachShakeNodeFree, 0);
- SnapCleanup(sm);
- sm->shake.restoreList = NULL;
- }
- sm->grab = action & 0x000f;
- sm->hwnd = hwnd;
- GetMinMaxInfo(hwnd, &sm->minmax);
- sm->rcWin = winfo.rcWindow;
- if(winfo.dwStyle & WS_CHILD)
- {
- /* map points into the parent's coordinate space */
- HWND parent = GetParent(hwnd);
- MapWindowPoints(0, parent, (LPPOINT)&sm->rcWin, 2);
- GetWindowRect(parent, &rcClipCursor);
- MapWindowPoints(parent, HWND_DESKTOP, (LPPOINT)&rcClipCursor, 2);
- }
- else if(!(winfo.dwExStyle & WS_EX_TOPMOST))
- {
- SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcClipCursor, 0);
- }
- else
- {
- rcClipCursor.left = rcClipCursor.top = 0;
- rcClipCursor.right = GetSystemMetrics(SM_CXSCREEN);
- rcClipCursor.bottom = GetSystemMetrics(SM_CYSCREEN);
- }
- sm->rcOrig = sm->rcWin;
-
- sm->ptCapture.x = (short)LOWORD(dwPos);
- sm->ptCapture.y = (short)HIWORD(dwPos);
- //ClipCursor(&rcClipCursor);
-
-
- /* notify WinProc we're beginning, but return instead of looping */
- SendMessageAW(hwnd, WM_ENTERSIZEMOVE, 0, 0);
- if(GetCapture() != hwnd)
- {
- SetCapture(hwnd);
- }
- return 0;
-}
-
-LRESULT CALLBACK LSMProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- LSMTlsCheck();
- switch(msg)
- {
- case WM_NCLBUTTONDOWN:
- {
- switch(wParam)
- {
- case HTLEFT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_LEFT, lParam);
- case HTTOPLEFT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_LEFT | LSM_TOP, lParam);
- case HTBOTTOMLEFT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_LEFT | LSM_BOTTOM, lParam);
- case HTRIGHT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_RIGHT, lParam);
- case HTTOPRIGHT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_RIGHT | LSM_TOP, lParam);
- case HTBOTTOMRIGHT:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_RIGHT | LSM_BOTTOM, lParam);
- case HTTOP:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_TOP, lParam);
- case HTBOTTOM:
- return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_SIZE | LSM_BOTTOM, lParam);
- case HTCAPTION:
- //return SendMessageAW(hwnd, WM_SYSCOMMAND, SC_MOVE | LSM_CAPTION, lParam);
- default:
- break;
- }
- }
- case WM_SYSCOMMAND:
- {
- switch(wParam & 0xfff0)
- {
- case SC_SIZE:
- /* begin resize 'loop' */
- return PrepareSizeMove(hwnd, wParam, lParam);
- default:
- break;
- }
- break;
- }
- case WM_CAPTURECHANGED:
- {
- /* nothing we can do; stop resizing & do clean-up */
- SIZEMOVEDATA *sizemove = LSMGet();
- if(sizemove && (HWND)lParam != sizemove->hwnd)
- {
- SendMessageAW(sizemove->hwnd, WM_EXITSIZEMOVE, 0, 0);
- ClipCursor(NULL);
- /* no longer unset LSM data:
- 1) never really needed to
- 2) necessary to hold onto shake data
- */
- if(GetForegroundWindow() != sizemove->hwnd)
- {
- /* forget shake data, consistent with Windows 7 behavior */
- ForEachShakeNode(&sizemove->shake, ForEachShakeNodeFree, 0);
- sizemove->shake.restoreList = NULL;
- }
- sizemove->shake.wCount = 0;
- sizemove->shake.wDir = 0;
- sizemove->shake.dwPrevTime = 0;
- SnapCleanup(sizemove);
-
- sizemove->grab = LSM_NOGRAB;
- }
- }
- default:
- break;
- }
- return DefWindowProcAW(hwnd, msg, wParam, lParam);
-}
-
-BOOL CALLBACK EnumWindowsShakeMinimize(HWND hwnd, LPARAM lParam)
-{
- SIZEMOVEDATA *sizemove = (SIZEMOVEDATA *)(lParam);
- if(hwnd != sizemove->hwnd && IsWindowVisible(hwnd) && !IsIconic(hwnd))
- {
- SHAKERESTORENODE *pNode = 0;
- WINDOWPLACEMENT tPlace;
- /* note: there are a few shell-created windows that glitch up graphically if you try to minimize them */
- DWORD dwProcID, dwShellPID;
- GetWindowThreadProcessId(hwnd, &dwProcID);
- GetWindowThreadProcessId(GetShellWindow(), &dwShellPID);
- if(dwProcID == dwShellPID)
- {
- return TRUE;
- }
- /* note: not supposed to minimize "parent application" windows. I assume this means windows of
- the window's application?
- See: http://social.technet.microsoft.com/Forums/windows/en-US/b047378c-2a5a-4661-a871-cec459cb9bdc/aeroshake-not-working?forum=w7itproui
- */
- GetWindowThreadProcessId(sizemove->hwnd, &dwShellPID);
- if(dwProcID == dwShellPID)
- {
- return TRUE;
- }
-
-
- pNode = LocalAlloc(0, sizeof(SHAKERESTORENODE));
- if(!pNode)
- return FALSE;
-
- pNode->hwnd = hwnd;
-
- pNode->place.length = sizeof(WINDOWPLACEMENT);
- pNode->place.flags = WPF_ASYNCWINDOWPLACEMENT;
- GetWindowPlacement(hwnd, &pNode->place);
- tPlace = pNode->place;
- tPlace.showCmd = SW_SHOWMINNOACTIVE;
- SetWindowPlacement(hwnd, &tPlace);
-
- pNode->next = sizemove->shake.restoreList;
- sizemove->shake.restoreList = pNode;
- }
- return TRUE;
-}
-
-BOOL ForEachShakeNodeRestore(PSHAKERESTORENODE node, LPARAM ignore)
-{
- if(node)
- {
- SetWindowPlacement(node->hwnd, &node->place);
- LocalFree(node);
- return TRUE;
- }
- return FALSE;
-}
-
-void ShakeCheck(const MSG *lpmsg, SIZEMOVEDATA *sizemove)
-{
- static DWORD dwShakeDisabled = 2;
- WORD dir = 0;
- int dx, dy;
- if(lpmsg->message != WM_MOUSEMOVE && lpmsg->message != WM_NCMOUSEMOVE)
- return;
- if(dwShakeDisabled == 2)
- {
- HKEY hKey;
- DWORD dwSizeDW = 4;
- DWORD dwType = REG_DWORD;
- if(/* major version number */LOBYTE(LOWORD(GetVersion())) < 6)
- {
- dwShakeDisabled = 1;
- }
- else
- {
- dwShakeDisabled = 0;
- if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Policies\\Microsoft\\Windows\\Explorer", 0, KEY_READ, &hKey)
- == ERROR_SUCCESS)
- {
- RegQueryValueExA(hKey, "NoWindowMinimizingShortcuts", 0, &dwType, (LPBYTE)&dwShakeDisabled, &dwSizeDW);
- RegCloseKey(hKey);
- }
- }
- }
- if(dwShakeDisabled)
- return;
- if(lpmsg->time - sizemove->shake.dwPrevTime < LSM_SHAKE_MINTIME)
- return;
-
- dx = lpmsg->pt.x - sizemove->ptCapture.x;
- dy = lpmsg->pt.y - sizemove->ptCapture.y;
- if(dx > 8)
- dir |= LSM_LEFT;
- else if(dx < -8)
- dir |= LSM_RIGHT;
- if(dy > 8)
- dir |= LSM_TOP;
- else if(dy < -8)
- dir |= LSM_BOTTOM;
-
- if(dir && ((lpmsg->time - sizemove->shake.dwPrevTime) < LSM_SHAKE_MAXTIME) && (dir != sizemove->shake.wDir))
- {
- /* Do Shake */
- sizemove->shake.wCount++;
- if(sizemove->shake.wCount >= 3)
- {
- if(sizemove->shake.restoreList)
- {
- ForEachShakeNode(&sizemove->shake, ForEachShakeNodeRestore, (LPARAM)sizemove);
- sizemove->shake.restoreList = 0;
- }
- else
- {
- EnumWindows(EnumWindowsShakeMinimize, (LPARAM)(sizemove));
- }
- RedrawWindow(GetDesktopWindow(), NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INTERNALPAINT | RDW_INVALIDATE
- | RDW_ALLCHILDREN | RDW_UPDATENOW);
- sizemove->shake.wCount = 0;
- }
- }
- else
- {
- sizemove->shake.wCount = 0;
- }
- sizemove->shake.dwPrevTime = lpmsg->time;
- sizemove->shake.wDir = dir;
-}
-
-void SnapCheck(POINT pt, SIZEMOVEDATA *sizemove)
-{
- static DWORD dwSnapActive = 2;
- HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
- MONITORINFO minfo;
-
- if(dwSnapActive == 2)
- {
- HKEY hKey;
- DWORD dwSizeDW = 4;
- DWORD dwType = REG_DWORD;
- dwSnapActive = 0;
- if(/* major version number */LOBYTE(LOWORD(GetVersion())) >= 6)
- {
- if(RegOpenKeyExA(HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, KEY_READ, &hKey)
- == ERROR_SUCCESS)
- {
- RegQueryValueExA(hKey, "WindowArrangementActive", 0, &dwType, (LPBYTE)&dwSnapActive, &dwSizeDW);
- RegCloseKey(hKey);
- }
- }
- }
- if(!dwSnapActive)
- return;
-
- minfo.cbSize = sizeof(MONITORINFO);
- if(monitor == NULL)
- return;
- if(!GetMonitorInfo(monitor, (LPMONITORINFO)&minfo))
- return;
- /* unsnap maximized windows */
- if(IsZoomed(sizemove->hwnd))
- {
- WINDOWPLACEMENT place;
- int dx, w;
- place.length = sizeof(WINDOWPLACEMENT);
- GetWindowPlacement(sizemove->hwnd, &place);
- place.showCmd = SW_RESTORE;
- OffsetRect(&place.rcNormalPosition, (sizemove->rcWin.left - place.rcNormalPosition.left),
- (sizemove->rcWin.top - place.rcNormalPosition.top));
- w = RECTWIDTH(place.rcNormalPosition);
- if((dx = (place.rcNormalPosition.left - sizemove->ptCapture.x)) > 0)
- {
- place.rcNormalPosition.left += dx << 1;
- place.rcNormalPosition.right = place.rcNormalPosition.left + w;
- }
- if((dx = (sizemove->ptCapture.x - place.rcNormalPosition.right)) > 0)
- {
- place.rcNormalPosition.left += dx << 1;
- place.rcNormalPosition.right = place.rcNormalPosition.left + w;
- }
- sizemove->rcWin = place.rcNormalPosition;
- SetWindowPlacement(sizemove->hwnd, &place);
- /* don't return here, it's possible to unsnap without leaving the top of the screen */
- }
- else if(sizemove->snap.isSnapped)
- {
- /* TODO: Aero snap lets you drag half-screen snapped windows across the top of the screen */
-
- sizemove->rcWin.right = sizemove->snap.rcRestore.right;
- sizemove->rcWin.bottom = sizemove->snap.rcRestore.bottom;
- sizemove->snap.isSnapped = 0;
- return;
- }
- sizemove->snap.rcWork = minfo.rcWork;
- if(pt.x <= minfo.rcWork.left)
- {
- /* left side stretch */
- sizemove->snap.rcWork.right = (RECTWIDTH(minfo.rcWork) - GetSystemMetrics(SM_CXFRAME)) >> 1;
- sizemove->snap.snapType = LSM_LEFT;
- }
- else if(pt.x+1 >= minfo.rcWork.right)
- {
- /* right side stretch */
- sizemove->snap.rcWork.left = (minfo.rcWork.left + RECTWIDTH(minfo.rcWork) + GetSystemMetrics(SM_CXFRAME)) >> 1;
- sizemove->snap.snapType = LSM_RIGHT;
- }
- else if(pt.y <= minfo.rcWork.top)
- {
- /* full stretch */
- sizemove->snap.snapType = LSM_TOP;
- }
- else
- {
- /* not snapping, clean up snap state */
- SnapCleanup(sizemove);
- return;
- }
- SetWindowPos(sizemove->snap.helperWindow, HWND_TOPMOST,
- sizemove->snap.rcWork.left, sizemove->snap.rcWork.top,
- RECTWIDTH(sizemove->snap.rcWork), RECTHEIGHT(sizemove->snap.rcWork),
- SWP_NOACTIVATE);
- ShowWindow(sizemove->snap.helperWindow, SW_SHOWNA);
-}
-
-void SnapFinalize(SIZEMOVEDATA *sizemove)
-{
- if(sizemove->snap.rcWork.left != sizemove->snap.rcWork.right
- && sizemove->snap.rcWork.top != sizemove->snap.rcWork.bottom)
- {
- if(sizemove->snap.snapType == LSM_TOP)
- {
- /* Aero appears to animate this... so we will too */
- ShowWindow(sizemove->hwnd, SW_MAXIMIZE);
- }
- else
- {
- SetWindowPos(sizemove->hwnd, 0, sizemove->snap.rcWork.left, sizemove->snap.rcWork.top,
- RECTWIDTH(sizemove->snap.rcWork), RECTHEIGHT(sizemove->snap.rcWork), 0);
- ZeroMemory(&sizemove->snap.rcRestore, sizeof(RECT));
- sizemove->snap.rcRestore.right = RECTWIDTH(sizemove->rcWin);
- sizemove->snap.rcRestore.bottom = RECTWIDTH(sizemove->rcWin);
- sizemove->snap.isSnapped = 1;
- }
- }
- SnapCleanup(sizemove);
-}
-
-BOOLEAN SizingCheck(const MSG *lpmsg)
-{
- SIZEMOVEDATA *sizemove = LSMGet();
- POINT pt = lpmsg->pt;
- int dx = 0, dy = 0;
- /*
- Discussion of rev3 changes.
- There was a bug in previous revisions that would cause
- the resize state to continue even if the Window lost
- mouse capture. Windows provides notification of losing
- mouse capture, but it crashes the program to take capture
- back while processing that message.
- So, we choose to yield to this other program and stop resizing.
- This is probably user32 behavior anyway.
- Windows also sends this notification in response to calling ReleaseCapture,
- so all clean-up code has been moved to the handler.
- This also allowed us to eliminate the function StopSizing.
- */
- if(!sizemove) /* not sizing */
- return 0;
- if(sizemove->grab == LSM_NOGRAB) /* not sizing */
- return 0;
- if(lpmsg->hwnd != sizemove->hwnd) /* wrong window */
- return 0;
- if(lpmsg->message == WM_NCLBUTTONUP || lpmsg->message == WM_LBUTTONUP)
- {
- SnapFinalize(sizemove);
- ReleaseCapture();
- return 1;
- }
- if(lpmsg->message == WM_KEYDOWN)
- {
- switch(lpmsg->wParam)
- {
- case VK_RETURN:
- ReleaseCapture();
- return 1;
- case VK_ESCAPE:
- {
- SetWindowPos(sizemove->hwnd, 0, sizemove->rcOrig.left, sizemove->rcOrig.top,
- RECTWIDTH(sizemove->rcOrig), RECTHEIGHT(sizemove->rcOrig), 0);
- ReleaseCapture();
- return 1;
- }
- case VK_UP:
- pt.y-=8;
- break;
- case VK_DOWN:
- pt.y+=8;
- break;
- case VK_LEFT:
- pt.x-=8;
- break;
- case VK_RIGHT:
- pt.x+=8;
- break;
- default:
- break;
- }
- }
-
- /* used to handle WM_MOUSEMOVE. This was unnecessary code */
-
- dx = pt.x - sizemove->ptCapture.x;
- dy = pt.y - sizemove->ptCapture.y;
- if(dx || dy)
- {
- BOOL changeCursor = (lpmsg->message == WM_KEYDOWN);
- WPARAM wpHit = 0;
-#ifdef TIME_LOOP
- LARGE_INTEGER pfBegin;
- LARGE_INTEGER pfDraw;
- LARGE_INTEGER pfFinal;
- LARGE_INTEGER pfFreq;
- QueryPerformanceFrequency(&pfFreq);
- QueryPerformanceCounter(&pfBegin);
-#endif
-
- if(sizemove->grab == LSM_CAPTION)
- {
- ShakeCheck(lpmsg, sizemove);
- SnapCheck(pt, sizemove);
- OffsetRect(&sizemove->rcWin, dx, dy);
- }
- else
- {
- /* note on minmax correction
- if you do not correct the capture pos (set later from `pt`),
- window will expand massively if user pulls back mouse
- after failing to shrink when resizing from the
- bottom or the right borders.
- */
- /* when resizing using keys, Windows also moves the cursor */
- if(sizemove->grab & LSM_LEFT)
- {
- int lmax = sizemove->rcWin.right - sizemove->minmax.ptMaxTrackSize.x;
- int lmin = sizemove->rcWin.right - sizemove->minmax.ptMinTrackSize.x;
- if(sizemove->rcWin.left + dx < lmax)
- {
- sizemove->rcWin.left = lmax;
- }
- else if(sizemove->rcWin.left + dx > lmin)
- {
- sizemove->rcWin.left = lmin;
- }
- else
- {
- sizemove->rcWin.left += dx;
- }
- pt.x = sizemove->rcWin.left;
- wpHit = WMSZ_LEFT;
- }
- else if(sizemove->grab & LSM_RIGHT)
- {
- int rmax = sizemove->rcWin.left + sizemove->minmax.ptMaxTrackSize.x;
- int rmin = sizemove->rcWin.left + sizemove->minmax.ptMinTrackSize.x;
- if(sizemove->rcWin.right + dx > rmax)
- {
- sizemove->rcWin.right = rmax;
- }
- else if(sizemove->rcWin.right + dx < rmin)
- {
- sizemove->rcWin.right = rmin;
- }
- else
- {
- sizemove->rcWin.right += dx;
- }
- pt.x = sizemove->rcWin.right;
- wpHit = WMSZ_RIGHT;
- }
- if(sizemove->grab & LSM_TOP)
- {
- int tmax = sizemove->rcWin.bottom - sizemove->minmax.ptMaxTrackSize.y;
- int tmin = sizemove->rcWin.bottom - sizemove->minmax.ptMinTrackSize.y;
- if(sizemove->rcWin.top + dy < tmax)
- {
- sizemove->rcWin.top = tmax;
- }
- else if(sizemove->rcWin.top + dy > tmin)
- {
- sizemove->rcWin.top = tmin;
- }
- else
- {
- sizemove->rcWin.top += dy;
- }
- pt.y = sizemove->rcWin.top;
- if(wpHit == WMSZ_LEFT)
- {
- wpHit = WMSZ_TOPLEFT;
- }
- else if(wpHit == WMSZ_RIGHT)
- {
- wpHit = WMSZ_TOPRIGHT;
- }
- else
- {
- wpHit = WMSZ_TOP;
- }
- }
- else if(sizemove->grab & LSM_BOTTOM)
- {
- int bmax = sizemove->rcWin.top + sizemove->minmax.ptMaxTrackSize.y;
- int bmin = sizemove->rcWin.top + sizemove->minmax.ptMinTrackSize.y;
- if(sizemove->rcWin.bottom + dy > bmax)
- {
- sizemove->rcWin.bottom = bmax;
- }
- else if(sizemove->rcWin.bottom + dy < bmin)
- {
- sizemove->rcWin.bottom = bmin;
- }
- else
- {
- sizemove->rcWin.bottom += dy;
- }
- pt.y = sizemove->rcWin.bottom;
- if(wpHit == WMSZ_LEFT)
- {
- wpHit = WMSZ_BOTTOMLEFT;
- }
- else if(wpHit == WMSZ_RIGHT)
- {
- wpHit = WMSZ_BOTTOMRIGHT;
- }
- else
- {
- wpHit = WMSZ_BOTTOM;
- }
- }
- }
-#ifdef TIME_LOOP
- QueryPerformanceCounter(&pfDraw);
-#endif
- SendMessageAW(sizemove->hwnd, WM_SIZING, wpHit, (LPARAM)&sizemove->rcWin);
- SetWindowPos(sizemove->hwnd, 0, sizemove->rcWin.left, sizemove->rcWin.top,
- RECTWIDTH(sizemove->rcWin), RECTHEIGHT(sizemove->rcWin), 0);
-
- sizemove->ptCapture = pt;
- /* when resizing using keys, Windows also moves the cursor */
- if(changeCursor)
- SetCursorPos(pt.x, pt.y);
-#ifdef TIME_LOOP
- QueryPerformanceCounter(&pfFinal);
- if(1)
- {
- double msTotal, msDraw;
- msTotal = (double)(*(long long*)(&pfFinal) - *(long long *)(&pfBegin)) / (*(long long *)(&pfFreq) / 1000);
- msDraw = (double)(*(long long*)(&pfFinal) - *(long long *)(&pfDraw)) / (*(long long *)(&pfFreq) / 1000);
- printf("draw time: %.03fms\ntotal: %.03fms\n", msDraw, msTotal);
- }
-#endif
- }
- return 1;
-}
-
-void LSMCleanup()
-{
- SIZEMOVEDATA *sm = LSMGet();
- if(sm)
- {
- if(sm->shake.restoreList)
- {
- ForEachShakeNode(&sm->shake, ForEachShakeNodeFree, 0);
- }
- if(sm->snap.helperWindow)
- {
- DestroyWindow(sm->snap.helperWindow);
- }
- LocalFree(sm);
- LSMSet(NULL);
- }
-}
diff --git a/src/external/cJSON.c b/src/external/cJSON.c
deleted file mode 100644
index ded7006..0000000
--- a/src/external/cJSON.c
+++ /dev/null
@@ -1,2980 +0,0 @@
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-/* cJSON */
-/* JSON parser in C. */
-
-/* disable warnings about old C89 functions in MSVC */
-#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
-#define _CRT_SECURE_NO_DEPRECATE
-#endif
-
-#ifdef __GNUC__
-#pragma GCC visibility push(default)
-#endif
-#if defined(_MSC_VER)
-#pragma warning (push)
-/* disable warning about single line comments in system headers */
-#pragma warning (disable : 4001)
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <ctype.h>
-
-#ifdef ENABLE_LOCALES
-#include <locale.h>
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-#ifdef __GNUC__
-#pragma GCC visibility pop
-#endif
-
-#include "cJSON.h"
-
-/* define our own boolean type */
-#ifdef true
-#undef true
-#endif
-#define true ((cJSON_bool)1)
-
-#ifdef false
-#undef false
-#endif
-#define false ((cJSON_bool)0)
-
-typedef struct {
- const unsigned char *json;
- size_t position;
-} error;
-static error global_error = { NULL, 0 };
-
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
-{
- return (const char*) (global_error.json + global_error.position);
-}
-
-CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) {
- if (!cJSON_IsString(item)) {
- return NULL;
- }
-
- return item->valuestring;
-}
-
-/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
-#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12)
- #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
-#endif
-
-CJSON_PUBLIC(const char*) cJSON_Version(void)
-{
- static char version[15];
- sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
-
- return version;
-}
-
-/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
-static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
-{
- if ((string1 == NULL) || (string2 == NULL))
- {
- return 1;
- }
-
- if (string1 == string2)
- {
- return 0;
- }
-
- for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
- {
- if (*string1 == '\0')
- {
- return 0;
- }
- }
-
- return tolower(*string1) - tolower(*string2);
-}
-
-typedef struct internal_hooks
-{
- void *(CJSON_CDECL *allocate)(size_t size);
- void (CJSON_CDECL *deallocate)(void *pointer);
- void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
-} internal_hooks;
-
-#if defined(_MSC_VER)
-/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
-static void * CJSON_CDECL internal_malloc(size_t size)
-{
- return malloc(size);
-}
-static void CJSON_CDECL internal_free(void *pointer)
-{
- free(pointer);
-}
-static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
-{
- return realloc(pointer, size);
-}
-#else
-#define internal_malloc malloc
-#define internal_free free
-#define internal_realloc realloc
-#endif
-
-/* strlen of character literals resolved at compile time */
-#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
-
-static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
-
-static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
-{
- size_t length = 0;
- unsigned char *copy = NULL;
-
- if (string == NULL)
- {
- return NULL;
- }
-
- length = strlen((const char*)string) + sizeof("");
- copy = (unsigned char*)hooks->allocate(length);
- if (copy == NULL)
- {
- return NULL;
- }
- memcpy(copy, string, length);
-
- return copy;
-}
-
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
-{
- if (hooks == NULL)
- {
- /* Reset hooks */
- global_hooks.allocate = malloc;
- global_hooks.deallocate = free;
- global_hooks.reallocate = realloc;
- return;
- }
-
- global_hooks.allocate = malloc;
- if (hooks->malloc_fn != NULL)
- {
- global_hooks.allocate = hooks->malloc_fn;
- }
-
- global_hooks.deallocate = free;
- if (hooks->free_fn != NULL)
- {
- global_hooks.deallocate = hooks->free_fn;
- }
-
- /* use realloc only if both free and malloc are used */
- global_hooks.reallocate = NULL;
- if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
- {
- global_hooks.reallocate = realloc;
- }
-}
-
-/* Internal constructor. */
-static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
-{
- cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
- if (node)
- {
- memset(node, '\0', sizeof(cJSON));
- }
-
- return node;
-}
-
-/* Delete a cJSON structure. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
-{
- cJSON *next = NULL;
- while (item != NULL)
- {
- next = item->next;
- if (!(item->type & cJSON_IsReference) && (item->child != NULL))
- {
- cJSON_Delete(item->child);
- }
- if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
- {
- global_hooks.deallocate(item->valuestring);
- }
- if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
- {
- global_hooks.deallocate(item->string);
- }
- global_hooks.deallocate(item);
- item = next;
- }
-}
-
-/* get the decimal point character of the current locale */
-static unsigned char get_decimal_point(void)
-{
-#ifdef ENABLE_LOCALES
- struct lconv *lconv = localeconv();
- return (unsigned char) lconv->decimal_point[0];
-#else
- return '.';
-#endif
-}
-
-typedef struct
-{
- const unsigned char *content;
- size_t length;
- size_t offset;
- size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
- internal_hooks hooks;
-} parse_buffer;
-
-/* check if the given size is left to read in a given parse buffer (starting with 1) */
-#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
-/* check if the buffer can be accessed at the given index (starting with 0) */
-#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
-#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
-/* get a pointer to the buffer at the position */
-#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
-
-/* Parse the input text to generate a number, and populate the result into item. */
-static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
-{
- double number = 0;
- unsigned char *after_end = NULL;
- unsigned char number_c_string[64];
- unsigned char decimal_point = get_decimal_point();
- size_t i = 0;
-
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false;
- }
-
- /* copy the number into a temporary buffer and replace '.' with the decimal point
- * of the current locale (for strtod)
- * This also takes care of '\0' not necessarily being available for marking the end of the input */
- for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
- {
- switch (buffer_at_offset(input_buffer)[i])
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '+':
- case '-':
- case 'e':
- case 'E':
- number_c_string[i] = buffer_at_offset(input_buffer)[i];
- break;
-
- case '.':
- number_c_string[i] = decimal_point;
- break;
-
- default:
- goto loop_end;
- }
- }
-loop_end:
- number_c_string[i] = '\0';
-
- number = strtod((const char*)number_c_string, (char**)&after_end);
- if (number_c_string == after_end)
- {
- return false; /* parse_error */
- }
-
- item->valuedouble = number;
-
- /* use saturation in case of overflow */
- if (number >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (number <= (double)INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)number;
- }
-
- item->type = cJSON_Number;
-
- input_buffer->offset += (size_t)(after_end - number_c_string);
- return true;
-}
-
-/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
-{
- if (number >= INT_MAX)
- {
- object->valueint = INT_MAX;
- }
- else if (number <= (double)INT_MIN)
- {
- object->valueint = INT_MIN;
- }
- else
- {
- object->valueint = (int)number;
- }
-
- return object->valuedouble = number;
-}
-
-typedef struct
-{
- unsigned char *buffer;
- size_t length;
- size_t offset;
- size_t depth; /* current nesting depth (for formatted printing) */
- cJSON_bool noalloc;
- cJSON_bool format; /* is this print a formatted print */
- internal_hooks hooks;
-} printbuffer;
-
-/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static unsigned char* ensure(printbuffer * const p, size_t needed)
-{
- unsigned char *newbuffer = NULL;
- size_t newsize = 0;
-
- if ((p == NULL) || (p->buffer == NULL))
- {
- return NULL;
- }
-
- if ((p->length > 0) && (p->offset >= p->length))
- {
- /* make sure that offset is valid */
- return NULL;
- }
-
- if (needed > INT_MAX)
- {
- /* sizes bigger than INT_MAX are currently not supported */
- return NULL;
- }
-
- needed += p->offset + 1;
- if (needed <= p->length)
- {
- return p->buffer + p->offset;
- }
-
- if (p->noalloc) {
- return NULL;
- }
-
- /* calculate new buffer size */
- if (needed > (INT_MAX / 2))
- {
- /* overflow of int, use INT_MAX if possible */
- if (needed <= INT_MAX)
- {
- newsize = INT_MAX;
- }
- else
- {
- return NULL;
- }
- }
- else
- {
- newsize = needed * 2;
- }
-
- if (p->hooks.reallocate != NULL)
- {
- /* reallocate with realloc if available */
- newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
- if (newbuffer == NULL)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
- }
- else
- {
- /* otherwise reallocate manually */
- newbuffer = (unsigned char*)p->hooks.allocate(newsize);
- if (!newbuffer)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
- if (newbuffer)
- {
- memcpy(newbuffer, p->buffer, p->offset + 1);
- }
- p->hooks.deallocate(p->buffer);
- }
- p->length = newsize;
- p->buffer = newbuffer;
-
- return newbuffer + p->offset;
-}
-
-/* calculate the new length of the string in a printbuffer and update the offset */
-static void update_offset(printbuffer * const buffer)
-{
- const unsigned char *buffer_pointer = NULL;
- if ((buffer == NULL) || (buffer->buffer == NULL))
- {
- return;
- }
- buffer_pointer = buffer->buffer + buffer->offset;
-
- buffer->offset += strlen((const char*)buffer_pointer);
-}
-
-/* securely comparison of floating-point variables */
-static cJSON_bool compare_double(double a, double b)
-{
- return (fabs(a - b) <= CJSON_DOUBLE_PRECISION);
-}
-
-/* Render the number nicely from the given item into a string. */
-static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- double d = item->valuedouble;
- int length = 0;
- size_t i = 0;
- unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
- unsigned char decimal_point = get_decimal_point();
- double test = 0.0;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* This checks for NaN and Infinity */
- if (!compare_double(d * 0, 0))
- {
- length = sprintf((char*)number_buffer, "null");
- }
- else
- {
- /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
- length = sprintf((char*)number_buffer, "%1.15g", d);
-
- /* Check whether the original double can be recovered */
- if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
- {
- /* If not, print with 17 decimal places of precision */
- length = sprintf((char*)number_buffer, "%1.17g", d);
- }
- }
-
- /* sprintf failed or buffer overrun occurred */
- if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
- {
- return false;
- }
-
- /* reserve appropriate space in the output */
- output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
- if (output_pointer == NULL)
- {
- return false;
- }
-
- /* copy the printed number to the output and replace locale
- * dependent decimal point with '.' */
- for (i = 0; i < ((size_t)length); i++)
- {
- if (number_buffer[i] == decimal_point)
- {
- output_pointer[i] = '.';
- continue;
- }
-
- output_pointer[i] = number_buffer[i];
- }
- output_pointer[i] = '\0';
-
- output_buffer->offset += (size_t)length;
-
- return true;
-}
-
-/* parse 4 digit hexadecimal number */
-static unsigned parse_hex4(const unsigned char * const input)
-{
- unsigned int h = 0;
- size_t i = 0;
-
- for (i = 0; i < 4; i++)
- {
- /* parse digit */
- if ((input[i] >= '0') && (input[i] <= '9'))
- {
- h += (unsigned int) input[i] - '0';
- }
- else if ((input[i] >= 'A') && (input[i] <= 'F'))
- {
- h += (unsigned int) 10 + input[i] - 'A';
- }
- else if ((input[i] >= 'a') && (input[i] <= 'f'))
- {
- h += (unsigned int) 10 + input[i] - 'a';
- }
- else /* invalid */
- {
- return 0;
- }
-
- if (i < 3)
- {
- /* shift left to make place for the next nibble */
- h = h << 4;
- }
- }
-
- return h;
-}
-
-/* converts a UTF-16 literal to UTF-8
- * A literal can be one or two sequences of the form \uXXXX */
-static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
-{
- long unsigned int codepoint = 0;
- unsigned int first_code = 0;
- const unsigned char *first_sequence = input_pointer;
- unsigned char utf8_length = 0;
- unsigned char utf8_position = 0;
- unsigned char sequence_length = 0;
- unsigned char first_byte_mark = 0;
-
- if ((input_end - first_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- /* get the first utf16 sequence */
- first_code = parse_hex4(first_sequence + 2);
-
- /* check that the code is valid */
- if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
- {
- goto fail;
- }
-
- /* UTF16 surrogate pair */
- if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
- {
- const unsigned char *second_sequence = first_sequence + 6;
- unsigned int second_code = 0;
- sequence_length = 12; /* \uXXXX\uXXXX */
-
- if ((input_end - second_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
- {
- /* missing second half of the surrogate pair */
- goto fail;
- }
-
- /* get the second utf16 sequence */
- second_code = parse_hex4(second_sequence + 2);
- /* check that the code is valid */
- if ((second_code < 0xDC00) || (second_code > 0xDFFF))
- {
- /* invalid second half of the surrogate pair */
- goto fail;
- }
-
-
- /* calculate the unicode codepoint from the surrogate pair */
- codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
- }
- else
- {
- sequence_length = 6; /* \uXXXX */
- codepoint = first_code;
- }
-
- /* encode as UTF-8
- * takes at maximum 4 bytes to encode:
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
- if (codepoint < 0x80)
- {
- /* normal ascii, encoding 0xxxxxxx */
- utf8_length = 1;
- }
- else if (codepoint < 0x800)
- {
- /* two bytes, encoding 110xxxxx 10xxxxxx */
- utf8_length = 2;
- first_byte_mark = 0xC0; /* 11000000 */
- }
- else if (codepoint < 0x10000)
- {
- /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
- utf8_length = 3;
- first_byte_mark = 0xE0; /* 11100000 */
- }
- else if (codepoint <= 0x10FFFF)
- {
- /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
- utf8_length = 4;
- first_byte_mark = 0xF0; /* 11110000 */
- }
- else
- {
- /* invalid unicode codepoint */
- goto fail;
- }
-
- /* encode as utf8 */
- for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
- {
- /* 10xxxxxx */
- (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- }
- /* encode first byte */
- if (utf8_length > 1)
- {
- (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
- }
- else
- {
- (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
- }
-
- *output_pointer += utf8_length;
-
- return sequence_length;
-
-fail:
- return 0;
-}
-
-/* Parse the input text into an unescaped cinput, and populate item. */
-static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
-{
- const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
- const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
- unsigned char *output_pointer = NULL;
- unsigned char *output = NULL;
-
- /* not a string */
- if (buffer_at_offset(input_buffer)[0] != '\"')
- {
- goto fail;
- }
-
- {
- /* calculate approximate size of the output (overestimate) */
- size_t allocation_length = 0;
- size_t skipped_bytes = 0;
- while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
- {
- /* is escape sequence */
- if (input_end[0] == '\\')
- {
- if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
- {
- /* prevent buffer overflow when last input character is a backslash */
- goto fail;
- }
- skipped_bytes++;
- input_end++;
- }
- input_end++;
- }
- if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
- {
- goto fail; /* string ended unexpectedly */
- }
-
- /* This is at most how much we need for the output */
- allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
- output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
- if (output == NULL)
- {
- goto fail; /* allocation failure */
- }
- }
-
- output_pointer = output;
- /* loop through the string literal */
- while (input_pointer < input_end)
- {
- if (*input_pointer != '\\')
- {
- *output_pointer++ = *input_pointer++;
- }
- /* escape sequence */
- else
- {
- unsigned char sequence_length = 2;
- if ((input_end - input_pointer) < 1)
- {
- goto fail;
- }
-
- switch (input_pointer[1])
- {
- case 'b':
- *output_pointer++ = '\b';
- break;
- case 'f':
- *output_pointer++ = '\f';
- break;
- case 'n':
- *output_pointer++ = '\n';
- break;
- case 'r':
- *output_pointer++ = '\r';
- break;
- case 't':
- *output_pointer++ = '\t';
- break;
- case '\"':
- case '\\':
- case '/':
- *output_pointer++ = input_pointer[1];
- break;
-
- /* UTF-16 literal */
- case 'u':
- sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
- if (sequence_length == 0)
- {
- /* failed to convert UTF16-literal to UTF-8 */
- goto fail;
- }
- break;
-
- default:
- goto fail;
- }
- input_pointer += sequence_length;
- }
- }
-
- /* zero terminate the output */
- *output_pointer = '\0';
-
- item->type = cJSON_String;
- item->valuestring = (char*)output;
-
- input_buffer->offset = (size_t) (input_end - input_buffer->content);
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (output != NULL)
- {
- input_buffer->hooks.deallocate(output);
- }
-
- if (input_pointer != NULL)
- {
- input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
- }
-
- return false;
-}
-
-/* Render the cstring provided to an escaped version that can be printed. */
-static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
-{
- const unsigned char *input_pointer = NULL;
- unsigned char *output = NULL;
- unsigned char *output_pointer = NULL;
- size_t output_length = 0;
- /* numbers of additional characters needed for escaping */
- size_t escape_characters = 0;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* empty string */
- if (input == NULL)
- {
- output = ensure(output_buffer, sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "\"\"");
-
- return true;
- }
-
- /* set "flag" to 1 if something needs to be escaped */
- for (input_pointer = input; *input_pointer; input_pointer++)
- {
- switch (*input_pointer)
- {
- case '\"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- /* one character escape sequence */
- escape_characters++;
- break;
- default:
- if (*input_pointer < 32)
- {
- /* UTF-16 escape sequence uXXXX */
- escape_characters += 5;
- }
- break;
- }
- }
- output_length = (size_t)(input_pointer - input) + escape_characters;
-
- output = ensure(output_buffer, output_length + sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
-
- /* no characters have to be escaped */
- if (escape_characters == 0)
- {
- output[0] = '\"';
- memcpy(output + 1, input, output_length);
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
- }
-
- output[0] = '\"';
- output_pointer = output + 1;
- /* copy the string */
- for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
- {
- if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
- {
- /* normal character, copy */
- *output_pointer = *input_pointer;
- }
- else
- {
- /* character needs to be escaped */
- *output_pointer++ = '\\';
- switch (*input_pointer)
- {
- case '\\':
- *output_pointer = '\\';
- break;
- case '\"':
- *output_pointer = '\"';
- break;
- case '\b':
- *output_pointer = 'b';
- break;
- case '\f':
- *output_pointer = 'f';
- break;
- case '\n':
- *output_pointer = 'n';
- break;
- case '\r':
- *output_pointer = 'r';
- break;
- case '\t':
- *output_pointer = 't';
- break;
- default:
- /* escape and print as unicode codepoint */
- sprintf((char*)output_pointer, "u%04x", *input_pointer);
- output_pointer += 4;
- break;
- }
- }
- }
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
-}
-
-/* Invoke print_string_ptr (which is useful) on an item. */
-static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
-{
- return print_string_ptr((unsigned char*)item->valuestring, p);
-}
-
-/* Predeclare these prototypes. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
-
-/* Utility to jump whitespace and cr/lf */
-static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL))
- {
- return NULL;
- }
-
- while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
- {
- buffer->offset++;
- }
-
- if (buffer->offset == buffer->length)
- {
- buffer->offset--;
- }
-
- return buffer;
-}
-
-/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
-static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
- {
- return NULL;
- }
-
- if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
- {
- buffer->offset += 3;
- }
-
- return buffer;
-}
-
-/* Parse an object - create a new root, and populate. */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
-{
- parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
- cJSON *item = NULL;
-
- /* reset error position */
- global_error.json = NULL;
- global_error.position = 0;
-
- if (value == NULL)
- {
- goto fail;
- }
-
- buffer.content = (const unsigned char*)value;
- buffer.length = strlen((const char*)value) + sizeof("");
- buffer.offset = 0;
- buffer.hooks = global_hooks;
-
- item = cJSON_New_Item(&global_hooks);
- if (item == NULL) /* memory fail */
- {
- goto fail;
- }
-
- if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
- {
- /* parse failure. ep is set. */
- goto fail;
- }
-
- /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
- if (require_null_terminated)
- {
- buffer_skip_whitespace(&buffer);
- if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
- {
- goto fail;
- }
- }
- if (return_parse_end)
- {
- *return_parse_end = (const char*)buffer_at_offset(&buffer);
- }
-
- return item;
-
-fail:
- if (item != NULL)
- {
- cJSON_Delete(item);
- }
-
- if (value != NULL)
- {
- error local_error;
- local_error.json = (const unsigned char*)value;
- local_error.position = 0;
-
- if (buffer.offset < buffer.length)
- {
- local_error.position = buffer.offset;
- }
- else if (buffer.length > 0)
- {
- local_error.position = buffer.length - 1;
- }
-
- if (return_parse_end != NULL)
- {
- *return_parse_end = (const char*)local_error.json + local_error.position;
- }
-
- global_error = local_error;
- }
-
- return NULL;
-}
-
-/* Default options for cJSON_Parse */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
-{
- return cJSON_ParseWithOpts(value, 0, 0);
-}
-
-#define cjson_min(a, b) ((a < b) ? a : b)
-
-static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
-{
- static const size_t default_buffer_size = 256;
- printbuffer buffer[1];
- unsigned char *printed = NULL;
-
- memset(buffer, 0, sizeof(buffer));
-
- /* create buffer */
- buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
- buffer->length = default_buffer_size;
- buffer->format = format;
- buffer->hooks = *hooks;
- if (buffer->buffer == NULL)
- {
- goto fail;
- }
-
- /* print the value */
- if (!print_value(item, buffer))
- {
- goto fail;
- }
- update_offset(buffer);
-
- /* check if reallocate is available */
- if (hooks->reallocate != NULL)
- {
- printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
- if (printed == NULL) {
- goto fail;
- }
- buffer->buffer = NULL;
- }
- else /* otherwise copy the JSON over to a new buffer */
- {
- printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
- if (printed == NULL)
- {
- goto fail;
- }
- memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
- printed[buffer->offset] = '\0'; /* just to be sure */
-
- /* free the buffer */
- hooks->deallocate(buffer->buffer);
- }
-
- return printed;
-
-fail:
- if (buffer->buffer != NULL)
- {
- hooks->deallocate(buffer->buffer);
- }
-
- if (printed != NULL)
- {
- hooks->deallocate(printed);
- }
-
- return NULL;
-}
-
-/* Render a cJSON item/entity/structure to text. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
-{
- return (char*)print(item, true, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
-{
- return (char*)print(item, false, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if (prebuffer < 0)
- {
- return NULL;
- }
-
- p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
- if (!p.buffer)
- {
- return NULL;
- }
-
- p.length = (size_t)prebuffer;
- p.offset = 0;
- p.noalloc = false;
- p.format = fmt;
- p.hooks = global_hooks;
-
- if (!print_value(item, &p))
- {
- global_hooks.deallocate(p.buffer);
- return NULL;
- }
-
- return (char*)p.buffer;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if ((length < 0) || (buffer == NULL))
- {
- return false;
- }
-
- p.buffer = (unsigned char*)buffer;
- p.length = (size_t)length;
- p.offset = 0;
- p.noalloc = true;
- p.format = format;
- p.hooks = global_hooks;
-
- return print_value(item, &p);
-}
-
-/* Parser core - when encountering text, process appropriately. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
-{
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false; /* no input */
- }
-
- /* parse the different types of values */
- /* null */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
- {
- item->type = cJSON_NULL;
- input_buffer->offset += 4;
- return true;
- }
- /* false */
- if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
- {
- item->type = cJSON_False;
- input_buffer->offset += 5;
- return true;
- }
- /* true */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
- {
- item->type = cJSON_True;
- item->valueint = 1;
- input_buffer->offset += 4;
- return true;
- }
- /* string */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
- {
- return parse_string(item, input_buffer);
- }
- /* number */
- if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
- {
- return parse_number(item, input_buffer);
- }
- /* array */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
- {
- return parse_array(item, input_buffer);
- }
- /* object */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
- {
- return parse_object(item, input_buffer);
- }
-
- return false;
-}
-
-/* Render a value to text. */
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output = NULL;
-
- if ((item == NULL) || (output_buffer == NULL))
- {
- return false;
- }
-
- switch ((item->type) & 0xFF)
- {
- case cJSON_NULL:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "null");
- return true;
-
- case cJSON_False:
- output = ensure(output_buffer, 6);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "false");
- return true;
-
- case cJSON_True:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "true");
- return true;
-
- case cJSON_Number:
- return print_number(item, output_buffer);
-
- case cJSON_Raw:
- {
- size_t raw_length = 0;
- if (item->valuestring == NULL)
- {
- return false;
- }
-
- raw_length = strlen(item->valuestring) + sizeof("");
- output = ensure(output_buffer, raw_length);
- if (output == NULL)
- {
- return false;
- }
- memcpy(output, item->valuestring, raw_length);
- return true;
- }
-
- case cJSON_String:
- return print_string(item, output_buffer);
-
- case cJSON_Array:
- return print_array(item, output_buffer);
-
- case cJSON_Object:
- return print_object(item, output_buffer);
-
- default:
- return false;
- }
-}
-
-/* Build an array from input text. */
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* head of the linked list */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (buffer_at_offset(input_buffer)[0] != '[')
- {
- /* not an array */
- goto fail;
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
- {
- /* empty array */
- goto success;
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- /* parse next value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
- {
- goto fail; /* expected end of array */
- }
-
-success:
- input_buffer->depth--;
-
- item->type = cJSON_Array;
- item->child = head;
-
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an array to text */
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_element = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output array. */
- /* opening square bracket */
- output_pointer = ensure(output_buffer, 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer = '[';
- output_buffer->offset++;
- output_buffer->depth++;
-
- while (current_element != NULL)
- {
- if (!print_value(current_element, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
- if (current_element->next)
- {
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ',';
- if(output_buffer->format)
- {
- *output_pointer++ = ' ';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
- }
- current_element = current_element->next;
- }
-
- output_pointer = ensure(output_buffer, 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ']';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Build an object from the text. */
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* linked list head */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
- {
- goto fail; /* not an object */
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
- {
- goto success; /* empty object */
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- /* parse the name of the child */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_string(current_item, input_buffer))
- {
- goto fail; /* failed to parse name */
- }
- buffer_skip_whitespace(input_buffer);
-
- /* swap valuestring and string, because we parsed the name */
- current_item->string = current_item->valuestring;
- current_item->valuestring = NULL;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
- {
- goto fail; /* invalid object */
- }
-
- /* parse the value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
- {
- goto fail; /* expected end of object */
- }
-
-success:
- input_buffer->depth--;
-
- item->type = cJSON_Object;
- item->child = head;
-
- input_buffer->offset++;
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an object to text. */
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_item = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output: */
- length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer++ = '{';
- output_buffer->depth++;
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- output_buffer->offset += length;
-
- while (current_item)
- {
- if (output_buffer->format)
- {
- size_t i;
- output_pointer = ensure(output_buffer, output_buffer->depth);
- if (output_pointer == NULL)
- {
- return false;
- }
- for (i = 0; i < output_buffer->depth; i++)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += output_buffer->depth;
- }
-
- /* print key */
- if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ':';
- if (output_buffer->format)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += length;
-
- /* print value */
- if (!print_value(current_item, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- /* print comma if not last */
- length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (current_item->next)
- {
- *output_pointer++ = ',';
- }
-
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
-
- current_item = current_item->next;
- }
-
- output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (output_buffer->format)
- {
- size_t i;
- for (i = 0; i < (output_buffer->depth - 1); i++)
- {
- *output_pointer++ = '\t';
- }
- }
- *output_pointer++ = '}';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Get Array size/item / object item. */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
-{
- cJSON *child = NULL;
- size_t size = 0;
-
- if (array == NULL)
- {
- return 0;
- }
-
- child = array->child;
-
- while(child != NULL)
- {
- size++;
- child = child->next;
- }
-
- /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
-
- return (int)size;
-}
-
-static cJSON* get_array_item(const cJSON *array, size_t index)
-{
- cJSON *current_child = NULL;
-
- if (array == NULL)
- {
- return NULL;
- }
-
- current_child = array->child;
- while ((current_child != NULL) && (index > 0))
- {
- index--;
- current_child = current_child->next;
- }
-
- return current_child;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
-{
- if (index < 0)
- {
- return NULL;
- }
-
- return get_array_item(array, (size_t)index);
-}
-
-static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
-{
- cJSON *current_element = NULL;
-
- if ((object == NULL) || (name == NULL))
- {
- return NULL;
- }
-
- current_element = object->child;
- if (case_sensitive)
- {
- while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
- {
- current_element = current_element->next;
- }
- }
- else
- {
- while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
- {
- current_element = current_element->next;
- }
- }
-
- if ((current_element == NULL) || (current_element->string == NULL)) {
- return NULL;
- }
-
- return current_element;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, true);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
-{
- return cJSON_GetObjectItem(object, string) ? 1 : 0;
-}
-
-/* Utility for array list handling. */
-static void suffix_object(cJSON *prev, cJSON *item)
-{
- prev->next = item;
- item->prev = prev;
-}
-
-/* Utility for handling references. */
-static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
-{
- cJSON *reference = NULL;
- if (item == NULL)
- {
- return NULL;
- }
-
- reference = cJSON_New_Item(hooks);
- if (reference == NULL)
- {
- return NULL;
- }
-
- memcpy(reference, item, sizeof(cJSON));
- reference->string = NULL;
- reference->type |= cJSON_IsReference;
- reference->next = reference->prev = NULL;
- return reference;
-}
-
-static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
-{
- cJSON *child = NULL;
-
- if ((item == NULL) || (array == NULL))
- {
- return false;
- }
-
- child = array->child;
-
- if (child == NULL)
- {
- /* list is empty, start new one */
- array->child = item;
- }
- else
- {
- /* append to the end */
- while (child->next)
- {
- child = child->next;
- }
- suffix_object(child, item);
- }
-
- return true;
-}
-
-/* Add item to array/object. */
-CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
-{
- add_item_to_array(array, item);
-}
-
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic push
-#endif
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#endif
-/* helper function to cast away const */
-static void* cast_away_const(const void* string)
-{
- return (void*)string;
-}
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic pop
-#endif
-
-
-static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
-{
- char *new_key = NULL;
- int new_type = cJSON_Invalid;
-
- if ((object == NULL) || (string == NULL) || (item == NULL))
- {
- return false;
- }
-
- if (constant_key)
- {
- new_key = (char*)cast_away_const(string);
- new_type = item->type | cJSON_StringIsConst;
- }
- else
- {
- new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
- if (new_key == NULL)
- {
- return false;
- }
-
- new_type = item->type & ~cJSON_StringIsConst;
- }
-
- if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
- {
- hooks->deallocate(item->string);
- }
-
- item->string = new_key;
- item->type = new_type;
-
- return add_item_to_array(object, item);
-}
-
-CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
-{
- add_item_to_object(object, string, item, &global_hooks, false);
-}
-
-/* Add an item to an object with constant string as key */
-CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
-{
- add_item_to_object(object, string, item, &global_hooks, true);
-}
-
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
-{
- if (array == NULL)
- {
- return;
- }
-
- add_item_to_array(array, create_reference(item, &global_hooks));
-}
-
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
-{
- if ((object == NULL) || (string == NULL))
- {
- return;
- }
-
- add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
-{
- cJSON *null = cJSON_CreateNull();
- if (add_item_to_object(object, name, null, &global_hooks, false))
- {
- return null;
- }
-
- cJSON_Delete(null);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
-{
- cJSON *true_item = cJSON_CreateTrue();
- if (add_item_to_object(object, name, true_item, &global_hooks, false))
- {
- return true_item;
- }
-
- cJSON_Delete(true_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
-{
- cJSON *false_item = cJSON_CreateFalse();
- if (add_item_to_object(object, name, false_item, &global_hooks, false))
- {
- return false_item;
- }
-
- cJSON_Delete(false_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
-{
- cJSON *bool_item = cJSON_CreateBool(boolean);
- if (add_item_to_object(object, name, bool_item, &global_hooks, false))
- {
- return bool_item;
- }
-
- cJSON_Delete(bool_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
-{
- cJSON *number_item = cJSON_CreateNumber(number);
- if (add_item_to_object(object, name, number_item, &global_hooks, false))
- {
- return number_item;
- }
-
- cJSON_Delete(number_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
-{
- cJSON *string_item = cJSON_CreateString(string);
- if (add_item_to_object(object, name, string_item, &global_hooks, false))
- {
- return string_item;
- }
-
- cJSON_Delete(string_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
-{
- cJSON *raw_item = cJSON_CreateRaw(raw);
- if (add_item_to_object(object, name, raw_item, &global_hooks, false))
- {
- return raw_item;
- }
-
- cJSON_Delete(raw_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
-{
- cJSON *object_item = cJSON_CreateObject();
- if (add_item_to_object(object, name, object_item, &global_hooks, false))
- {
- return object_item;
- }
-
- cJSON_Delete(object_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
-{
- cJSON *array = cJSON_CreateArray();
- if (add_item_to_object(object, name, array, &global_hooks, false))
- {
- return array;
- }
-
- cJSON_Delete(array);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
-{
- if ((parent == NULL) || (item == NULL))
- {
- return NULL;
- }
-
- if (item->prev != NULL)
- {
- /* not the first element */
- item->prev->next = item->next;
- }
- if (item->next != NULL)
- {
- /* not the last element */
- item->next->prev = item->prev;
- }
-
- if (item == parent->child)
- {
- /* first element */
- parent->child = item->next;
- }
- /* make sure the detached item doesn't point anywhere anymore */
- item->prev = NULL;
- item->next = NULL;
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
-{
- if (which < 0)
- {
- return NULL;
- }
-
- return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
-{
- cJSON_Delete(cJSON_DetachItemFromArray(array, which));
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItem(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObject(object, string));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
-}
-
-/* Replace array/object items with new ones. */
-CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- cJSON *after_inserted = NULL;
-
- if (which < 0)
- {
- return;
- }
-
- after_inserted = get_array_item(array, (size_t)which);
- if (after_inserted == NULL)
- {
- add_item_to_array(array, newitem);
- return;
- }
-
- newitem->next = after_inserted;
- newitem->prev = after_inserted->prev;
- after_inserted->prev = newitem;
- if (after_inserted == array->child)
- {
- array->child = newitem;
- }
- else
- {
- newitem->prev->next = newitem;
- }
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
-{
- if ((parent == NULL) || (replacement == NULL) || (item == NULL))
- {
- return false;
- }
-
- if (replacement == item)
- {
- return true;
- }
-
- replacement->next = item->next;
- replacement->prev = item->prev;
-
- if (replacement->next != NULL)
- {
- replacement->next->prev = replacement;
- }
- if (replacement->prev != NULL)
- {
- replacement->prev->next = replacement;
- }
- if (parent->child == item)
- {
- parent->child = replacement;
- }
-
- item->next = NULL;
- item->prev = NULL;
- cJSON_Delete(item);
-
- return true;
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- if (which < 0)
- {
- return;
- }
-
- cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
-}
-
-static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
-{
- if ((replacement == NULL) || (string == NULL))
- {
- return false;
- }
-
- /* replace the name in the replacement */
- if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
- {
- cJSON_free(replacement->string);
- }
- replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- replacement->type &= ~cJSON_StringIsConst;
-
- cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
-
- return true;
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
-{
- replace_item_in_object(object, string, newitem, false);
-}
-
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
-{
- replace_item_in_object(object, string, newitem, true);
-}
-
-/* Create basic types: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_NULL;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_True;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = boolean ? cJSON_True : cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Number;
- item->valuedouble = num;
-
- /* use saturation in case of overflow */
- if (num >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (num <= (double)INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)num;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_String;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL)
- {
- item->type = cJSON_String | cJSON_IsReference;
- item->valuestring = (char*)cast_away_const(string);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL) {
- item->type = cJSON_Object | cJSON_IsReference;
- item->child = (cJSON*)cast_away_const(child);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL) {
- item->type = cJSON_Array | cJSON_IsReference;
- item->child = (cJSON*)cast_away_const(child);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Raw;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type=cJSON_Array;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item)
- {
- item->type = cJSON_Object;
- }
-
- return item;
-}
-
-/* Create Arrays: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if (!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber((double)numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0;a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (strings == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for (i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateString(strings[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p,n);
- }
- p = n;
- }
-
- return a;
-}
-
-/* Duplication */
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
-{
- cJSON *newitem = NULL;
- cJSON *child = NULL;
- cJSON *next = NULL;
- cJSON *newchild = NULL;
-
- /* Bail on bad ptr */
- if (!item)
- {
- goto fail;
- }
- /* Create new item */
- newitem = cJSON_New_Item(&global_hooks);
- if (!newitem)
- {
- goto fail;
- }
- /* Copy over all vars */
- newitem->type = item->type & (~cJSON_IsReference);
- newitem->valueint = item->valueint;
- newitem->valuedouble = item->valuedouble;
- if (item->valuestring)
- {
- newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
- if (!newitem->valuestring)
- {
- goto fail;
- }
- }
- if (item->string)
- {
- newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
- if (!newitem->string)
- {
- goto fail;
- }
- }
- /* If non-recursive, then we're done! */
- if (!recurse)
- {
- return newitem;
- }
- /* Walk the ->next chain for the child. */
- child = item->child;
- while (child != NULL)
- {
- newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
- if (!newchild)
- {
- goto fail;
- }
- if (next != NULL)
- {
- /* If newitem->child already set, then crosswire ->prev and ->next and move on */
- next->next = newchild;
- newchild->prev = next;
- next = newchild;
- }
- else
- {
- /* Set newitem->child and move to it */
- newitem->child = newchild;
- next = newchild;
- }
- child = child->next;
- }
-
- return newitem;
-
-fail:
- if (newitem != NULL)
- {
- cJSON_Delete(newitem);
- }
-
- return NULL;
-}
-
-static void skip_oneline_comment(char **input)
-{
- *input += static_strlen("//");
-
- for (; (*input)[0] != '\0'; ++(*input))
- {
- if ((*input)[0] == '\n') {
- *input += static_strlen("\n");
- return;
- }
- }
-}
-
-static void skip_multiline_comment(char **input)
-{
- *input += static_strlen("/*");
-
- for (; (*input)[0] != '\0'; ++(*input))
- {
- if (((*input)[0] == '*') && ((*input)[1] == '/'))
- {
- *input += static_strlen("*/");
- return;
- }
- }
-}
-
-static void minify_string(char **input, char **output) {
- (*output)[0] = (*input)[0];
- *input += static_strlen("\"");
- *output += static_strlen("\"");
-
-
- for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
- (*output)[0] = (*input)[0];
-
- if ((*input)[0] == '\"') {
- (*output)[0] = '\"';
- *input += static_strlen("\"");
- *output += static_strlen("\"");
- return;
- } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
- (*output)[1] = (*input)[1];
- *input += static_strlen("\"");
- *output += static_strlen("\"");
- }
- }
-}
-
-CJSON_PUBLIC(void) cJSON_Minify(char *json)
-{
- char *into = json;
-
- if (json == NULL)
- {
- return;
- }
-
- while (json[0] != '\0')
- {
- switch (json[0])
- {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- json++;
- break;
-
- case '/':
- if (json[1] == '/')
- {
- skip_oneline_comment(&json);
- }
- else if (json[1] == '*')
- {
- skip_multiline_comment(&json);
- } else {
- json++;
- }
- break;
-
- case '\"':
- minify_string(&json, (char**)&into);
- break;
-
- default:
- into[0] = json[0];
- json++;
- into++;
- }
- }
-
- /* and null-terminate. */
- *into = '\0';
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Invalid;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_False;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xff) == cJSON_True;
-}
-
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & (cJSON_True | cJSON_False)) != 0;
-}
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_NULL;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Number;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_String;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Array;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Object;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Raw;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
-{
- if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
- {
- return false;
- }
-
- /* check if type is valid */
- switch (a->type & 0xFF)
- {
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- case cJSON_Number:
- case cJSON_String:
- case cJSON_Raw:
- case cJSON_Array:
- case cJSON_Object:
- break;
-
- default:
- return false;
- }
-
- /* identical objects are equal */
- if (a == b)
- {
- return true;
- }
-
- switch (a->type & 0xFF)
- {
- /* in these cases and equal type is enough */
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- return true;
-
- case cJSON_Number:
- if (compare_double(a->valuedouble, b->valuedouble))
- {
- return true;
- }
- return false;
-
- case cJSON_String:
- case cJSON_Raw:
- if ((a->valuestring == NULL) || (b->valuestring == NULL))
- {
- return false;
- }
- if (strcmp(a->valuestring, b->valuestring) == 0)
- {
- return true;
- }
-
- return false;
-
- case cJSON_Array:
- {
- cJSON *a_element = a->child;
- cJSON *b_element = b->child;
-
- for (; (a_element != NULL) && (b_element != NULL);)
- {
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
-
- a_element = a_element->next;
- b_element = b_element->next;
- }
-
- /* one of the arrays is longer than the other */
- if (a_element != b_element) {
- return false;
- }
-
- return true;
- }
-
- case cJSON_Object:
- {
- cJSON *a_element = NULL;
- cJSON *b_element = NULL;
- cJSON_ArrayForEach(a_element, a)
- {
- /* TODO This has O(n^2) runtime, which is horrible! */
- b_element = get_object_item(b, a_element->string, case_sensitive);
- if (b_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
- }
-
- /* doing this twice, once on a and b to prevent true comparison if a subset of b
- * TODO: Do this the proper way, this is just a fix for now */
- cJSON_ArrayForEach(b_element, b)
- {
- a_element = get_object_item(a, b_element->string, case_sensitive);
- if (a_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(b_element, a_element, case_sensitive))
- {
- return false;
- }
- }
-
- return true;
- }
-
- default:
- return false;
- }
-}
-
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
-{
- return global_hooks.allocate(size);
-}
-
-CJSON_PUBLIC(void) cJSON_free(void *object)
-{
- global_hooks.deallocate(object);
-}
-
diff --git a/src/external/cJSON.h b/src/external/cJSON.h
deleted file mode 100644
index 23d7851..0000000
--- a/src/external/cJSON.h
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-*/
-
-#ifndef cJSON__h
-#define cJSON__h
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
-#define __WINDOWS__
-#endif
-
-#ifdef __WINDOWS__
-
-/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
-
-CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
-CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
-CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
-
-For *nix builds that support visibility attribute, you can define similar behavior by
-
-setting default visibility to hidden by adding
--fvisibility=hidden (for gcc)
-or
--xldscope=hidden (for sun cc)
-to CFLAGS
-
-then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
-
-*/
-
-#define CJSON_CDECL __cdecl
-#define CJSON_STDCALL __stdcall
-
-/* export symbols by default, this is necessary for copy pasting the C and header file */
-#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_EXPORT_SYMBOLS
-#endif
-
-#if defined(CJSON_HIDE_SYMBOLS)
-#define CJSON_PUBLIC(type) type CJSON_STDCALL
-#elif defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
-#elif defined(CJSON_IMPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
-#endif
-#else /* !__WINDOWS__ */
-#define CJSON_CDECL
-#define CJSON_STDCALL
-
-#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
-#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
-#else
-#define CJSON_PUBLIC(type) type
-#endif
-#endif
-
-/* project version */
-#define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 7
-#define CJSON_VERSION_PATCH 12
-
-#include <stddef.h>
-
-/* cJSON Types: */
-#define cJSON_Invalid (0)
-#define cJSON_False (1 << 0)
-#define cJSON_True (1 << 1)
-#define cJSON_NULL (1 << 2)
-#define cJSON_Number (1 << 3)
-#define cJSON_String (1 << 4)
-#define cJSON_Array (1 << 5)
-#define cJSON_Object (1 << 6)
-#define cJSON_Raw (1 << 7) /* raw json */
-
-#define cJSON_IsReference 256
-#define cJSON_StringIsConst 512
-
-/* The cJSON structure: */
-typedef struct cJSON
-{
- /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
- struct cJSON *next;
- struct cJSON *prev;
- /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
- struct cJSON *child;
-
- /* The type of the item, as above. */
- int type;
-
- /* The item's string, if type==cJSON_String and type == cJSON_Raw */
- char *valuestring;
- /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
- int valueint;
- /* The item's number, if type==cJSON_Number */
- double valuedouble;
-
- /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
- char *string;
-} cJSON;
-
-typedef struct cJSON_Hooks
-{
- /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
- void *(CJSON_CDECL *malloc_fn)(size_t sz);
- void (CJSON_CDECL *free_fn)(void *ptr);
-} cJSON_Hooks;
-
-typedef int cJSON_bool;
-
-/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
- * This is to prevent stack overflows. */
-#ifndef CJSON_NESTING_LIMIT
-#define CJSON_NESTING_LIMIT 1000
-#endif
-
-/* Precision of double variables comparison */
-#ifndef CJSON_DOUBLE_PRECISION
-#define CJSON_DOUBLE_PRECISION .0000000000000001
-#endif
-
-/* returns the version of cJSON as a string */
-CJSON_PUBLIC(const char*) cJSON_Version(void);
-
-/* Supply malloc, realloc and free functions to cJSON */
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
-
-/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
-/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
-/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
-/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
-
-/* Render a cJSON entity to text for transfer/storage. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
-/* Render a cJSON entity to text for transfer/storage without any formatting. */
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
-/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
-/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
-/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
-/* Delete a cJSON entity and all subentities. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
-
-/* Returns the number of items in an array (or object). */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
-/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
-/* Get item "string" from object. Case insensitive. */
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
-/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
-
-/* Check if the item is a string and return its valuestring */
-CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
-
-/* These functions check the type of an item */
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
-
-/* These calls create a cJSON item of the appropriate type. */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
-/* raw json */
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
-
-/* Create a string where valuestring references a string so
- * it will not be freed by cJSON_Delete */
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
-/* Create an object/array that only references it's elements so
- * they will not be freed by cJSON_Delete */
-CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
-
-/* These utilities create an Array of count items.
- * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
-
-/* Append item to the specified array/object. */
-CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
-/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
- * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
- * writing to `item->string` */
-CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
-/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
-
-/* Remove/Detach items from Arrays/Objects. */
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
-
-/* Update array items. */
-CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
-CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
-
-/* Duplicate a cJSON item */
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
-/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
- * need to be released. With recurse!=0, it will duplicate any children connected to the item.
- * The item->next and ->prev pointers are always zero on return from Duplicate. */
-/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
- * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
-
-/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
- * The input pointer json cannot point to a read-only address area, such as a string constant,
- * but should point to a readable and writable adress area. */
-CJSON_PUBLIC(void) cJSON_Minify(char *json);
-
-/* Helper functions for creating and adding items to an object at the same time.
- * They return the added item or NULL on failure. */
-CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
-CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
-CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
-CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
-CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
-
-/* When assigning an integer value, it needs to be propagated to valuedouble too. */
-#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
-/* helper for the cJSON_SetNumberValue macro */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
-#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
-
-/* Macro for iterating over an array or object */
-#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
-
-/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
-CJSON_PUBLIC(void) cJSON_free(void *object);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
diff --git a/src/external/stb_image.h b/src/external/stb_image.h
deleted file mode 100644
index a6202a3..0000000
--- a/src/external/stb_image.h
+++ /dev/null
@@ -1,7547 +0,0 @@
-/* stb_image - v2.22 - public domain image loader - http://nothings.org/stb
- no warranty implied; use at your own risk
-
- Do this:
- #define STB_IMAGE_IMPLEMENTATION
- before you include this file in *one* C or C++ file to create the implementation.
-
- // i.e. it should look like this:
- #include ...
- #include ...
- #include ...
- #define STB_IMAGE_IMPLEMENTATION
- #include "stb_image.h"
-
- You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
- And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
-
-
- QUICK NOTES:
- Primarily of interest to game developers and other people who can
- avoid problematic images and only need the trivial interface
-
- JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
- PNG 1/2/4/8/16-bit-per-channel
-
- TGA (not sure what subset, if a subset)
- BMP non-1bpp, non-RLE
- PSD (composited view only, no extra channels, 8/16 bit-per-channel)
-
- GIF (*comp always reports as 4-channel)
- HDR (radiance rgbE format)
- PIC (Softimage PIC)
- PNM (PPM and PGM binary only)
-
- Animated GIF still needs a proper API, but here's one way to do it:
- http://gist.github.com/urraka/685d9a6340b26b830d49
-
- - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
- - decode from arbitrary I/O callbacks
- - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
-
- Full documentation under "DOCUMENTATION" below.
-
-
-LICENSE
-
- See end of file for license information.
-
-RECENT REVISION HISTORY:
-
- 2.22 (2019-03-04) gif fixes, fix warnings
- 2.21 (2019-02-25) fix typo in comment
- 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
- 2.19 (2018-02-11) fix warning
- 2.18 (2018-01-30) fix warnings
- 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
- 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
- 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
- 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
- 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
- 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
- 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
- RGB-format JPEG; remove white matting in PSD;
- allocate large structures on the stack;
- correct channel count for PNG & BMP
- 2.10 (2016-01-22) avoid warning introduced in 2.09
- 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
-
- See end of file for full revision history.
-
-
- ============================ Contributors =========================
-
- Image formats Extensions, features
- Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
- Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
- Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
- Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
- Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
- Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
- Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
- github:urraka (animated gif) Junggon Kim (PNM comments)
- Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
- socks-the-fox (16-bit PNG)
- Jeremy Sawicki (handle all ImageNet JPGs)
- Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
- Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
- Arseny Kapoulkine
- John-Mark Allen
- Carmelo J Fdez-Aguera
-
- Bug & warning fixes
- Marc LeBlanc David Woo Guillaume George Martins Mozeiko
- Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan
- Dave Moore Roy Eltham Hayaki Saito Nathan Reed
- Won Chun Luke Graham Johan Duparc Nick Verigakis
- the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh
- Janez Zemva John Bartholomew Michal Cichon github:romigrou
- Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
- Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar
- Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex
- Ryamond Barbiero Paul Du Bois Engin Manap github:grim210
- Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw
- Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus
- Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo
- Christian Floisand Kevin Schmidt JR Smith github:darealshinji
- Blazej Dariusz Roszkowski github:Michaelangel007
-*/
-
-#ifndef STBI_INCLUDE_STB_IMAGE_H
-#define STBI_INCLUDE_STB_IMAGE_H
-
-// DOCUMENTATION
-//
-// Limitations:
-// - no 12-bit-per-channel JPEG
-// - no JPEGs with arithmetic coding
-// - GIF always returns *comp=4
-//
-// Basic usage (see HDR discussion below for HDR usage):
-// int x,y,n;
-// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
-// // ... process data if not NULL ...
-// // ... x = width, y = height, n = # 8-bit components per pixel ...
-// // ... replace '0' with '1'..'4' to force that many components per pixel
-// // ... but 'n' will always be the number that it would have been if you said 0
-// stbi_image_free(data)
-//
-// Standard parameters:
-// int *x -- outputs image width in pixels
-// int *y -- outputs image height in pixels
-// int *channels_in_file -- outputs # of image components in image file
-// int desired_channels -- if non-zero, # of image components requested in result
-//
-// The return value from an image loader is an 'unsigned char *' which points
-// to the pixel data, or NULL on an allocation failure or if the image is
-// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
-// with each pixel consisting of N interleaved 8-bit components; the first
-// pixel pointed to is top-left-most in the image. There is no padding between
-// image scanlines or between pixels, regardless of format. The number of
-// components N is 'desired_channels' if desired_channels is non-zero, or
-// *channels_in_file otherwise. If desired_channels is non-zero,
-// *channels_in_file has the number of components that _would_ have been
-// output otherwise. E.g. if you set desired_channels to 4, you will always
-// get RGBA output, but you can check *channels_in_file to see if it's trivially
-// opaque because e.g. there were only 3 channels in the source image.
-//
-// An output image with N components has the following components interleaved
-// in this order in each pixel:
-//
-// N=#comp components
-// 1 grey
-// 2 grey, alpha
-// 3 red, green, blue
-// 4 red, green, blue, alpha
-//
-// If image loading fails for any reason, the return value will be NULL,
-// and *x, *y, *channels_in_file will be unchanged. The function
-// stbi_failure_reason() can be queried for an extremely brief, end-user
-// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
-// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
-// more user-friendly ones.
-//
-// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
-//
-// ===========================================================================
-//
-// UNICODE:
-//
-// If compiling for Windows and you wish to use Unicode filenames, compile
-// with
-// #define STBI_WINDOWS_UTF8
-// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert
-// Windows wchar_t filenames to utf8.
-//
-// ===========================================================================
-//
-// Philosophy
-//
-// stb libraries are designed with the following priorities:
-//
-// 1. easy to use
-// 2. easy to maintain
-// 3. good performance
-//
-// Sometimes I let "good performance" creep up in priority over "easy to maintain",
-// and for best performance I may provide less-easy-to-use APIs that give higher
-// performance, in addition to the easy-to-use ones. Nevertheless, it's important
-// to keep in mind that from the standpoint of you, a client of this library,
-// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
-//
-// Some secondary priorities arise directly from the first two, some of which
-// provide more explicit reasons why performance can't be emphasized.
-//
-// - Portable ("ease of use")
-// - Small source code footprint ("easy to maintain")
-// - No dependencies ("ease of use")
-//
-// ===========================================================================
-//
-// I/O callbacks
-//
-// I/O callbacks allow you to read from arbitrary sources, like packaged
-// files or some other source. Data read from callbacks are processed
-// through a small internal buffer (currently 128 bytes) to try to reduce
-// overhead.
-//
-// The three functions you must define are "read" (reads some bytes of data),
-// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
-//
-// ===========================================================================
-//
-// SIMD support
-//
-// The JPEG decoder will try to automatically use SIMD kernels on x86 when
-// supported by the compiler. For ARM Neon support, you must explicitly
-// request it.
-//
-// (The old do-it-yourself SIMD API is no longer supported in the current
-// code.)
-//
-// On x86, SSE2 will automatically be used when available based on a run-time
-// test; if not, the generic C versions are used as a fall-back. On ARM targets,
-// the typical path is to have separate builds for NEON and non-NEON devices
-// (at least this is true for iOS and Android). Therefore, the NEON support is
-// toggled by a build flag: define STBI_NEON to get NEON loops.
-//
-// If for some reason you do not want to use any of SIMD code, or if
-// you have issues compiling it, you can disable it entirely by
-// defining STBI_NO_SIMD.
-//
-// ===========================================================================
-//
-// HDR image support (disable by defining STBI_NO_HDR)
-//
-// stb_image supports loading HDR images in general, and currently the Radiance
-// .HDR file format specifically. You can still load any file through the existing
-// interface; if you attempt to load an HDR file, it will be automatically remapped
-// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
-// both of these constants can be reconfigured through this interface:
-//
-// stbi_hdr_to_ldr_gamma(2.2f);
-// stbi_hdr_to_ldr_scale(1.0f);
-//
-// (note, do not use _inverse_ constants; stbi_image will invert them
-// appropriately).
-//
-// Additionally, there is a new, parallel interface for loading files as
-// (linear) floats to preserve the full dynamic range:
-//
-// float *data = stbi_loadf(filename, &x, &y, &n, 0);
-//
-// If you load LDR images through this interface, those images will
-// be promoted to floating point values, run through the inverse of
-// constants corresponding to the above:
-//
-// stbi_ldr_to_hdr_scale(1.0f);
-// stbi_ldr_to_hdr_gamma(2.2f);
-//
-// Finally, given a filename (or an open file or memory block--see header
-// file for details) containing image data, you can query for the "most
-// appropriate" interface to use (that is, whether the image is HDR or
-// not), using:
-//
-// stbi_is_hdr(char *filename);
-//
-// ===========================================================================
-//
-// iPhone PNG support:
-//
-// By default we convert iphone-formatted PNGs back to RGB, even though
-// they are internally encoded differently. You can disable this conversion
-// by calling stbi_convert_iphone_png_to_rgb(0), in which case
-// you will always just get the native iphone "format" through (which
-// is BGR stored in RGB).
-//
-// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
-// pixel to remove any premultiplied alpha *only* if the image file explicitly
-// says there's premultiplied data (currently only happens in iPhone images,
-// and only if iPhone convert-to-rgb processing is on).
-//
-// ===========================================================================
-//
-// ADDITIONAL CONFIGURATION
-//
-// - You can suppress implementation of any of the decoders to reduce
-// your code footprint by #defining one or more of the following
-// symbols before creating the implementation.
-//
-// STBI_NO_JPEG
-// STBI_NO_PNG
-// STBI_NO_BMP
-// STBI_NO_PSD
-// STBI_NO_TGA
-// STBI_NO_GIF
-// STBI_NO_HDR
-// STBI_NO_PIC
-// STBI_NO_PNM (.ppm and .pgm)
-//
-// - You can request *only* certain decoders and suppress all other ones
-// (this will be more forward-compatible, as addition of new decoders
-// doesn't require you to disable them explicitly):
-//
-// STBI_ONLY_JPEG
-// STBI_ONLY_PNG
-// STBI_ONLY_BMP
-// STBI_ONLY_PSD
-// STBI_ONLY_TGA
-// STBI_ONLY_GIF
-// STBI_ONLY_HDR
-// STBI_ONLY_PIC
-// STBI_ONLY_PNM (.ppm and .pgm)
-//
-// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
-// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
-//
-
-
-#ifndef STBI_NO_STDIO
-#include <stdio.h>
-#endif // STBI_NO_STDIO
-
-#define STBI_VERSION 1
-
-enum
-{
- STBI_default = 0, // only used for desired_channels
-
- STBI_grey = 1,
- STBI_grey_alpha = 2,
- STBI_rgb = 3,
- STBI_rgb_alpha = 4
-};
-
-#include <stdlib.h>
-typedef unsigned char stbi_uc;
-typedef unsigned short stbi_us;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef STBIDEF
-#ifdef STB_IMAGE_STATIC
-#define STBIDEF static
-#else
-#define STBIDEF extern
-#endif
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// PRIMARY API - works on images of any type
-//
-
-//
-// load image by filename, open file, or memory buffer
-//
-
-typedef struct
-{
- int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
- void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
- int (*eof) (void *user); // returns nonzero if we are at end of file/data
-} stbi_io_callbacks;
-
-////////////////////////////////////
-//
-// 8-bits-per-channel interface
-//
-
-STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);
-STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
-
-#ifndef STBI_NO_STDIO
-STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
-STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
-// for stbi_load_from_file, file pointer is left pointing immediately after image
-#endif
-
-#ifndef STBI_NO_GIF
-STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
-#endif
-
-#ifdef STBI_WINDOWS_UTF8
-STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
-#endif
-
-////////////////////////////////////
-//
-// 16-bits-per-channel interface
-//
-
-STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
-STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
-
-#ifndef STBI_NO_STDIO
-STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
-STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
-#endif
-
-////////////////////////////////////
-//
-// float-per-channel interface
-//
-#ifndef STBI_NO_LINEAR
- STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
- STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
-
- #ifndef STBI_NO_STDIO
- STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
- STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
- #endif
-#endif
-
-#ifndef STBI_NO_HDR
- STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
- STBIDEF void stbi_hdr_to_ldr_scale(float scale);
-#endif // STBI_NO_HDR
-
-#ifndef STBI_NO_LINEAR
- STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
- STBIDEF void stbi_ldr_to_hdr_scale(float scale);
-#endif // STBI_NO_LINEAR
-
-// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
-STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
-STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
-#ifndef STBI_NO_STDIO
-STBIDEF int stbi_is_hdr (char const *filename);
-STBIDEF int stbi_is_hdr_from_file(FILE *f);
-#endif // STBI_NO_STDIO
-
-
-// get a VERY brief reason for failure
-// NOT THREADSAFE
-STBIDEF const char *stbi_failure_reason (void);
-
-// free the loaded image -- this is just free()
-STBIDEF void stbi_image_free (void *retval_from_stbi_load);
-
-// get image dimensions & components without fully decoding
-STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
-STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
-STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
-STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
-
-#ifndef STBI_NO_STDIO
-STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
-STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
-STBIDEF int stbi_is_16_bit (char const *filename);
-STBIDEF int stbi_is_16_bit_from_file(FILE *f);
-#endif
-
-
-
-// for image formats that explicitly notate that they have premultiplied alpha,
-// we just return the colors as stored in the file. set this flag to force
-// unpremultiplication. results are undefined if the unpremultiply overflow.
-STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
-
-// indicate whether we should process iphone images back to canonical format,
-// or just pass them through "as-is"
-STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
-
-// flip the image vertically, so the first pixel in the output array is the bottom left
-STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
-
-// ZLIB client - used by PNG, available for other purposes
-
-STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
-STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
-STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
-STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
-
-STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
-STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-//
-//
-//// end header file /////////////////////////////////////////////////////
-#endif // STBI_INCLUDE_STB_IMAGE_H
-
-#ifdef STB_IMAGE_IMPLEMENTATION
-
-#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
- || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
- || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
- || defined(STBI_ONLY_ZLIB)
- #ifndef STBI_ONLY_JPEG
- #define STBI_NO_JPEG
- #endif
- #ifndef STBI_ONLY_PNG
- #define STBI_NO_PNG
- #endif
- #ifndef STBI_ONLY_BMP
- #define STBI_NO_BMP
- #endif
- #ifndef STBI_ONLY_PSD
- #define STBI_NO_PSD
- #endif
- #ifndef STBI_ONLY_TGA
- #define STBI_NO_TGA
- #endif
- #ifndef STBI_ONLY_GIF
- #define STBI_NO_GIF
- #endif
- #ifndef STBI_ONLY_HDR
- #define STBI_NO_HDR
- #endif
- #ifndef STBI_ONLY_PIC
- #define STBI_NO_PIC
- #endif
- #ifndef STBI_ONLY_PNM
- #define STBI_NO_PNM
- #endif
-#endif
-
-#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
-#define STBI_NO_ZLIB
-#endif
-
-
-#include <stdarg.h>
-#include <stddef.h> // ptrdiff_t on osx
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
-#include <math.h> // ldexp, pow
-#endif
-
-#ifndef STBI_NO_STDIO
-#include <stdio.h>
-#endif
-
-#ifndef STBI_ASSERT
-#include <assert.h>
-#define STBI_ASSERT(x) assert(x)
-#endif
-
-#ifdef __cplusplus
-#define STBI_EXTERN extern "C"
-#else
-#define STBI_EXTERN extern
-#endif
-
-
-#ifndef _MSC_VER
- #ifdef __cplusplus
- #define stbi_inline inline
- #else
- #define stbi_inline
- #endif
-#else
- #define stbi_inline __forceinline
-#endif
-
-
-#ifdef _MSC_VER
-typedef unsigned short stbi__uint16;
-typedef signed short stbi__int16;
-typedef unsigned int stbi__uint32;
-typedef signed int stbi__int32;
-#else
-#include <stdint.h>
-typedef uint16_t stbi__uint16;
-typedef int16_t stbi__int16;
-typedef uint32_t stbi__uint32;
-typedef int32_t stbi__int32;
-#endif
-
-// should produce compiler error if size is wrong
-typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
-
-#ifdef _MSC_VER
-#define STBI_NOTUSED(v) (void)(v)
-#else
-#define STBI_NOTUSED(v) (void)sizeof(v)
-#endif
-
-#ifdef _MSC_VER
-#define STBI_HAS_LROTL
-#endif
-
-#ifdef STBI_HAS_LROTL
- #define stbi_lrot(x,y) _lrotl(x,y)
-#else
- #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
-#endif
-
-#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
-// ok
-#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
-// ok
-#else
-#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
-#endif
-
-#ifndef STBI_MALLOC
-#define STBI_MALLOC(sz) malloc(sz)
-#define STBI_REALLOC(p,newsz) realloc(p,newsz)
-#define STBI_FREE(p) free(p)
-#endif
-
-#ifndef STBI_REALLOC_SIZED
-#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
-#endif
-
-// x86/x64 detection
-#if defined(__x86_64__) || defined(_M_X64)
-#define STBI__X64_TARGET
-#elif defined(__i386) || defined(_M_IX86)
-#define STBI__X86_TARGET
-#endif
-
-#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
-// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
-// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
-// but previous attempts to provide the SSE2 functions with runtime
-// detection caused numerous issues. The way architecture extensions are
-// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
-// New behavior: if compiled with -msse2, we use SSE2 without any
-// detection; if not, we don't use it at all.
-#define STBI_NO_SIMD
-#endif
-
-#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
-// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
-//
-// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
-// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
-// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
-// simultaneously enabling "-mstackrealign".
-//
-// See https://github.com/nothings/stb/issues/81 for more information.
-//
-// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
-// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
-#define STBI_NO_SIMD
-#endif
-
-#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
-#define STBI_SSE2
-#include <emmintrin.h>
-
-#ifdef _MSC_VER
-
-#if _MSC_VER >= 1400 // not VC6
-#include <intrin.h> // __cpuid
-static int stbi__cpuid3(void)
-{
- int info[4];
- __cpuid(info,1);
- return info[3];
-}
-#else
-static int stbi__cpuid3(void)
-{
- int res;
- __asm {
- mov eax,1
- cpuid
- mov res,edx
- }
- return res;
-}
-#endif
-
-#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
-
-#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
-static int stbi__sse2_available(void)
-{
- int info3 = stbi__cpuid3();
- return ((info3 >> 26) & 1) != 0;
-}
-#endif
-
-#else // assume GCC-style if not VC++
-#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
-
-#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
-static int stbi__sse2_available(void)
-{
- // If we're even attempting to compile this on GCC/Clang, that means
- // -msse2 is on, which means the compiler is allowed to use SSE2
- // instructions at will, and so are we.
- return 1;
-}
-#endif
-
-#endif
-#endif
-
-// ARM NEON
-#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
-#undef STBI_NEON
-#endif
-
-#ifdef STBI_NEON
-#include <arm_neon.h>
-// assume GCC or Clang on ARM targets
-#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
-#endif
-
-#ifndef STBI_SIMD_ALIGN
-#define STBI_SIMD_ALIGN(type, name) type name
-#endif
-
-///////////////////////////////////////////////
-//
-// stbi__context struct and start_xxx functions
-
-// stbi__context structure is our basic context used by all images, so it
-// contains all the IO context, plus some basic image information
-typedef struct
-{
- stbi__uint32 img_x, img_y;
- int img_n, img_out_n;
-
- stbi_io_callbacks io;
- void *io_user_data;
-
- int read_from_callbacks;
- int buflen;
- stbi_uc buffer_start[128];
-
- stbi_uc *img_buffer, *img_buffer_end;
- stbi_uc *img_buffer_original, *img_buffer_original_end;
-} stbi__context;
-
-
-static void stbi__refill_buffer(stbi__context *s);
-
-// initialize a memory-decode context
-static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
-{
- s->io.read = NULL;
- s->read_from_callbacks = 0;
- s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
- s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
-}
-
-// initialize a callback-based context
-static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
-{
- s->io = *c;
- s->io_user_data = user;
- s->buflen = sizeof(s->buffer_start);
- s->read_from_callbacks = 1;
- s->img_buffer_original = s->buffer_start;
- stbi__refill_buffer(s);
- s->img_buffer_original_end = s->img_buffer_end;
-}
-
-#ifndef STBI_NO_STDIO
-
-static int stbi__stdio_read(void *user, char *data, int size)
-{
- return (int) fread(data,1,size,(FILE*) user);
-}
-
-static void stbi__stdio_skip(void *user, int n)
-{
- fseek((FILE*) user, n, SEEK_CUR);
-}
-
-static int stbi__stdio_eof(void *user)
-{
- return feof((FILE*) user);
-}
-
-static stbi_io_callbacks stbi__stdio_callbacks =
-{
- stbi__stdio_read,
- stbi__stdio_skip,
- stbi__stdio_eof,
-};
-
-static void stbi__start_file(stbi__context *s, FILE *f)
-{
- stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
-}
-
-//static void stop_file(stbi__context *s) { }
-
-#endif // !STBI_NO_STDIO
-
-static void stbi__rewind(stbi__context *s)
-{
- // conceptually rewind SHOULD rewind to the beginning of the stream,
- // but we just rewind to the beginning of the initial buffer, because
- // we only use it after doing 'test', which only ever looks at at most 92 bytes
- s->img_buffer = s->img_buffer_original;
- s->img_buffer_end = s->img_buffer_original_end;
-}
-
-enum
-{
- STBI_ORDER_RGB,
- STBI_ORDER_BGR
-};
-
-typedef struct
-{
- int bits_per_channel;
- int num_channels;
- int channel_order;
-} stbi__result_info;
-
-#ifndef STBI_NO_JPEG
-static int stbi__jpeg_test(stbi__context *s);
-static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_PNG
-static int stbi__png_test(stbi__context *s);
-static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
-static int stbi__png_is16(stbi__context *s);
-#endif
-
-#ifndef STBI_NO_BMP
-static int stbi__bmp_test(stbi__context *s);
-static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_TGA
-static int stbi__tga_test(stbi__context *s);
-static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_PSD
-static int stbi__psd_test(stbi__context *s);
-static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);
-static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);
-static int stbi__psd_is16(stbi__context *s);
-#endif
-
-#ifndef STBI_NO_HDR
-static int stbi__hdr_test(stbi__context *s);
-static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_PIC
-static int stbi__pic_test(stbi__context *s);
-static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_GIF
-static int stbi__gif_test(stbi__context *s);
-static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
-static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-#ifndef STBI_NO_PNM
-static int stbi__pnm_test(stbi__context *s);
-static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
-static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
-#endif
-
-// this is not threadsafe
-static const char *stbi__g_failure_reason;
-
-STBIDEF const char *stbi_failure_reason(void)
-{
- return stbi__g_failure_reason;
-}
-
-static int stbi__err(const char *str)
-{
- stbi__g_failure_reason = str;
- return 0;
-}
-
-static void *stbi__malloc(size_t size)
-{
- return STBI_MALLOC(size);
-}
-
-// stb_image uses ints pervasively, including for offset calculations.
-// therefore the largest decoded image size we can support with the
-// current code, even on 64-bit targets, is INT_MAX. this is not a
-// significant limitation for the intended use case.
-//
-// we do, however, need to make sure our size calculations don't
-// overflow. hence a few helper functions for size calculations that
-// multiply integers together, making sure that they're non-negative
-// and no overflow occurs.
-
-// return 1 if the sum is valid, 0 on overflow.
-// negative terms are considered invalid.
-static int stbi__addsizes_valid(int a, int b)
-{
- if (b < 0) return 0;
- // now 0 <= b <= INT_MAX, hence also
- // 0 <= INT_MAX - b <= INTMAX.
- // And "a + b <= INT_MAX" (which might overflow) is the
- // same as a <= INT_MAX - b (no overflow)
- return a <= INT_MAX - b;
-}
-
-// returns 1 if the product is valid, 0 on overflow.
-// negative factors are considered invalid.
-static int stbi__mul2sizes_valid(int a, int b)
-{
- if (a < 0 || b < 0) return 0;
- if (b == 0) return 1; // mul-by-0 is always safe
- // portable way to check for no overflows in a*b
- return a <= INT_MAX/b;
-}
-
-// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
-static int stbi__mad2sizes_valid(int a, int b, int add)
-{
- return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
-}
-
-// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
-static int stbi__mad3sizes_valid(int a, int b, int c, int add)
-{
- return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
- stbi__addsizes_valid(a*b*c, add);
-}
-
-// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
-#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
-static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
-{
- return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
- stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);
-}
-#endif
-
-// mallocs with size overflow checking
-static void *stbi__malloc_mad2(int a, int b, int add)
-{
- if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
- return stbi__malloc(a*b + add);
-}
-
-static void *stbi__malloc_mad3(int a, int b, int c, int add)
-{
- if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
- return stbi__malloc(a*b*c + add);
-}
-
-#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
-static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
-{
- if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
- return stbi__malloc(a*b*c*d + add);
-}
-#endif
-
-// stbi__err - error
-// stbi__errpf - error returning pointer to float
-// stbi__errpuc - error returning pointer to unsigned char
-
-#ifdef STBI_NO_FAILURE_STRINGS
- #define stbi__err(x,y) 0
-#elif defined(STBI_FAILURE_USERMSG)
- #define stbi__err(x,y) stbi__err(y)
-#else
- #define stbi__err(x,y) stbi__err(x)
-#endif
-
-#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
-#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
-
-STBIDEF void stbi_image_free(void *retval_from_stbi_load)
-{
- STBI_FREE(retval_from_stbi_load);
-}
-
-#ifndef STBI_NO_LINEAR
-static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
-#endif
-
-#ifndef STBI_NO_HDR
-static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
-#endif
-
-static int stbi__vertically_flip_on_load = 0;
-
-STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
-{
- stbi__vertically_flip_on_load = flag_true_if_should_flip;
-}
-
-static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
-{
- memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
- ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
- ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
- ri->num_channels = 0;
-
- #ifndef STBI_NO_JPEG
- if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
- #endif
- #ifndef STBI_NO_PNG
- if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
- #endif
- #ifndef STBI_NO_BMP
- if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri);
- #endif
- #ifndef STBI_NO_GIF
- if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri);
- #endif
- #ifndef STBI_NO_PSD
- if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);
- #endif
- #ifndef STBI_NO_PIC
- if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
- #endif
- #ifndef STBI_NO_PNM
- if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
- #endif
-
- #ifndef STBI_NO_HDR
- if (stbi__hdr_test(s)) {
- float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);
- return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
- }
- #endif
-
- #ifndef STBI_NO_TGA
- // test tga last because it's a crappy test!
- if (stbi__tga_test(s))
- return stbi__tga_load(s,x,y,comp,req_comp, ri);
- #endif
-
- return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
-}
-
-static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)
-{
- int i;
- int img_len = w * h * channels;
- stbi_uc *reduced;
-
- reduced = (stbi_uc *) stbi__malloc(img_len);
- if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
-
- for (i = 0; i < img_len; ++i)
- reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
-
- STBI_FREE(orig);
- return reduced;
-}
-
-static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)
-{
- int i;
- int img_len = w * h * channels;
- stbi__uint16 *enlarged;
-
- enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
- if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
-
- for (i = 0; i < img_len; ++i)
- enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
-
- STBI_FREE(orig);
- return enlarged;
-}
-
-static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
-{
- int row;
- size_t bytes_per_row = (size_t)w * bytes_per_pixel;
- stbi_uc temp[2048];
- stbi_uc *bytes = (stbi_uc *)image;
-
- for (row = 0; row < (h>>1); row++) {
- stbi_uc *row0 = bytes + row*bytes_per_row;
- stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
- // swap row0 with row1
- size_t bytes_left = bytes_per_row;
- while (bytes_left) {
- size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
- memcpy(temp, row0, bytes_copy);
- memcpy(row0, row1, bytes_copy);
- memcpy(row1, temp, bytes_copy);
- row0 += bytes_copy;
- row1 += bytes_copy;
- bytes_left -= bytes_copy;
- }
- }
-}
-
-#ifndef STBI_NO_GIF
-static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel)
-{
- int slice;
- int slice_size = w * h * bytes_per_pixel;
-
- stbi_uc *bytes = (stbi_uc *)image;
- for (slice = 0; slice < z; ++slice) {
- stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
- bytes += slice_size;
- }
-}
-#endif
-
-static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
-{
- stbi__result_info ri;
- void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
-
- if (result == NULL)
- return NULL;
-
- if (ri.bits_per_channel != 8) {
- STBI_ASSERT(ri.bits_per_channel == 16);
- result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
- ri.bits_per_channel = 8;
- }
-
- // @TODO: move stbi__convert_format to here
-
- if (stbi__vertically_flip_on_load) {
- int channels = req_comp ? req_comp : *comp;
- stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
- }
-
- return (unsigned char *) result;
-}
-
-static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
-{
- stbi__result_info ri;
- void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
-
- if (result == NULL)
- return NULL;
-
- if (ri.bits_per_channel != 16) {
- STBI_ASSERT(ri.bits_per_channel == 8);
- result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
- ri.bits_per_channel = 16;
- }
-
- // @TODO: move stbi__convert_format16 to here
- // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
-
- if (stbi__vertically_flip_on_load) {
- int channels = req_comp ? req_comp : *comp;
- stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
- }
-
- return (stbi__uint16 *) result;
-}
-
-#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)
-static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
-{
- if (stbi__vertically_flip_on_load && result != NULL) {
- int channels = req_comp ? req_comp : *comp;
- stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
- }
-}
-#endif
-
-#ifndef STBI_NO_STDIO
-
-#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
-STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
-STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
-#endif
-
-#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
-STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
-{
- return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
-}
-#endif
-
-static FILE *stbi__fopen(char const *filename, char const *mode)
-{
- FILE *f;
-#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
- wchar_t wMode[64];
- wchar_t wFilename[1024];
- if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
- return 0;
-
- if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
- return 0;
-
-#if _MSC_VER >= 1400
- if (0 != _wfopen_s(&f, wFilename, wMode))
- f = 0;
-#else
- f = _wfopen(wFilename, wMode);
-#endif
-
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- if (0 != fopen_s(&f, filename, mode))
- f=0;
-#else
- f = fopen(filename, mode);
-#endif
- return f;
-}
-
-
-STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
-{
- FILE *f = stbi__fopen(filename, "rb");
- unsigned char *result;
- if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
- result = stbi_load_from_file(f,x,y,comp,req_comp);
- fclose(f);
- return result;
-}
-
-STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- unsigned char *result;
- stbi__context s;
- stbi__start_file(&s,f);
- result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
- if (result) {
- // need to 'unget' all the characters in the IO buffer
- fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
- }
- return result;
-}
-
-STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- stbi__uint16 *result;
- stbi__context s;
- stbi__start_file(&s,f);
- result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
- if (result) {
- // need to 'unget' all the characters in the IO buffer
- fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
- }
- return result;
-}
-
-STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp)
-{
- FILE *f = stbi__fopen(filename, "rb");
- stbi__uint16 *result;
- if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file");
- result = stbi_load_from_file_16(f,x,y,comp,req_comp);
- fclose(f);
- return result;
-}
-
-
-#endif //!STBI_NO_STDIO
-
-STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
-{
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
-}
-
-STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)
-{
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
- return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
-}
-
-STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
-}
-
-STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
-{
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
- return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
-}
-
-#ifndef STBI_NO_GIF
-STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
-{
- unsigned char *result;
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
-
- result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
- if (stbi__vertically_flip_on_load) {
- stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
- }
-
- return result;
-}
-#endif
-
-#ifndef STBI_NO_LINEAR
-static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
-{
- unsigned char *data;
- #ifndef STBI_NO_HDR
- if (stbi__hdr_test(s)) {
- stbi__result_info ri;
- float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);
- if (hdr_data)
- stbi__float_postprocess(hdr_data,x,y,comp,req_comp);
- return hdr_data;
- }
- #endif
- data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
- if (data)
- return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
- return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
-}
-
-STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
-{
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__loadf_main(&s,x,y,comp,req_comp);
-}
-
-STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
-{
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
- return stbi__loadf_main(&s,x,y,comp,req_comp);
-}
-
-#ifndef STBI_NO_STDIO
-STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
-{
- float *result;
- FILE *f = stbi__fopen(filename, "rb");
- if (!f) return stbi__errpf("can't fopen", "Unable to open file");
- result = stbi_loadf_from_file(f,x,y,comp,req_comp);
- fclose(f);
- return result;
-}
-
-STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
-{
- stbi__context s;
- stbi__start_file(&s,f);
- return stbi__loadf_main(&s,x,y,comp,req_comp);
-}
-#endif // !STBI_NO_STDIO
-
-#endif // !STBI_NO_LINEAR
-
-// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
-// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
-// reports false!
-
-STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
-{
- #ifndef STBI_NO_HDR
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__hdr_test(&s);
- #else
- STBI_NOTUSED(buffer);
- STBI_NOTUSED(len);
- return 0;
- #endif
-}
-
-#ifndef STBI_NO_STDIO
-STBIDEF int stbi_is_hdr (char const *filename)
-{
- FILE *f = stbi__fopen(filename, "rb");
- int result=0;
- if (f) {
- result = stbi_is_hdr_from_file(f);
- fclose(f);
- }
- return result;
-}
-
-STBIDEF int stbi_is_hdr_from_file(FILE *f)
-{
- #ifndef STBI_NO_HDR
- long pos = ftell(f);
- int res;
- stbi__context s;
- stbi__start_file(&s,f);
- res = stbi__hdr_test(&s);
- fseek(f, pos, SEEK_SET);
- return res;
- #else
- STBI_NOTUSED(f);
- return 0;
- #endif
-}
-#endif // !STBI_NO_STDIO
-
-STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
-{
- #ifndef STBI_NO_HDR
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
- return stbi__hdr_test(&s);
- #else
- STBI_NOTUSED(clbk);
- STBI_NOTUSED(user);
- return 0;
- #endif
-}
-
-#ifndef STBI_NO_LINEAR
-static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
-
-STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
-STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
-#endif
-
-static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
-
-STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
-STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Common code used by all image loaders
-//
-
-enum
-{
- STBI__SCAN_load=0,
- STBI__SCAN_type,
- STBI__SCAN_header
-};
-
-static void stbi__refill_buffer(stbi__context *s)
-{
- int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
- if (n == 0) {
- // at end of file, treat same as if from memory, but need to handle case
- // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
- s->read_from_callbacks = 0;
- s->img_buffer = s->buffer_start;
- s->img_buffer_end = s->buffer_start+1;
- *s->img_buffer = 0;
- } else {
- s->img_buffer = s->buffer_start;
- s->img_buffer_end = s->buffer_start + n;
- }
-}
-
-stbi_inline static stbi_uc stbi__get8(stbi__context *s)
-{
- if (s->img_buffer < s->img_buffer_end)
- return *s->img_buffer++;
- if (s->read_from_callbacks) {
- stbi__refill_buffer(s);
- return *s->img_buffer++;
- }
- return 0;
-}
-
-stbi_inline static int stbi__at_eof(stbi__context *s)
-{
- if (s->io.read) {
- if (!(s->io.eof)(s->io_user_data)) return 0;
- // if feof() is true, check if buffer = end
- // special case: we've only got the special 0 character at the end
- if (s->read_from_callbacks == 0) return 1;
- }
-
- return s->img_buffer >= s->img_buffer_end;
-}
-
-static void stbi__skip(stbi__context *s, int n)
-{
- if (n < 0) {
- s->img_buffer = s->img_buffer_end;
- return;
- }
- if (s->io.read) {
- int blen = (int) (s->img_buffer_end - s->img_buffer);
- if (blen < n) {
- s->img_buffer = s->img_buffer_end;
- (s->io.skip)(s->io_user_data, n - blen);
- return;
- }
- }
- s->img_buffer += n;
-}
-
-static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
-{
- if (s->io.read) {
- int blen = (int) (s->img_buffer_end - s->img_buffer);
- if (blen < n) {
- int res, count;
-
- memcpy(buffer, s->img_buffer, blen);
-
- count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
- res = (count == (n-blen));
- s->img_buffer = s->img_buffer_end;
- return res;
- }
- }
-
- if (s->img_buffer+n <= s->img_buffer_end) {
- memcpy(buffer, s->img_buffer, n);
- s->img_buffer += n;
- return 1;
- } else
- return 0;
-}
-
-static int stbi__get16be(stbi__context *s)
-{
- int z = stbi__get8(s);
- return (z << 8) + stbi__get8(s);
-}
-
-static stbi__uint32 stbi__get32be(stbi__context *s)
-{
- stbi__uint32 z = stbi__get16be(s);
- return (z << 16) + stbi__get16be(s);
-}
-
-#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
-// nothing
-#else
-static int stbi__get16le(stbi__context *s)
-{
- int z = stbi__get8(s);
- return z + (stbi__get8(s) << 8);
-}
-#endif
-
-#ifndef STBI_NO_BMP
-static stbi__uint32 stbi__get32le(stbi__context *s)
-{
- stbi__uint32 z = stbi__get16le(s);
- return z + (stbi__get16le(s) << 16);
-}
-#endif
-
-#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// generic converter from built-in img_n to req_comp
-// individual types do this automatically as much as possible (e.g. jpeg
-// does all cases internally since it needs to colorspace convert anyway,
-// and it never has alpha, so very few cases ). png can automatically
-// interleave an alpha=255 channel, but falls back to this for other cases
-//
-// assume data buffer is malloced, so malloc a new one and free that one
-// only failure mode is malloc failing
-
-static stbi_uc stbi__compute_y(int r, int g, int b)
-{
- return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
-}
-
-static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
-{
- int i,j;
- unsigned char *good;
-
- if (req_comp == img_n) return data;
- STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
-
- good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);
- if (good == NULL) {
- STBI_FREE(data);
- return stbi__errpuc("outofmem", "Out of memory");
- }
-
- for (j=0; j < (int) y; ++j) {
- unsigned char *src = data + j * x * img_n ;
- unsigned char *dest = good + j * x * req_comp;
-
- #define STBI__COMBO(a,b) ((a)*8+(b))
- #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
- // convert source image with img_n components to one with req_comp components;
- // avoid switch per pixel, so use switch per scanline and massive macros
- switch (STBI__COMBO(img_n, req_comp)) {
- STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break;
- STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
- STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break;
- STBI__CASE(2,1) { dest[0]=src[0]; } break;
- STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
- STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
- STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break;
- STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
- STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break;
- STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
- STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;
- STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
- default: STBI_ASSERT(0);
- }
- #undef STBI__CASE
- }
-
- STBI_FREE(data);
- return good;
-}
-
-static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
-{
- return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
-}
-
-static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
-{
- int i,j;
- stbi__uint16 *good;
-
- if (req_comp == img_n) return data;
- STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
-
- good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);
- if (good == NULL) {
- STBI_FREE(data);
- return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
- }
-
- for (j=0; j < (int) y; ++j) {
- stbi__uint16 *src = data + j * x * img_n ;
- stbi__uint16 *dest = good + j * x * req_comp;
-
- #define STBI__COMBO(a,b) ((a)*8+(b))
- #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
- // convert source image with img_n components to one with req_comp components;
- // avoid switch per pixel, so use switch per scanline and massive macros
- switch (STBI__COMBO(img_n, req_comp)) {
- STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break;
- STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
- STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break;
- STBI__CASE(2,1) { dest[0]=src[0]; } break;
- STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
- STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
- STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break;
- STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
- STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break;
- STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
- STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break;
- STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
- default: STBI_ASSERT(0);
- }
- #undef STBI__CASE
- }
-
- STBI_FREE(data);
- return good;
-}
-
-#ifndef STBI_NO_LINEAR
-static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
-{
- int i,k,n;
- float *output;
- if (!data) return NULL;
- output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
- if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); }
- // compute number of non-alpha components
- if (comp & 1) n = comp; else n = comp-1;
- for (i=0; i < x*y; ++i) {
- for (k=0; k < n; ++k) {
- output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
- }
- }
- if (n < comp) {
- for (i=0; i < x*y; ++i) {
- output[i*comp + n] = data[i*comp + n]/255.0f;
- }
- }
- STBI_FREE(data);
- return output;
-}
-#endif
-
-#ifndef STBI_NO_HDR
-#define stbi__float2int(x) ((int) (x))
-static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
-{
- int i,k,n;
- stbi_uc *output;
- if (!data) return NULL;
- output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);
- if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); }
- // compute number of non-alpha components
- if (comp & 1) n = comp; else n = comp-1;
- for (i=0; i < x*y; ++i) {
- for (k=0; k < n; ++k) {
- float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
- if (z < 0) z = 0;
- if (z > 255) z = 255;
- output[i*comp + k] = (stbi_uc) stbi__float2int(z);
- }
- if (k < comp) {
- float z = data[i*comp+k] * 255 + 0.5f;
- if (z < 0) z = 0;
- if (z > 255) z = 255;
- output[i*comp + k] = (stbi_uc) stbi__float2int(z);
- }
- }
- STBI_FREE(data);
- return output;
-}
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// "baseline" JPEG/JFIF decoder
-//
-// simple implementation
-// - doesn't support delayed output of y-dimension
-// - simple interface (only one output format: 8-bit interleaved RGB)
-// - doesn't try to recover corrupt jpegs
-// - doesn't allow partial loading, loading multiple at once
-// - still fast on x86 (copying globals into locals doesn't help x86)
-// - allocates lots of intermediate memory (full size of all components)
-// - non-interleaved case requires this anyway
-// - allows good upsampling (see next)
-// high-quality
-// - upsampled channels are bilinearly interpolated, even across blocks
-// - quality integer IDCT derived from IJG's 'slow'
-// performance
-// - fast huffman; reasonable integer IDCT
-// - some SIMD kernels for common paths on targets with SSE2/NEON
-// - uses a lot of intermediate memory, could cache poorly
-
-#ifndef STBI_NO_JPEG
-
-// huffman decoding acceleration
-#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
-
-typedef struct
-{
- stbi_uc fast[1 << FAST_BITS];
- // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
- stbi__uint16 code[256];
- stbi_uc values[256];
- stbi_uc size[257];
- unsigned int maxcode[18];
- int delta[17]; // old 'firstsymbol' - old 'firstcode'
-} stbi__huffman;
-
-typedef struct
-{
- stbi__context *s;
- stbi__huffman huff_dc[4];
- stbi__huffman huff_ac[4];
- stbi__uint16 dequant[4][64];
- stbi__int16 fast_ac[4][1 << FAST_BITS];
-
-// sizes for components, interleaved MCUs
- int img_h_max, img_v_max;
- int img_mcu_x, img_mcu_y;
- int img_mcu_w, img_mcu_h;
-
-// definition of jpeg image component
- struct
- {
- int id;
- int h,v;
- int tq;
- int hd,ha;
- int dc_pred;
-
- int x,y,w2,h2;
- stbi_uc *data;
- void *raw_data, *raw_coeff;
- stbi_uc *linebuf;
- short *coeff; // progressive only
- int coeff_w, coeff_h; // number of 8x8 coefficient blocks
- } img_comp[4];
-
- stbi__uint32 code_buffer; // jpeg entropy-coded buffer
- int code_bits; // number of valid bits
- unsigned char marker; // marker seen while filling entropy buffer
- int nomore; // flag if we saw a marker so must stop
-
- int progressive;
- int spec_start;
- int spec_end;
- int succ_high;
- int succ_low;
- int eob_run;
- int jfif;
- int app14_color_transform; // Adobe APP14 tag
- int rgb;
-
- int scan_n, order[4];
- int restart_interval, todo;
-
-// kernels
- void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);
- void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);
- stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);
-} stbi__jpeg;
-
-static int stbi__build_huffman(stbi__huffman *h, int *count)
-{
- int i,j,k=0;
- unsigned int code;
- // build size list for each symbol (from JPEG spec)
- for (i=0; i < 16; ++i)
- for (j=0; j < count[i]; ++j)
- h->size[k++] = (stbi_uc) (i+1);
- h->size[k] = 0;
-
- // compute actual symbols (from jpeg spec)
- code = 0;
- k = 0;
- for(j=1; j <= 16; ++j) {
- // compute delta to add to code to compute symbol id
- h->delta[j] = k - code;
- if (h->size[k] == j) {
- while (h->size[k] == j)
- h->code[k++] = (stbi__uint16) (code++);
- if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG");
- }
- // compute largest code + 1 for this size, preshifted as needed later
- h->maxcode[j] = code << (16-j);
- code <<= 1;
- }
- h->maxcode[j] = 0xffffffff;
-
- // build non-spec acceleration table; 255 is flag for not-accelerated
- memset(h->fast, 255, 1 << FAST_BITS);
- for (i=0; i < k; ++i) {
- int s = h->size[i];
- if (s <= FAST_BITS) {
- int c = h->code[i] << (FAST_BITS-s);
- int m = 1 << (FAST_BITS-s);
- for (j=0; j < m; ++j) {
- h->fast[c+j] = (stbi_uc) i;
- }
- }
- }
- return 1;
-}
-
-// build a table that decodes both magnitude and value of small ACs in
-// one go.
-static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)
-{
- int i;
- for (i=0; i < (1 << FAST_BITS); ++i) {
- stbi_uc fast = h->fast[i];
- fast_ac[i] = 0;
- if (fast < 255) {
- int rs = h->values[fast];
- int run = (rs >> 4) & 15;
- int magbits = rs & 15;
- int len = h->size[fast];
-
- if (magbits && len + magbits <= FAST_BITS) {
- // magnitude code followed by receive_extend code
- int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
- int m = 1 << (magbits - 1);
- if (k < m) k += (~0U << magbits) + 1;
- // if the result is small enough, we can fit it in fast_ac table
- if (k >= -128 && k <= 127)
- fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));
- }
- }
- }
-}
-
-static void stbi__grow_buffer_unsafe(stbi__jpeg *j)
-{
- do {
- unsigned int b = j->nomore ? 0 : stbi__get8(j->s);
- if (b == 0xff) {
- int c = stbi__get8(j->s);
- while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes
- if (c != 0) {
- j->marker = (unsigned char) c;
- j->nomore = 1;
- return;
- }
- }
- j->code_buffer |= b << (24 - j->code_bits);
- j->code_bits += 8;
- } while (j->code_bits <= 24);
-}
-
-// (1 << n) - 1
-static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
-
-// decode a jpeg huffman value from the bitstream
-stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
-{
- unsigned int temp;
- int c,k;
-
- if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
-
- // look at the top FAST_BITS and determine what symbol ID it is,
- // if the code is <= FAST_BITS
- c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
- k = h->fast[c];
- if (k < 255) {
- int s = h->size[k];
- if (s > j->code_bits)
- return -1;
- j->code_buffer <<= s;
- j->code_bits -= s;
- return h->values[k];
- }
-
- // naive test is to shift the code_buffer down so k bits are
- // valid, then test against maxcode. To speed this up, we've
- // preshifted maxcode left so that it has (16-k) 0s at the
- // end; in other words, regardless of the number of bits, it
- // wants to be compared against something shifted to have 16;
- // that way we don't need to shift inside the loop.
- temp = j->code_buffer >> 16;
- for (k=FAST_BITS+1 ; ; ++k)
- if (temp < h->maxcode[k])
- break;
- if (k == 17) {
- // error! code not found
- j->code_bits -= 16;
- return -1;
- }
-
- if (k > j->code_bits)
- return -1;
-
- // convert the huffman code to the symbol id
- c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
- STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
-
- // convert the id to a symbol
- j->code_bits -= k;
- j->code_buffer <<= k;
- return h->values[c];
-}
-
-// bias[n] = (-1<<n) + 1
-static const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};
-
-// combined JPEG 'receive' and JPEG 'extend', since baseline
-// always extends everything it receives.
-stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
-{
- unsigned int k;
- int sgn;
- if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
-
- sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
- k = stbi_lrot(j->code_buffer, n);
- STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));
- j->code_buffer = k & ~stbi__bmask[n];
- k &= stbi__bmask[n];
- j->code_bits -= n;
- return k + (stbi__jbias[n] & ~sgn);
-}
-
-// get some unsigned bits
-stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
-{
- unsigned int k;
- if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
- k = stbi_lrot(j->code_buffer, n);
- j->code_buffer = k & ~stbi__bmask[n];
- k &= stbi__bmask[n];
- j->code_bits -= n;
- return k;
-}
-
-stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
-{
- unsigned int k;
- if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
- k = j->code_buffer;
- j->code_buffer <<= 1;
- --j->code_bits;
- return k & 0x80000000;
-}
-
-// given a value that's at position X in the zigzag stream,
-// where does it appear in the 8x8 matrix coded as row-major?
-static const stbi_uc stbi__jpeg_dezigzag[64+15] =
-{
- 0, 1, 8, 16, 9, 2, 3, 10,
- 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34,
- 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36,
- 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46,
- 53, 60, 61, 54, 47, 55, 62, 63,
- // let corrupt input sample past end
- 63, 63, 63, 63, 63, 63, 63, 63,
- 63, 63, 63, 63, 63, 63, 63
-};
-
-// decode one 64-entry block--
-static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant)
-{
- int diff,dc,k;
- int t;
-
- if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
- t = stbi__jpeg_huff_decode(j, hdc);
- if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
-
- // 0 all the ac values now so we can do it 32-bits at a time
- memset(data,0,64*sizeof(data[0]));
-
- diff = t ? stbi__extend_receive(j, t) : 0;
- dc = j->img_comp[b].dc_pred + diff;
- j->img_comp[b].dc_pred = dc;
- data[0] = (short) (dc * dequant[0]);
-
- // decode AC components, see JPEG spec
- k = 1;
- do {
- unsigned int zig;
- int c,r,s;
- if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
- c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
- r = fac[c];
- if (r) { // fast-AC path
- k += (r >> 4) & 15; // run
- s = r & 15; // combined length
- j->code_buffer <<= s;
- j->code_bits -= s;
- // decode into unzigzag'd location
- zig = stbi__jpeg_dezigzag[k++];
- data[zig] = (short) ((r >> 8) * dequant[zig]);
- } else {
- int rs = stbi__jpeg_huff_decode(j, hac);
- if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
- s = rs & 15;
- r = rs >> 4;
- if (s == 0) {
- if (rs != 0xf0) break; // end block
- k += 16;
- } else {
- k += r;
- // decode into unzigzag'd location
- zig = stbi__jpeg_dezigzag[k++];
- data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);
- }
- }
- } while (k < 64);
- return 1;
-}
-
-static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)
-{
- int diff,dc;
- int t;
- if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
-
- if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
-
- if (j->succ_high == 0) {
- // first scan for DC coefficient, must be first
- memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
- t = stbi__jpeg_huff_decode(j, hdc);
- diff = t ? stbi__extend_receive(j, t) : 0;
-
- dc = j->img_comp[b].dc_pred + diff;
- j->img_comp[b].dc_pred = dc;
- data[0] = (short) (dc << j->succ_low);
- } else {
- // refinement scan for DC coefficient
- if (stbi__jpeg_get_bit(j))
- data[0] += (short) (1 << j->succ_low);
- }
- return 1;
-}
-
-// @OPTIMIZE: store non-zigzagged during the decode passes,
-// and only de-zigzag when dequantizing
-static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)
-{
- int k;
- if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
-
- if (j->succ_high == 0) {
- int shift = j->succ_low;
-
- if (j->eob_run) {
- --j->eob_run;
- return 1;
- }
-
- k = j->spec_start;
- do {
- unsigned int zig;
- int c,r,s;
- if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
- c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
- r = fac[c];
- if (r) { // fast-AC path
- k += (r >> 4) & 15; // run
- s = r & 15; // combined length
- j->code_buffer <<= s;
- j->code_bits -= s;
- zig = stbi__jpeg_dezigzag[k++];
- data[zig] = (short) ((r >> 8) << shift);
- } else {
- int rs = stbi__jpeg_huff_decode(j, hac);
- if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
- s = rs & 15;
- r = rs >> 4;
- if (s == 0) {
- if (r < 15) {
- j->eob_run = (1 << r);
- if (r)
- j->eob_run += stbi__jpeg_get_bits(j, r);
- --j->eob_run;
- break;
- }
- k += 16;
- } else {
- k += r;
- zig = stbi__jpeg_dezigzag[k++];
- data[zig] = (short) (stbi__extend_receive(j,s) << shift);
- }
- }
- } while (k <= j->spec_end);
- } else {
- // refinement scan for these AC coefficients
-
- short bit = (short) (1 << j->succ_low);
-
- if (j->eob_run) {
- --j->eob_run;
- for (k = j->spec_start; k <= j->spec_end; ++k) {
- short *p = &data[stbi__jpeg_dezigzag[k]];
- if (*p != 0)
- if (stbi__jpeg_get_bit(j))
- if ((*p & bit)==0) {
- if (*p > 0)
- *p += bit;
- else
- *p -= bit;
- }
- }
- } else {
- k = j->spec_start;
- do {
- int r,s;
- int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
- if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
- s = rs & 15;
- r = rs >> 4;
- if (s == 0) {
- if (r < 15) {
- j->eob_run = (1 << r) - 1;
- if (r)
- j->eob_run += stbi__jpeg_get_bits(j, r);
- r = 64; // force end of block
- } else {
- // r=15 s=0 should write 16 0s, so we just do
- // a run of 15 0s and then write s (which is 0),
- // so we don't have to do anything special here
- }
- } else {
- if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
- // sign bit
- if (stbi__jpeg_get_bit(j))
- s = bit;
- else
- s = -bit;
- }
-
- // advance by r
- while (k <= j->spec_end) {
- short *p = &data[stbi__jpeg_dezigzag[k++]];
- if (*p != 0) {
- if (stbi__jpeg_get_bit(j))
- if ((*p & bit)==0) {
- if (*p > 0)
- *p += bit;
- else
- *p -= bit;
- }
- } else {
- if (r == 0) {
- *p = (short) s;
- break;
- }
- --r;
- }
- }
- } while (k <= j->spec_end);
- }
- }
- return 1;
-}
-
-// take a -128..127 value and stbi__clamp it and convert to 0..255
-stbi_inline static stbi_uc stbi__clamp(int x)
-{
- // trick to use a single test to catch both cases
- if ((unsigned int) x > 255) {
- if (x < 0) return 0;
- if (x > 255) return 255;
- }
- return (stbi_uc) x;
-}
-
-#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5)))
-#define stbi__fsh(x) ((x) * 4096)
-
-// derived from jidctint -- DCT_ISLOW
-#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
- int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
- p2 = s2; \
- p3 = s6; \
- p1 = (p2+p3) * stbi__f2f(0.5411961f); \
- t2 = p1 + p3*stbi__f2f(-1.847759065f); \
- t3 = p1 + p2*stbi__f2f( 0.765366865f); \
- p2 = s0; \
- p3 = s4; \
- t0 = stbi__fsh(p2+p3); \
- t1 = stbi__fsh(p2-p3); \
- x0 = t0+t3; \
- x3 = t0-t3; \
- x1 = t1+t2; \
- x2 = t1-t2; \
- t0 = s7; \
- t1 = s5; \
- t2 = s3; \
- t3 = s1; \
- p3 = t0+t2; \
- p4 = t1+t3; \
- p1 = t0+t3; \
- p2 = t1+t2; \
- p5 = (p3+p4)*stbi__f2f( 1.175875602f); \
- t0 = t0*stbi__f2f( 0.298631336f); \
- t1 = t1*stbi__f2f( 2.053119869f); \
- t2 = t2*stbi__f2f( 3.072711026f); \
- t3 = t3*stbi__f2f( 1.501321110f); \
- p1 = p5 + p1*stbi__f2f(-0.899976223f); \
- p2 = p5 + p2*stbi__f2f(-2.562915447f); \
- p3 = p3*stbi__f2f(-1.961570560f); \
- p4 = p4*stbi__f2f(-0.390180644f); \
- t3 += p1+p4; \
- t2 += p2+p3; \
- t1 += p2+p4; \
- t0 += p1+p3;
-
-static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
-{
- int i,val[64],*v=val;
- stbi_uc *o;
- short *d = data;
-
- // columns
- for (i=0; i < 8; ++i,++d, ++v) {
- // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
- if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
- && d[40]==0 && d[48]==0 && d[56]==0) {
- // no shortcut 0 seconds
- // (1|2|3|4|5|6|7)==0 0 seconds
- // all separate -0.047 seconds
- // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
- int dcterm = d[0]*4;
- v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
- } else {
- STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])
- // constants scaled things up by 1<<12; let's bring them back
- // down, but keep 2 extra bits of precision
- x0 += 512; x1 += 512; x2 += 512; x3 += 512;
- v[ 0] = (x0+t3) >> 10;
- v[56] = (x0-t3) >> 10;
- v[ 8] = (x1+t2) >> 10;
- v[48] = (x1-t2) >> 10;
- v[16] = (x2+t1) >> 10;
- v[40] = (x2-t1) >> 10;
- v[24] = (x3+t0) >> 10;
- v[32] = (x3-t0) >> 10;
- }
- }
-
- for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
- // no fast case since the first 1D IDCT spread components out
- STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
- // constants scaled things up by 1<<12, plus we had 1<<2 from first
- // loop, plus horizontal and vertical each scale by sqrt(8) so together
- // we've got an extra 1<<3, so 1<<17 total we need to remove.
- // so we want to round that, which means adding 0.5 * 1<<17,
- // aka 65536. Also, we'll end up with -128 to 127 that we want
- // to encode as 0..255 by adding 128, so we'll add that before the shift
- x0 += 65536 + (128<<17);
- x1 += 65536 + (128<<17);
- x2 += 65536 + (128<<17);
- x3 += 65536 + (128<<17);
- // tried computing the shifts into temps, or'ing the temps to see
- // if any were out of range, but that was slower
- o[0] = stbi__clamp((x0+t3) >> 17);
- o[7] = stbi__clamp((x0-t3) >> 17);
- o[1] = stbi__clamp((x1+t2) >> 17);
- o[6] = stbi__clamp((x1-t2) >> 17);
- o[2] = stbi__clamp((x2+t1) >> 17);
- o[5] = stbi__clamp((x2-t1) >> 17);
- o[3] = stbi__clamp((x3+t0) >> 17);
- o[4] = stbi__clamp((x3-t0) >> 17);
- }
-}
-
-#ifdef STBI_SSE2
-// sse2 integer IDCT. not the fastest possible implementation but it
-// produces bit-identical results to the generic C version so it's
-// fully "transparent".
-static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
-{
- // This is constructed to match our regular (generic) integer IDCT exactly.
- __m128i row0, row1, row2, row3, row4, row5, row6, row7;
- __m128i tmp;
-
- // dot product constant: even elems=x, odd elems=y
- #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))
-
- // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
- // out(1) = c1[even]*x + c1[odd]*y
- #define dct_rot(out0,out1, x,y,c0,c1) \
- __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
- __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
- __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
- __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
- __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
- __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
-
- // out = in << 12 (in 16-bit, out 32-bit)
- #define dct_widen(out, in) \
- __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
- __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
-
- // wide add
- #define dct_wadd(out, a, b) \
- __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
- __m128i out##_h = _mm_add_epi32(a##_h, b##_h)
-
- // wide sub
- #define dct_wsub(out, a, b) \
- __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
- __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
-
- // butterfly a/b, add bias, then shift by "s" and pack
- #define dct_bfly32o(out0, out1, a,b,bias,s) \
- { \
- __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
- __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
- dct_wadd(sum, abiased, b); \
- dct_wsub(dif, abiased, b); \
- out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
- out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
- }
-
- // 8-bit interleave step (for transposes)
- #define dct_interleave8(a, b) \
- tmp = a; \
- a = _mm_unpacklo_epi8(a, b); \
- b = _mm_unpackhi_epi8(tmp, b)
-
- // 16-bit interleave step (for transposes)
- #define dct_interleave16(a, b) \
- tmp = a; \
- a = _mm_unpacklo_epi16(a, b); \
- b = _mm_unpackhi_epi16(tmp, b)
-
- #define dct_pass(bias,shift) \
- { \
- /* even part */ \
- dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
- __m128i sum04 = _mm_add_epi16(row0, row4); \
- __m128i dif04 = _mm_sub_epi16(row0, row4); \
- dct_widen(t0e, sum04); \
- dct_widen(t1e, dif04); \
- dct_wadd(x0, t0e, t3e); \
- dct_wsub(x3, t0e, t3e); \
- dct_wadd(x1, t1e, t2e); \
- dct_wsub(x2, t1e, t2e); \
- /* odd part */ \
- dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
- dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
- __m128i sum17 = _mm_add_epi16(row1, row7); \
- __m128i sum35 = _mm_add_epi16(row3, row5); \
- dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
- dct_wadd(x4, y0o, y4o); \
- dct_wadd(x5, y1o, y5o); \
- dct_wadd(x6, y2o, y5o); \
- dct_wadd(x7, y3o, y4o); \
- dct_bfly32o(row0,row7, x0,x7,bias,shift); \
- dct_bfly32o(row1,row6, x1,x6,bias,shift); \
- dct_bfly32o(row2,row5, x2,x5,bias,shift); \
- dct_bfly32o(row3,row4, x3,x4,bias,shift); \
- }
-
- __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
- __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));
- __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
- __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
- __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));
- __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));
- __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));
- __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));
-
- // rounding biases in column/row passes, see stbi__idct_block for explanation.
- __m128i bias_0 = _mm_set1_epi32(512);
- __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));
-
- // load
- row0 = _mm_load_si128((const __m128i *) (data + 0*8));
- row1 = _mm_load_si128((const __m128i *) (data + 1*8));
- row2 = _mm_load_si128((const __m128i *) (data + 2*8));
- row3 = _mm_load_si128((const __m128i *) (data + 3*8));
- row4 = _mm_load_si128((const __m128i *) (data + 4*8));
- row5 = _mm_load_si128((const __m128i *) (data + 5*8));
- row6 = _mm_load_si128((const __m128i *) (data + 6*8));
- row7 = _mm_load_si128((const __m128i *) (data + 7*8));
-
- // column pass
- dct_pass(bias_0, 10);
-
- {
- // 16bit 8x8 transpose pass 1
- dct_interleave16(row0, row4);
- dct_interleave16(row1, row5);
- dct_interleave16(row2, row6);
- dct_interleave16(row3, row7);
-
- // transpose pass 2
- dct_interleave16(row0, row2);
- dct_interleave16(row1, row3);
- dct_interleave16(row4, row6);
- dct_interleave16(row5, row7);
-
- // transpose pass 3
- dct_interleave16(row0, row1);
- dct_interleave16(row2, row3);
- dct_interleave16(row4, row5);
- dct_interleave16(row6, row7);
- }
-
- // row pass
- dct_pass(bias_1, 17);
-
- {
- // pack
- __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
- __m128i p1 = _mm_packus_epi16(row2, row3);
- __m128i p2 = _mm_packus_epi16(row4, row5);
- __m128i p3 = _mm_packus_epi16(row6, row7);
-
- // 8bit 8x8 transpose pass 1
- dct_interleave8(p0, p2); // a0e0a1e1...
- dct_interleave8(p1, p3); // c0g0c1g1...
-
- // transpose pass 2
- dct_interleave8(p0, p1); // a0c0e0g0...
- dct_interleave8(p2, p3); // b0d0f0h0...
-
- // transpose pass 3
- dct_interleave8(p0, p2); // a0b0c0d0...
- dct_interleave8(p1, p3); // a4b4c4d4...
-
- // store
- _mm_storel_epi64((__m128i *) out, p0); out += out_stride;
- _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;
- _mm_storel_epi64((__m128i *) out, p2); out += out_stride;
- _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;
- _mm_storel_epi64((__m128i *) out, p1); out += out_stride;
- _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;
- _mm_storel_epi64((__m128i *) out, p3); out += out_stride;
- _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));
- }
-
-#undef dct_const
-#undef dct_rot
-#undef dct_widen
-#undef dct_wadd
-#undef dct_wsub
-#undef dct_bfly32o
-#undef dct_interleave8
-#undef dct_interleave16
-#undef dct_pass
-}
-
-#endif // STBI_SSE2
-
-#ifdef STBI_NEON
-
-// NEON integer IDCT. should produce bit-identical
-// results to the generic C version.
-static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
-{
- int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
-
- int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
- int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
- int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));
- int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));
- int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
- int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
- int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
- int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
- int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));
- int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));
- int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));
- int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));
-
-#define dct_long_mul(out, inq, coeff) \
- int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
- int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
-
-#define dct_long_mac(out, acc, inq, coeff) \
- int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
- int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
-
-#define dct_widen(out, inq) \
- int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
- int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
-
-// wide add
-#define dct_wadd(out, a, b) \
- int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
- int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
-
-// wide sub
-#define dct_wsub(out, a, b) \
- int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
- int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
-
-// butterfly a/b, then shift using "shiftop" by "s" and pack
-#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
- { \
- dct_wadd(sum, a, b); \
- dct_wsub(dif, a, b); \
- out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
- out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
- }
-
-#define dct_pass(shiftop, shift) \
- { \
- /* even part */ \
- int16x8_t sum26 = vaddq_s16(row2, row6); \
- dct_long_mul(p1e, sum26, rot0_0); \
- dct_long_mac(t2e, p1e, row6, rot0_1); \
- dct_long_mac(t3e, p1e, row2, rot0_2); \
- int16x8_t sum04 = vaddq_s16(row0, row4); \
- int16x8_t dif04 = vsubq_s16(row0, row4); \
- dct_widen(t0e, sum04); \
- dct_widen(t1e, dif04); \
- dct_wadd(x0, t0e, t3e); \
- dct_wsub(x3, t0e, t3e); \
- dct_wadd(x1, t1e, t2e); \
- dct_wsub(x2, t1e, t2e); \
- /* odd part */ \
- int16x8_t sum15 = vaddq_s16(row1, row5); \
- int16x8_t sum17 = vaddq_s16(row1, row7); \
- int16x8_t sum35 = vaddq_s16(row3, row5); \
- int16x8_t sum37 = vaddq_s16(row3, row7); \
- int16x8_t sumodd = vaddq_s16(sum17, sum35); \
- dct_long_mul(p5o, sumodd, rot1_0); \
- dct_long_mac(p1o, p5o, sum17, rot1_1); \
- dct_long_mac(p2o, p5o, sum35, rot1_2); \
- dct_long_mul(p3o, sum37, rot2_0); \
- dct_long_mul(p4o, sum15, rot2_1); \
- dct_wadd(sump13o, p1o, p3o); \
- dct_wadd(sump24o, p2o, p4o); \
- dct_wadd(sump23o, p2o, p3o); \
- dct_wadd(sump14o, p1o, p4o); \
- dct_long_mac(x4, sump13o, row7, rot3_0); \
- dct_long_mac(x5, sump24o, row5, rot3_1); \
- dct_long_mac(x6, sump23o, row3, rot3_2); \
- dct_long_mac(x7, sump14o, row1, rot3_3); \
- dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
- dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
- dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
- dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
- }
-
- // load
- row0 = vld1q_s16(data + 0*8);
- row1 = vld1q_s16(data + 1*8);
- row2 = vld1q_s16(data + 2*8);
- row3 = vld1q_s16(data + 3*8);
- row4 = vld1q_s16(data + 4*8);
- row5 = vld1q_s16(data + 5*8);
- row6 = vld1q_s16(data + 6*8);
- row7 = vld1q_s16(data + 7*8);
-
- // add DC bias
- row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
-
- // column pass
- dct_pass(vrshrn_n_s32, 10);
-
- // 16bit 8x8 transpose
- {
-// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
-// whether compilers actually get this is another story, sadly.
-#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
-#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
-#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }
-
- // pass 1
- dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
- dct_trn16(row2, row3);
- dct_trn16(row4, row5);
- dct_trn16(row6, row7);
-
- // pass 2
- dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
- dct_trn32(row1, row3);
- dct_trn32(row4, row6);
- dct_trn32(row5, row7);
-
- // pass 3
- dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
- dct_trn64(row1, row5);
- dct_trn64(row2, row6);
- dct_trn64(row3, row7);
-
-#undef dct_trn16
-#undef dct_trn32
-#undef dct_trn64
- }
-
- // row pass
- // vrshrn_n_s32 only supports shifts up to 16, we need
- // 17. so do a non-rounding shift of 16 first then follow
- // up with a rounding shift by 1.
- dct_pass(vshrn_n_s32, 16);
-
- {
- // pack and round
- uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
- uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
- uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
- uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
- uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
- uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
- uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
- uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
-
- // again, these can translate into one instruction, but often don't.
-#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
-#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
-#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }
-
- // sadly can't use interleaved stores here since we only write
- // 8 bytes to each scan line!
-
- // 8x8 8-bit transpose pass 1
- dct_trn8_8(p0, p1);
- dct_trn8_8(p2, p3);
- dct_trn8_8(p4, p5);
- dct_trn8_8(p6, p7);
-
- // pass 2
- dct_trn8_16(p0, p2);
- dct_trn8_16(p1, p3);
- dct_trn8_16(p4, p6);
- dct_trn8_16(p5, p7);
-
- // pass 3
- dct_trn8_32(p0, p4);
- dct_trn8_32(p1, p5);
- dct_trn8_32(p2, p6);
- dct_trn8_32(p3, p7);
-
- // store
- vst1_u8(out, p0); out += out_stride;
- vst1_u8(out, p1); out += out_stride;
- vst1_u8(out, p2); out += out_stride;
- vst1_u8(out, p3); out += out_stride;
- vst1_u8(out, p4); out += out_stride;
- vst1_u8(out, p5); out += out_stride;
- vst1_u8(out, p6); out += out_stride;
- vst1_u8(out, p7);
-
-#undef dct_trn8_8
-#undef dct_trn8_16
-#undef dct_trn8_32
- }
-
-#undef dct_long_mul
-#undef dct_long_mac
-#undef dct_widen
-#undef dct_wadd
-#undef dct_wsub
-#undef dct_bfly32o
-#undef dct_pass
-}
-
-#endif // STBI_NEON
-
-#define STBI__MARKER_none 0xff
-// if there's a pending marker from the entropy stream, return that
-// otherwise, fetch from the stream and get a marker. if there's no
-// marker, return 0xff, which is never a valid marker value
-static stbi_uc stbi__get_marker(stbi__jpeg *j)
-{
- stbi_uc x;
- if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }
- x = stbi__get8(j->s);
- if (x != 0xff) return STBI__MARKER_none;
- while (x == 0xff)
- x = stbi__get8(j->s); // consume repeated 0xff fill bytes
- return x;
-}
-
-// in each scan, we'll have scan_n components, and the order
-// of the components is specified by order[]
-#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
-
-// after a restart interval, stbi__jpeg_reset the entropy decoder and
-// the dc prediction
-static void stbi__jpeg_reset(stbi__jpeg *j)
-{
- j->code_bits = 0;
- j->code_buffer = 0;
- j->nomore = 0;
- j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;
- j->marker = STBI__MARKER_none;
- j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
- j->eob_run = 0;
- // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
- // since we don't even allow 1<<30 pixels
-}
-
-static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
-{
- stbi__jpeg_reset(z);
- if (!z->progressive) {
- if (z->scan_n == 1) {
- int i,j;
- STBI_SIMD_ALIGN(short, data[64]);
- int n = z->order[0];
- // non-interleaved data, we just need to process one block at a time,
- // in trivial scanline order
- // number of blocks to do just depends on how many actual "pixels" this
- // component has, independent of interleaved MCU blocking and such
- int w = (z->img_comp[n].x+7) >> 3;
- int h = (z->img_comp[n].y+7) >> 3;
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i) {
- int ha = z->img_comp[n].ha;
- if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
- z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
- // every data block is an MCU, so countdown the restart interval
- if (--z->todo <= 0) {
- if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
- // if it's NOT a restart, then just bail, so we get corrupt data
- // rather than no data
- if (!STBI__RESTART(z->marker)) return 1;
- stbi__jpeg_reset(z);
- }
- }
- }
- return 1;
- } else { // interleaved
- int i,j,k,x,y;
- STBI_SIMD_ALIGN(short, data[64]);
- for (j=0; j < z->img_mcu_y; ++j) {
- for (i=0; i < z->img_mcu_x; ++i) {
- // scan an interleaved mcu... process scan_n components in order
- for (k=0; k < z->scan_n; ++k) {
- int n = z->order[k];
- // scan out an mcu's worth of this component; that's just determined
- // by the basic H and V specified for the component
- for (y=0; y < z->img_comp[n].v; ++y) {
- for (x=0; x < z->img_comp[n].h; ++x) {
- int x2 = (i*z->img_comp[n].h + x)*8;
- int y2 = (j*z->img_comp[n].v + y)*8;
- int ha = z->img_comp[n].ha;
- if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
- z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);
- }
- }
- }
- // after all interleaved components, that's an interleaved MCU,
- // so now count down the restart interval
- if (--z->todo <= 0) {
- if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
- if (!STBI__RESTART(z->marker)) return 1;
- stbi__jpeg_reset(z);
- }
- }
- }
- return 1;
- }
- } else {
- if (z->scan_n == 1) {
- int i,j;
- int n = z->order[0];
- // non-interleaved data, we just need to process one block at a time,
- // in trivial scanline order
- // number of blocks to do just depends on how many actual "pixels" this
- // component has, independent of interleaved MCU blocking and such
- int w = (z->img_comp[n].x+7) >> 3;
- int h = (z->img_comp[n].y+7) >> 3;
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i) {
- short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
- if (z->spec_start == 0) {
- if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
- return 0;
- } else {
- int ha = z->img_comp[n].ha;
- if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
- return 0;
- }
- // every data block is an MCU, so countdown the restart interval
- if (--z->todo <= 0) {
- if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
- if (!STBI__RESTART(z->marker)) return 1;
- stbi__jpeg_reset(z);
- }
- }
- }
- return 1;
- } else { // interleaved
- int i,j,k,x,y;
- for (j=0; j < z->img_mcu_y; ++j) {
- for (i=0; i < z->img_mcu_x; ++i) {
- // scan an interleaved mcu... process scan_n components in order
- for (k=0; k < z->scan_n; ++k) {
- int n = z->order[k];
- // scan out an mcu's worth of this component; that's just determined
- // by the basic H and V specified for the component
- for (y=0; y < z->img_comp[n].v; ++y) {
- for (x=0; x < z->img_comp[n].h; ++x) {
- int x2 = (i*z->img_comp[n].h + x);
- int y2 = (j*z->img_comp[n].v + y);
- short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
- if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
- return 0;
- }
- }
- }
- // after all interleaved components, that's an interleaved MCU,
- // so now count down the restart interval
- if (--z->todo <= 0) {
- if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
- if (!STBI__RESTART(z->marker)) return 1;
- stbi__jpeg_reset(z);
- }
- }
- }
- return 1;
- }
- }
-}
-
-static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)
-{
- int i;
- for (i=0; i < 64; ++i)
- data[i] *= dequant[i];
-}
-
-static void stbi__jpeg_finish(stbi__jpeg *z)
-{
- if (z->progressive) {
- // dequantize and idct the data
- int i,j,n;
- for (n=0; n < z->s->img_n; ++n) {
- int w = (z->img_comp[n].x+7) >> 3;
- int h = (z->img_comp[n].y+7) >> 3;
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i) {
- short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
- stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
- z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
- }
- }
- }
- }
-}
-
-static int stbi__process_marker(stbi__jpeg *z, int m)
-{
- int L;
- switch (m) {
- case STBI__MARKER_none: // no marker found
- return stbi__err("expected marker","Corrupt JPEG");
-
- case 0xDD: // DRI - specify restart interval
- if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG");
- z->restart_interval = stbi__get16be(z->s);
- return 1;
-
- case 0xDB: // DQT - define quantization table
- L = stbi__get16be(z->s)-2;
- while (L > 0) {
- int q = stbi__get8(z->s);
- int p = q >> 4, sixteen = (p != 0);
- int t = q & 15,i;
- if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG");
- if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG");
-
- for (i=0; i < 64; ++i)
- z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));
- L -= (sixteen ? 129 : 65);
- }
- return L==0;
-
- case 0xC4: // DHT - define huffman table
- L = stbi__get16be(z->s)-2;
- while (L > 0) {
- stbi_uc *v;
- int sizes[16],i,n=0;
- int q = stbi__get8(z->s);
- int tc = q >> 4;
- int th = q & 15;
- if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");
- for (i=0; i < 16; ++i) {
- sizes[i] = stbi__get8(z->s);
- n += sizes[i];
- }
- L -= 17;
- if (tc == 0) {
- if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
- v = z->huff_dc[th].values;
- } else {
- if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;
- v = z->huff_ac[th].values;
- }
- for (i=0; i < n; ++i)
- v[i] = stbi__get8(z->s);
- if (tc != 0)
- stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
- L -= n;
- }
- return L==0;
- }
-
- // check for comment block or APP blocks
- if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
- L = stbi__get16be(z->s);
- if (L < 2) {
- if (m == 0xFE)
- return stbi__err("bad COM len","Corrupt JPEG");
- else
- return stbi__err("bad APP len","Corrupt JPEG");
- }
- L -= 2;
-
- if (m == 0xE0 && L >= 5) { // JFIF APP0 segment
- static const unsigned char tag[5] = {'J','F','I','F','\0'};
- int ok = 1;
- int i;
- for (i=0; i < 5; ++i)
- if (stbi__get8(z->s) != tag[i])
- ok = 0;
- L -= 5;
- if (ok)
- z->jfif = 1;
- } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment
- static const unsigned char tag[6] = {'A','d','o','b','e','\0'};
- int ok = 1;
- int i;
- for (i=0; i < 6; ++i)
- if (stbi__get8(z->s) != tag[i])
- ok = 0;
- L -= 6;
- if (ok) {
- stbi__get8(z->s); // version
- stbi__get16be(z->s); // flags0
- stbi__get16be(z->s); // flags1
- z->app14_color_transform = stbi__get8(z->s); // color transform
- L -= 6;
- }
- }
-
- stbi__skip(z->s, L);
- return 1;
- }
-
- return stbi__err("unknown marker","Corrupt JPEG");
-}
-
-// after we see SOS
-static int stbi__process_scan_header(stbi__jpeg *z)
-{
- int i;
- int Ls = stbi__get16be(z->s);
- z->scan_n = stbi__get8(z->s);
- if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG");
- if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG");
- for (i=0; i < z->scan_n; ++i) {
- int id = stbi__get8(z->s), which;
- int q = stbi__get8(z->s);
- for (which = 0; which < z->s->img_n; ++which)
- if (z->img_comp[which].id == id)
- break;
- if (which == z->s->img_n) return 0; // no match
- z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG");
- z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG");
- z->order[i] = which;
- }
-
- {
- int aa;
- z->spec_start = stbi__get8(z->s);
- z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
- aa = stbi__get8(z->s);
- z->succ_high = (aa >> 4);
- z->succ_low = (aa & 15);
- if (z->progressive) {
- if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
- return stbi__err("bad SOS", "Corrupt JPEG");
- } else {
- if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG");
- if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG");
- z->spec_end = 63;
- }
- }
-
- return 1;
-}
-
-static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)
-{
- int i;
- for (i=0; i < ncomp; ++i) {
- if (z->img_comp[i].raw_data) {
- STBI_FREE(z->img_comp[i].raw_data);
- z->img_comp[i].raw_data = NULL;
- z->img_comp[i].data = NULL;
- }
- if (z->img_comp[i].raw_coeff) {
- STBI_FREE(z->img_comp[i].raw_coeff);
- z->img_comp[i].raw_coeff = 0;
- z->img_comp[i].coeff = 0;
- }
- if (z->img_comp[i].linebuf) {
- STBI_FREE(z->img_comp[i].linebuf);
- z->img_comp[i].linebuf = NULL;
- }
- }
- return why;
-}
-
-static int stbi__process_frame_header(stbi__jpeg *z, int scan)
-{
- stbi__context *s = z->s;
- int Lf,p,i,q, h_max=1,v_max=1,c;
- Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG
- p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
- s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
- s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires
- c = stbi__get8(s);
- if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG");
- s->img_n = c;
- for (i=0; i < c; ++i) {
- z->img_comp[i].data = NULL;
- z->img_comp[i].linebuf = NULL;
- }
-
- if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
-
- z->rgb = 0;
- for (i=0; i < s->img_n; ++i) {
- static const unsigned char rgb[3] = { 'R', 'G', 'B' };
- z->img_comp[i].id = stbi__get8(s);
- if (s->img_n == 3 && z->img_comp[i].id == rgb[i])
- ++z->rgb;
- q = stbi__get8(s);
- z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
- z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
- z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
- }
-
- if (scan != STBI__SCAN_load) return 1;
-
- if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode");
-
- for (i=0; i < s->img_n; ++i) {
- if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
- if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
- }
-
- // compute interleaved mcu info
- z->img_h_max = h_max;
- z->img_v_max = v_max;
- z->img_mcu_w = h_max * 8;
- z->img_mcu_h = v_max * 8;
- // these sizes can't be more than 17 bits
- z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
- z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
-
- for (i=0; i < s->img_n; ++i) {
- // number of effective pixels (e.g. for non-interleaved MCU)
- z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
- z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
- // to simplify generation, we'll allocate enough memory to decode
- // the bogus oversized data from using interleaved MCUs and their
- // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
- // discard the extra data until colorspace conversion
- //
- // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)
- // so these muls can't overflow with 32-bit ints (which we require)
- z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
- z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
- z->img_comp[i].coeff = 0;
- z->img_comp[i].raw_coeff = 0;
- z->img_comp[i].linebuf = NULL;
- z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);
- if (z->img_comp[i].raw_data == NULL)
- return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
- // align blocks for idct using mmx/sse
- z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
- if (z->progressive) {
- // w2, h2 are multiples of 8 (see above)
- z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;
- z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;
- z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);
- if (z->img_comp[i].raw_coeff == NULL)
- return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
- z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);
- }
- }
-
- return 1;
-}
-
-// use comparisons since in some cases we handle more than one case (e.g. SOF)
-#define stbi__DNL(x) ((x) == 0xdc)
-#define stbi__SOI(x) ((x) == 0xd8)
-#define stbi__EOI(x) ((x) == 0xd9)
-#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
-#define stbi__SOS(x) ((x) == 0xda)
-
-#define stbi__SOF_progressive(x) ((x) == 0xc2)
-
-static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
-{
- int m;
- z->jfif = 0;
- z->app14_color_transform = -1; // valid values are 0,1,2
- z->marker = STBI__MARKER_none; // initialize cached marker to empty
- m = stbi__get_marker(z);
- if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");
- if (scan == STBI__SCAN_type) return 1;
- m = stbi__get_marker(z);
- while (!stbi__SOF(m)) {
- if (!stbi__process_marker(z,m)) return 0;
- m = stbi__get_marker(z);
- while (m == STBI__MARKER_none) {
- // some files have extra padding after their blocks, so ok, we'll scan
- if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
- m = stbi__get_marker(z);
- }
- }
- z->progressive = stbi__SOF_progressive(m);
- if (!stbi__process_frame_header(z, scan)) return 0;
- return 1;
-}
-
-// decode image to YCbCr format
-static int stbi__decode_jpeg_image(stbi__jpeg *j)
-{
- int m;
- for (m = 0; m < 4; m++) {
- j->img_comp[m].raw_data = NULL;
- j->img_comp[m].raw_coeff = NULL;
- }
- j->restart_interval = 0;
- if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
- m = stbi__get_marker(j);
- while (!stbi__EOI(m)) {
- if (stbi__SOS(m)) {
- if (!stbi__process_scan_header(j)) return 0;
- if (!stbi__parse_entropy_coded_data(j)) return 0;
- if (j->marker == STBI__MARKER_none ) {
- // handle 0s at the end of image data from IP Kamera 9060
- while (!stbi__at_eof(j->s)) {
- int x = stbi__get8(j->s);
- if (x == 255) {
- j->marker = stbi__get8(j->s);
- break;
- }
- }
- // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
- }
- } else if (stbi__DNL(m)) {
- int Ld = stbi__get16be(j->s);
- stbi__uint32 NL = stbi__get16be(j->s);
- if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
- if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
- } else {
- if (!stbi__process_marker(j, m)) return 0;
- }
- m = stbi__get_marker(j);
- }
- if (j->progressive)
- stbi__jpeg_finish(j);
- return 1;
-}
-
-// static jfif-centered resampling (across block boundaries)
-
-typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,
- int w, int hs);
-
-#define stbi__div4(x) ((stbi_uc) ((x) >> 2))
-
-static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- STBI_NOTUSED(out);
- STBI_NOTUSED(in_far);
- STBI_NOTUSED(w);
- STBI_NOTUSED(hs);
- return in_near;
-}
-
-static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- // need to generate two samples vertically for every one in input
- int i;
- STBI_NOTUSED(hs);
- for (i=0; i < w; ++i)
- out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);
- return out;
-}
-
-static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- // need to generate two samples horizontally for every one in input
- int i;
- stbi_uc *input = in_near;
-
- if (w == 1) {
- // if only one sample, can't do any interpolation
- out[0] = out[1] = input[0];
- return out;
- }
-
- out[0] = input[0];
- out[1] = stbi__div4(input[0]*3 + input[1] + 2);
- for (i=1; i < w-1; ++i) {
- int n = 3*input[i]+2;
- out[i*2+0] = stbi__div4(n+input[i-1]);
- out[i*2+1] = stbi__div4(n+input[i+1]);
- }
- out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);
- out[i*2+1] = input[w-1];
-
- STBI_NOTUSED(in_far);
- STBI_NOTUSED(hs);
-
- return out;
-}
-
-#define stbi__div16(x) ((stbi_uc) ((x) >> 4))
-
-static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- // need to generate 2x2 samples for every one in input
- int i,t0,t1;
- if (w == 1) {
- out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
- return out;
- }
-
- t1 = 3*in_near[0] + in_far[0];
- out[0] = stbi__div4(t1+2);
- for (i=1; i < w; ++i) {
- t0 = t1;
- t1 = 3*in_near[i]+in_far[i];
- out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
- out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
- }
- out[w*2-1] = stbi__div4(t1+2);
-
- STBI_NOTUSED(hs);
-
- return out;
-}
-
-#if defined(STBI_SSE2) || defined(STBI_NEON)
-static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- // need to generate 2x2 samples for every one in input
- int i=0,t0,t1;
-
- if (w == 1) {
- out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
- return out;
- }
-
- t1 = 3*in_near[0] + in_far[0];
- // process groups of 8 pixels for as long as we can.
- // note we can't handle the last pixel in a row in this loop
- // because we need to handle the filter boundary conditions.
- for (; i < ((w-1) & ~7); i += 8) {
-#if defined(STBI_SSE2)
- // load and perform the vertical filtering pass
- // this uses 3*x + y = 4*x + (y - x)
- __m128i zero = _mm_setzero_si128();
- __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));
- __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));
- __m128i farw = _mm_unpacklo_epi8(farb, zero);
- __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
- __m128i diff = _mm_sub_epi16(farw, nearw);
- __m128i nears = _mm_slli_epi16(nearw, 2);
- __m128i curr = _mm_add_epi16(nears, diff); // current row
-
- // horizontal filter works the same based on shifted vers of current
- // row. "prev" is current row shifted right by 1 pixel; we need to
- // insert the previous pixel value (from t1).
- // "next" is current row shifted left by 1 pixel, with first pixel
- // of next block of 8 pixels added in.
- __m128i prv0 = _mm_slli_si128(curr, 2);
- __m128i nxt0 = _mm_srli_si128(curr, 2);
- __m128i prev = _mm_insert_epi16(prv0, t1, 0);
- __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);
-
- // horizontal filter, polyphase implementation since it's convenient:
- // even pixels = 3*cur + prev = cur*4 + (prev - cur)
- // odd pixels = 3*cur + next = cur*4 + (next - cur)
- // note the shared term.
- __m128i bias = _mm_set1_epi16(8);
- __m128i curs = _mm_slli_epi16(curr, 2);
- __m128i prvd = _mm_sub_epi16(prev, curr);
- __m128i nxtd = _mm_sub_epi16(next, curr);
- __m128i curb = _mm_add_epi16(curs, bias);
- __m128i even = _mm_add_epi16(prvd, curb);
- __m128i odd = _mm_add_epi16(nxtd, curb);
-
- // interleave even and odd pixels, then undo scaling.
- __m128i int0 = _mm_unpacklo_epi16(even, odd);
- __m128i int1 = _mm_unpackhi_epi16(even, odd);
- __m128i de0 = _mm_srli_epi16(int0, 4);
- __m128i de1 = _mm_srli_epi16(int1, 4);
-
- // pack and write output
- __m128i outv = _mm_packus_epi16(de0, de1);
- _mm_storeu_si128((__m128i *) (out + i*2), outv);
-#elif defined(STBI_NEON)
- // load and perform the vertical filtering pass
- // this uses 3*x + y = 4*x + (y - x)
- uint8x8_t farb = vld1_u8(in_far + i);
- uint8x8_t nearb = vld1_u8(in_near + i);
- int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
- int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
- int16x8_t curr = vaddq_s16(nears, diff); // current row
-
- // horizontal filter works the same based on shifted vers of current
- // row. "prev" is current row shifted right by 1 pixel; we need to
- // insert the previous pixel value (from t1).
- // "next" is current row shifted left by 1 pixel, with first pixel
- // of next block of 8 pixels added in.
- int16x8_t prv0 = vextq_s16(curr, curr, 7);
- int16x8_t nxt0 = vextq_s16(curr, curr, 1);
- int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
- int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);
-
- // horizontal filter, polyphase implementation since it's convenient:
- // even pixels = 3*cur + prev = cur*4 + (prev - cur)
- // odd pixels = 3*cur + next = cur*4 + (next - cur)
- // note the shared term.
- int16x8_t curs = vshlq_n_s16(curr, 2);
- int16x8_t prvd = vsubq_s16(prev, curr);
- int16x8_t nxtd = vsubq_s16(next, curr);
- int16x8_t even = vaddq_s16(curs, prvd);
- int16x8_t odd = vaddq_s16(curs, nxtd);
-
- // undo scaling and round, then store with even/odd phases interleaved
- uint8x8x2_t o;
- o.val[0] = vqrshrun_n_s16(even, 4);
- o.val[1] = vqrshrun_n_s16(odd, 4);
- vst2_u8(out + i*2, o);
-#endif
-
- // "previous" value for next iter
- t1 = 3*in_near[i+7] + in_far[i+7];
- }
-
- t0 = t1;
- t1 = 3*in_near[i] + in_far[i];
- out[i*2] = stbi__div16(3*t1 + t0 + 8);
-
- for (++i; i < w; ++i) {
- t0 = t1;
- t1 = 3*in_near[i]+in_far[i];
- out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
- out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
- }
- out[w*2-1] = stbi__div4(t1+2);
-
- STBI_NOTUSED(hs);
-
- return out;
-}
-#endif
-
-static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
-{
- // resample with nearest-neighbor
- int i,j;
- STBI_NOTUSED(in_far);
- for (i=0; i < w; ++i)
- for (j=0; j < hs; ++j)
- out[i*hs+j] = in_near[i];
- return out;
-}
-
-// this is a reduced-precision calculation of YCbCr-to-RGB introduced
-// to make sure the code produces the same results in both SIMD and scalar
-#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
-static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
-{
- int i;
- for (i=0; i < count; ++i) {
- int y_fixed = (y[i] << 20) + (1<<19); // rounding
- int r,g,b;
- int cr = pcr[i] - 128;
- int cb = pcb[i] - 128;
- r = y_fixed + cr* stbi__float2fixed(1.40200f);
- g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
- b = y_fixed + cb* stbi__float2fixed(1.77200f);
- r >>= 20;
- g >>= 20;
- b >>= 20;
- if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
- if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
- if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
- out[0] = (stbi_uc)r;
- out[1] = (stbi_uc)g;
- out[2] = (stbi_uc)b;
- out[3] = 255;
- out += step;
- }
-}
-
-#if defined(STBI_SSE2) || defined(STBI_NEON)
-static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)
-{
- int i = 0;
-
-#ifdef STBI_SSE2
- // step == 3 is pretty ugly on the final interleave, and i'm not convinced
- // it's useful in practice (you wouldn't use it for textures, for example).
- // so just accelerate step == 4 case.
- if (step == 4) {
- // this is a fairly straightforward implementation and not super-optimized.
- __m128i signflip = _mm_set1_epi8(-0x80);
- __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
- __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
- __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
- __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
- __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
- __m128i xw = _mm_set1_epi16(255); // alpha channel
-
- for (; i+7 < count; i += 8) {
- // load
- __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
- __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
- __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
- __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
- __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
-
- // unpack to short (and left-shift cr, cb by 8)
- __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
- __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
- __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
-
- // color transform
- __m128i yws = _mm_srli_epi16(yw, 4);
- __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
- __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
- __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
- __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
- __m128i rws = _mm_add_epi16(cr0, yws);
- __m128i gwt = _mm_add_epi16(cb0, yws);
- __m128i bws = _mm_add_epi16(yws, cb1);
- __m128i gws = _mm_add_epi16(gwt, cr1);
-
- // descale
- __m128i rw = _mm_srai_epi16(rws, 4);
- __m128i bw = _mm_srai_epi16(bws, 4);
- __m128i gw = _mm_srai_epi16(gws, 4);
-
- // back to byte, set up for transpose
- __m128i brb = _mm_packus_epi16(rw, bw);
- __m128i gxb = _mm_packus_epi16(gw, xw);
-
- // transpose to interleave channels
- __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
- __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
- __m128i o0 = _mm_unpacklo_epi16(t0, t1);
- __m128i o1 = _mm_unpackhi_epi16(t0, t1);
-
- // store
- _mm_storeu_si128((__m128i *) (out + 0), o0);
- _mm_storeu_si128((__m128i *) (out + 16), o1);
- out += 32;
- }
- }
-#endif
-
-#ifdef STBI_NEON
- // in this version, step=3 support would be easy to add. but is there demand?
- if (step == 4) {
- // this is a fairly straightforward implementation and not super-optimized.
- uint8x8_t signflip = vdup_n_u8(0x80);
- int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f));
- int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));
- int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));
- int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f));
-
- for (; i+7 < count; i += 8) {
- // load
- uint8x8_t y_bytes = vld1_u8(y + i);
- uint8x8_t cr_bytes = vld1_u8(pcr + i);
- uint8x8_t cb_bytes = vld1_u8(pcb + i);
- int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
- int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
-
- // expand to s16
- int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
- int16x8_t crw = vshll_n_s8(cr_biased, 7);
- int16x8_t cbw = vshll_n_s8(cb_biased, 7);
-
- // color transform
- int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
- int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
- int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
- int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
- int16x8_t rws = vaddq_s16(yws, cr0);
- int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
- int16x8_t bws = vaddq_s16(yws, cb1);
-
- // undo scaling, round, convert to byte
- uint8x8x4_t o;
- o.val[0] = vqrshrun_n_s16(rws, 4);
- o.val[1] = vqrshrun_n_s16(gws, 4);
- o.val[2] = vqrshrun_n_s16(bws, 4);
- o.val[3] = vdup_n_u8(255);
-
- // store, interleaving r/g/b/a
- vst4_u8(out, o);
- out += 8*4;
- }
- }
-#endif
-
- for (; i < count; ++i) {
- int y_fixed = (y[i] << 20) + (1<<19); // rounding
- int r,g,b;
- int cr = pcr[i] - 128;
- int cb = pcb[i] - 128;
- r = y_fixed + cr* stbi__float2fixed(1.40200f);
- g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
- b = y_fixed + cb* stbi__float2fixed(1.77200f);
- r >>= 20;
- g >>= 20;
- b >>= 20;
- if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
- if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
- if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
- out[0] = (stbi_uc)r;
- out[1] = (stbi_uc)g;
- out[2] = (stbi_uc)b;
- out[3] = 255;
- out += step;
- }
-}
-#endif
-
-// set up the kernels
-static void stbi__setup_jpeg(stbi__jpeg *j)
-{
- j->idct_block_kernel = stbi__idct_block;
- j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
- j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
-
-#ifdef STBI_SSE2
- if (stbi__sse2_available()) {
- j->idct_block_kernel = stbi__idct_simd;
- j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
- j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
- }
-#endif
-
-#ifdef STBI_NEON
- j->idct_block_kernel = stbi__idct_simd;
- j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
- j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
-#endif
-}
-
-// clean up the temporary component buffers
-static void stbi__cleanup_jpeg(stbi__jpeg *j)
-{
- stbi__free_jpeg_components(j, j->s->img_n, 0);
-}
-
-typedef struct
-{
- resample_row_func resample;
- stbi_uc *line0,*line1;
- int hs,vs; // expansion factor in each axis
- int w_lores; // horizontal pixels pre-expansion
- int ystep; // how far through vertical expansion we are
- int ypos; // which pre-expansion row we're on
-} stbi__resample;
-
-// fast 0..255 * 0..255 => 0..255 rounded multiplication
-static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)
-{
- unsigned int t = x*y + 128;
- return (stbi_uc) ((t + (t >>8)) >> 8);
-}
-
-static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)
-{
- int n, decode_n, is_rgb;
- z->s->img_n = 0; // make stbi__cleanup_jpeg safe
-
- // validate req_comp
- if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
-
- // load a jpeg image from whichever source, but leave in YCbCr format
- if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }
-
- // determine actual number of components to generate
- n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;
-
- is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));
-
- if (z->s->img_n == 3 && n < 3 && !is_rgb)
- decode_n = 1;
- else
- decode_n = z->s->img_n;
-
- // resample and color-convert
- {
- int k;
- unsigned int i,j;
- stbi_uc *output;
- stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL };
-
- stbi__resample res_comp[4];
-
- for (k=0; k < decode_n; ++k) {
- stbi__resample *r = &res_comp[k];
-
- // allocate line buffer big enough for upsampling off the edges
- // with upsample factor of 4
- z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);
- if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
-
- r->hs = z->img_h_max / z->img_comp[k].h;
- r->vs = z->img_v_max / z->img_comp[k].v;
- r->ystep = r->vs >> 1;
- r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
- r->ypos = 0;
- r->line0 = r->line1 = z->img_comp[k].data;
-
- if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
- else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
- else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
- else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
- else r->resample = stbi__resample_row_generic;
- }
-
- // can't error after this so, this is safe
- output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);
- if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
-
- // now go ahead and resample
- for (j=0; j < z->s->img_y; ++j) {
- stbi_uc *out = output + n * z->s->img_x * j;
- for (k=0; k < decode_n; ++k) {
- stbi__resample *r = &res_comp[k];
- int y_bot = r->ystep >= (r->vs >> 1);
- coutput[k] = r->resample(z->img_comp[k].linebuf,
- y_bot ? r->line1 : r->line0,
- y_bot ? r->line0 : r->line1,
- r->w_lores, r->hs);
- if (++r->ystep >= r->vs) {
- r->ystep = 0;
- r->line0 = r->line1;
- if (++r->ypos < z->img_comp[k].y)
- r->line1 += z->img_comp[k].w2;
- }
- }
- if (n >= 3) {
- stbi_uc *y = coutput[0];
- if (z->s->img_n == 3) {
- if (is_rgb) {
- for (i=0; i < z->s->img_x; ++i) {
- out[0] = y[i];
- out[1] = coutput[1][i];
- out[2] = coutput[2][i];
- out[3] = 255;
- out += n;
- }
- } else {
- z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
- }
- } else if (z->s->img_n == 4) {
- if (z->app14_color_transform == 0) { // CMYK
- for (i=0; i < z->s->img_x; ++i) {
- stbi_uc m = coutput[3][i];
- out[0] = stbi__blinn_8x8(coutput[0][i], m);
- out[1] = stbi__blinn_8x8(coutput[1][i], m);
- out[2] = stbi__blinn_8x8(coutput[2][i], m);
- out[3] = 255;
- out += n;
- }
- } else if (z->app14_color_transform == 2) { // YCCK
- z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
- for (i=0; i < z->s->img_x; ++i) {
- stbi_uc m = coutput[3][i];
- out[0] = stbi__blinn_8x8(255 - out[0], m);
- out[1] = stbi__blinn_8x8(255 - out[1], m);
- out[2] = stbi__blinn_8x8(255 - out[2], m);
- out += n;
- }
- } else { // YCbCr + alpha? Ignore the fourth channel for now
- z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
- }
- } else
- for (i=0; i < z->s->img_x; ++i) {
- out[0] = out[1] = out[2] = y[i];
- out[3] = 255; // not used if n==3
- out += n;
- }
- } else {
- if (is_rgb) {
- if (n == 1)
- for (i=0; i < z->s->img_x; ++i)
- *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
- else {
- for (i=0; i < z->s->img_x; ++i, out += 2) {
- out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
- out[1] = 255;
- }
- }
- } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {
- for (i=0; i < z->s->img_x; ++i) {
- stbi_uc m = coutput[3][i];
- stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);
- stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);
- stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);
- out[0] = stbi__compute_y(r, g, b);
- out[1] = 255;
- out += n;
- }
- } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {
- for (i=0; i < z->s->img_x; ++i) {
- out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);
- out[1] = 255;
- out += n;
- }
- } else {
- stbi_uc *y = coutput[0];
- if (n == 1)
- for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
- else
- for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; }
- }
- }
- }
- stbi__cleanup_jpeg(z);
- *out_x = z->s->img_x;
- *out_y = z->s->img_y;
- if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output
- return output;
- }
-}
-
-static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- unsigned char* result;
- stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
- STBI_NOTUSED(ri);
- j->s = s;
- stbi__setup_jpeg(j);
- result = load_jpeg_image(j, x,y,comp,req_comp);
- STBI_FREE(j);
- return result;
-}
-
-static int stbi__jpeg_test(stbi__context *s)
-{
- int r;
- stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
- j->s = s;
- stbi__setup_jpeg(j);
- r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
- stbi__rewind(s);
- STBI_FREE(j);
- return r;
-}
-
-static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
-{
- if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
- stbi__rewind( j->s );
- return 0;
- }
- if (x) *x = j->s->img_x;
- if (y) *y = j->s->img_y;
- if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;
- return 1;
-}
-
-static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
-{
- int result;
- stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
- j->s = s;
- result = stbi__jpeg_info_raw(j, x, y, comp);
- STBI_FREE(j);
- return result;
-}
-#endif
-
-// public domain zlib decode v0.2 Sean Barrett 2006-11-18
-// simple implementation
-// - all input must be provided in an upfront buffer
-// - all output is written to a single output buffer (can malloc/realloc)
-// performance
-// - fast huffman
-
-#ifndef STBI_NO_ZLIB
-
-// fast-way is faster to check than jpeg huffman, but slow way is slower
-#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
-#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
-
-// zlib-style huffman encoding
-// (jpegs packs from left, zlib from right, so can't share code)
-typedef struct
-{
- stbi__uint16 fast[1 << STBI__ZFAST_BITS];
- stbi__uint16 firstcode[16];
- int maxcode[17];
- stbi__uint16 firstsymbol[16];
- stbi_uc size[288];
- stbi__uint16 value[288];
-} stbi__zhuffman;
-
-stbi_inline static int stbi__bitreverse16(int n)
-{
- n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
- n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
- n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
- n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
- return n;
-}
-
-stbi_inline static int stbi__bit_reverse(int v, int bits)
-{
- STBI_ASSERT(bits <= 16);
- // to bit reverse n bits, reverse 16 and shift
- // e.g. 11 bits, bit reverse and shift away 5
- return stbi__bitreverse16(v) >> (16-bits);
-}
-
-static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)
-{
- int i,k=0;
- int code, next_code[16], sizes[17];
-
- // DEFLATE spec for generating codes
- memset(sizes, 0, sizeof(sizes));
- memset(z->fast, 0, sizeof(z->fast));
- for (i=0; i < num; ++i)
- ++sizes[sizelist[i]];
- sizes[0] = 0;
- for (i=1; i < 16; ++i)
- if (sizes[i] > (1 << i))
- return stbi__err("bad sizes", "Corrupt PNG");
- code = 0;
- for (i=1; i < 16; ++i) {
- next_code[i] = code;
- z->firstcode[i] = (stbi__uint16) code;
- z->firstsymbol[i] = (stbi__uint16) k;
- code = (code + sizes[i]);
- if (sizes[i])
- if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
- z->maxcode[i] = code << (16-i); // preshift for inner loop
- code <<= 1;
- k += sizes[i];
- }
- z->maxcode[16] = 0x10000; // sentinel
- for (i=0; i < num; ++i) {
- int s = sizelist[i];
- if (s) {
- int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
- stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
- z->size [c] = (stbi_uc ) s;
- z->value[c] = (stbi__uint16) i;
- if (s <= STBI__ZFAST_BITS) {
- int j = stbi__bit_reverse(next_code[s],s);
- while (j < (1 << STBI__ZFAST_BITS)) {
- z->fast[j] = fastv;
- j += (1 << s);
- }
- }
- ++next_code[s];
- }
- }
- return 1;
-}
-
-// zlib-from-memory implementation for PNG reading
-// because PNG allows splitting the zlib stream arbitrarily,
-// and it's annoying structurally to have PNG call ZLIB call PNG,
-// we require PNG read all the IDATs and combine them into a single
-// memory buffer
-
-typedef struct
-{
- stbi_uc *zbuffer, *zbuffer_end;
- int num_bits;
- stbi__uint32 code_buffer;
-
- char *zout;
- char *zout_start;
- char *zout_end;
- int z_expandable;
-
- stbi__zhuffman z_length, z_distance;
-} stbi__zbuf;
-
-stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
-{
- if (z->zbuffer >= z->zbuffer_end) return 0;
- return *z->zbuffer++;
-}
-
-static void stbi__fill_bits(stbi__zbuf *z)
-{
- do {
- STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
- z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
- z->num_bits += 8;
- } while (z->num_bits <= 24);
-}
-
-stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
-{
- unsigned int k;
- if (z->num_bits < n) stbi__fill_bits(z);
- k = z->code_buffer & ((1 << n) - 1);
- z->code_buffer >>= n;
- z->num_bits -= n;
- return k;
-}
-
-static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
-{
- int b,s,k;
- // not resolved by fast table, so compute it the slow way
- // use jpeg approach, which requires MSbits at top
- k = stbi__bit_reverse(a->code_buffer, 16);
- for (s=STBI__ZFAST_BITS+1; ; ++s)
- if (k < z->maxcode[s])
- break;
- if (s == 16) return -1; // invalid code!
- // code size is s, so:
- b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
- STBI_ASSERT(z->size[b] == s);
- a->code_buffer >>= s;
- a->num_bits -= s;
- return z->value[b];
-}
-
-stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
-{
- int b,s;
- if (a->num_bits < 16) stbi__fill_bits(a);
- b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
- if (b) {
- s = b >> 9;
- a->code_buffer >>= s;
- a->num_bits -= s;
- return b & 511;
- }
- return stbi__zhuffman_decode_slowpath(a, z);
-}
-
-static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
-{
- char *q;
- int cur, limit, old_limit;
- z->zout = zout;
- if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
- cur = (int) (z->zout - z->zout_start);
- limit = old_limit = (int) (z->zout_end - z->zout_start);
- while (cur + n > limit)
- limit *= 2;
- q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
- STBI_NOTUSED(old_limit);
- if (q == NULL) return stbi__err("outofmem", "Out of memory");
- z->zout_start = q;
- z->zout = q + cur;
- z->zout_end = q + limit;
- return 1;
-}
-
-static const int stbi__zlength_base[31] = {
- 3,4,5,6,7,8,9,10,11,13,
- 15,17,19,23,27,31,35,43,51,59,
- 67,83,99,115,131,163,195,227,258,0,0 };
-
-static const int stbi__zlength_extra[31]=
-{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
-
-static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
-257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
-
-static const int stbi__zdist_extra[32] =
-{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
-
-static int stbi__parse_huffman_block(stbi__zbuf *a)
-{
- char *zout = a->zout;
- for(;;) {
- int z = stbi__zhuffman_decode(a, &a->z_length);
- if (z < 256) {
- if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
- if (zout >= a->zout_end) {
- if (!stbi__zexpand(a, zout, 1)) return 0;
- zout = a->zout;
- }
- *zout++ = (char) z;
- } else {
- stbi_uc *p;
- int len,dist;
- if (z == 256) {
- a->zout = zout;
- return 1;
- }
- z -= 257;
- len = stbi__zlength_base[z];
- if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
- z = stbi__zhuffman_decode(a, &a->z_distance);
- if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
- dist = stbi__zdist_base[z];
- if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
- if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
- if (zout + len > a->zout_end) {
- if (!stbi__zexpand(a, zout, len)) return 0;
- zout = a->zout;
- }
- p = (stbi_uc *) (zout - dist);
- if (dist == 1) { // run of one byte; common in images.
- stbi_uc v = *p;
- if (len) { do *zout++ = v; while (--len); }
- } else {
- if (len) { do *zout++ = *p++; while (--len); }
- }
- }
- }
-}
-
-static int stbi__compute_huffman_codes(stbi__zbuf *a)
-{
- static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
- stbi__zhuffman z_codelength;
- stbi_uc lencodes[286+32+137];//padding for maximum single op
- stbi_uc codelength_sizes[19];
- int i,n;
-
- int hlit = stbi__zreceive(a,5) + 257;
- int hdist = stbi__zreceive(a,5) + 1;
- int hclen = stbi__zreceive(a,4) + 4;
- int ntot = hlit + hdist;
-
- memset(codelength_sizes, 0, sizeof(codelength_sizes));
- for (i=0; i < hclen; ++i) {
- int s = stbi__zreceive(a,3);
- codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
- }
- if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
-
- n = 0;
- while (n < ntot) {
- int c = stbi__zhuffman_decode(a, &z_codelength);
- if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
- if (c < 16)
- lencodes[n++] = (stbi_uc) c;
- else {
- stbi_uc fill = 0;
- if (c == 16) {
- c = stbi__zreceive(a,2)+3;
- if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
- fill = lencodes[n-1];
- } else if (c == 17)
- c = stbi__zreceive(a,3)+3;
- else {
- STBI_ASSERT(c == 18);
- c = stbi__zreceive(a,7)+11;
- }
- if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
- memset(lencodes+n, fill, c);
- n += c;
- }
- }
- if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
- if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
- if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
- return 1;
-}
-
-static int stbi__parse_uncompressed_block(stbi__zbuf *a)
-{
- stbi_uc header[4];
- int len,nlen,k;
- if (a->num_bits & 7)
- stbi__zreceive(a, a->num_bits & 7); // discard
- // drain the bit-packed data into header
- k = 0;
- while (a->num_bits > 0) {
- header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
- a->code_buffer >>= 8;
- a->num_bits -= 8;
- }
- STBI_ASSERT(a->num_bits == 0);
- // now fill header the normal way
- while (k < 4)
- header[k++] = stbi__zget8(a);
- len = header[1] * 256 + header[0];
- nlen = header[3] * 256 + header[2];
- if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
- if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
- if (a->zout + len > a->zout_end)
- if (!stbi__zexpand(a, a->zout, len)) return 0;
- memcpy(a->zout, a->zbuffer, len);
- a->zbuffer += len;
- a->zout += len;
- return 1;
-}
-
-static int stbi__parse_zlib_header(stbi__zbuf *a)
-{
- int cmf = stbi__zget8(a);
- int cm = cmf & 15;
- /* int cinfo = cmf >> 4; */
- int flg = stbi__zget8(a);
- if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
- if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
- if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
- // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
- return 1;
-}
-
-static const stbi_uc stbi__zdefault_length[288] =
-{
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
-};
-static const stbi_uc stbi__zdefault_distance[32] =
-{
- 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
-};
-/*
-Init algorithm:
-{
- int i; // use <= to match clearly with spec
- for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
- for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
- for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
- for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
-
- for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
-}
-*/
-
-static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
-{
- int final, type;
- if (parse_header)
- if (!stbi__parse_zlib_header(a)) return 0;
- a->num_bits = 0;
- a->code_buffer = 0;
- do {
- final = stbi__zreceive(a,1);
- type = stbi__zreceive(a,2);
- if (type == 0) {
- if (!stbi__parse_uncompressed_block(a)) return 0;
- } else if (type == 3) {
- return 0;
- } else {
- if (type == 1) {
- // use fixed code lengths
- if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
- if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
- } else {
- if (!stbi__compute_huffman_codes(a)) return 0;
- }
- if (!stbi__parse_huffman_block(a)) return 0;
- }
- } while (!final);
- return 1;
-}
-
-static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
-{
- a->zout_start = obuf;
- a->zout = obuf;
- a->zout_end = obuf + olen;
- a->z_expandable = exp;
-
- return stbi__parse_zlib(a, parse_header);
-}
-
-STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
-{
- stbi__zbuf a;
- char *p = (char *) stbi__malloc(initial_size);
- if (p == NULL) return NULL;
- a.zbuffer = (stbi_uc *) buffer;
- a.zbuffer_end = (stbi_uc *) buffer + len;
- if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
- if (outlen) *outlen = (int) (a.zout - a.zout_start);
- return a.zout_start;
- } else {
- STBI_FREE(a.zout_start);
- return NULL;
- }
-}
-
-STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
-{
- return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
-}
-
-STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
-{
- stbi__zbuf a;
- char *p = (char *) stbi__malloc(initial_size);
- if (p == NULL) return NULL;
- a.zbuffer = (stbi_uc *) buffer;
- a.zbuffer_end = (stbi_uc *) buffer + len;
- if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
- if (outlen) *outlen = (int) (a.zout - a.zout_start);
- return a.zout_start;
- } else {
- STBI_FREE(a.zout_start);
- return NULL;
- }
-}
-
-STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
-{
- stbi__zbuf a;
- a.zbuffer = (stbi_uc *) ibuffer;
- a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
- if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
- return (int) (a.zout - a.zout_start);
- else
- return -1;
-}
-
-STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
-{
- stbi__zbuf a;
- char *p = (char *) stbi__malloc(16384);
- if (p == NULL) return NULL;
- a.zbuffer = (stbi_uc *) buffer;
- a.zbuffer_end = (stbi_uc *) buffer+len;
- if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
- if (outlen) *outlen = (int) (a.zout - a.zout_start);
- return a.zout_start;
- } else {
- STBI_FREE(a.zout_start);
- return NULL;
- }
-}
-
-STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
-{
- stbi__zbuf a;
- a.zbuffer = (stbi_uc *) ibuffer;
- a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
- if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
- return (int) (a.zout - a.zout_start);
- else
- return -1;
-}
-#endif
-
-// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
-// simple implementation
-// - only 8-bit samples
-// - no CRC checking
-// - allocates lots of intermediate memory
-// - avoids problem of streaming data between subsystems
-// - avoids explicit window management
-// performance
-// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
-
-#ifndef STBI_NO_PNG
-typedef struct
-{
- stbi__uint32 length;
- stbi__uint32 type;
-} stbi__pngchunk;
-
-static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
-{
- stbi__pngchunk c;
- c.length = stbi__get32be(s);
- c.type = stbi__get32be(s);
- return c;
-}
-
-static int stbi__check_png_header(stbi__context *s)
-{
- static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
- int i;
- for (i=0; i < 8; ++i)
- if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
- return 1;
-}
-
-typedef struct
-{
- stbi__context *s;
- stbi_uc *idata, *expanded, *out;
- int depth;
-} stbi__png;
-
-
-enum {
- STBI__F_none=0,
- STBI__F_sub=1,
- STBI__F_up=2,
- STBI__F_avg=3,
- STBI__F_paeth=4,
- // synthetic filters used for first scanline to avoid needing a dummy row of 0s
- STBI__F_avg_first,
- STBI__F_paeth_first
-};
-
-static stbi_uc first_row_filter[5] =
-{
- STBI__F_none,
- STBI__F_sub,
- STBI__F_none,
- STBI__F_avg_first,
- STBI__F_paeth_first
-};
-
-static int stbi__paeth(int a, int b, int c)
-{
- int p = a + b - c;
- int pa = abs(p-a);
- int pb = abs(p-b);
- int pc = abs(p-c);
- if (pa <= pb && pa <= pc) return a;
- if (pb <= pc) return b;
- return c;
-}
-
-static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
-
-// create the png data from post-deflated data
-static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
-{
- int bytes = (depth == 16? 2 : 1);
- stbi__context *s = a->s;
- stbi__uint32 i,j,stride = x*out_n*bytes;
- stbi__uint32 img_len, img_width_bytes;
- int k;
- int img_n = s->img_n; // copy it into a local for later
-
- int output_bytes = out_n*bytes;
- int filter_bytes = img_n*bytes;
- int width = x;
-
- STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
- a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
- if (!a->out) return stbi__err("outofmem", "Out of memory");
-
- if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
- img_width_bytes = (((img_n * x * depth) + 7) >> 3);
- img_len = (img_width_bytes + 1) * y;
-
- // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
- // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
- // so just check for raw_len < img_len always.
- if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
-
- for (j=0; j < y; ++j) {
- stbi_uc *cur = a->out + stride*j;
- stbi_uc *prior;
- int filter = *raw++;
-
- if (filter > 4)
- return stbi__err("invalid filter","Corrupt PNG");
-
- if (depth < 8) {
- STBI_ASSERT(img_width_bytes <= x);
- cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
- filter_bytes = 1;
- width = img_width_bytes;
- }
- prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
-
- // if first row, use special filter that doesn't sample previous row
- if (j == 0) filter = first_row_filter[filter];
-
- // handle first byte explicitly
- for (k=0; k < filter_bytes; ++k) {
- switch (filter) {
- case STBI__F_none : cur[k] = raw[k]; break;
- case STBI__F_sub : cur[k] = raw[k]; break;
- case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
- case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
- case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
- case STBI__F_avg_first : cur[k] = raw[k]; break;
- case STBI__F_paeth_first: cur[k] = raw[k]; break;
- }
- }
-
- if (depth == 8) {
- if (img_n != out_n)
- cur[img_n] = 255; // first pixel
- raw += img_n;
- cur += out_n;
- prior += out_n;
- } else if (depth == 16) {
- if (img_n != out_n) {
- cur[filter_bytes] = 255; // first pixel top byte
- cur[filter_bytes+1] = 255; // first pixel bottom byte
- }
- raw += filter_bytes;
- cur += output_bytes;
- prior += output_bytes;
- } else {
- raw += 1;
- cur += 1;
- prior += 1;
- }
-
- // this is a little gross, so that we don't switch per-pixel or per-component
- if (depth < 8 || img_n == out_n) {
- int nk = (width - 1)*filter_bytes;
- #define STBI__CASE(f) \
- case f: \
- for (k=0; k < nk; ++k)
- switch (filter) {
- // "none" filter turns into a memcpy here; make that explicit.
- case STBI__F_none: memcpy(cur, raw, nk); break;
- STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
- STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
- STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
- STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
- STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
- STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
- }
- #undef STBI__CASE
- raw += nk;
- } else {
- STBI_ASSERT(img_n+1 == out_n);
- #define STBI__CASE(f) \
- case f: \
- for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
- for (k=0; k < filter_bytes; ++k)
- switch (filter) {
- STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
- STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
- STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
- STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
- STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
- STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
- STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
- }
- #undef STBI__CASE
-
- // the loop above sets the high byte of the pixels' alpha, but for
- // 16 bit png files we also need the low byte set. we'll do that here.
- if (depth == 16) {
- cur = a->out + stride*j; // start at the beginning of the row again
- for (i=0; i < x; ++i,cur+=output_bytes) {
- cur[filter_bytes+1] = 255;
- }
- }
- }
- }
-
- // we make a separate pass to expand bits to pixels; for performance,
- // this could run two scanlines behind the above code, so it won't
- // intefere with filtering but will still be in the cache.
- if (depth < 8) {
- for (j=0; j < y; ++j) {
- stbi_uc *cur = a->out + stride*j;
- stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
- // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
- // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
- stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
-
- // note that the final byte might overshoot and write more data than desired.
- // we can allocate enough data that this never writes out of memory, but it
- // could also overwrite the next scanline. can it overwrite non-empty data
- // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
- // so we need to explicitly clamp the final ones
-
- if (depth == 4) {
- for (k=x*img_n; k >= 2; k-=2, ++in) {
- *cur++ = scale * ((*in >> 4) );
- *cur++ = scale * ((*in ) & 0x0f);
- }
- if (k > 0) *cur++ = scale * ((*in >> 4) );
- } else if (depth == 2) {
- for (k=x*img_n; k >= 4; k-=4, ++in) {
- *cur++ = scale * ((*in >> 6) );
- *cur++ = scale * ((*in >> 4) & 0x03);
- *cur++ = scale * ((*in >> 2) & 0x03);
- *cur++ = scale * ((*in ) & 0x03);
- }
- if (k > 0) *cur++ = scale * ((*in >> 6) );
- if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
- if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
- } else if (depth == 1) {
- for (k=x*img_n; k >= 8; k-=8, ++in) {
- *cur++ = scale * ((*in >> 7) );
- *cur++ = scale * ((*in >> 6) & 0x01);
- *cur++ = scale * ((*in >> 5) & 0x01);
- *cur++ = scale * ((*in >> 4) & 0x01);
- *cur++ = scale * ((*in >> 3) & 0x01);
- *cur++ = scale * ((*in >> 2) & 0x01);
- *cur++ = scale * ((*in >> 1) & 0x01);
- *cur++ = scale * ((*in ) & 0x01);
- }
- if (k > 0) *cur++ = scale * ((*in >> 7) );
- if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
- if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
- if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
- if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
- if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
- if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
- }
- if (img_n != out_n) {
- int q;
- // insert alpha = 255
- cur = a->out + stride*j;
- if (img_n == 1) {
- for (q=x-1; q >= 0; --q) {
- cur[q*2+1] = 255;
- cur[q*2+0] = cur[q];
- }
- } else {
- STBI_ASSERT(img_n == 3);
- for (q=x-1; q >= 0; --q) {
- cur[q*4+3] = 255;
- cur[q*4+2] = cur[q*3+2];
- cur[q*4+1] = cur[q*3+1];
- cur[q*4+0] = cur[q*3+0];
- }
- }
- }
- }
- } else if (depth == 16) {
- // force the image data from big-endian to platform-native.
- // this is done in a separate pass due to the decoding relying
- // on the data being untouched, but could probably be done
- // per-line during decode if care is taken.
- stbi_uc *cur = a->out;
- stbi__uint16 *cur16 = (stbi__uint16*)cur;
-
- for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
- *cur16 = (cur[0] << 8) | cur[1];
- }
- }
-
- return 1;
-}
-
-static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
-{
- int bytes = (depth == 16 ? 2 : 1);
- int out_bytes = out_n * bytes;
- stbi_uc *final;
- int p;
- if (!interlaced)
- return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
-
- // de-interlacing
- final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
- for (p=0; p < 7; ++p) {
- int xorig[] = { 0,4,0,2,0,1,0 };
- int yorig[] = { 0,0,4,0,2,0,1 };
- int xspc[] = { 8,8,4,4,2,2,1 };
- int yspc[] = { 8,8,8,4,4,2,2 };
- int i,j,x,y;
- // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
- x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
- y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
- if (x && y) {
- stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
- if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
- STBI_FREE(final);
- return 0;
- }
- for (j=0; j < y; ++j) {
- for (i=0; i < x; ++i) {
- int out_y = j*yspc[p]+yorig[p];
- int out_x = i*xspc[p]+xorig[p];
- memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
- a->out + (j*x+i)*out_bytes, out_bytes);
- }
- }
- STBI_FREE(a->out);
- image_data += img_len;
- image_data_len -= img_len;
- }
- }
- a->out = final;
-
- return 1;
-}
-
-static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
-{
- stbi__context *s = z->s;
- stbi__uint32 i, pixel_count = s->img_x * s->img_y;
- stbi_uc *p = z->out;
-
- // compute color-based transparency, assuming we've
- // already got 255 as the alpha value in the output
- STBI_ASSERT(out_n == 2 || out_n == 4);
-
- if (out_n == 2) {
- for (i=0; i < pixel_count; ++i) {
- p[1] = (p[0] == tc[0] ? 0 : 255);
- p += 2;
- }
- } else {
- for (i=0; i < pixel_count; ++i) {
- if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
- p[3] = 0;
- p += 4;
- }
- }
- return 1;
-}
-
-static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
-{
- stbi__context *s = z->s;
- stbi__uint32 i, pixel_count = s->img_x * s->img_y;
- stbi__uint16 *p = (stbi__uint16*) z->out;
-
- // compute color-based transparency, assuming we've
- // already got 65535 as the alpha value in the output
- STBI_ASSERT(out_n == 2 || out_n == 4);
-
- if (out_n == 2) {
- for (i = 0; i < pixel_count; ++i) {
- p[1] = (p[0] == tc[0] ? 0 : 65535);
- p += 2;
- }
- } else {
- for (i = 0; i < pixel_count; ++i) {
- if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
- p[3] = 0;
- p += 4;
- }
- }
- return 1;
-}
-
-static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
-{
- stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
- stbi_uc *p, *temp_out, *orig = a->out;
-
- p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
- if (p == NULL) return stbi__err("outofmem", "Out of memory");
-
- // between here and free(out) below, exitting would leak
- temp_out = p;
-
- if (pal_img_n == 3) {
- for (i=0; i < pixel_count; ++i) {
- int n = orig[i]*4;
- p[0] = palette[n ];
- p[1] = palette[n+1];
- p[2] = palette[n+2];
- p += 3;
- }
- } else {
- for (i=0; i < pixel_count; ++i) {
- int n = orig[i]*4;
- p[0] = palette[n ];
- p[1] = palette[n+1];
- p[2] = palette[n+2];
- p[3] = palette[n+3];
- p += 4;
- }
- }
- STBI_FREE(a->out);
- a->out = temp_out;
-
- STBI_NOTUSED(len);
-
- return 1;
-}
-
-static int stbi__unpremultiply_on_load = 0;
-static int stbi__de_iphone_flag = 0;
-
-STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
-{
- stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
-}
-
-STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
-{
- stbi__de_iphone_flag = flag_true_if_should_convert;
-}
-
-static void stbi__de_iphone(stbi__png *z)
-{
- stbi__context *s = z->s;
- stbi__uint32 i, pixel_count = s->img_x * s->img_y;
- stbi_uc *p = z->out;
-
- if (s->img_out_n == 3) { // convert bgr to rgb
- for (i=0; i < pixel_count; ++i) {
- stbi_uc t = p[0];
- p[0] = p[2];
- p[2] = t;
- p += 3;
- }
- } else {
- STBI_ASSERT(s->img_out_n == 4);
- if (stbi__unpremultiply_on_load) {
- // convert bgr to rgb and unpremultiply
- for (i=0; i < pixel_count; ++i) {
- stbi_uc a = p[3];
- stbi_uc t = p[0];
- if (a) {
- stbi_uc half = a / 2;
- p[0] = (p[2] * 255 + half) / a;
- p[1] = (p[1] * 255 + half) / a;
- p[2] = ( t * 255 + half) / a;
- } else {
- p[0] = p[2];
- p[2] = t;
- }
- p += 4;
- }
- } else {
- // convert bgr to rgb
- for (i=0; i < pixel_count; ++i) {
- stbi_uc t = p[0];
- p[0] = p[2];
- p[2] = t;
- p += 4;
- }
- }
- }
-}
-
-#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
-
-static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
-{
- stbi_uc palette[1024], pal_img_n=0;
- stbi_uc has_trans=0, tc[3]={0};
- stbi__uint16 tc16[3];
- stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
- int first=1,k,interlace=0, color=0, is_iphone=0;
- stbi__context *s = z->s;
-
- z->expanded = NULL;
- z->idata = NULL;
- z->out = NULL;
-
- if (!stbi__check_png_header(s)) return 0;
-
- if (scan == STBI__SCAN_type) return 1;
-
- for (;;) {
- stbi__pngchunk c = stbi__get_chunk_header(s);
- switch (c.type) {
- case STBI__PNG_TYPE('C','g','B','I'):
- is_iphone = 1;
- stbi__skip(s, c.length);
- break;
- case STBI__PNG_TYPE('I','H','D','R'): {
- int comp,filter;
- if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
- first = 0;
- if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
- s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
- s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
- z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
- color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
- if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
- if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
- comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
- filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
- interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
- if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
- if (!pal_img_n) {
- s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
- if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
- if (scan == STBI__SCAN_header) return 1;
- } else {
- // if paletted, then pal_n is our final components, and
- // img_n is # components to decompress/filter.
- s->img_n = 1;
- if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
- // if SCAN_header, have to scan to see if we have a tRNS
- }
- break;
- }
-
- case STBI__PNG_TYPE('P','L','T','E'): {
- if (first) return stbi__err("first not IHDR", "Corrupt PNG");
- if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
- pal_len = c.length / 3;
- if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
- for (i=0; i < pal_len; ++i) {
- palette[i*4+0] = stbi__get8(s);
- palette[i*4+1] = stbi__get8(s);
- palette[i*4+2] = stbi__get8(s);
- palette[i*4+3] = 255;
- }
- break;
- }
-
- case STBI__PNG_TYPE('t','R','N','S'): {
- if (first) return stbi__err("first not IHDR", "Corrupt PNG");
- if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
- if (pal_img_n) {
- if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
- if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
- if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
- pal_img_n = 4;
- for (i=0; i < c.length; ++i)
- palette[i*4+3] = stbi__get8(s);
- } else {
- if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
- if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
- has_trans = 1;
- if (z->depth == 16) {
- for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
- } else {
- for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
- }
- }
- break;
- }
-
- case STBI__PNG_TYPE('I','D','A','T'): {
- if (first) return stbi__err("first not IHDR", "Corrupt PNG");
- if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
- if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
- if ((int)(ioff + c.length) < (int)ioff) return 0;
- if (ioff + c.length > idata_limit) {
- stbi__uint32 idata_limit_old = idata_limit;
- stbi_uc *p;
- if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
- while (ioff + c.length > idata_limit)
- idata_limit *= 2;
- STBI_NOTUSED(idata_limit_old);
- p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
- z->idata = p;
- }
- if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
- ioff += c.length;
- break;
- }
-
- case STBI__PNG_TYPE('I','E','N','D'): {
- stbi__uint32 raw_len, bpl;
- if (first) return stbi__err("first not IHDR", "Corrupt PNG");
- if (scan != STBI__SCAN_load) return 1;
- if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
- // initial guess for decoded data size to avoid unnecessary reallocs
- bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
- raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
- z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
- if (z->expanded == NULL) return 0; // zlib should set error
- STBI_FREE(z->idata); z->idata = NULL;
- if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
- s->img_out_n = s->img_n+1;
- else
- s->img_out_n = s->img_n;
- if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
- if (has_trans) {
- if (z->depth == 16) {
- if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
- } else {
- if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
- }
- }
- if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
- stbi__de_iphone(z);
- if (pal_img_n) {
- // pal_img_n == 3 or 4
- s->img_n = pal_img_n; // record the actual colors we had
- s->img_out_n = pal_img_n;
- if (req_comp >= 3) s->img_out_n = req_comp;
- if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
- return 0;
- } else if (has_trans) {
- // non-paletted image with tRNS -> source image has (constant) alpha
- ++s->img_n;
- }
- STBI_FREE(z->expanded); z->expanded = NULL;
- return 1;
- }
-
- default:
- // if critical, fail
- if (first) return stbi__err("first not IHDR", "Corrupt PNG");
- if ((c.type & (1 << 29)) == 0) {
- #ifndef STBI_NO_FAILURE_STRINGS
- // not threadsafe
- static char invalid_chunk[] = "XXXX PNG chunk not known";
- invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
- invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
- invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
- invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
- #endif
- return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
- }
- stbi__skip(s, c.length);
- break;
- }
- // end of PNG chunk, read and skip CRC
- stbi__get32be(s);
- }
-}
-
-static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)
-{
- void *result=NULL;
- if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
- if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
- if (p->depth < 8)
- ri->bits_per_channel = 8;
- else
- ri->bits_per_channel = p->depth;
- result = p->out;
- p->out = NULL;
- if (req_comp && req_comp != p->s->img_out_n) {
- if (ri->bits_per_channel == 8)
- result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
- else
- result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
- p->s->img_out_n = req_comp;
- if (result == NULL) return result;
- }
- *x = p->s->img_x;
- *y = p->s->img_y;
- if (n) *n = p->s->img_n;
- }
- STBI_FREE(p->out); p->out = NULL;
- STBI_FREE(p->expanded); p->expanded = NULL;
- STBI_FREE(p->idata); p->idata = NULL;
-
- return result;
-}
-
-static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- stbi__png p;
- p.s = s;
- return stbi__do_png(&p, x,y,comp,req_comp, ri);
-}
-
-static int stbi__png_test(stbi__context *s)
-{
- int r;
- r = stbi__check_png_header(s);
- stbi__rewind(s);
- return r;
-}
-
-static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
-{
- if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
- stbi__rewind( p->s );
- return 0;
- }
- if (x) *x = p->s->img_x;
- if (y) *y = p->s->img_y;
- if (comp) *comp = p->s->img_n;
- return 1;
-}
-
-static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
-{
- stbi__png p;
- p.s = s;
- return stbi__png_info_raw(&p, x, y, comp);
-}
-
-static int stbi__png_is16(stbi__context *s)
-{
- stbi__png p;
- p.s = s;
- if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
- return 0;
- if (p.depth != 16) {
- stbi__rewind(p.s);
- return 0;
- }
- return 1;
-}
-#endif
-
-// Microsoft/Windows BMP image
-
-#ifndef STBI_NO_BMP
-static int stbi__bmp_test_raw(stbi__context *s)
-{
- int r;
- int sz;
- if (stbi__get8(s) != 'B') return 0;
- if (stbi__get8(s) != 'M') return 0;
- stbi__get32le(s); // discard filesize
- stbi__get16le(s); // discard reserved
- stbi__get16le(s); // discard reserved
- stbi__get32le(s); // discard data offset
- sz = stbi__get32le(s);
- r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
- return r;
-}
-
-static int stbi__bmp_test(stbi__context *s)
-{
- int r = stbi__bmp_test_raw(s);
- stbi__rewind(s);
- return r;
-}
-
-
-// returns 0..31 for the highest set bit
-static int stbi__high_bit(unsigned int z)
-{
- int n=0;
- if (z == 0) return -1;
- if (z >= 0x10000) { n += 16; z >>= 16; }
- if (z >= 0x00100) { n += 8; z >>= 8; }
- if (z >= 0x00010) { n += 4; z >>= 4; }
- if (z >= 0x00004) { n += 2; z >>= 2; }
- if (z >= 0x00002) { n += 1; z >>= 1; }
- return n;
-}
-
-static int stbi__bitcount(unsigned int a)
-{
- a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
- a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
- a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
- a = (a + (a >> 8)); // max 16 per 8 bits
- a = (a + (a >> 16)); // max 32 per 8 bits
- return a & 0xff;
-}
-
-// extract an arbitrarily-aligned N-bit value (N=bits)
-// from v, and then make it 8-bits long and fractionally
-// extend it to full full range.
-static int stbi__shiftsigned(unsigned int v, int shift, int bits)
-{
- static unsigned int mul_table[9] = {
- 0,
- 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,
- 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,
- };
- static unsigned int shift_table[9] = {
- 0, 0,0,1,0,2,4,6,0,
- };
- if (shift < 0)
- v <<= -shift;
- else
- v >>= shift;
- STBI_ASSERT(v >= 0 && v < 256);
- v >>= (8-bits);
- STBI_ASSERT(bits >= 0 && bits <= 8);
- return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];
-}
-
-typedef struct
-{
- int bpp, offset, hsz;
- unsigned int mr,mg,mb,ma, all_a;
-} stbi__bmp_data;
-
-static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
-{
- int hsz;
- if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
- stbi__get32le(s); // discard filesize
- stbi__get16le(s); // discard reserved
- stbi__get16le(s); // discard reserved
- info->offset = stbi__get32le(s);
- info->hsz = hsz = stbi__get32le(s);
- info->mr = info->mg = info->mb = info->ma = 0;
-
- if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
- if (hsz == 12) {
- s->img_x = stbi__get16le(s);
- s->img_y = stbi__get16le(s);
- } else {
- s->img_x = stbi__get32le(s);
- s->img_y = stbi__get32le(s);
- }
- if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
- info->bpp = stbi__get16le(s);
- if (hsz != 12) {
- int compress = stbi__get32le(s);
- if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
- stbi__get32le(s); // discard sizeof
- stbi__get32le(s); // discard hres
- stbi__get32le(s); // discard vres
- stbi__get32le(s); // discard colorsused
- stbi__get32le(s); // discard max important
- if (hsz == 40 || hsz == 56) {
- if (hsz == 56) {
- stbi__get32le(s);
- stbi__get32le(s);
- stbi__get32le(s);
- stbi__get32le(s);
- }
- if (info->bpp == 16 || info->bpp == 32) {
- if (compress == 0) {
- if (info->bpp == 32) {
- info->mr = 0xffu << 16;
- info->mg = 0xffu << 8;
- info->mb = 0xffu << 0;
- info->ma = 0xffu << 24;
- info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
- } else {
- info->mr = 31u << 10;
- info->mg = 31u << 5;
- info->mb = 31u << 0;
- }
- } else if (compress == 3) {
- info->mr = stbi__get32le(s);
- info->mg = stbi__get32le(s);
- info->mb = stbi__get32le(s);
- // not documented, but generated by photoshop and handled by mspaint
- if (info->mr == info->mg && info->mg == info->mb) {
- // ?!?!?
- return stbi__errpuc("bad BMP", "bad BMP");
- }
- } else
- return stbi__errpuc("bad BMP", "bad BMP");
- }
- } else {
- int i;
- if (hsz != 108 && hsz != 124)
- return stbi__errpuc("bad BMP", "bad BMP");
- info->mr = stbi__get32le(s);
- info->mg = stbi__get32le(s);
- info->mb = stbi__get32le(s);
- info->ma = stbi__get32le(s);
- stbi__get32le(s); // discard color space
- for (i=0; i < 12; ++i)
- stbi__get32le(s); // discard color space parameters
- if (hsz == 124) {
- stbi__get32le(s); // discard rendering intent
- stbi__get32le(s); // discard offset of profile data
- stbi__get32le(s); // discard size of profile data
- stbi__get32le(s); // discard reserved
- }
- }
- }
- return (void *) 1;
-}
-
-
-static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- stbi_uc *out;
- unsigned int mr=0,mg=0,mb=0,ma=0, all_a;
- stbi_uc pal[256][4];
- int psize=0,i,j,width;
- int flip_vertically, pad, target;
- stbi__bmp_data info;
- STBI_NOTUSED(ri);
-
- info.all_a = 255;
- if (stbi__bmp_parse_header(s, &info) == NULL)
- return NULL; // error code already set
-
- flip_vertically = ((int) s->img_y) > 0;
- s->img_y = abs((int) s->img_y);
-
- mr = info.mr;
- mg = info.mg;
- mb = info.mb;
- ma = info.ma;
- all_a = info.all_a;
-
- if (info.hsz == 12) {
- if (info.bpp < 24)
- psize = (info.offset - 14 - 24) / 3;
- } else {
- if (info.bpp < 16)
- psize = (info.offset - 14 - info.hsz) >> 2;
- }
-
- s->img_n = ma ? 4 : 3;
- if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
- target = req_comp;
- else
- target = s->img_n; // if they want monochrome, we'll post-convert
-
- // sanity-check size
- if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))
- return stbi__errpuc("too large", "Corrupt BMP");
-
- out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);
- if (!out) return stbi__errpuc("outofmem", "Out of memory");
- if (info.bpp < 16) {
- int z=0;
- if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
- for (i=0; i < psize; ++i) {
- pal[i][2] = stbi__get8(s);
- pal[i][1] = stbi__get8(s);
- pal[i][0] = stbi__get8(s);
- if (info.hsz != 12) stbi__get8(s);
- pal[i][3] = 255;
- }
- stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
- if (info.bpp == 1) width = (s->img_x + 7) >> 3;
- else if (info.bpp == 4) width = (s->img_x + 1) >> 1;
- else if (info.bpp == 8) width = s->img_x;
- else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
- pad = (-width)&3;
- if (info.bpp == 1) {
- for (j=0; j < (int) s->img_y; ++j) {
- int bit_offset = 7, v = stbi__get8(s);
- for (i=0; i < (int) s->img_x; ++i) {
- int color = (v>>bit_offset)&0x1;
- out[z++] = pal[color][0];
- out[z++] = pal[color][1];
- out[z++] = pal[color][2];
- if (target == 4) out[z++] = 255;
- if (i+1 == (int) s->img_x) break;
- if((--bit_offset) < 0) {
- bit_offset = 7;
- v = stbi__get8(s);
- }
- }
- stbi__skip(s, pad);
- }
- } else {
- for (j=0; j < (int) s->img_y; ++j) {
- for (i=0; i < (int) s->img_x; i += 2) {
- int v=stbi__get8(s),v2=0;
- if (info.bpp == 4) {
- v2 = v & 15;
- v >>= 4;
- }
- out[z++] = pal[v][0];
- out[z++] = pal[v][1];
- out[z++] = pal[v][2];
- if (target == 4) out[z++] = 255;
- if (i+1 == (int) s->img_x) break;
- v = (info.bpp == 8) ? stbi__get8(s) : v2;
- out[z++] = pal[v][0];
- out[z++] = pal[v][1];
- out[z++] = pal[v][2];
- if (target == 4) out[z++] = 255;
- }
- stbi__skip(s, pad);
- }
- }
- } else {
- int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
- int z = 0;
- int easy=0;
- stbi__skip(s, info.offset - 14 - info.hsz);
- if (info.bpp == 24) width = 3 * s->img_x;
- else if (info.bpp == 16) width = 2*s->img_x;
- else /* bpp = 32 and pad = 0 */ width=0;
- pad = (-width) & 3;
- if (info.bpp == 24) {
- easy = 1;
- } else if (info.bpp == 32) {
- if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
- easy = 2;
- }
- if (!easy) {
- if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
- // right shift amt to put high bit in position #7
- rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr);
- gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg);
- bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb);
- ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma);
- }
- for (j=0; j < (int) s->img_y; ++j) {
- if (easy) {
- for (i=0; i < (int) s->img_x; ++i) {
- unsigned char a;
- out[z+2] = stbi__get8(s);
- out[z+1] = stbi__get8(s);
- out[z+0] = stbi__get8(s);
- z += 3;
- a = (easy == 2 ? stbi__get8(s) : 255);
- all_a |= a;
- if (target == 4) out[z++] = a;
- }
- } else {
- int bpp = info.bpp;
- for (i=0; i < (int) s->img_x; ++i) {
- stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
- unsigned int a;
- out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
- out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
- out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
- a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
- all_a |= a;
- if (target == 4) out[z++] = STBI__BYTECAST(a);
- }
- }
- stbi__skip(s, pad);
- }
- }
-
- // if alpha channel is all 0s, replace with all 255s
- if (target == 4 && all_a == 0)
- for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
- out[i] = 255;
-
- if (flip_vertically) {
- stbi_uc t;
- for (j=0; j < (int) s->img_y>>1; ++j) {
- stbi_uc *p1 = out + j *s->img_x*target;
- stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
- for (i=0; i < (int) s->img_x*target; ++i) {
- t = p1[i]; p1[i] = p2[i]; p2[i] = t;
- }
- }
- }
-
- if (req_comp && req_comp != target) {
- out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
- if (out == NULL) return out; // stbi__convert_format frees input on failure
- }
-
- *x = s->img_x;
- *y = s->img_y;
- if (comp) *comp = s->img_n;
- return out;
-}
-#endif
-
-// Targa Truevision - TGA
-// by Jonathan Dummer
-#ifndef STBI_NO_TGA
-// returns STBI_rgb or whatever, 0 on error
-static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
-{
- // only RGB or RGBA (incl. 16bit) or grey allowed
- if (is_rgb16) *is_rgb16 = 0;
- switch(bits_per_pixel) {
- case 8: return STBI_grey;
- case 16: if(is_grey) return STBI_grey_alpha;
- // fallthrough
- case 15: if(is_rgb16) *is_rgb16 = 1;
- return STBI_rgb;
- case 24: // fallthrough
- case 32: return bits_per_pixel/8;
- default: return 0;
- }
-}
-
-static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
-{
- int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
- int sz, tga_colormap_type;
- stbi__get8(s); // discard Offset
- tga_colormap_type = stbi__get8(s); // colormap type
- if( tga_colormap_type > 1 ) {
- stbi__rewind(s);
- return 0; // only RGB or indexed allowed
- }
- tga_image_type = stbi__get8(s); // image type
- if ( tga_colormap_type == 1 ) { // colormapped (paletted) image
- if (tga_image_type != 1 && tga_image_type != 9) {
- stbi__rewind(s);
- return 0;
- }
- stbi__skip(s,4); // skip index of first colormap entry and number of entries
- sz = stbi__get8(s); // check bits per palette color entry
- if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {
- stbi__rewind(s);
- return 0;
- }
- stbi__skip(s,4); // skip image x and y origin
- tga_colormap_bpp = sz;
- } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
- if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {
- stbi__rewind(s);
- return 0; // only RGB or grey allowed, +/- RLE
- }
- stbi__skip(s,9); // skip colormap specification and image x/y origin
- tga_colormap_bpp = 0;
- }
- tga_w = stbi__get16le(s);
- if( tga_w < 1 ) {
- stbi__rewind(s);
- return 0; // test width
- }
- tga_h = stbi__get16le(s);
- if( tga_h < 1 ) {
- stbi__rewind(s);
- return 0; // test height
- }
- tga_bits_per_pixel = stbi__get8(s); // bits per pixel
- stbi__get8(s); // ignore alpha bits
- if (tga_colormap_bpp != 0) {
- if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
- // when using a colormap, tga_bits_per_pixel is the size of the indexes
- // I don't think anything but 8 or 16bit indexes makes sense
- stbi__rewind(s);
- return 0;
- }
- tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
- } else {
- tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
- }
- if(!tga_comp) {
- stbi__rewind(s);
- return 0;
- }
- if (x) *x = tga_w;
- if (y) *y = tga_h;
- if (comp) *comp = tga_comp;
- return 1; // seems to have passed everything
-}
-
-static int stbi__tga_test(stbi__context *s)
-{
- int res = 0;
- int sz, tga_color_type;
- stbi__get8(s); // discard Offset
- tga_color_type = stbi__get8(s); // color type
- if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed
- sz = stbi__get8(s); // image type
- if ( tga_color_type == 1 ) { // colormapped (paletted) image
- if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9
- stbi__skip(s,4); // skip index of first colormap entry and number of entries
- sz = stbi__get8(s); // check bits per palette color entry
- if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
- stbi__skip(s,4); // skip image x and y origin
- } else { // "normal" image w/o colormap
- if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE
- stbi__skip(s,9); // skip colormap specification and image x/y origin
- }
- if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width
- if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height
- sz = stbi__get8(s); // bits per pixel
- if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index
- if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
-
- res = 1; // if we got this far, everything's good and we can return 1 instead of 0
-
-errorEnd:
- stbi__rewind(s);
- return res;
-}
-
-// read 16bit value and convert to 24bit RGB
-static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)
-{
- stbi__uint16 px = (stbi__uint16)stbi__get16le(s);
- stbi__uint16 fiveBitMask = 31;
- // we have 3 channels with 5bits each
- int r = (px >> 10) & fiveBitMask;
- int g = (px >> 5) & fiveBitMask;
- int b = px & fiveBitMask;
- // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
- out[0] = (stbi_uc)((r * 255)/31);
- out[1] = (stbi_uc)((g * 255)/31);
- out[2] = (stbi_uc)((b * 255)/31);
-
- // some people claim that the most significant bit might be used for alpha
- // (possibly if an alpha-bit is set in the "image descriptor byte")
- // but that only made 16bit test images completely translucent..
- // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
-}
-
-static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- // read in the TGA header stuff
- int tga_offset = stbi__get8(s);
- int tga_indexed = stbi__get8(s);
- int tga_image_type = stbi__get8(s);
- int tga_is_RLE = 0;
- int tga_palette_start = stbi__get16le(s);
- int tga_palette_len = stbi__get16le(s);
- int tga_palette_bits = stbi__get8(s);
- int tga_x_origin = stbi__get16le(s);
- int tga_y_origin = stbi__get16le(s);
- int tga_width = stbi__get16le(s);
- int tga_height = stbi__get16le(s);
- int tga_bits_per_pixel = stbi__get8(s);
- int tga_comp, tga_rgb16=0;
- int tga_inverted = stbi__get8(s);
- // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
- // image data
- unsigned char *tga_data;
- unsigned char *tga_palette = NULL;
- int i, j;
- unsigned char raw_data[4] = {0};
- int RLE_count = 0;
- int RLE_repeating = 0;
- int read_next_pixel = 1;
- STBI_NOTUSED(ri);
-
- // do a tiny bit of precessing
- if ( tga_image_type >= 8 )
- {
- tga_image_type -= 8;
- tga_is_RLE = 1;
- }
- tga_inverted = 1 - ((tga_inverted >> 5) & 1);
-
- // If I'm paletted, then I'll use the number of bits from the palette
- if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
- else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
-
- if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
- return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
-
- // tga info
- *x = tga_width;
- *y = tga_height;
- if (comp) *comp = tga_comp;
-
- if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))
- return stbi__errpuc("too large", "Corrupt TGA");
-
- tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
- if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
-
- // skip to the data's starting position (offset usually = 0)
- stbi__skip(s, tga_offset );
-
- if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
- for (i=0; i < tga_height; ++i) {
- int row = tga_inverted ? tga_height -i - 1 : i;
- stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
- stbi__getn(s, tga_row, tga_width * tga_comp);
- }
- } else {
- // do I need to load a palette?
- if ( tga_indexed)
- {
- // any data to skip? (offset usually = 0)
- stbi__skip(s, tga_palette_start );
- // load the palette
- tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);
- if (!tga_palette) {
- STBI_FREE(tga_data);
- return stbi__errpuc("outofmem", "Out of memory");
- }
- if (tga_rgb16) {
- stbi_uc *pal_entry = tga_palette;
- STBI_ASSERT(tga_comp == STBI_rgb);
- for (i=0; i < tga_palette_len; ++i) {
- stbi__tga_read_rgb16(s, pal_entry);
- pal_entry += tga_comp;
- }
- } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
- STBI_FREE(tga_data);
- STBI_FREE(tga_palette);
- return stbi__errpuc("bad palette", "Corrupt TGA");
- }
- }
- // load the data
- for (i=0; i < tga_width * tga_height; ++i)
- {
- // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
- if ( tga_is_RLE )
- {
- if ( RLE_count == 0 )
- {
- // yep, get the next byte as a RLE command
- int RLE_cmd = stbi__get8(s);
- RLE_count = 1 + (RLE_cmd & 127);
- RLE_repeating = RLE_cmd >> 7;
- read_next_pixel = 1;
- } else if ( !RLE_repeating )
- {
- read_next_pixel = 1;
- }
- } else
- {
- read_next_pixel = 1;
- }
- // OK, if I need to read a pixel, do it now
- if ( read_next_pixel )
- {
- // load however much data we did have
- if ( tga_indexed )
- {
- // read in index, then perform the lookup
- int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);
- if ( pal_idx >= tga_palette_len ) {
- // invalid index
- pal_idx = 0;
- }
- pal_idx *= tga_comp;
- for (j = 0; j < tga_comp; ++j) {
- raw_data[j] = tga_palette[pal_idx+j];
- }
- } else if(tga_rgb16) {
- STBI_ASSERT(tga_comp == STBI_rgb);
- stbi__tga_read_rgb16(s, raw_data);
- } else {
- // read in the data raw
- for (j = 0; j < tga_comp; ++j) {
- raw_data[j] = stbi__get8(s);
- }
- }
- // clear the reading flag for the next pixel
- read_next_pixel = 0;
- } // end of reading a pixel
-
- // copy data
- for (j = 0; j < tga_comp; ++j)
- tga_data[i*tga_comp+j] = raw_data[j];
-
- // in case we're in RLE mode, keep counting down
- --RLE_count;
- }
- // do I need to invert the image?
- if ( tga_inverted )
- {
- for (j = 0; j*2 < tga_height; ++j)
- {
- int index1 = j * tga_width * tga_comp;
- int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
- for (i = tga_width * tga_comp; i > 0; --i)
- {
- unsigned char temp = tga_data[index1];
- tga_data[index1] = tga_data[index2];
- tga_data[index2] = temp;
- ++index1;
- ++index2;
- }
- }
- }
- // clear my palette, if I had one
- if ( tga_palette != NULL )
- {
- STBI_FREE( tga_palette );
- }
- }
-
- // swap RGB - if the source data was RGB16, it already is in the right order
- if (tga_comp >= 3 && !tga_rgb16)
- {
- unsigned char* tga_pixel = tga_data;
- for (i=0; i < tga_width * tga_height; ++i)
- {
- unsigned char temp = tga_pixel[0];
- tga_pixel[0] = tga_pixel[2];
- tga_pixel[2] = temp;
- tga_pixel += tga_comp;
- }
- }
-
- // convert to target component count
- if (req_comp && req_comp != tga_comp)
- tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
-
- // the things I do to get rid of an error message, and yet keep
- // Microsoft's C compilers happy... [8^(
- tga_palette_start = tga_palette_len = tga_palette_bits =
- tga_x_origin = tga_y_origin = 0;
- // OK, done
- return tga_data;
-}
-#endif
-
-// *************************************************************************************************
-// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
-
-#ifndef STBI_NO_PSD
-static int stbi__psd_test(stbi__context *s)
-{
- int r = (stbi__get32be(s) == 0x38425053);
- stbi__rewind(s);
- return r;
-}
-
-static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount)
-{
- int count, nleft, len;
-
- count = 0;
- while ((nleft = pixelCount - count) > 0) {
- len = stbi__get8(s);
- if (len == 128) {
- // No-op.
- } else if (len < 128) {
- // Copy next len+1 bytes literally.
- len++;
- if (len > nleft) return 0; // corrupt data
- count += len;
- while (len) {
- *p = stbi__get8(s);
- p += 4;
- len--;
- }
- } else if (len > 128) {
- stbi_uc val;
- // Next -len+1 bytes in the dest are replicated from next source byte.
- // (Interpret len as a negative 8-bit int.)
- len = 257 - len;
- if (len > nleft) return 0; // corrupt data
- val = stbi__get8(s);
- count += len;
- while (len) {
- *p = val;
- p += 4;
- len--;
- }
- }
- }
-
- return 1;
-}
-
-static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
-{
- int pixelCount;
- int channelCount, compression;
- int channel, i;
- int bitdepth;
- int w,h;
- stbi_uc *out;
- STBI_NOTUSED(ri);
-
- // Check identifier
- if (stbi__get32be(s) != 0x38425053) // "8BPS"
- return stbi__errpuc("not PSD", "Corrupt PSD image");
-
- // Check file type version.
- if (stbi__get16be(s) != 1)
- return stbi__errpuc("wrong version", "Unsupported version of PSD image");
-
- // Skip 6 reserved bytes.
- stbi__skip(s, 6 );
-
- // Read the number of channels (R, G, B, A, etc).
- channelCount = stbi__get16be(s);
- if (channelCount < 0 || channelCount > 16)
- return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
-
- // Read the rows and columns of the image.
- h = stbi__get32be(s);
- w = stbi__get32be(s);
-
- // Make sure the depth is 8 bits.
- bitdepth = stbi__get16be(s);
- if (bitdepth != 8 && bitdepth != 16)
- return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
-
- // Make sure the color mode is RGB.
- // Valid options are:
- // 0: Bitmap
- // 1: Grayscale
- // 2: Indexed color
- // 3: RGB color
- // 4: CMYK color
- // 7: Multichannel
- // 8: Duotone
- // 9: Lab color
- if (stbi__get16be(s) != 3)
- return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
-
- // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
- stbi__skip(s,stbi__get32be(s) );
-
- // Skip the image resources. (resolution, pen tool paths, etc)
- stbi__skip(s, stbi__get32be(s) );
-
- // Skip the reserved data.
- stbi__skip(s, stbi__get32be(s) );
-
- // Find out if the data is compressed.
- // Known values:
- // 0: no compression
- // 1: RLE compressed
- compression = stbi__get16be(s);
- if (compression > 1)
- return stbi__errpuc("bad compression", "PSD has an unknown compression format");
-
- // Check size
- if (!stbi__mad3sizes_valid(4, w, h, 0))
- return stbi__errpuc("too large", "Corrupt PSD");
-
- // Create the destination image.
-
- if (!compression && bitdepth == 16 && bpc == 16) {
- out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);
- ri->bits_per_channel = 16;
- } else
- out = (stbi_uc *) stbi__malloc(4 * w*h);
-
- if (!out) return stbi__errpuc("outofmem", "Out of memory");
- pixelCount = w*h;
-
- // Initialize the data to zero.
- //memset( out, 0, pixelCount * 4 );
-
- // Finally, the image data.
- if (compression) {
- // RLE as used by .PSD and .TIFF
- // Loop until you get the number of unpacked bytes you are expecting:
- // Read the next source byte into n.
- // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
- // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
- // Else if n is 128, noop.
- // Endloop
-
- // The RLE-compressed data is preceded by a 2-byte data count for each row in the data,
- // which we're going to just skip.
- stbi__skip(s, h * channelCount * 2 );
-
- // Read the RLE data by channel.
- for (channel = 0; channel < 4; channel++) {
- stbi_uc *p;
-
- p = out+channel;
- if (channel >= channelCount) {
- // Fill this channel with default data.
- for (i = 0; i < pixelCount; i++, p += 4)
- *p = (channel == 3 ? 255 : 0);
- } else {
- // Read the RLE data.
- if (!stbi__psd_decode_rle(s, p, pixelCount)) {
- STBI_FREE(out);
- return stbi__errpuc("corrupt", "bad RLE data");
- }
- }
- }
-
- } else {
- // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
- // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.
-
- // Read the data by channel.
- for (channel = 0; channel < 4; channel++) {
- if (channel >= channelCount) {
- // Fill this channel with default data.
- if (bitdepth == 16 && bpc == 16) {
- stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
- stbi__uint16 val = channel == 3 ? 65535 : 0;
- for (i = 0; i < pixelCount; i++, q += 4)
- *q = val;
- } else {
- stbi_uc *p = out+channel;
- stbi_uc val = channel == 3 ? 255 : 0;
- for (i = 0; i < pixelCount; i++, p += 4)
- *p = val;
- }
- } else {
- if (ri->bits_per_channel == 16) { // output bpc
- stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
- for (i = 0; i < pixelCount; i++, q += 4)
- *q = (stbi__uint16) stbi__get16be(s);
- } else {
- stbi_uc *p = out+channel;
- if (bitdepth == 16) { // input bpc
- for (i = 0; i < pixelCount; i++, p += 4)
- *p = (stbi_uc) (stbi__get16be(s) >> 8);
- } else {
- for (i = 0; i < pixelCount; i++, p += 4)
- *p = stbi__get8(s);
- }
- }
- }
- }
- }
-
- // remove weird white matte from PSD
- if (channelCount >= 4) {
- if (ri->bits_per_channel == 16) {
- for (i=0; i < w*h; ++i) {
- stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;
- if (pixel[3] != 0 && pixel[3] != 65535) {
- float a = pixel[3] / 65535.0f;
- float ra = 1.0f / a;
- float inv_a = 65535.0f * (1 - ra);
- pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);
- pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);
- pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);
- }
- }
- } else {
- for (i=0; i < w*h; ++i) {
- unsigned char *pixel = out + 4*i;
- if (pixel[3] != 0 && pixel[3] != 255) {
- float a = pixel[3] / 255.0f;
- float ra = 1.0f / a;
- float inv_a = 255.0f * (1 - ra);
- pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);
- pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);
- pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);
- }
- }
- }
- }
-
- // convert to desired output format
- if (req_comp && req_comp != 4) {
- if (ri->bits_per_channel == 16)
- out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);
- else
- out = stbi__convert_format(out, 4, req_comp, w, h);
- if (out == NULL) return out; // stbi__convert_format frees input on failure
- }
-
- if (comp) *comp = 4;
- *y = h;
- *x = w;
-
- return out;
-}
-#endif
-
-// *************************************************************************************************
-// Softimage PIC loader
-// by Tom Seddon
-//
-// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
-// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
-
-#ifndef STBI_NO_PIC
-static int stbi__pic_is4(stbi__context *s,const char *str)
-{
- int i;
- for (i=0; i<4; ++i)
- if (stbi__get8(s) != (stbi_uc)str[i])
- return 0;
-
- return 1;
-}
-
-static int stbi__pic_test_core(stbi__context *s)
-{
- int i;
-
- if (!stbi__pic_is4(s,"\x53\x80\xF6\x34"))
- return 0;
-
- for(i=0;i<84;++i)
- stbi__get8(s);
-
- if (!stbi__pic_is4(s,"PICT"))
- return 0;
-
- return 1;
-}
-
-typedef struct
-{
- stbi_uc size,type,channel;
-} stbi__pic_packet;
-
-static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest)
-{
- int mask=0x80, i;
-
- for (i=0; i<4; ++i, mask>>=1) {
- if (channel & mask) {
- if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short");
- dest[i]=stbi__get8(s);
- }
- }
-
- return dest;
-}
-
-static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src)
-{
- int mask=0x80,i;
-
- for (i=0;i<4; ++i, mask>>=1)
- if (channel&mask)
- dest[i]=src[i];
-}
-
-static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result)
-{
- int act_comp=0,num_packets=0,y,chained;
- stbi__pic_packet packets[10];
-
- // this will (should...) cater for even some bizarre stuff like having data
- // for the same channel in multiple packets.
- do {
- stbi__pic_packet *packet;
-
- if (num_packets==sizeof(packets)/sizeof(packets[0]))
- return stbi__errpuc("bad format","too many packets");
-
- packet = &packets[num_packets++];
-
- chained = stbi__get8(s);
- packet->size = stbi__get8(s);
- packet->type = stbi__get8(s);
- packet->channel = stbi__get8(s);
-
- act_comp |= packet->channel;
-
- if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)");
- if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp");
- } while (chained);
-
- *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
-
- for(y=0; y<height; ++y) {
- int packet_idx;
-
- for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {
- stbi__pic_packet *packet = &packets[packet_idx];
- stbi_uc *dest = result+y*width*4;
-
- switch (packet->type) {
- default:
- return stbi__errpuc("bad format","packet has bad compression type");
-
- case 0: {//uncompressed
- int x;
-
- for(x=0;x<width;++x, dest+=4)
- if (!stbi__readval(s,packet->channel,dest))
- return 0;
- break;
- }
-
- case 1://Pure RLE
- {
- int left=width, i;
-
- while (left>0) {
- stbi_uc count,value[4];
-
- count=stbi__get8(s);
- if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)");
-
- if (count > left)
- count = (stbi_uc) left;
-
- if (!stbi__readval(s,packet->channel,value)) return 0;
-
- for(i=0; i<count; ++i,dest+=4)
- stbi__copyval(packet->channel,dest,value);
- left -= count;
- }
- }
- break;
-
- case 2: {//Mixed RLE
- int left=width;
- while (left>0) {
- int count = stbi__get8(s), i;
- if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");
-
- if (count >= 128) { // Repeated
- stbi_uc value[4];
-
- if (count==128)
- count = stbi__get16be(s);
- else
- count -= 127;
- if (count > left)
- return stbi__errpuc("bad file","scanline overrun");
-
- if (!stbi__readval(s,packet->channel,value))
- return 0;
-
- for(i=0;i<count;++i, dest += 4)
- stbi__copyval(packet->channel,dest,value);
- } else { // Raw
- ++count;
- if (count>left) return stbi__errpuc("bad file","scanline overrun");
-
- for(i=0;i<count;++i, dest+=4)
- if (!stbi__readval(s,packet->channel,dest))
- return 0;
- }
- left-=count;
- }
- break;
- }
- }
- }
- }
-
- return result;
-}
-
-static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri)
-{
- stbi_uc *result;
- int i, x,y, internal_comp;
- STBI_NOTUSED(ri);
-
- if (!comp) comp = &internal_comp;
-
- for (i=0; i<92; ++i)
- stbi__get8(s);
-
- x = stbi__get16be(s);
- y = stbi__get16be(s);
- if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");
- if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode");
-
- stbi__get32be(s); //skip `ratio'
- stbi__get16be(s); //skip `fields'
- stbi__get16be(s); //skip `pad'
-
- // intermediate buffer is RGBA
- result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
- memset(result, 0xff, x*y*4);
-
- if (!stbi__pic_load_core(s,x,y,comp, result)) {
- STBI_FREE(result);
- result=0;
- }
- *px = x;
- *py = y;
- if (req_comp == 0) req_comp = *comp;
- result=stbi__convert_format(result,4,req_comp,x,y);
-
- return result;
-}
-
-static int stbi__pic_test(stbi__context *s)
-{
- int r = stbi__pic_test_core(s);
- stbi__rewind(s);
- return r;
-}
-#endif
-
-// *************************************************************************************************
-// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
-
-#ifndef STBI_NO_GIF
-typedef struct
-{
- stbi__int16 prefix;
- stbi_uc first;
- stbi_uc suffix;
-} stbi__gif_lzw;
-
-typedef struct
-{
- int w,h;
- stbi_uc *out; // output buffer (always 4 components)
- stbi_uc *background; // The current "background" as far as a gif is concerned
- stbi_uc *history;
- int flags, bgindex, ratio, transparent, eflags;
- stbi_uc pal[256][4];
- stbi_uc lpal[256][4];
- stbi__gif_lzw codes[8192];
- stbi_uc *color_table;
- int parse, step;
- int lflags;
- int start_x, start_y;
- int max_x, max_y;
- int cur_x, cur_y;
- int line_size;
- int delay;
-} stbi__gif;
-
-static int stbi__gif_test_raw(stbi__context *s)
-{
- int sz;
- if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
- sz = stbi__get8(s);
- if (sz != '9' && sz != '7') return 0;
- if (stbi__get8(s) != 'a') return 0;
- return 1;
-}
-
-static int stbi__gif_test(stbi__context *s)
-{
- int r = stbi__gif_test_raw(s);
- stbi__rewind(s);
- return r;
-}
-
-static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp)
-{
- int i;
- for (i=0; i < num_entries; ++i) {
- pal[i][2] = stbi__get8(s);
- pal[i][1] = stbi__get8(s);
- pal[i][0] = stbi__get8(s);
- pal[i][3] = transp == i ? 0 : 255;
- }
-}
-
-static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)
-{
- stbi_uc version;
- if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
- return stbi__err("not GIF", "Corrupt GIF");
-
- version = stbi__get8(s);
- if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF");
- if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF");
-
- stbi__g_failure_reason = "";
- g->w = stbi__get16le(s);
- g->h = stbi__get16le(s);
- g->flags = stbi__get8(s);
- g->bgindex = stbi__get8(s);
- g->ratio = stbi__get8(s);
- g->transparent = -1;
-
- if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
-
- if (is_info) return 1;
-
- if (g->flags & 0x80)
- stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
-
- return 1;
-}
-
-static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
-{
- stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
- if (!stbi__gif_header(s, g, comp, 1)) {
- STBI_FREE(g);
- stbi__rewind( s );
- return 0;
- }
- if (x) *x = g->w;
- if (y) *y = g->h;
- STBI_FREE(g);
- return 1;
-}
-
-static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
-{
- stbi_uc *p, *c;
- int idx;
-
- // recurse to decode the prefixes, since the linked-list is backwards,
- // and working backwards through an interleaved image would be nasty
- if (g->codes[code].prefix >= 0)
- stbi__out_gif_code(g, g->codes[code].prefix);
-
- if (g->cur_y >= g->max_y) return;
-
- idx = g->cur_x + g->cur_y;
- p = &g->out[idx];
- g->history[idx / 4] = 1;
-
- c = &g->color_table[g->codes[code].suffix * 4];
- if (c[3] > 128) { // don't render transparent pixels;
- p[0] = c[2];
- p[1] = c[1];
- p[2] = c[0];
- p[3] = c[3];
- }
- g->cur_x += 4;
-
- if (g->cur_x >= g->max_x) {
- g->cur_x = g->start_x;
- g->cur_y += g->step;
-
- while (g->cur_y >= g->max_y && g->parse > 0) {
- g->step = (1 << g->parse) * g->line_size;
- g->cur_y = g->start_y + (g->step >> 1);
- --g->parse;
- }
- }
-}
-
-static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
-{
- stbi_uc lzw_cs;
- stbi__int32 len, init_code;
- stbi__uint32 first;
- stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
- stbi__gif_lzw *p;
-
- lzw_cs = stbi__get8(s);
- if (lzw_cs > 12) return NULL;
- clear = 1 << lzw_cs;
- first = 1;
- codesize = lzw_cs + 1;
- codemask = (1 << codesize) - 1;
- bits = 0;
- valid_bits = 0;
- for (init_code = 0; init_code < clear; init_code++) {
- g->codes[init_code].prefix = -1;
- g->codes[init_code].first = (stbi_uc) init_code;
- g->codes[init_code].suffix = (stbi_uc) init_code;
- }
-
- // support no starting clear code
- avail = clear+2;
- oldcode = -1;
-
- len = 0;
- for(;;) {
- if (valid_bits < codesize) {
- if (len == 0) {
- len = stbi__get8(s); // start new block
- if (len == 0)
- return g->out;
- }
- --len;
- bits |= (stbi__int32) stbi__get8(s) << valid_bits;
- valid_bits += 8;
- } else {
- stbi__int32 code = bits & codemask;
- bits >>= codesize;
- valid_bits -= codesize;
- // @OPTIMIZE: is there some way we can accelerate the non-clear path?
- if (code == clear) { // clear code
- codesize = lzw_cs + 1;
- codemask = (1 << codesize) - 1;
- avail = clear + 2;
- oldcode = -1;
- first = 0;
- } else if (code == clear + 1) { // end of stream code
- stbi__skip(s, len);
- while ((len = stbi__get8(s)) > 0)
- stbi__skip(s,len);
- return g->out;
- } else if (code <= avail) {
- if (first) {
- return stbi__errpuc("no clear code", "Corrupt GIF");
- }
-
- if (oldcode >= 0) {
- p = &g->codes[avail++];
- if (avail > 8192) {
- return stbi__errpuc("too many codes", "Corrupt GIF");
- }
-
- p->prefix = (stbi__int16) oldcode;
- p->first = g->codes[oldcode].first;
- p->suffix = (code == avail) ? p->first : g->codes[code].first;
- } else if (code == avail)
- return stbi__errpuc("illegal code in raster", "Corrupt GIF");
-
- stbi__out_gif_code(g, (stbi__uint16) code);
-
- if ((avail & codemask) == 0 && avail <= 0x0FFF) {
- codesize++;
- codemask = (1 << codesize) - 1;
- }
-
- oldcode = code;
- } else {
- return stbi__errpuc("illegal code in raster", "Corrupt GIF");
- }
- }
- }
-}
-
-// this function is designed to support animated gifs, although stb_image doesn't support it
-// two back is the image from two frames ago, used for a very specific disposal format
-static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)
-{
- int dispose;
- int first_frame;
- int pi;
- int pcount;
- STBI_NOTUSED(req_comp);
-
- // on first frame, any non-written pixels get the background colour (non-transparent)
- first_frame = 0;
- if (g->out == 0) {
- if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
- if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))
- return stbi__errpuc("too large", "GIF image is too large");
- pcount = g->w * g->h;
- g->out = (stbi_uc *) stbi__malloc(4 * pcount);
- g->background = (stbi_uc *) stbi__malloc(4 * pcount);
- g->history = (stbi_uc *) stbi__malloc(pcount);
- if (!g->out || !g->background || !g->history)
- return stbi__errpuc("outofmem", "Out of memory");
-
- // image is treated as "transparent" at the start - ie, nothing overwrites the current background;
- // background colour is only used for pixels that are not rendered first frame, after that "background"
- // color refers to the color that was there the previous frame.
- memset(g->out, 0x00, 4 * pcount);
- memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)
- memset(g->history, 0x00, pcount); // pixels that were affected previous frame
- first_frame = 1;
- } else {
- // second frame - how do we dispoase of the previous one?
- dispose = (g->eflags & 0x1C) >> 2;
- pcount = g->w * g->h;
-
- if ((dispose == 3) && (two_back == 0)) {
- dispose = 2; // if I don't have an image to revert back to, default to the old background
- }
-
- if (dispose == 3) { // use previous graphic
- for (pi = 0; pi < pcount; ++pi) {
- if (g->history[pi]) {
- memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
- }
- }
- } else if (dispose == 2) {
- // restore what was changed last frame to background before that frame;
- for (pi = 0; pi < pcount; ++pi) {
- if (g->history[pi]) {
- memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
- }
- }
- } else {
- // This is a non-disposal case eithe way, so just
- // leave the pixels as is, and they will become the new background
- // 1: do not dispose
- // 0: not specified.
- }
-
- // background is what out is after the undoing of the previou frame;
- memcpy( g->background, g->out, 4 * g->w * g->h );
- }
-
- // clear my history;
- memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
-
- for (;;) {
- int tag = stbi__get8(s);
- switch (tag) {
- case 0x2C: /* Image Descriptor */
- {
- stbi__int32 x, y, w, h;
- stbi_uc *o;
-
- x = stbi__get16le(s);
- y = stbi__get16le(s);
- w = stbi__get16le(s);
- h = stbi__get16le(s);
- if (((x + w) > (g->w)) || ((y + h) > (g->h)))
- return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
-
- g->line_size = g->w * 4;
- g->start_x = x * 4;
- g->start_y = y * g->line_size;
- g->max_x = g->start_x + w * 4;
- g->max_y = g->start_y + h * g->line_size;
- g->cur_x = g->start_x;
- g->cur_y = g->start_y;
-
- // if the width of the specified rectangle is 0, that means
- // we may not see *any* pixels or the image is malformed;
- // to make sure this is caught, move the current y down to
- // max_y (which is what out_gif_code checks).
- if (w == 0)
- g->cur_y = g->max_y;
-
- g->lflags = stbi__get8(s);
-
- if (g->lflags & 0x40) {
- g->step = 8 * g->line_size; // first interlaced spacing
- g->parse = 3;
- } else {
- g->step = g->line_size;
- g->parse = 0;
- }
-
- if (g->lflags & 0x80) {
- stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
- g->color_table = (stbi_uc *) g->lpal;
- } else if (g->flags & 0x80) {
- g->color_table = (stbi_uc *) g->pal;
- } else
- return stbi__errpuc("missing color table", "Corrupt GIF");
-
- o = stbi__process_gif_raster(s, g);
- if (!o) return NULL;
-
- // if this was the first frame,
- pcount = g->w * g->h;
- if (first_frame && (g->bgindex > 0)) {
- // if first frame, any pixel not drawn to gets the background color
- for (pi = 0; pi < pcount; ++pi) {
- if (g->history[pi] == 0) {
- g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
- memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
- }
- }
- }
-
- return o;
- }
-
- case 0x21: // Comment Extension.
- {
- int len;
- int ext = stbi__get8(s);
- if (ext == 0xF9) { // Graphic Control Extension.
- len = stbi__get8(s);
- if (len == 4) {
- g->eflags = stbi__get8(s);
- g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.
-
- // unset old transparent
- if (g->transparent >= 0) {
- g->pal[g->transparent][3] = 255;
- }
- if (g->eflags & 0x01) {
- g->transparent = stbi__get8(s);
- if (g->transparent >= 0) {
- g->pal[g->transparent][3] = 0;
- }
- } else {
- // don't need transparent
- stbi__skip(s, 1);
- g->transparent = -1;
- }
- } else {
- stbi__skip(s, len);
- break;
- }
- }
- while ((len = stbi__get8(s)) != 0) {
- stbi__skip(s, len);
- }
- break;
- }
-
- case 0x3B: // gif stream termination code
- return (stbi_uc *) s; // using '1' causes warning on some compilers
-
- default:
- return stbi__errpuc("unknown code", "Corrupt GIF");
- }
- }
-}
-
-static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
-{
- if (stbi__gif_test(s)) {
- int layers = 0;
- stbi_uc *u = 0;
- stbi_uc *out = 0;
- stbi_uc *two_back = 0;
- stbi__gif g;
- int stride;
- memset(&g, 0, sizeof(g));
- if (delays) {
- *delays = 0;
- }
-
- do {
- u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
- if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
-
- if (u) {
- *x = g.w;
- *y = g.h;
- ++layers;
- stride = g.w * g.h * 4;
-
- if (out) {
- out = (stbi_uc*) STBI_REALLOC( out, layers * stride );
- if (delays) {
- *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );
- }
- } else {
- out = (stbi_uc*)stbi__malloc( layers * stride );
- if (delays) {
- *delays = (int*) stbi__malloc( layers * sizeof(int) );
- }
- }
- memcpy( out + ((layers - 1) * stride), u, stride );
- if (layers >= 2) {
- two_back = out - 2 * stride;
- }
-
- if (delays) {
- (*delays)[layers - 1U] = g.delay;
- }
- }
- } while (u != 0);
-
- // free temp buffer;
- STBI_FREE(g.out);
- STBI_FREE(g.history);
- STBI_FREE(g.background);
-
- // do the final conversion after loading everything;
- if (req_comp && req_comp != 4)
- out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
-
- *z = layers;
- return out;
- } else {
- return stbi__errpuc("not GIF", "Image was not as a gif type.");
- }
-}
-
-static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- stbi_uc *u = 0;
- stbi__gif g;
- memset(&g, 0, sizeof(g));
- STBI_NOTUSED(ri);
-
- u = stbi__gif_load_next(s, &g, comp, req_comp, 0);
- if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
- if (u) {
- *x = g.w;
- *y = g.h;
-
- // moved conversion to after successful load so that the same
- // can be done for multiple frames.
- if (req_comp && req_comp != 4)
- u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
- } else if (g.out) {
- // if there was an error and we allocated an image buffer, free it!
- STBI_FREE(g.out);
- }
-
- // free buffers needed for multiple frame loading;
- STBI_FREE(g.history);
- STBI_FREE(g.background);
-
- return u;
-}
-
-static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp)
-{
- return stbi__gif_info_raw(s,x,y,comp);
-}
-#endif
-
-// *************************************************************************************************
-// Radiance RGBE HDR loader
-// originally by Nicolas Schulz
-#ifndef STBI_NO_HDR
-static int stbi__hdr_test_core(stbi__context *s, const char *signature)
-{
- int i;
- for (i=0; signature[i]; ++i)
- if (stbi__get8(s) != signature[i])
- return 0;
- stbi__rewind(s);
- return 1;
-}
-
-static int stbi__hdr_test(stbi__context* s)
-{
- int r = stbi__hdr_test_core(s, "#?RADIANCE\n");
- stbi__rewind(s);
- if(!r) {
- r = stbi__hdr_test_core(s, "#?RGBE\n");
- stbi__rewind(s);
- }
- return r;
-}
-
-#define STBI__HDR_BUFLEN 1024
-static char *stbi__hdr_gettoken(stbi__context *z, char *buffer)
-{
- int len=0;
- char c = '\0';
-
- c = (char) stbi__get8(z);
-
- while (!stbi__at_eof(z) && c != '\n') {
- buffer[len++] = c;
- if (len == STBI__HDR_BUFLEN-1) {
- // flush to end of line
- while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
- ;
- break;
- }
- c = (char) stbi__get8(z);
- }
-
- buffer[len] = 0;
- return buffer;
-}
-
-static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
-{
- if ( input[3] != 0 ) {
- float f1;
- // Exponent
- f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
- if (req_comp <= 2)
- output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
- else {
- output[0] = input[0] * f1;
- output[1] = input[1] * f1;
- output[2] = input[2] * f1;
- }
- if (req_comp == 2) output[1] = 1;
- if (req_comp == 4) output[3] = 1;
- } else {
- switch (req_comp) {
- case 4: output[3] = 1; /* fallthrough */
- case 3: output[0] = output[1] = output[2] = 0;
- break;
- case 2: output[1] = 1; /* fallthrough */
- case 1: output[0] = 0;
- break;
- }
- }
-}
-
-static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- char buffer[STBI__HDR_BUFLEN];
- char *token;
- int valid = 0;
- int width, height;
- stbi_uc *scanline;
- float *hdr_data;
- int len;
- unsigned char count, value;
- int i, j, k, c1,c2, z;
- const char *headerToken;
- STBI_NOTUSED(ri);
-
- // Check identifier
- headerToken = stbi__hdr_gettoken(s,buffer);
- if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0)
- return stbi__errpf("not HDR", "Corrupt HDR image");
-
- // Parse header
- for(;;) {
- token = stbi__hdr_gettoken(s,buffer);
- if (token[0] == 0) break;
- if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
- }
-
- if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format");
-
- // Parse width and height
- // can't use sscanf() if we're not using stdio!
- token = stbi__hdr_gettoken(s,buffer);
- if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
- token += 3;
- height = (int) strtol(token, &token, 10);
- while (*token == ' ') ++token;
- if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
- token += 3;
- width = (int) strtol(token, NULL, 10);
-
- *x = width;
- *y = height;
-
- if (comp) *comp = 3;
- if (req_comp == 0) req_comp = 3;
-
- if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))
- return stbi__errpf("too large", "HDR image is too large");
-
- // Read data
- hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
- if (!hdr_data)
- return stbi__errpf("outofmem", "Out of memory");
-
- // Load image data
- // image data is stored as some number of sca
- if ( width < 8 || width >= 32768) {
- // Read flat data
- for (j=0; j < height; ++j) {
- for (i=0; i < width; ++i) {
- stbi_uc rgbe[4];
- main_decode_loop:
- stbi__getn(s, rgbe, 4);
- stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
- }
- }
- } else {
- // Read RLE-encoded data
- scanline = NULL;
-
- for (j = 0; j < height; ++j) {
- c1 = stbi__get8(s);
- c2 = stbi__get8(s);
- len = stbi__get8(s);
- if (c1 != 2 || c2 != 2 || (len & 0x80)) {
- // not run-length encoded, so we have to actually use THIS data as a decoded
- // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
- stbi_uc rgbe[4];
- rgbe[0] = (stbi_uc) c1;
- rgbe[1] = (stbi_uc) c2;
- rgbe[2] = (stbi_uc) len;
- rgbe[3] = (stbi_uc) stbi__get8(s);
- stbi__hdr_convert(hdr_data, rgbe, req_comp);
- i = 1;
- j = 0;
- STBI_FREE(scanline);
- goto main_decode_loop; // yes, this makes no sense
- }
- len <<= 8;
- len |= stbi__get8(s);
- if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
- if (scanline == NULL) {
- scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);
- if (!scanline) {
- STBI_FREE(hdr_data);
- return stbi__errpf("outofmem", "Out of memory");
- }
- }
-
- for (k = 0; k < 4; ++k) {
- int nleft;
- i = 0;
- while ((nleft = width - i) > 0) {
- count = stbi__get8(s);
- if (count > 128) {
- // Run
- value = stbi__get8(s);
- count -= 128;
- if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
- for (z = 0; z < count; ++z)
- scanline[i++ * 4 + k] = value;
- } else {
- // Dump
- if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
- for (z = 0; z < count; ++z)
- scanline[i++ * 4 + k] = stbi__get8(s);
- }
- }
- }
- for (i=0; i < width; ++i)
- stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
- }
- if (scanline)
- STBI_FREE(scanline);
- }
-
- return hdr_data;
-}
-
-static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
-{
- char buffer[STBI__HDR_BUFLEN];
- char *token;
- int valid = 0;
- int dummy;
-
- if (!x) x = &dummy;
- if (!y) y = &dummy;
- if (!comp) comp = &dummy;
-
- if (stbi__hdr_test(s) == 0) {
- stbi__rewind( s );
- return 0;
- }
-
- for(;;) {
- token = stbi__hdr_gettoken(s,buffer);
- if (token[0] == 0) break;
- if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
- }
-
- if (!valid) {
- stbi__rewind( s );
- return 0;
- }
- token = stbi__hdr_gettoken(s,buffer);
- if (strncmp(token, "-Y ", 3)) {
- stbi__rewind( s );
- return 0;
- }
- token += 3;
- *y = (int) strtol(token, &token, 10);
- while (*token == ' ') ++token;
- if (strncmp(token, "+X ", 3)) {
- stbi__rewind( s );
- return 0;
- }
- token += 3;
- *x = (int) strtol(token, NULL, 10);
- *comp = 3;
- return 1;
-}
-#endif // STBI_NO_HDR
-
-#ifndef STBI_NO_BMP
-static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
-{
- void *p;
- stbi__bmp_data info;
-
- info.all_a = 255;
- p = stbi__bmp_parse_header(s, &info);
- stbi__rewind( s );
- if (p == NULL)
- return 0;
- if (x) *x = s->img_x;
- if (y) *y = s->img_y;
- if (comp) *comp = info.ma ? 4 : 3;
- return 1;
-}
-#endif
-
-#ifndef STBI_NO_PSD
-static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)
-{
- int channelCount, dummy, depth;
- if (!x) x = &dummy;
- if (!y) y = &dummy;
- if (!comp) comp = &dummy;
- if (stbi__get32be(s) != 0x38425053) {
- stbi__rewind( s );
- return 0;
- }
- if (stbi__get16be(s) != 1) {
- stbi__rewind( s );
- return 0;
- }
- stbi__skip(s, 6);
- channelCount = stbi__get16be(s);
- if (channelCount < 0 || channelCount > 16) {
- stbi__rewind( s );
- return 0;
- }
- *y = stbi__get32be(s);
- *x = stbi__get32be(s);
- depth = stbi__get16be(s);
- if (depth != 8 && depth != 16) {
- stbi__rewind( s );
- return 0;
- }
- if (stbi__get16be(s) != 3) {
- stbi__rewind( s );
- return 0;
- }
- *comp = 4;
- return 1;
-}
-
-static int stbi__psd_is16(stbi__context *s)
-{
- int channelCount, depth;
- if (stbi__get32be(s) != 0x38425053) {
- stbi__rewind( s );
- return 0;
- }
- if (stbi__get16be(s) != 1) {
- stbi__rewind( s );
- return 0;
- }
- stbi__skip(s, 6);
- channelCount = stbi__get16be(s);
- if (channelCount < 0 || channelCount > 16) {
- stbi__rewind( s );
- return 0;
- }
- (void) stbi__get32be(s);
- (void) stbi__get32be(s);
- depth = stbi__get16be(s);
- if (depth != 16) {
- stbi__rewind( s );
- return 0;
- }
- return 1;
-}
-#endif
-
-#ifndef STBI_NO_PIC
-static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
-{
- int act_comp=0,num_packets=0,chained,dummy;
- stbi__pic_packet packets[10];
-
- if (!x) x = &dummy;
- if (!y) y = &dummy;
- if (!comp) comp = &dummy;
-
- if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
- stbi__rewind(s);
- return 0;
- }
-
- stbi__skip(s, 88);
-
- *x = stbi__get16be(s);
- *y = stbi__get16be(s);
- if (stbi__at_eof(s)) {
- stbi__rewind( s);
- return 0;
- }
- if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
- stbi__rewind( s );
- return 0;
- }
-
- stbi__skip(s, 8);
-
- do {
- stbi__pic_packet *packet;
-
- if (num_packets==sizeof(packets)/sizeof(packets[0]))
- return 0;
-
- packet = &packets[num_packets++];
- chained = stbi__get8(s);
- packet->size = stbi__get8(s);
- packet->type = stbi__get8(s);
- packet->channel = stbi__get8(s);
- act_comp |= packet->channel;
-
- if (stbi__at_eof(s)) {
- stbi__rewind( s );
- return 0;
- }
- if (packet->size != 8) {
- stbi__rewind( s );
- return 0;
- }
- } while (chained);
-
- *comp = (act_comp & 0x10 ? 4 : 3);
-
- return 1;
-}
-#endif
-
-// *************************************************************************************************
-// Portable Gray Map and Portable Pixel Map loader
-// by Ken Miller
-//
-// PGM: http://netpbm.sourceforge.net/doc/pgm.html
-// PPM: http://netpbm.sourceforge.net/doc/ppm.html
-//
-// Known limitations:
-// Does not support comments in the header section
-// Does not support ASCII image data (formats P2 and P3)
-// Does not support 16-bit-per-channel
-
-#ifndef STBI_NO_PNM
-
-static int stbi__pnm_test(stbi__context *s)
-{
- char p, t;
- p = (char) stbi__get8(s);
- t = (char) stbi__get8(s);
- if (p != 'P' || (t != '5' && t != '6')) {
- stbi__rewind( s );
- return 0;
- }
- return 1;
-}
-
-static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
-{
- stbi_uc *out;
- STBI_NOTUSED(ri);
-
- if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
- return 0;
-
- *x = s->img_x;
- *y = s->img_y;
- if (comp) *comp = s->img_n;
-
- if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
- return stbi__errpuc("too large", "PNM too large");
-
- out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
- if (!out) return stbi__errpuc("outofmem", "Out of memory");
- stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
-
- if (req_comp && req_comp != s->img_n) {
- out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
- if (out == NULL) return out; // stbi__convert_format frees input on failure
- }
- return out;
-}
-
-static int stbi__pnm_isspace(char c)
-{
- return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
-}
-
-static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)
-{
- for (;;) {
- while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
- *c = (char) stbi__get8(s);
-
- if (stbi__at_eof(s) || *c != '#')
- break;
-
- while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )
- *c = (char) stbi__get8(s);
- }
-}
-
-static int stbi__pnm_isdigit(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-static int stbi__pnm_getinteger(stbi__context *s, char *c)
-{
- int value = 0;
-
- while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
- value = value*10 + (*c - '0');
- *c = (char) stbi__get8(s);
- }
-
- return value;
-}
-
-static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
-{
- int maxv, dummy;
- char c, p, t;
-
- if (!x) x = &dummy;
- if (!y) y = &dummy;
- if (!comp) comp = &dummy;
-
- stbi__rewind(s);
-
- // Get identifier
- p = (char) stbi__get8(s);
- t = (char) stbi__get8(s);
- if (p != 'P' || (t != '5' && t != '6')) {
- stbi__rewind(s);
- return 0;
- }
-
- *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
-
- c = (char) stbi__get8(s);
- stbi__pnm_skip_whitespace(s, &c);
-
- *x = stbi__pnm_getinteger(s, &c); // read width
- stbi__pnm_skip_whitespace(s, &c);
-
- *y = stbi__pnm_getinteger(s, &c); // read height
- stbi__pnm_skip_whitespace(s, &c);
-
- maxv = stbi__pnm_getinteger(s, &c); // read max value
-
- if (maxv > 255)
- return stbi__err("max value > 255", "PPM image not 8-bit");
- else
- return 1;
-}
-#endif
-
-static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
-{
- #ifndef STBI_NO_JPEG
- if (stbi__jpeg_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_PNG
- if (stbi__png_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_GIF
- if (stbi__gif_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_BMP
- if (stbi__bmp_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_PSD
- if (stbi__psd_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_PIC
- if (stbi__pic_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_PNM
- if (stbi__pnm_info(s, x, y, comp)) return 1;
- #endif
-
- #ifndef STBI_NO_HDR
- if (stbi__hdr_info(s, x, y, comp)) return 1;
- #endif
-
- // test tga last because it's a crappy test!
- #ifndef STBI_NO_TGA
- if (stbi__tga_info(s, x, y, comp))
- return 1;
- #endif
- return stbi__err("unknown image type", "Image not of any known type, or corrupt");
-}
-
-static int stbi__is_16_main(stbi__context *s)
-{
- #ifndef STBI_NO_PNG
- if (stbi__png_is16(s)) return 1;
- #endif
-
- #ifndef STBI_NO_PSD
- if (stbi__psd_is16(s)) return 1;
- #endif
-
- return 0;
-}
-
-#ifndef STBI_NO_STDIO
-STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
-{
- FILE *f = stbi__fopen(filename, "rb");
- int result;
- if (!f) return stbi__err("can't fopen", "Unable to open file");
- result = stbi_info_from_file(f, x, y, comp);
- fclose(f);
- return result;
-}
-
-STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
-{
- int r;
- stbi__context s;
- long pos = ftell(f);
- stbi__start_file(&s, f);
- r = stbi__info_main(&s,x,y,comp);
- fseek(f,pos,SEEK_SET);
- return r;
-}
-
-STBIDEF int stbi_is_16_bit(char const *filename)
-{
- FILE *f = stbi__fopen(filename, "rb");
- int result;
- if (!f) return stbi__err("can't fopen", "Unable to open file");
- result = stbi_is_16_bit_from_file(f);
- fclose(f);
- return result;
-}
-
-STBIDEF int stbi_is_16_bit_from_file(FILE *f)
-{
- int r;
- stbi__context s;
- long pos = ftell(f);
- stbi__start_file(&s, f);
- r = stbi__is_16_main(&s);
- fseek(f,pos,SEEK_SET);
- return r;
-}
-#endif // !STBI_NO_STDIO
-
-STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
-{
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__info_main(&s,x,y,comp);
-}
-
-STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
-{
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
- return stbi__info_main(&s,x,y,comp);
-}
-
-STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
-{
- stbi__context s;
- stbi__start_mem(&s,buffer,len);
- return stbi__is_16_main(&s);
-}
-
-STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)
-{
- stbi__context s;
- stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
- return stbi__is_16_main(&s);
-}
-
-#endif // STB_IMAGE_IMPLEMENTATION
-
-/*
- revision history:
- 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
- 2.19 (2018-02-11) fix warning
- 2.18 (2018-01-30) fix warnings
- 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
- 1-bit BMP
- *_is_16_bit api
- avoid warnings
- 2.16 (2017-07-23) all functions have 16-bit variants;
- STBI_NO_STDIO works again;
- compilation fixes;
- fix rounding in unpremultiply;
- optimize vertical flip;
- disable raw_len validation;
- documentation fixes
- 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
- warning fixes; disable run-time SSE detection on gcc;
- uniform handling of optional "return" values;
- thread-safe initialization of zlib tables
- 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
- 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
- 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
- 2.11 (2016-04-02) allocate large structures on the stack
- remove white matting for transparent PSD
- fix reported channel count for PNG & BMP
- re-enable SSE2 in non-gcc 64-bit
- support RGB-formatted JPEG
- read 16-bit PNGs (only as 8-bit)
- 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
- 2.09 (2016-01-16) allow comments in PNM files
- 16-bit-per-pixel TGA (not bit-per-component)
- info() for TGA could break due to .hdr handling
- info() for BMP to shares code instead of sloppy parse
- can use STBI_REALLOC_SIZED if allocator doesn't support realloc
- code cleanup
- 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
- 2.07 (2015-09-13) fix compiler warnings
- partial animated GIF support
- limited 16-bpc PSD support
- #ifdef unused functions
- bug with < 92 byte PIC,PNM,HDR,TGA
- 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
- 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
- 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
- 2.03 (2015-04-12) extra corruption checking (mmozeiko)
- stbi_set_flip_vertically_on_load (nguillemot)
- fix NEON support; fix mingw support
- 2.02 (2015-01-19) fix incorrect assert, fix warning
- 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
- 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
- 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
- progressive JPEG (stb)
- PGM/PPM support (Ken Miller)
- STBI_MALLOC,STBI_REALLOC,STBI_FREE
- GIF bugfix -- seemingly never worked
- STBI_NO_*, STBI_ONLY_*
- 1.48 (2014-12-14) fix incorrectly-named assert()
- 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
- optimize PNG (ryg)
- fix bug in interlaced PNG with user-specified channel count (stb)
- 1.46 (2014-08-26)
- fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
- 1.45 (2014-08-16)
- fix MSVC-ARM internal compiler error by wrapping malloc
- 1.44 (2014-08-07)
- various warning fixes from Ronny Chevalier
- 1.43 (2014-07-15)
- fix MSVC-only compiler problem in code changed in 1.42
- 1.42 (2014-07-09)
- don't define _CRT_SECURE_NO_WARNINGS (affects user code)
- fixes to stbi__cleanup_jpeg path
- added STBI_ASSERT to avoid requiring assert.h
- 1.41 (2014-06-25)
- fix search&replace from 1.36 that messed up comments/error messages
- 1.40 (2014-06-22)
- fix gcc struct-initialization warning
- 1.39 (2014-06-15)
- fix to TGA optimization when req_comp != number of components in TGA;
- fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
- add support for BMP version 5 (more ignored fields)
- 1.38 (2014-06-06)
- suppress MSVC warnings on integer casts truncating values
- fix accidental rename of 'skip' field of I/O
- 1.37 (2014-06-04)
- remove duplicate typedef
- 1.36 (2014-06-03)
- convert to header file single-file library
- if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
- 1.35 (2014-05-27)
- various warnings
- fix broken STBI_SIMD path
- fix bug where stbi_load_from_file no longer left file pointer in correct place
- fix broken non-easy path for 32-bit BMP (possibly never used)
- TGA optimization by Arseny Kapoulkine
- 1.34 (unknown)
- use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
- 1.33 (2011-07-14)
- make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
- 1.32 (2011-07-13)
- support for "info" function for all supported filetypes (SpartanJ)
- 1.31 (2011-06-20)
- a few more leak fixes, bug in PNG handling (SpartanJ)
- 1.30 (2011-06-11)
- added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
- removed deprecated format-specific test/load functions
- removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
- error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
- fix inefficiency in decoding 32-bit BMP (David Woo)
- 1.29 (2010-08-16)
- various warning fixes from Aurelien Pocheville
- 1.28 (2010-08-01)
- fix bug in GIF palette transparency (SpartanJ)
- 1.27 (2010-08-01)
- cast-to-stbi_uc to fix warnings
- 1.26 (2010-07-24)
- fix bug in file buffering for PNG reported by SpartanJ
- 1.25 (2010-07-17)
- refix trans_data warning (Won Chun)
- 1.24 (2010-07-12)
- perf improvements reading from files on platforms with lock-heavy fgetc()
- minor perf improvements for jpeg
- deprecated type-specific functions so we'll get feedback if they're needed
- attempt to fix trans_data warning (Won Chun)
- 1.23 fixed bug in iPhone support
- 1.22 (2010-07-10)
- removed image *writing* support
- stbi_info support from Jetro Lauha
- GIF support from Jean-Marc Lienher
- iPhone PNG-extensions from James Brown
- warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
- 1.21 fix use of 'stbi_uc' in header (reported by jon blow)
- 1.20 added support for Softimage PIC, by Tom Seddon
- 1.19 bug in interlaced PNG corruption check (found by ryg)
- 1.18 (2008-08-02)
- fix a threading bug (local mutable static)
- 1.17 support interlaced PNG
- 1.16 major bugfix - stbi__convert_format converted one too many pixels
- 1.15 initialize some fields for thread safety
- 1.14 fix threadsafe conversion bug
- header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
- 1.13 threadsafe
- 1.12 const qualifiers in the API
- 1.11 Support installable IDCT, colorspace conversion routines
- 1.10 Fixes for 64-bit (don't use "unsigned long")
- optimized upsampling by Fabian "ryg" Giesen
- 1.09 Fix format-conversion for PSD code (bad global variables!)
- 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
- 1.07 attempt to fix C++ warning/errors again
- 1.06 attempt to fix C++ warning/errors again
- 1.05 fix TGA loading to return correct *comp and use good luminance calc
- 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
- 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
- 1.02 support for (subset of) HDR files, float interface for preferred access to them
- 1.01 fix bug: possible bug in handling right-side up bmps... not sure
- fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
- 1.00 interface to zlib that skips zlib header
- 0.99 correct handling of alpha in palette
- 0.98 TGA loader by lonesock; dynamically add loaders (untested)
- 0.97 jpeg errors on too large a file; also catch another malloc failure
- 0.96 fix detection of invalid v value - particleman@mollyrocket forum
- 0.95 during header scan, seek to markers in case of padding
- 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
- 0.93 handle jpegtran output; verbose errors
- 0.92 read 4,8,16,24,32-bit BMP files of several formats
- 0.91 output 24-bit Windows 3.0 BMP files
- 0.90 fix a few more warnings; bump version number to approach 1.0
- 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
- 0.60 fix compiling as c++
- 0.59 fix warnings: merge Dave Moore's -Wall fixes
- 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
- 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
- 0.56 fix bug: zlib uncompressed mode len vs. nlen
- 0.55 fix bug: restart_interval not initialized to 0
- 0.54 allow NULL for 'int *comp'
- 0.53 fix bug in png 3->4; speedup png decoding
- 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
- 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
- on 'test' only check type, not whether we support this variant
- 0.50 (2006-11-19)
- first released version
-*/
-
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2017 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
-*/
diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h
deleted file mode 100644
index 767f005..0000000
--- a/src/external/stb_truetype.h
+++ /dev/null
@@ -1,4882 +0,0 @@
-// stb_truetype.h - v1.21 - public domain
-// authored from 2009-2016 by Sean Barrett / RAD Game Tools
-//
-// This library processes TrueType files:
-// parse files
-// extract glyph metrics
-// extract glyph shapes
-// render glyphs to one-channel bitmaps with antialiasing (box filter)
-// render glyphs to one-channel SDF bitmaps (signed-distance field/function)
-//
-// Todo:
-// non-MS cmaps
-// crashproof on bad data
-// hinting? (no longer patented)
-// cleartype-style AA?
-// optimize: use simple memory allocator for intermediates
-// optimize: build edge-list directly from curves
-// optimize: rasterize directly from curves?
-//
-// ADDITIONAL CONTRIBUTORS
-//
-// Mikko Mononen: compound shape support, more cmap formats
-// Tor Andersson: kerning, subpixel rendering
-// Dougall Johnson: OpenType / Type 2 font handling
-// Daniel Ribeiro Maciel: basic GPOS-based kerning
-//
-// Misc other:
-// Ryan Gordon
-// Simon Glass
-// github:IntellectualKitty
-// Imanol Celaya
-// Daniel Ribeiro Maciel
-//
-// Bug/warning reports/fixes:
-// "Zer" on mollyrocket Fabian "ryg" Giesen
-// Cass Everitt Martins Mozeiko
-// stoiko (Haemimont Games) Cap Petschulat
-// Brian Hook Omar Cornut
-// Walter van Niftrik github:aloucks
-// David Gow Peter LaValle
-// David Given Sergey Popov
-// Ivan-Assen Ivanov Giumo X. Clanjor
-// Anthony Pesch Higor Euripedes
-// Johan Duparc Thomas Fields
-// Hou Qiming Derek Vinyard
-// Rob Loach Cort Stratton
-// Kenney Phillis Jr. github:oyvindjam
-// Brian Costabile github:vassvik
-//
-// VERSION HISTORY
-//
-// 1.21 (2019-02-25) fix warning
-// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
-// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
-// 1.18 (2018-01-29) add missing function
-// 1.17 (2017-07-23) make more arguments const; doc fix
-// 1.16 (2017-07-12) SDF support
-// 1.15 (2017-03-03) make more arguments const
-// 1.14 (2017-01-16) num-fonts-in-TTC function
-// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
-// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
-// 1.11 (2016-04-02) fix unused-variable warning
-// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
-// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
-// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
-// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
-// variant PackFontRanges to pack and render in separate phases;
-// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
-// fixed an assert() bug in the new rasterizer
-// replace assert() with STBTT_assert() in new rasterizer
-//
-// Full history can be found at the end of this file.
-//
-// LICENSE
-//
-// See end of file for license information.
-//
-// USAGE
-//
-// Include this file in whatever places need to refer to it. In ONE C/C++
-// file, write:
-// #define STB_TRUETYPE_IMPLEMENTATION
-// before the #include of this file. This expands out the actual
-// implementation into that C/C++ file.
-//
-// To make the implementation private to the file that generates the implementation,
-// #define STBTT_STATIC
-//
-// Simple 3D API (don't ship this, but it's fine for tools and quick start)
-// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
-// stbtt_GetBakedQuad() -- compute quad to draw for a given char
-//
-// Improved 3D API (more shippable):
-// #include "stb_rect_pack.h" -- optional, but you really want it
-// stbtt_PackBegin()
-// stbtt_PackSetOversampling() -- for improved quality on small fonts
-// stbtt_PackFontRanges() -- pack and renders
-// stbtt_PackEnd()
-// stbtt_GetPackedQuad()
-//
-// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
-// stbtt_InitFont()
-// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
-// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections
-//
-// Render a unicode codepoint to a bitmap
-// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
-// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
-// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
-//
-// Character advance/positioning
-// stbtt_GetCodepointHMetrics()
-// stbtt_GetFontVMetrics()
-// stbtt_GetFontVMetricsOS2()
-// stbtt_GetCodepointKernAdvance()
-//
-// Starting with version 1.06, the rasterizer was replaced with a new,
-// faster and generally-more-precise rasterizer. The new rasterizer more
-// accurately measures pixel coverage for anti-aliasing, except in the case
-// where multiple shapes overlap, in which case it overestimates the AA pixel
-// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
-// this turns out to be a problem, you can re-enable the old rasterizer with
-// #define STBTT_RASTERIZER_VERSION 1
-// which will incur about a 15% speed hit.
-//
-// ADDITIONAL DOCUMENTATION
-//
-// Immediately after this block comment are a series of sample programs.
-//
-// After the sample programs is the "header file" section. This section
-// includes documentation for each API function.
-//
-// Some important concepts to understand to use this library:
-//
-// Codepoint
-// Characters are defined by unicode codepoints, e.g. 65 is
-// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
-// the hiragana for "ma".
-//
-// Glyph
-// A visual character shape (every codepoint is rendered as
-// some glyph)
-//
-// Glyph index
-// A font-specific integer ID representing a glyph
-//
-// Baseline
-// Glyph shapes are defined relative to a baseline, which is the
-// bottom of uppercase characters. Characters extend both above
-// and below the baseline.
-//
-// Current Point
-// As you draw text to the screen, you keep track of a "current point"
-// which is the origin of each character. The current point's vertical
-// position is the baseline. Even "baked fonts" use this model.
-//
-// Vertical Font Metrics
-// The vertical qualities of the font, used to vertically position
-// and space the characters. See docs for stbtt_GetFontVMetrics.
-//
-// Font Size in Pixels or Points
-// The preferred interface for specifying font sizes in stb_truetype
-// is to specify how tall the font's vertical extent should be in pixels.
-// If that sounds good enough, skip the next paragraph.
-//
-// Most font APIs instead use "points", which are a common typographic
-// measurement for describing font size, defined as 72 points per inch.
-// stb_truetype provides a point API for compatibility. However, true
-// "per inch" conventions don't make much sense on computer displays
-// since different monitors have different number of pixels per
-// inch. For example, Windows traditionally uses a convention that
-// there are 96 pixels per inch, thus making 'inch' measurements have
-// nothing to do with inches, and thus effectively defining a point to
-// be 1.333 pixels. Additionally, the TrueType font data provides
-// an explicit scale factor to scale a given font's glyphs to points,
-// but the author has observed that this scale factor is often wrong
-// for non-commercial fonts, thus making fonts scaled in points
-// according to the TrueType spec incoherently sized in practice.
-//
-// DETAILED USAGE:
-//
-// Scale:
-// Select how high you want the font to be, in points or pixels.
-// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
-// a scale factor SF that will be used by all other functions.
-//
-// Baseline:
-// You need to select a y-coordinate that is the baseline of where
-// your text will appear. Call GetFontBoundingBox to get the baseline-relative
-// bounding box for all characters. SF*-y0 will be the distance in pixels
-// that the worst-case character could extend above the baseline, so if
-// you want the top edge of characters to appear at the top of the
-// screen where y=0, then you would set the baseline to SF*-y0.
-//
-// Current point:
-// Set the current point where the first character will appear. The
-// first character could extend left of the current point; this is font
-// dependent. You can either choose a current point that is the leftmost
-// point and hope, or add some padding, or check the bounding box or
-// left-side-bearing of the first character to be displayed and set
-// the current point based on that.
-//
-// Displaying a character:
-// Compute the bounding box of the character. It will contain signed values
-// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
-// then the character should be displayed in the rectangle from
-// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
-//
-// Advancing for the next character:
-// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
-//
-//
-// ADVANCED USAGE
-//
-// Quality:
-//
-// - Use the functions with Subpixel at the end to allow your characters
-// to have subpixel positioning. Since the font is anti-aliased, not
-// hinted, this is very import for quality. (This is not possible with
-// baked fonts.)
-//
-// - Kerning is now supported, and if you're supporting subpixel rendering
-// then kerning is worth using to give your text a polished look.
-//
-// Performance:
-//
-// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
-// if you don't do this, stb_truetype is forced to do the conversion on
-// every call.
-//
-// - There are a lot of memory allocations. We should modify it to take
-// a temp buffer and allocate from the temp buffer (without freeing),
-// should help performance a lot.
-//
-// NOTES
-//
-// The system uses the raw data found in the .ttf file without changing it
-// and without building auxiliary data structures. This is a bit inefficient
-// on little-endian systems (the data is big-endian), but assuming you're
-// caching the bitmaps or glyph shapes this shouldn't be a big deal.
-//
-// It appears to be very hard to programmatically determine what font a
-// given file is in a general way. I provide an API for this, but I don't
-// recommend it.
-//
-//
-// PERFORMANCE MEASUREMENTS FOR 1.06:
-//
-// 32-bit 64-bit
-// Previous release: 8.83 s 7.68 s
-// Pool allocations: 7.72 s 6.34 s
-// Inline sort : 6.54 s 5.65 s
-// New rasterizer : 5.63 s 5.00 s
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-////
-//// SAMPLE PROGRAMS
-////
-//
-// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
-//
-#if 0
-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-#include "stb_truetype.h"
-
-unsigned char ttf_buffer[1<<20];
-unsigned char temp_bitmap[512*512];
-
-stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
-GLuint ftex;
-
-void my_stbtt_initfont(void)
-{
- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
- stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
- // can free ttf_buffer at this point
- glGenTextures(1, &ftex);
- glBindTexture(GL_TEXTURE_2D, ftex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
- // can free temp_bitmap at this point
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-}
-
-void my_stbtt_print(float x, float y, char *text)
-{
- // assume orthographic projection with units = screen pixels, origin at top left
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, ftex);
- glBegin(GL_QUADS);
- while (*text) {
- if (*text >= 32 && *text < 128) {
- stbtt_aligned_quad q;
- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
- }
- ++text;
- }
- glEnd();
-}
-#endif
-//
-//
-//////////////////////////////////////////////////////////////////////////////
-//
-// Complete program (this compiles): get a single bitmap, print as ASCII art
-//
-#if 0
-#include <stdio.h>
-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-#include "stb_truetype.h"
-
-char ttf_buffer[1<<25];
-
-int main(int argc, char **argv)
-{
- stbtt_fontinfo font;
- unsigned char *bitmap;
- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
-
- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
-
- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
-
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i)
- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
- putchar('\n');
- }
- return 0;
-}
-#endif
-//
-// Output:
-//
-// .ii.
-// @@@@@@.
-// V@Mio@@o
-// :i. V@V
-// :oM@@M
-// :@@@MM@M
-// @@o o@M
-// :@@. M@M
-// @@@o@@@@
-// :M@@V:@@.
-//
-//////////////////////////////////////////////////////////////////////////////
-//
-// Complete program: print "Hello World!" banner, with bugs
-//
-#if 0
-char buffer[24<<20];
-unsigned char screen[20][79];
-
-int main(int arg, char **argv)
-{
- stbtt_fontinfo font;
- int i,j,ascent,baseline,ch=0;
- float scale, xpos=2; // leave a little padding in case the character extends left
- char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
-
- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
- stbtt_InitFont(&font, buffer, 0);
-
- scale = stbtt_ScaleForPixelHeight(&font, 15);
- stbtt_GetFontVMetrics(&font, &ascent,0,0);
- baseline = (int) (ascent*scale);
-
- while (text[ch]) {
- int advance,lsb,x0,y0,x1,y1;
- float x_shift = xpos - (float) floor(xpos);
- stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
- stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
- stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
- // because this API is really for baking character bitmaps into textures. if you want to render
- // a sequence of characters, you really need to render each bitmap to a temp buffer, then
- // "alpha blend" that into the working buffer
- xpos += (advance * scale);
- if (text[ch+1])
- xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
- ++ch;
- }
-
- for (j=0; j < 20; ++j) {
- for (i=0; i < 78; ++i)
- putchar(" .:ioVM@"[screen[j][i]>>5]);
- putchar('\n');
- }
-
- return 0;
-}
-#endif
-
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-////
-//// INTEGRATION WITH YOUR CODEBASE
-////
-//// The following sections allow you to supply alternate definitions
-//// of C library functions used by stb_truetype, e.g. if you don't
-//// link with the C runtime library.
-
-#ifdef STB_TRUETYPE_IMPLEMENTATION
- // #define your own (u)stbtt_int8/16/32 before including to override this
- #ifndef stbtt_uint8
- typedef unsigned char stbtt_uint8;
- typedef signed char stbtt_int8;
- typedef unsigned short stbtt_uint16;
- typedef signed short stbtt_int16;
- typedef unsigned int stbtt_uint32;
- typedef signed int stbtt_int32;
- #endif
-
- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
-
- // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
- #ifndef STBTT_ifloor
- #include <math.h>
- #define STBTT_ifloor(x) ((int) floor(x))
- #define STBTT_iceil(x) ((int) ceil(x))
- #endif
-
- #ifndef STBTT_sqrt
- #include <math.h>
- #define STBTT_sqrt(x) sqrt(x)
- #define STBTT_pow(x,y) pow(x,y)
- #endif
-
- #ifndef STBTT_fmod
- #include <math.h>
- #define STBTT_fmod(x,y) fmod(x,y)
- #endif
-
- #ifndef STBTT_cos
- #include <math.h>
- #define STBTT_cos(x) cos(x)
- #define STBTT_acos(x) acos(x)
- #endif
-
- #ifndef STBTT_fabs
- #include <math.h>
- #define STBTT_fabs(x) fabs(x)
- #endif
-
- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
- #ifndef STBTT_malloc
- #include <stdlib.h>
- #define STBTT_malloc(x,u) ((void)(u),malloc(x))
- #define STBTT_free(x,u) ((void)(u),free(x))
- #endif
-
- #ifndef STBTT_assert
- #include <assert.h>
- #define STBTT_assert(x) assert(x)
- #endif
-
- #ifndef STBTT_strlen
- #include <string.h>
- #define STBTT_strlen(x) strlen(x)
- #endif
-
- #ifndef STBTT_memcpy
- #include <string.h>
- #define STBTT_memcpy memcpy
- #define STBTT_memset memset
- #endif
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-////
-//// INTERFACE
-////
-////
-
-#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
-#define __STB_INCLUDE_STB_TRUETYPE_H__
-
-#ifdef STBTT_STATIC
-#define STBTT_DEF static
-#else
-#define STBTT_DEF extern
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// private structure
-typedef struct
-{
- unsigned char *data;
- int cursor;
- int size;
-} stbtt__buf;
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// TEXTURE BAKING API
-//
-// If you use this API, you only have to call two functions ever.
-//
-
-typedef struct
-{
- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
- float xoff,yoff,xadvance;
-} stbtt_bakedchar;
-
-STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
- float pixel_height, // height of font in pixels
- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
- int first_char, int num_chars, // characters to bake
- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
-// if return is positive, the first unused row of the bitmap
-// if return is negative, returns the negative of the number of characters that fit
-// if return is 0, no characters fit and no rows were used
-// This uses a very crappy packing.
-
-typedef struct
-{
- float x0,y0,s0,t0; // top-left
- float x1,y1,s1,t1; // bottom-right
-} stbtt_aligned_quad;
-
-STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above
- int char_index, // character to display
- float *xpos, float *ypos, // pointers to current position in screen pixel space
- stbtt_aligned_quad *q, // output: quad to draw
- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
-// Call GetBakedQuad with char_index = 'character - first_char', and it
-// creates the quad you need to draw and advances the current position.
-//
-// The coordinate system used assumes y increases downwards.
-//
-// Characters will extend both above and below the current position;
-// see discussion of "BASELINE" above.
-//
-// It's inefficient; you might want to c&p it and optimize it.
-
-STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
-// Query the font vertical metrics without having to create a font first.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// NEW TEXTURE BAKING API
-//
-// This provides options for packing multiple fonts into one atlas, not
-// perfectly but better than nothing.
-
-typedef struct
-{
- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
- float xoff,yoff,xadvance;
- float xoff2,yoff2;
-} stbtt_packedchar;
-
-typedef struct stbtt_pack_context stbtt_pack_context;
-typedef struct stbtt_fontinfo stbtt_fontinfo;
-#ifndef STB_RECT_PACK_VERSION
-typedef struct stbrp_rect stbrp_rect;
-#endif
-
-STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
-// Initializes a packing context stored in the passed-in stbtt_pack_context.
-// Future calls using this context will pack characters into the bitmap passed
-// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
-// the distance from one row to the next (or 0 to mean they are packed tightly
-// together). "padding" is the amount of padding to leave between each
-// character (normally you want '1' for bitmaps you'll use as textures with
-// bilinear filtering).
-//
-// Returns 0 on failure, 1 on success.
-
-STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
-// Cleans up the packing context and frees all memory.
-
-#define STBTT_POINT_SIZE(x) (-(x))
-
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
- int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
-// Creates character bitmaps from the font_index'th font found in fontdata (use
-// font_index=0 if you don't know what that is). It creates num_chars_in_range
-// bitmaps for characters with unicode values starting at first_unicode_char_in_range
-// and increasing. Data for how to render them is stored in chardata_for_range;
-// pass these to stbtt_GetPackedQuad to get back renderable quads.
-//
-// font_size is the full height of the character from ascender to descender,
-// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
-// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
-// and pass that result as 'font_size':
-// ..., 20 , ... // font max minus min y is 20 pixels tall
-// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
-
-typedef struct
-{
- float font_size;
- int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
- int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
- int num_chars;
- stbtt_packedchar *chardata_for_range; // output
- unsigned char h_oversample, v_oversample; // don't set these, they're used internally
-} stbtt_pack_range;
-
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
-// Creates character bitmaps from multiple ranges of characters stored in
-// ranges. This will usually create a better-packed bitmap than multiple
-// calls to stbtt_PackFontRange. Note that you can call this multiple
-// times within a single PackBegin/PackEnd.
-
-STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
-// Oversampling a font increases the quality by allowing higher-quality subpixel
-// positioning, and is especially valuable at smaller text sizes.
-//
-// This function sets the amount of oversampling for all following calls to
-// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
-// pack context. The default (no oversampling) is achieved by h_oversample=1
-// and v_oversample=1. The total number of pixels required is
-// h_oversample*v_oversample larger than the default; for example, 2x2
-// oversampling requires 4x the storage of 1x1. For best results, render
-// oversampled textures with bilinear filtering. Look at the readme in
-// stb/tests/oversample for information about oversampled fonts
-//
-// To use with PackFontRangesGather etc., you must set it before calls
-// call to PackFontRangesGatherRects.
-
-STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
-// If skip != 0, this tells stb_truetype to skip any codepoints for which
-// there is no corresponding glyph. If skip=0, which is the default, then
-// codepoints without a glyph recived the font's "missing character" glyph,
-// typically an empty box by convention.
-
-STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
- int char_index, // character to display
- float *xpos, float *ypos, // pointers to current position in screen pixel space
- stbtt_aligned_quad *q, // output: quad to draw
- int align_to_integer);
-
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
-STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
-// Calling these functions in sequence is roughly equivalent to calling
-// stbtt_PackFontRanges(). If you more control over the packing of multiple
-// fonts, or if you want to pack custom data into a font texture, take a look
-// at the source to of stbtt_PackFontRanges() and create a custom version
-// using these functions, e.g. call GatherRects multiple times,
-// building up a single array of rects, then call PackRects once,
-// then call RenderIntoRects repeatedly. This may result in a
-// better packing than calling PackFontRanges multiple times
-// (or it may not).
-
-// this is an opaque structure that you shouldn't mess with which holds
-// all the context needed from PackBegin to PackEnd.
-struct stbtt_pack_context {
- void *user_allocator_context;
- void *pack_info;
- int width;
- int height;
- int stride_in_bytes;
- int padding;
- int skip_missing;
- unsigned int h_oversample, v_oversample;
- unsigned char *pixels;
- void *nodes;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// FONT LOADING
-//
-//
-
-STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
-// This function will determine the number of fonts in a font file. TrueType
-// collection (.ttc) files may contain multiple fonts, while TrueType font
-// (.ttf) files only contain one font. The number of fonts can be used for
-// indexing with the previous function where the index is between zero and one
-// less than the total fonts. If an error occurs, -1 is returned.
-
-STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
-// Each .ttf/.ttc file may have more than one font. Each font has a sequential
-// index number starting from 0. Call this function to get the font offset for
-// a given index; it returns -1 if the index is out of range. A regular .ttf
-// file will only define one font and it always be at offset 0, so it will
-// return '0' for index 0, and -1 for all other indices.
-
-// The following structure is defined publicly so you can declare one on
-// the stack or as a global or etc, but you should treat it as opaque.
-struct stbtt_fontinfo
-{
- void * userdata;
- unsigned char * data; // pointer to .ttf file
- int fontstart; // offset of start of font
-
- int numGlyphs; // number of glyphs, needed for range checking
-
- int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf
- int index_map; // a cmap mapping for our chosen character encoding
- int indexToLocFormat; // format needed to map from glyph index to glyph
-
- stbtt__buf cff; // cff font data
- stbtt__buf charstrings; // the charstring index
- stbtt__buf gsubrs; // global charstring subroutines index
- stbtt__buf subrs; // private charstring subroutines index
- stbtt__buf fontdicts; // array of font dicts
- stbtt__buf fdselect; // map from glyph to fontdict
-};
-
-STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
-// Given an offset into the file that defines a font, this function builds
-// the necessary cached info for the rest of the system. You must allocate
-// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
-// need to do anything special to free it, because the contents are pure
-// value data with no additional data structures. Returns 0 on failure.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// CHARACTER TO GLYPH-INDEX CONVERSIOn
-
-STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
-// If you're going to perform multiple operations on the same character
-// and you want a speed-up, call this function with the character you're
-// going to process, then use glyph-based functions instead of the
-// codepoint-based functions.
-// Returns 0 if the character codepoint is not defined in the font.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// CHARACTER PROPERTIES
-//
-
-STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
-// computes a scale factor to produce a font whose "height" is 'pixels' tall.
-// Height is measured as the distance from the highest ascender to the lowest
-// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
-// and computing:
-// scale = pixels / (ascent - descent)
-// so if you prefer to measure height by the ascent only, use a similar calculation.
-
-STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
-// computes a scale factor to produce a font whose EM size is mapped to
-// 'pixels' tall. This is probably what traditional APIs compute, but
-// I'm not positive.
-
-STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
-// ascent is the coordinate above the baseline the font extends; descent
-// is the coordinate below the baseline the font extends (i.e. it is typically negative)
-// lineGap is the spacing between one row's descent and the next row's ascent...
-// so you should advance the vertical position by "*ascent - *descent + *lineGap"
-// these are expressed in unscaled coordinates, so you must multiply by
-// the scale factor for a given size
-
-STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);
-// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
-// table (specific to MS/Windows TTF files).
-//
-// Returns 1 on success (table present), 0 on failure.
-
-STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
-// the bounding box around all possible characters
-
-STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
-// leftSideBearing is the offset from the current horizontal position to the left edge of the character
-// advanceWidth is the offset from the current horizontal position to the next horizontal position
-// these are expressed in unscaled coordinates
-
-STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
-// an additional amount to add to the 'advance' value between ch1 and ch2
-
-STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
-// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
-
-STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
-STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-// as above, but takes one or more glyph indices for greater efficiency
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// GLYPH SHAPES (you probably don't need these, but they have to go before
-// the bitmaps for C declaration-order reasons)
-//
-
-#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
- enum {
- STBTT_vmove=1,
- STBTT_vline,
- STBTT_vcurve,
- STBTT_vcubic
- };
-#endif
-
-#ifndef stbtt_vertex // you can predefine this to use different values
- // (we share this with other code at RAD)
- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
- typedef struct
- {
- stbtt_vertex_type x,y,cx,cy,cx1,cy1;
- unsigned char type,padding;
- } stbtt_vertex;
-#endif
-
-STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
-// returns non-zero if nothing is drawn for this glyph
-
-STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
-STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
-// returns # of vertices and fills *vertices with the pointer to them
-// these are expressed in "unscaled" coordinates
-//
-// The shape is a series of contours. Each one starts with
-// a STBTT_moveto, then consists of a series of mixed
-// STBTT_lineto and STBTT_curveto segments. A lineto
-// draws a line from previous endpoint to its x,y; a curveto
-// draws a quadratic bezier from previous endpoint to
-// its x,y, using cx,cy as the bezier control point.
-
-STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
-// frees the data allocated above
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// BITMAP RENDERING
-//
-
-STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
-// frees the bitmap allocated below
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-// allocates a large-enough single-channel 8bpp bitmap and renders the
-// specified character/glyph at the specified scale into it, with
-// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
-// *width & *height are filled out with the width & height of the bitmap,
-// which is stored left-to-right, top-to-bottom.
-//
-// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
-// shift for the character
-
-STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
-// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
-// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
-// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
-// width and height and positioning info for it first.
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
-// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
-// shift for the character
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
-// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
-// is performed (see stbtt_PackSetOversampling)
-
-STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-// get the bbox of the bitmap centered around the glyph origin; so the
-// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
-// the bitmap top left is (leftSideBearing*scale,iy0).
-// (Note that the bitmap uses y-increases-down, but the shape uses
-// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
-
-STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
-// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
-// shift for the character
-
-// the following functions are equivalent to the above functions, but operate
-// on glyph indices instead of Unicode codepoints (for efficiency)
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
-STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
-
-
-// @TODO: don't expose this structure
-typedef struct
-{
- int w,h,stride;
- unsigned char *pixels;
-} stbtt__bitmap;
-
-// rasterize a shape with quadratic beziers into a bitmap
-STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into
- float flatness_in_pixels, // allowable error of curve in pixels
- stbtt_vertex *vertices, // array of vertices defining shape
- int num_verts, // number of vertices in above array
- float scale_x, float scale_y, // scale applied to input vertices
- float shift_x, float shift_y, // translation applied to input vertices
- int x_off, int y_off, // another translation applied to input
- int invert, // if non-zero, vertically flip shape
- void *userdata); // context for to STBTT_MALLOC
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Signed Distance Function (or Field) rendering
-
-STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
-// frees the SDF bitmap allocated below
-
-STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
-// These functions compute a discretized SDF field for a single character, suitable for storing
-// in a single-channel texture, sampling with bilinear filtering, and testing against
-// larger than some threshold to produce scalable fonts.
-// info -- the font
-// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
-// glyph/codepoint -- the character to generate the SDF for
-// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
-// which allows effects like bit outlines
-// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
-// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
-// if positive, > onedge_value is inside; if negative, < onedge_value is inside
-// width,height -- output height & width of the SDF bitmap (including padding)
-// xoff,yoff -- output origin of the character
-// return value -- a 2D array of bytes 0..255, width*height in size
-//
-// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
-// optimal use of the limited 0..255 for your application, trading off precision
-// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
-//
-// Example:
-// scale = stbtt_ScaleForPixelHeight(22)
-// padding = 5
-// onedge_value = 180
-// pixel_dist_scale = 180/5.0 = 36.0
-//
-// This will create an SDF bitmap in which the character is about 22 pixels
-// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
-// shape, sample the SDF at each pixel and fill the pixel if the SDF value
-// is greater than or equal to 180/255. (You'll actually want to antialias,
-// which is beyond the scope of this example.) Additionally, you can compute
-// offset outlines (e.g. to stroke the character border inside & outside,
-// or only outside). For example, to fill outside the character up to 3 SDF
-// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
-// choice of variables maps a range from 5 pixels outside the shape to
-// 2 pixels inside the shape to 0..255; this is intended primarily for apply
-// outside effects only (the interior range is needed to allow proper
-// antialiasing of the font at *smaller* sizes)
-//
-// The function computes the SDF analytically at each SDF pixel, not by e.g.
-// building a higher-res bitmap and approximating it. In theory the quality
-// should be as high as possible for an SDF of this size & representation, but
-// unclear if this is true in practice (perhaps building a higher-res bitmap
-// and computing from that can allow drop-out prevention).
-//
-// The algorithm has not been optimized at all, so expect it to be slow
-// if computing lots of characters or very large sizes.
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Finding the right font...
-//
-// You should really just solve this offline, keep your own tables
-// of what font is what, and don't try to get it out of the .ttf file.
-// That's because getting it out of the .ttf file is really hard, because
-// the names in the file can appear in many possible encodings, in many
-// possible languages, and e.g. if you need a case-insensitive comparison,
-// the details of that depend on the encoding & language in a complex way
-// (actually underspecified in truetype, but also gigantic).
-//
-// But you can use the provided functions in two possible ways:
-// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
-// unicode-encoded names to try to find the font you want;
-// you can run this before calling stbtt_InitFont()
-//
-// stbtt_GetFontNameString() lets you get any of the various strings
-// from the file yourself and do your own comparisons on them.
-// You have to have called stbtt_InitFont() first.
-
-
-STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
-// returns the offset (not index) of the font that matches, or -1 if none
-// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
-// if you use any other flag, use a font name like "Arial"; this checks
-// the 'macStyle' header field; i don't know if fonts set this consistently
-#define STBTT_MACSTYLE_DONTCARE 0
-#define STBTT_MACSTYLE_BOLD 1
-#define STBTT_MACSTYLE_ITALIC 2
-#define STBTT_MACSTYLE_UNDERSCORE 4
-#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
-
-STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
-// returns 1/0 whether the first string interpreted as utf8 is identical to
-// the second string interpreted as big-endian utf16... useful for strings from next func
-
-STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
-// returns the string (which may be big-endian double byte, e.g. for unicode)
-// and puts the length in bytes in *length.
-//
-// some of the values for the IDs are below; for more see the truetype spec:
-// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
-// http://www.microsoft.com/typography/otspec/name.htm
-
-enum { // platformID
- STBTT_PLATFORM_ID_UNICODE =0,
- STBTT_PLATFORM_ID_MAC =1,
- STBTT_PLATFORM_ID_ISO =2,
- STBTT_PLATFORM_ID_MICROSOFT =3
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
- STBTT_UNICODE_EID_UNICODE_1_0 =0,
- STBTT_UNICODE_EID_UNICODE_1_1 =1,
- STBTT_UNICODE_EID_ISO_10646 =2,
- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
- STBTT_MS_EID_SYMBOL =0,
- STBTT_MS_EID_UNICODE_BMP =1,
- STBTT_MS_EID_SHIFTJIS =2,
- STBTT_MS_EID_UNICODE_FULL =10
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7
-};
-
-enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D
-};
-
-enum { // languageID for STBTT_PLATFORM_ID_MAC
- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __STB_INCLUDE_STB_TRUETYPE_H__
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-////
-//// IMPLEMENTATION
-////
-////
-
-#ifdef STB_TRUETYPE_IMPLEMENTATION
-
-#ifndef STBTT_MAX_OVERSAMPLE
-#define STBTT_MAX_OVERSAMPLE 8
-#endif
-
-#if STBTT_MAX_OVERSAMPLE > 255
-#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
-#endif
-
-typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
-
-#ifndef STBTT_RASTERIZER_VERSION
-#define STBTT_RASTERIZER_VERSION 2
-#endif
-
-#ifdef _MSC_VER
-#define STBTT__NOTUSED(v) (void)(v)
-#else
-#define STBTT__NOTUSED(v) (void)sizeof(v)
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-//
-// stbtt__buf helpers to parse data from file
-//
-
-static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
-{
- if (b->cursor >= b->size)
- return 0;
- return b->data[b->cursor++];
-}
-
-static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
-{
- if (b->cursor >= b->size)
- return 0;
- return b->data[b->cursor];
-}
-
-static void stbtt__buf_seek(stbtt__buf *b, int o)
-{
- STBTT_assert(!(o > b->size || o < 0));
- b->cursor = (o > b->size || o < 0) ? b->size : o;
-}
-
-static void stbtt__buf_skip(stbtt__buf *b, int o)
-{
- stbtt__buf_seek(b, b->cursor + o);
-}
-
-static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
-{
- stbtt_uint32 v = 0;
- int i;
- STBTT_assert(n >= 1 && n <= 4);
- for (i = 0; i < n; i++)
- v = (v << 8) | stbtt__buf_get8(b);
- return v;
-}
-
-static stbtt__buf stbtt__new_buf(const void *p, size_t size)
-{
- stbtt__buf r;
- STBTT_assert(size < 0x40000000);
- r.data = (stbtt_uint8*) p;
- r.size = (int) size;
- r.cursor = 0;
- return r;
-}
-
-#define stbtt__buf_get16(b) stbtt__buf_get((b), 2)
-#define stbtt__buf_get32(b) stbtt__buf_get((b), 4)
-
-static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
-{
- stbtt__buf r = stbtt__new_buf(NULL, 0);
- if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
- r.data = b->data + o;
- r.size = s;
- return r;
-}
-
-static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
-{
- int count, start, offsize;
- start = b->cursor;
- count = stbtt__buf_get16(b);
- if (count) {
- offsize = stbtt__buf_get8(b);
- STBTT_assert(offsize >= 1 && offsize <= 4);
- stbtt__buf_skip(b, offsize * count);
- stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
- }
- return stbtt__buf_range(b, start, b->cursor - start);
-}
-
-static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
-{
- int b0 = stbtt__buf_get8(b);
- if (b0 >= 32 && b0 <= 246) return b0 - 139;
- else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
- else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
- else if (b0 == 28) return stbtt__buf_get16(b);
- else if (b0 == 29) return stbtt__buf_get32(b);
- STBTT_assert(0);
- return 0;
-}
-
-static void stbtt__cff_skip_operand(stbtt__buf *b) {
- int v, b0 = stbtt__buf_peek8(b);
- STBTT_assert(b0 >= 28);
- if (b0 == 30) {
- stbtt__buf_skip(b, 1);
- while (b->cursor < b->size) {
- v = stbtt__buf_get8(b);
- if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
- break;
- }
- } else {
- stbtt__cff_int(b);
- }
-}
-
-static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
-{
- stbtt__buf_seek(b, 0);
- while (b->cursor < b->size) {
- int start = b->cursor, end, op;
- while (stbtt__buf_peek8(b) >= 28)
- stbtt__cff_skip_operand(b);
- end = b->cursor;
- op = stbtt__buf_get8(b);
- if (op == 12) op = stbtt__buf_get8(b) | 0x100;
- if (op == key) return stbtt__buf_range(b, start, end-start);
- }
- return stbtt__buf_range(b, 0, 0);
-}
-
-static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
-{
- int i;
- stbtt__buf operands = stbtt__dict_get(b, key);
- for (i = 0; i < outcount && operands.cursor < operands.size; i++)
- out[i] = stbtt__cff_int(&operands);
-}
-
-static int stbtt__cff_index_count(stbtt__buf *b)
-{
- stbtt__buf_seek(b, 0);
- return stbtt__buf_get16(b);
-}
-
-static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
-{
- int count, offsize, start, end;
- stbtt__buf_seek(&b, 0);
- count = stbtt__buf_get16(&b);
- offsize = stbtt__buf_get8(&b);
- STBTT_assert(i >= 0 && i < count);
- STBTT_assert(offsize >= 1 && offsize <= 4);
- stbtt__buf_skip(&b, i*offsize);
- start = stbtt__buf_get(&b, offsize);
- end = stbtt__buf_get(&b, offsize);
- return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
-}
-
-//////////////////////////////////////////////////////////////////////////
-//
-// accessors to parse data from file
-//
-
-// on platforms that don't allow misaligned reads, if we want to allow
-// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
-
-#define ttBYTE(p) (* (stbtt_uint8 *) (p))
-#define ttCHAR(p) (* (stbtt_int8 *) (p))
-#define ttFixed(p) ttLONG(p)
-
-static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-
-#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
-#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
-
-static int stbtt__isfont(stbtt_uint8 *font)
-{
- // check the version number
- if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
- if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts
- return 0;
-}
-
-// @OPTIMIZE: binary search
-static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
-{
- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
- stbtt_uint32 tabledir = fontstart + 12;
- stbtt_int32 i;
- for (i=0; i < num_tables; ++i) {
- stbtt_uint32 loc = tabledir + 16*i;
- if (stbtt_tag(data+loc+0, tag))
- return ttULONG(data+loc+8);
- }
- return 0;
-}
-
-static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
-{
- // if it's just a font, there's only one valid index
- if (stbtt__isfont(font_collection))
- return index == 0 ? 0 : -1;
-
- // check if it's a TTC
- if (stbtt_tag(font_collection, "ttcf")) {
- // version 1?
- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
- stbtt_int32 n = ttLONG(font_collection+8);
- if (index >= n)
- return -1;
- return ttULONG(font_collection+12+index*4);
- }
- }
- return -1;
-}
-
-static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
-{
- // if it's just a font, there's only one valid font
- if (stbtt__isfont(font_collection))
- return 1;
-
- // check if it's a TTC
- if (stbtt_tag(font_collection, "ttcf")) {
- // version 1?
- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
- return ttLONG(font_collection+8);
- }
- }
- return 0;
-}
-
-static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
-{
- stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
- stbtt__buf pdict;
- stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
- if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
- pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
- stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
- if (!subrsoff) return stbtt__new_buf(NULL, 0);
- stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
- return stbtt__cff_get_index(&cff);
-}
-
-static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
-{
- stbtt_uint32 cmap, t;
- stbtt_int32 i,numTables;
-
- info->data = data;
- info->fontstart = fontstart;
- info->cff = stbtt__new_buf(NULL, 0);
-
- cmap = stbtt__find_table(data, fontstart, "cmap"); // required
- info->loca = stbtt__find_table(data, fontstart, "loca"); // required
- info->head = stbtt__find_table(data, fontstart, "head"); // required
- info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
- info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
- info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
- info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
- info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
-
- if (!cmap || !info->head || !info->hhea || !info->hmtx)
- return 0;
- if (info->glyf) {
- // required for truetype
- if (!info->loca) return 0;
- } else {
- // initialization for CFF / Type2 fonts (OTF)
- stbtt__buf b, topdict, topdictidx;
- stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
- stbtt_uint32 cff;
-
- cff = stbtt__find_table(data, fontstart, "CFF ");
- if (!cff) return 0;
-
- info->fontdicts = stbtt__new_buf(NULL, 0);
- info->fdselect = stbtt__new_buf(NULL, 0);
-
- // @TODO this should use size from table (not 512MB)
- info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
- b = info->cff;
-
- // read the header
- stbtt__buf_skip(&b, 2);
- stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
-
- // @TODO the name INDEX could list multiple fonts,
- // but we just use the first one.
- stbtt__cff_get_index(&b); // name INDEX
- topdictidx = stbtt__cff_get_index(&b);
- topdict = stbtt__cff_index_get(topdictidx, 0);
- stbtt__cff_get_index(&b); // string INDEX
- info->gsubrs = stbtt__cff_get_index(&b);
-
- stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
- stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
- stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
- stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
- info->subrs = stbtt__get_subrs(b, topdict);
-
- // we only support Type 2 charstrings
- if (cstype != 2) return 0;
- if (charstrings == 0) return 0;
-
- if (fdarrayoff) {
- // looks like a CID font
- if (!fdselectoff) return 0;
- stbtt__buf_seek(&b, fdarrayoff);
- info->fontdicts = stbtt__cff_get_index(&b);
- info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
- }
-
- stbtt__buf_seek(&b, charstrings);
- info->charstrings = stbtt__cff_get_index(&b);
- }
-
- t = stbtt__find_table(data, fontstart, "maxp");
- if (t)
- info->numGlyphs = ttUSHORT(data+t+4);
- else
- info->numGlyphs = 0xffff;
-
- // find a cmap encoding table we understand *now* to avoid searching
- // later. (todo: could make this installable)
- // the same regardless of glyph.
- numTables = ttUSHORT(data + cmap + 2);
- info->index_map = 0;
- for (i=0; i < numTables; ++i) {
- stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
- // find an encoding we understand:
- switch(ttUSHORT(data+encoding_record)) {
- case STBTT_PLATFORM_ID_MICROSOFT:
- switch (ttUSHORT(data+encoding_record+2)) {
- case STBTT_MS_EID_UNICODE_BMP:
- case STBTT_MS_EID_UNICODE_FULL:
- // MS/Unicode
- info->index_map = cmap + ttULONG(data+encoding_record+4);
- break;
- }
- break;
- case STBTT_PLATFORM_ID_UNICODE:
- // Mac/iOS has these
- // all the encodingIDs are unicode, so we don't bother to check it
- info->index_map = cmap + ttULONG(data+encoding_record+4);
- break;
- }
- }
- if (info->index_map == 0)
- return 0;
-
- info->indexToLocFormat = ttUSHORT(data+info->head + 50);
- return 1;
-}
-
-STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
-{
- stbtt_uint8 *data = info->data;
- stbtt_uint32 index_map = info->index_map;
-
- stbtt_uint16 format = ttUSHORT(data + index_map + 0);
- if (format == 0) { // apple byte encoding
- stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
- if (unicode_codepoint < bytes-6)
- return ttBYTE(data + index_map + 6 + unicode_codepoint);
- return 0;
- } else if (format == 6) {
- stbtt_uint32 first = ttUSHORT(data + index_map + 6);
- stbtt_uint32 count = ttUSHORT(data + index_map + 8);
- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
- return 0;
- } else if (format == 2) {
- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
- return 0;
- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-
- // do a binary search of the segments
- stbtt_uint32 endCount = index_map + 14;
- stbtt_uint32 search = endCount;
-
- if (unicode_codepoint > 0xffff)
- return 0;
-
- // they lie from endCount .. endCount + segCount
- // but searchRange is the nearest power of two, so...
- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
- search += rangeShift*2;
-
- // now decrement to bias correctly to find smallest
- search -= 2;
- while (entrySelector) {
- stbtt_uint16 end;
- searchRange >>= 1;
- end = ttUSHORT(data + search + searchRange*2);
- if (unicode_codepoint > end)
- search += searchRange*2;
- --entrySelector;
- }
- search += 2;
-
- {
- stbtt_uint16 offset, start;
- stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
-
- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
- if (unicode_codepoint < start)
- return 0;
-
- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
- if (offset == 0)
- return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
-
- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
- }
- } else if (format == 12 || format == 13) {
- stbtt_uint32 ngroups = ttULONG(data+index_map+12);
- stbtt_int32 low,high;
- low = 0; high = (stbtt_int32)ngroups;
- // Binary search the right group.
- while (low < high) {
- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
- if ((stbtt_uint32) unicode_codepoint < start_char)
- high = mid;
- else if ((stbtt_uint32) unicode_codepoint > end_char)
- low = mid+1;
- else {
- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
- if (format == 12)
- return start_glyph + unicode_codepoint-start_char;
- else // format == 13
- return start_glyph;
- }
- }
- return 0; // not found
- }
- // @TODO
- STBTT_assert(0);
- return 0;
-}
-
-STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
-{
- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
-}
-
-static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
-{
- v->type = type;
- v->x = (stbtt_int16) x;
- v->y = (stbtt_int16) y;
- v->cx = (stbtt_int16) cx;
- v->cy = (stbtt_int16) cy;
-}
-
-static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
-{
- int g1,g2;
-
- STBTT_assert(!info->cff.size);
-
- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
-
- if (info->indexToLocFormat == 0) {
- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
- } else {
- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
- }
-
- return g1==g2 ? -1 : g1; // if length is 0, return -1
-}
-
-static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-
-STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-{
- if (info->cff.size) {
- stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
- } else {
- int g = stbtt__GetGlyfOffset(info, glyph_index);
- if (g < 0) return 0;
-
- if (x0) *x0 = ttSHORT(info->data + g + 2);
- if (y0) *y0 = ttSHORT(info->data + g + 4);
- if (x1) *x1 = ttSHORT(info->data + g + 6);
- if (y1) *y1 = ttSHORT(info->data + g + 8);
- }
- return 1;
-}
-
-STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
-{
- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
-}
-
-STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
-{
- stbtt_int16 numberOfContours;
- int g;
- if (info->cff.size)
- return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
- g = stbtt__GetGlyfOffset(info, glyph_index);
- if (g < 0) return 1;
- numberOfContours = ttSHORT(info->data + g);
- return numberOfContours == 0;
-}
-
-static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
- stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
-{
- if (start_off) {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
- } else {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
- else
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
- }
- return num_vertices;
-}
-
-static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- stbtt_int16 numberOfContours;
- stbtt_uint8 *endPtsOfContours;
- stbtt_uint8 *data = info->data;
- stbtt_vertex *vertices=0;
- int num_vertices=0;
- int g = stbtt__GetGlyfOffset(info, glyph_index);
-
- *pvertices = NULL;
-
- if (g < 0) return 0;
-
- numberOfContours = ttSHORT(data + g);
-
- if (numberOfContours > 0) {
- stbtt_uint8 flags=0,flagcount;
- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
- stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
- stbtt_uint8 *points;
- endPtsOfContours = (data + g + 10);
- ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
- points = data + g + 10 + numberOfContours * 2 + 2 + ins;
-
- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
-
- m = n + 2*numberOfContours; // a loose bound on how many vertices we might need
- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
- if (vertices == 0)
- return 0;
-
- next_move = 0;
- flagcount=0;
-
- // in first pass, we load uninterpreted data into the allocated array
- // above, shifted to the end of the array so we won't overwrite it when
- // we create our final data starting from the front
-
- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
-
- // first load flags
-
- for (i=0; i < n; ++i) {
- if (flagcount == 0) {
- flags = *points++;
- if (flags & 8)
- flagcount = *points++;
- } else
- --flagcount;
- vertices[off+i].type = flags;
- }
-
- // now load x coordinates
- x=0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- if (flags & 2) {
- stbtt_int16 dx = *points++;
- x += (flags & 16) ? dx : -dx; // ???
- } else {
- if (!(flags & 16)) {
- x = x + (stbtt_int16) (points[0]*256 + points[1]);
- points += 2;
- }
- }
- vertices[off+i].x = (stbtt_int16) x;
- }
-
- // now load y coordinates
- y=0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- if (flags & 4) {
- stbtt_int16 dy = *points++;
- y += (flags & 32) ? dy : -dy; // ???
- } else {
- if (!(flags & 32)) {
- y = y + (stbtt_int16) (points[0]*256 + points[1]);
- points += 2;
- }
- }
- vertices[off+i].y = (stbtt_int16) y;
- }
-
- // now convert them to our format
- num_vertices=0;
- sx = sy = cx = cy = scx = scy = 0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- x = (stbtt_int16) vertices[off+i].x;
- y = (stbtt_int16) vertices[off+i].y;
-
- if (next_move == i) {
- if (i != 0)
- num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
-
- // now start the new one
- start_off = !(flags & 1);
- if (start_off) {
- // if we start off with an off-curve point, then when we need to find a point on the curve
- // where we can start, and we need to save some state for when we wraparound.
- scx = x;
- scy = y;
- if (!(vertices[off+i+1].type & 1)) {
- // next point is also a curve point, so interpolate an on-point curve
- sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
- sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
- } else {
- // otherwise just use the next point as our start point
- sx = (stbtt_int32) vertices[off+i+1].x;
- sy = (stbtt_int32) vertices[off+i+1].y;
- ++i; // we're using point i+1 as the starting point, so skip it
- }
- } else {
- sx = x;
- sy = y;
- }
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
- was_off = 0;
- next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
- ++j;
- } else {
- if (!(flags & 1)) { // if it's a curve
- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
- cx = x;
- cy = y;
- was_off = 1;
- } else {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
- else
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
- was_off = 0;
- }
- }
- }
- num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- } else if (numberOfContours == -1) {
- // Compound shapes.
- int more = 1;
- stbtt_uint8 *comp = data + g + 10;
- num_vertices = 0;
- vertices = 0;
- while (more) {
- stbtt_uint16 flags, gidx;
- int comp_num_verts = 0, i;
- stbtt_vertex *comp_verts = 0, *tmp = 0;
- float mtx[6] = {1,0,0,1,0,0}, m, n;
-
- flags = ttSHORT(comp); comp+=2;
- gidx = ttSHORT(comp); comp+=2;
-
- if (flags & 2) { // XY values
- if (flags & 1) { // shorts
- mtx[4] = ttSHORT(comp); comp+=2;
- mtx[5] = ttSHORT(comp); comp+=2;
- } else {
- mtx[4] = ttCHAR(comp); comp+=1;
- mtx[5] = ttCHAR(comp); comp+=1;
- }
- }
- else {
- // @TODO handle matching point
- STBTT_assert(0);
- }
- if (flags & (1<<3)) { // WE_HAVE_A_SCALE
- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = mtx[2] = 0;
- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = mtx[2] = 0;
- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- }
-
- // Find transformation scales.
- m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
- n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
-
- // Get indexed glyph.
- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
- if (comp_num_verts > 0) {
- // Transform vertices.
- for (i = 0; i < comp_num_verts; ++i) {
- stbtt_vertex* v = &comp_verts[i];
- stbtt_vertex_type x,y;
- x=v->x; y=v->y;
- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
- x=v->cx; y=v->cy;
- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
- }
- // Append vertices.
- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
- if (!tmp) {
- if (vertices) STBTT_free(vertices, info->userdata);
- if (comp_verts) STBTT_free(comp_verts, info->userdata);
- return 0;
- }
- if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
- STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
- if (vertices) STBTT_free(vertices, info->userdata);
- vertices = tmp;
- STBTT_free(comp_verts, info->userdata);
- num_vertices += comp_num_verts;
- }
- // More components ?
- more = flags & (1<<5);
- }
- } else if (numberOfContours < 0) {
- // @TODO other compound variations?
- STBTT_assert(0);
- } else {
- // numberOfCounters == 0, do nothing
- }
-
- *pvertices = vertices;
- return num_vertices;
-}
-
-typedef struct
-{
- int bounds;
- int started;
- float first_x, first_y;
- float x, y;
- stbtt_int32 min_x, max_x, min_y, max_y;
-
- stbtt_vertex *pvertices;
- int num_vertices;
-} stbtt__csctx;
-
-#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
-
-static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
-{
- if (x > c->max_x || !c->started) c->max_x = x;
- if (y > c->max_y || !c->started) c->max_y = y;
- if (x < c->min_x || !c->started) c->min_x = x;
- if (y < c->min_y || !c->started) c->min_y = y;
- c->started = 1;
-}
-
-static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
-{
- if (c->bounds) {
- stbtt__track_vertex(c, x, y);
- if (type == STBTT_vcubic) {
- stbtt__track_vertex(c, cx, cy);
- stbtt__track_vertex(c, cx1, cy1);
- }
- } else {
- stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
- c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
- c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
- }
- c->num_vertices++;
-}
-
-static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
-{
- if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
- stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
-{
- stbtt__csctx_close_shape(ctx);
- ctx->first_x = ctx->x = ctx->x + dx;
- ctx->first_y = ctx->y = ctx->y + dy;
- stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
-{
- ctx->x += dx;
- ctx->y += dy;
- stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
-{
- float cx1 = ctx->x + dx1;
- float cy1 = ctx->y + dy1;
- float cx2 = cx1 + dx2;
- float cy2 = cy1 + dy2;
- ctx->x = cx2 + dx3;
- ctx->y = cy2 + dy3;
- stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
-}
-
-static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
-{
- int count = stbtt__cff_index_count(&idx);
- int bias = 107;
- if (count >= 33900)
- bias = 32768;
- else if (count >= 1240)
- bias = 1131;
- n += bias;
- if (n < 0 || n >= count)
- return stbtt__new_buf(NULL, 0);
- return stbtt__cff_index_get(idx, n);
-}
-
-static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
-{
- stbtt__buf fdselect = info->fdselect;
- int nranges, start, end, v, fmt, fdselector = -1, i;
-
- stbtt__buf_seek(&fdselect, 0);
- fmt = stbtt__buf_get8(&fdselect);
- if (fmt == 0) {
- // untested
- stbtt__buf_skip(&fdselect, glyph_index);
- fdselector = stbtt__buf_get8(&fdselect);
- } else if (fmt == 3) {
- nranges = stbtt__buf_get16(&fdselect);
- start = stbtt__buf_get16(&fdselect);
- for (i = 0; i < nranges; i++) {
- v = stbtt__buf_get8(&fdselect);
- end = stbtt__buf_get16(&fdselect);
- if (glyph_index >= start && glyph_index < end) {
- fdselector = v;
- break;
- }
- start = end;
- }
- }
- if (fdselector == -1) stbtt__new_buf(NULL, 0);
- return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
-}
-
-static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
-{
- int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
- int has_subrs = 0, clear_stack;
- float s[48];
- stbtt__buf subr_stack[10], subrs = info->subrs, b;
- float f;
-
-#define STBTT__CSERR(s) (0)
-
- // this currently ignores the initial width value, which isn't needed if we have hmtx
- b = stbtt__cff_index_get(info->charstrings, glyph_index);
- while (b.cursor < b.size) {
- i = 0;
- clear_stack = 1;
- b0 = stbtt__buf_get8(&b);
- switch (b0) {
- // @TODO implement hinting
- case 0x13: // hintmask
- case 0x14: // cntrmask
- if (in_header)
- maskbits += (sp / 2); // implicit "vstem"
- in_header = 0;
- stbtt__buf_skip(&b, (maskbits + 7) / 8);
- break;
-
- case 0x01: // hstem
- case 0x03: // vstem
- case 0x12: // hstemhm
- case 0x17: // vstemhm
- maskbits += (sp / 2);
- break;
-
- case 0x15: // rmoveto
- in_header = 0;
- if (sp < 2) return STBTT__CSERR("rmoveto stack");
- stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
- break;
- case 0x04: // vmoveto
- in_header = 0;
- if (sp < 1) return STBTT__CSERR("vmoveto stack");
- stbtt__csctx_rmove_to(c, 0, s[sp-1]);
- break;
- case 0x16: // hmoveto
- in_header = 0;
- if (sp < 1) return STBTT__CSERR("hmoveto stack");
- stbtt__csctx_rmove_to(c, s[sp-1], 0);
- break;
-
- case 0x05: // rlineto
- if (sp < 2) return STBTT__CSERR("rlineto stack");
- for (; i + 1 < sp; i += 2)
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- break;
-
- // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
- // starting from a different place.
-
- case 0x07: // vlineto
- if (sp < 1) return STBTT__CSERR("vlineto stack");
- goto vlineto;
- case 0x06: // hlineto
- if (sp < 1) return STBTT__CSERR("hlineto stack");
- for (;;) {
- if (i >= sp) break;
- stbtt__csctx_rline_to(c, s[i], 0);
- i++;
- vlineto:
- if (i >= sp) break;
- stbtt__csctx_rline_to(c, 0, s[i]);
- i++;
- }
- break;
-
- case 0x1F: // hvcurveto
- if (sp < 4) return STBTT__CSERR("hvcurveto stack");
- goto hvcurveto;
- case 0x1E: // vhcurveto
- if (sp < 4) return STBTT__CSERR("vhcurveto stack");
- for (;;) {
- if (i + 3 >= sp) break;
- stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
- i += 4;
- hvcurveto:
- if (i + 3 >= sp) break;
- stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
- i += 4;
- }
- break;
-
- case 0x08: // rrcurveto
- if (sp < 6) return STBTT__CSERR("rcurveline stack");
- for (; i + 5 < sp; i += 6)
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- break;
-
- case 0x18: // rcurveline
- if (sp < 8) return STBTT__CSERR("rcurveline stack");
- for (; i + 5 < sp - 2; i += 6)
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- break;
-
- case 0x19: // rlinecurve
- if (sp < 8) return STBTT__CSERR("rlinecurve stack");
- for (; i + 1 < sp - 6; i += 2)
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- break;
-
- case 0x1A: // vvcurveto
- case 0x1B: // hhcurveto
- if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
- f = 0.0;
- if (sp & 1) { f = s[i]; i++; }
- for (; i + 3 < sp; i += 4) {
- if (b0 == 0x1B)
- stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
- else
- stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
- f = 0.0;
- }
- break;
-
- case 0x0A: // callsubr
- if (!has_subrs) {
- if (info->fdselect.size)
- subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
- has_subrs = 1;
- }
- // fallthrough
- case 0x1D: // callgsubr
- if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
- v = (int) s[--sp];
- if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
- subr_stack[subr_stack_height++] = b;
- b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
- if (b.size == 0) return STBTT__CSERR("subr not found");
- b.cursor = 0;
- clear_stack = 0;
- break;
-
- case 0x0B: // return
- if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
- b = subr_stack[--subr_stack_height];
- clear_stack = 0;
- break;
-
- case 0x0E: // endchar
- stbtt__csctx_close_shape(c);
- return 1;
-
- case 0x0C: { // two-byte escape
- float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
- float dx, dy;
- int b1 = stbtt__buf_get8(&b);
- switch (b1) {
- // @TODO These "flex" implementations ignore the flex-depth and resolution,
- // and always draw beziers.
- case 0x22: // hflex
- if (sp < 7) return STBTT__CSERR("hflex stack");
- dx1 = s[0];
- dx2 = s[1];
- dy2 = s[2];
- dx3 = s[3];
- dx4 = s[4];
- dx5 = s[5];
- dx6 = s[6];
- stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
- stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
- break;
-
- case 0x23: // flex
- if (sp < 13) return STBTT__CSERR("flex stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dy3 = s[5];
- dx4 = s[6];
- dy4 = s[7];
- dx5 = s[8];
- dy5 = s[9];
- dx6 = s[10];
- dy6 = s[11];
- //fd is s[12]
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
- stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
- break;
-
- case 0x24: // hflex1
- if (sp < 9) return STBTT__CSERR("hflex1 stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dx4 = s[5];
- dx5 = s[6];
- dy5 = s[7];
- dx6 = s[8];
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
- stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
- break;
-
- case 0x25: // flex1
- if (sp < 11) return STBTT__CSERR("flex1 stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dy3 = s[5];
- dx4 = s[6];
- dy4 = s[7];
- dx5 = s[8];
- dy5 = s[9];
- dx6 = dy6 = s[10];
- dx = dx1+dx2+dx3+dx4+dx5;
- dy = dy1+dy2+dy3+dy4+dy5;
- if (STBTT_fabs(dx) > STBTT_fabs(dy))
- dy6 = -dy;
- else
- dx6 = -dx;
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
- stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
- break;
-
- default:
- return STBTT__CSERR("unimplemented");
- }
- } break;
-
- default:
- if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
- return STBTT__CSERR("reserved operator");
-
- // push immediate
- if (b0 == 255) {
- f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
- } else {
- stbtt__buf_skip(&b, -1);
- f = (float)(stbtt_int16)stbtt__cff_int(&b);
- }
- if (sp >= 48) return STBTT__CSERR("push stack overflow");
- s[sp++] = f;
- clear_stack = 0;
- break;
- }
- if (clear_stack) sp = 0;
- }
- return STBTT__CSERR("no endchar");
-
-#undef STBTT__CSERR
-}
-
-static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- // runs the charstring twice, once to count and once to output (to avoid realloc)
- stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
- stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
- if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
- *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
- output_ctx.pvertices = *pvertices;
- if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
- STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
- return output_ctx.num_vertices;
- }
- }
- *pvertices = NULL;
- return 0;
-}
-
-static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-{
- stbtt__csctx c = STBTT__CSCTX_INIT(1);
- int r = stbtt__run_charstring(info, glyph_index, &c);
- if (x0) *x0 = r ? c.min_x : 0;
- if (y0) *y0 = r ? c.min_y : 0;
- if (x1) *x1 = r ? c.max_x : 0;
- if (y1) *y1 = r ? c.max_y : 0;
- return r ? c.num_vertices : 0;
-}
-
-STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- if (!info->cff.size)
- return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
- else
- return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
-}
-
-STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
-{
- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
- if (glyph_index < numOfLongHorMetrics) {
- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
- } else {
- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
- }
-}
-
-static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
-{
- stbtt_uint8 *data = info->data + info->kern;
- stbtt_uint32 needle, straw;
- int l, r, m;
-
- // we only look at the first table. it must be 'horizontal' and format 0.
- if (!info->kern)
- return 0;
- if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
- return 0;
- if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
- return 0;
-
- l = 0;
- r = ttUSHORT(data+10) - 1;
- needle = glyph1 << 16 | glyph2;
- while (l <= r) {
- m = (l + r) >> 1;
- straw = ttULONG(data+18+(m*6)); // note: unaligned read
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else
- return ttSHORT(data+22+(m*6));
- }
- return 0;
-}
-
-static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
-{
- stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
- switch(coverageFormat) {
- case 1: {
- stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
-
- // Binary search.
- stbtt_int32 l=0, r=glyphCount-1, m;
- int straw, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *glyphArray = coverageTable + 4;
- stbtt_uint16 glyphID;
- m = (l + r) >> 1;
- glyphID = ttUSHORT(glyphArray + 2 * m);
- straw = glyphID;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- return m;
- }
- }
- } break;
-
- case 2: {
- stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
- stbtt_uint8 *rangeArray = coverageTable + 4;
-
- // Binary search.
- stbtt_int32 l=0, r=rangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *rangeRecord;
- m = (l + r) >> 1;
- rangeRecord = rangeArray + 6 * m;
- strawStart = ttUSHORT(rangeRecord);
- strawEnd = ttUSHORT(rangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else {
- stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
- return startCoverageIndex + glyph - strawStart;
- }
- }
- } break;
-
- default: {
- // There are no other cases.
- STBTT_assert(0);
- } break;
- }
-
- return -1;
-}
-
-static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
-{
- stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
- switch(classDefFormat)
- {
- case 1: {
- stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
- stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
- stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
-
- if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
- return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
-
- classDefTable = classDef1ValueArray + 2 * glyphCount;
- } break;
-
- case 2: {
- stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
- stbtt_uint8 *classRangeRecords = classDefTable + 4;
-
- // Binary search.
- stbtt_int32 l=0, r=classRangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *classRangeRecord;
- m = (l + r) >> 1;
- classRangeRecord = classRangeRecords + 6 * m;
- strawStart = ttUSHORT(classRangeRecord);
- strawEnd = ttUSHORT(classRangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else
- return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
- }
-
- classDefTable = classRangeRecords + 6 * classRangeCount;
- } break;
-
- default: {
- // There are no other cases.
- STBTT_assert(0);
- } break;
- }
-
- return -1;
-}
-
-// Define to STBTT_assert(x) if you want to break on unimplemented formats.
-#define STBTT_GPOS_TODO_assert(x)
-
-static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
-{
- stbtt_uint16 lookupListOffset;
- stbtt_uint8 *lookupList;
- stbtt_uint16 lookupCount;
- stbtt_uint8 *data;
- stbtt_int32 i;
-
- if (!info->gpos) return 0;
-
- data = info->data + info->gpos;
-
- if (ttUSHORT(data+0) != 1) return 0; // Major version 1
- if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
-
- lookupListOffset = ttUSHORT(data+8);
- lookupList = data + lookupListOffset;
- lookupCount = ttUSHORT(lookupList);
-
- for (i=0; i<lookupCount; ++i) {
- stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
- stbtt_uint8 *lookupTable = lookupList + lookupOffset;
-
- stbtt_uint16 lookupType = ttUSHORT(lookupTable);
- stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
- stbtt_uint8 *subTableOffsets = lookupTable + 6;
- switch(lookupType) {
- case 2: { // Pair Adjustment Positioning Subtable
- stbtt_int32 sti;
- for (sti=0; sti<subTableCount; sti++) {
- stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
- stbtt_uint8 *table = lookupTable + subtableOffset;
- stbtt_uint16 posFormat = ttUSHORT(table);
- stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
- stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
- if (coverageIndex == -1) continue;
-
- switch (posFormat) {
- case 1: {
- stbtt_int32 l, r, m;
- int straw, needle;
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
- stbtt_int32 valueRecordPairSizeInBytes = 2;
- stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
- stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
- stbtt_uint8 *pairValueTable = table + pairPosOffset;
- stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
- stbtt_uint8 *pairValueArray = pairValueTable + 2;
- // TODO: Support more formats.
- STBTT_GPOS_TODO_assert(valueFormat1 == 4);
- if (valueFormat1 != 4) return 0;
- STBTT_GPOS_TODO_assert(valueFormat2 == 0);
- if (valueFormat2 != 0) return 0;
-
- STBTT_assert(coverageIndex < pairSetCount);
- STBTT__NOTUSED(pairSetCount);
-
- needle=glyph2;
- r=pairValueCount-1;
- l=0;
-
- // Binary search.
- while (l <= r) {
- stbtt_uint16 secondGlyph;
- stbtt_uint8 *pairValue;
- m = (l + r) >> 1;
- pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
- secondGlyph = ttUSHORT(pairValue);
- straw = secondGlyph;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
- return xAdvance;
- }
- }
- } break;
-
- case 2: {
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
-
- stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
- stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
- int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
- int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
-
- stbtt_uint16 class1Count = ttUSHORT(table + 12);
- stbtt_uint16 class2Count = ttUSHORT(table + 14);
- STBTT_assert(glyph1class < class1Count);
- STBTT_assert(glyph2class < class2Count);
-
- // TODO: Support more formats.
- STBTT_GPOS_TODO_assert(valueFormat1 == 4);
- if (valueFormat1 != 4) return 0;
- STBTT_GPOS_TODO_assert(valueFormat2 == 0);
- if (valueFormat2 != 0) return 0;
-
- if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
- stbtt_uint8 *class1Records = table + 16;
- stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
- stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
- return xAdvance;
- }
- } break;
-
- default: {
- // There are no other cases.
- STBTT_assert(0);
- break;
- };
- }
- }
- break;
- };
-
- default:
- // TODO: Implement other stuff.
- break;
- }
- }
-
- return 0;
-}
-
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
-{
- int xAdvance = 0;
-
- if (info->gpos)
- xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
-
- if (info->kern)
- xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
-
- return xAdvance;
-}
-
-STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
-{
- if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
- return 0;
- return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
-}
-
-STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
-{
- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
-}
-
-STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
-{
- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
- if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
-}
-
-STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)
-{
- int tab = stbtt__find_table(info->data, info->fontstart, "OS/2");
- if (!tab)
- return 0;
- if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68);
- if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);
- if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);
- return 1;
-}
-
-STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
-{
- *x0 = ttSHORT(info->data + info->head + 36);
- *y0 = ttSHORT(info->data + info->head + 38);
- *x1 = ttSHORT(info->data + info->head + 40);
- *y1 = ttSHORT(info->data + info->head + 42);
-}
-
-STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
-{
- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
- return (float) height / fheight;
-}
-
-STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
-{
- int unitsPerEm = ttUSHORT(info->data + info->head + 18);
- return pixels / unitsPerEm;
-}
-
-STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
-{
- STBTT_free(v, info->userdata);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// antialiasing software rasterizer
-//
-
-STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
- // e.g. space character
- if (ix0) *ix0 = 0;
- if (iy0) *iy0 = 0;
- if (ix1) *ix1 = 0;
- if (iy1) *iy1 = 0;
- } else {
- // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
- if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
- if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
- if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
- if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
- }
-}
-
-STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
-}
-
-STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
-}
-
-STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Rasterizer
-
-typedef struct stbtt__hheap_chunk
-{
- struct stbtt__hheap_chunk *next;
-} stbtt__hheap_chunk;
-
-typedef struct stbtt__hheap
-{
- struct stbtt__hheap_chunk *head;
- void *first_free;
- int num_remaining_in_head_chunk;
-} stbtt__hheap;
-
-static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
-{
- if (hh->first_free) {
- void *p = hh->first_free;
- hh->first_free = * (void **) p;
- return p;
- } else {
- if (hh->num_remaining_in_head_chunk == 0) {
- int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
- stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
- if (c == NULL)
- return NULL;
- c->next = hh->head;
- hh->head = c;
- hh->num_remaining_in_head_chunk = count;
- }
- --hh->num_remaining_in_head_chunk;
- return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
- }
-}
-
-static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
-{
- *(void **) p = hh->first_free;
- hh->first_free = p;
-}
-
-static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
-{
- stbtt__hheap_chunk *c = hh->head;
- while (c) {
- stbtt__hheap_chunk *n = c->next;
- STBTT_free(c, userdata);
- c = n;
- }
-}
-
-typedef struct stbtt__edge {
- float x0,y0, x1,y1;
- int invert;
-} stbtt__edge;
-
-
-typedef struct stbtt__active_edge
-{
- struct stbtt__active_edge *next;
- #if STBTT_RASTERIZER_VERSION==1
- int x,dx;
- float ey;
- int direction;
- #elif STBTT_RASTERIZER_VERSION==2
- float fx,fdx,fdy;
- float direction;
- float sy;
- float ey;
- #else
- #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
- #endif
-} stbtt__active_edge;
-
-#if STBTT_RASTERIZER_VERSION == 1
-#define STBTT_FIXSHIFT 10
-#define STBTT_FIX (1 << STBTT_FIXSHIFT)
-#define STBTT_FIXMASK (STBTT_FIX-1)
-
-static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
-{
- stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
- STBTT_assert(z != NULL);
- if (!z) return z;
-
- // round dx down to avoid overshooting
- if (dxdy < 0)
- z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
- else
- z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
-
- z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
- z->x -= off_x * STBTT_FIX;
-
- z->ey = e->y1;
- z->next = 0;
- z->direction = e->invert ? 1 : -1;
- return z;
-}
-#elif STBTT_RASTERIZER_VERSION == 2
-static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
-{
- stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
- STBTT_assert(z != NULL);
- //STBTT_assert(e->y0 <= start_point);
- if (!z) return z;
- z->fdx = dxdy;
- z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
- z->fx = e->x0 + dxdy * (start_point - e->y0);
- z->fx -= off_x;
- z->direction = e->invert ? 1.0f : -1.0f;
- z->sy = e->y0;
- z->ey = e->y1;
- z->next = 0;
- return z;
-}
-#else
-#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
-
-#if STBTT_RASTERIZER_VERSION == 1
-// note: this routine clips fills that extend off the edges... ideally this
-// wouldn't happen, but it could happen if the truetype glyph bounding boxes
-// are wrong, or if the user supplies a too-small bitmap
-static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
-{
- // non-zero winding fill
- int x0=0, w=0;
-
- while (e) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w += e->direction;
- } else {
- int x1 = e->x; w += e->direction;
- // if we went to zero, we need to draw
- if (w == 0) {
- int i = x0 >> STBTT_FIXSHIFT;
- int j = x1 >> STBTT_FIXSHIFT;
-
- if (i < len && j >= 0) {
- if (i == j) {
- // x0,x1 are the same pixel, so compute combined coverage
- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
- } else {
- if (i >= 0) // add antialiasing for x0
- scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
- else
- i = -1; // clip
-
- if (j < len) // add antialiasing for x1
- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
- else
- j = len; // clip
-
- for (++i; i < j; ++i) // fill pixels between x0 and x1
- scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
- }
- }
- }
- }
-
- e = e->next;
- }
-}
-
-static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-{
- stbtt__hheap hh = { 0, 0, 0 };
- stbtt__active_edge *active = NULL;
- int y,j=0;
- int max_weight = (255 / vsubsample); // weight per vertical scanline
- int s; // vertical subsample index
- unsigned char scanline_data[512], *scanline;
-
- if (result->w > 512)
- scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
- else
- scanline = scanline_data;
-
- y = off_y * vsubsample;
- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
-
- while (j < result->h) {
- STBTT_memset(scanline, 0, result->w);
- for (s=0; s < vsubsample; ++s) {
- // find center of pixel for this scanline
- float scan_y = y + 0.5f;
- stbtt__active_edge **step = &active;
-
- // update all active edges;
- // remove all active edges that terminate before the center of this scanline
- while (*step) {
- stbtt__active_edge * z = *step;
- if (z->ey <= scan_y) {
- *step = z->next; // delete from list
- STBTT_assert(z->direction);
- z->direction = 0;
- stbtt__hheap_free(&hh, z);
- } else {
- z->x += z->dx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
- }
-
- // resort the list if needed
- for(;;) {
- int changed=0;
- step = &active;
- while (*step && (*step)->next) {
- if ((*step)->x > (*step)->next->x) {
- stbtt__active_edge *t = *step;
- stbtt__active_edge *q = t->next;
-
- t->next = q->next;
- q->next = t;
- *step = q;
- changed = 1;
- }
- step = &(*step)->next;
- }
- if (!changed) break;
- }
-
- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
- while (e->y0 <= scan_y) {
- if (e->y1 > scan_y) {
- stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
- if (z != NULL) {
- // find insertion point
- if (active == NULL)
- active = z;
- else if (z->x < active->x) {
- // insert at front
- z->next = active;
- active = z;
- } else {
- // find thing to insert AFTER
- stbtt__active_edge *p = active;
- while (p->next && p->next->x < z->x)
- p = p->next;
- // at this point, p->next->x is NOT < z->x
- z->next = p->next;
- p->next = z;
- }
- }
- }
- ++e;
- }
-
- // now process all active edges in XOR fashion
- if (active)
- stbtt__fill_active_edges(scanline, result->w, active, max_weight);
-
- ++y;
- }
- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
- ++j;
- }
-
- stbtt__hheap_cleanup(&hh, userdata);
-
- if (scanline != scanline_data)
- STBTT_free(scanline, userdata);
-}
-
-#elif STBTT_RASTERIZER_VERSION == 2
-
-// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
-// (i.e. it has already been clipped to those)
-static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
-{
- if (y0 == y1) return;
- STBTT_assert(y0 < y1);
- STBTT_assert(e->sy <= e->ey);
- if (y0 > e->ey) return;
- if (y1 < e->sy) return;
- if (y0 < e->sy) {
- x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
- y0 = e->sy;
- }
- if (y1 > e->ey) {
- x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
- y1 = e->ey;
- }
-
- if (x0 == x)
- STBTT_assert(x1 <= x+1);
- else if (x0 == x+1)
- STBTT_assert(x1 >= x);
- else if (x0 <= x)
- STBTT_assert(x1 <= x);
- else if (x0 >= x+1)
- STBTT_assert(x1 >= x+1);
- else
- STBTT_assert(x1 >= x && x1 <= x+1);
-
- if (x0 <= x && x1 <= x)
- scanline[x] += e->direction * (y1-y0);
- else if (x0 >= x+1 && x1 >= x+1)
- ;
- else {
- STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
- scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
- }
-}
-
-static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
-{
- float y_bottom = y_top+1;
-
- while (e) {
- // brute force every pixel
-
- // compute intersection points with top & bottom
- STBTT_assert(e->ey >= y_top);
-
- if (e->fdx == 0) {
- float x0 = e->fx;
- if (x0 < len) {
- if (x0 >= 0) {
- stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
- stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
- } else {
- stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
- }
- }
- } else {
- float x0 = e->fx;
- float dx = e->fdx;
- float xb = x0 + dx;
- float x_top, x_bottom;
- float sy0,sy1;
- float dy = e->fdy;
- STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
-
- // compute endpoints of line segment clipped to this scanline (if the
- // line segment starts on this scanline. x0 is the intersection of the
- // line with y_top, but that may be off the line segment.
- if (e->sy > y_top) {
- x_top = x0 + dx * (e->sy - y_top);
- sy0 = e->sy;
- } else {
- x_top = x0;
- sy0 = y_top;
- }
- if (e->ey < y_bottom) {
- x_bottom = x0 + dx * (e->ey - y_top);
- sy1 = e->ey;
- } else {
- x_bottom = xb;
- sy1 = y_bottom;
- }
-
- if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
- // from here on, we don't have to range check x values
-
- if ((int) x_top == (int) x_bottom) {
- float height;
- // simple case, only spans one pixel
- int x = (int) x_top;
- height = sy1 - sy0;
- STBTT_assert(x >= 0 && x < len);
- scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
- scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
- } else {
- int x,x1,x2;
- float y_crossing, step, sign, area;
- // covers 2+ pixels
- if (x_top > x_bottom) {
- // flip scanline vertically; signed area is the same
- float t;
- sy0 = y_bottom - (sy0 - y_top);
- sy1 = y_bottom - (sy1 - y_top);
- t = sy0, sy0 = sy1, sy1 = t;
- t = x_bottom, x_bottom = x_top, x_top = t;
- dx = -dx;
- dy = -dy;
- t = x0, x0 = xb, xb = t;
- }
-
- x1 = (int) x_top;
- x2 = (int) x_bottom;
- // compute intersection with y axis at x1+1
- y_crossing = (x1+1 - x0) * dy + y_top;
-
- sign = e->direction;
- // area of the rectangle covered from y0..y_crossing
- area = sign * (y_crossing-sy0);
- // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
- scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
-
- step = sign * dy;
- for (x = x1+1; x < x2; ++x) {
- scanline[x] += area + step/2;
- area += step;
- }
- y_crossing += dy * (x2 - (x1+1));
-
- STBTT_assert(STBTT_fabs(area) <= 1.01f);
-
- scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
-
- scanline_fill[x2] += sign * (sy1-sy0);
- }
- } else {
- // if edge goes outside of box we're drawing, we require
- // clipping logic. since this does not match the intended use
- // of this library, we use a different, very slow brute
- // force implementation
- int x;
- for (x=0; x < len; ++x) {
- // cases:
- //
- // there can be up to two intersections with the pixel. any intersection
- // with left or right edges can be handled by splitting into two (or three)
- // regions. intersections with top & bottom do not necessitate case-wise logic.
- //
- // the old way of doing this found the intersections with the left & right edges,
- // then used some simple logic to produce up to three segments in sorted order
- // from top-to-bottom. however, this had a problem: if an x edge was epsilon
- // across the x border, then the corresponding y position might not be distinct
- // from the other y segment, and it might ignored as an empty segment. to avoid
- // that, we need to explicitly produce segments based on x positions.
-
- // rename variables to clearly-defined pairs
- float y0 = y_top;
- float x1 = (float) (x);
- float x2 = (float) (x+1);
- float x3 = xb;
- float y3 = y_bottom;
-
- // x = e->x + e->dx * (y-y_top)
- // (y-y_top) = (x - e->x) / e->dx
- // y = (x - e->x) / e->dx + y_top
- float y1 = (x - x0) / dx + y_top;
- float y2 = (x+1 - x0) / dx + y_top;
-
- if (x0 < x1 && x3 > x2) { // three segments descending down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else if (x3 < x1 && x0 > x2) { // three segments descending down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else { // one segment
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
- }
- }
- }
- }
- e = e->next;
- }
-}
-
-// directly AA rasterize edges w/o supersampling
-static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-{
- stbtt__hheap hh = { 0, 0, 0 };
- stbtt__active_edge *active = NULL;
- int y,j=0, i;
- float scanline_data[129], *scanline, *scanline2;
-
- STBTT__NOTUSED(vsubsample);
-
- if (result->w > 64)
- scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
- else
- scanline = scanline_data;
-
- scanline2 = scanline + result->w;
-
- y = off_y;
- e[n].y0 = (float) (off_y + result->h) + 1;
-
- while (j < result->h) {
- // find center of pixel for this scanline
- float scan_y_top = y + 0.0f;
- float scan_y_bottom = y + 1.0f;
- stbtt__active_edge **step = &active;
-
- STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
- STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
-
- // update all active edges;
- // remove all active edges that terminate before the top of this scanline
- while (*step) {
- stbtt__active_edge * z = *step;
- if (z->ey <= scan_y_top) {
- *step = z->next; // delete from list
- STBTT_assert(z->direction);
- z->direction = 0;
- stbtt__hheap_free(&hh, z);
- } else {
- step = &((*step)->next); // advance through list
- }
- }
-
- // insert all edges that start before the bottom of this scanline
- while (e->y0 <= scan_y_bottom) {
- if (e->y0 != e->y1) {
- stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
- if (z != NULL) {
- if (j == 0 && off_y != 0) {
- if (z->ey < scan_y_top) {
- // this can happen due to subpixel positioning and some kind of fp rounding error i think
- z->ey = scan_y_top;
- }
- }
- STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
- // insert at front
- z->next = active;
- active = z;
- }
- }
- ++e;
- }
-
- // now process all active edges
- if (active)
- stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
-
- {
- float sum = 0;
- for (i=0; i < result->w; ++i) {
- float k;
- int m;
- sum += scanline2[i];
- k = scanline[i] + sum;
- k = (float) STBTT_fabs(k)*255 + 0.5f;
- m = (int) k;
- if (m > 255) m = 255;
- result->pixels[j*result->stride + i] = (unsigned char) m;
- }
- }
- // advance all the edges
- step = &active;
- while (*step) {
- stbtt__active_edge *z = *step;
- z->fx += z->fdx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
-
- ++y;
- ++j;
- }
-
- stbtt__hheap_cleanup(&hh, userdata);
-
- if (scanline != scanline_data)
- STBTT_free(scanline, userdata);
-}
-#else
-#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
-
-#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)
-
-static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
-{
- int i,j;
- for (i=1; i < n; ++i) {
- stbtt__edge t = p[i], *a = &t;
- j = i;
- while (j > 0) {
- stbtt__edge *b = &p[j-1];
- int c = STBTT__COMPARE(a,b);
- if (!c) break;
- p[j] = p[j-1];
- --j;
- }
- if (i != j)
- p[j] = t;
- }
-}
-
-static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
-{
- /* threshold for transitioning to insertion sort */
- while (n > 12) {
- stbtt__edge t;
- int c01,c12,c,m,i,j;
-
- /* compute median of three */
- m = n >> 1;
- c01 = STBTT__COMPARE(&p[0],&p[m]);
- c12 = STBTT__COMPARE(&p[m],&p[n-1]);
- /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
- if (c01 != c12) {
- /* otherwise, we'll need to swap something else to middle */
- int z;
- c = STBTT__COMPARE(&p[0],&p[n-1]);
- /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
- /* 0<mid && mid>n: 0>n => 0; 0<n => n */
- z = (c == c12) ? 0 : n-1;
- t = p[z];
- p[z] = p[m];
- p[m] = t;
- }
- /* now p[m] is the median-of-three */
- /* swap it to the beginning so it won't move around */
- t = p[0];
- p[0] = p[m];
- p[m] = t;
-
- /* partition loop */
- i=1;
- j=n-1;
- for(;;) {
- /* handling of equality is crucial here */
- /* for sentinels & efficiency with duplicates */
- for (;;++i) {
- if (!STBTT__COMPARE(&p[i], &p[0])) break;
- }
- for (;;--j) {
- if (!STBTT__COMPARE(&p[0], &p[j])) break;
- }
- /* make sure we haven't crossed */
- if (i >= j) break;
- t = p[i];
- p[i] = p[j];
- p[j] = t;
-
- ++i;
- --j;
- }
- /* recurse on smaller side, iterate on larger */
- if (j < (n-i)) {
- stbtt__sort_edges_quicksort(p,j);
- p = p+i;
- n = n-i;
- } else {
- stbtt__sort_edges_quicksort(p+i, n-i);
- n = j;
- }
- }
-}
-
-static void stbtt__sort_edges(stbtt__edge *p, int n)
-{
- stbtt__sort_edges_quicksort(p, n);
- stbtt__sort_edges_ins_sort(p, n);
-}
-
-typedef struct
-{
- float x,y;
-} stbtt__point;
-
-static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
-{
- float y_scale_inv = invert ? -scale_y : scale_y;
- stbtt__edge *e;
- int n,i,j,k,m;
-#if STBTT_RASTERIZER_VERSION == 1
- int vsubsample = result->h < 8 ? 15 : 5;
-#elif STBTT_RASTERIZER_VERSION == 2
- int vsubsample = 1;
-#else
- #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
-
- // now we have to blow out the windings into explicit edge lists
- n = 0;
- for (i=0; i < windings; ++i)
- n += wcount[i];
-
- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
- if (e == 0) return;
- n = 0;
-
- m=0;
- for (i=0; i < windings; ++i) {
- stbtt__point *p = pts + m;
- m += wcount[i];
- j = wcount[i]-1;
- for (k=0; k < wcount[i]; j=k++) {
- int a=k,b=j;
- // skip the edge if horizontal
- if (p[j].y == p[k].y)
- continue;
- // add edge from j to k to the list
- e[n].invert = 0;
- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
- e[n].invert = 1;
- a=j,b=k;
- }
- e[n].x0 = p[a].x * scale_x + shift_x;
- e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
- e[n].x1 = p[b].x * scale_x + shift_x;
- e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
- ++n;
- }
- }
-
- // now sort the edges by their highest point (should snap to integer, and then by x)
- //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
- stbtt__sort_edges(e, n);
-
- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
-
- STBTT_free(e, userdata);
-}
-
-static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
-{
- if (!points) return; // during first pass, it's unallocated
- points[n].x = x;
- points[n].y = y;
-}
-
-// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
-static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
-{
- // midpoint
- float mx = (x0 + 2*x1 + x2)/4;
- float my = (y0 + 2*y1 + y2)/4;
- // versus directly drawn line
- float dx = (x0+x2)/2 - mx;
- float dy = (y0+y2)/2 - my;
- if (n > 16) // 65536 segments on one curve better be enough!
- return 1;
- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
- } else {
- stbtt__add_point(points, *num_points,x2,y2);
- *num_points = *num_points+1;
- }
- return 1;
-}
-
-static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
-{
- // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
- float dx0 = x1-x0;
- float dy0 = y1-y0;
- float dx1 = x2-x1;
- float dy1 = y2-y1;
- float dx2 = x3-x2;
- float dy2 = y3-y2;
- float dx = x3-x0;
- float dy = y3-y0;
- float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
- float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
- float flatness_squared = longlen*longlen-shortlen*shortlen;
-
- if (n > 16) // 65536 segments on one curve better be enough!
- return;
-
- if (flatness_squared > objspace_flatness_squared) {
- float x01 = (x0+x1)/2;
- float y01 = (y0+y1)/2;
- float x12 = (x1+x2)/2;
- float y12 = (y1+y2)/2;
- float x23 = (x2+x3)/2;
- float y23 = (y2+y3)/2;
-
- float xa = (x01+x12)/2;
- float ya = (y01+y12)/2;
- float xb = (x12+x23)/2;
- float yb = (y12+y23)/2;
-
- float mx = (xa+xb)/2;
- float my = (ya+yb)/2;
-
- stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
- stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
- } else {
- stbtt__add_point(points, *num_points,x3,y3);
- *num_points = *num_points+1;
- }
-}
-
-// returns number of contours
-static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
-{
- stbtt__point *points=0;
- int num_points=0;
-
- float objspace_flatness_squared = objspace_flatness * objspace_flatness;
- int i,n=0,start=0, pass;
-
- // count how many "moves" there are to get the contour count
- for (i=0; i < num_verts; ++i)
- if (vertices[i].type == STBTT_vmove)
- ++n;
-
- *num_contours = n;
- if (n == 0) return 0;
-
- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
-
- if (*contour_lengths == 0) {
- *num_contours = 0;
- return 0;
- }
-
- // make two passes through the points so we don't need to realloc
- for (pass=0; pass < 2; ++pass) {
- float x=0,y=0;
- if (pass == 1) {
- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
- if (points == NULL) goto error;
- }
- num_points = 0;
- n= -1;
- for (i=0; i < num_verts; ++i) {
- switch (vertices[i].type) {
- case STBTT_vmove:
- // start the next contour
- if (n >= 0)
- (*contour_lengths)[n] = num_points - start;
- ++n;
- start = num_points;
-
- x = vertices[i].x, y = vertices[i].y;
- stbtt__add_point(points, num_points++, x,y);
- break;
- case STBTT_vline:
- x = vertices[i].x, y = vertices[i].y;
- stbtt__add_point(points, num_points++, x, y);
- break;
- case STBTT_vcurve:
- stbtt__tesselate_curve(points, &num_points, x,y,
- vertices[i].cx, vertices[i].cy,
- vertices[i].x, vertices[i].y,
- objspace_flatness_squared, 0);
- x = vertices[i].x, y = vertices[i].y;
- break;
- case STBTT_vcubic:
- stbtt__tesselate_cubic(points, &num_points, x,y,
- vertices[i].cx, vertices[i].cy,
- vertices[i].cx1, vertices[i].cy1,
- vertices[i].x, vertices[i].y,
- objspace_flatness_squared, 0);
- x = vertices[i].x, y = vertices[i].y;
- break;
- }
- }
- (*contour_lengths)[n] = num_points - start;
- }
-
- return points;
-error:
- STBTT_free(points, userdata);
- STBTT_free(*contour_lengths, userdata);
- *contour_lengths = 0;
- *num_contours = 0;
- return NULL;
-}
-
-STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
-{
- float scale = scale_x > scale_y ? scale_y : scale_x;
- int winding_count = 0;
- int *winding_lengths = NULL;
- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
- if (windings) {
- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
- STBTT_free(winding_lengths, userdata);
- STBTT_free(windings, userdata);
- }
-}
-
-STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
-{
- STBTT_free(bitmap, userdata);
-}
-
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-{
- int ix0,iy0,ix1,iy1;
- stbtt__bitmap gbm;
- stbtt_vertex *vertices;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-
- if (scale_x == 0) scale_x = scale_y;
- if (scale_y == 0) {
- if (scale_x == 0) {
- STBTT_free(vertices, info->userdata);
- return NULL;
- }
- scale_y = scale_x;
- }
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
-
- // now we get the size
- gbm.w = (ix1 - ix0);
- gbm.h = (iy1 - iy0);
- gbm.pixels = NULL; // in case we error
-
- if (width ) *width = gbm.w;
- if (height) *height = gbm.h;
- if (xoff ) *xoff = ix0;
- if (yoff ) *yoff = iy0;
-
- if (gbm.w && gbm.h) {
- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
- if (gbm.pixels) {
- gbm.stride = gbm.w;
-
- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
- }
- }
- STBTT_free(vertices, info->userdata);
- return gbm.pixels;
-}
-
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
-{
- int ix0,iy0;
- stbtt_vertex *vertices;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
- stbtt__bitmap gbm;
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
- gbm.pixels = output;
- gbm.w = out_w;
- gbm.h = out_h;
- gbm.stride = out_stride;
-
- if (gbm.w && gbm.h)
- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
-
- STBTT_free(vertices, info->userdata);
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
-{
- stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
-}
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
-{
- stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
-{
- stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
-}
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
-{
- stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// bitmap baking
-//
-// This is SUPER-CRAPPY packing to keep source code small
-
-static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
- float pixel_height, // height of font in pixels
- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
- int first_char, int num_chars, // characters to bake
- stbtt_bakedchar *chardata)
-{
- float scale;
- int x,y,bottom_y, i;
- stbtt_fontinfo f;
- f.userdata = NULL;
- if (!stbtt_InitFont(&f, data, offset))
- return -1;
- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
- x=y=1;
- bottom_y = 1;
-
- scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
-
- for (i=0; i < num_chars; ++i) {
- int advance, lsb, x0,y0,x1,y1,gw,gh;
- int g = stbtt_FindGlyphIndex(&f, first_char + i);
- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
- gw = x1-x0;
- gh = y1-y0;
- if (x + gw + 1 >= pw)
- y = bottom_y, x = 1; // advance to next row
- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
- return -i;
- STBTT_assert(x+gw < pw);
- STBTT_assert(y+gh < ph);
- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
- chardata[i].x0 = (stbtt_int16) x;
- chardata[i].y0 = (stbtt_int16) y;
- chardata[i].x1 = (stbtt_int16) (x + gw);
- chardata[i].y1 = (stbtt_int16) (y + gh);
- chardata[i].xadvance = scale * advance;
- chardata[i].xoff = (float) x0;
- chardata[i].yoff = (float) y0;
- x = x + gw + 1;
- if (y+gh+1 > bottom_y)
- bottom_y = y+gh+1;
- }
- return bottom_y;
-}
-
-STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
-{
- float d3d_bias = opengl_fillrule ? 0 : -0.5f;
- float ipw = 1.0f / pw, iph = 1.0f / ph;
- const stbtt_bakedchar *b = chardata + char_index;
- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
-
- q->x0 = round_x + d3d_bias;
- q->y0 = round_y + d3d_bias;
- q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
- q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
-
- q->s0 = b->x0 * ipw;
- q->t0 = b->y0 * iph;
- q->s1 = b->x1 * ipw;
- q->t1 = b->y1 * iph;
-
- *xpos += b->xadvance;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// rectangle packing replacement routines if you don't have stb_rect_pack.h
-//
-
-#ifndef STB_RECT_PACK_VERSION
-
-typedef int stbrp_coord;
-
-////////////////////////////////////////////////////////////////////////////////////
-// //
-// //
-// COMPILER WARNING ?!?!? //
-// //
-// //
-// if you get a compile warning due to these symbols being defined more than //
-// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //
-// //
-////////////////////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- int width,height;
- int x,y,bottom_y;
-} stbrp_context;
-
-typedef struct
-{
- unsigned char x;
-} stbrp_node;
-
-struct stbrp_rect
-{
- stbrp_coord x,y;
- int id,w,h,was_packed;
-};
-
-static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
-{
- con->width = pw;
- con->height = ph;
- con->x = 0;
- con->y = 0;
- con->bottom_y = 0;
- STBTT__NOTUSED(nodes);
- STBTT__NOTUSED(num_nodes);
-}
-
-static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
-{
- int i;
- for (i=0; i < num_rects; ++i) {
- if (con->x + rects[i].w > con->width) {
- con->x = 0;
- con->y = con->bottom_y;
- }
- if (con->y + rects[i].h > con->height)
- break;
- rects[i].x = con->x;
- rects[i].y = con->y;
- rects[i].was_packed = 1;
- con->x += rects[i].w;
- if (con->y + rects[i].h > con->bottom_y)
- con->bottom_y = con->y + rects[i].h;
- }
- for ( ; i < num_rects; ++i)
- rects[i].was_packed = 0;
-}
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// bitmap baking
-//
-// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
-// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
-
-STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
-{
- stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
- int num_nodes = pw - padding;
- stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
-
- if (context == NULL || nodes == NULL) {
- if (context != NULL) STBTT_free(context, alloc_context);
- if (nodes != NULL) STBTT_free(nodes , alloc_context);
- return 0;
- }
-
- spc->user_allocator_context = alloc_context;
- spc->width = pw;
- spc->height = ph;
- spc->pixels = pixels;
- spc->pack_info = context;
- spc->nodes = nodes;
- spc->padding = padding;
- spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
- spc->h_oversample = 1;
- spc->v_oversample = 1;
- spc->skip_missing = 0;
-
- stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
-
- if (pixels)
- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-
- return 1;
-}
-
-STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
-{
- STBTT_free(spc->nodes , spc->user_allocator_context);
- STBTT_free(spc->pack_info, spc->user_allocator_context);
-}
-
-STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
-{
- STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
- STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
- if (h_oversample <= STBTT_MAX_OVERSAMPLE)
- spc->h_oversample = h_oversample;
- if (v_oversample <= STBTT_MAX_OVERSAMPLE)
- spc->v_oversample = v_oversample;
-}
-
-STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
-{
- spc->skip_missing = skip;
-}
-
-#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
-
-static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
-{
- unsigned char buffer[STBTT_MAX_OVERSAMPLE];
- int safe_w = w - kernel_width;
- int j;
- STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
- for (j=0; j < h; ++j) {
- int i;
- unsigned int total;
- STBTT_memset(buffer, 0, kernel_width);
-
- total = 0;
-
- // make kernel_width a constant in common cases so compiler can optimize out the divide
- switch (kernel_width) {
- case 2:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 2);
- }
- break;
- case 3:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 3);
- }
- break;
- case 4:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 4);
- }
- break;
- case 5:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 5);
- }
- break;
- default:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / kernel_width);
- }
- break;
- }
-
- for (; i < w; ++i) {
- STBTT_assert(pixels[i] == 0);
- total -= buffer[i & STBTT__OVER_MASK];
- pixels[i] = (unsigned char) (total / kernel_width);
- }
-
- pixels += stride_in_bytes;
- }
-}
-
-static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
-{
- unsigned char buffer[STBTT_MAX_OVERSAMPLE];
- int safe_h = h - kernel_width;
- int j;
- STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
- for (j=0; j < w; ++j) {
- int i;
- unsigned int total;
- STBTT_memset(buffer, 0, kernel_width);
-
- total = 0;
-
- // make kernel_width a constant in common cases so compiler can optimize out the divide
- switch (kernel_width) {
- case 2:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
- }
- break;
- case 3:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
- }
- break;
- case 4:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
- }
- break;
- case 5:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
- }
- break;
- default:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
- }
- break;
- }
-
- for (; i < h; ++i) {
- STBTT_assert(pixels[i*stride_in_bytes] == 0);
- total -= buffer[i & STBTT__OVER_MASK];
- pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
- }
-
- pixels += 1;
- }
-}
-
-static float stbtt__oversample_shift(int oversample)
-{
- if (!oversample)
- return 0.0f;
-
- // The prefilter is a box filter of width "oversample",
- // which shifts phase by (oversample - 1)/2 pixels in
- // oversampled space. We want to shift in the opposite
- // direction to counter this.
- return (float)-(oversample - 1) / (2.0f * (float)oversample);
-}
-
-// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
-{
- int i,j,k;
-
- k=0;
- for (i=0; i < num_ranges; ++i) {
- float fh = ranges[i].font_size;
- float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
- ranges[i].h_oversample = (unsigned char) spc->h_oversample;
- ranges[i].v_oversample = (unsigned char) spc->v_oversample;
- for (j=0; j < ranges[i].num_chars; ++j) {
- int x0,y0,x1,y1;
- int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
- int glyph = stbtt_FindGlyphIndex(info, codepoint);
- if (glyph == 0 && spc->skip_missing) {
- rects[k].w = rects[k].h = 0;
- } else {
- stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- 0,0,
- &x0,&y0,&x1,&y1);
- rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
- rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
- }
- ++k;
- }
- }
-
- return k;
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
-{
- stbtt_MakeGlyphBitmapSubpixel(info,
- output,
- out_w - (prefilter_x - 1),
- out_h - (prefilter_y - 1),
- out_stride,
- scale_x,
- scale_y,
- shift_x,
- shift_y,
- glyph);
-
- if (prefilter_x > 1)
- stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
-
- if (prefilter_y > 1)
- stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
-
- *sub_x = stbtt__oversample_shift(prefilter_x);
- *sub_y = stbtt__oversample_shift(prefilter_y);
-}
-
-// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
-{
- int i,j,k, return_value = 1;
-
- // save current values
- int old_h_over = spc->h_oversample;
- int old_v_over = spc->v_oversample;
-
- k = 0;
- for (i=0; i < num_ranges; ++i) {
- float fh = ranges[i].font_size;
- float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
- float recip_h,recip_v,sub_x,sub_y;
- spc->h_oversample = ranges[i].h_oversample;
- spc->v_oversample = ranges[i].v_oversample;
- recip_h = 1.0f / spc->h_oversample;
- recip_v = 1.0f / spc->v_oversample;
- sub_x = stbtt__oversample_shift(spc->h_oversample);
- sub_y = stbtt__oversample_shift(spc->v_oversample);
- for (j=0; j < ranges[i].num_chars; ++j) {
- stbrp_rect *r = &rects[k];
- if (r->was_packed && r->w != 0 && r->h != 0) {
- stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
- int advance, lsb, x0,y0,x1,y1;
- int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
- int glyph = stbtt_FindGlyphIndex(info, codepoint);
- stbrp_coord pad = (stbrp_coord) spc->padding;
-
- // pad on left and top
- r->x += pad;
- r->y += pad;
- r->w -= pad;
- r->h -= pad;
- stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
- stbtt_GetGlyphBitmapBox(info, glyph,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- &x0,&y0,&x1,&y1);
- stbtt_MakeGlyphBitmapSubpixel(info,
- spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w - spc->h_oversample+1,
- r->h - spc->v_oversample+1,
- spc->stride_in_bytes,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- 0,0,
- glyph);
-
- if (spc->h_oversample > 1)
- stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w, r->h, spc->stride_in_bytes,
- spc->h_oversample);
-
- if (spc->v_oversample > 1)
- stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w, r->h, spc->stride_in_bytes,
- spc->v_oversample);
-
- bc->x0 = (stbtt_int16) r->x;
- bc->y0 = (stbtt_int16) r->y;
- bc->x1 = (stbtt_int16) (r->x + r->w);
- bc->y1 = (stbtt_int16) (r->y + r->h);
- bc->xadvance = scale * advance;
- bc->xoff = (float) x0 * recip_h + sub_x;
- bc->yoff = (float) y0 * recip_v + sub_y;
- bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
- bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
- } else {
- return_value = 0; // if any fail, report failure
- }
-
- ++k;
- }
- }
-
- // restore original values
- spc->h_oversample = old_h_over;
- spc->v_oversample = old_v_over;
-
- return return_value;
-}
-
-STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
-{
- stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
-}
-
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
-{
- stbtt_fontinfo info;
- int i,j,n, return_value = 1;
- //stbrp_context *context = (stbrp_context *) spc->pack_info;
- stbrp_rect *rects;
-
- // flag all characters as NOT packed
- for (i=0; i < num_ranges; ++i)
- for (j=0; j < ranges[i].num_chars; ++j)
- ranges[i].chardata_for_range[j].x0 =
- ranges[i].chardata_for_range[j].y0 =
- ranges[i].chardata_for_range[j].x1 =
- ranges[i].chardata_for_range[j].y1 = 0;
-
- n = 0;
- for (i=0; i < num_ranges; ++i)
- n += ranges[i].num_chars;
-
- rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
- if (rects == NULL)
- return 0;
-
- info.userdata = spc->user_allocator_context;
- stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
-
- n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
-
- stbtt_PackFontRangesPackRects(spc, rects, n);
-
- return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
-
- STBTT_free(rects, spc->user_allocator_context);
- return return_value;
-}
-
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
- int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
-{
- stbtt_pack_range range;
- range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
- range.array_of_unicode_codepoints = NULL;
- range.num_chars = num_chars_in_range;
- range.chardata_for_range = chardata_for_range;
- range.font_size = font_size;
- return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
-}
-
-STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
-{
- int i_ascent, i_descent, i_lineGap;
- float scale;
- stbtt_fontinfo info;
- stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
- scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
- stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
- *ascent = (float) i_ascent * scale;
- *descent = (float) i_descent * scale;
- *lineGap = (float) i_lineGap * scale;
-}
-
-STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
-{
- float ipw = 1.0f / pw, iph = 1.0f / ph;
- const stbtt_packedchar *b = chardata + char_index;
-
- if (align_to_integer) {
- float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
- float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
- q->x0 = x;
- q->y0 = y;
- q->x1 = x + b->xoff2 - b->xoff;
- q->y1 = y + b->yoff2 - b->yoff;
- } else {
- q->x0 = *xpos + b->xoff;
- q->y0 = *ypos + b->yoff;
- q->x1 = *xpos + b->xoff2;
- q->y1 = *ypos + b->yoff2;
- }
-
- q->s0 = b->x0 * ipw;
- q->t0 = b->y0 * iph;
- q->s1 = b->x1 * ipw;
- q->t1 = b->y1 * iph;
-
- *xpos += b->xadvance;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// sdf computation
-//
-
-#define STBTT_min(a,b) ((a) < (b) ? (a) : (b))
-#define STBTT_max(a,b) ((a) < (b) ? (b) : (a))
-
-static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
-{
- float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
- float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
- float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
- float roperp = orig[1]*ray[0] - orig[0]*ray[1];
-
- float a = q0perp - 2*q1perp + q2perp;
- float b = q1perp - q0perp;
- float c = q0perp - roperp;
-
- float s0 = 0., s1 = 0.;
- int num_s = 0;
-
- if (a != 0.0) {
- float discr = b*b - a*c;
- if (discr > 0.0) {
- float rcpna = -1 / a;
- float d = (float) STBTT_sqrt(discr);
- s0 = (b+d) * rcpna;
- s1 = (b-d) * rcpna;
- if (s0 >= 0.0 && s0 <= 1.0)
- num_s = 1;
- if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
- if (num_s == 0) s0 = s1;
- ++num_s;
- }
- }
- } else {
- // 2*b*s + c = 0
- // s = -c / (2*b)
- s0 = c / (-2 * b);
- if (s0 >= 0.0 && s0 <= 1.0)
- num_s = 1;
- }
-
- if (num_s == 0)
- return 0;
- else {
- float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
- float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
-
- float q0d = q0[0]*rayn_x + q0[1]*rayn_y;
- float q1d = q1[0]*rayn_x + q1[1]*rayn_y;
- float q2d = q2[0]*rayn_x + q2[1]*rayn_y;
- float rod = orig[0]*rayn_x + orig[1]*rayn_y;
-
- float q10d = q1d - q0d;
- float q20d = q2d - q0d;
- float q0rd = q0d - rod;
-
- hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
- hits[0][1] = a*s0+b;
-
- if (num_s > 1) {
- hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
- hits[1][1] = a*s1+b;
- return 2;
- } else {
- return 1;
- }
- }
-}
-
-static int equal(float *a, float *b)
-{
- return (a[0] == b[0] && a[1] == b[1]);
-}
-
-static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
-{
- int i;
- float orig[2], ray[2] = { 1, 0 };
- float y_frac;
- int winding = 0;
-
- orig[0] = x;
- orig[1] = y;
-
- // make sure y never passes through a vertex of the shape
- y_frac = (float) STBTT_fmod(y, 1.0f);
- if (y_frac < 0.01f)
- y += 0.01f;
- else if (y_frac > 0.99f)
- y -= 0.01f;
- orig[1] = y;
-
- // test a ray from (-infinity,y) to (x,y)
- for (i=0; i < nverts; ++i) {
- if (verts[i].type == STBTT_vline) {
- int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
- int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
- if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
- float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
- winding += (y0 < y1) ? 1 : -1;
- }
- }
- if (verts[i].type == STBTT_vcurve) {
- int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
- int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy;
- int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ;
- int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
- int by = STBTT_max(y0,STBTT_max(y1,y2));
- if (y > ay && y < by && x > ax) {
- float q0[2],q1[2],q2[2];
- float hits[2][2];
- q0[0] = (float)x0;
- q0[1] = (float)y0;
- q1[0] = (float)x1;
- q1[1] = (float)y1;
- q2[0] = (float)x2;
- q2[1] = (float)y2;
- if (equal(q0,q1) || equal(q1,q2)) {
- x0 = (int)verts[i-1].x;
- y0 = (int)verts[i-1].y;
- x1 = (int)verts[i ].x;
- y1 = (int)verts[i ].y;
- if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
- float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
- winding += (y0 < y1) ? 1 : -1;
- }
- } else {
- int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
- if (num_hits >= 1)
- if (hits[0][0] < 0)
- winding += (hits[0][1] < 0 ? -1 : 1);
- if (num_hits >= 2)
- if (hits[1][0] < 0)
- winding += (hits[1][1] < 0 ? -1 : 1);
- }
- }
- }
- }
- return winding;
-}
-
-static float stbtt__cuberoot( float x )
-{
- if (x<0)
- return -(float) STBTT_pow(-x,1.0f/3.0f);
- else
- return (float) STBTT_pow( x,1.0f/3.0f);
-}
-
-// x^3 + c*x^2 + b*x + a = 0
-static int stbtt__solve_cubic(float a, float b, float c, float* r)
-{
- float s = -a / 3;
- float p = b - a*a / 3;
- float q = a * (2*a*a - 9*b) / 27 + c;
- float p3 = p*p*p;
- float d = q*q + 4*p3 / 27;
- if (d >= 0) {
- float z = (float) STBTT_sqrt(d);
- float u = (-q + z) / 2;
- float v = (-q - z) / 2;
- u = stbtt__cuberoot(u);
- v = stbtt__cuberoot(v);
- r[0] = s + u + v;
- return 1;
- } else {
- float u = (float) STBTT_sqrt(-p/3);
- float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
- float m = (float) STBTT_cos(v);
- float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
- r[0] = s + u * 2 * m;
- r[1] = s - u * (m + n);
- r[2] = s - u * (m - n);
-
- //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
- //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
- //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
- return 3;
- }
-}
-
-STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
-{
- float scale_x = scale, scale_y = scale;
- int ix0,iy0,ix1,iy1;
- int w,h;
- unsigned char *data;
-
- // if one scale is 0, use same scale for both
- if (scale_x == 0) scale_x = scale_y;
- if (scale_y == 0) {
- if (scale_x == 0) return NULL; // if both scales are 0, return NULL
- scale_y = scale_x;
- }
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
-
- // if empty, return NULL
- if (ix0 == ix1 || iy0 == iy1)
- return NULL;
-
- ix0 -= padding;
- iy0 -= padding;
- ix1 += padding;
- iy1 += padding;
-
- w = (ix1 - ix0);
- h = (iy1 - iy0);
-
- if (width ) *width = w;
- if (height) *height = h;
- if (xoff ) *xoff = ix0;
- if (yoff ) *yoff = iy0;
-
- // invert for y-downwards bitmaps
- scale_y = -scale_y;
-
- {
- int x,y,i,j;
- float *precompute;
- stbtt_vertex *verts;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
- data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
- precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
-
- for (i=0,j=num_verts-1; i < num_verts; j=i++) {
- if (verts[i].type == STBTT_vline) {
- float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
- float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
- float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
- precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
- } else if (verts[i].type == STBTT_vcurve) {
- float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
- float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
- float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
- float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
- float len2 = bx*bx + by*by;
- if (len2 != 0.0f)
- precompute[i] = 1.0f / (bx*bx + by*by);
- else
- precompute[i] = 0.0f;
- } else
- precompute[i] = 0.0f;
- }
-
- for (y=iy0; y < iy1; ++y) {
- for (x=ix0; x < ix1; ++x) {
- float val;
- float min_dist = 999999.0f;
- float sx = (float) x + 0.5f;
- float sy = (float) y + 0.5f;
- float x_gspace = (sx / scale_x);
- float y_gspace = (sy / scale_y);
-
- int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
-
- for (i=0; i < num_verts; ++i) {
- float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
-
- // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve
- float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
- if (dist2 < min_dist*min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
-
- if (verts[i].type == STBTT_vline) {
- float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
-
- // coarse culling against bbox
- //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
- // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
- float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
- STBTT_assert(i != 0);
- if (dist < min_dist) {
- // check position along line
- // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
- // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
- float dx = x1-x0, dy = y1-y0;
- float px = x0-sx, py = y0-sy;
- // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
- // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
- float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
- if (t >= 0.0f && t <= 1.0f)
- min_dist = dist;
- }
- } else if (verts[i].type == STBTT_vcurve) {
- float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
- float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y;
- float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
- float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
- float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
- float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
- // coarse culling against bbox to avoid computing cubic unnecessarily
- if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
- int num=0;
- float ax = x1-x0, ay = y1-y0;
- float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
- float mx = x0 - sx, my = y0 - sy;
- float res[3],px,py,t,it;
- float a_inv = precompute[i];
- if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
- float a = 3*(ax*bx + ay*by);
- float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
- float c = mx*ax+my*ay;
- if (a == 0.0) { // if a is 0, it's linear
- if (b != 0.0) {
- res[num++] = -c/b;
- }
- } else {
- float discriminant = b*b - 4*a*c;
- if (discriminant < 0)
- num = 0;
- else {
- float root = (float) STBTT_sqrt(discriminant);
- res[0] = (-b - root)/(2*a);
- res[1] = (-b + root)/(2*a);
- num = 2; // don't bother distinguishing 1-solution case, as code below will still work
- }
- }
- } else {
- float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
- float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
- float d = (mx*ax+my*ay) * a_inv;
- num = stbtt__solve_cubic(b, c, d, res);
- }
- if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
- t = res[0], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
- t = res[1], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
- t = res[2], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- }
- }
- }
- if (winding == 0)
- min_dist = -min_dist; // if outside the shape, value is negative
- val = onedge_value + pixel_dist_scale * min_dist;
- if (val < 0)
- val = 0;
- else if (val > 255)
- val = 255;
- data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
- }
- }
- STBTT_free(precompute, info->userdata);
- STBTT_free(verts, info->userdata);
- }
- return data;
-}
-
-STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
-}
-
-STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
-{
- STBTT_free(bitmap, userdata);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// font name matching -- recommended not to use this
-//
-
-// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
-{
- stbtt_int32 i=0;
-
- // convert utf16 to utf8 and compare the results while converting
- while (len2) {
- stbtt_uint16 ch = s2[0]*256 + s2[1];
- if (ch < 0x80) {
- if (i >= len1) return -1;
- if (s1[i++] != ch) return -1;
- } else if (ch < 0x800) {
- if (i+1 >= len1) return -1;
- if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
- } else if (ch >= 0xd800 && ch < 0xdc00) {
- stbtt_uint32 c;
- stbtt_uint16 ch2 = s2[2]*256 + s2[3];
- if (i+3 >= len1) return -1;
- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
- if (s1[i++] != 0xf0 + (c >> 18)) return -1;
- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
- s2 += 2; // plus another 2 below
- len2 -= 2;
- } else if (ch >= 0xdc00 && ch < 0xe000) {
- return -1;
- } else {
- if (i+2 >= len1) return -1;
- if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
- }
- s2 += 2;
- len2 -= 2;
- }
- return i;
-}
-
-static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
-{
- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
-}
-
-// returns results in whatever encoding you request... but note that 2-byte encodings
-// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
-STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
-{
- stbtt_int32 i,count,stringOffset;
- stbtt_uint8 *fc = font->data;
- stbtt_uint32 offset = font->fontstart;
- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
- if (!nm) return NULL;
-
- count = ttUSHORT(fc+nm+2);
- stringOffset = nm + ttUSHORT(fc+nm+4);
- for (i=0; i < count; ++i) {
- stbtt_uint32 loc = nm + 6 + 12 * i;
- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
- *length = ttUSHORT(fc+loc+8);
- return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
- }
- }
- return NULL;
-}
-
-static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
-{
- stbtt_int32 i;
- stbtt_int32 count = ttUSHORT(fc+nm+2);
- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
-
- for (i=0; i < count; ++i) {
- stbtt_uint32 loc = nm + 6 + 12 * i;
- stbtt_int32 id = ttUSHORT(fc+loc+6);
- if (id == target_id) {
- // find the encoding
- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
-
- // is this a Unicode encoding?
- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
- stbtt_int32 slen = ttUSHORT(fc+loc+8);
- stbtt_int32 off = ttUSHORT(fc+loc+10);
-
- // check if there's a prefix match
- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
- if (matchlen >= 0) {
- // check for target_id+1 immediately following, with same encoding & language
- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
- slen = ttUSHORT(fc+loc+12+8);
- off = ttUSHORT(fc+loc+12+10);
- if (slen == 0) {
- if (matchlen == nlen)
- return 1;
- } else if (matchlen < nlen && name[matchlen] == ' ') {
- ++matchlen;
- if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
- return 1;
- }
- } else {
- // if nothing immediately following
- if (matchlen == nlen)
- return 1;
- }
- }
- }
-
- // @TODO handle other encodings
- }
- }
- return 0;
-}
-
-static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
-{
- stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
- stbtt_uint32 nm,hd;
- if (!stbtt__isfont(fc+offset)) return 0;
-
- // check italics/bold/underline flags in macStyle...
- if (flags) {
- hd = stbtt__find_table(fc, offset, "head");
- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
- }
-
- nm = stbtt__find_table(fc, offset, "name");
- if (!nm) return 0;
-
- if (flags) {
- // if we checked the macStyle flags, then just check the family and ignore the subfamily
- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
- } else {
- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
- }
-
- return 0;
-}
-
-static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
-{
- stbtt_int32 i;
- for (i=0;;++i) {
- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
- if (off < 0) return off;
- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
- return off;
- }
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#endif
-
-STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
- float pixel_height, unsigned char *pixels, int pw, int ph,
- int first_char, int num_chars, stbtt_bakedchar *chardata)
-{
- return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
-}
-
-STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
-{
- return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
-}
-
-STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
-{
- return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
-}
-
-STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
-{
- return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
-}
-
-STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
-{
- return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
-}
-
-STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
-{
- return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif // STB_TRUETYPE_IMPLEMENTATION
-
-
-// FULL VERSION HISTORY
-//
-// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
-// 1.18 (2018-01-29) add missing function
-// 1.17 (2017-07-23) make more arguments const; doc fix
-// 1.16 (2017-07-12) SDF support
-// 1.15 (2017-03-03) make more arguments const
-// 1.14 (2017-01-16) num-fonts-in-TTC function
-// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
-// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
-// 1.11 (2016-04-02) fix unused-variable warning
-// 1.10 (2016-04-02) allow user-defined fabs() replacement
-// fix memory leak if fontsize=0.0
-// fix warning from duplicate typedef
-// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
-// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
-// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
-// allow PackFontRanges to pack and render in separate phases;
-// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
-// fixed an assert() bug in the new rasterizer
-// replace assert() with STBTT_assert() in new rasterizer
-// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
-// also more precise AA rasterizer, except if shapes overlap
-// remove need for STBTT_sort
-// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
-// 1.04 (2015-04-15) typo in example
-// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
-// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
-// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
-// non-oversampled; STBTT_POINT_SIZE for packed case only
-// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
-// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
-// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID
-// 0.8b (2014-07-07) fix a warning
-// 0.8 (2014-05-25) fix a few more warnings
-// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
-// 0.6c (2012-07-24) improve documentation
-// 0.6b (2012-07-20) fix a few more warnings
-// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
-// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
-// 0.5 (2011-12-09) bugfixes:
-// subpixel glyph renderer computed wrong bounding box
-// first vertex of shape can be off-curve (FreeSans)
-// 0.4b (2011-12-03) fixed an error in the font baking example
-// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
-// bugfixes for:
-// codepoint-to-glyph conversion using table fmt=12
-// codepoint-to-glyph conversion using table fmt=4
-// stbtt_GetBakedQuad with non-square texture (Zer)
-// updated Hello World! sample to use kerning and subpixel
-// fixed some warnings
-// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
-// userdata, malloc-from-userdata, non-zero fill (stb)
-// 0.2 (2009-03-11) Fix unsigned/signed char warnings
-// 0.1 (2009-03-09) First public release
-//
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2017 Sean Barrett
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
-*/
diff --git a/src/external/utf8.h b/src/external/utf8.h
deleted file mode 100644
index 1554884..0000000
--- a/src/external/utf8.h
+++ /dev/null
@@ -1,1258 +0,0 @@
-// The latest version of this library is available on GitHub;
-// https://github.com/sheredom/utf8.h
-
-// This is free and unencumbered software released into the public domain.
-//
-// Anyone is free to copy, modify, publish, use, compile, sell, or
-// distribute this software, either in source code form or as a compiled
-// binary, for any purpose, commercial or non-commercial, and by any
-// means.
-//
-// In jurisdictions that recognize copyright laws, the author or authors
-// of this software dedicate any and all copyright interest in the
-// software to the public domain. We make this dedication for the benefit
-// of the public at large and to the detriment of our heirs and
-// successors. We intend this dedication to be an overt act of
-// relinquishment in perpetuity of all present and future rights to this
-// software under copyright law.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-//
-// For more information, please refer to <http://unlicense.org/>
-
-#ifndef SHEREDOM_UTF8_H_INCLUDED
-#define SHEREDOM_UTF8_H_INCLUDED
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-
-// disable 'bytes padding added after construct' warning
-#pragma warning(disable : 4820)
-#endif
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if defined(_MSC_VER)
-typedef __int32 utf8_int32_t;
-#else
-#include <stdint.h>
-typedef int32_t utf8_int32_t;
-#endif
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#pragma clang diagnostic ignored "-Wcast-qual"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define utf8_nonnull __attribute__((nonnull))
-#define utf8_pure __attribute__((pure))
-#define utf8_restrict __restrict__
-#define utf8_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define utf8_nonnull
-#define utf8_pure
-#define utf8_restrict __restrict
-#define utf8_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __cplusplus
-#define utf8_null NULL
-#else
-#define utf8_null 0
-#endif
-
- // Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
- // src2 respectively, case insensitive.
- utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1,
- const void *src2);
-
- // Append the utf8 string src onto the utf8 string dst.
- utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst,
- const void *utf8_restrict src);
-
- // Find the first match of the utf8 codepoint chr in the utf8 string src.
- utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src,
- utf8_int32_t chr);
-
- // Return less than 0, 0, greater than 0 if src1 < src2,
- // src1 == src2, src1 > src2 respectively.
- utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1,
- const void *src2);
-
- // Copy the utf8 string src onto the memory allocated in dst.
- utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst,
- const void *utf8_restrict src);
-
- // Number of utf8 codepoints in the utf8 string src that consists entirely
- // of utf8 codepoints not from the utf8 string reject.
- utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src,
- const void *reject);
-
- // Duplicate the utf8 string src by getting its size, malloc'ing a new buffer
- // copying over the data, and returning that. Or 0 if malloc failed.
- utf8_nonnull utf8_weak void *utf8dup(const void *src);
-
- // Number of utf8 codepoints in the utf8 string str,
- // excluding the null terminating byte.
- utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str);
-
- // Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
- // src2 respectively, case insensitive. Checking at most n bytes of each utf8
- // string.
- utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1,
- const void *src2, size_t n);
-
- // Append the utf8 string src onto the utf8 string dst,
- // writing at most n+1 bytes. Can produce an invalid utf8
- // string if n falls partway through a utf8 codepoint.
- utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst,
- const void *utf8_restrict src, size_t n);
-
- // Return less than 0, 0, greater than 0 if src1 < src2,
- // src1 == src2, src1 > src2 respectively. Checking at most n
- // bytes of each utf8 string.
- utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1,
- const void *src2, size_t n);
-
- // Copy the utf8 string src onto the memory allocated in dst.
- // Copies at most n bytes. If there is no terminating null byte in
- // the first n bytes of src, the string placed into dst will not be
- // null-terminated. If the size (in bytes) of src is less than n,
- // extra null terminating bytes are appended to dst such that at
- // total of n bytes are written. Can produce an invalid utf8
- // string if n falls partway through a utf8 codepoint.
- utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst,
- const void *utf8_restrict src, size_t n);
-
- // Similar to utf8dup, except that at most n bytes of src are copied. If src is
- // longer than n, only n bytes are copied and a null byte is added.
- //
- // Returns a new string if successful, 0 otherwise
- utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n);
-
- // Locates the first occurence in the utf8 string str of any byte in the
- // utf8 string accept, or 0 if no match was found.
- utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str,
- const void *accept);
-
- // Find the last match of the utf8 codepoint chr in the utf8 string src.
- utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr);
-
- // Number of bytes in the utf8 string str,
- // including the null terminating byte.
- utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str);
-
- // Number of utf8 codepoints in the utf8 string src that consists entirely
- // of utf8 codepoints from the utf8 string accept.
- utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src,
- const void *accept);
-
- // The position of the utf8 string needle in the utf8 string haystack.
- utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack,
- const void *needle);
-
- // The position of the utf8 string needle in the utf8 string haystack, case
- // insensitive.
- utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack,
- const void *needle);
-
- // Return 0 on success, or the position of the invalid
- // utf8 codepoint on failure.
- utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str);
-
- // Sets out_codepoint to the next utf8 codepoint in str, and returns the address
- // of the utf8 codepoint after the current one in str.
- utf8_nonnull utf8_weak void *
- utf8codepoint(const void *utf8_restrict str,
- utf8_int32_t *utf8_restrict out_codepoint);
-
- // Returns the size of the given codepoint in bytes.
- utf8_weak size_t utf8codepointsize(utf8_int32_t chr);
-
- // Write a codepoint to the given string, and return the address to the next
- // place after the written codepoint. Pass how many bytes left in the buffer to
- // n. If there is not enough space for the codepoint, this function returns
- // null.
- utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str,
- utf8_int32_t chr, size_t n);
-
- // Returns 1 if the given character is lowercase, or 0 if it is not.
- utf8_weak int utf8islower(utf8_int32_t chr);
-
- // Returns 1 if the given character is uppercase, or 0 if it is not.
- utf8_weak int utf8isupper(utf8_int32_t chr);
-
- // Transform the given string into all lowercase codepoints.
- utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str);
-
- // Transform the given string into all uppercase codepoints.
- utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str);
-
- // Make a codepoint lower case if possible.
- utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp);
-
- // Make a codepoint upper case if possible.
- utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp);
-
-#undef utf8_weak
-#undef utf8_pure
-#undef utf8_nonnull
-
- int utf8casecmp(const void *src1, const void *src2) {
- utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
-
- for (;;) {
- src1 = utf8codepoint(src1, &src1_cp);
- src2 = utf8codepoint(src2, &src2_cp);
-
- // take a copy of src1 & src2
- src1_orig_cp = src1_cp;
- src2_orig_cp = src2_cp;
-
- // lower the srcs if required
- src1_cp = utf8lwrcodepoint(src1_cp);
- src2_cp = utf8lwrcodepoint(src2_cp);
-
- // check if the lowered codepoints match
- if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
- return 0;
- } else if (src1_cp == src2_cp) {
- continue;
- }
-
- // if they don't match, then we return the difference between the characters
- return src1_cp - src2_cp;
- }
- }
-
- void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src) {
- char *d = (char *)dst;
- const char *s = (const char *)src;
-
- // find the null terminating byte in dst
- while ('\0' != *d) {
- d++;
- }
-
- // overwriting the null terminating byte in dst, append src byte-by-byte
- while ('\0' != *s) {
- *d++ = *s++;
- }
-
- // write out a new null terminating byte into dst
- *d = '\0';
-
- return dst;
- }
-
- void *utf8chr(const void *src, utf8_int32_t chr) {
- char c[5] = {'\0', '\0', '\0', '\0', '\0'};
-
- if (0 == chr) {
- // being asked to return position of null terminating byte, so
- // just run s to the end, and return!
- const char *s = (const char *)src;
- while ('\0' != *s) {
- s++;
- }
- return (void *)s;
- } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
- // 1-byte/7-bit ascii
- // (0b0xxxxxxx)
- c[0] = (char)chr;
- } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
- // 2-byte/11-bit utf8 code point
- // (0b110xxxxx 0b10xxxxxx)
- c[0] = 0xc0 | (char)(chr >> 6);
- c[1] = 0x80 | (char)(chr & 0x3f);
- } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
- // 3-byte/16-bit utf8 code point
- // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
- c[0] = 0xe0 | (char)(chr >> 12);
- c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
- c[2] = 0x80 | (char)(chr & 0x3f);
- } else { // if (0 == ((int)0xffe00000 & chr)) {
- // 4-byte/21-bit utf8 code point
- // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
- c[0] = 0xf0 | (char)(chr >> 18);
- c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
- c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
- c[3] = 0x80 | (char)(chr & 0x3f);
- }
-
- // we've made c into a 2 utf8 codepoint string, one for the chr we are
- // seeking, another for the null terminating byte. Now use utf8str to
- // search
- return utf8str(src, c);
- }
-
- int utf8cmp(const void *src1, const void *src2) {
- const unsigned char *s1 = (const unsigned char *)src1;
- const unsigned char *s2 = (const unsigned char *)src2;
-
- while (('\0' != *s1) || ('\0' != *s2)) {
- if (*s1 < *s2) {
- return -1;
- } else if (*s1 > *s2) {
- return 1;
- }
-
- s1++;
- s2++;
- }
-
- // both utf8 strings matched
- return 0;
- }
-
- int utf8coll(const void *src1, const void *src2);
-
- void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src) {
- char *d = (char *)dst;
- const char *s = (const char *)src;
-
- // overwriting anything previously in dst, write byte-by-byte
- // from src
- while ('\0' != *s) {
- *d++ = *s++;
- }
-
- // append null terminating byte
- *d = '\0';
-
- return dst;
- }
-
- size_t utf8cspn(const void *src, const void *reject) {
- const char *s = (const char *)src;
- size_t chars = 0;
-
- while ('\0' != *s) {
- const char *r = (const char *)reject;
- size_t offset = 0;
-
- while ('\0' != *r) {
- // checking that if *r is the start of a utf8 codepoint
- // (it is not 0b10xxxxxx) and we have successfully matched
- // a previous character (0 < offset) - we found a match
- if ((0x80 != (0xc0 & *r)) && (0 < offset)) {
- return chars;
- } else {
- if (*r == s[offset]) {
- // part of a utf8 codepoint matched, so move our checking
- // onwards to the next byte
- offset++;
- r++;
- } else {
- // r could be in the middle of an unmatching utf8 code point,
- // so we need to march it on to the next character beginning,
-
- do {
- r++;
- } while (0x80 == (0xc0 & *r));
-
- // reset offset too as we found a mismatch
- offset = 0;
- }
- }
- }
-
- // the current utf8 codepoint in src did not match reject, but src
- // could have been partway through a utf8 codepoint, so we need to
- // march it onto the next utf8 codepoint starting byte
- do {
- s++;
- } while ((0x80 == (0xc0 & *s)));
- chars++;
- }
-
- return chars;
- }
-
- size_t utf8size(const void *str);
-
- void *utf8dup(const void *src) {
- const char *s = (const char *)src;
- char *n = utf8_null;
-
- // figure out how many bytes (including the terminator) we need to copy first
- size_t bytes = utf8size(src);
-
- n = (char *)malloc(bytes);
-
- if (utf8_null == n) {
- // out of memory so we bail
- return utf8_null;
- } else {
- bytes = 0;
-
- // copy src byte-by-byte into our new utf8 string
- while ('\0' != s[bytes]) {
- n[bytes] = s[bytes];
- bytes++;
- }
-
- // append null terminating byte
- n[bytes] = '\0';
- return n;
- }
- }
-
- void *utf8fry(const void *str);
-
- size_t utf8len(const void *str) {
- const unsigned char *s = (const unsigned char *)str;
- size_t length = 0;
-
- while ('\0' != *s) {
- if (0xf0 == (0xf8 & *s)) {
- // 4-byte utf8 code point (began with 0b11110xxx)
- s += 4;
- } else if (0xe0 == (0xf0 & *s)) {
- // 3-byte utf8 code point (began with 0b1110xxxx)
- s += 3;
- } else if (0xc0 == (0xe0 & *s)) {
- // 2-byte utf8 code point (began with 0b110xxxxx)
- s += 2;
- } else { // if (0x00 == (0x80 & *s)) {
- // 1-byte ascii (began with 0b0xxxxxxx)
- s += 1;
- }
-
- // no matter the bytes we marched s forward by, it was
- // only 1 utf8 codepoint
- length++;
- }
-
- return length;
- }
-
- int utf8ncasecmp(const void *src1, const void *src2, size_t n) {
- utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
-
- do {
- const unsigned char *const s1 = (const unsigned char *)src1;
- const unsigned char *const s2 = (const unsigned char *)src2;
-
- // first check that we have enough bytes left in n to contain an entire
- // codepoint
- if (0 == n) {
- return 0;
- }
-
- if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) {
- const utf8_int32_t c1 = (0xe0 & *s1);
- const utf8_int32_t c2 = (0xe0 & *s2);
-
- if (c1 < c2) {
- return -1;
- } else if (c1 > c2) {
- return 1;
- } else {
- return 0;
- }
- }
-
- if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) {
- const utf8_int32_t c1 = (0xf0 & *s1);
- const utf8_int32_t c2 = (0xf0 & *s2);
-
- if (c1 < c2) {
- return -1;
- } else if (c1 > c2) {
- return 1;
- } else {
- return 0;
- }
- }
-
- if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) {
- const utf8_int32_t c1 = (0xf8 & *s1);
- const utf8_int32_t c2 = (0xf8 & *s2);
-
- if (c1 < c2) {
- return -1;
- } else if (c1 > c2) {
- return 1;
- } else {
- return 0;
- }
- }
-
- src1 = utf8codepoint(src1, &src1_cp);
- src2 = utf8codepoint(src2, &src2_cp);
- n -= utf8codepointsize(src1_cp);
-
- // Take a copy of src1 & src2
- src1_orig_cp = src1_cp;
- src2_orig_cp = src2_cp;
-
- // Lower srcs if required
- src1_cp = utf8lwrcodepoint(src1_cp);
- src2_cp = utf8lwrcodepoint(src2_cp);
-
- // Check if the lowered codepoints match
- if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
- return 0;
- } else if (src1_cp == src2_cp) {
- continue;
- }
-
- // If they don't match, then we return which of the original's are less
- if (src1_orig_cp < src2_orig_cp) {
- return -1;
- } else if (src1_orig_cp > src2_orig_cp) {
- return 1;
- }
- } while (0 < n);
-
- // both utf8 strings matched
- return 0;
- }
-
- void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src,
- size_t n) {
- char *d = (char *)dst;
- const char *s = (const char *)src;
-
- // find the null terminating byte in dst
- while ('\0' != *d) {
- d++;
- }
-
- // overwriting the null terminating byte in dst, append src byte-by-byte
- // stopping if we run out of space
- do {
- *d++ = *s++;
- } while (('\0' != *s) && (0 != --n));
-
- // write out a new null terminating byte into dst
- *d = '\0';
-
- return dst;
- }
-
- int utf8ncmp(const void *src1, const void *src2, size_t n) {
- const unsigned char *s1 = (const unsigned char *)src1;
- const unsigned char *s2 = (const unsigned char *)src2;
-
- while ((0 != n--) && (('\0' != *s1) || ('\0' != *s2))) {
- if (*s1 < *s2) {
- return -1;
- } else if (*s1 > *s2) {
- return 1;
- }
-
- s1++;
- s2++;
- }
-
- // both utf8 strings matched
- return 0;
- }
-
- void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src,
- size_t n) {
- char *d = (char *)dst;
- const char *s = (const char *)src;
- size_t index;
-
- // overwriting anything previously in dst, write byte-by-byte
- // from src
- for (index = 0; index < n; index++) {
- d[index] = s[index];
- if ('\0' == s[index]) {
- break;
- }
- }
-
- // append null terminating byte
- for (; index < n; index++) {
- d[index] = 0;
- }
-
- return dst;
- }
-
- void *utf8ndup(const void *src, size_t n) {
- const char *s = (const char *)src;
- char *c = utf8_null;
- size_t bytes = 0;
-
- // Find the end of the string or stop when n is reached
- while ('\0' != s[bytes] && bytes < n) {
- bytes++;
- }
-
- // In case bytes is actually less than n, we need to set it
- // to be used later in the copy byte by byte.
- n = bytes;
-
- c = (char *)malloc(bytes + 1);
- if (utf8_null == c) {
- // out of memory so we bail
- return utf8_null;
- }
-
- bytes = 0;
-
- // copy src byte-by-byte into our new utf8 string
- while ('\0' != s[bytes] && bytes < n) {
- c[bytes] = s[bytes];
- bytes++;
- }
-
- // append null terminating byte
- c[bytes] = '\0';
- return c;
- }
-
- void *utf8rchr(const void *src, int chr) {
- const char *s = (const char *)src;
- const char *match = utf8_null;
- char c[5] = {'\0', '\0', '\0', '\0', '\0'};
-
- if (0 == chr) {
- // being asked to return position of null terminating byte, so
- // just run s to the end, and return!
- while ('\0' != *s) {
- s++;
- }
- return (void *)s;
- } else if (0 == ((int)0xffffff80 & chr)) {
- // 1-byte/7-bit ascii
- // (0b0xxxxxxx)
- c[0] = (char)chr;
- } else if (0 == ((int)0xfffff800 & chr)) {
- // 2-byte/11-bit utf8 code point
- // (0b110xxxxx 0b10xxxxxx)
- c[0] = 0xc0 | (char)(chr >> 6);
- c[1] = 0x80 | (char)(chr & 0x3f);
- } else if (0 == ((int)0xffff0000 & chr)) {
- // 3-byte/16-bit utf8 code point
- // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
- c[0] = 0xe0 | (char)(chr >> 12);
- c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
- c[2] = 0x80 | (char)(chr & 0x3f);
- } else { // if (0 == ((int)0xffe00000 & chr)) {
- // 4-byte/21-bit utf8 code point
- // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
- c[0] = 0xf0 | (char)(chr >> 18);
- c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
- c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
- c[3] = 0x80 | (char)(chr & 0x3f);
- }
-
- // we've created a 2 utf8 codepoint string in c that is
- // the utf8 character asked for by chr, and a null
- // terminating byte
-
- while ('\0' != *s) {
- size_t offset = 0;
-
- while (s[offset] == c[offset]) {
- offset++;
- }
-
- if ('\0' == c[offset]) {
- // we found a matching utf8 code point
- match = s;
- s += offset;
- } else {
- s += offset;
-
- // need to march s along to next utf8 codepoint start
- // (the next byte that doesn't match 0b10xxxxxx)
- if ('\0' != *s) {
- do {
- s++;
- } while (0x80 == (0xc0 & *s));
- }
- }
- }
-
- // return the last match we found (or 0 if no match was found)
- return (void *)match;
- }
-
- void *utf8pbrk(const void *str, const void *accept) {
- const char *s = (const char *)str;
-
- while ('\0' != *s) {
- const char *a = (const char *)accept;
- size_t offset = 0;
-
- while ('\0' != *a) {
- // checking that if *a is the start of a utf8 codepoint
- // (it is not 0b10xxxxxx) and we have successfully matched
- // a previous character (0 < offset) - we found a match
- if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
- return (void *)s;
- } else {
- if (*a == s[offset]) {
- // part of a utf8 codepoint matched, so move our checking
- // onwards to the next byte
- offset++;
- a++;
- } else {
- // r could be in the middle of an unmatching utf8 code point,
- // so we need to march it on to the next character beginning,
-
- do {
- a++;
- } while (0x80 == (0xc0 & *a));
-
- // reset offset too as we found a mismatch
- offset = 0;
- }
- }
- }
-
- // we found a match on the last utf8 codepoint
- if (0 < offset) {
- return (void *)s;
- }
-
- // the current utf8 codepoint in src did not match accept, but src
- // could have been partway through a utf8 codepoint, so we need to
- // march it onto the next utf8 codepoint starting byte
- do {
- s++;
- } while ((0x80 == (0xc0 & *s)));
- }
-
- return utf8_null;
- }
-
- size_t utf8size(const void *str) {
- const char *s = (const char *)str;
- size_t size = 0;
- while ('\0' != s[size]) {
- size++;
- }
-
- // we are including the null terminating byte in the size calculation
- size++;
- return size;
- }
-
- size_t utf8spn(const void *src, const void *accept) {
- const char *s = (const char *)src;
- size_t chars = 0;
-
- while ('\0' != *s) {
- const char *a = (const char *)accept;
- size_t offset = 0;
-
- while ('\0' != *a) {
- // checking that if *r is the start of a utf8 codepoint
- // (it is not 0b10xxxxxx) and we have successfully matched
- // a previous character (0 < offset) - we found a match
- if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
- // found a match, so increment the number of utf8 codepoints
- // that have matched and stop checking whether any other utf8
- // codepoints in a match
- chars++;
- s += offset;
- break;
- } else {
- if (*a == s[offset]) {
- offset++;
- a++;
- } else {
- // a could be in the middle of an unmatching utf8 codepoint,
- // so we need to march it on to the next character beginning,
- do {
- a++;
- } while (0x80 == (0xc0 & *a));
-
- // reset offset too as we found a mismatch
- offset = 0;
- }
- }
- }
-
- // if a got to its terminating null byte, then we didn't find a match.
- // Return the current number of matched utf8 codepoints
- if ('\0' == *a) {
- return chars;
- }
- }
-
- return chars;
- }
-
- void *utf8str(const void *haystack, const void *needle) {
- const char *h = (const char *)haystack;
- utf8_int32_t throwaway_codepoint;
-
- // if needle has no utf8 codepoints before the null terminating
- // byte then return haystack
- if ('\0' == *((const char *)needle)) {
- return (void *)haystack;
- }
-
- while ('\0' != *h) {
- const char *maybeMatch = h;
- const char *n = (const char *)needle;
-
- while (*h == *n && (*h != '\0' && *n != '\0')) {
- n++;
- h++;
- }
-
- if ('\0' == *n) {
- // we found the whole utf8 string for needle in haystack at
- // maybeMatch, so return it
- return (void *)maybeMatch;
- } else {
- // h could be in the middle of an unmatching utf8 codepoint,
- // so we need to march it on to the next character beginning
- // starting from the current character
- h = (const char*)utf8codepoint(maybeMatch, &throwaway_codepoint);
- }
- }
-
- // no match
- return utf8_null;
- }
-
- void *utf8casestr(const void *haystack, const void *needle) {
- const void *h = haystack;
-
- // if needle has no utf8 codepoints before the null terminating
- // byte then return haystack
- if ('\0' == *((const char *)needle)) {
- return (void *)haystack;
- }
-
- for (;;) {
- const void *maybeMatch = h;
- const void *n = needle;
- utf8_int32_t h_cp, n_cp;
-
- // Get the next code point and track it
- const void *nextH = h = utf8codepoint(h, &h_cp);
- n = utf8codepoint(n, &n_cp);
-
- while ((0 != h_cp) && (0 != n_cp)) {
- h_cp = utf8lwrcodepoint(h_cp);
- n_cp = utf8lwrcodepoint(n_cp);
-
- // if we find a mismatch, bail out!
- if (h_cp != n_cp) {
- break;
- }
-
- h = utf8codepoint(h, &h_cp);
- n = utf8codepoint(n, &n_cp);
- }
-
- if (0 == n_cp) {
- // we found the whole utf8 string for needle in haystack at
- // maybeMatch, so return it
- return (void *)maybeMatch;
- }
-
- if (0 == h_cp) {
- // no match
- return utf8_null;
- }
-
- // Roll back to the next code point in the haystack to test
- h = nextH;
- }
- }
-
- void *utf8valid(const void *str) {
- const char *s = (const char *)str;
-
- while ('\0' != *s) {
- if (0xf0 == (0xf8 & *s)) {
- // ensure each of the 3 following bytes in this 4-byte
- // utf8 codepoint began with 0b10xxxxxx
- if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) ||
- (0x80 != (0xc0 & s[3]))) {
- return (void *)s;
- }
-
- // ensure that our utf8 codepoint ended after 4 bytes
- if (0x80 == (0xc0 & s[4])) {
- return (void *)s;
- }
-
- // ensure that the top 5 bits of this 4-byte utf8
- // codepoint were not 0, as then we could have used
- // one of the smaller encodings
- if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) {
- return (void *)s;
- }
-
- // 4-byte utf8 code point (began with 0b11110xxx)
- s += 4;
- } else if (0xe0 == (0xf0 & *s)) {
- // ensure each of the 2 following bytes in this 3-byte
- // utf8 codepoint began with 0b10xxxxxx
- if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) {
- return (void *)s;
- }
-
- // ensure that our utf8 codepoint ended after 3 bytes
- if (0x80 == (0xc0 & s[3])) {
- return (void *)s;
- }
-
- // ensure that the top 5 bits of this 3-byte utf8
- // codepoint were not 0, as then we could have used
- // one of the smaller encodings
- if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) {
- return (void *)s;
- }
-
- // 3-byte utf8 code point (began with 0b1110xxxx)
- s += 3;
- } else if (0xc0 == (0xe0 & *s)) {
- // ensure the 1 following byte in this 2-byte
- // utf8 codepoint began with 0b10xxxxxx
- if (0x80 != (0xc0 & s[1])) {
- return (void *)s;
- }
-
- // ensure that our utf8 codepoint ended after 2 bytes
- if (0x80 == (0xc0 & s[2])) {
- return (void *)s;
- }
-
- // ensure that the top 4 bits of this 2-byte utf8
- // codepoint were not 0, as then we could have used
- // one of the smaller encodings
- if (0 == (0x1e & s[0])) {
- return (void *)s;
- }
-
- // 2-byte utf8 code point (began with 0b110xxxxx)
- s += 2;
- } else if (0x00 == (0x80 & *s)) {
- // 1-byte ascii (began with 0b0xxxxxxx)
- s += 1;
- } else {
- // we have an invalid 0b1xxxxxxx utf8 code point entry
- return (void *)s;
- }
- }
-
- return utf8_null;
- }
-
- void *utf8codepoint(const void *utf8_restrict str,
- utf8_int32_t *utf8_restrict out_codepoint) {
- const char *s = (const char *)str;
-
- if (0xf0 == (0xf8 & s[0])) {
- // 4 byte utf8 codepoint
- *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
- ((0x3f & s[2]) << 6) | (0x3f & s[3]);
- s += 4;
- } else if (0xe0 == (0xf0 & s[0])) {
- // 3 byte utf8 codepoint
- *out_codepoint =
- ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
- s += 3;
- } else if (0xc0 == (0xe0 & s[0])) {
- // 2 byte utf8 codepoint
- *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
- s += 2;
- } else {
- // 1 byte utf8 codepoint otherwise
- *out_codepoint = s[0];
- s += 1;
- }
-
- return (void *)s;
- }
-
- size_t utf8codepointsize(utf8_int32_t chr) {
- if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
- return 1;
- } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
- return 2;
- } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
- return 3;
- } else { // if (0 == ((int)0xffe00000 & chr)) {
- return 4;
- }
- }
-
- void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n) {
- char *s = (char *)str;
-
- if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
- // 1-byte/7-bit ascii
- // (0b0xxxxxxx)
- if (n < 1) {
- return utf8_null;
- }
- s[0] = (char)chr;
- s += 1;
- } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
- // 2-byte/11-bit utf8 code point
- // (0b110xxxxx 0b10xxxxxx)
- if (n < 2) {
- return utf8_null;
- }
- s[0] = 0xc0 | (char)(chr >> 6);
- s[1] = 0x80 | (char)(chr & 0x3f);
- s += 2;
- } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
- // 3-byte/16-bit utf8 code point
- // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
- if (n < 3) {
- return utf8_null;
- }
- s[0] = 0xe0 | (char)(chr >> 12);
- s[1] = 0x80 | (char)((chr >> 6) & 0x3f);
- s[2] = 0x80 | (char)(chr & 0x3f);
- s += 3;
- } else { // if (0 == ((int)0xffe00000 & chr)) {
- // 4-byte/21-bit utf8 code point
- // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
- if (n < 4) {
- return utf8_null;
- }
- s[0] = 0xf0 | (char)(chr >> 18);
- s[1] = 0x80 | (char)((chr >> 12) & 0x3f);
- s[2] = 0x80 | (char)((chr >> 6) & 0x3f);
- s[3] = 0x80 | (char)(chr & 0x3f);
- s += 4;
- }
-
- return s;
- }
-
- int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); }
-
- int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); }
-
- void utf8lwr(void *utf8_restrict str) {
- void *p, *pn;
- utf8_int32_t cp;
-
- p = (char *)str;
- pn = utf8codepoint(p, &cp);
-
- while (cp != 0) {
- const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp);
- const size_t size = utf8codepointsize(lwr_cp);
-
- if (lwr_cp != cp) {
- utf8catcodepoint(p, lwr_cp, size);
- }
-
- p = pn;
- pn = utf8codepoint(p, &cp);
- }
- }
-
- void utf8upr(void *utf8_restrict str) {
- void *p, *pn;
- utf8_int32_t cp;
-
- p = (char *)str;
- pn = utf8codepoint(p, &cp);
-
- while (cp != 0) {
- const utf8_int32_t lwr_cp = utf8uprcodepoint(cp);
- const size_t size = utf8codepointsize(lwr_cp);
-
- if (lwr_cp != cp) {
- utf8catcodepoint(p, lwr_cp, size);
- }
-
- p = pn;
- pn = utf8codepoint(p, &cp);
- }
- }
-
- utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) {
- if (((0x0041 <= cp) && (0x005a >= cp)) ||
- ((0x00c0 <= cp) && (0x00d6 >= cp)) ||
- ((0x00d8 <= cp) && (0x00de >= cp)) ||
- ((0x0391 <= cp) && (0x03a1 >= cp)) ||
- ((0x03a3 <= cp) && (0x03ab >= cp))) {
- cp += 32;
- } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
- ((0x0132 <= cp) && (0x0137 >= cp)) ||
- ((0x014a <= cp) && (0x0177 >= cp)) ||
- ((0x0182 <= cp) && (0x0185 >= cp)) ||
- ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
- ((0x01de <= cp) && (0x01ef >= cp)) ||
- ((0x01f8 <= cp) && (0x021f >= cp)) ||
- ((0x0222 <= cp) && (0x0233 >= cp)) ||
- ((0x0246 <= cp) && (0x024f >= cp)) ||
- ((0x03d8 <= cp) && (0x03ef >= cp))) {
- cp |= 0x1;
- } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
- ((0x0179 <= cp) && (0x017e >= cp)) ||
- ((0x01af <= cp) && (0x01b0 >= cp)) ||
- ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
- ((0x01cd <= cp) && (0x01dc >= cp))) {
- cp += 1;
- cp &= ~0x1;
- } else {
- switch (cp) {
- default: break;
- case 0x0178: cp = 0x00ff; break;
- case 0x0243: cp = 0x0180; break;
- case 0x018e: cp = 0x01dd; break;
- case 0x023d: cp = 0x019a; break;
- case 0x0220: cp = 0x019e; break;
- case 0x01b7: cp = 0x0292; break;
- case 0x01c4: cp = 0x01c6; break;
- case 0x01c7: cp = 0x01c9; break;
- case 0x01ca: cp = 0x01cc; break;
- case 0x01f1: cp = 0x01f3; break;
- case 0x01f7: cp = 0x01bf; break;
- case 0x0187: cp = 0x0188; break;
- case 0x018b: cp = 0x018c; break;
- case 0x0191: cp = 0x0192; break;
- case 0x0198: cp = 0x0199; break;
- case 0x01a7: cp = 0x01a8; break;
- case 0x01ac: cp = 0x01ad; break;
- case 0x01af: cp = 0x01b0; break;
- case 0x01b8: cp = 0x01b9; break;
- case 0x01bc: cp = 0x01bd; break;
- case 0x01f4: cp = 0x01f5; break;
- case 0x023b: cp = 0x023c; break;
- case 0x0241: cp = 0x0242; break;
- case 0x03fd: cp = 0x037b; break;
- case 0x03fe: cp = 0x037c; break;
- case 0x03ff: cp = 0x037d; break;
- case 0x037f: cp = 0x03f3; break;
- case 0x0386: cp = 0x03ac; break;
- case 0x0388: cp = 0x03ad; break;
- case 0x0389: cp = 0x03ae; break;
- case 0x038a: cp = 0x03af; break;
- case 0x038c: cp = 0x03cc; break;
- case 0x038e: cp = 0x03cd; break;
- case 0x038f: cp = 0x03ce; break;
- case 0x0370: cp = 0x0371; break;
- case 0x0372: cp = 0x0373; break;
- case 0x0376: cp = 0x0377; break;
- case 0x03f4: cp = 0x03d1; break;
- case 0x03cf: cp = 0x03d7; break;
- case 0x03f9: cp = 0x03f2; break;
- case 0x03f7: cp = 0x03f8; break;
- case 0x03fa: cp = 0x03fb; break;
- };
- }
-
- return cp;
- }
-
- utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) {
- if (((0x0061 <= cp) && (0x007a >= cp)) ||
- ((0x00e0 <= cp) && (0x00f6 >= cp)) ||
- ((0x00f8 <= cp) && (0x00fe >= cp)) ||
- ((0x03b1 <= cp) && (0x03c1 >= cp)) ||
- ((0x03c3 <= cp) && (0x03cb >= cp))) {
- cp -= 32;
- } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
- ((0x0132 <= cp) && (0x0137 >= cp)) ||
- ((0x014a <= cp) && (0x0177 >= cp)) ||
- ((0x0182 <= cp) && (0x0185 >= cp)) ||
- ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
- ((0x01de <= cp) && (0x01ef >= cp)) ||
- ((0x01f8 <= cp) && (0x021f >= cp)) ||
- ((0x0222 <= cp) && (0x0233 >= cp)) ||
- ((0x0246 <= cp) && (0x024f >= cp)) ||
- ((0x03d8 <= cp) && (0x03ef >= cp))) {
- cp &= ~0x1;
- } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
- ((0x0179 <= cp) && (0x017e >= cp)) ||
- ((0x01af <= cp) && (0x01b0 >= cp)) ||
- ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
- ((0x01cd <= cp) && (0x01dc >= cp))) {
- cp -= 1;
- cp |= 0x1;
- } else {
- switch (cp) {
- default: break;
- case 0x00ff: cp = 0x0178; break;
- case 0x0180: cp = 0x0243; break;
- case 0x01dd: cp = 0x018e; break;
- case 0x019a: cp = 0x023d; break;
- case 0x019e: cp = 0x0220; break;
- case 0x0292: cp = 0x01b7; break;
- case 0x01c6: cp = 0x01c4; break;
- case 0x01c9: cp = 0x01c7; break;
- case 0x01cc: cp = 0x01ca; break;
- case 0x01f3: cp = 0x01f1; break;
- case 0x01bf: cp = 0x01f7; break;
- case 0x0188: cp = 0x0187; break;
- case 0x018c: cp = 0x018b; break;
- case 0x0192: cp = 0x0191; break;
- case 0x0199: cp = 0x0198; break;
- case 0x01a8: cp = 0x01a7; break;
- case 0x01ad: cp = 0x01ac; break;
- case 0x01b0: cp = 0x01af; break;
- case 0x01b9: cp = 0x01b8; break;
- case 0x01bd: cp = 0x01bc; break;
- case 0x01f5: cp = 0x01f4; break;
- case 0x023c: cp = 0x023b; break;
- case 0x0242: cp = 0x0241; break;
- case 0x037b: cp = 0x03fd; break;
- case 0x037c: cp = 0x03fe; break;
- case 0x037d: cp = 0x03ff; break;
- case 0x03f3: cp = 0x037f; break;
- case 0x03ac: cp = 0x0386; break;
- case 0x03ad: cp = 0x0388; break;
- case 0x03ae: cp = 0x0389; break;
- case 0x03af: cp = 0x038a; break;
- case 0x03cc: cp = 0x038c; break;
- case 0x03cd: cp = 0x038e; break;
- case 0x03ce: cp = 0x038f; break;
- case 0x0371: cp = 0x0370; break;
- case 0x0373: cp = 0x0372; break;
- case 0x0377: cp = 0x0376; break;
- case 0x03d1: cp = 0x03f4; break;
- case 0x03d7: cp = 0x03cf; break;
- case 0x03f2: cp = 0x03f9; break;
- case 0x03f8: cp = 0x03f7; break;
- case 0x03fb: cp = 0x03fa; break;
- };
- }
-
- return cp;
- }
-
-#undef utf8_restrict
-#undef utf8_null
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#endif // SHEREDOM_UTF8_H_INCLUDED
diff --git a/src/input.c b/src/input.c
deleted file mode 100644
index 4ee795b..0000000
--- a/src/input.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-inline bool is_left_down(mouse_input *input)
-{
- return input->left_state & MOUSE_DOWN;
-}
-
-inline bool is_left_released(mouse_input *input)
-{
- return input->left_state & MOUSE_RELEASE;
-}
-
-inline bool is_left_clicked(mouse_input *input)
-{
- return input->left_state & MOUSE_CLICK;
-}
-
-inline bool is_left_double_clicked(mouse_input *input)
-{
- return input->left_state & MOUSE_DOUBLE_CLICK;
-}
-
-inline bool is_right_down(mouse_input *input)
-{
- return input->right_state & MOUSE_DOWN;
-}
-
-inline bool is_right_released(mouse_input *input)
-{
- return input->right_state & MOUSE_RELEASE;
-}
-
-inline bool is_right_clicked(mouse_input *input)
-{
- return input->right_state & MOUSE_CLICK;
-}
-
-inline bool keyboard_is_key_down(keyboard_input *keyboard, s16 key)
-{
- return keyboard->keys[key];
-}
-
-inline bool keyboard_is_key_pressed(keyboard_input *keyboard, s16 key)
-{
- return keyboard->input_keys[key];
-}
-
-inline void keyboard_set_input_text(keyboard_input *keyboard, char *text)
-{
- string_copyn(keyboard->input_text, text, MAX_INPUT_LENGTH);
- u32 len = utf8len(keyboard->input_text);
- keyboard->cursor = len;
- keyboard->input_text_len = len;
-}
-
-mouse_input mouse_input_create()
-{
- mouse_input mouse;
- mouse.x = -1;
- mouse.y = -1;
- mouse.move_x = 0;
- mouse.move_y = 0;
- mouse.left_state = 0;
- mouse.right_state = 0;
-
- return mouse;
-}
-
-keyboard_input keyboard_input_create()
-{
- keyboard_input keyboard;
- keyboard.modifier_state = 0;
- keyboard.input_text = mem_alloc(MAX_INPUT_LENGTH);
- keyboard.input_text[0] = '\0';
- keyboard.take_input = false;
- keyboard.cursor = 0;
- keyboard.input_text_len = 0;
- keyboard.input_mode = INPUT_FULL;
- keyboard.has_selection = false;
- keyboard.selection_begin_offset = 0;
- keyboard.selection_length = 0;
- memset(keyboard.keys, 0, MAX_KEYCODE);
-
- return keyboard;
-}
-
-void keyboard_set_input_mode(keyboard_input *keyboard, keyboard_input_mode mode)
-{
- keyboard->input_mode = mode;
-}
-
-inline void keyboard_input_destroy(keyboard_input *keyboard)
-{
- mem_free(keyboard->input_text);
-}
-
-static void keyboard_handle_input_copy_and_paste(platform_window *window, keyboard_input *keyboard)
-{
- bool is_lctrl_down = keyboard->keys[KEY_LEFT_CONTROL];
-
- if (is_lctrl_down && keyboard_is_key_pressed(keyboard, KEY_V))
- {
- char buf[MAX_INPUT_LENGTH];
- bool result = platform_get_clipboard(window, buf);
-
- if (keyboard->input_mode == INPUT_NUMERIC && !is_string_numeric(buf))
- {
- return;
- }
-
- if (keyboard->has_selection)
- {
- keyboard->cursor = keyboard->selection_begin_offset;
- utf8_str_remove_range(keyboard->input_text, keyboard->selection_begin_offset,
- keyboard->selection_begin_offset + keyboard->selection_length);
- keyboard->has_selection = false;
- keyboard->text_changed = true;
- }
-
- if (result)
- {
- s32 len = utf8len(buf);
- utf8_str_insert_utf8str(keyboard->input_text, keyboard->cursor, buf);
-
- keyboard->cursor += len;
- keyboard->input_text_len += len;
- keyboard->text_changed = true;
- }
- }
- else if (is_lctrl_down && keyboard_is_key_pressed(keyboard, KEY_C))
- {
- char buffer[MAX_INPUT_LENGTH];
- utf8_str_copy_range(keyboard->input_text,
- keyboard->selection_begin_offset,
- keyboard->selection_begin_offset+keyboard->selection_length,
- buffer);
-
- if (!string_equals(buffer, ""))
- platform_set_clipboard(window, buffer);
- }
-}
-
-void keyboard_handle_input_string(platform_window *window, keyboard_input *keyboard, char *ch)
-{
- keyboard_handle_input_copy_and_paste(window, keyboard);
-
- bool is_lctrl_down = keyboard->keys[KEY_LEFT_CONTROL];
-
- if (ch)
- {
- if (keyboard->input_text_len < MAX_INPUT_LENGTH && !is_lctrl_down)
- {
- if (keyboard->has_selection)
- {
- keyboard->cursor = keyboard->selection_begin_offset;
- utf8_str_remove_range(keyboard->input_text, keyboard->selection_begin_offset,
- keyboard->selection_begin_offset + keyboard->selection_length);
- keyboard->has_selection = false;
- keyboard->text_changed = true;
- }
-
- if (keyboard->input_text_len)
- {
- utf8_str_insert_at(keyboard->input_text, keyboard->cursor, *ch);
- keyboard->text_changed = true;
- }
- else
- {
- string_appendn(keyboard->input_text, ch, MAX_INPUT_LENGTH);
- keyboard->text_changed = true;
- }
-
- keyboard->cursor++;
- keyboard->input_text_len = utf8len(keyboard->input_text);
- }
- }
- else
- {
- bool is_lctrl_down = keyboard->keys[KEY_LEFT_CONTROL];
-
- // cursor movement
- if (keyboard_is_key_down(keyboard, KEY_LEFT) && keyboard->cursor > 0)
- {
- if (is_lctrl_down)
- keyboard->cursor = 0;
- else
- keyboard->cursor--;
- }
- if (keyboard_is_key_down(keyboard, KEY_RIGHT) && keyboard->cursor < keyboard->input_text_len)
- {
- if (is_lctrl_down)
- keyboard->cursor = utf8len(keyboard->input_text);
- else
- keyboard->cursor++;
- }
- }
-
- if (keyboard_is_key_down(keyboard, KEY_BACKSPACE))
- {
- current_keyboard_to_handle->keys[KEY_BACKSPACE] = false;
-
- bool is_lctrl_down = keyboard->keys[KEY_LEFT_CONTROL];
-
- if (keyboard->has_selection)
- {
- utf8_str_remove_range(keyboard->input_text, keyboard->selection_begin_offset,
- keyboard->selection_begin_offset + keyboard->selection_length);
- keyboard->has_selection = false;
- keyboard->text_changed = true;
-
- if (keyboard->selection_begin_offset < keyboard->cursor)
- {
- keyboard->cursor -= keyboard->selection_length-1;
- }
- }
- else if (is_lctrl_down)
- {
- for (s32 i = 0; i < keyboard->cursor; i++)
- {
- utf8_str_remove_at(keyboard->input_text, 0);
- }
- keyboard->cursor = 0;
- keyboard->text_changed = true;
- }
- else if (keyboard->cursor > 0)
- {
- utf8_str_remove_at(keyboard->input_text, keyboard->cursor-1);
-
- keyboard->cursor--;
- keyboard->text_changed = true;
- }
-
- keyboard->input_text_len = utf8len(keyboard->input_text);
- }
-} \ No newline at end of file
diff --git a/src/input.h b/src/input.h
deleted file mode 100644
index 21f4df9..0000000
--- a/src/input.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_INPUT
-#define INCLUDE_INPUT
-
-/* The unknown key */
-#define KEY_UNKNOWN -1
-
-#define MOUSE_OFFSCREEN 32767
-
-/* Printable keys */
-#define KEY_SPACE 32
-#define KEY_APOSTROPHE 39 /* ' */
-#define KEY_COMMA 44 /* , */
-#define KEY_MINUS 45 /* - */
-#define KEY_PERIOD 46 /* . */
-#define KEY_SLASH 47 /* / */
-#define KEY_0 48
-#define KEY_1 49
-#define KEY_2 50
-#define KEY_3 51
-#define KEY_4 52
-#define KEY_5 53
-#define KEY_6 54
-#define KEY_7 55
-#define KEY_8 56
-#define KEY_9 57
-#define KEY_SEMICOLON 59 /* ; */
-#define KEY_EQUAL 61 /* = */
-#define KEY_A 65
-#define KEY_B 66
-#define KEY_C 67
-#define KEY_D 68
-#define KEY_E 69
-#define KEY_F 70
-#define KEY_G 71
-#define KEY_H 72
-#define KEY_I 73
-#define KEY_J 74
-#define KEY_K 75
-#define KEY_L 76
-#define KEY_M 77
-#define KEY_N 78
-#define KEY_O 79
-#define KEY_P 80
-#define KEY_Q 81
-#define KEY_R 82
-#define KEY_S 83
-#define KEY_T 84
-#define KEY_U 85
-#define KEY_V 86
-#define KEY_W 87
-#define KEY_X 88
-#define KEY_Y 89
-#define KEY_Z 90
-#define KEY_LEFT_BRACKET 91 /* [ */
-#define KEY_BACKSLASH 92 /* \ */
-#define KEY_RIGHT_BRACKET 93 /* ] */
-#define KEY_GRAVE_ACCENT 96 /* ` */
-#define KEY_WORLD_1 161 /* non-US #1 */
-#define KEY_WORLD_2 162 /* non-US #2 */
-
-/* Function keys */
-#define KEY_ESCAPE 256
-#define KEY_ENTER 257
-#define KEY_TAB 258
-#define KEY_BACKSPACE 259
-#define KEY_INSERT 260
-#define KEY_DELETE 261
-#define KEY_RIGHT 262
-#define KEY_LEFT 263
-#define KEY_DOWN 264
-#define KEY_UP 265
-#define KEY_PAGE_UP 266
-#define KEY_PAGE_DOWN 267
-#define KEY_HOME 268
-#define KEY_END 269
-#define KEY_CAPS_LOCK 280
-#define KEY_SCROLL_LOCK 281
-#define KEY_NUM_LOCK 282
-#define KEY_PRINT_SCREEN 283
-#define KEY_PAUSE 284
-#define KEY_F1 290
-#define KEY_F2 291
-#define KEY_F3 292
-#define KEY_F4 293
-#define KEY_F5 294
-#define KEY_F6 295
-#define KEY_F7 296
-#define KEY_F8 297
-#define KEY_F9 298
-#define KEY_F10 299
-#define KEY_F11 300
-#define KEY_F12 301
-#define KEY_F13 302
-#define KEY_F14 303
-#define KEY_F15 304
-#define KEY_F16 305
-#define KEY_F17 306
-#define KEY_F18 307
-#define KEY_F19 308
-#define KEY_F20 309
-#define KEY_F21 310
-#define KEY_F22 311
-#define KEY_F23 312
-#define KEY_F24 313
-#define KEY_F25 314
-#define KEY_KP_0 320
-#define KEY_KP_1 321
-#define KEY_KP_2 322
-#define KEY_KP_3 323
-#define KEY_KP_4 324
-#define KEY_KP_5 325
-#define KEY_KP_6 326
-#define KEY_KP_7 327
-#define KEY_KP_8 328
-#define KEY_KP_9 329
-#define KEY_KP_DECIMAL 330
-#define KEY_KP_DIVIDE 331
-#define KEY_KP_MULTIPLY 332
-#define KEY_KP_SUBTRACT 333
-#define KEY_KP_ADD 334
-#define KEY_KP_ENTER 335
-#define KEY_KP_EQUAL 336
-#define KEY_LEFT_SHIFT 340
-#define KEY_LEFT_CONTROL 341
-#define KEY_LEFT_ALT 342
-#define KEY_LEFT_SUPER 343
-#define KEY_RIGHT_SHIFT 344
-#define KEY_RIGHT_CONTROL 345
-#define KEY_RIGHT_ALT 346
-#define KEY_RIGHT_SUPER 347
-#define KEY_MENU 348
-
-#define KEY_LAST KEY_MENU
-
-#define MAX_KEYCODE 512
-
-#define MOUSE_DOWN (1 << 1)
-#define MOUSE_RELEASE (1 << 2)
-#define MOUSE_DOUBLE_CLICK (1 << 3)
-#define MOUSE_CLICK (1 << 4)
-
-#define SCROLL_UP 1
-#define SCROLL_DOWN -1
-
-
-// should be max path length
-#ifdef OS_LINUX
-#define MAX_INPUT_LENGTH 4096+1
-#define MAX_PATH_LENGTH 255+1
-#endif
-
-#ifdef OS_WIN
-#define MAX_INPUT_LENGTH 4096+1
-#define MAX_PATH_LENGTH 259+1
-#endif
-
-typedef struct t_mouse_input
-{
- s16 x;
- s16 y;
- s16 move_x;
- s16 move_y;
- s16 total_move_x;
- s16 total_move_y;
- s8 left_state;
- s8 right_state;
- s8 scroll_state;
-} mouse_input;
-
-typedef enum t_keyboard_input_mode
-{
- INPUT_NUMERIC,
- INPUT_FULL,
-} keyboard_input_mode;
-
-typedef struct t_keyboard_input
-{
- keyboard_input_mode input_mode;
- int modifier_state;
- bool take_input;
- u32 cursor;
-
- // input
- bool text_changed; // is set when text is pasted in, incase the new text is the same length as the old text so we know to store a new undo entry
- bool has_selection;
- s32 selection_begin_offset;
- s32 selection_length;
- char *input_text;
- // input
-
- s32 input_text_len;
- bool keys[MAX_KEYCODE];
- bool input_keys[MAX_KEYCODE];
-} keyboard_input;
-
-int keycode_map[MAX_KEYCODE];
-
-bool is_left_down(mouse_input *input);
-bool is_left_released(mouse_input *input);
-bool is_left_clicked(mouse_input *input);
-bool is_left_double_clicked(mouse_input *input);
-bool is_right_down(mouse_input *input);
-bool is_right_released(mouse_input *input);
-bool is_right_clicked(mouse_input *input);
-
-bool keyboard_is_key_down(keyboard_input *keyboard, s16 key);
-bool keyboard_is_key_pressed(keyboard_input *keyboard, s16 key);
-void keyboard_set_input_text(keyboard_input *keyboard, char *text);
-void keyboard_set_input_mode(keyboard_input *keyboard, keyboard_input_mode mode);
-
-typedef struct t_platform_window platform_window;
-void keyboard_handle_input_string(platform_window *window, keyboard_input *keyboard, char *text);
-
-mouse_input mouse_input_create();
-keyboard_input keyboard_input_create();
-void keyboard_input_destroy(keyboard_input *keyboard);
-
-#endif \ No newline at end of file
diff --git a/src/linux/platform.c b/src/linux/platform.c
deleted file mode 100644
index 140715b..0000000
--- a/src/linux/platform.c
+++ /dev/null
@@ -1,1593 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#include <locale.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <X11/X.h>
-#include <X11/Xlib.h>
-#include <X11/extensions/Xrandr.h>
-#include <X11/Xatom.h>
-#include <time.h>
-#include <X11/XKBlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <dirent.h>
-#include <errno.h>
-#include <dlfcn.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <X11/cursorfont.h>
-
-#define GET_ATOM(X) window.X = XInternAtom(window.display, #X, False)
-
-struct t_platform_window
-{
- Display *display;
- Window parent;
- XVisualInfo *visual_info;
- Colormap cmap;
- Window window;
- GLXContext gl_context;
- XWindowAttributes window_attributes;
- XEvent event;
- char *clipboard_str;
- s32 clipboard_strlen;
-
- Atom xdnd_req;
- Atom xdnd_source;
- Atom XdndEnter;
- Atom XdndPosition;
- Atom XdndStatus;
- Atom XdndTypeList;
- Atom XdndActionCopy;
- Atom XdndDrop;
- Atom XdndFinished;
- Atom XdndSelection;
- Atom XdndLeave;
- Atom quit;
- Atom PRIMARY;
- Atom CLIPBOARD;
- Atom UTF8_STRING;
- Atom COMPOUND_STRING;
- Atom TARGETS;
- Atom MULTIPLE;
- Atom _NET_WM_STATE;
-
- // shared window properties
- s32 width;
- s32 height;
- bool is_open;
- bool has_focus;
- cursor_type curr_cursor_type;
- cursor_type next_cursor_type;
-};
-
-bool platform_get_clipboard(platform_window *window, char *buffer)
-{
- char *result;
- unsigned long ressize, restail;
- int resbits;
- Atom bufid = XInternAtom(window->display, "CLIPBOARD", False),
- fmtid = XInternAtom(window->display, "UTF8_STRING", False),
- propid = XInternAtom(window->display, "XSEL_DATA", False),
- incrid = XInternAtom(window->display, "INCR", False);
- XEvent event;
-
- if(window->CLIPBOARD != None) {
-
- if (settings_window && XGetSelectionOwner(window->display, window->CLIPBOARD) == settings_window->window)
- {
- snprintf(buffer, MAX_INPUT_LENGTH, "%s", settings_window->clipboard_str);
- return true;
- }
- else if (XGetSelectionOwner(window->display, window->CLIPBOARD) ==
- main_window->window)
- {
- snprintf(buffer, MAX_INPUT_LENGTH, "%s", main_window->clipboard_str);
- return true;
- }
- }
-
- XConvertSelection(window->display, bufid, fmtid, propid, window->window, CurrentTime);
- do {
- XNextEvent(window->display, &event);
- } while (event.type != SelectionNotify || event.xselection.selection != bufid);
-
- if (event.xselection.property)
- {
- XGetWindowProperty(window->display, window->window, propid, 0, LONG_MAX/4, False, AnyPropertyType,
- &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);
-
- if (fmtid == incrid)
- printf("Buffer is too large and INCR reading is not implemented yet.\n");
- else
- snprintf(buffer, MAX_INPUT_LENGTH, "%s", result);
-
- XFree(result);
- return True;
- }
- else // request failed, e.g. owner can't convert to the target format
- return False;
-}
-
-bool platform_set_clipboard(platform_window *window, char *buffer)
-{
- if (buffer)
- {
- if(window->CLIPBOARD != None && XGetSelectionOwner(window->display, window->CLIPBOARD) != window->window) {
- XSetSelectionOwner(window->display, window->CLIPBOARD, window->window, CurrentTime);
- }
-
- window->clipboard_strlen = strlen(buffer);
- if(!window->clipboard_str) {
- window->clipboard_str = mem_alloc(window->clipboard_strlen+1);
- } else {
- window->clipboard_str = mem_realloc(window->clipboard_str, window->clipboard_strlen+1);
- }
- string_copyn(window->clipboard_str, buffer, window->clipboard_strlen+1);
-
- return true;
- }
-
- return false;
-}
-
-void platform_create_config_directory()
-{
- char *env = getenv("HOME");
- char tmp[PATH_MAX];
- snprintf(tmp, PATH_MAX, "%s%s", env, "/.config/moedit");
-
- if (!platform_directory_exists(tmp))
- {
- mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
- }
-}
-
-char* get_config_save_location(char *buffer)
-{
- char *env = getenv("HOME");
- snprintf(buffer, PATH_MAX, "%s%s", env, "/.config/moedit/config.txt");
- return buffer;
-}
-
-inline void platform_set_cursor(platform_window *window, cursor_type type)
-{
- if (window->next_cursor_type != type)
- {
- window->next_cursor_type = type;
- }
-}
-
-bool is_platform_in_darkmode()
-{
- return false;
-}
-
-bool get_active_directory(char *buffer)
-{
- char cwd[PATH_MAX];
- if (getcwd(cwd, sizeof(cwd)) != NULL) {
- string_copyn(buffer, cwd, PATH_MAX);
- } else {
- return false;
- }
- return true;
-}
-
-bool set_active_directory(char *path)
-{
- return !chdir(path);
-}
-
-bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 len)
-{
- bool result = false;
-
- FILE *file = fopen(path, mode);
-
- if (!file)
- {
- goto done_failure;
- }
- else
- {
- fwrite(buffer, 1, len, file); // TODO(Aldrik): is this correct?
- //fprintf(file, "%s", buffer);
- }
-
- //done:
- fclose(file);
- done_failure:
- return result;
-}
-
-void platform_window_set_title(platform_window *window, char *name)
-{
- Atom WM_NAME = XInternAtom(window->display, "WM_NAME", False);
- Atom _NET_WM_NAME = XInternAtom(window->display, "_NET_WM_NAME", False);
- Atom _NET_WM_ICON_NAME = XInternAtom(window->display, "_NET_WM_ICON_NAME", False);
-
- char *list[1] = { (char *) name };
- XTextProperty property;
-
- XStoreName(window->display, window->window, name);
-
- Xutf8TextListToTextProperty(window->display, list, 1, XUTF8StringStyle,
- &property);
- XSetTextProperty(window->display, window->window, &property, WM_NAME);
- XSetTextProperty(window->display, window->window, &property, _NET_WM_NAME);
- XSetTextProperty(window->display, window->window, &property, XA_WM_NAME);
- XSetTextProperty(window->display, window->window, &property, _NET_WM_ICON_NAME);
- XFree(property.value);
-}
-
-bool platform_file_exists(char *path)
-{
- if(access(path, F_OK) != -1) {
- return 1;
- }
-
- return 0;
-}
-
-bool platform_directory_exists(char *path)
-{
- DIR* dir = opendir(path);
- if (dir) {
- /* Directory exists. */
- closedir(dir);
- return 1;
- } else if (ENOENT == errno) {
- return 0; // does not exist
- } else {
- return 0; // error opening dir
- }
-}
-
-file_content platform_read_file_content(char *path, const char *mode)
-{
- file_content result;
- result.content = 0;
- result.content_length = 0;
- result.file_error = 0;
-
- FILE *file = fopen(path, mode);
-
- if (!file)
- {
- if (errno == EMFILE)
- result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS;
- else if (errno == ENFILE)
- result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM;
- else if (errno == EACCES)
- result.file_error = FILE_ERROR_NO_ACCESS;
- else if (errno == EPERM)
- result.file_error = FILE_ERROR_NO_ACCESS;
- else if (errno == ENOENT)
- result.file_error = FILE_ERROR_NOT_FOUND;
- else if (errno == ECONNABORTED)
- result.file_error = FILE_ERROR_CONNECTION_ABORTED;
- else if (errno == ECONNREFUSED)
- result.file_error = FILE_ERROR_CONNECTION_REFUSED;
- else if (errno == ENETDOWN)
- result.file_error = FILE_ERROR_NETWORK_DOWN;
- else if (errno == EREMOTEIO)
- result.file_error = FILE_ERROR_REMOTE_IO_ERROR;
- else if (errno == ESTALE)
- result.file_error = FILE_ERROR_STALE;
- else
- {
- result.file_error = FILE_ERROR_GENERIC;
- printf("ERROR: %d\n", errno);
- }
-
- goto done_failure;
- }
-
- fseek(file, 0 , SEEK_END);
- int length = ftell(file);
- fseek(file, 0, SEEK_SET);
-
- s32 length_to_alloc = length+1;
-
- result.content = mem_alloc(length_to_alloc);
- if (!result.content) goto done;
-
- memset(result.content, 0, length);
- s32 read_result = fread(result.content, 1, length, file);
- if (read_result == 0 && length != 0)
- {
- mem_free(result.content);
- result.content = 0;
- return result;
- }
-
- result.content_length = read_result;
-
- ((char*)result.content)[length] = 0;
-
- done:
- fclose(file);
- done_failure:
- return result;
-}
-
-inline void platform_destroy_file_content(file_content *content)
-{
- assert(content);
- mem_free(content->content);
-}
-
-// Translate an X11 key code to a GLFW key code.
-//
-static s32 translate_keycode(platform_window *window, s32 scancode)
-{
- s32 keySym;
-
- // Valid key code range is [8,255], according to the Xlib manual
-
- if (1)
- {
- // Try secondary keysym, for numeric keypad keys
- // Note: This way we always force "NumLock = ON", which is intentional
- // since the returned key code should correspond to a physical
- // location.
- keySym = XkbKeycodeToKeysym(window->display, scancode, 0, 1);
- switch (keySym)
- {
- case XK_KP_0: return KEY_KP_0;
- case XK_KP_1: return KEY_KP_1;
- case XK_KP_2: return KEY_KP_2;
- case XK_KP_3: return KEY_KP_3;
- case XK_KP_4: return KEY_KP_4;
- case XK_KP_5: return KEY_KP_5;
- case XK_KP_6: return KEY_KP_6;
- case XK_KP_7: return KEY_KP_7;
- case XK_KP_8: return KEY_KP_8;
- case XK_KP_9: return KEY_KP_9;
- case XK_KP_Separator:
- case XK_KP_Decimal: return KEY_KP_DECIMAL;
- case XK_KP_Equal: return KEY_KP_EQUAL;
- case XK_KP_Enter: return KEY_KP_ENTER;
- default: break;
- }
-
- // Now try primary keysym for function keys (non-printable keys)
- // These should not depend on the current keyboard layout
- keySym = XkbKeycodeToKeysym(window->display, scancode, 0, 0);
- }
-
- switch (keySym)
- {
- case XK_Escape: return KEY_ESCAPE;
- case XK_Tab: return KEY_TAB;
- case XK_Shift_L: return KEY_LEFT_SHIFT;
- case XK_Shift_R: return KEY_RIGHT_SHIFT;
- case XK_Control_L: return KEY_LEFT_CONTROL;
- case XK_Control_R: return KEY_RIGHT_CONTROL;
- case XK_Meta_L:
- case XK_Alt_L: return KEY_LEFT_ALT;
- case XK_Mode_switch: // Mapped to Alt_R on many keyboards
- case XK_ISO_Level3_Shift: // AltGr on at least some machines
- case XK_Meta_R:
- case XK_Alt_R: return KEY_RIGHT_ALT;
- case XK_Super_L: return KEY_LEFT_SUPER;
- case XK_Super_R: return KEY_RIGHT_SUPER;
- case XK_Menu: return KEY_MENU;
- case XK_Num_Lock: return KEY_NUM_LOCK;
- case XK_Caps_Lock: return KEY_CAPS_LOCK;
- case XK_Print: return KEY_PRINT_SCREEN;
- case XK_Scroll_Lock: return KEY_SCROLL_LOCK;
- case XK_Pause: return KEY_PAUSE;
- case XK_Delete: return KEY_DELETE;
- case XK_BackSpace: return KEY_BACKSPACE;
- case XK_Return: return KEY_ENTER;
- case XK_Home: return KEY_HOME;
- case XK_End: return KEY_END;
- case XK_Page_Up: return KEY_PAGE_UP;
- case XK_Page_Down: return KEY_PAGE_DOWN;
- case XK_Insert: return KEY_INSERT;
- case XK_Left: return KEY_LEFT;
- case XK_Right: return KEY_RIGHT;
- case XK_Down: return KEY_DOWN;
- case XK_Up: return KEY_UP;
- case XK_F1: return KEY_F1;
- case XK_F2: return KEY_F2;
- case XK_F3: return KEY_F3;
- case XK_F4: return KEY_F4;
- case XK_F5: return KEY_F5;
- case XK_F6: return KEY_F6;
- case XK_F7: return KEY_F7;
- case XK_F8: return KEY_F8;
- case XK_F9: return KEY_F9;
- case XK_F10: return KEY_F10;
- case XK_F11: return KEY_F11;
- case XK_F12: return KEY_F12;
- case XK_F13: return KEY_F13;
- case XK_F14: return KEY_F14;
- case XK_F15: return KEY_F15;
- case XK_F16: return KEY_F16;
- case XK_F17: return KEY_F17;
- case XK_F18: return KEY_F18;
- case XK_F19: return KEY_F19;
- case XK_F20: return KEY_F20;
- case XK_F21: return KEY_F21;
- case XK_F22: return KEY_F22;
- case XK_F23: return KEY_F23;
- case XK_F24: return KEY_F24;
- case XK_F25: return KEY_F25;
-
- // Numeric keypad
- case XK_KP_Divide: return KEY_KP_DIVIDE;
- case XK_KP_Multiply: return KEY_KP_MULTIPLY;
- case XK_KP_Subtract: return KEY_KP_SUBTRACT;
- case XK_KP_Add: return KEY_KP_ADD;
-
- // These should have been detected in secondary keysym test above!
- case XK_KP_Insert: return KEY_KP_0;
- case XK_KP_End: return KEY_KP_1;
- case XK_KP_Down: return KEY_KP_2;
- case XK_KP_Page_Down: return KEY_KP_3;
- case XK_KP_Left: return KEY_KP_4;
- case XK_KP_Right: return KEY_KP_6;
- case XK_KP_Home: return KEY_KP_7;
- case XK_KP_Up: return KEY_KP_8;
- case XK_KP_Page_Up: return KEY_KP_9;
- case XK_KP_Delete: return KEY_KP_DECIMAL;
- case XK_KP_Equal: return KEY_KP_EQUAL;
- case XK_KP_Enter: return KEY_KP_ENTER;
-
- // Last resort: Check for printable keys (should not happen if the XKB
- // extension is available). This will give a layout dependent mapping
- // (which is wrong, and we may miss some keys, especially on non-US
- // keyboards), but it's better than nothing...
- case XK_a: return KEY_A;
- case XK_b: return KEY_B;
- case XK_c: return KEY_C;
- case XK_d: return KEY_D;
- case XK_e: return KEY_E;
- case XK_f: return KEY_F;
- case XK_g: return KEY_G;
- case XK_h: return KEY_H;
- case XK_i: return KEY_I;
- case XK_j: return KEY_J;
- case XK_k: return KEY_K;
- case XK_l: return KEY_L;
- case XK_m: return KEY_M;
- case XK_n: return KEY_N;
- case XK_o: return KEY_O;
- case XK_p: return KEY_P;
- case XK_q: return KEY_Q;
- case XK_r: return KEY_R;
- case XK_s: return KEY_S;
- case XK_t: return KEY_T;
- case XK_u: return KEY_U;
- case XK_v: return KEY_V;
- case XK_w: return KEY_W;
- case XK_x: return KEY_X;
- case XK_y: return KEY_Y;
- case XK_z: return KEY_Z;
- case XK_1: return KEY_1;
- case XK_2: return KEY_2;
- case XK_3: return KEY_3;
- case XK_4: return KEY_4;
- case XK_5: return KEY_5;
- case XK_6: return KEY_6;
- case XK_7: return KEY_7;
- case XK_8: return KEY_8;
- case XK_9: return KEY_9;
- case XK_0: return KEY_0;
- case XK_space: return KEY_SPACE;
- case XK_minus: return KEY_MINUS;
- case XK_equal: return KEY_EQUAL;
- case XK_bracketleft: return KEY_LEFT_BRACKET;
- case XK_bracketright: return KEY_RIGHT_BRACKET;
- case XK_backslash: return KEY_BACKSLASH;
- case XK_semicolon: return KEY_SEMICOLON;
- case XK_apostrophe: return KEY_APOSTROPHE;
- case XK_grave: return KEY_GRAVE_ACCENT;
- case XK_comma: return KEY_COMMA;
- case XK_period: return KEY_PERIOD;
- case XK_slash: return KEY_SLASH;
- case XK_less: return KEY_WORLD_1; // At least in some layouts...
- default: break;
- }
-
- // No matching translation was found
- return KEY_UNKNOWN;
-}
-
-static void create_key_tables(platform_window window)
-{
- s32 scancode, key;
- char name[XkbKeyNameLength + 1];
- XkbDescPtr desc = XkbGetMap(window.display, 0, XkbUseCoreKbd);
- XkbGetNames(window.display, XkbKeyNamesMask, desc);
-
- // uncomment for layout independant input for games.
-#if 0
- for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++)
- {
- memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength);
- name[XkbKeyNameLength] = '\0';
-
- // Map the key name to a GLFW key code. Note: We only map printable
- // keys here, and we use the US keyboard layout. The rest of the
- // keys (function keys) are mapped using traditional KeySym
- // translations.
- if (strcmp(name, "TLDE") == 0) key = KEY_GRAVE_ACCENT;
- else if (strcmp(name, "AE01") == 0) key = KEY_1;
- else if (strcmp(name, "AE02") == 0) key = KEY_2;
- else if (strcmp(name, "AE03") == 0) key = KEY_3;
- else if (strcmp(name, "AE04") == 0) key = KEY_4;
- else if (strcmp(name, "AE05") == 0) key = KEY_5;
- else if (strcmp(name, "AE06") == 0) key = KEY_6;
- else if (strcmp(name, "AE07") == 0) key = KEY_7;
- else if (strcmp(name, "AE08") == 0) key = KEY_8;
- else if (strcmp(name, "AE09") == 0) key = KEY_9;
- else if (strcmp(name, "AE10") == 0) key = KEY_0;
- else if (strcmp(name, "AE11") == 0) key = KEY_MINUS;
- else if (strcmp(name, "AE12") == 0) key = KEY_EQUAL;
- else if (strcmp(name, "AD01") == 0) key = KEY_Q;
- else if (strcmp(name, "AD02") == 0) key = KEY_W;
- else if (strcmp(name, "AD03") == 0) key = KEY_E;
- else if (strcmp(name, "AD04") == 0) key = KEY_R;
- else if (strcmp(name, "AD05") == 0) key = KEY_T;
- else if (strcmp(name, "AD06") == 0) key = KEY_Y;
- else if (strcmp(name, "AD07") == 0) key = KEY_U;
- else if (strcmp(name, "AD08") == 0) key = KEY_I;
- else if (strcmp(name, "AD09") == 0) key = KEY_O;
- else if (strcmp(name, "AD10") == 0) key = KEY_P;
- else if (strcmp(name, "AD11") == 0) key = KEY_LEFT_BRACKET;
- else if (strcmp(name, "AD12") == 0) key = KEY_RIGHT_BRACKET;
- else if (strcmp(name, "AC01") == 0) key = KEY_A;
- else if (strcmp(name, "AC02") == 0) key = KEY_S;
- else if (strcmp(name, "AC03") == 0) key = KEY_D;
- else if (strcmp(name, "AC04") == 0) key = KEY_F;
- else if (strcmp(name, "AC05") == 0) key = KEY_G;
- else if (strcmp(name, "AC06") == 0) key = KEY_H;
- else if (strcmp(name, "AC07") == 0) key = KEY_J;
- else if (strcmp(name, "AC08") == 0) key = KEY_K;
- else if (strcmp(name, "AC09") == 0) key = KEY_L;
- else if (strcmp(name, "AC10") == 0) key = KEY_SEMICOLON;
- else if (strcmp(name, "AC11") == 0) key = KEY_APOSTROPHE;
- else if (strcmp(name, "AB01") == 0) key = KEY_Z;
- else if (strcmp(name, "AB02") == 0) key = KEY_X;
- else if (strcmp(name, "AB03") == 0) key = KEY_C;
- else if (strcmp(name, "AB04") == 0) key = KEY_V;
- else if (strcmp(name, "AB05") == 0) key = KEY_B;
- else if (strcmp(name, "AB06") == 0) key = KEY_N;
- else if (strcmp(name, "AB07") == 0) key = KEY_M;
- else if (strcmp(name, "AB08") == 0) key = KEY_COMMA;
- else if (strcmp(name, "AB09") == 0) key = KEY_PERIOD;
- else if (strcmp(name, "AB10") == 0) key = KEY_SLASH;
- else if (strcmp(name, "BKSL") == 0) key = KEY_BACKSLASH;
- else if (strcmp(name, "LSGT") == 0) key = KEY_WORLD_1;
- else key = KEY_UNKNOWN;
-
- if ((scancode >= 0) && (scancode < 256))
- keycode_map[scancode] = key;
- }
-#endif
-
- for (scancode = 0; scancode < MAX_KEYCODE; scancode++)
- {
- // Translate the un-translated key codes using traditional X11 KeySym
- // lookups
-
- keycode_map[scancode] = translate_keycode(&window, scancode);
- }
-
- XkbFreeNames(desc, XkbKeyNamesMask, True);
- XkbFreeKeyboard(desc, 0, True);
-}
-
-inline void platform_init(int argc, char **argv)
-{
-#if 0
- dlerror(); // clear error
- void *x11 = dlopen("libX11.so.6", RTLD_NOW | RTLD_GLOBAL);
- void *randr = dlopen("libXrandr.so", RTLD_NOW | RTLD_GLOBAL);
-#endif
-
- setlocale(LC_ALL, "en_US.UTF-8");
-
- XInitThreads();
-
- // get fullpath of the directory the binary is residing in
- binary_path = platform_get_full_path(argv[0]);
-
- platform_create_config_directory();
-
- char buf[MAX_INPUT_LENGTH];
- get_directory_from_path(buf, binary_path);
- string_copyn(binary_path, buf, MAX_INPUT_LENGTH);
-
- assets_create();
-}
-
-inline void platform_destroy()
-{
- assets_destroy();
-
-#if defined(MODE_DEVELOPER)
- memory_print_leaks();
-#endif
-}
-
-inline void platform_window_make_current(platform_window *window)
-{
- glXMakeCurrent(window->display, window->window, window->gl_context);
-}
-
-void platform_window_set_size(platform_window *window, u16 width, u16 height)
-{
- XResizeWindow(window->display, window->window, width, height);
-}
-
-void platform_window_set_position(platform_window *window, u16 x, u16 y)
-{
- XMoveWindow(window->display, window->window, x, y);
-}
-
-
-vec2 platform_get_window_size(platform_window *window)
-{
- vec2 res;
- res.x = window->width;
- res.y = window->height;
- return res;
-}
-
-platform_window platform_open_window(char *name, u16 width, u16 height, u16 max_w, u16 max_h, u16 min_w, u16 min_h)
-{
- bool has_max_size = max_w || max_h;
-
- platform_window window;
- window.has_focus = true;
- window.curr_cursor_type = CURSOR_DEFAULT;
- window.next_cursor_type = CURSOR_DEFAULT;
- window.clipboard_str = 0;
- window.clipboard_strlen = 0;
-
- static int att[] =
- {
- GLX_X_RENDERABLE , True,
- GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
- GLX_RENDER_TYPE , GLX_RGBA_BIT,
- GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
- GLX_RED_SIZE , 8,
- GLX_GREEN_SIZE , 8,
- GLX_BLUE_SIZE , 8,
- GLX_ALPHA_SIZE , 8,
- GLX_DEPTH_SIZE , 24,
- GLX_STENCIL_SIZE , 8,
- GLX_DOUBLEBUFFER , True,
- //GLX_SAMPLE_BUFFERS , 1,
- //GLX_SAMPLES , 4,
- None
- };
-
- window.display = XOpenDisplay(NULL);
-
- if(window.display == NULL) {
- return window;
- }
-
- window.parent = DefaultRootWindow(window.display);
-
- int fbcount;
- GLXFBConfig* fbc = glXChooseFBConfig(window.display, DefaultScreen(window.display), att, &fbcount);
- int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
-
- int i;
- for (i=0; i<fbcount; ++i)
- {
- XVisualInfo *vi = glXGetVisualFromFBConfig(window.display, fbc[i] );
- if ( vi )
- {
- int samp_buf, samples;
- glXGetFBConfigAttrib(window.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
- glXGetFBConfigAttrib(window.display, fbc[i], GLX_SAMPLES , &samples );
-
- if ( best_fbc < 0 || (samp_buf && samples > best_num_samp))
- best_fbc = i, best_num_samp = samples;
- if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
- worst_fbc = i, worst_num_samp = samples;
- }
- XFree(vi);
- }
-
- GLXFBConfig bestFbc = fbc[best_fbc];
- XFree(fbc);
-
- XVisualInfo *vi = glXGetVisualFromFBConfig(window.display, bestFbc );
- window.visual_info = vi;
-
- if(window.visual_info == NULL) {
- return window;
- }
-
- window.cmap = XCreateColormap(window.display, window.parent, window.visual_info->visual, AllocNone);
-
- // calculate window center
- XRRScreenResources *screens = XRRGetScreenResources(window.display, window.parent);
- XRRCrtcInfo *info = XRRGetCrtcInfo(window.display, screens, screens->crtcs[0]);
-
- s32 center_x = (info->width / 2) - (width / 2);
- s32 center_y = (info->height / 2) - (height / 2);
-
- XRRFreeCrtcInfo(info);
- XRRFreeScreenResources(screens);
-
- XSetWindowAttributes window_attributes;
- window_attributes.colormap = window.cmap;
- window_attributes.border_pixel = 0;
- window_attributes.event_mask = KeyPressMask | KeyReleaseMask | PointerMotionMask |
- ButtonPressMask | ButtonReleaseMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask;
-
- window.window = XCreateWindow(window.display, window.parent, center_x, center_y, width, height, 0, window.visual_info->depth, InputOutput, window.visual_info->visual, CWColormap | CWEventMask | CWBorderPixel, &window_attributes);
-
- XMapWindow(window.display, window.window);
- XFlush(window.display);
-
- XSync(window.display, False);
-
- XSizeHints hints;
-
- if (has_max_size)
- hints.flags = PMaxSize | PMinSize | USPosition;
- else
- hints.flags = PMinSize | USPosition;
- hints.x = center_x;
- hints.y = center_y;
- hints.max_width = width;
- hints.max_height = height;
- hints.min_width = min_w;
- hints.min_height = min_h;
-
- XSetWMNormalHints(window.display, window.window, &hints);
-
- // window name
- {
- Atom WM_NAME = XInternAtom(window.display, "WM_NAME", False);
- Atom _NET_WM_NAME = XInternAtom(window.display, "_NET_WM_NAME", False);
- Atom _NET_WM_ICON_NAME = XInternAtom(window.display, "_NET_WM_ICON_NAME", False);
-
- char *list[1] = { (char *) name };
- XTextProperty property;
-
- XStoreName(window.display, window.window, name);
-
- Xutf8TextListToTextProperty(window.display, list, 1, XUTF8StringStyle,
- &property);
- XSetTextProperty(window.display, window.window, &property, WM_NAME);
- XSetTextProperty(window.display, window.window, &property, _NET_WM_NAME);
- XSetTextProperty(window.display, window.window, &property, XA_WM_NAME);
- XSetTextProperty(window.display, window.window, &property, _NET_WM_ICON_NAME);
- XFree(property.value);
-
- XClassHint class_hint;
- class_hint.res_name = name;
- class_hint.res_class = name;
- XSetClassHint(window.display, window.window, &class_hint);
- }
-
-
- // hide taskbar icon and stay on top
-#if 0
- {
- Atom wm_state = XInternAtom(window.display,
- "_NET_WM_STATE", False);
- Atom taskbar_atom = XInternAtom(window.display,
- "_NET_WM_STATE_SKIP_TASKBAR", False);
- Atom above_atom = XInternAtom(window.display,
- "_NET_WM_STATE_ABOVE", False);
-
- Atom atom_list[2] = {taskbar_atom, above_atom};
- XChangeProperty(window.display, window.window, wm_state, XA_ATOM, 32,
- PropModeReplace, (const unsigned char*)&atom_list, 2);
- }
-#endif
-
- {
- XWMHints* win_hints = XAllocWMHints();
- win_hints->flags = StateHint | IconPositionHint;
- win_hints->initial_state = IconicState;
- win_hints->icon_x = 0;
- win_hints->icon_y = 0;
-
- /* pass the hints to the window manager. */
- XSetWMHints(window.display, window.window, win_hints);
- XFree(win_hints);
- }
-
- static GLXContext share_list = 0;
-
- // get opengl context
- window.gl_context = glXCreateContext(window.display, window.visual_info,
- share_list, GL_TRUE);
-
- if (share_list == 0)
- share_list = window.gl_context;
- glXMakeCurrent(window.display, window.window, window.gl_context);
-
- // blending
- glEnable(GL_DEPTH_TEST);
- //glDepthMask(true);
- //glClearDepth(50);
- glDepthFunc(GL_LEQUAL);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- // setup multisampling
-#if 0
- glEnable(GL_ALPHA_TEST);
- glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
- glEnable(GL_SAMPLE_ALPHA_TO_ONE);
- glEnable(GL_MULTISAMPLE);
- glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
-#endif
-
- window.is_open = true;
- window.width = width;
- window.height = height;
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, width, height, 0, -1, 1);
-
- glMatrixMode(GL_MODELVIEW);
-
- create_key_tables(window);
-
- // recieve window close event
- window.quit = XInternAtom(window.display, "WM_DELETE_WINDOW", False);
-
- GET_ATOM(XdndEnter);
- GET_ATOM(XdndPosition);
- GET_ATOM(XdndStatus);
- GET_ATOM(XdndTypeList);
- GET_ATOM(XdndActionCopy);
- GET_ATOM(XdndDrop);
- GET_ATOM(XdndFinished);
- GET_ATOM(XdndSelection);
- GET_ATOM(XdndLeave);
- GET_ATOM(PRIMARY);
- GET_ATOM(CLIPBOARD);
- GET_ATOM(UTF8_STRING);
- GET_ATOM(COMPOUND_STRING);
- GET_ATOM(TARGETS);
- GET_ATOM(MULTIPLE);
- GET_ATOM(_NET_WM_STATE);
-
- array atoms = array_create(sizeof(Atom));
- array_push(&atoms, &window.quit);
- array_push(&atoms, &window.XdndEnter);
- array_push(&atoms, &window.XdndPosition);
- array_push(&atoms, &window.XdndStatus);
- array_push(&atoms, &window.XdndTypeList);
- array_push(&atoms, &window.XdndActionCopy);
- array_push(&atoms, &window.XdndDrop);
- array_push(&atoms, &window.XdndFinished);
- array_push(&atoms, &window.XdndSelection);
- array_push(&atoms, &window.XdndLeave);
- array_push(&atoms, &window.PRIMARY);
- array_push(&atoms, &window.CLIPBOARD);
- array_push(&atoms, &window.UTF8_STRING);
- array_push(&atoms, &window.COMPOUND_STRING);
- array_push(&atoms, &window.TARGETS);
- array_push(&atoms, &window.MULTIPLE);
- array_push(&atoms, &window._NET_WM_STATE);
-
- XSetWMProtocols(window.display, window.window, atoms.data, atoms.length);
- array_destroy(&atoms);
-
- Atom XdndAware = XInternAtom(window.display, "XdndAware", False);
- Atom xdnd_version = 5;
- XChangeProperty(window.display, window.window, XdndAware, XA_ATOM, 32,
- PropModeReplace, (unsigned char*)&xdnd_version, 1);
-
-
- XFlush(window.display);
- XSync(window.display, True);
-
- return window;
-}
-
-inline bool platform_window_is_valid(platform_window *window)
-{
- return window->window && window->display;
-}
-
-void platform_destroy_window(platform_window *window)
-{
- glXMakeCurrent(window->display, None, NULL);
- glXDestroyContext(window->display, window->gl_context);
- XDestroyWindow(window->display, window->window);
- XCloseDisplay(window->display);
- XFree(window->visual_info);
- mem_free(window->clipboard_str);
-
- window->window = 0;
- window->display = 0;
-}
-
-void platform_hide_window_taskbar_icon(platform_window *window)
-{
- XClientMessageEvent m;
- memset(&m, 0, sizeof(XClientMessageEvent));
- m.type = ClientMessage;
- m.display = window->display;
- m.window = window->window;
- m.message_type = window->_NET_WM_STATE;
- m.format=32;
- m.data.l[0] = 1;
- m.data.l[1] = XInternAtom(window->display, "_NET_WM_STATE_SKIP_TASKBAR", False);
- m.data.l[2] = None;
- m.data.l[3] = 1;
- m.data.l[4] = 0;
- XSendEvent(window->display, window->window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&m);
-
- XFlush(window->display);
-}
-
-void platform_handle_events(platform_window *window, mouse_input *mouse, keyboard_input *keyboard)
-{
- mouse->left_state &= ~MOUSE_CLICK;
- mouse->right_state &= ~MOUSE_CLICK;
- mouse->left_state &= ~MOUSE_DOUBLE_CLICK;
- mouse->right_state &= ~MOUSE_DOUBLE_CLICK;
- mouse->left_state &= ~MOUSE_RELEASE;
- mouse->right_state &= ~MOUSE_RELEASE;
- memset(keyboard->input_keys, 0, MAX_KEYCODE);
- mouse->move_x = 0;
- mouse->move_y = 0;
- mouse->scroll_state = 0;
- keyboard->text_changed = false;
-
- XClientMessageEvent m;
-
- s32 pending_events = XPending(window->display);
- for (s32 i = 0; i < pending_events; i++)
- {
- XNextEvent(window->display, &window->event);
- if (window->event.type == ClientMessage)
- {
- static int xdnd_version=0;
-
- if ((Atom)window->event.xclient.data.l[0] == window->quit) {
- window->is_open = false;
- }
-
- if (window->event.xclient.message_type == window->XdndDrop)
- {
- if (window->xdnd_req == None) {
- /* say again - not interested! */
- memset(&m, 0, sizeof(XClientMessageEvent));
- m.type = ClientMessage;
- m.display = window->event.xclient.display;
- m.window = window->event.xclient.data.l[0];
- m.message_type = window->XdndFinished;
- m.format=32;
- m.data.l[0] = window->window;
- m.data.l[1] = 0;
- m.data.l[2] = None; /* fail! */
- XSendEvent(window->display, window->event.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
- } else {
- /* convert */
- if(xdnd_version >= 1) {
- XConvertSelection(window->display, window->XdndSelection, window->xdnd_req, window->PRIMARY, window->window, window->event.xclient.data.l[2]);
- } else {
- printf("time to find the time.\n");
- //XConvertSelection(window->display, window->XdndSelection, window->xdnd_req, window->PRIMARY, window->xwindow, CurrentTime);
- }
- }
- }
- }
- else if (window->event.type == LeaveNotify)
- {
- mouse->x = MOUSE_OFFSCREEN;
- mouse->y = MOUSE_OFFSCREEN;
- }
- else if (window->event.type == ConfigureNotify)
- {
- XConfigureEvent xce = window->event.xconfigure;
- window->width = xce.width;
- window->height = xce.height;
- glViewport(0, 0, window->width, window->height);
- }
- else if (window->event.type == FocusIn)
- {
- window->has_focus = true;
- }
- else if (window->event.type == FocusOut)
- {
- mouse->x = MOUSE_OFFSCREEN;
- mouse->y = MOUSE_OFFSCREEN;
- window->has_focus = false;
-
- // if windows loses focus, set all keys to not pressed
- memset(keyboard->keys, 0, MAX_KEYCODE);
- }
- else if (window->event.type == MotionNotify)
- {
- s32 x = mouse->x;
- s32 y = mouse->y;
-
- mouse->total_move_x += window->event.xmotion.x - mouse->x;
- mouse->total_move_y += window->event.xmotion.y - mouse->y;
-
- mouse->x = window->event.xmotion.x;
- mouse->y = window->event.xmotion.y;
-
- mouse->move_x = mouse->x - x;
- mouse->move_y = mouse->y - y;
- }
- else if (window->event.type == ButtonPress)
- {
- Time ev_time = window->event.xbutton.time;
- static Time last_ev_time = 0;
-
- bool is_left_down = window->event.xbutton.button == Button1;
- bool is_right_down = window->event.xbutton.button == Button3;
- bool is_middle_down = window->event.xbutton.button == Button2;
- bool scroll_up = window->event.xbutton.button == Button4;
- bool scroll_down = window->event.xbutton.button == Button5;
-
- if (scroll_up)
- mouse->scroll_state = SCROLL_UP;
- if (scroll_down)
- mouse->scroll_state = SCROLL_DOWN;
-
- if (is_left_down)
- {
- if (ev_time - last_ev_time < 200)
- {
- mouse->left_state |= MOUSE_DOUBLE_CLICK;
- }
-
- mouse->left_state |= MOUSE_DOWN;
- mouse->left_state |= MOUSE_CLICK;
-
- mouse->total_move_x = 0;
- mouse->total_move_y = 0;
- last_ev_time = ev_time;
- }
- if (is_right_down)
- {
- mouse->right_state |= MOUSE_DOWN;
- mouse->right_state |= MOUSE_CLICK;
- }
- }
- else if (window->event.type == ButtonRelease)
- {
- bool is_left_up = window->event.xbutton.button == Button1;
- bool is_right_up = window->event.xbutton.button == Button3;
- bool is_middle_up = window->event.xbutton.button == Button2;
-
- if (is_left_up)
- {
- mouse->left_state = MOUSE_RELEASE;
- }
- if (is_right_up)
- {
- mouse->right_state = MOUSE_RELEASE;
- }
- }
- else if(window->event.type == KeyPress)
- {
- s32 key = window->event.xkey.keycode;
-
- keyboard->keys[keycode_map[key]] = true;
- keyboard->input_keys[keycode_map[key]] = true;
-
- // https://gist.github.com/rickyzhang82/8581a762c9f9fc6ddb8390872552c250
- //printf("state: %d\n", window->event.xkey.state);
-
- // remove key control key from mask so it doesnt block input
- window->event.xkey.state &= ~ControlMask;
-
- // replace capslock with shiftkey else keylookup returns 0...
- if (window->event.xkey.state == 2)
- window->event.xkey.state = 1;
-
- KeySym ksym = XLookupKeysym(&window->event.xkey, window->event.xkey.state);
-
- if (keyboard->take_input)
- {
- char *ch = 0;
- switch(ksym)
- {
- case XK_space: ch = " "; break;
- case XK_exclam: ch = "!"; break;
- case XK_quotedbl: ch = "\""; break;
- case XK_numbersign: ch = "#"; break;
- case XK_dollar: ch = "$"; break;
- case XK_percent: ch = "%"; break;
- case XK_ampersand: ch = "&"; break;
- case XK_apostrophe: ch = "`"; break;
- case XK_parenleft: ch = "("; break;
- case XK_parenright: ch = ")"; break;
- case XK_asterisk: ch = "*"; break;
- case XK_plus: ch = "+"; break;
- case XK_comma: ch = ","; break;
- case XK_minus: ch = "-"; break;
- case XK_period: ch = "."; break;
- case XK_slash: ch = "/"; break;
- case XK_0: ch = "0"; break;
- case XK_1: ch = "1"; break;
- case XK_2: ch = "2"; break;
- case XK_3: ch = "3"; break;
- case XK_4: ch = "4"; break;
- case XK_5: ch = "5"; break;
- case XK_6: ch = "6"; break;
- case XK_7: ch = "7"; break;
- case XK_8: ch = "8"; break;
- case XK_9: ch = "9"; break;
-
- case XK_colon: ch = ":"; break;
- case XK_semicolon: ch = ";"; break;
- case XK_less: ch = "<"; break;
- case XK_equal: ch = "="; break;
- case XK_greater: ch = ">"; break;
- case XK_question: ch = "?"; break;
- case XK_at: ch = "@"; break;
- case XK_bracketleft: ch = "["; break;
- case XK_backslash: ch = "\\"; break;
- case XK_bracketright: ch = "]"; break;
- case XK_asciicircum: ch = "^"; break;
- case XK_underscore: ch = "_"; break;
- case XK_grave: ch = "`"; break;
- case XK_braceleft: ch = "{"; break;
- case XK_bar: ch = "|"; break;
- case XK_braceright: ch = "}"; break;
- case XK_asciitilde: ch = "~"; break;
- }
-
- if ((ksym >= XK_A && ksym <= XK_Z) || (ksym >= XK_a && ksym <= XK_z))
- {
- ch = XKeysymToString(ksym);
- }
-
- if (ch && keyboard->input_mode == INPUT_NUMERIC)
- {
- if (!(*ch >= 48 && *ch <= 57))
- {
- ch = 0;
- }
- }
-
- keyboard_handle_input_string(window, keyboard, ch);
- }
- }
- else if (window->event.type == KeyRelease)
- {
- s32 key = window->event.xkey.keycode;
- keyboard->keys[keycode_map[key]] = false;
-
- KeySym ksym = XLookupKeysym(&window->event.xkey, 0);
- }
- else if (window->event.type == SelectionClear)
- {
- window->clipboard_str = 0;
- window->clipboard_strlen = 0;
- }
- else if (window->event.type == SelectionRequest)
- {
- Atom formats[] = {window->UTF8_STRING, window->COMPOUND_STRING, XA_STRING};
- Atom targets[] = {window->TARGETS, window->MULTIPLE, window->UTF8_STRING, window->COMPOUND_STRING, XA_STRING};
- int formatCount = sizeof(formats) / sizeof(formats[0]);
-
- XSelectionEvent event = {.type = SelectionNotify, .selection = window->event.xselectionrequest.selection, .target = window->event.xselectionrequest.target, .display = window->event.xselectionrequest.display, .requestor = window->event.xselectionrequest.requestor, .time = window->event.xselectionrequest.time};
-
- if(window->event.xselectionrequest.target == window->TARGETS) {
- XChangeProperty(window->display, window->event.xselectionrequest.requestor, window->event.xselectionrequest.property, XA_ATOM, 32, PropModeReplace, (unsigned char*)targets, sizeof(targets) / sizeof(targets[0]));
-
- event.property = window->event.xselectionrequest.property;
- } else {
- event.property = None;
- int i;
- for(i = 0; i < formatCount; i++) {
- if(window->event.xselectionrequest.target == formats[i]) {
- XChangeProperty(window->display, window->event.xselectionrequest.requestor, window->event.xselectionrequest.property, window->event.xselectionrequest.target, 8, PropModeReplace, (unsigned char*)window->clipboard_str, window->clipboard_strlen);
-
- event.property = window->event.xselectionrequest.property;
- break;
- }
- }
- }
-
- XSendEvent(window->display, window->event.xselectionrequest.requestor, False, 0, (XEvent*)&event);
- XFlush(window->display);
- }
- }
-}
-
-inline void platform_show_alert(char *title, char *message)
-{
- char command[MAX_INPUT_LENGTH];
- snprintf(command, MAX_INPUT_LENGTH, "notify-send \"%s\" \"%s\"", title, message);
- platform_run_command(command);
-}
-
-inline void platform_window_swap_buffers(platform_window *window)
-{
- // set cursor if changed
- if (window->curr_cursor_type != window->next_cursor_type)
- {
- int cursor_shape = 0;
- switch(window->next_cursor_type)
- {
- case CURSOR_DEFAULT: cursor_shape = XC_arrow; break;
- case CURSOR_POINTER: cursor_shape = XC_hand1; break;
- }
- Cursor cursor = XCreateFontCursor(window->display, cursor_shape);
- XDefineCursor(window->display, window->window, cursor);
- window->curr_cursor_type = window->next_cursor_type;
- }
-
- glXSwapBuffers(window->display, window->window);
-}
-
-u64 platform_get_time(time_type time_type, time_precision precision)
-{
- s32 type = CLOCK_REALTIME;
- switch(time_type)
- {
- case TIME_FULL: type = CLOCK_REALTIME; break;
- case TIME_THREAD: type = CLOCK_THREAD_CPUTIME_ID; break;
- case TIME_PROCESS: type = CLOCK_PROCESS_CPUTIME_ID; break;
- }
-
- struct timespec tms;
- if (clock_gettime(type,&tms)) {
- return -1;
- }
-
- long result = 0;
-
- if (precision == TIME_NS)
- {
- result = tms.tv_sec * 1000000000;
- result += tms.tv_nsec;
- if (tms.tv_nsec % 1000 >= 500) {
- ++result;
- }
- }
- else if (precision == TIME_US)
- {
- result = tms.tv_sec * 1000000;
- result += tms.tv_nsec/1000;
- if (tms.tv_nsec % 1000 >= 500) {
- ++result;
- }
- }
- else if (precision == TIME_MILI_S)
- {
- result = tms.tv_sec * 1000;
- result += tms.tv_nsec/1000000;
- if (tms.tv_nsec % 1000 >= 500) {
- ++result;
- }
- }
- else if (precision == TIME_S)
- {
- result = tms.tv_sec;
- result += tms.tv_nsec/1000000000;
- if (tms.tv_nsec % 1000 >= 500) {
- ++result;
- }
- }
-
- return result;
-}
-
-inline s32 platform_get_cpu_count()
-{
- return (int)sysconf(_SC_NPROCESSORS_ONLN);
-}
-
-inline s32 platform_get_memory_size()
-{
- uint64_t aid = (uint64_t) sysconf(_SC_PHYS_PAGES);
- aid *= (uint64_t) sysconf(_SC_PAGESIZE);
- aid /= (uint64_t) (1024 * 1024);
- return (int)(aid);
-}
-
-void platform_show_message(platform_window *window, char *message, char *title)
-{
- char command[MAX_INPUT_LENGTH];
- snprintf(command, MAX_INPUT_LENGTH, "zenity --info --text=\"%s\" --title=\"%s\" --width=240", message, title);
- FILE *f = popen(command, "r");
-}
-
-static void* platform_open_file_dialog_thread(void *data)
-{
- struct open_dialog_args *args = data;
-
- FILE *f;
-
- char current_val[MAX_INPUT_LENGTH];
- string_copyn(current_val, args->buffer, MAX_INPUT_LENGTH);
-
- char file_filter[MAX_INPUT_LENGTH];
- file_filter[0] = 0;
- if (args->file_filter)
- snprintf(file_filter, MAX_INPUT_LENGTH, "--file-filter=\"%s\"", args->file_filter);
-
- char start_path[MAX_INPUT_LENGTH];
- start_path[0] = 0;
- if (args->start_path)
- snprintf(start_path, MAX_INPUT_LENGTH, "--filename=\"%s\"", args->start_path);
-
-
- char command[MAX_INPUT_LENGTH];
-
- if (args->type == OPEN_FILE)
- {
- snprintf(command, MAX_INPUT_LENGTH, "zenity --file-selection %s %s", file_filter, start_path);
- }
- else if (args->type == OPEN_DIRECTORY)
- {
- snprintf(command, MAX_INPUT_LENGTH, "zenity --file-selection --directory %s %s", file_filter, start_path);
- }
- else if (args->type == SAVE_FILE)
- {
- snprintf(command, MAX_INPUT_LENGTH, "zenity --file-selection --save --confirm-overwrite %s %s", file_filter, start_path);
- }
-
- f = popen(command, "r");
-
- char buffer[MAX_INPUT_LENGTH];
- char *result = fgets(buffer, MAX_INPUT_LENGTH, f);
-
- if (!result)
- return 0;
-
- // replace newlines with 0, we only want one file path
- s32 len = strlen(buffer);
- for (s32 x = 0; x < len; x++)
- {
- if (buffer[x] == '\n') buffer[x] = 0;
- }
-
- if (strcmp(buffer, current_val) != 0 && strcmp(buffer, "") != 0)
- {
- string_copyn(args->buffer, buffer, MAX_INPUT_LENGTH);
- s32 len = strlen(args->buffer);
- args->buffer[len] = 0;
- }
-
- return 0;
-}
-
-void *platform_open_file_dialog_block(void *arg)
-{
- thread thr = thread_start(platform_open_file_dialog_thread, arg);
- thread_join(&thr);
- mem_free(arg);
- return 0;
-}
-
-void platform_list_files_block(array *list, char *start_dir, array filters, bool recursive, memory_bucket *bucket, bool include_directories, bool *is_cancelled)
-{
- assert(list);
-
- s32 len = 0;
- char *matched_filter = 0;
-
- char *subdirname_buf;
- if (bucket)
- subdirname_buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- subdirname_buf = mem_alloc(MAX_INPUT_LENGTH);
-
- DIR *d;
- struct dirent *dir;
- d = opendir(start_dir);
- if (d) {
- set_active_directory(start_dir);
- while ((dir = readdir(d)) != NULL) {
- if (*is_cancelled) break;
- set_active_directory(start_dir);
-
- if (dir->d_type == DT_DIR)
- {
- if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0))
- continue;
-
- if (include_directories)
- {
- if ((len = filter_matches(&filters, dir->d_name,
- &matched_filter)) && len != -1)
- {
- char *buf;
- if (bucket)
- buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- buf = mem_alloc(MAX_INPUT_LENGTH);
-
- //realpath(dir->d_name, buf);
- snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, dir->d_name);
-
- found_file f;
- f.path = buf;
-
- if (bucket)
- f.matched_filter = memory_bucket_reserve(bucket, len+1);
- else
- f.matched_filter = mem_alloc(len+1);
-
- string_copyn(f.matched_filter, matched_filter, len+1);
-
- mutex_lock(&list->mutex);
- array_push_size(list, &f, sizeof(found_file));
- mutex_unlock(&list->mutex);
- }
- }
-
- if (recursive)
- {
- string_copyn(subdirname_buf, start_dir, MAX_INPUT_LENGTH);
- string_appendn(subdirname_buf, dir->d_name, MAX_INPUT_LENGTH);
- string_appendn(subdirname_buf, "/", MAX_INPUT_LENGTH);
-
- // do recursive search
- platform_list_files_block(list, subdirname_buf, filters, recursive, bucket, include_directories, is_cancelled);
- }
- }
- // we handle DT_UNKNOWN for file systems that do not support type lookup.
- else if (dir->d_type == DT_REG || dir->d_type == DT_UNKNOWN)
- {
- // check if name matches pattern
- if ((len = filter_matches(&filters, dir->d_name,
- &matched_filter)) && len != -1)
- {
- char *buf;
- if (bucket)
- buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- buf = mem_alloc(MAX_INPUT_LENGTH);
-
- //realpath(dir->d_name, buf);
- snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, dir->d_name);
-
- found_file f;
- f.path = buf;
-
- if (bucket)
- f.matched_filter = memory_bucket_reserve(bucket, len+1);
- else
- f.matched_filter = mem_alloc(len+1);
-
- string_copyn(f.matched_filter, matched_filter, len+1);
-
- mutex_lock(&list->mutex);
- array_push_size(list, &f, sizeof(found_file));
- mutex_unlock(&list->mutex);
-
- }
- }
- }
- closedir(d);
- }
-
- if (!bucket)
- mem_free(subdirname_buf);
-}
-
-char *platform_get_full_path(char *file)
-{
- char *buf = mem_alloc(PATH_MAX);
- buf[0] = 0;
-
- char *result = realpath(file, buf);
-
- if (!result)
- {
- buf[0] = 0;
- return buf;
- }
-
- return buf;
-}
-
-inline u64 string_to_u64(char *str)
-{
- return (u64)strtoull(str, 0, 10);
-}
-
-inline u32 string_to_u32(char *str)
-{
- return (u32)strtoul(str, 0, 10);
-}
-
-inline u16 string_to_u16(char *str)
-{
- return (u16)strtoul(str, 0, 10);
-}
-
-inline u8 string_to_u8(char *str)
-{
- return (u8)strtoul(str, 0, 10);
-}
-
-inline s64 string_to_s64(char *str)
-{
- return (s64)strtoll(str, 0, 10);
-}
-
-inline s32 string_to_s32(char *str)
-{
- return (u32)strtol(str, 0, 10);
-}
-
-inline s16 string_to_s16(char *str)
-{
- return (s16)strtol(str, 0, 10);
-}
-
-inline s8 string_to_s8(char *str)
-{
- return (s8)strtol(str, 0, 10);
-}
-
-inline void platform_open_url(char *url)
-{
- char buffer[MAX_INPUT_LENGTH];
- snprintf(buffer, MAX_INPUT_LENGTH, "xdg-open %s", url);
- platform_run_command(buffer);
-}
-
-inline void platform_run_command(char *command)
-{
- s32 result = system(command);
-}
-
-void platform_set_icon(platform_window *window, image *img)
-{
- s32 w = img->width;
- s32 h = img->height;
-
- s32 nelements = (w * h) + 2;
-
- unsigned long data[nelements];
- int i = 0;
- (data)[i++] = w;
- (data)[i++] = h;
-
- for (s32 y = 0; y < h; y++)
- {
- for (s32 x = 0; x < w; x++)
- {
- s32 *pixel = (s32*)(&((data)[i++]));
-
- s32 img_pixel = *(((s32*)img->data+(x+(y*w))));
-
- // 0xAABBGGRR
- s32 a = (img_pixel>>24) & 0x000000FF;
- s32 b = (img_pixel>>16) & 0x000000FF;
- s32 g = (img_pixel>> 8) & 0x000000FF;
- s32 r = (img_pixel>> 0) & 0x000000FF;
-
- //s32 c = (r << 24) | (g << 16) | (b << 8) | (a << 0);
- s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0);
- *pixel = c;
- }
- }
-
- Atom property = XInternAtom(window->display, "_NET_WM_ICON", 0);
- Atom cardinal = XInternAtom(window->display, "CARDINAL", False);
-
- int result = XChangeProperty(window->display, window->window,
- property, cardinal, 32, PropModeReplace,
- (unsigned char *)data, nelements);
-}
diff --git a/src/linux/thread.c b/src/linux/thread.c
deleted file mode 100644
index b6295ef..0000000
--- a/src/linux/thread.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-// stop gcc from reporting implicit declaration warning..
-extern long int syscall (long int __sysno, ...);
-extern int pthread_tryjoin_np(pthread_t thread, void **retval);
-
-thread thread_start(void *(*start_routine) (void *), void *arg)
-{
- thread result;
- result.valid = false;
-
- pthread_attr_t attr;
- int attr_init_result = pthread_attr_init(&attr);
- if (attr_init_result)
- return result;
-
- int start_thread_result = pthread_create(&result.thread, &attr, start_routine, arg);
- if (start_thread_result)
- {
- pthread_attr_destroy(&attr);
- return result;
- }
-
- result.valid = true;
- pthread_attr_destroy(&attr);
-
- return result;
-}
-
-inline void thread_detach(thread *thread)
-{
- if (thread->valid)
- {
- pthread_detach(thread->thread);
- }
-}
-
-inline void thread_join(thread *thread)
-{
- if (thread->valid)
- {
- void *retval;
- pthread_join(thread->thread, &retval);
- }
-}
-
-bool thread_tryjoin(thread *thread)
-{
- if (thread->valid)
- {
- void *retval;
- bool thread_joined = !pthread_tryjoin_np(thread->thread, &retval);
- return thread_joined;
- }
- return false;
-}
-
-inline void thread_stop(thread *thread)
-{
- if (thread->valid)
- {
- pthread_cancel(thread->thread);
- }
-}
-
-mutex mutex_create()
-{
- mutex result;
-
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
-
- pthread_mutex_init(&result.mutex, &attr);
-
- pthread_mutexattr_destroy(&attr);
-
- return result;
-}
-
-mutex mutex_create_recursive()
-{
- mutex result;
-
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-
- pthread_mutex_init(&result.mutex, &attr);
-
- pthread_mutexattr_destroy(&attr);
-
- return result;
-}
-
-inline void mutex_lock(mutex *mutex)
-{
- pthread_mutex_lock(&mutex->mutex);
-}
-
-inline bool mutex_trylock(mutex *mutex)
-{
- return !pthread_mutex_trylock(&mutex->mutex);
-}
-
-inline void mutex_unlock(mutex *mutex)
-{
- pthread_mutex_unlock(&mutex->mutex);
-}
-
-inline void mutex_destroy(mutex *mutex)
-{
- mutex_unlock(mutex);
- pthread_mutex_destroy(&mutex->mutex);
-}
-
-inline u32 thread_get_id()
-{
- return (u32)syscall(__NR_gettid);
-}
-
-inline void thread_sleep(u64 microseconds)
-{
- usleep(microseconds);
-}
diff --git a/src/localization.c b/src/localization.c
deleted file mode 100644
index 2ee39e3..0000000
--- a/src/localization.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-mo_file load_localization_file(u8 *start_addr, u8 *end_addr, u8 *img_start, u8 *img_end,
- char *locale_id, char *locale_name)
-{
- mo_file mo;
- mo.translations = array_create(sizeof(mo_translation));
-
- {
- mo.header = *(mo_header*)start_addr;
- mo.locale_id = mem_alloc(strlen(locale_id)+1);
- string_copyn(mo.locale_id, locale_id, strlen(locale_id)+1);
-
- mo.locale_full = mem_alloc(strlen(locale_name)+1);
- string_copyn(mo.locale_full, locale_name, strlen(locale_name)+1);
-
- mo.icon = assets_load_image(img_start, img_end, false);
-
- char *buffer = (char*)start_addr;
- mo_entry *identifiers = (mo_entry*)(buffer + mo.header.identifier_table_offset);
- mo_entry *translations = (mo_entry*)(buffer + mo.header.translation_table_offset);
-
- for (s32 i = 0; i < mo.header.number_of_strings; i++)
- {
- mo_entry *entry = &identifiers[i];
- mo_entry *trans = &translations[i];
-
- mo_translation translation;
- translation.identifier_len = entry->length;
- translation.identifier = buffer+entry->offset;
- translation.translation = buffer+trans->offset;
-
- array_push(&mo.translations, &translation);
- //printf("%s=%s\n", translation.identifier, translation.translation);
- }
- }
-
- return mo;
-}
-
-char* locale_get_name()
-{
- if (!global_localization.active_localization)
- {
- return "[NO LOCALE]";
- }
-
- return global_localization.active_localization->locale_full;
-}
-
-char* locale_get_id()
-{
- if (!global_localization.active_localization)
- {
- return "[NO LOCALE]";
- }
-
- return global_localization.active_localization->locale_id;
-}
-
-bool set_locale(char *country_id)
-{
- if (country_id == 0 && global_localization.mo_files.length)
- {
- global_localization.active_localization = array_at(&global_localization.mo_files, 0);
- return true;
- }
-
- for (s32 i = 0; i < global_localization.mo_files.length; i++)
- {
- mo_file *file = array_at(&global_localization.mo_files, i);
- if (strcmp(file->locale_id, country_id) == 0)
- {
- global_localization.active_localization = file;
- return true;
- }
- }
-
- // if localization is not found, default to first in list (english), return false to report error
- if (global_localization.mo_files.length)
- global_localization.active_localization = array_at(&global_localization.mo_files, 0);
- else
- global_localization.active_localization = 0;
-
- return false;
-}
-
-char* localize(const char *identifier)
-{
- if (!global_localization.active_localization)
- {
- //printf("NO LOCALE SELECTED.");
- return (char*)identifier;
- }
-
- s32 len = strlen(identifier);
- for (s32 i = 0; i < global_localization.active_localization->translations.length; i++)
- {
- mo_translation *trans = array_at(&global_localization.active_localization->translations, i);
-
- if (trans->identifier_len == len && strcmp(identifier, trans->identifier) == 0)
- {
- return trans->translation;
- }
- }
- printf("MISSING TRANSLATION: [%s][%s]\n", identifier, global_localization.active_localization->locale_id);
- return "MISSING";
-}
-
-void load_available_localizations()
-{
- global_localization.mo_files = array_create(sizeof(mo_file));
- array_reserve(&global_localization.mo_files, 10);
-
- mo_file en = load_localization_file(_binary____data_translations_en_English_mo_start,
- _binary____data_translations_en_English_mo_end,
- _binary____data_imgs_en_png_start,
- _binary____data_imgs_en_png_end,
- "en", "English");
-
- mo_file nl = load_localization_file(_binary____data_translations_nl_Dutch_mo_start,
- _binary____data_translations_nl_Dutch_mo_end,
- _binary____data_imgs_nl_png_start,
- _binary____data_imgs_nl_png_end,
- "nl", "Dutch");
-
- array_push(&global_localization.mo_files, &en);
- array_push(&global_localization.mo_files, &nl);
-}
-
-void destroy_available_localizations()
-{
- for (s32 i = 0; i < global_localization.mo_files.length; i++)
- {
- mo_file *file = array_at(&global_localization.mo_files, i);
- array_destroy(&file->translations);
- mem_free(file->locale_id);
- mem_free(file->locale_full);
-
- if (file->icon)
- assets_destroy_image(file->icon);
- }
- array_destroy(&global_localization.mo_files);
-}
diff --git a/src/localization.h b/src/localization.h
deleted file mode 100644
index 4a35bf1..0000000
--- a/src/localization.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_LOCALIZATION
-#define INCLUDE_LOCALIZATION
-
-// https://www.science.co.il/language/Locale-codes.php
-
-typedef struct t_mo_entry
-{
- s32 length;
- s32 offset;
-} mo_entry;
-
-typedef struct t_mo_translation
-{
- s32 identifier_len;
- char *identifier;
- char *translation;
-} mo_translation;
-
-typedef struct t_mo_header
-{
- s32 magic_number;
- s32 file_format_revision;
- s32 number_of_strings;
- s32 identifier_table_offset;
- s32 translation_table_offset;
- s32 hashtable_size;
- s32 hashtable_offset;
-} mo_header;
-
-typedef struct t_mo_file
-{
- mo_header header;
- array translations;
- char *locale_id;
- char *locale_full;
- image *icon;
-} mo_file;
-
-typedef struct t_localization
-{
- array mo_files;
- mo_file *active_localization;
-} localization;
-
-localization global_localization;
-
-char* locale_get_id();
-char* locale_get_name();
-char* localize(const char *identifier);
-bool set_locale(char *country_id);
-void load_available_localizations();
-
-#endif \ No newline at end of file
diff --git a/src/memory.h b/src/memory.h
deleted file mode 100644
index f63edef..0000000
--- a/src/memory.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_MEMORY
-#define INCLUDE_MEMORY
-
-#define mem_alloc(size) malloc(size)
-#define mem_free(p) free(p)
-#define mem_realloc(p, size) realloc(p, size)
-#define memory_print_leaks() {}
-
-#define STBI_MALLOC(sz) mem_alloc(sz)
-#define STBI_REALLOC(p, newsz) mem_realloc(p, newsz)
-#define STBI_FREE(p) mem_free(p)
-
-#endif \ No newline at end of file
diff --git a/src/memory_bucket.c b/src/memory_bucket.c
deleted file mode 100644
index bc9f7e9..0000000
--- a/src/memory_bucket.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-inline memory_bucket memory_bucket_init(s32 bucket_size)
-{
- assert(bucket_size >= MAX_INPUT_LENGTH);
-
- memory_bucket collection;
- collection.bucket_mutex = mutex_create();
- collection.buckets = array_create(sizeof(memory_bucket_entry));
-
- memory_bucket_entry bucket;
- bucket.data = mem_alloc(bucket_size);
- bucket.length = bucket_size;
- bucket.cursor = 0;
- array_push(&collection.buckets, &bucket);
- return collection;
-}
-
-void* memory_bucket_reserve(memory_bucket *bucket, s32 reserve_length)
-{
- mutex_lock(&bucket->bucket_mutex);
- memory_bucket_entry *bucket_entry = 0;
- for (s32 i = 0; i < bucket->buckets.length; i++)
- {
- bucket_entry = array_at(&bucket->buckets, i);
-
- if (bucket_entry->length - bucket_entry->cursor < reserve_length) continue;
-
- void *space = bucket_entry->data+bucket_entry->cursor;
- bucket_entry->cursor += reserve_length;
- mutex_unlock(&bucket->bucket_mutex);
-
- return space;
- }
-
- // failed to find suitable space, allocate new bucket
- memory_bucket_entry new_bucket;
- new_bucket.data = mem_alloc(bucket_entry->length);
- new_bucket.length = bucket_entry->length;
- new_bucket.cursor = 0;
- array_push(&bucket->buckets, &new_bucket);
- mutex_unlock(&bucket->bucket_mutex);
-
- return new_bucket.data;
-}
-
-inline void memory_bucket_reset(memory_bucket *bucket)
-{
- mutex_lock(&bucket->bucket_mutex);
- for (s32 i = 0; i < bucket->buckets.length; i++)
- {
- memory_bucket_entry *bucket_entry = array_at(&bucket->buckets, i);
- bucket_entry->cursor = 0;
- }
- mutex_unlock(&bucket->bucket_mutex);
-}
-
-inline void memory_bucket_destroy(memory_bucket *bucket)
-{
- mutex_lock(&bucket->bucket_mutex);
- for (s32 i = 0; i < bucket->buckets.length; i++)
- {
- memory_bucket_entry *bucket_entry = array_at(&bucket->buckets, i);
- mem_free(bucket_entry->data);
- }
- array_destroy(&bucket->buckets);
- mutex_unlock(&bucket->bucket_mutex);
-
- mutex_destroy(&bucket->bucket_mutex);
-} \ No newline at end of file
diff --git a/src/memory_bucket.h b/src/memory_bucket.h
deleted file mode 100644
index 180af80..0000000
--- a/src/memory_bucket.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#define kilobytes(num) num*1000
-#define megabytes(num) kilobytes(num*1000)
-
-typedef struct t_memory_bucket_entry
-{
- char *data;
- s32 length;
- s32 cursor;
-} memory_bucket_entry;
-
-typedef struct t_memory_bucket
-{
- mutex bucket_mutex;
- array buckets;
-} memory_bucket;
-
-memory_bucket memory_bucket_init(s32 bucket_size);
-void* memory_bucket_reserve(memory_bucket *bucket, s32 reserve_length);
-void memory_bucket_reset(memory_bucket *bucket);
-void memory_bucket_destroy(memory_bucket *bucket); \ No newline at end of file
diff --git a/src/mo_edit.c b/src/mo_edit.c
index bbd12ee..4843809 100644
--- a/src/mo_edit.c
+++ b/src/mo_edit.c
@@ -8,16 +8,12 @@
#include "project_base.h"
// TODO(Aldrik): option to disable menu item
-// TODO(Aldrik): move the delete button for term to edit panel on the topright and put a exclamation mark at the old spot to indicate a missing translation
+// TODO(Aldrik): move the delete button for term to edit panel on the topright
// TODO(Aldrik): language mo file name preview when entering name
// TODO(Aldrik): option to mark languages with colors
// TODO(Aldrik): change save icon
-// TODO(Aldrik): scissor on textbox in scroll
// TODO(Aldrik): delete file of language when deleting language
// TODO(Aldrik): confirmation on deletion of term and language (replace delete button with "Confirm" button to confirm deletion)
-// TODO(Aldrik): reset textbox vertical scroll when new term is selected
-// TODO(Aldrik): ctrl+x to select+delete selection
-// TODO(Aldrik): ctrl+del to delete up to space char
s32 global_language_id = 1;
char project_path[MAX_INPUT_LENGTH];
@@ -77,10 +73,6 @@ image *logo_small_img;
font *font_medium;
font *font_small;
font *font_mini;
-s32 scroll_y = 0;
-
-#include "settings.h"
-#include "settings.c"
static void load_assets()
{
@@ -389,7 +381,7 @@ int main(int argc, char **argv)
bool is_command_line_run = (argc > 1);
if (is_command_line_run)
{
- handle_command_line_arguments(argc, argv);
+ //handle_command_line_arguments(argc, argv);
return 0;
}
@@ -410,7 +402,7 @@ int main(int argc, char **argv)
platform_window window = platform_open_window("mo-edit", window_w, window_h, 0, 0, 800, 600);
main_window = &window;
- settings_page_create();
+ //settings_page_create();
load_available_localizations();
set_locale("en");
@@ -453,7 +445,7 @@ int main(int argc, char **argv)
platform_handle_events(&window, &mouse, &keyboard);
platform_set_cursor(&window, CURSOR_DEFAULT);
- settings_page_update_render();
+ //settings_page_update_render();
platform_window_make_current(&window);
@@ -510,6 +502,7 @@ int main(int argc, char **argv)
}
}
ui_end_menu_bar();
+ ui_push_separator();
// TODO(Aldrik): make this a setting, resizable panel
@@ -831,7 +824,7 @@ int main(int argc, char **argv)
}
}
- settings_page_hide_without_save();
+ //settings_page_hide_without_save();
// write config file
if (!string_equals(project_path, ""))
@@ -853,7 +846,7 @@ int main(int argc, char **argv)
settings_config_write_to_file(&config, config_path_buffer);
settings_config_destroy(&config);
- settings_page_destroy();
+ //settings_page_destroy();
destroy_available_localizations();
diff --git a/src/platform.h b/src/platform.h
deleted file mode 100644
index b9f242c..0000000
--- a/src/platform.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_PLATFORM
-#define INCLUDE_PLATFORM
-
-typedef struct t_platform_window platform_window;
-
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-
-typedef struct t_found_file
-{
- char *matched_filter;
- char *path;
-} found_file;
-
-typedef struct t_file_match
-{
- found_file file;
- s16 file_error;
- s32 file_size;
-
- u32 line_nr;
- s32 word_match_offset;
- s32 word_match_length;
- s32 word_match_offset_x; // highlight render offset
- s32 word_match_width; // highlight render width
- char *line_info; // will be null when no match is found
-} file_match;
-
-typedef struct t_search_result
-{
- array work_queue;
- array files;
- array matches;
- u64 find_duration_us;
- array errors;
- bool show_error_message; // error occured
- bool found_file_matches; // found/finding file matches
- s32 files_searched;
- s32 files_matched;
- s32 search_result_source_dir_len;
- bool match_found; // found text match
- mutex mutex;
- bool walking_file_system;
- bool cancel_search;
- bool done_finding_matches;
- s32 search_id;
- u64 start_time;
- bool done_finding_files;
- memory_bucket mem_bucket;
- bool is_command_line_search;
- bool threads_closed;
-
- char *export_path;
- char *file_filter;
- char *directory_to_search;
- char *text_to_find;
- s32 max_thread_count;
- s32 max_file_size;
- bool is_recursive;
-} search_result;
-
-typedef struct t_find_text_args
-{
- file_match file;
- search_result *search_result_buffer;
-} find_text_args;
-
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-
-typedef struct t_file_content
-{
- s64 content_length;
- void *content;
- s16 file_error;
-} file_content;
-
-typedef enum t_time_type
-{
- TIME_FULL, // realtime
- TIME_THREAD, // run time for calling thread
- TIME_PROCESS, // run time for calling process
-} time_type;
-
-typedef enum t_time_precision
-{
- TIME_NS, // nanoseconds
- TIME_US, // microseconds
- TIME_MILI_S, // miliseconds
- TIME_S, // seconds
-} time_precision;
-
-typedef struct t_cpu_info
-{
- s32 model;
- char model_name[255];
- float32 frequency;
- u32 cache_size;
- u32 cache_alignment;
-} cpu_info;
-
-typedef enum t_file_dialog_type
-{
- OPEN_FILE,
- OPEN_DIRECTORY,
- SAVE_FILE,
-} file_dialog_type;
-
-typedef enum t_file_open_error
-{
- FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS = 1,
- FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM = 2,
- FILE_ERROR_NO_ACCESS = 3,
- FILE_ERROR_NOT_FOUND = 4,
- FILE_ERROR_CONNECTION_ABORTED = 5,
- FILE_ERROR_CONNECTION_REFUSED = 6,
- FILE_ERROR_NETWORK_DOWN = 7,
- FILE_ERROR_REMOTE_IO_ERROR = 8,
- FILE_ERROR_STALE = 9, // NFS server file is removed/renamed
- FILE_ERROR_GENERIC = 10,
-} file_open_error;
-
-struct open_dialog_args
-{
- char *buffer;
- char *file_filter;
- char *start_path;
- char *default_save_file_extension;
- file_dialog_type type;
-};
-
-typedef struct t_list_file_args
-{
- array *list;
- char *start_dir;
- char *pattern;
- bool recursive;
- bool include_directories;
- bool *state;
- bool *is_cancelled;
- memory_bucket *bucket;
-} list_file_args;
-
-typedef enum t_cursor_type
-{
- CURSOR_DEFAULT,
- CURSOR_POINTER,
-} cursor_type;
-
-typedef struct t_vec2
-{
- s32 x;
- s32 y;
-} vec2;
-
-platform_window *main_window = 0;
-platform_window *settings_window = 0;
-
-bool platform_window_is_valid(platform_window *window);
-platform_window platform_open_window(char *name, u16 width, u16 height, u16 max_w, u16 max_h, u16 min_w, u16 min_h);
-void platform_get_focus(platform_window *window);
-bool platform_set_clipboard(platform_window *window, char *buffer);
-bool platform_get_clipboard(platform_window *window, char *buffer);
-void platform_window_set_size(platform_window *window, u16 width, u16 height);
-void platform_window_set_position(platform_window *window, u16 x, u16 y);
-void platform_destroy_window(platform_window *window);
-void platform_handle_events(platform_window *window, mouse_input *mouse, keyboard_input *keyboard);
-void platform_window_swap_buffers(platform_window *window);
-void platform_set_cursor(platform_window *window, cursor_type type);
-void platform_window_set_title(platform_window *window, char *name);
-file_content platform_read_file_content(char *path, const char *mode);
-bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 len);
-void platform_destroy_file_content(file_content *content);
-bool get_active_directory(char *buffer);
-bool set_active_directory(char *path);
-void platform_show_message(platform_window *window, char *message, char *title);
-array get_filters(char *filter);
-void platform_list_files_block(array *list, char *start_dir, array filters, bool recursive, memory_bucket *bucket, bool include_directories, bool *is_cancelled);
-void platform_list_files(array *list, char *start_dir, char *filter, bool recursive, memory_bucket *bucket, bool *is_cancelled, bool *state);
-void platform_open_file_dialog(file_dialog_type type, char *buffer, char *file_filter, char *start_path);
-bool is_platform_in_darkmode();
-void *platform_open_file_dialog_block(void *arg);
-char *platform_get_full_path(char *file);
-void platform_open_url(char *command);
-void platform_run_command(char *command);
-void platform_window_make_current(platform_window *window);
-void platform_init(int argc, char **argv);
-void platform_destroy();
-void platform_set_icon(platform_window *window, image *img);
-void platform_autocomplete_path(char *buffer, bool want_dir);
-bool platform_directory_exists(char *path);
-void platform_create_directory(char *path);
-bool platform_file_exists(char *path);
-void platform_show_alert(char *title, char *message);
-char *get_config_save_location(char *buffer);
-char *get_file_extension(char *path);
-void get_name_from_path(char *buffer, char *path);
-void get_directory_from_path(char *buffer, char *path);
-vec2 platform_get_window_size(platform_window *window);
-s32 filter_matches(array *filters, char *string, char **matched_filter);
-
-u64 platform_get_time(time_type time_type, time_precision precision);
-s32 platform_get_memory_size();
-s32 platform_get_cpu_count();
-
-u64 string_to_u64(char *str);
-u32 string_to_u32(char *str);
-u16 string_to_u16(char *str);
-u8 string_to_u8(char *str);
-
-s64 string_to_s64(char *str);
-s32 string_to_s32(char *str);
-s16 string_to_s16(char *str);
-s8 string_to_s8(char *str);
-
-#endif \ No newline at end of file
diff --git a/src/platform_shared.c b/src/platform_shared.c
deleted file mode 100644
index 4107ca3..0000000
--- a/src/platform_shared.c
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-void get_name_from_path(char *buffer, char *path)
-{
- buffer[0] = 0;
-
- s32 len = strlen(path);
- if (len == 1)
- {
- return;
- }
-
- char *path_end = path + len;
-#ifdef OS_LINUX
- while (*path_end != '/' && path_end >= path)
- {
- --path_end;
- }
-#endif
-#ifdef OS_WIN
- while (*path_end != '\\' && path_end >= path)
- {
- --path_end;
- }
-#endif
-
- string_copyn(buffer, path_end+1, MAX_INPUT_LENGTH);
-}
-
-void get_directory_from_path(char *buffer, char *path)
-{
- buffer[0] = 0;
-
- s32 len = strlen(path);
- if (len == 1)
- {
- return;
- }
-
- char *path_end = path + len;
-#ifdef OS_LINUX
- while (*path_end != '/' && path_end >= path)
- {
- --path_end;
- }
-#endif
-#ifdef OS_WIN
- while (*path_end != '\\' && path_end >= path)
- {
- --path_end;
- }
-#endif
-
- s32 offset = path_end - path;
- char ch = path[offset+1];
- path[offset+1] = 0;
- string_copyn(buffer, path, MAX_INPUT_LENGTH);
- path[offset+1] = ch;
-}
-
-void platform_autocomplete_path(char *buffer, bool want_dir)
-{
- char dir[MAX_INPUT_LENGTH];
- char name[MAX_INPUT_LENGTH];
- get_directory_from_path(dir, buffer);
- get_name_from_path(name, buffer);
-
- // nothing to autocomplete
- if (name[0] == 0)
- {
- return;
- }
-
- // create filter
- string_appendn(name, "*", MAX_INPUT_LENGTH);
-
- array files = array_create(sizeof(found_file));
- array filters = get_filters(name);
- bool is_cancelled = false;
- platform_list_files_block(&files, dir, filters, false, 0, want_dir, &is_cancelled);
-
- s32 index_to_take = -1;
- if (want_dir)
- {
- for (s32 i = 0; i < files.length; i++)
- {
- found_file *file = array_at(&files, i);
-
- if (platform_directory_exists(file->path))
- {
- index_to_take = i;
- break;
- }
- }
- }
- else
- {
- index_to_take = 0;
- }
-
- array_destroy(&filters);
-
- if (files.length > 0 && index_to_take != -1)
- {
- found_file *file = array_at(&files, index_to_take);
- string_copyn(buffer, file->path, MAX_INPUT_LENGTH);
- }
-
- for (s32 i = 0; i < files.length; i++)
- {
- found_file *match = array_at(&files, i);
- mem_free(match->matched_filter);
- mem_free(match->path);
- }
- array_destroy(&files);
-}
-
-array get_filters(char *pattern)
-{
- array result = array_create(MAX_INPUT_LENGTH);
-
- char current_filter[MAX_INPUT_LENGTH];
- s32 filter_len = 0;
- while(*pattern)
- {
- char ch = *pattern;
-
- if (ch == ',')
- {
- current_filter[filter_len] = 0;
- array_push(&result, current_filter);
- filter_len = 0;
- }
- else
- {
- if(filter_len < MAX_INPUT_LENGTH-1)
- {
- current_filter[filter_len++] = ch;
- }
- else
- {
- current_filter[filter_len] = ch;
- }
- }
-
- pattern++;
- }
- current_filter[filter_len] = 0;
- array_push(&result, current_filter);
-
- return result;
-}
-
-void *platform_list_files_thread(void *args)
-{
- list_file_args *info = args;
-
- array filters = get_filters(info->pattern);
-
- array *list = info->list;
- char *start_dir = info->start_dir;
- bool recursive = info->recursive;
-
- platform_list_files_block(info->list, info->start_dir, filters, info->recursive, info->bucket, info->include_directories, info->is_cancelled);
-
- mutex_lock(&info->list->mutex);
- //if (!(*info->is_cancelled))
- *(info->state) = true;
- mutex_unlock(&info->list->mutex);
-
- array_destroy(&filters);
-
- return 0;
-}
-
-void platform_list_files(array *list, char *start_dir, char *filter, bool recursive, memory_bucket *bucket, bool *is_cancelled, bool *state)
-{
- list_file_args *args = memory_bucket_reserve(bucket, sizeof(list_file_args));
- args->list = list;
- args->start_dir = start_dir;
- args->pattern = filter;
- args->recursive = recursive;
- args->state = state;
- args->include_directories = 0;
- args->bucket = bucket;
- args->is_cancelled = is_cancelled;
-
- thread thr = thread_start(platform_list_files_thread, args);
- thread_detach(&thr);
-}
-
-void platform_open_file_dialog(file_dialog_type type, char *buffer, char *file_filter, char *start_path)
-{
- struct open_dialog_args *args = mem_alloc(sizeof(struct open_dialog_args));
- args->buffer = buffer;
- args->type = type;
- args->file_filter = file_filter;
- args->start_path = start_path;
-
- thread thr;
- thr.valid = false;
-
- while (!thr.valid)
- thr = thread_start(platform_open_file_dialog_block, args);
- thread_detach(&thr);
-}
-
-void destroy_found_file_array(array *found_files)
-{
- for (s32 i = 0; i < found_files->length; i++)
- {
- found_file *f = array_at(found_files, i);
- mem_free(f->matched_filter);
- mem_free(f->path);
- }
- array_destroy(found_files);
-}
-
-char *get_file_extension(char *path)
-{
- while(*path != '.' && *path)
- {
- path++;
- }
- return path;
-}
-
-s32 filter_matches(array *filters, char *string, char **matched_filter)
-{
- for (s32 i = 0; i < filters->length; i++)
- {
- char *filter = array_at(filters, i);
- if (string_match(filter, string))
- {
- *matched_filter = filter;
- return strlen(filter);
- }
- }
- return -1;
-}
diff --git a/src/project-base b/src/project-base
new file mode 160000
+Subproject 6511e9516eb9e749dc7d2efe2f9f88103d83713
diff --git a/src/project_base.h b/src/project_base.h
index 6bfa7e1..5290a0c 100644
--- a/src/project_base.h
+++ b/src/project_base.h
@@ -24,6 +24,7 @@
#include "stdint.h"
#include "string.h"
+#include "assert.h"
#include <GL/gl.h>
#ifdef OS_LINUX
@@ -55,56 +56,60 @@
#define true 1
#define false 0
-#include "thread.h"
-#include "array.h"
-#include "memory.h"
+#include "asset_definitions.h"
+
+#include "project-base/src/thread.h"
+#include "project-base/src/array.h"
+#include "project-base/src/memory.h"
#define STB_IMAGE_IMPLEMENTATION
-#include "external/stb_image.h"
+#include "project-base/src/external/stb_image.h"
#define STB_TRUETYPE_IMPLEMENTATION
-#include "external/stb_truetype.h"
-
-#include "external/utf8.h"
-#include "input.h"
-#include "assets.h"
-#include "memory_bucket.h"
-#include "platform.h"
-#include "render.h"
-#include "camera.h"
-#include "ui.h"
-#include "string_utils.h"
-#include "settings_config.h"
-#include "localization.h"
-#include "command_line.h"
-
-#include "platform_shared.c"
+#include "project-base/src/external/stb_truetype.h"
+
+#include "project-base/src/external/utf8.h"
+#include "project-base/src/input.h"
+#include "project-base/src/assets.h"
+#include "project-base/src/memory_bucket.h"
+#include "project-base/src/platform.h"
+#include "project-base/src/render.h"
+#include "project-base/src/camera.h"
+#include "project-base/src/ui.h"
+#include "project-base/src/string_utils.h"
+#include "project-base/src/settings_config.h"
+#include "project-base/src/localization.h"
+
+#include "project-base/src/platform_shared.c"
#ifdef OS_LINUX
#define DEFAULT_DIRECTORY "/home/"
-#include "linux/thread.c"
-#include "linux/platform.c"
+#define CONFIG_DIRECTORY "/.config/moedit"
+
+#include "project-base/src/linux/thread.c"
+#include "project-base/src/linux/platform.c"
#endif
#ifdef OS_WIN
#define DEFAULT_DIRECTORY "C:/"
-#include "windows/thread.c"
-#include "windows/platform.c"
+#define CONFIG_DIRECTORY "\\moedit"
+
+#include "project-base/src/windows/thread.c"
+#include "project-base/src/windows/platform.c"
#endif
-#include "input.c"
-#include "array.c"
-#include "assets.c"
-#include "render.c"
-#include "camera.c"
-#include "ui.c"
-#include "string_utils.c"
-#include "settings_config.c"
-#include "localization.c"
-#include "memory_bucket.c"
-#include "command_line.c"
-
-#include "external/cJSON.h"
-#include "external/cJSON.c"
+#include "project-base/src/input.c"
+#include "project-base/src/array.c"
+#include "project-base/src/assets.c"
+#include "project-base/src/render.c"
+#include "project-base/src/camera.c"
+#include "project-base/src/ui.c"
+#include "project-base/src/string_utils.c"
+#include "project-base/src/settings_config.c"
+#include "project-base/src/localization.c"
+#include "project-base/src/memory_bucket.c"
+
+#include "project-base/src/external/cJSON.h"
+#include "project-base/src/external/cJSON.c"
#endif \ No newline at end of file
diff --git a/src/render.c b/src/render.c
deleted file mode 100644
index 666b2f9..0000000
--- a/src/render.c
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-inline void render_clear()
-{
- glClearColor(255/255.0, 255/255.0, 255/255.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-}
-
-inline void render_set_rotation(float32 rotation, float32 x, float32 y, s32 depth)
-{
- glRotatef(rotation, x, y, depth);
-}
-
-inline void set_render_depth(s32 depth)
-{
- render_depth = depth;
-}
-
-void render_image(image *image, s32 x, s32 y, s32 width, s32 height)
-{
- assert(image);
- if (image->loaded)
- {
- glBindTexture(GL_TEXTURE_2D, image->textureID);
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glColor4f(1., 1., 1., 1.);
- glTexCoord2i(0, 0); glVertex3i(x, y, render_depth);
- glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth);
- glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth);
- glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth);
- glEnd();
-
- glDisable(GL_TEXTURE_2D);
- }
-}
-
-void render_image_tint(image *image, s32 x, s32 y, s32 width, s32 height, color tint)
-{
- assert(image);
- if (image->loaded)
- {
- glBindTexture(GL_TEXTURE_2D, image->textureID);
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
- glTexCoord2i(0, 0); glVertex3i(x, y, render_depth);
- glTexCoord2i(0, 1); glVertex3i(x, y+height, render_depth);
- glTexCoord2i(1, 1); glVertex3i(x+width, y+height, render_depth);
- glTexCoord2i(1, 0); glVertex3i(x+width, y, render_depth);
- glEnd();
-
- glDisable(GL_TEXTURE_2D);
- }
-}
-
-s32 render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint)
-{
- if (!font->loaded)
- return 0;
-
- glEnable(GL_TEXTURE_2D);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
-
- char *ellipse = "...";
- bool in_ellipse = false;
-
- s32 x_ = x;
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- glyph g = font->glyphs[ch];
-
- glBindTexture(GL_TEXTURE_2D, g.textureID);
- glBegin(GL_QUADS);
-
- s32 width = g.width;
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- s32 y_ = y + font->px_h + g.yoff;
- s32 x_to_render = x_ + (lsb*font->scale);
-
- glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth);
- glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth);
- glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth);
- glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth);
-
- glEnd();
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x_ += (advance*font->scale)+(kern*font->scale);
-
- if (!in_ellipse && (x_-x) > maxw-(font->glyphs['.'].width*3))
- {
- in_ellipse = true;
- text = ellipse;
- }
- }
-
- glDisable(GL_TEXTURE_2D);
-
- return maxw;
-}
-
-s32 render_text(font *font, s32 x, s32 y, char *text, color tint)
-{
- if (!font->loaded)
- return 0;
-
- glEnable(GL_TEXTURE_2D);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
-
- s32 x_ = x;
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- glyph g = font->glyphs[ch];
-
- glBindTexture(GL_TEXTURE_2D, g.textureID);
- glBegin(GL_QUADS);
-
- s32 width = g.width;
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- s32 y_ = y + font->px_h + g.yoff;
- s32 x_to_render = x_ + (lsb*font->scale);
-
- glTexCoord2i(0, 0); glVertex3i(x_to_render,y_, render_depth);
- glTexCoord2i(0, 1); glVertex3i(x_to_render,y_+g.height, render_depth);
- glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y_+g.height, render_depth);
- glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y_, render_depth);
-
- glEnd();
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x_ += (advance*font->scale)+(kern*font->scale);
- }
-
- glDisable(GL_TEXTURE_2D);
-
- return x_ - x;
-}
-
-s32 render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width)
-{
- if (!font->loaded)
- return 0;
-
- glEnable(GL_TEXTURE_2D);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
-
- s32 x_ = x;
- s32 y_ = y;
- bool is_new_line = false;
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- if (ch == '\n')
- {
- x_ = x;
- y_ += font->size;
- is_new_line = true;
- continue;
- }
-
- if (is_new_line && ch == ' ')
- {
- is_new_line = false;
- continue;
- }
- else if (is_new_line && ch != ' ')
- {
- is_new_line = false;
- }
-
-
- glyph g = font->glyphs[ch];
-
- glBindTexture(GL_TEXTURE_2D, g.textureID);
- glBegin(GL_QUADS);
-
- s32 width = g.width;
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- s32 y__ = y_ + font->px_h + g.yoff;
- s32 x_to_render = x_ + (lsb*font->scale);
-
- glTexCoord2i(0, 0); glVertex3i(x_to_render,y__, render_depth);
- glTexCoord2i(0, 1); glVertex3i(x_to_render,y__+g.height, render_depth);
- glTexCoord2i(1, 1); glVertex3i(x_to_render+g.width,y__+g.height, render_depth);
- glTexCoord2i(1, 0); glVertex3i(x_to_render+g.width,y__, render_depth);
-
- glEnd();
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x_ += (advance*font->scale)+(kern*font->scale);
-
- if (x_ > x+cutoff_width)
- {
- x_ = x;
- y_ += font->size;
- is_new_line = true;
- }
- }
-
- glDisable(GL_TEXTURE_2D);
-
- return (y_ - y) + font->size;
-
-}
-
-s32 calculate_cursor_position(font *font, char *text, s32 click_x)
-{
- if (!font->loaded)
- return 0;
-
- s32 x = 0;
- s32 index = 0;
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
-
- glyph g = font->glyphs[ch];
-
- s32 width = g.width;
- s32 width_next = font->glyphs[ch_next].width;
-
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x += (advance*font->scale)+(kern*font->scale);
-
- if (x - (width_next/5) > click_x)
- {
- return index;
- }
-
- ++index;
- }
-
- return index;
-}
-
-s32 calculate_text_width_from_upto(font *font, char *text, s32 from, s32 index)
-{
- if (!font->loaded)
- return 0;
-
- s32 x = 0;
- utf8_int32_t ch;
- s32 i = 0;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (index == i) return x;
-
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- glyph g = font->glyphs[ch];
- s32 width = g.width;
-
- if (i >= from)
- {
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x += (advance*font->scale)+(kern*font->scale);
- }
-
- i++;
- }
-
- return x;
-}
-
-s32 calculate_text_width_upto(font *font, char *text, s32 index)
-{
- if (!font->loaded)
- return 0;
-
- s32 x = 0;
- utf8_int32_t ch;
- s32 i = 0;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (index == i) return x;
-
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- glyph g = font->glyphs[ch];
- s32 width = g.width;
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x += (advance*font->scale)+(kern*font->scale);
-
- i++;
- }
-
- return x;
-}
-
-s32 calculate_text_width(font *font, char *text)
-{
- if (!font->loaded)
- return 0;
-
- s32 x = 0;
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch == 9) ch = 32;
- utf8_int32_t ch_next;
- utf8codepoint(text, &ch_next);
- if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END)
- {
- ch = 0x3f;
- }
-
- glyph g = font->glyphs[ch];
- s32 width = g.width;
-
- int advance, lsb, kern;
- stbtt_GetCodepointHMetrics(&font->info, ch, &advance, &lsb);
-
- /* add kerning */
- kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
- x += (advance*font->scale)+(kern*font->scale);
- }
-
- return x;
-}
-
-void render_triangle(s32 x, s32 y, s32 w, s32 h, color tint)
-{
- glBegin(GL_TRIANGLES);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
- glVertex3i(x+(w/2), y+h, render_depth);
- glVertex3i(x, y, render_depth);
- glVertex3i(x+w, y, render_depth);
- glEnd();
-}
-
-void render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint)
-{
- glBegin(GL_QUADS);
- glColor4f(tint.r/255.0f, tint.g/255.0f, tint.b/255.0f, tint.a/255.0f);
- glVertex3i(x, y, render_depth);
- glVertex3i(x, y+height, render_depth);
- glVertex3i(x+width, y+height, render_depth);
- glVertex3i(x+width, y, render_depth);
- glEnd();
-}
-
-void render_rectangle_tint(s32 x, s32 y, s32 width, s32 height, color tint[4])
-{
- glBegin(GL_QUADS);
- glColor4f(tint[0].r/255.0f, tint[0].g/255.0f, tint[0].b/255.0f, tint[0].a/255.0f);
- glVertex3i(x, y, render_depth);
- glColor4f(tint[1].r/255.0f, tint[1].g/255.0f, tint[1].b/255.0f, tint[1].a/255.0f);
- glVertex3i(x, y+height, render_depth);
- glColor4f(tint[2].r/255.0f, tint[2].g/255.0f, tint[2].b/255.0f, tint[2].a/255.0f);
- glVertex3i(x+width, y+height, render_depth);
- glColor4f(tint[3].r/255.0f, tint[3].g/255.0f, tint[3].b/255.0f, tint[3].a/255.0f);
- glVertex3i(x+width, y, render_depth);
- glEnd();
-}
-
-void render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint)
-{
- // left
- render_rectangle(x, y, outline_w, height, tint);
- // right
- render_rectangle(x+width-outline_w, y, outline_w, height, tint);
- // top
- render_rectangle(x+outline_w, y, width-(outline_w*2), outline_w, tint);
- // bottom
- render_rectangle(x+outline_w, y+height-outline_w, width-(outline_w*2), outline_w, tint);
-}
-
-void render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h)
-{
- glEnable(GL_SCISSOR_TEST);
- glScissor(x-1, window->height-h-y-1, w+1, h+1);
-}
-
-vec4 render_get_scissor()
-{
- vec4 vec;
- glGetIntegerv(GL_SCISSOR_BOX, (GLint*)(&vec));
- vec.x += 1;
- vec.y += 1;
- vec.w -= 1;
- vec.h -= 1;
- return vec;
-}
-
-void render_reset_scissor()
-{
- glDisable(GL_SCISSOR_TEST);
-} \ No newline at end of file
diff --git a/src/render.h b/src/render.h
deleted file mode 100644
index 4d29eea..0000000
--- a/src/render.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_RENDER
-#define INCLUDE_RENDER
-
-typedef struct t_color {
- u8 r;
- u8 g;
- u8 b;
- u8 a;
-} color;
-
-typedef struct t_vec4
-{
- s32 x;
- s32 y;
- s32 w;
- s32 h;
-} vec4;
-
-s32 render_depth = 1;
-void set_render_depth(s32 depth);
-
-#define rgb(r_,g_,b_) (color){ r_, g_, b_, 255 }
-#define rgba(r_,g_,b_,a_) (color){r_,g_,b_,a_}
-
-void render_clear();
-
-// images
-void render_image(image *image, s32 x, s32 y, s32 width, s32 height);
-void render_image_tint(image *image, s32 x, s32 y, s32 width, s32 height, color tint);
-
-// text
-s32 render_text(font *font, s32 x, s32 y, char *text, color tint);
-s32 render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint);
-s32 render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width);
-s32 render_text_vertical(font *font, s32 x, s32 y, char *text, color tint);
-
-s32 calculate_cursor_position(font *font, char *text, s32 click_x);
-s32 calculate_text_width(font *font, char *text);
-s32 calculate_text_width_upto(font *font, char *text, s32 index);
-s32 calculate_text_width_from_upto(font *font, char *text, s32 from, s32 index);
-
-// primitives
-void render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint);
-void render_rectangle_tint(s32 x, s32 y, s32 width, s32 height, color tint[4]);
-void render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint);
-void render_triangle(s32 x, s32 y, s32 w, s32 h, color tint);
-
-// utils
-void render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h);
-vec4 render_get_scissor();
-void render_reset_scissor();
-
-void render_set_rotation(float32 rotation, float32 x, float32 y, s32 depth);
-
-#endif \ No newline at end of file
diff --git a/src/settings.c b/src/settings.c
deleted file mode 100644
index b379470..0000000
--- a/src/settings.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-void reset_status_text();
-void set_status_text_to_finished_search();
-
-void settings_page_create()
-{
- global_settings_page.active = false;
- global_settings_page.font_small = assets_load_font(_binary____data_fonts_mono_ttf_start,
- _binary____data_fonts_mono_ttf_end, 16);
- global_settings_page.logo_img = assets_load_image(_binary____data_imgs_logo_64_png_start,
- _binary____data_imgs_logo_64_png_end, true);
- global_settings_page.keyboard = keyboard_input_create();
- global_settings_page.mouse = mouse_input_create();
-
- camera cam;
- cam.x = 0;
- cam.y = 0;
- cam.rotation = 0;
-
- global_settings_page.camera = cam;
-
- global_settings_page.btn_close = ui_create_button();
- global_settings_page.btn_save = ui_create_button();
- global_settings_page.dropdown_language = ui_create_dropdown();
- global_settings_page.dropdown_doubleclick = ui_create_dropdown();
- global_settings_page.textbox_max_file_size = ui_create_textbox(9);
- global_settings_page.textbox_max_thread_count = ui_create_textbox(5);
- global_settings_page.checkbox_parallelize_search = ui_create_checkbox(false);
-}
-
-static void load_current_settings_into_ui()
-{
- if (global_settings_page.max_thread_count != 0)
- snprintf(global_settings_page.textbox_max_thread_count.buffer, global_settings_page.textbox_max_thread_count.max_len, "%d",global_settings_page.max_thread_count);
-
- if (global_settings_page.max_file_size != 0)
- snprintf(global_settings_page.textbox_max_file_size.buffer, global_settings_page.textbox_max_file_size.max_len, "%d", global_settings_page.max_file_size);
-}
-
-void settings_page_update_render()
-{
- if (global_settings_page.active)
- {
- platform_window_make_current(&global_settings_page.window);
- platform_handle_events(&global_settings_page.window, &global_settings_page.mouse, &global_settings_page.keyboard);
- platform_set_cursor(&global_settings_page.window, CURSOR_DEFAULT);
-
- render_clear();
-
- camera_apply_transformations(&global_settings_page.window, &global_settings_page.camera);
-
- global_ui_context.layout.active_window = &global_settings_page.window;
- global_ui_context.keyboard = &global_settings_page.keyboard;
- global_ui_context.mouse = &global_settings_page.mouse;
-
- ui_begin(3);
- {
- ui_begin_menu_bar();
- {
- if (ui_push_menu(localize("general")))
- {
- global_settings_page.selected_tab_index = 0;
- }
- if (ui_push_menu(localize("interface")))
- {
- global_settings_page.selected_tab_index = 1;
- }
- }
- ui_end_menu_bar();
-
- ui_push_separator();
-
- if (global_settings_page.selected_tab_index == 0)
- {
- /////////////////////////////////////
- // max file size
- /////////////////////////////////////
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- ui_push_text(localize("max_file_size"));
- ui_push_text(localize("zero_for_no_limit"));
- }
- ui_block_end();
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_textbox(&global_settings_page.textbox_max_file_size, "0"))
- {
- keyboard_set_input_mode(&global_settings_page.keyboard, INPUT_NUMERIC);
- }
- ui_push_text("KB");
- }
- ui_block_end();
-
- /////////////////////////////////////
- // max threads
- /////////////////////////////////////
- global_ui_context.layout.offset_y += 10;
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- ui_push_text(localize("max_threads"));
- ui_push_text(localize("minimum_of_1"));
- }
- ui_block_end();
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_textbox(&global_settings_page.textbox_max_thread_count, "0"))
- {
- keyboard_set_input_mode(&global_settings_page.keyboard, INPUT_NUMERIC);
- }
- ui_push_text("Threads");
-
- }
- ui_block_end();
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_hypertext_link(localize("copy_config_path")))
- {
- char buffer[PATH_MAX];
- platform_set_clipboard(main_window, get_config_save_location(buffer));
- }
- }
- ui_block_end();
- }
- else if (global_settings_page.selected_tab_index == 1)
- {
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- ui_push_text(localize("language"));
- }
- ui_block_end();
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_dropdown(&global_settings_page.dropdown_language, locale_get_name()))
- {
- for (s32 i = 0; i < global_localization.mo_files.length; i++)
- {
- mo_file *file = array_at(&global_localization.mo_files, i);
-
- if (ui_push_dropdown_item(file->icon, file->locale_full, i))
- {
- set_locale(file->locale_id);
- }
- }
- }
- }
- ui_block_end();
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- ui_push_text(localize("double_click_action"));
- }
- ui_block_end();
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- char* available_options[OPTION_RESULT+1] = {
- localize("double_click_action_1"),
- localize("double_click_action_2"),
- localize("double_click_action_3"),
- localize("double_click_action_4"),
- };
-
- if (ui_push_dropdown(&global_settings_page.dropdown_doubleclick, available_options[global_settings_page.current_double_click_selection_option]))
- {
- for (s32 i = 0; i < OPTION_RESULT+1; i++)
- {
- if (ui_push_dropdown_item(0, available_options[i], i))
- {
- global_settings_page.current_double_click_selection_option = i;
- }
- }
- }
- }
- ui_block_end();
-
-#if 0
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- ui_push_text("Style");
- }
- ui_block_end();
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_color_button("Light", global_ui_context.style.id == UI_STYLE_LIGHT, rgb(250, 250, 250)))
- {
- ui_set_style(UI_STYLE_LIGHT);
- }
- if (ui_push_color_button("Dark", global_ui_context.style.id == UI_STYLE_DARK, rgb(50, 50, 50)))
- {
- ui_set_style(UI_STYLE_DARK);
- }
- }
- ui_block_end();
-#endif
- }
-
- global_ui_context.layout.offset_y = global_settings_page.window.height - 33;
-
- ui_block_begin(LAYOUT_HORIZONTAL);
- {
- if (ui_push_button(&global_settings_page.btn_close, localize("close")))
- {
- global_settings_page.textbox_max_thread_count.buffer[0] = 0;
- global_settings_page.textbox_max_file_size.buffer[0] = 0;
- ui_set_style(global_settings_page.current_style);
- global_settings_page.current_double_click_selection_option = global_settings_page.selected_double_click_selection_option;
- global_settings_page.active = false;
- set_locale(global_settings_page.current_locale_id);
- settings_page_hide();
-
- return;
- }
- if (ui_push_button(&global_settings_page.btn_close, localize("save")))
- {
- global_settings_page.current_style = global_ui_context.style.id;
- global_settings_page.max_thread_count = string_to_s32(global_settings_page.textbox_max_thread_count.buffer);
- if (global_settings_page.max_thread_count < 1)
- global_settings_page.max_thread_count = DEFAULT_THREAD_COUNT;
-
- global_settings_page.max_file_size = string_to_s32(global_settings_page.textbox_max_file_size.buffer);
-
- global_settings_page.textbox_max_thread_count.buffer[0] = 0;
- global_settings_page.textbox_max_file_size.buffer[0] = 0;
- global_settings_page.selected_double_click_selection_option = global_settings_page.current_double_click_selection_option;
-
- global_settings_page.active = false;
- settings_page_hide();
- return;
- }
- }
- ui_block_end();
- }
- ui_end();
-
- if (!global_settings_page.window.is_open)
- {
- global_settings_page.active = false;
- settings_page_hide();
- return;
- }
-
- platform_window_swap_buffers(&global_settings_page.window);
- }
-}
-
-void settings_page_show()
-{
- if (platform_window_is_valid(&global_settings_page.window)) return;
-
- load_current_settings_into_ui();
-
- global_settings_page.window = platform_open_window(localize("text_search_settings"),
- 450, 280, 450, 280, 450, 280);
-
- settings_window = &global_settings_page.window;
-
- global_settings_page.active = true;
- global_settings_page.selected_tab_index = 0;
- global_settings_page.current_locale_id = locale_get_id();
- global_settings_page.current_double_click_selection_option = global_settings_page.selected_double_click_selection_option;
-
- platform_set_icon(&global_settings_page.window, global_settings_page.logo_img);
-}
-
-void settings_page_hide()
-{
- if (platform_window_is_valid(&global_settings_page.window))
- {
- settings_window = 0;
-
- platform_destroy_window(&global_settings_page.window);
-
- global_settings_page.btn_close.state = false;
- global_settings_page.btn_save.state = false;
- global_settings_page.active = false;
-
- global_settings_page.mouse.x = -1;
- global_settings_page.mouse.y = -1;
- }
-}
-
-void settings_page_hide_without_save()
-{
- if (platform_window_is_valid(&global_settings_page.window))
- {
- global_settings_page.textbox_max_thread_count.buffer[0] = 0;
- global_settings_page.textbox_max_file_size.buffer[0] = 0;
-
- global_settings_page.active = false;
- set_locale(global_settings_page.current_locale_id);
- settings_page_hide();
- }
-}
-
-void settings_page_destroy()
-{
- keyboard_input_destroy(&global_settings_page.keyboard);
- ui_destroy_textbox(&global_settings_page.textbox_max_file_size);
- ui_destroy_textbox(&global_settings_page.textbox_max_thread_count);
- assets_destroy_font(global_settings_page.font_small);
- assets_destroy_image(global_settings_page.logo_img);
-
- if (platform_window_is_valid(&global_settings_page.window))
- platform_destroy_window(&global_settings_page.window);
-}
diff --git a/src/settings.h b/src/settings.h
deleted file mode 100644
index 11ab307..0000000
--- a/src/settings.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_SETTINGS
-#define INCLUDE_SETTINGS
-
-typedef enum t_double_click_option
-{
- OPTION_PATH,
- OPTION_PATH_LINE,
- OPTION_PATH_LINE_FILTER,
- OPTION_RESULT,
-} double_click_option;
-
-typedef struct t_settings_page
-{
- platform_window window;
- keyboard_input keyboard;
- mouse_input mouse;
- camera camera;
- bool active;
-
- font *font_small;
- image *logo_img;
-
- button_state btn_close;
- button_state btn_save;
- dropdown_state dropdown_language;
- dropdown_state dropdown_doubleclick;
- textbox_state textbox_max_file_size;
- textbox_state textbox_max_thread_count;
- checkbox_state checkbox_parallelize_search;
- s32 selected_tab_index;
-
- char *current_locale_id;
- s32 max_thread_count;
- s32 max_file_size;
- u16 current_style;
- u16 selected_double_click_selection_option; // saved state
- u16 current_double_click_selection_option; // unsaved state
-} settings_page;
-
-#define DEFAULT_THREAD_COUNT 10
-#define DEFAULT_MAX_FILE_SIZE 0
-#define DEFAULT_RECURSIVE_STATE 1
-#define DEFAULT_STYLE 1
-
-settings_page global_settings_page;
-
-void settings_page_create();
-void settings_page_hide_without_save();
-void settings_page_update_render();
-void settings_page_show();
-void settings_page_hide();
-void settings_page_destroy();
-
-#endif \ No newline at end of file
diff --git a/src/settings_config.c b/src/settings_config.c
deleted file mode 100644
index d2bc018..0000000
--- a/src/settings_config.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-void settings_config_write_to_file(settings_config *config, char *path)
-{
- // @hardcoded
- s32 len = kilobytes(20);
- char *buffer = mem_alloc(len);
- buffer[0] = 0;
-
- for (s32 i = 0; i < config->settings.length; i++)
- {
- config_setting *setting = array_at(&config->settings, i);
-
- char entry_buf[MAX_INPUT_LENGTH];
- snprintf(entry_buf, MAX_INPUT_LENGTH, "%s = \"%s\"\n", setting->name, setting->value);
- string_appendn(buffer, entry_buf, MAX_INPUT_LENGTH);
- }
-
- set_active_directory(binary_path);
- platform_write_file_content(path, "w+", buffer, strlen(buffer));
- mem_free(buffer);
-}
-
-static void get_config_from_string(settings_config *config, char *string)
-{
- config_setting current_entry;
- current_entry.name = 0;
- current_entry.value = 0;
-
- s32 len = 0;
- bool in_literal = false;
- char *original_string = string;
-
- while(*string)
- {
-
- // property name
- if (*string == ' ' && !current_entry.name)
- {
- current_entry.name = mem_alloc(len+1);
- string_copyn(current_entry.name, string-len, len);
- current_entry.name[len] = 0;
- string_trim(current_entry.name);
- }
-
- // property value
- if (*string == '"' && (*(string+1) == 0 || !in_literal))
- {
- in_literal = !in_literal;
-
- if (in_literal)
- {
- len = -1;
- }
- else
- {
- current_entry.value = mem_alloc(len+1);
- string_copyn(current_entry.value, string-len, len);
- current_entry.value[len] = 0;
- string_trim(current_entry.value);
- }
- }
-
- ++len;
- ++string;
- }
-
- array_push(&config->settings, &current_entry);
-}
-
-static void convert_crlf_to_lf(char *buffer)
-{
- char *buffer_original = buffer;
-
- int write_offset = 0;
- int read_offset = 0;
-
- while(buffer[read_offset])
- {
- if (buffer[read_offset] != 0x0D)
- {
- buffer_original[write_offset] = buffer[read_offset];
-
- ++write_offset;
- }
-
- ++read_offset;
- }
-}
-
-settings_config settings_config_load_from_file(char *path)
-{
- settings_config config;
- config.settings = array_create(sizeof(config_setting));
-
- set_active_directory(binary_path);
-
- file_content content = platform_read_file_content(path, "rb");
-
- if (!content.content || content.file_error)
- {
- platform_destroy_file_content(&content);
- return config;
- }
-
- convert_crlf_to_lf(content.content);
-
- s32 token_offset = 0;
- for (s32 i = 0; i < content.content_length; i++)
- {
- char ch = ((char*)content.content)[i];
- char prev_ch = i-1 > 0 ? ((char*)content.content)[i-1] : 255;
-
- // end of line [lf]
- if (ch == 0x0A && prev_ch != 0x0D)
- {
- char line[MAX_INPUT_LENGTH];
-
- s32 line_len = i - token_offset;
- snprintf(line, MAX_INPUT_LENGTH, "%.*s", line_len, (char*)content.content+token_offset);
- token_offset = i + 1;
-
- get_config_from_string(&config, line);
- }
- }
-
- platform_destroy_file_content(&content);
-
- return config;
-}
-
-config_setting* settings_config_get_setting(settings_config *config, char *name)
-{
- for (s32 i = 0; i < config->settings.length; i++)
- {
- config_setting *setting = array_at(&config->settings, i);
- if (setting && setting->name && name && strcmp(setting->name, name) == 0)
- {
- return setting;
- }
- }
- return 0;
-}
-
-char* settings_config_get_string(settings_config *config, char *name)
-{
- config_setting* setting = settings_config_get_setting(config, name);
- if (setting)
- return setting->value;
- else
- return 0;
-}
-
-s64 settings_config_get_number(settings_config *config, char *name)
-{
- config_setting* setting = settings_config_get_setting(config, name);
- if (setting && setting->value)
- return string_to_u64(setting->value);
- else
- return 0;
-}
-
-void settings_config_set_string(settings_config *config, char *name, char *value)
-{
- config_setting* setting = settings_config_get_setting(config, name);
- if (setting)
- {
- s32 len = strlen(value);
- mem_free(setting->value);
- setting->value = mem_alloc(len+1);
- string_copyn(setting->value, value, len+1);
- }
- else
- {
- config_setting new_entry;
- new_entry.name = 0;
-
- // name
- s32 len = strlen(name);
- new_entry.name = mem_alloc(len+1);
- string_copyn(new_entry.name, name, len+1);
-
- // value
- len = strlen(value);
- new_entry.value = mem_alloc(len+1);
- string_copyn(new_entry.value, value, len+1);
-
- array_push(&config->settings, &new_entry);
- }
-}
-
-void settings_config_set_number(settings_config *config, char *name, s64 value)
-{
- config_setting* setting = settings_config_get_setting(config, name);
- if (setting)
- {
- char num_buf[20];
- snprintf(num_buf, 20, "%"PRId64"", value);
-
- s32 len = strlen(num_buf);
- mem_free(setting->value);
- setting->value = mem_alloc(len+1);
- string_copyn(setting->value, num_buf, len+1);
- }
- else
- {
- config_setting new_entry;
-
- // name
- s32 len = strlen(name);
- new_entry.name = mem_alloc(len+1);
- string_copyn(new_entry.name, name, len+1);
-
- // value
- char num_buf[20];
- snprintf(num_buf, 20, "%"PRId64"", value);
-
- len = strlen(num_buf);
- new_entry.value = mem_alloc(len+1);
- string_copyn(new_entry.value, num_buf, len+1);
- array_push(&config->settings, &new_entry);
- }
-}
-
-void settings_config_destroy(settings_config *config)
-{
- for (s32 i = 0; i < config->settings.length; i++)
- {
- config_setting *entry = array_at(&config->settings, i);
-
- mem_free(entry->name);
- mem_free(entry->value);
- }
-
- array_destroy(&config->settings);
-} \ No newline at end of file
diff --git a/src/settings_config.h b/src/settings_config.h
deleted file mode 100644
index 1b931e1..0000000
--- a/src/settings_config.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_SETTINGS_CONFIG
-#define INCLUDE_SETTINGS_CONFIG
-
-typedef struct t_config_setting
-{
- char *name;
- char *value;
-} config_setting;
-
-typedef struct t_settings_config
-{
- array settings;
-} settings_config;
-
-/* Example of file:
-* NAME = "Aldrik Ramaekers"
-* AGE = "69"
-* NUMBER = "15"
-*/
-
-settings_config settings_config_load_from_file(char *path);
-void settings_config_write_to_file(settings_config *config, char *path);
-void settings_config_destroy(settings_config *config);
-
-config_setting* settings_config_get_setting(settings_config *config, char *name);
-char* settings_config_get_string(settings_config *config, char *name);
-s64 settings_config_get_number(settings_config *config, char *name);
-
-void settings_config_set_string(settings_config *config, char *name, char *value);
-void settings_config_set_number(settings_config *config, char *name, s64 value);
-
-
-
-#endif \ No newline at end of file
diff --git a/src/string_utils.c b/src/string_utils.c
deleted file mode 100644
index 2ad2f0d..0000000
--- a/src/string_utils.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-bool string_match(char *first, char *second)
-{
- // If we reach at the end of both strings, we are done
- if (*first == '\0' && *second == '\0')
- return true;
-
- // Make sure that the characters after '*' are present
- // in second string. This function assumes that the first
- // string will not contain two consecutive '*'
- if (*first == '*' && *(first+1) != '\0' && *second == '\0')
- return false;
-
- // If the first string contains '?', or current characters
- // of both strings string_match
- if (*first == '?' || *first == *second)
- return string_match(first+1, second+1);
-
- // If there is *, then there are two possibilities
- // a) We consider current character of second string
- // b) We ignore current character of second string.
- if (*first == '*')
- return string_match(first+1, second) || string_match(first, second+1);
- return false;
-}
-
-bool string_is_asteriks(char *text)
-{
- utf8_int32_t ch;
- while((text = utf8codepoint(text, &ch)) && ch)
- {
- if (ch != '*') return false;
- }
- return true;
-}
-
-bool string_contains_ex(char *text_to_search, char *text_to_find, array *text_matches, bool *cancel_search)
-{
- bool final_result = false;
- bool is_asteriks_only = false;
-
- // * wildcard at the start of text to find is not needed
- if (string_is_asteriks(text_to_find))
- {
- is_asteriks_only = true;
- text_to_find += strlen(text_to_find);
- }
-
- // remove all asteriks from start
- utf8_int32_t br;
- while(utf8codepoint(text_to_find, &br) && br == '*')
- {
- text_to_find = utf8codepoint(text_to_find, &br);
- }
-
- char *text_to_find_original = text_to_find;
- bool save_info = (text_matches != 0);
-
- utf8_int32_t text_to_search_ch = 0;
- utf8_int32_t text_to_find_ch = 0;
- size_t text_to_find_char_len = utf8len(text_to_find);
-
- s32 line_nr_val = 1;
- s32 word_offset_val = 0;
- s32 word_match_len_val = 0;
- char* line_start_ptr = text_to_search;
-
- //printf("%s %s\n", text_to_search, text_to_find);
- s32 index = 0;
- char *text_to_ss = text_to_search;
- while((text_to_ss = utf8codepoint(text_to_search, &text_to_search_ch))
- && text_to_search_ch)
- {
- if (cancel_search && *cancel_search) goto set_info_and_return_failure;
-
- word_offset_val++;
- if (text_to_search_ch == '\n')
- {
- line_nr_val++;
- word_offset_val = 0;
- line_start_ptr = text_to_search;
- }
-
- char *text_to_search_current_attempt = text_to_search;
- utf8_int32_t text_to_search_current_attempt_ch = 0;
-
- bool in_wildcard = false;
-
- text_to_find = utf8codepoint(text_to_find, &text_to_find_ch);
- text_to_search_current_attempt = utf8codepoint(text_to_search_current_attempt,
- &text_to_search_current_attempt_ch);
-
- text_to_search = text_to_ss;
- word_match_len_val = 0;
- while(text_to_search_current_attempt_ch)
- {
- if (cancel_search && *cancel_search) goto set_info_and_return_failure;
-
- // wildcard, accept any character in text to search
- if (text_to_find_ch == '?')
- goto continue_search;
-
- // character matches,
- if (text_to_find_ch == text_to_search_current_attempt_ch && in_wildcard)
- in_wildcard = false;
-
- // wildcard, accept any characters in text to search untill next char is found
- if (text_to_find_ch == '*')
- {
- text_to_find = utf8codepoint(text_to_find, &text_to_find_ch);
- in_wildcard = true;
- }
-
- // text to find has reached 0byte, word has been found
- if (text_to_find_ch == 0)
- {
- done:
- if (save_info)
- {
- text_match new_match;
- new_match.line_nr = line_nr_val;
- new_match.word_offset = word_offset_val;
- new_match.word_match_len = word_match_len_val;
- new_match.line_start = line_start_ptr;
- new_match.line_info = 0;
- array_push(text_matches, &new_match);
- }
-
- final_result = true;
-
- if (is_asteriks_only)
- {
- return final_result;
- }
-
- break;
- }
-
- // character does not match, continue search
- if (text_to_find_ch != text_to_search_current_attempt_ch && !in_wildcard)
- break;
-
- continue_search:
- if (!in_wildcard)
- text_to_find = utf8codepoint(text_to_find, &text_to_find_ch);
-
- text_to_search_current_attempt = utf8codepoint(
- text_to_search_current_attempt,
- &text_to_search_current_attempt_ch);
-
- if (!text_to_search_current_attempt_ch && !text_to_find_ch) goto done;
-
- word_match_len_val++;
- }
-
- text_to_find = text_to_find_original;
- index++;
- }
-
- return final_result;
-
- set_info_and_return_failure:
- return false;
-}
-
-static char *ltrim(char *str, const char *seps)
-{
- size_t totrim;
- if (seps == NULL) {
- seps = "\t\n\v\f\r ";
- }
- totrim = strspn(str, seps);
- if (totrim > 0) {
- size_t len = strlen(str);
- if (totrim == len) {
- str[0] = '\0';
- }
- else {
- memmove(str, str + totrim, len + 1 - totrim);
- }
- }
- return str;
-}
-
-static char *rtrim(char *str, const char *seps)
-{
- int i;
- if (seps == NULL) {
- seps = "\t\n\v\f\r ";
- }
- i = strlen(str) - 1;
- while (i >= 0 && strchr(seps, str[i]) != NULL) {
- str[i] = '\0';
- i--;
- }
- return str;
-}
-
-inline void string_trim(char *string)
-{
- ltrim(rtrim(string, 0), 0);
-}
-
-inline bool string_equals(char *first, char *second)
-{
- return (strcmp(first, second) == 0);
-}
-
-// replaces " with \" for file formats
-void string_appendf(char *buffer, char *text)
-{
- u32 len = strlen(buffer);
- while(*text)
- {
- if (*text < 32)
- {
- buffer[len] = ' ';
- len++;
- text++;
- continue;
- }
-
- if (*text == '"')
- {
- buffer[len] = '\\';
- len++;
- }
- if (*text == '\\')
- {
- buffer[len] = '\\';
- len++;
- }
-
- buffer[len] = *text;
- len++;
- text++;
- }
-}
-
-void string_copyn(char *buffer, char *text, s32 bufferlen)
-{
- u32 len = 0;
- while(*text && len < bufferlen)
- {
- buffer[len] = *text;
- len++;
- text++;
- }
- buffer[len] = 0;
-}
-
-void string_appendn(char *buffer, char *text, s32 bufferlen)
-{
- u32 len = strlen(buffer);
- while(*text && len < bufferlen)
- {
- buffer[len] = *text;
- len++;
- text++;
- }
- buffer[len] = 0;
-}
-
-void string_append(char *buffer, char *text)
-{
- u32 len = strlen(buffer);
- while(*text)
- {
- buffer[len] = *text;
- len++;
- text++;
- }
- buffer[len] = 0;
-}
-
-bool string_remove(char **buffer, char *text)
-{
- s32 len = strlen(text);
- char tmp[200];
- memcpy(tmp, *buffer, len);
- memset(tmp+len, 0, 1);
-
- if (string_equals(tmp, text))
- {
- *buffer += len;
- return true;
- }
-
- return false;
-}
-
-char* string_get_json_literal(char **buffer, char *tmp)
-{
- char *buf_start = *buffer;
- char *buf = *buffer;
- s32 len = 0;
- while(*buf)
- {
- if ((*buf == ',' || *buf == '}') && (len > 0 && *(buf-1) == '"') && (len > 1 && *(buf-2) != '\\'))
- {
- memcpy(tmp, buf_start, len);
- memset(tmp+len-1, 0, 1);
- *buffer += len-1;
- return tmp;
- }
-
- len++;
- buf++;
- }
-
- return tmp;
-}
-
-s32 string_get_json_ulong_number(char **buffer)
-{
- char tmp[20];
- char *buf_start = *buffer;
- char *buf = *buffer;
- s32 len = 0;
- while(*buf)
- {
- if (*buf == ',' || *buf == '}')
- {
- memcpy(tmp, buf_start, len);
- memset(tmp+len, 0, 1);
- *buffer += len;
- return string_to_u64(tmp);
- }
-
- len++;
- buf++;
- }
-
- return 0;
-}
-
-s32 string_get_json_number(char **buffer)
-{
- char tmp[20];
- char *buf_start = *buffer;
- char *buf = *buffer;
- s32 len = 0;
- while(*buf)
- {
- if (*buf == ',' || *buf == '}')
- {
- memcpy(tmp, buf_start, len);
- memset(tmp+len, 0, 1);
- *buffer += len;
- return string_to_s32(tmp);
- }
-
- len++;
- buf++;
- }
-
- return 0;
-}
-
-void utf8_str_remove_range(char *str, s32 from, s32 to)
-{
- char *orig_str = str;
- s32 i = 0;
- utf8_int32_t ch = 0;
- s32 total_len = strlen(str)+1+4;
- char *replacement = calloc(total_len,1);
- char *rep_off = replacement;
- replacement[0] = 0;
-
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (i < from || i >= to)
- {
- rep_off = utf8catcodepoint(rep_off, ch, 5);
- }
-
- ++i;
- }
- *rep_off = 0;
-
- string_copyn(orig_str, replacement, MAX_INPUT_LENGTH);
- mem_free(replacement);
-}
-
-void utf8_str_remove_at(char *str, s32 at)
-{
- char *orig_str = str;
- s32 i = 0;
- utf8_int32_t ch = 0;
- s32 total_len = strlen(str)+1+4;
- char *replacement = calloc(total_len,1);
- char *rep_off = replacement;
- replacement[0] = 0;
-
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (at != i)
- {
- rep_off = utf8catcodepoint(rep_off, ch, 5);
- }
-
- ++i;
- }
- *rep_off = 0;
-
- string_copyn(orig_str, replacement, MAX_INPUT_LENGTH);
- mem_free(replacement);
-}
-
-void utf8_str_insert_utf8str(char *str, s32 at, char *toinsert)
-{
- s32 index = 0;
- utf8_int32_t ch;
- while((toinsert = utf8codepoint(toinsert, &ch)) && ch)
- {
- utf8_str_insert_at(str, at+index, ch);
- index++;
- }
-}
-
-void utf8_str_insert_at(char *str, s32 at, utf8_int32_t newval)
-{
- char *orig_str = str;
- s32 i = 0;
- utf8_int32_t ch = 0;
- s32 total_len = strlen(str)+1+4;
- char *replacement = calloc(total_len,1);
- char *rep_off = replacement;
- replacement[0] = 0;
-
- while((str = utf8codepoint(str, &ch)))
- {
- if (at == i)
- {
- rep_off = utf8catcodepoint(rep_off, newval, 5);
- }
-
- rep_off = utf8catcodepoint(rep_off, ch, 5);
-
- ++i;
-
- if (!ch) break;
- }
- *rep_off = 0;
-
- string_copyn(orig_str, replacement, MAX_INPUT_LENGTH);
- mem_free(replacement);
-}
-
-char *utf8_str_copy_upto(char *str, s32 roof, char *buffer)
-{
- utf8_int32_t ch = 0;
- s32 index = 0;
- char *orig_buffer = buffer;
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (index == roof) break;
- buffer = utf8catcodepoint(buffer, ch, 5);
- index++;
- }
- buffer = utf8catcodepoint(buffer, 0, 5);
-
- return orig_buffer;
-}
-
-char *utf8_str_copy_range(char *str, s32 floor, s32 roof, char *buffer)
-{
- utf8_int32_t ch = 0;
- s32 index = 0;
- char *orig_buffer = buffer;
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (index == roof) break;
- if (index >= floor)
- buffer = utf8catcodepoint(buffer, ch, 5);
- index++;
- }
- buffer = utf8catcodepoint(buffer, 0, 5);
-
- return orig_buffer;
-}
-
-void utf8_str_replace_at(char *str, s32 at, utf8_int32_t newval)
-{
- char *orig_str = str;
- s32 i = 0;
- utf8_int32_t ch = 0;
- s32 total_len = strlen(str)+1+4;
- char *replacement = calloc(total_len,1);
- char *rep_off = replacement;
- replacement[0] = 0;
-
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (at == i)
- {
- rep_off = utf8catcodepoint(rep_off, newval, 5);
- }
- else
- {
- rep_off = utf8catcodepoint(rep_off, ch, 5);
- }
- ++i;
- }
- *rep_off = 0;
-
- string_copyn(orig_str, replacement, MAX_INPUT_LENGTH);
- mem_free(replacement);
-}
-
-char* utf8_str_upto(char *str, s32 index)
-{
- s32 i = 0;
- utf8_int32_t ch;
- char *prev_str = str;
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (index == i) return prev_str;
- prev_str = str;
- ++i;
- }
-
- return str;
-}
-
-utf8_int32_t utf8_str_at(char *str, s32 index)
-{
- s32 i = 0;
- utf8_int32_t ch;
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (index == i) return ch;
-
- ++i;
- }
-
- return 0;
-}
-
-bool is_string_numeric(char *str)
-{
- utf8_int32_t ch;
- while((str = utf8codepoint(str, &ch)) && ch)
- {
- if (!(ch >= 48 && ch <= 57))
- {
- return false;
- }
- }
-
- return true;
-} \ No newline at end of file
diff --git a/src/string_utils.h b/src/string_utils.h
deleted file mode 100644
index 2b5c85f..0000000
--- a/src/string_utils.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_STRING_UTILS
-#define INCLUDE_STRING_UTILS
-
-#if 0
-printf("[lll][llll*] : %d\n", string_contains("lll", "llll*"));
-printf("[lllll][l*lop] : %d\n", string_contains("lllll", "l*lop"));
-printf("[22lllll][l*l] : %d\n", string_contains("22lllll", "l*l"));
-
-printf("[hello world][h?lo] : %d\n", string_contains("hello world", "h?lo"));
-printf("[\"hello sailor\"][sailor] : %d\n", string_contains(" wsdf asd \"hello sailor\" asdf asdf ", "sailor"));
-printf("[\"hello sailor\"][*sailor] : %d\n", string_contains(" wsdf asd \"hello sailor\" asdf asdf ", "*sailor"));
-printf("\n");
-
-printf("[\"hello sailor\"][*sailor\"] : %d\n", string_contains(" wsdf asd \"hello sailor\" asdf asdf ", "*sailor\""));
-printf("[\"hello sailor\"][*sailor*] : %d\n", string_contains(" wsdf asd \"hello sailor\" asdf asdf ", "*sailor*"));
-printf("[\"hello sailor\"][sailor*] : %d\n", string_contains(" wsdf asd \"hello sailor\" asdf asdf ", "sailor*"));
-printf("[22lllll pi23hjp rbksje LSKJDh l][LS*] : %d\n",
- string_contains("22lllll pi23hjp rbksje LSKJDh l", "LS*"));
-printf("[22lllll lal][l*l] : %d\n", string_contains("22lllll lal", "l*l"));
-printf("[22lllll][*l*l] : %d\n", string_contains("lllll", "*l*l"));
-printf("[hello world][hello] : %d\n", string_contains("hello world", "hello"));
-printf("[hello world][h?llo] : %d\n", string_contains("hello world", "h?llo"));
-printf("[hello world][h????] : %d\n", string_contains("hello world", "h????"));
-printf("[hello world][h*lo] : %d\n", string_contains("hello world", "h*lo"));
-printf("[hello world][*] : %d\n", string_contains("hello world", "*"));
-printf("[hello world][h*] : %d\n", string_contains("hello world", "h*"));
-printf("[hello world][*o] : %d\n", string_contains("hello world", "*o"));
-printf("[hello world][h*o] : %d\n", string_contains("hello world", "h*o"));
-printf("[hello world][*lo] : %d\n", string_contains("hello world", "*lo"));
-printf("[hello world][hel*lo] : %d\n", string_contains("hello world", "hel*lo"));
-
-printf("[lllll][l*l] : %d\n", string_contains("lllll", "l*l"));
-printf("[llllllll][l*llll] : %d\n", string_contains("lllll", "l*llll"));
-printf("[llllllll][l*lll] : %d\n", string_contains("lllll", "l*lll"));
-printf("[llllllll][llll*l] : %d\n", string_contains("lllll", "llll*l"));
-printf("[llllllll][*] : %d\n", string_contains("lllll", "*"));
-printf("[lllll][l?lll] : %d\n", string_contains("lllll", "l?lll"));
-
-printf("[lllll][lllll] : %d\n", string_contains("lllll", "lllll"));
-printf("[lllll][*llll] : %d\n", string_contains("lllll", "*llll"));
-printf("[lllll][llll*] : %d\n", string_contains("lllll", "llll*"));
-printf("[lllll][*llll*] : %d\n", string_contains("lllll", "*llll*"));
-printf("[lllll][*lllll*] : %d\n", string_contains("lllll", "*lllll*"));
-printf("[lllll][*ll*] : %d\n", string_contains("lllll", "*ll*"));
-#endif
-
-typedef struct t_text_match
-{
- u32 line_nr;
- s32 word_offset;
- s32 word_match_len;
- char *line_start;
- char *line_info;
-} text_match;
-
-#define string_contains(big, small) string_contains_ex(big, small, 0, 0)
-bool string_match(char *first, char *second);
-bool string_contains_ex(char *big, char *small, array *text_matches, bool *cancel_search);
-void string_trim(char *string);
-bool string_equals(char *first, char *second);
-void string_append(char *buffer, char *text);
-void string_copyn(char *buffer, char *text, s32 bufferlen);
-void string_appendn(char *buffer, char *text, s32 bufferlen);
-void string_appendf(char *buffer, char *text);
-bool string_remove(char **buffer, char *text);
-char* string_get_json_literal(char **buffer, char *tmp);
-s32 string_get_json_number(char **buffer);
-s32 string_get_json_ulong_number(char **buffer);
-
-utf8_int32_t utf8_str_at(char *str, s32 index);
-void utf8_str_remove_at(char *str, s32 at);
-void utf8_str_remove_range(char *str, s32 from, s32 to);
-void utf8_str_insert_at(char *str, s32 at, utf8_int32_t newval);
-void utf8_str_insert_utf8str(char *str, s32 at, char *toinsert);
-void utf8_str_replace_at(char *str, s32 at, utf8_int32_t newval);
-char* utf8_str_upto(char *str, s32 index);
-char *utf8_str_copy_upto(char *str, s32 roof, char *buffer);
-char *utf8_str_copy_range(char *str, s32 floor, s32 roof, char *buffer);
-bool is_string_numeric(char *str);
-
-#endif \ No newline at end of file
diff --git a/src/test.txt b/src/test.txt
deleted file mode 100644
index 4d6a3b4..0000000
--- a/src/test.txt
+++ /dev/null
@@ -1 +0,0 @@
-ECHO is on.
diff --git a/src/thread.h b/src/thread.h
deleted file mode 100644
index b019fdb..0000000
--- a/src/thread.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_THREAD
-#define INCLUDE_THREAD
-
-#ifdef OS_LINUX
-#include <pthread.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-
-struct t_thread
-{
- pthread_t thread;
- bool valid;
-};
-
-struct t_mutex
-{
- pthread_mutex_t mutex;
-};
-#endif
-
-#ifdef OS_WIN
-#include <windows.h>
-#include <process.h> /* _beginthread, _endthread */
-#include <stddef.h>
-#include <stdlib.h>
-#include <conio.h>
-
-struct t_thread
-{
- HANDLE thread;
- bool valid;
-};
-
-struct t_mutex
-{
- HANDLE mutex;
-};
-#endif
-
-typedef struct t_thread thread;
-typedef struct t_mutex mutex;
-
-thread thread_start(void *(*start_routine) (void *), void *arg);
-void thread_join(thread *thread);
-bool thread_tryjoin(thread *thread);
-void thread_detach(thread *thread);
-void thread_stop(thread *thread);
-u32 thread_get_id();
-void thread_sleep(u64 microseconds);
-
-mutex mutex_create_recursive();
-mutex mutex_create();
-
-void mutex_lock(mutex *mutex);
-bool mutex_trylock(mutex *mutex);
-void mutex_unlock(mutex *mutex);
-
-void mutex_destroy(mutex *mutex);
-
-#endif \ No newline at end of file
diff --git a/src/ui.c b/src/ui.c
deleted file mode 100644
index d8d3a16..0000000
--- a/src/ui.c
+++ /dev/null
@@ -1,1544 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-inline void ui_begin(s32 id)
-{
- global_ui_context.item_hovered = false;
- global_ui_context.next_id = id * 100;
- global_ui_context.layout.offset_x = 0;
- global_ui_context.layout.offset_y = 0;
- global_ui_context.layout.width = global_ui_context.layout.active_window->width;
- global_ui_context.layout.height = global_ui_context.layout.active_window->height;
- global_ui_context.layout.active_dropdown_state = 0;
-}
-
-inline void ui_end()
-{
-
-}
-
-inline checkbox_state ui_create_checkbox(bool selected)
-{
- checkbox_state state;
- state.state = selected;
-
- return state;
-}
-
-inline textbox_state ui_create_textbox(u16 max_len)
-{
- assert(max_len > 0);
- assert(max_len <= MAX_INPUT_LENGTH);
-
- textbox_state state;
- state.max_len = max_len;
- state.buffer = mem_alloc(max_len+1);
- state.buffer[0] = 0;
- state.state = false;
- state.text_offset_x = 0;
- state.history = array_create(sizeof(textbox_history_entry));
- state.future = array_create(sizeof(textbox_history_entry));
- array_reserve(&state.history, 100);
- state.history.reserve_jump = 100;
- array_reserve(&state.future, 100);
- state.future.reserve_jump = 100;
- state.selection_start_index = 0;
- state.double_clicked_to_select = false;
- state.double_clicked_to_select_cursor_index = 0;
- state.diff = 0;
- state.last_click_cursor_index = -1;
- state.attempting_to_select = false;
- state.deselect_on_enter = true;
-
- return state;
-}
-
-void ui_destroy_textbox(textbox_state *state)
-{
- for (s32 i = 0; i < state->history.length; i++)
- {
- char **history_entry = array_at(&state->history, i);
- mem_free(*history_entry);
- }
- array_destroy(&state->history);
- array_destroy(&state->future);
-
- mem_free(state->buffer);
-}
-
-inline button_state ui_create_button()
-{
- button_state state;
- state.state = 0;
-
- return state;
-}
-
-inline scroll_state ui_create_scroll(s32 scroll)
-{
- scroll_state state;
- state.scroll = 0;
- state.height = scroll;
-
- return state;
-}
-
-void ui_set_textbox_text(textbox_state *textbox, char *text)
-{
- if (global_ui_context.current_active_textbox == textbox)
- {
- keyboard_set_input_text(global_ui_context.keyboard, text);
- }
-
- string_copyn(textbox->buffer, text, textbox->max_len);
-}
-
-inline dropdown_state ui_create_dropdown()
-{
- dropdown_state state;
- state.state = 0;
- state.selected_index = 0;
- return state;
-}
-
-void ui_set_style(u16 style)
-{
- global_ui_context.style.id = style;
- if (style == UI_STYLE_LIGHT)
- {
- global_ui_context.style.hypertext_foreground = rgb(66, 134, 244);
- global_ui_context.style.hypertext_hover_foreground = rgb(221, 93, 202);
- global_ui_context.style.image_outline_tint = rgb(200,200,200);
- global_ui_context.style.scrollbar_handle_background = rgb(225,225,225);
- global_ui_context.style.info_bar_background = rgb(225,225,225);
- global_ui_context.style.error_foreground = rgb(224,79,95);
- global_ui_context.style.item_hover_background = rgb(240,220,220);
- global_ui_context.style.scrollbar_background = rgb(255,255,255);
- global_ui_context.style.background = rgb(255,255,255);
- global_ui_context.style.menu_hover_background = rgb(200,200,200);
- global_ui_context.style.menu_background = rgb(225,225,225);
- global_ui_context.style.widget_hover_background = rgb(200,200,200);
- global_ui_context.style.widget_background = rgb(225,225,225);
- global_ui_context.style.border = rgb(180,180,180);
- global_ui_context.style.foreground = rgb(10, 10, 10);
- global_ui_context.style.textbox_background = rgb(240,240,240);
- global_ui_context.style.textbox_foreground = rgb(10,10,10);
- global_ui_context.style.textbox_placeholder_foreground = rgb(80,80,80);
- global_ui_context.style.textbox_active_border = rgb(66, 134, 244);
- }
- if (style == UI_STYLE_DARK)
- {
- global_ui_context.style.hypertext_foreground = rgb(66, 134, 244);
- global_ui_context.style.hypertext_hover_foreground = rgb(221, 93, 202);
- global_ui_context.style.scrollbar_handle_background = rgb(50,50,50);
- global_ui_context.style.menu_hover_background = rgb(60,60,60);
- global_ui_context.style.item_hover_background = rgb(80,60,60);
- global_ui_context.style.image_outline_tint = rgb(200,200,200);
- global_ui_context.style.error_foreground = rgb(224,79,95);
- global_ui_context.style.scrollbar_background = rgb(80,80,80);
- global_ui_context.style.widget_hover_background = rgb(50,50,50);
- global_ui_context.style.widget_background = rgb(65,65,65);
- global_ui_context.style.info_bar_background = rgb(65,65,65);
- global_ui_context.style.menu_background = rgb(65,65,65);
- global_ui_context.style.background = rgb(80, 80, 80);
- global_ui_context.style.border = rgb(60,60,60);
- global_ui_context.style.foreground = rgb(240,240,240);
- global_ui_context.style.textbox_background = rgb(65,65,65);
- global_ui_context.style.textbox_foreground = rgb(240, 240,240);
- global_ui_context.style.textbox_active_border = rgb(66, 134, 244);
- }
-}
-
-static scroll_state empty_scroll;
-inline void ui_create(platform_window *window, keyboard_input *keyboard, mouse_input *mouse, camera *camera, font *font_small)
-{
- ui_set_style(UI_STYLE_LIGHT);
-
- global_ui_context.layout.layout_direction = LAYOUT_VERTICAL;
- global_ui_context.layout.offset_x = 0;
- global_ui_context.layout.offset_y = 0;
- global_ui_context.layout.active_window = window;
- global_ui_context.layout.width = global_ui_context.layout.active_window->width;
- empty_scroll = ui_create_scroll(1);
- global_ui_context.layout.scroll = &empty_scroll;
-
- global_ui_context.keyboard = keyboard;
- global_ui_context.mouse = mouse;
- global_ui_context.font_small = font_small;
- global_ui_context.active_menus = array_create(sizeof(s32));
- global_ui_context.menu_item_count = 0;
- global_ui_context.camera = camera;
-
- array_reserve(&global_ui_context.active_menus, 100);
-}
-
-static void ui_pop_scissor()
-{
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 w = global_ui_context.layout.width;
- s32 h = global_ui_context.layout.height;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y - WIDGET_PADDING;
-
- render_set_scissor(global_ui_context.layout.active_window,
- 0,y,global_ui_context.layout.active_window->width,h);
- }
- else
- {
- render_reset_scissor();
- }
-}
-
-inline void ui_block_begin(layout_direction direction)
-{
- global_ui_context.layout.layout_direction = direction;
- global_ui_context.layout.block_height = 0;
- global_ui_context.layout.start_offset_y = global_ui_context.layout.offset_y;
- global_ui_context.layout.start_offset_x = global_ui_context.layout.offset_x;
-
- ui_pop_scissor();
-}
-
-inline void ui_block_end()
-{
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- {
- global_ui_context.layout.offset_y += global_ui_context.layout.block_height + WIDGET_PADDING;
- }
- global_ui_context.layout.offset_x = global_ui_context.layout.start_offset_x;
-}
-
-inline void ui_set_active_window(platform_window *window)
-{
- global_ui_context.layout.active_window = window;
-}
-
-inline void ui_begin_menu_bar()
-{
- s32 w = global_ui_context.layout.width;
- s32 h = global_ui_context.layout.active_window->height;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y;
-
- global_ui_context.layout.offset_x = 0;
- global_ui_context.layout.layout_direction = LAYOUT_HORIZONTAL;
-
- render_rectangle(0, y, w, MENU_BAR_HEIGHT, global_ui_context.style.menu_background);
- render_rectangle(0, y, w, 1, global_ui_context.style.border);
- global_ui_context.layout.menu_offset_y = 0;
-}
-
-inline bool ui_is_menu_active(u32 id)
-{
- for (int i = 0; i < global_ui_context.active_menus.length; i++)
- {
- s32 *iid = array_at(&global_ui_context.active_menus, i);
- if (*iid == id) return true;
- }
- return false;
-}
-
-inline u32 ui_get_id()
-{
- return global_ui_context.next_id++;
-}
-
-inline void ui_push_separator()
-{
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y;
- s32 w = global_ui_context.layout.width;
-
- render_rectangle(x, y, w, 1, global_ui_context.style.border);
- global_ui_context.layout.offset_y += 1 + WIDGET_PADDING;
-}
-
-void ui_push_vertical_dragbar()
-{
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x + global_ui_context.layout.width;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y - WIDGET_PADDING;
- s32 h = global_ui_context.layout.height;
-
- render_rectangle(x, y, 5, h, global_ui_context.style.border);
-}
-
-inline void ui_push_menu_item_separator()
-{
- global_ui_context.layout.menu_offset_y += 1;
-}
-
-static s32 ui_get_scroll()
-{
- if (global_ui_context.layout.scroll->in_scroll)
- {
- return global_ui_context.layout.scroll->scroll;
- }
-
- return 0;
-}
-
-bool ui_push_color_button(char *text, bool selected, color c)
-{
- bool result = false;
-
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 text_x = x + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 text_y = y + (BUTTON_HEIGHT/2) - (global_ui_context.font_small->px_h/2);
- s32 total_w =
- BUTTON_HORIZONTAL_TEXT_PADDING + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
- s32 h = BUTTON_HEIGHT;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- color bg_color = c;
-
- s32 virt_top = y;
- s32 virt_bottom = y + h;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- bg_color.r-=20;
- bg_color.g-=20;
- bg_color.b-=20;
-
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- result = true;
- }
- }
-
- if (selected)
- {
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 4, global_ui_context.style.border);
- }
- else
- {
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 1, global_ui_context.style.border);
- }
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result;
-}
-
-bool ui_push_dropdown_item(image *icon, char *title, s32 index)
-{
- bool result = false;
-
- set_render_depth(30);
-
- u32 id = ui_get_id();
- global_ui_context.layout.dropdown_item_count++;
- s32 h = BUTTON_HEIGHT;
- s32 x = global_ui_context.layout.dropdown_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() + ((global_ui_context.layout.dropdown_item_count)*h-(1*global_ui_context.layout.dropdown_item_count));
- s32 text_x = x + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 text_y = y + (BUTTON_HEIGHT/2) - (global_ui_context.font_small->px_h / 2);
- s32 total_w = DROPDOWN_ITEM_WIDTH
- + BUTTON_HORIZONTAL_TEXT_PADDING + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- color bg_color = global_ui_context.style.widget_background;
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y > y && mouse_y < y + h)
- {
- global_ui_context.item_hovered = true;
-
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.layout.active_dropdown_state->selected_index = index;
- result = true;
- }
-
- bg_color = global_ui_context.style.widget_hover_background;
- }
-
-
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 1, global_ui_context.style.border);
- if (icon)
- {
- render_image(icon, x+(BUTTON_HORIZONTAL_TEXT_PADDING/2),
- y + (h - (h-10))/2, h-10, h-10);
- text_x += h-10;
- }
- render_text(global_ui_context.font_small, text_x+(BUTTON_HORIZONTAL_TEXT_PADDING/2)-5, text_y, title, global_ui_context.style.foreground);
-
-
-#if 0
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-#endif
- set_render_depth(1);
-
- return result;
-}
-
-bool ui_push_dropdown(dropdown_state *state, char *title)
-{
- bool result = false;
-
- global_ui_context.layout.active_dropdown_state = state;
-
- u32 id = ui_get_id();
- global_ui_context.layout.dropdown_item_count = 0;
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 text_x = x + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 text_y = y + (BUTTON_HEIGHT/2) - (global_ui_context.font_small->px_h/2);
- s32 total_w = DROPDOWN_WIDTH + BUTTON_HORIZONTAL_TEXT_PADDING + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
- s32 h = BUTTON_HEIGHT;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- color bg_color = global_ui_context.style.widget_background;
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= y && mouse_y < y + h && !global_ui_context.item_hovered)
- {
- global_ui_context.item_hovered = true;
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- if (is_left_clicked(global_ui_context.mouse))
- {
- state->state = !state->state;
- }
-
- bg_color = global_ui_context.style.widget_hover_background;
- }
- else if (is_left_down(global_ui_context.mouse) && state->state)
- {
- state->state = false;
- // render dropdown this frame so item can be selected
- result = true;
- }
-
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 1, global_ui_context.style.border);
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
-
- render_triangle(x+total_w - h, y+(h-(h-12))/2, h-12, h-12, global_ui_context.style.border);
- global_ui_context.layout.dropdown_x = global_ui_context.layout.offset_x;
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result || state->state;
-}
-
-bool ui_push_menu(char *title)
-{
- bool result = false;
-
- global_ui_context.layout.menu_offset_y = 0;
- global_ui_context.menu_item_count = 0;
- u32 id = ui_get_id();
-
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 w = calculate_text_width(global_ui_context.font_small, title) +
- (MENU_HORIZONTAL_PADDING*2);
- s32 text_h = global_ui_context.font_small->px_h;
- s32 h = MENU_BAR_HEIGHT-1;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y+1;
- s32 text_y = global_ui_context.layout.offset_y - (text_h / 2) + (h / 2) + global_ui_context.camera->y;
- s32 text_x = x + MENU_HORIZONTAL_PADDING;
-
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- color bg_color = global_ui_context.style.menu_background;
-
- bool is_open = ui_is_menu_active(id);
- result = is_open;
-
- if (mouse_x >= x && mouse_x < x + w && mouse_y >= y && mouse_y < y + h)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- if (is_left_clicked(global_ui_context.mouse))
- {
- if (is_open)
- array_remove_by(&global_ui_context.active_menus, &id);
- else
- array_push(&global_ui_context.active_menus, &id);
-
- result = !is_open;
- is_open = result;
- }
-
- bg_color = global_ui_context.style.menu_hover_background;
- }
- else if (is_left_down(global_ui_context.mouse))
- {
- if (is_open)
- array_remove_by(&global_ui_context.active_menus, &id);
- is_open = false;
- }
- if (!global_ui_context.layout.active_window->has_focus && is_open)
- {
- array_remove_by(&global_ui_context.active_menus, &id);
- is_open = false;
- }
-
- render_rectangle(x, y, w, h, bg_color);
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
-
- global_ui_context.layout.prev_offset_x = global_ui_context.layout.offset_x;
- global_ui_context.layout.offset_x += w;
-
- return result;
-}
-
-static void ui_set_active_textbox(textbox_state *state)
-{
- if (global_ui_context.current_active_textbox && global_ui_context.current_active_textbox != state)
- {
- global_ui_context.current_active_textbox->state = false;
- }
- global_ui_context.current_active_textbox = state;
-}
-
-void ui_set_textbox_active(textbox_state *textbox)
-{
- ui_set_active_textbox(textbox);
- keyboard_set_input_text(global_ui_context.keyboard, textbox->buffer);
- textbox->state = true;
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- global_ui_context.keyboard->take_input = textbox->state;
-}
-
-bool ui_push_textbox(textbox_state *state, char *placeholder)
-{
- bool result = false;
- static u64 cursor_tick = 0;
- static u64 last_cursor_pos = -1;
-
- if (!global_ui_context.layout.active_window->has_focus)
- state->state = false;
-
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 text_x = x + 5;
- s32 text_y = y + (TEXTBOX_HEIGHT/2) - (global_ui_context.font_small->px_h/2);
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- if (global_ui_context.layout.block_height < TEXTBOX_HEIGHT)
- global_ui_context.layout.block_height = TEXTBOX_HEIGHT;
-
- bool has_text = state->buffer[0] != 0;
-
- if (!state->state)
- {
- render_rectangle(x, y, TEXTBOX_WIDTH, TEXTBOX_HEIGHT, global_ui_context.style.textbox_background);
- render_rectangle_outline(x, y, TEXTBOX_WIDTH, TEXTBOX_HEIGHT, 1, global_ui_context.style.border);
- }
- else
- {
- cursor_tick++;
- render_rectangle(x, y, TEXTBOX_WIDTH, TEXTBOX_HEIGHT, global_ui_context.style.textbox_background);
- render_rectangle_outline(x, y, TEXTBOX_WIDTH, TEXTBOX_HEIGHT, 1, global_ui_context.style.textbox_active_border);
- }
-
- s32 virt_top = y;
- s32 virt_bottom = y + TEXTBOX_HEIGHT;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- /////////////////////////////////////////////
- /////////////////////////////////////////////
- /////////////////////////////////////////////
-
- bool is_selecting = false;
- bool clicked_to_select = false;
- bool double_clicked_to_select_first = false;
- bool clicked_to_set_cursor = false;
- if (mouse_x >= x && mouse_x < x + TEXTBOX_WIDTH && mouse_y >= virt_top && mouse_y < virt_bottom)
- {
- if (is_left_double_clicked(global_ui_context.mouse) && has_text)
- {
- ui_set_active_textbox(state);
-
- global_ui_context.keyboard->selection_begin_offset = 0;
- global_ui_context.keyboard->selection_length = utf8len(global_ui_context.keyboard->input_text);
- global_ui_context.keyboard->has_selection = true;
- state->selection_start_index = 0;
-
- global_ui_context.mouse->left_state &= ~MOUSE_DOUBLE_CLICK;
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
-
- state->double_clicked_to_select = true;
- double_clicked_to_select_first = true;
- }
- if (is_left_clicked(global_ui_context.mouse))
- {
- ui_set_active_textbox(state);
-
- keyboard_set_input_text(global_ui_context.keyboard, state->buffer);
- cursor_tick = 0;
-
- if (global_ui_context.keyboard->has_selection)
- {
- global_ui_context.keyboard->has_selection = false;
- }
-
- clicked_to_set_cursor = true;
-
- state->state = true;
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- result = true;
-
- global_ui_context.keyboard->take_input = state->state;
- }
- }
- else if (is_left_clicked(global_ui_context.mouse))
- {
- if (state->state)
- {
- global_ui_context.keyboard->has_selection = false;
- }
-
- state->state = false;
- }
-
- if (is_left_released(global_ui_context.mouse))
- {
- state->attempting_to_select = false;
- }
-
- if (state->state && global_ui_context.keyboard->has_selection && is_left_down(global_ui_context.mouse))
- is_selecting = true;
-
- if (keyboard_is_key_pressed(global_ui_context.keyboard, KEY_ENTER) && state->deselect_on_enter)
- {
- global_ui_context.keyboard->has_selection = false;
- state->state = false;
- }
-
- // calculate scissor rectangle
- if (global_ui_context.layout.scroll->in_scroll)
- {
- vec4 v = render_get_scissor();
- s32 scissor_x = v.x + WIDGET_PADDING + 5;
- s32 scissor_y = global_ui_context.layout.scroll->scroll_start_offset_y;
- s32 scissor_w = v.w;
- s32 scissor_h = global_ui_context.layout.scroll->height - 2;
-
- render_set_scissor(global_ui_context.layout.active_window, scissor_x,
- scissor_y, scissor_w, scissor_h);
- }
- else
- {
- s32 scissor_x = x - global_ui_context.camera->x+3;
- s32 scissor_y = y - global_ui_context.camera->y;
- s32 scissor_w = TEXTBOX_WIDTH - 5;
- s32 scissor_h = TEXTBOX_HEIGHT;
-
- render_set_scissor(global_ui_context.layout.active_window,
- scissor_x, scissor_y, scissor_w, scissor_h);
- }
-
- s32 cursor_text_w;
- s32 cursor_x = 0;
-
- //if (!global_ui_context.keyboard->has_selection)
- //state->diff = 0;
-
- // select first character on click
- if (clicked_to_set_cursor)
- {
- global_ui_context.keyboard->cursor = calculate_cursor_position(global_ui_context.font_small,
- state->buffer, mouse_x + state->diff - text_x);
-
- state->last_click_cursor_index = global_ui_context.keyboard->cursor;
- state->attempting_to_select = true;
-
- global_ui_context.keyboard->selection_begin_offset = global_ui_context.keyboard->cursor;
-
-#if 0
- global_ui_context.keyboard->has_selection = true;
- global_ui_context.keyboard->selection_begin_offset = calculate_cursor_position(global_ui_context.font_small,
- state->buffer, mouse_x + state->diff - text_x);
- global_ui_context.keyboard->selection_length = 1;
- state->selection_start_index = global_ui_context.keyboard->selection_begin_offset;
-#endif
- }
-
- if (state->state)
- {
- s32 len = utf8len(global_ui_context.keyboard->input_text);
- s32 old_len = utf8len(state->buffer);
-
- // check if text changes, add to history if true
- bool is_lctrl_down = global_ui_context.keyboard->keys[KEY_LEFT_CONTROL];
-
- // go to previous state
- if (is_lctrl_down && keyboard_is_key_pressed(global_ui_context.keyboard, KEY_Z) && state->history.length)
- {
- textbox_history_entry history_entry;
- history_entry.text = mem_alloc(strlen(state->buffer)+1);
- history_entry.cursor_offset = last_cursor_pos;
- string_copyn(history_entry.text, state->buffer, MAX_INPUT_LENGTH);
- array_push(&state->future, &history_entry);
-
- global_ui_context.keyboard->text_changed = true;
-
- textbox_history_entry *old_text = array_at(&state->history, state->history.length-1);
- string_copyn(state->buffer, old_text->text, MAX_INPUT_LENGTH);
- keyboard_set_input_text(global_ui_context.keyboard, state->buffer);
-
- mem_free(old_text->text);
- array_remove_at(&state->history, state->history.length-1);
-
- global_ui_context.keyboard->cursor = old_text->cursor_offset;
- }
- else if (is_lctrl_down &&
- keyboard_is_key_pressed(global_ui_context.keyboard, KEY_Y) && state->future.length)
- {
- textbox_history_entry history_entry;
- history_entry.text = mem_alloc(strlen(state->buffer)+1);
- history_entry.cursor_offset = last_cursor_pos;
- string_copyn(history_entry.text, state->buffer, MAX_INPUT_LENGTH);
- array_push(&state->history, &history_entry);
-
- global_ui_context.keyboard->text_changed = true;
-
- textbox_history_entry *old_text = array_at(&state->future, state->future.length-1);
- string_copyn(state->buffer, old_text->text, MAX_INPUT_LENGTH);
- keyboard_set_input_text(global_ui_context.keyboard, state->buffer);
-
- mem_free(old_text->text);
- array_remove_at(&state->future, state->future.length-1);
-
- global_ui_context.keyboard->cursor = old_text->cursor_offset;
- }
- else
- {
- if (global_ui_context.keyboard->text_changed)
- {
- if (last_cursor_pos != -1)
- {
- textbox_history_entry history_entry;
- history_entry.text = mem_alloc(strlen(state->buffer)+1);
- history_entry.cursor_offset = last_cursor_pos;
- string_copyn(history_entry.text, state->buffer, MAX_INPUT_LENGTH);
- array_push(&state->history, &history_entry);
- }
- }
-
- string_copyn(state->buffer, global_ui_context.keyboard->input_text, state->max_len);
- if (global_ui_context.keyboard->cursor > state->max_len)
- {
- global_ui_context.keyboard->cursor = state->max_len;
- utf8_str_replace_at(global_ui_context.keyboard->input_text,global_ui_context.keyboard->cursor, 0);
- }
- }
-
- // cursor ticking after text change
- if (last_cursor_pos != global_ui_context.keyboard->cursor || global_ui_context.keyboard->text_changed)
- cursor_tick = 0;
-
- // draw cursor
- cursor_text_w = calculate_text_width_upto(global_ui_context.font_small,
- state->buffer, global_ui_context.keyboard->cursor);
-
- s32 text_w = calculate_text_width(global_ui_context.font_small, state->buffer);
-
- cursor_x = text_x + cursor_text_w - state->diff;
-
- // change offset after cursor position change
- if (!is_selecting && !global_ui_context.keyboard->has_selection)
- {
- if (cursor_text_w < state->diff)
- {
- state->diff = cursor_text_w;
- }
- if (cursor_text_w - state->diff > TEXTBOX_WIDTH - 10)
- {
- state->diff = (cursor_text_w) - (TEXTBOX_WIDTH - 10);
- }
- }
-
- // make sure offset is recalculated when text changes or a portion of text is changed so the textbox doesnt end up half empty
-#if 1
- if (!clicked_to_select && !clicked_to_set_cursor && !is_selecting && !global_ui_context.keyboard->has_selection && global_ui_context.keyboard->text_changed)
- {
- if ((text_w > TEXTBOX_WIDTH -10) && (global_ui_context.keyboard->text_changed || global_ui_context.keyboard->cursor != last_cursor_pos))
- {
- state->diff = text_w - TEXTBOX_WIDTH + 10;
- }
- else if ((text_w <= TEXTBOX_WIDTH -10))
- {
- state->diff = 0;
- }
- }
-#endif
- }
-
- s32 curr_index = calculate_cursor_position(global_ui_context.font_small,
- state->buffer, mouse_x + state->diff - text_x);
-
- //////////////////////////////////
- {
- if (curr_index != state->last_click_cursor_index && state->attempting_to_select)
- {
- clicked_to_select = true;
- state->attempting_to_select = false;
- }
- }
-
-
- // select first character on click
- if (clicked_to_select)
- {
-#if 1
- global_ui_context.keyboard->has_selection = true;
- //global_ui_context.keyboard->selection_begin_offset = calculate_cursor_position(global_ui_context.font_small,
- //state->buffer, mouse_x + state->diff - text_x);
- global_ui_context.keyboard->selection_length = 0;
- state->selection_start_index = global_ui_context.keyboard->selection_begin_offset;
-
- state->selection_start_index--;
-#endif
- }
-
-
- if (is_selecting)
- {
- // move text offset x when selecting so we can select more text than available on screen.
- if (global_ui_context.mouse->x < x + 10)
- {
- s32 text_w = calculate_text_width(global_ui_context.font_small, state->buffer);
- if (text_w > TEXTBOX_WIDTH-10)
- {
- state->diff -= TEXTBOX_SCROLL_X_SPEED;
- if (state->diff < 0) state->diff = 0;
- }
- }
- if (global_ui_context.mouse->x > x + TEXTBOX_WIDTH - 10)
- {
- s32 text_w = calculate_text_width(global_ui_context.font_small, state->buffer);
- s32 diff = text_w - TEXTBOX_WIDTH + 10;
-
- if (text_w > TEXTBOX_WIDTH-10)
- {
- state->diff += TEXTBOX_SCROLL_X_SPEED;
- if (state->diff > diff)
- state->diff = diff;
- }
- }
- ///////////////////////////////////////////////////////////
- }
-
- // change selection area based on cursor position.
- // if double clicked to select the entire textbox we should only
- // do this when the mouse has moved enough to select a new character
-#if 1
- if (is_selecting)
- {
- s32 index = calculate_cursor_position(global_ui_context.font_small,
- state->buffer, mouse_x + state->diff - text_x)+1;
-
- if (double_clicked_to_select_first)
- state->double_clicked_to_select_cursor_index = index;
-
- if (!state->double_clicked_to_select || (state->double_clicked_to_select && index != state->double_clicked_to_select_cursor_index))
- {
- if (index <= state->selection_start_index+1)
- {
- global_ui_context.keyboard->selection_begin_offset = index - 1;
- global_ui_context.keyboard->selection_length = state->selection_start_index - index + 2;
- }
- else if (index > state->selection_start_index)
- {
- global_ui_context.keyboard->selection_begin_offset = state->selection_start_index+1;
- global_ui_context.keyboard->selection_length = index - state->selection_start_index-2;
- }
-
- state->double_clicked_to_select = false;
- }
- }
-
- // render cursor
- if (state->state)
- {
- last_cursor_pos = global_ui_context.keyboard->cursor;
-
- s32 cursor_y = y + 4;
- s32 cursor_h = TEXTBOX_HEIGHT - 8;
- s32 cursor_w = 2;
-
- if (cursor_tick % 40 < 20 && !global_ui_context.keyboard->has_selection)
- render_rectangle(cursor_x, cursor_y, cursor_w, cursor_h, global_ui_context.style.textbox_foreground);
- }
-
-
- // render selection area
- if (global_ui_context.keyboard->has_selection && state->state)
- {
- char tmp_buffer[MAX_INPUT_LENGTH];
- utf8_str_copy_upto(state->buffer,
- global_ui_context.keyboard->selection_begin_offset,
- tmp_buffer);
-
- s32 selection_start_x = calculate_text_width(global_ui_context.font_small,
- tmp_buffer);
-
- utf8_str_copy_upto(
- utf8_str_upto(
- state->buffer,
- global_ui_context.keyboard->selection_begin_offset),
- global_ui_context.keyboard->selection_length,
- tmp_buffer);
-
- s32 selection_width = calculate_text_width(global_ui_context.font_small,
- tmp_buffer);
-
- render_rectangle(text_x - state->diff + selection_start_x, y+4, selection_width, TEXTBOX_HEIGHT-8, global_ui_context.style.textbox_active_border);
- }
-#endif
-
- if (!has_text)
- {
- render_text(global_ui_context.font_small, text_x - state->diff, text_y,
- placeholder, global_ui_context.style.textbox_placeholder_foreground);
- }
- else
- {
- render_text(global_ui_context.font_small, text_x - state->diff, text_y,
- state->buffer, global_ui_context.style.foreground);
- }
-
- ui_pop_scissor();
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += TEXTBOX_WIDTH + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += TEXTBOX_HEIGHT + WIDGET_PADDING;
-
- return result || state->state;
-}
-
-bool ui_push_hypertext_link(char *text)
-{
- bool result = false;
-
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + WIDGET_PADDING;
- s32 text_h = global_ui_context.font_small->px_h;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (global_ui_context.font_small->px_h/2) + spacing_y;
- s32 total_w = calculate_text_width(global_ui_context.font_small, text) +
- WIDGET_PADDING + WIDGET_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- if (global_ui_context.layout.block_height < global_ui_context.font_small->px_h)
- global_ui_context.layout.block_height = global_ui_context.font_small->px_h;
-
- color bg_color = global_ui_context.style.hypertext_foreground;
- if (mouse_x >= text_x && mouse_x < text_x + total_w && mouse_y >= text_y && mouse_y < text_y+text_h && !global_ui_context.item_hovered)
- {
- if (is_left_clicked(global_ui_context.mouse))
- {
- result = true;
- }
- bg_color = global_ui_context.style.hypertext_hover_foreground;
- }
-
- s32 text_width = render_text(global_ui_context.font_small, text_x, text_y, text, bg_color);
-
- if (result)
- render_rectangle(text_x, text_y + text_h-1, text_width, 1, bg_color);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += CHECKBOX_SIZE + WIDGET_PADDING;
-
- return result;
-}
-
-void ui_push_textf(font *f, char *text)
-{
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + WIDGET_PADDING;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (f->px_h/2) + spacing_y;
- s32 total_w = calculate_text_width(f, text) +
- WIDGET_PADDING + WIDGET_PADDING;
-
- if (global_ui_context.layout.block_height < f->px_h)
- global_ui_context.layout.block_height = f->px_h+5;
-
- render_text(f, text_x, text_y, text, global_ui_context.style.foreground);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += CHECKBOX_SIZE + WIDGET_PADDING;
-}
-
-
-void ui_push_textf_width(font *f, char *text, s32 maxw)
-{
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + WIDGET_PADDING;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (f->px_h/2) + spacing_y;
- s32 total_w = maxw +
- WIDGET_PADDING + WIDGET_PADDING;
- maxw -= (WIDGET_PADDING*2);
- if (global_ui_context.layout.block_height < f->px_h)
- global_ui_context.layout.block_height = f->px_h+5;
-
- render_text_ellipsed(f, text_x, text_y, maxw, text, global_ui_context.style.foreground);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += CHECKBOX_SIZE + WIDGET_PADDING;
-}
-
-
-void ui_push_text(char *text)
-{
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + WIDGET_PADDING;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (global_ui_context.font_small->px_h/2) + spacing_y;
- s32 total_w = calculate_text_width(global_ui_context.font_small, text) +
- WIDGET_PADDING + WIDGET_PADDING;
-
- if (global_ui_context.layout.block_height < global_ui_context.font_small->px_h)
- global_ui_context.layout.block_height = global_ui_context.font_small->px_h+5;
-
- render_text(global_ui_context.font_small, text_x, text_y, text, global_ui_context.style.foreground);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += CHECKBOX_SIZE + WIDGET_PADDING;
-}
-
-void ui_push_rect(s32 w, color c)
-{
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 total_w = w +
- WIDGET_PADDING + WIDGET_PADDING;
- s32 h = BUTTON_HEIGHT;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- {
- render_rectangle(x+WIDGET_PADDING,y,w,h,c);
- }
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-}
-
-bool ui_push_text_width(char *text, s32 maxw, bool active)
-{
- bool result = false;
-
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + WIDGET_PADDING;
- s32 h = BUTTON_HEIGHT;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (global_ui_context.font_small->px_h/2) + spacing_y;
- s32 total_w = maxw +
- WIDGET_PADDING + WIDGET_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
- s32 virt_top = y;
- s32 virt_bottom = y + h;
-
- if (global_ui_context.layout.block_height < global_ui_context.font_small->px_h)
- global_ui_context.layout.block_height = global_ui_context.font_small->px_h+5;
- maxw -= (WIDGET_PADDING*2);
- if (active)
- {
- bool hovered = false;
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- hovered = true;
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- if (is_left_clicked(global_ui_context.mouse))
- {
- result = true;
- }
- }
-
- if (hovered)
- {
- render_rectangle_outline(x-1,y+spacing_y,total_w, h, 1, global_ui_context.style.textbox_active_border);
- }
- }
-
- render_text_ellipsed(global_ui_context.font_small, text_x, text_y, maxw, text, global_ui_context.style.foreground);
-
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result;
-}
-
-bool ui_push_checkbox(checkbox_state *state, char *title)
-{
- bool result = false;
-
- s32 spacing_y = (BLOCK_HEIGHT - CHECKBOX_SIZE)/2;
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll() - spacing_y;
- s32 text_x = x + CHECKBOX_SIZE + WIDGET_PADDING;
- s32 text_y = y + (BLOCK_HEIGHT/2) - (global_ui_context.font_small->px_h/2) + spacing_y;
- s32 total_w = calculate_text_width(global_ui_context.font_small, title) +
- CHECKBOX_SIZE + WIDGET_PADDING + WIDGET_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- if (global_ui_context.layout.block_height < CHECKBOX_SIZE)
- global_ui_context.layout.block_height = CHECKBOX_SIZE;
-
- render_rectangle_outline(x, y, CHECKBOX_SIZE, CHECKBOX_SIZE, 1, global_ui_context.style.border);
-
- s32 virt_top = y;
- s32 virt_bottom = y + CHECKBOX_SIZE;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- if (mouse_x >= x && mouse_x < x + CHECKBOX_SIZE && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- if (is_left_clicked(global_ui_context.mouse))
- {
- state->state = !state->state;
- result = true;
- }
- }
-
- if (state->state)
- {
- s32 spacing = 2;
- render_rectangle(x+spacing, y+spacing, CHECKBOX_SIZE-(spacing*2), CHECKBOX_SIZE-(spacing*2), global_ui_context.style.border);
- }
-
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w;
- else
- global_ui_context.layout.offset_y += CHECKBOX_SIZE + WIDGET_PADDING;
-
- return result;
-}
-
-inline bool is_shortcut_down(s32 shortcut_keys[2])
-{
- return keyboard_is_key_down(global_ui_context.keyboard, shortcut_keys[0]) &&
- keyboard_is_key_pressed(global_ui_context.keyboard, shortcut_keys[1]);
-}
-
-bool ui_push_menu_item(char *title, char *shortcut)
-{
- bool result = false;
-
- set_render_depth(30);
-
- global_ui_context.menu_item_count++;
-
- s32 x = global_ui_context.layout.prev_offset_x + global_ui_context.camera->x;
- s32 w = MENU_ITEM_WIDTH;
- s32 text_h = global_ui_context.font_small->px_h;
- s32 h = MENU_BAR_HEIGHT;
- s32 y = global_ui_context.layout.offset_y + (global_ui_context.menu_item_count * h)+1 +
- global_ui_context.layout.menu_offset_y + global_ui_context.camera->y;
- s32 text_y = y - (text_h / 2) + (h / 2);
- s32 text_x = x + MENU_HORIZONTAL_PADDING;
- s32 text_2_x = x + w - MENU_HORIZONTAL_PADDING
- - calculate_text_width(global_ui_context.font_small, shortcut);
-
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- color bg_color = global_ui_context.style.menu_background;
-
- if ((mouse_x >= x && mouse_x < x + w && mouse_y >= y && mouse_y < y + h))
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- bg_color = global_ui_context.style.menu_hover_background;
- global_ui_context.item_hovered = true;
-
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- result = true;
- }
- }
-
- render_rectangle(x, y, w, h, bg_color);
- render_rectangle(x, y+MENU_BAR_HEIGHT, w, 1, global_ui_context.style.border);
-
- // borders
- render_rectangle(x, y, w, 1, bg_color);
- render_rectangle(x, y, 1, MENU_BAR_HEIGHT, global_ui_context.style.border);
- render_rectangle(x+w, y, 1, MENU_BAR_HEIGHT+1, global_ui_context.style.border);
-
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
- render_text(global_ui_context.font_small, text_2_x, text_y, shortcut, global_ui_context.style.foreground);
-
- set_render_depth(1);
-
- return result;
-}
-
-bool ui_push_image(image *img, s32 w, s32 h, s32 outline, color tint)
-{
- bool result = false;
-
- if (!img->loaded) return result;
-
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 total_w = w;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- s32 virt_top = y;
- s32 virt_bottom = y + h;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
-
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- }
- if (is_left_released(global_ui_context.mouse))
- {
- result = true;
- }
- }
-
- render_image_tint(img,x,y,w,h,global_ui_context.style.image_outline_tint);
- render_image_tint(img,x+outline,y+outline,w-(outline*2),h-(outline*2),tint);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result;
-}
-
-bool ui_push_button(button_state *state, char *title)
-{
- bool result = false;
-
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 text_x = x + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 text_y = y + (BUTTON_HEIGHT/2) - (global_ui_context.font_small->px_h/2);
- s32 total_w = calculate_text_width(global_ui_context.font_small, title) +
- BUTTON_HORIZONTAL_TEXT_PADDING + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
- s32 h = BUTTON_HEIGHT;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- color bg_color = global_ui_context.style.widget_background;
-
- s32 virt_top = y;
- s32 virt_bottom = y + h;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- bg_color = global_ui_context.style.widget_hover_background;
-
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- state->state = 1;
- }
- if (is_left_released(global_ui_context.mouse) && state->state)
- {
- state->state = 0;
- result = true;
- }
- }
- if (is_left_released(global_ui_context.mouse))
- {
- //state->state = 0;
- }
-
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 1, global_ui_context.style.border);
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result;
-}
-
-
-bool ui_push_button_image(button_state *state, char *title, image *img)
-{
- bool result = false;
-
- s32 x = global_ui_context.layout.offset_x + WIDGET_PADDING + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y + ui_get_scroll();
- s32 text_x = x + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 text_y = y + (BUTTON_HEIGHT/2) - (global_ui_context.font_small->px_h/2);
- s32 text_w = calculate_text_width(global_ui_context.font_small, title);
- s32 total_w = text_w + BUTTON_HORIZONTAL_TEXT_PADDING;
- s32 mouse_x = global_ui_context.mouse->x + global_ui_context.camera->x;
- s32 mouse_y = global_ui_context.mouse->y + global_ui_context.camera->y;
- s32 h = BUTTON_HEIGHT;
-
- if (title[0] == 0)
- {
- total_w = 0;
- }
-
- color bg_color = global_ui_context.style.widget_background;
-
- if (global_ui_context.layout.block_height < h)
- global_ui_context.layout.block_height = h;
-
- int icon_w = 1;
- int icon_h = 1;
- if (img->loaded)
- {
- float max_icon_size = BUTTON_HEIGHT - (BUTTON_IMAGE_PADDING*2);
- float scale = 1.0f;
- if (img->width >= img->height)
- {
- scale = img->width / max_icon_size;
-
- icon_w = img->width / scale;
- icon_h = icon_w;
- }
- else if (img->height >= img->width)
- {
- scale = img->height / max_icon_size;
-
- icon_h = img->height / scale;
- icon_w = icon_h;
- }
-
- total_w += icon_w + (BUTTON_IMAGE_SPACING*2);
- }
-
- s32 virt_top = y;
- s32 virt_bottom = y + h;
- if (global_ui_context.layout.scroll->in_scroll)
- {
- s32 bottom = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- if (bottom < virt_bottom)
- virt_bottom = bottom;
- s32 top = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
- if (top > virt_top)
- virt_top = top;
- }
-
- if (mouse_x >= x && mouse_x < x + total_w && mouse_y >= virt_top && mouse_y < virt_bottom && !global_ui_context.item_hovered)
- {
- platform_set_cursor(global_ui_context.layout.active_window, CURSOR_POINTER);
- bg_color = global_ui_context.style.widget_hover_background;
-
- if (is_left_clicked(global_ui_context.mouse))
- {
- global_ui_context.mouse->left_state &= ~MOUSE_CLICK;
- state->state = 1;
- }
- if (is_left_released(global_ui_context.mouse) && state->state)
- {
- state->state = 0;
- result = true;
- }
- }
- if (is_left_released(global_ui_context.mouse))
- {
- state->state = 0;
- }
-
- render_rectangle(x, y, total_w, BUTTON_HEIGHT, bg_color);
- render_rectangle_outline(x, y, total_w, BUTTON_HEIGHT, 1, global_ui_context.style.border);
- render_text(global_ui_context.font_small, text_x, text_y, title, global_ui_context.style.foreground);
- render_image(img, x + total_w - icon_w - BUTTON_IMAGE_SPACING, y + BUTTON_IMAGE_PADDING, img->width, img->height);
-
- if (global_ui_context.layout.layout_direction == LAYOUT_HORIZONTAL)
- global_ui_context.layout.offset_x += total_w + WIDGET_PADDING;
- else
- global_ui_context.layout.offset_y += BUTTON_HEIGHT + WIDGET_PADDING;
-
- return result;
-}
-
-inline void ui_end_menu_bar()
-{
- global_ui_context.layout.layout_direction = LAYOUT_VERTICAL;
- global_ui_context.layout.offset_x = 0;
- global_ui_context.layout.offset_y += MENU_BAR_HEIGHT;
-
- ui_push_separator();
-}
-
-inline void ui_destroy()
-{
- array_destroy(&global_ui_context.active_menus);
-}
-
-void ui_scroll_begin(scroll_state *state)
-{
- global_ui_context.layout.scroll = state;
- global_ui_context.layout.scroll->in_scroll = true;
- //global_ui_context.layout.scroll->height = height;
-
- s32 w = global_ui_context.layout.width;
- s32 h = state->height;
- s32 x = global_ui_context.layout.offset_x + global_ui_context.camera->x;
- s32 y = global_ui_context.layout.offset_y + global_ui_context.camera->y - WIDGET_PADDING;
-
- //global_ui_context.layout.offset_x += WIDGET_PADDING;
- global_ui_context.layout.start_offset_x = global_ui_context.layout.offset_x;
- //global_ui_context.layout.offset_y += WIDGET_PADDING;
- global_ui_context.layout.scroll->scroll_start_offset_y = global_ui_context.layout.offset_y;
-
- //render_rectangle_outline(x, y, w, h, 1, global_ui_context.style.border);
- render_set_scissor(global_ui_context.layout.active_window, x, y, w, h);
-}
-
-void ui_scroll_end()
-{
- s32 max_scroll = (global_ui_context.layout.scroll->scroll_start_offset_y -
- global_ui_context.layout.offset_y) + global_ui_context.layout.scroll->height;
-
- //global_ui_context.layout.offset_x -= WIDGET_PADDING;
- global_ui_context.layout.offset_y = global_ui_context.layout.scroll->scroll_start_offset_y + global_ui_context.layout.scroll->height;
- global_ui_context.layout.offset_y += WIDGET_PADDING;
-
- // draw scrollbar
- if (max_scroll < 0)
- {
- s32 scroll_y = 0;
- if (global_ui_context.mouse->scroll_state == SCROLL_UP)
- scroll_y+=SCROLL_SPEED;
- if (global_ui_context.mouse->scroll_state == SCROLL_DOWN)
- scroll_y-=SCROLL_SPEED;
- global_ui_context.layout.scroll->scroll += scroll_y;
- if (global_ui_context.layout.scroll->scroll > 0)
- global_ui_context.layout.scroll->scroll = 0;
- if (global_ui_context.layout.scroll->scroll < max_scroll)
- global_ui_context.layout.scroll->scroll = max_scroll;
-
- float percentage = global_ui_context.layout.scroll->scroll /
- (float)max_scroll;
-
- float scrollbar_height_percentage = -(max_scroll - global_ui_context.layout.scroll->height) / (float)global_ui_context.layout.scroll->height;
-
- s32 scrollbar_height = global_ui_context.layout.scroll->height / scrollbar_height_percentage;
- s32 scrollbar_pos_y = global_ui_context.layout.scroll->scroll_start_offset_y - WIDGET_PADDING;
-
- s32 tw = global_ui_context.layout.width - WIDGET_PADDING*2;
- s32 tx = global_ui_context.layout.offset_x + global_ui_context.camera->x + WIDGET_PADDING;
-
- s32 scrollbar_pos_x = tx + tw + WIDGET_PADDING - 10;
-
- scrollbar_pos_y += (global_ui_context.layout.scroll->height -
- scrollbar_height + WIDGET_PADDING - 2) * percentage;
-
- render_reset_scissor();
- render_rectangle(scrollbar_pos_x, scrollbar_pos_y, 10, scrollbar_height, global_ui_context.style.scrollbar_handle_background);
- }
- render_reset_scissor();
- global_ui_context.layout.scroll->in_scroll = false;
-}
diff --git a/src/ui.h b/src/ui.h
deleted file mode 100644
index c172a8c..0000000
--- a/src/ui.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#ifndef INCLUDE_UI
-#define INCLUDE_UI
-
-#define BLOCK_HEIGHT 25
-#define MENU_BAR_HEIGHT 25
-#define MENU_HORIZONTAL_PADDING 10
-#define WIDGET_PADDING 8
-#define BUTTON_HORIZONTAL_TEXT_PADDING 15
-#define MENU_ITEM_WIDTH 220
-#define CHECKBOX_SIZE BLOCK_HEIGHT - 8
-
-static s32 TEXTBOX_WIDTH = 280;
-
-#define TEXTBOX_HEIGHT BLOCK_HEIGHT
-#define BUTTON_HEIGHT BLOCK_HEIGHT
-#define BUTTON_IMAGE_PADDING 5
-#define BUTTON_IMAGE_SPACING 8
-#define DROPDOWN_WIDTH 225
-#define DROPDOWN_ITEM_WIDTH 225
-#define TEXTBOX_SCROLL_X_SPEED 32
-
-typedef enum t_ui_style_type
-{
- UI_STYLE_LIGHT = 1,
- UI_STYLE_DARK = 2,
-} ui_style_type;
-
-typedef struct t_ui_style
-{
- u16 id;
- color foreground;
- color background;
- color border;
- color textbox_background;
- color textbox_active_border;
- color textbox_foreground;
- color image_outline_tint;
- color scrollbar_handle_background;
- color info_bar_background;
- color error_foreground;
- color item_hover_background;
- color scrollbar_background;
- color menu_background;
- color menu_hover_background;
- color menu_foreground;
- color widget_hover_background;
- color widget_background;
- color hypertext_foreground;
- color hypertext_hover_foreground;
- color textbox_placeholder_foreground;
-} ui_style;
-
-typedef enum t_layout_direction
-{
- LAYOUT_HORIZONTAL,
- LAYOUT_VERTICAL,
-} layout_direction;
-
-typedef struct t_dropdown_state
-{
- bool state;
- int selected_index;
-} dropdown_state;
-
-typedef struct t_scroll_state
-{
- s32 height;
- s32 scroll;
- s32 scroll_start_offset_y;
- bool in_scroll;
-} scroll_state;
-
-typedef struct t_ui_layout
-{
- s32 dropdown_item_count;
- s32 dropdown_x;
- s32 offset_x;
- s32 offset_y;
- platform_window *active_window;
- layout_direction layout_direction;
- s32 prev_offset_x;
- s32 width;
- s32 height;
- s32 menu_offset_y;
- s32 block_height;
- s32 start_offset_y;
- s32 start_offset_x;
- scroll_state *scroll;
- s32 padding;
- dropdown_state *active_dropdown_state;
-} ui_layout;
-
-typedef struct t_textbox_history_entry
-{
- char *text;
- s32 cursor_offset;
-} textbox_history_entry;
-
-typedef struct t_textbox_state
-{
- bool deselect_on_enter;
- char *buffer;
- s32 selection_start_index;
- bool state;
- s32 diff;
- bool double_clicked_to_select;
- s32 double_clicked_to_select_cursor_index;
- s32 max_len;
- s32 text_offset_x;
- bool attempting_to_select;
- array history;
- array future;
- s32 last_click_cursor_index;
-} textbox_state;
-
-typedef struct t_checkbox_state
-{
- bool state;
-} checkbox_state;
-
-typedef struct t_button_state
-{
- bool state;
-} button_state;
-
-typedef struct t_ui_context
-{
- ui_style style;
- ui_layout layout;
- keyboard_input *keyboard;
- mouse_input *mouse;
- camera *camera;
- font *font_small;
- array active_menus;
- u32 next_id;
- s32 menu_item_count;
- dropdown_state *active_dropdown;
- textbox_state *current_active_textbox;
- bool item_hovered;
-} ui_context;
-
-ui_context global_ui_context;
-
-u32 ui_get_id();
-void ui_create(platform_window *window, keyboard_input *keyboard, mouse_input *mouse, camera *camera, font *font_small);
-void ui_set_active_window(platform_window *window);
-void ui_destroy();
-void ui_begin();
-void ui_end();
-bool ui_is_menu_active(u32 id);
-char* name_of_day(s32 day);
-char* name_of_month(s32 month);
-void ui_set_style(u16 style);
-void set_active_textbox(textbox_state *textbox);
-void ui_set_textbox_text(textbox_state *textbox, char *text);
-void ui_set_textbox_active(textbox_state *textbox);
-
-// widget initialization
-checkbox_state ui_create_checkbox(bool selected);
-textbox_state ui_create_textbox(u16 max_len);
-button_state ui_create_button();
-scroll_state ui_create_scroll(s32 scroll);
-dropdown_state ui_create_dropdown();
-
-void ui_destroy_textbox(textbox_state *state);
-
-// widgets
-bool is_shortcut_down(s32 shortcut_keys[2]);
-void ui_begin_menu_bar();
-bool ui_push_menu(char *title);
-bool ui_push_menu_item(char *title, char *shortcut);
-void ui_push_menu_item_separator();
-bool ui_push_dropdown(dropdown_state *state, char *title);
-bool ui_push_dropdown_item(image *icon, char *title, s32 index);
-void ui_push_separator();
-void ui_push_rect(s32 w, color rec);
-void ui_push_vertical_dragbar();
-void ui_block_begin(layout_direction direction);
-void ui_block_end();
-void ui_end_menu_bar();
-void ui_push_text(char *text);
-bool ui_push_text_width(char *text, s32 maxw, bool active);
-void ui_push_textf(font *f, char *text);
-void ui_push_textf_width(font *f, char *text, s32 maxw);
-bool ui_push_hypertext_link(char *text);
-bool ui_push_color_button(char *text, bool selected, color color);
-bool ui_push_image(image *img, s32 w, s32 h, s32 outline, color tint);
-bool ui_push_checkbox(checkbox_state *state, char *title);
-bool ui_push_textbox(textbox_state *state, char *title);
-bool ui_push_button(button_state *button, char *title);
-bool ui_push_button_image(button_state *button, char *title, image *img);
-void ui_scroll_begin(scroll_state *state);
-void ui_scroll_end();
-
-#endif \ No newline at end of file
diff --git a/src/windows/platform.c b/src/windows/platform.c
deleted file mode 100644
index 24fd6f6..0000000
--- a/src/windows/platform.c
+++ /dev/null
@@ -1,1268 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#include <locale.h>
-#include <windows.h>
-#include <GL/gl.h>
-#include <stdbool.h>
-#include <sysinfoapi.h>
-#include <wingdi.h>
-#include <errno.h>
-#include <shlwapi.h>
-#include <objbase.h>
-#include <shellapi.h>
-#include <gdiplus.h>
-#include <shlobj.h>
-#include "../external/LooplessSizeMove.c"
-
-struct t_platform_window
-{
- HWND window_handle;
- HDC hdc;
- HGLRC gl_context;
- WNDCLASS window_class;
-
- s32 min_width;
- s32 min_height;
- s32 max_width;
- s32 max_height;
-
- // shared window properties
- s32 width;
- s32 height;
- bool is_open;
- bool has_focus;
- cursor_type curr_cursor_type;
- cursor_type next_cursor_type;
-};
-
-extern BOOL GetPhysicallyInstalledSystemMemory(PULONGLONG TotalMemoryInKilobytes);
-
-LARGE_INTEGER perf_frequency;
-static HINSTANCE instance;
-platform_window *current_window_to_handle;
-keyboard_input *current_keyboard_to_handle;
-mouse_input *current_mouse_to_handle;
-
-int cmd_show;
-
-bool platform_get_clipboard(platform_window *window, char *buffer)
-{
- if (!OpenClipboard(NULL))
- return false;
-
- if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
- {
- CloseClipboard();
- return false;
- }
-
- wchar_t* clip_str = GetClipboardData(CF_UNICODETEXT);
- if (!clip_str)
- {
- CloseClipboard();
- return false;
- }
-
- WideCharToMultiByte(CP_UTF8, 0, clip_str, -1, buffer, MAX_INPUT_LENGTH ,0,0);
-
- CloseClipboard();
- return true;
-}
-
-bool platform_set_clipboard(platform_window *window, char *buffer)
-{
- HANDLE clipboard_data;
-
- int char_num = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, 0, 0);
- wchar_t *convstr = mem_alloc(char_num*2);
- int result = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, convstr, char_num);
-
- size_t len = result;
- size_t size = (len+1) * sizeof(wchar_t);
- LPSTR dst;
-
- if (!OpenClipboard(NULL))
- return false;
-
- clipboard_data = GlobalAlloc(GMEM_MOVEABLE, size);
- if (clipboard_data)
- {
- dst = GlobalLock(clipboard_data);
- memmove(dst, convstr, size);
- dst[len*2] = 0;
- GlobalUnlock(clipboard_data);
-
- SetClipboardData(CF_UNICODETEXT, clipboard_data);
- }
- else
- {
- CloseClipboard();
- return false;
- }
-
- CloseClipboard();
- return true;
-}
-
-inline void platform_show_alert(char *title, char *message)
-{
- // not implemented
-}
-
-inline void platform_destroy()
-{
- assets_destroy();
-
-#if defined(MODE_DEVELOPER)
- memory_print_leaks();
-#endif
-}
-
-bool is_platform_in_darkmode()
-{
- char *key = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\";
-
- HKEY result;
- LSTATUS o = RegOpenKeyExA(HKEY_CURRENT_USER, key, 0, KEY_READ, &result);
-
- if (o == 0)
- {
- BYTE data;
- DWORD len = 4;
- RegQueryValueExA(result, "AppsUseLightTheme", NULL, NULL, &data, &len);
-
- if (data == 1) return true;
- }
-
- return false;
-}
-
-inline void platform_set_cursor(platform_window *window, cursor_type type)
-{
- if (window->next_cursor_type != type)
- {
- window->next_cursor_type = type;
- }
-}
-
-bool platform_directory_exists(char *path)
-{
- return PathFileExistsA(path) == TRUE;
-}
-
-static void create_key_tables()
-{
- keycode_map[0x30] = KEY_0;
- keycode_map[0x31] = KEY_1;
- keycode_map[0x32] = KEY_2;
- keycode_map[0x33] = KEY_3;
- keycode_map[0x34] = KEY_4;
- keycode_map[0x35] = KEY_5;
- keycode_map[0x36] = KEY_6;
- keycode_map[0x37] = KEY_7;
- keycode_map[0x38] = KEY_8;
- keycode_map[0x39] = KEY_9;
- keycode_map[0x41] = KEY_A;
- keycode_map[0x42] = KEY_B;
- keycode_map[0x43] = KEY_C;
- keycode_map[0x44] = KEY_D;
- keycode_map[0x45] = KEY_E;
- keycode_map[0x46] = KEY_F;
- keycode_map[0x47] = KEY_G;
- keycode_map[0x48] = KEY_H;
- keycode_map[0x49] = KEY_I;
- keycode_map[0x4A] = KEY_J;
- keycode_map[0x4B] = KEY_K;
- keycode_map[0x4C] = KEY_L;
- keycode_map[0x4D] = KEY_M;
- keycode_map[0x4E] = KEY_N;
- keycode_map[0x4F] = KEY_O;
- keycode_map[0x50] = KEY_P;
- keycode_map[0x51] = KEY_Q;
- keycode_map[0x52] = KEY_R;
- keycode_map[0x53] = KEY_S;
- keycode_map[0x54] = KEY_T;
- keycode_map[0x55] = KEY_U;
- keycode_map[0x56] = KEY_V;
- keycode_map[0x57] = KEY_W;
- keycode_map[0x58] = KEY_X;
- keycode_map[0x59] = KEY_Y;
- keycode_map[0x5A] = KEY_Z;
-
- keycode_map[VK_OEM_7] = KEY_APOSTROPHE;
- keycode_map[VK_OEM_102] = KEY_BACKSLASH;
- keycode_map[VK_OEM_COMMA] = KEY_COMMA;
- keycode_map[VK_OEM_3] = KEY_GRAVE_ACCENT;
- keycode_map[VK_OEM_4] = KEY_LEFT_BRACKET;
- keycode_map[VK_OEM_MINUS] = KEY_MINUS;
- keycode_map[VK_OEM_PERIOD] = KEY_PERIOD;
-
- keycode_map[VK_BACK] = KEY_BACKSPACE;
- keycode_map[VK_DELETE] = KEY_DELETE;
- keycode_map[VK_END] = KEY_END;
- keycode_map[VK_RETURN] = KEY_ENTER;
- keycode_map[VK_ESCAPE] = KEY_ESCAPE;
- keycode_map[VK_HOME] = KEY_HOME;
- keycode_map[VK_INSERT] = KEY_INSERT;
- keycode_map[VK_MENU] = KEY_MENU;
- keycode_map[VK_NEXT] = KEY_PAGE_DOWN;
- keycode_map[VK_PRIOR] = KEY_PAGE_UP;
- keycode_map[VK_PAUSE] = KEY_PAUSE;
- keycode_map[VK_TAB] = KEY_TAB;
- keycode_map[VK_CAPITAL] = KEY_CAPS_LOCK;
- keycode_map[VK_NUMLOCK] = KEY_NUM_LOCK;
- keycode_map[VK_SCROLL] = KEY_SCROLL_LOCK;
- keycode_map[0x70] = KEY_F1;
- keycode_map[0x71] = KEY_F2;
- keycode_map[0x72] = KEY_F3;
- keycode_map[0x73] = KEY_F4;
- keycode_map[0x74] = KEY_F5;
- keycode_map[0x75] = KEY_F6;
- keycode_map[0x76] = KEY_F7;
- keycode_map[0x77] = KEY_F8;
- keycode_map[0x78] = KEY_F9;
- keycode_map[0x79] = KEY_F10;
- keycode_map[0x7A] = KEY_F11;
- keycode_map[0x7B] = KEY_F12;
- keycode_map[0x7C] = KEY_F13;
- keycode_map[0x7D] = KEY_F14;
- keycode_map[0x7E] = KEY_F15;
- keycode_map[0x7F] = KEY_F16;
- keycode_map[0x80] = KEY_F17;
- keycode_map[0x81] = KEY_F18;
- keycode_map[0x82] = KEY_F19;
- keycode_map[0x83] = KEY_F20;
- keycode_map[0x84] = KEY_F21;
- keycode_map[0x85] = KEY_F22;
- keycode_map[0x86] = KEY_F23;
- keycode_map[0x87] = KEY_F24;
- keycode_map[0x88] = KEY_LEFT_ALT;
- keycode_map[VK_CONTROL] = KEY_LEFT_CONTROL;
- keycode_map[VK_LCONTROL] = KEY_LEFT_CONTROL;
- keycode_map[VK_LSHIFT] = KEY_LEFT_SHIFT;
- keycode_map[VK_LWIN] = KEY_LEFT_SUPER;
- keycode_map[VK_SNAPSHOT] = KEY_PRINT_SCREEN;
- keycode_map[VK_RMENU] = KEY_RIGHT_ALT;
- keycode_map[VK_RCONTROL] = KEY_RIGHT_CONTROL;
- keycode_map[VK_RSHIFT] = KEY_RIGHT_SHIFT;
- keycode_map[VK_RWIN] = KEY_RIGHT_SUPER;
- keycode_map[VK_DOWN] = KEY_DOWN;
- keycode_map[VK_LEFT] = KEY_LEFT;
- keycode_map[VK_RIGHT] = KEY_RIGHT;
- keycode_map[VK_UP] = KEY_UP;
-
- keycode_map[VK_NUMPAD0] = KEY_KP_0;
- keycode_map[VK_NUMPAD1] = KEY_KP_1;
- keycode_map[VK_NUMPAD2] = KEY_KP_2;
- keycode_map[VK_NUMPAD3] = KEY_KP_3;
- keycode_map[VK_NUMPAD4] = KEY_KP_4;
- keycode_map[VK_NUMPAD5] = KEY_KP_5;
- keycode_map[VK_NUMPAD6] = KEY_KP_6;
- keycode_map[VK_NUMPAD7] = KEY_KP_7;
- keycode_map[VK_NUMPAD8] = KEY_KP_8;
- keycode_map[VK_NUMPAD9] = KEY_KP_9;
- keycode_map[VK_ADD] = KEY_KP_ADD;
- keycode_map[VK_DECIMAL] = KEY_KP_DECIMAL;
- keycode_map[VK_DIVIDE] = KEY_KP_DIVIDE;
- keycode_map[VK_MULTIPLY] = KEY_KP_MULTIPLY;
- keycode_map[VK_SUBTRACT] = KEY_KP_SUBTRACT;
-}
-
-bool platform_file_exists(char *path)
-{
- DWORD dwAttrib = GetFileAttributes(path);
-
- return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
- !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
-}
-
-void platform_create_config_directory()
-{
- char tmp[PATH_MAX];
- if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, tmp)))
- {
- string_appendn(tmp, "/moedit", PATH_MAX);
- }
-
-
- if (!platform_directory_exists(tmp))
- {
- CreateDirectoryA(tmp, NULL);
- }
-}
-
-char* get_config_save_location(char *buffer)
-{
- if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer)))
- {
- string_appendn(buffer, "\\moedit\\config.txt", MAX_INPUT_LENGTH);
- return buffer;
- }
-
- return 0;
-}
-
-void platform_show_message(platform_window *window, char *message, char *title)
-{
- HWND handle = window ? window->window_handle : NULL;
- MessageBox(handle, message, title, MB_ICONINFORMATION | MB_OK);
-}
-
-LRESULT CALLBACK main_window_callback(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
-{
- LRESULT result = 0;
-
- if (message == WM_SIZE)
- {
- u32 width = lparam&0xFFFF;
- u32 height = lparam>>16;
-
- current_window_to_handle->width = width;
- current_window_to_handle->height = height;
- }
- else if (message == WM_CHAR)
- {
- if (current_keyboard_to_handle->take_input)
- {
- char buf[5];
- memset(buf, 0, 5);
- char *ch = 0;
-
- wchar_t codep = wparam;
-
- WideCharToMultiByte(CP_UTF8, 0, &codep, 1, buf, 5 ,0,0);
-
- if (utf8len(buf) == 1)
- {
- char val = buf[0];
-
- if (current_keyboard_to_handle->input_mode == INPUT_NUMERIC)
- {
- if (!(val >= 48 && val <= 57))
- {
- ch = 0;
- }
- else
- {
- snprintf(buf, 2, "%c", val);
- ch = buf;
- }
- }
- else if (val >= 32 && val <= 126)
- {
- snprintf(buf, 5, "%c", val);
- ch = buf;
- }
- }
-
- if (ch != 0)
- keyboard_handle_input_string(current_window_to_handle, current_keyboard_to_handle, ch);
- }
- }
- else if (message == WM_MOUSELEAVE)
- {
- //current_mouse_to_handle->x = MOUSE_OFFSCREEN;
- //current_mouse_to_handle->y = MOUSE_OFFSCREEN;
- }
- else if (message == WM_KILLFOCUS)
- {
- current_mouse_to_handle->x = MOUSE_OFFSCREEN;
- current_mouse_to_handle->y = MOUSE_OFFSCREEN;
-
- current_window_to_handle->has_focus = false;
- memset(current_keyboard_to_handle->keys, 0, MAX_KEYCODE);
- }
- else if (message == WM_SETFOCUS)
- {
- current_window_to_handle->has_focus = true;
- }
- else if (message == WM_KEYDOWN)
- {
- s32 key = wparam;
-
- current_keyboard_to_handle->keys[keycode_map[key]] = true;
- current_keyboard_to_handle->input_keys[keycode_map[key]] = true;
-
- if (current_keyboard_to_handle->take_input)
- keyboard_handle_input_string(current_window_to_handle,
- current_keyboard_to_handle, 0);
- }
- else if (message == WM_KEYUP)
- {
- s32 key = wparam;
- current_keyboard_to_handle->keys[keycode_map[key]] = false;
- current_keyboard_to_handle->input_keys[keycode_map[key]] = false;
- }
- else if (message == WM_LBUTTONDOWN ||
- message == WM_RBUTTONDOWN ||
- message == WM_MBUTTONDOWN ||
- message == WM_MOUSEWHEEL)
- {
- bool is_left_down = wparam & MK_LBUTTON;
- bool is_right_down = wparam & MK_RBUTTON;
- bool is_middle_down = wparam & MK_MBUTTON;
-
- u64 ev_time = platform_get_time(TIME_FULL, TIME_MILI_S);
- static u64 last_ev_time = 0;
-
- if (message == WM_MOUSEWHEEL)
- {
- s16 scroll_val = wparam>>16;
-
- if (scroll_val < 0)
- current_mouse_to_handle->scroll_state = SCROLL_DOWN;
- else
- current_mouse_to_handle->scroll_state = SCROLL_UP;
- }
-
- if (is_left_down)
- {
- if (ev_time - last_ev_time < 200)
- {
- current_mouse_to_handle->left_state |= MOUSE_DOUBLE_CLICK;
- }
-
- current_mouse_to_handle->left_state |= MOUSE_DOWN;
- current_mouse_to_handle->left_state |= MOUSE_CLICK;
-
- current_mouse_to_handle->total_move_x = 0;
- current_mouse_to_handle->total_move_y = 0;
- last_ev_time = ev_time;
- }
- if (is_right_down)
- {
- current_mouse_to_handle->right_state |= MOUSE_DOWN;
- current_mouse_to_handle->right_state |= MOUSE_CLICK;
- }
- }
- else if (message == WM_LBUTTONUP ||
- message == WM_RBUTTONUP ||
- message == WM_MBUTTONUP)
- {
- bool is_left_up = message == WM_LBUTTONUP;
- bool is_right_up = message == WM_RBUTTONUP;
- bool is_middle_up = message == WM_MBUTTONUP;
-
- if (is_left_up)
- {
- current_mouse_to_handle->left_state = MOUSE_RELEASE;
- }
- if (is_right_up)
- {
- current_mouse_to_handle->right_state = MOUSE_RELEASE;
- }
- }
- else if (message == WM_MOUSEMOVE)
- {
- current_window_to_handle->curr_cursor_type = -999;
-
-#if 0
- s32 x = lparam&0xFFFF;
- s32 y = lparam>>16;
-
- current_mouse_to_handle->x = x;
- current_mouse_to_handle->y = y;
-#endif
-
- TRACKMOUSEEVENT track;
- track.cbSize = sizeof(track);
- track.dwFlags = TME_LEAVE;
- track.hwndTrack = current_window_to_handle->window_handle;
- TrackMouseEvent(&track);
- }
- else if (message == WM_GETMINMAXINFO)
- {
- MINMAXINFO *info = (MINMAXINFO*)lparam;
-
- info->ptMinTrackSize.x = current_window_to_handle->min_width;
- info->ptMinTrackSize.y = current_window_to_handle->min_height;
-
- if (current_window_to_handle->max_width)
- info->ptMaxTrackSize.x = current_window_to_handle->max_width;
- if (current_window_to_handle->max_height)
- info->ptMaxTrackSize.y = current_window_to_handle->max_height;
- }
- else if (message == WM_DESTROY)
- {
- current_window_to_handle->is_open = false;
- }
- else if (message == WM_CLOSE)
- {
- current_window_to_handle->is_open = false;
- }
- else
- {
- result = LSMProc(window, message, wparam, lparam);
- }
-
- return result;
-}
-
-void platform_window_set_title(platform_window *window, char *name)
-{
- SetWindowText(window->window_handle, name);
-}
-
-vec2 platform_get_window_size(platform_window *window)
-{
- RECT rec;
- GetWindowRect(window->window_handle, &rec);
- vec2 res;
- res.x = rec.right - rec.left;
- res.y = rec.bottom - rec.top;
- return res;
-}
-
-void platform_get_focus(platform_window *window)
-{
- SetFocus(window->window_handle);
-}
-
-platform_window platform_open_window(char *name, u16 width, u16 height, u16 max_w, u16 max_h, u16 min_w, u16 min_h)
-{
-#if !defined(MODE_GDBDEBUG) && !defined(MODE_DEVELOPER)
- ShowWindow(GetConsoleWindow(), SW_HIDE);
-#endif
-
- platform_window window;
- window.has_focus = true;
- window.window_handle = 0;
- window.hdc = 0;
- window.width = width;
- window.height = height;
- window.min_width = min_w;
- window.min_height = min_h;
- window.max_width = max_w;
- window.max_height = max_h;
- window.curr_cursor_type = -1;
- window.next_cursor_type = CURSOR_DEFAULT;
-
- current_window_to_handle = &window;
-
- memset(&window.window_class, 0, sizeof(WNDCLASS));
- window.window_class.style = CS_OWNDC;
- window.window_class.lpfnWndProc = main_window_callback;
- window.window_class.hInstance = instance;
- window.window_class.lpszClassName = name;
- window.window_class.hIcon = LoadIcon(NULL, IDI_WINLOGO);
- //window.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
-
- if (RegisterClass(&window.window_class))
- {
- int style = WS_VISIBLE|WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX;
-
- if (min_w != max_w && min_h != max_h)
- style |= WS_SIZEBOX;
- else
- style |= WS_THICKFRAME;
-
- window.window_handle = CreateWindowEx(0,
- window.window_class.lpszClassName,
- name,
- style,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- width,
- height,
- 0,
- 0,
- instance,
- 0);
-
- if (window.window_handle)
- {
- window.hdc = GetDC(window.window_handle);
-
- PIXELFORMATDESCRIPTOR format;
- memset(&format, 0, sizeof(PIXELFORMATDESCRIPTOR));
- format.nSize = sizeof(PIXELFORMATDESCRIPTOR);
- format.nVersion = 1;
- format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
- format.cColorBits = 24;
- format.cAlphaBits = 8;
- format.iLayerType = PFD_MAIN_PLANE; // PFD_TYPE_RGBA
- s32 suggested_format_index = ChoosePixelFormat(window.hdc, &format);
-
- PIXELFORMATDESCRIPTOR actual_format;
- DescribePixelFormat(window.hdc, suggested_format_index, sizeof(actual_format), &actual_format);
- SetPixelFormat(window.hdc, suggested_format_index, &actual_format);
-
- window.gl_context = wglCreateContext(window.hdc);
-
- static HGLRC share_list = 0;
- if (share_list == 0)
- {
- share_list = window.gl_context;
- }
- else
- {
- wglShareLists(share_list, window.gl_context);
- }
-
- wglMakeCurrent(window.hdc, window.gl_context);
-
- ShowWindow(window.window_handle, cmd_show);
-
- // blending
- glEnable(GL_DEPTH_TEST);
- //glDepthMask(true);
- //glClearDepth(50);
- glDepthFunc(GL_LEQUAL);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- // setup multisampling
-#if 0
- glEnable(GL_ALPHA_TEST);
- glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
- glEnable(GL_SAMPLE_ALPHA_TO_ONE);
- glEnable(GL_MULTISAMPLE);
- glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
-#endif
-
- // https://stackoverflow.com/questions/5627229/sub-pixel-drawing-with-opengl
- //glHint(GL_POINT_SMOOTH, GL_NICEST);
- //glHint(GL_LINE_SMOOTH, GL_NICEST);
- //glHint(GL_POLYGON_SMOOTH, GL_NICEST);
-
- //glEnable(GL_SMOOTH);
- //glEnable(GL_POINT_SMOOTH);
- //glEnable(GL_LINE_SMOOTH);
- //glEnable(GL_POLYGON_SMOOTH);
- //////////////////
-
- window.is_open = true;
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, width, height, 0, -1, 1);
-
- //GLint m_viewport[4];
- //glGetIntegerv( GL_VIEWPORT, m_viewport );
- //printf("%d %d %d %d\n", m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]);
-
- glMatrixMode(GL_MODELVIEW);
-
- TRACKMOUSEEVENT track;
- track.cbSize = sizeof(track);
- track.dwFlags = TME_LEAVE;
- track.hwndTrack = window.window_handle;
- TrackMouseEvent(&track);
- }
- else
- {
- platform_show_message(0, "An error occured within Windows, please restart the program.", "Error");
- abort();
- }
- }
- else
- {
- platform_show_message(0, "An error occured within Windows, please restart the program.", "Error");
- abort();
- }
-
- platform_get_focus(&window);
-
- return window;
-}
-
-void platform_window_set_size(platform_window *window, u16 width, u16 height)
-{
- s32 style = GetWindowLong(window->window_handle, GWL_STYLE);
- BOOL menu = FALSE;
-
- RECT rec;
- rec.left = 0;
- rec.top = 0;
- rec.right = width;
- rec.bottom = height;
- AdjustWindowRectEx(&rec, style, menu, 0);
- SetWindowPos(window->window_handle,NULL,rec.left,rec.top,rec.right,rec.bottom,SWP_NOMOVE|SWP_NOZORDER);
-}
-
-void platform_window_set_position(platform_window *window, u16 x, u16 y)
-{
- RECT rec;
- GetWindowRect(window->window_handle, &rec);
- MoveWindow(window->window_handle, x, y, rec.right-rec.left, rec.bottom-rec.top, FALSE);
-}
-
-bool platform_window_is_valid(platform_window *window)
-{
- return window->hdc && window->window_handle;
-}
-
-void platform_destroy_window(platform_window *window)
-{
- wglMakeCurrent(NULL, NULL);
- wglDeleteContext(window->gl_context);
-
- ReleaseDC(window->window_handle, window->hdc);
- CloseWindow(window->window_handle);
- DestroyWindow(window->window_handle);
- UnregisterClassA(window->window_class.lpszClassName, instance);
- window->hdc = 0;
- window->window_handle = 0;
-}
-
-void platform_handle_events(platform_window *window, mouse_input *mouse, keyboard_input *keyboard)
-{
- current_window_to_handle = window;
- current_keyboard_to_handle = keyboard;
- current_mouse_to_handle = mouse;
-
- mouse->left_state &= ~MOUSE_CLICK;
- mouse->right_state &= ~MOUSE_CLICK;
- mouse->left_state &= ~MOUSE_DOUBLE_CLICK;
- mouse->right_state &= ~MOUSE_DOUBLE_CLICK;
- mouse->left_state &= ~MOUSE_RELEASE;
- mouse->right_state &= ~MOUSE_RELEASE;
- memset(keyboard->input_keys, 0, MAX_KEYCODE);
- mouse->move_x = 0;
- mouse->move_y = 0;
- mouse->scroll_state = 0;
- keyboard->text_changed = false;
-
- // mouse position (including outside of window)
- current_window_to_handle->has_focus = GetFocus() == current_window_to_handle->window_handle;
-
- if (current_window_to_handle->has_focus)
- {
- if((GetKeyState(VK_LBUTTON) & 0x100) == 0)
- {
- current_mouse_to_handle->left_state = MOUSE_RELEASE;
- }
-
- RECT rec;
- GetWindowRect(window->window_handle, &rec);
- POINT p;
- GetCursorPos(&p);
- mouse->x = p.x - rec.left - GetSystemMetrics(SM_CYSIZEFRAME);
- mouse->y = p.y - rec.top - GetSystemMetrics(SM_CYSIZE) - GetSystemMetrics(SM_CYFRAME);
- //printf("%d %d\n",GetSystemMetrics(SM_CYSIZE), GetSystemMetrics(SM_CYFRAME));
- }
-
- MSG message;
- while(PeekMessageA(&message, window->window_handle, 0, 0, TRUE))
- {
- TranslateMessage(&message);
- SizingCheck(&message);
- DispatchMessage(&message);
- }
-
- glViewport(0, 0, window->width, window->height);
-}
-
-void platform_window_swap_buffers(platform_window *window)
-{
- // set cursor if changed
- if (window->curr_cursor_type != window->next_cursor_type)
- {
- char *cursor_shape = 0;
- switch(window->next_cursor_type)
- {
- case CURSOR_DEFAULT: cursor_shape = IDC_ARROW; break;
- case CURSOR_POINTER: cursor_shape = IDC_HAND; break;
- }
-
- HCURSOR cursor = LoadCursorA(NULL, cursor_shape);
-
- window->curr_cursor_type = window->next_cursor_type;
- SetCursor(cursor);
- }
-
- SwapBuffers(window->hdc);
-}
-
-file_content platform_read_file_content(char *path, const char *mode)
-{
- file_content result;
- result.content = 0;
- result.content_length = 0;
- result.file_error = 0;
-
- FILE *file = fopen(path, mode);
- if (!file)
- {
- if (errno == EMFILE)
- result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS;
- else if (errno == ENFILE)
- result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM;
- else if (errno == EACCES)
- result.file_error = FILE_ERROR_NO_ACCESS;
- else if (errno == EPERM)
- result.file_error = FILE_ERROR_NO_ACCESS;
- else if (errno == ENOENT)
- result.file_error = FILE_ERROR_NOT_FOUND;
- else if (errno == ECONNABORTED)
- result.file_error = FILE_ERROR_CONNECTION_ABORTED;
- else if (errno == ECONNREFUSED)
- result.file_error = FILE_ERROR_CONNECTION_REFUSED;
- else if (errno == ENETDOWN)
- result.file_error = FILE_ERROR_NETWORK_DOWN;
- else
- {
- result.file_error = FILE_ERROR_GENERIC;
- printf("ERROR: %d\n", errno);
- }
-
- goto done_failure;
- }
-
- fseek(file, 0 , SEEK_END);
- int length = ftell(file);
- fseek(file, 0, SEEK_SET);
-
- s32 length_to_alloc = length+1;
-
- result.content = mem_alloc(length_to_alloc);
- if (!result.content) goto done;
-
- memset(result.content, 0, length);
- s32 read_result = fread(result.content, 1, length, file);
- if (read_result == 0 && length != 0)
- {
- mem_free(result.content);
- result.content = 0;
- return result;
- }
-
- result.content_length = read_result;
-
- ((char*)result.content)[length] = 0;
-
- done:
- fclose(file);
- done_failure:
- return result;
-}
-
-bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 len)
-{
- bool result = false;
-
- FILE *file = fopen(path, mode);
-
- if (!file)
- {
- goto done_failure;
- }
- else
- {
- fwrite(buffer, 1, len, file);
- //fprintf(file, buffer);
- }
-
- //done:
- fclose(file);
- done_failure:
- return result;
-}
-
-void platform_destroy_file_content(file_content *content)
-{
- assert(content);
- mem_free(content->content);
-}
-
-bool get_active_directory(char *buffer)
-{
- return GetCurrentDirectory(MAX_INPUT_LENGTH, buffer);
-}
-
-bool set_active_directory(char *path)
-{
- return SetCurrentDirectory(path);
-}
-
-void platform_list_files_block(array *list, char *start_dir, array filters, bool recursive, memory_bucket *bucket, bool include_directories, bool *is_cancelled)
-{
- assert(list);
- s32 len = 0;
- char *matched_filter = 0;
-
- char *subdirname_buf;
- if (bucket)
- subdirname_buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- subdirname_buf = mem_alloc(MAX_INPUT_LENGTH);
-
- char *start_dir_fix;
- if (bucket)
- start_dir_fix = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- start_dir_fix = mem_alloc(MAX_INPUT_LENGTH);
- snprintf(start_dir_fix, MAX_INPUT_LENGTH, "%s*", start_dir);
-
- char *start_dir_clean;
- if (bucket)
- start_dir_clean = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- start_dir_clean = mem_alloc(MAX_INPUT_LENGTH);
- string_copyn(start_dir_clean, start_dir, MAX_INPUT_LENGTH);
-
- WIN32_FIND_DATAA file_info;
- HWND handle = FindFirstFileA(start_dir_fix, &file_info);
-
- if (!bucket)
- mem_free(start_dir_fix);
-
- if (handle == INVALID_HANDLE_VALUE)
- {
- return;
- }
-
- do
- {
- if (*is_cancelled) break;
- char *name = file_info.cFileName;
-
- // symbolic link is not allowed..
- if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
- continue;
-
-
- if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
- if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
- continue;
-
- if (include_directories)
- {
- if ((len = filter_matches(&filters, name,
- &matched_filter)) && len != -1)
- {
- // is file
- char *buf;
- if (bucket)
- buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- buf = mem_alloc(MAX_INPUT_LENGTH);
- snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, name);
-
- found_file f;
- f.path = buf;
-
- if (bucket)
- f.matched_filter= memory_bucket_reserve(bucket, len+1);
- else
- f.matched_filter= mem_alloc(len+1);
-
- string_copyn(f.matched_filter, matched_filter, len+1);
-
- mutex_lock(&list->mutex);
- array_push_size(list, &f, sizeof(found_file));
- mutex_unlock(&list->mutex);
- }
- }
-
- if (recursive)
- {
- string_copyn(subdirname_buf, start_dir_clean, MAX_INPUT_LENGTH);
- string_appendn(subdirname_buf, name, MAX_INPUT_LENGTH);
- string_appendn(subdirname_buf, "\\", MAX_INPUT_LENGTH);
-
- // is directory
- platform_list_files_block(list, subdirname_buf, filters, recursive, bucket, include_directories, is_cancelled);
- }
- }
- else if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ||
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ||
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ||
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
- {
- if ((len = filter_matches(&filters, name,
- &matched_filter)) && len != -1)
- {
- // is file
- char *buf;
- if (bucket)
- buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
- else
- buf = mem_alloc(MAX_INPUT_LENGTH);
-
- snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, name);
-
- found_file f;
- f.path = buf;
-
- if (bucket)
- f.matched_filter = memory_bucket_reserve(bucket, len+1);
- else
- f.matched_filter = mem_alloc(len+1);
-
- string_copyn(f.matched_filter, matched_filter, len+1);
-
- mutex_lock(&list->mutex);
- array_push_size(list, &f, sizeof(found_file));
- mutex_unlock(&list->mutex);
- }
- }
- }
- while (FindNextFile(handle, &file_info) != 0);
-
- if (!bucket)
- mem_free(start_dir_clean);
-
- FindClose(handle);
-}
-
-static void* platform_open_file_dialog_implementation(void *data)
-{
- struct open_dialog_args *args = data;
-
- OPENFILENAME info;
- info.lStructSize = sizeof(OPENFILENAME);
- info.hwndOwner = NULL;
- info.hInstance = NULL;
-
- char filter[MAX_INPUT_LENGTH];
- memset(filter, 0, MAX_INPUT_LENGTH);
-
- if (args->file_filter)
- {
- string_copyn(filter, args->file_filter, MAX_INPUT_LENGTH);
- filter[strlen(filter)] = 0;
- filter[strlen(filter)+1] = 0;
- info.lpstrFilter = filter;
- }
- else
- {
- info.lpstrFilter = NULL;
- }
-
- char szFile[MAX_INPUT_LENGTH];
- memset(szFile, 0, MAX_INPUT_LENGTH);
-
- info.lpstrCustomFilter = NULL;
- info.nMaxCustFilter = MAX_INPUT_LENGTH;
- info.nFilterIndex = 0;
- info.lpstrFile = (char*)szFile;
- info.nMaxFile = MAX_INPUT_LENGTH;
-
- info.lpstrDefExt = args->default_save_file_extension;
-
- info.lpstrFileTitle = NULL;
- info.lpstrInitialDir = args->start_path;
- info.lpstrTitle = NULL;
-
- if (args->type == SAVE_FILE)
- {
- info.Flags = OFN_EXTENSIONDIFFERENT | OFN_OVERWRITEPROMPT;
- GetSaveFileNameA(&info);
- string_copyn(args->buffer, info.lpstrFile, MAX_INPUT_LENGTH);
- }
- else if (args->type == OPEN_DIRECTORY)
- {
- BROWSEINFOA inf;
- PIDLIST_ABSOLUTE result = SHBrowseForFolderA(&inf);
- if (!result) return 0;
-
- SHGetPathFromIDListA(result, args->buffer);
- }
- else if (args->type == OPEN_FILE)
- {
- info.Flags = OFN_FILEMUSTEXIST;
- GetOpenFileNameA(&info);
- string_copyn(args->buffer, info.lpstrFile, MAX_INPUT_LENGTH);
- }
-
- return 0;
-}
-
-void *platform_open_file_dialog_block(void *arg)
-{
- platform_open_file_dialog_implementation(arg);
- mem_free(arg);
- return 0;
-}
-
-char *platform_get_full_path(char *file)
-{
- char *buf = mem_alloc(MAX_INPUT_LENGTH);
- if (!GetFullPathNameA(file, MAX_INPUT_LENGTH, buf, NULL))
- {
- buf[0] = 0;
- }
-
- return buf;
-}
-
-void platform_open_url(char *command)
-{
- platform_run_command(command);
-}
-
-void platform_run_command(char *command)
-{
- // might be start instead of open
- ShellExecuteA(NULL, "open", command, NULL, NULL, SW_SHOWDEFAULT);
-}
-
-void platform_window_make_current(platform_window *window)
-{
- wglMakeCurrent(window->hdc, window->gl_context);
-}
-
-void platform_init(int argc, char **argv)
-{
- setlocale(LC_ALL, "en_US.UTF-8");
-
- QueryPerformanceFrequency(&perf_frequency);
- CoInitialize(NULL);
- create_key_tables();
-
- instance = GetModuleHandle(NULL);
- cmd_show = argc;
-
- // get fullpath of the directory the exe is residing in
- binary_path = platform_get_full_path(argv[0]);
-
- platform_create_config_directory();
-
- char buf[MAX_INPUT_LENGTH];
- get_directory_from_path(buf, binary_path);
- string_copyn(binary_path, buf, MAX_INPUT_LENGTH);
-
- assets_create();
-}
-
-void platform_set_icon(platform_window *window, image *img)
-{
- BYTE *bmp;
- s32 data_len = img->width * img->height * 4;
- s32 total_len = data_len + 40 * 4;
-
- bmp = mem_alloc(total_len);
-
- struct {
- int32_t header_size, width, geight;
- int16_t color_plane, bits_per_pixel;
- int32_t compression_mode, img_length, obsolete[4];
- } bmp_header = {40, img->width, img->height * 2, 1, 32, BI_RGB, data_len, {0,0,0,0} };
-
- memcpy(bmp, &bmp_header, 40);
-
- s32 index = 0;
- for (s32 y = img->height-1; y >= 0; y--)
- {
- for (s32 x = 0; x < img->width; x++)
- {
- s32 img_pixel = *(((s32*)img->data+(x+(y*img->width))));
-
- // 0xAABBGGRR
- s32 a = (img_pixel>>24) & 0x000000FF;
- s32 b = (img_pixel>>16) & 0x000000FF;
- s32 g = (img_pixel>> 8) & 0x000000FF;
- s32 r = (img_pixel>> 0) & 0x000000FF;
-
- //s32 c = (r << 24) | (g << 16) | (b << 8) | (a << 0);
- s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0);
- memcpy(bmp+40+(index*4), &c, 4);
-
- ++index;
- }
- }
-
- HICON icon = CreateIconFromResourceEx(bmp, total_len, TRUE, 0x00030000, img->width, img->height, LR_DEFAULTCOLOR);
-
- SendMessage(window->window_handle, WM_SETICON, ICON_SMALL, (LPARAM)icon);
- SendMessage(window->window_handle, WM_SETICON, ICON_BIG, (LPARAM)icon);
-
- mem_free(bmp);
-
- if (!icon)
- printf("Failed to load icon, error code: %ld.\n", GetLastError());
-}
-
-u64 platform_get_time(time_type time_type, time_precision precision)
-{
- LARGE_INTEGER counter;
- QueryPerformanceCounter(&counter);
-
- double sec = counter.QuadPart / (double)(perf_frequency.QuadPart);
-
- //printf("%I64d %I64d %f\n", counter.QuadPart, perf_frequency.QuadPart, sec);
-
- double val = sec;
-
- if (precision == TIME_NS)
- {
- return val*1000000000;
- }
- if (precision == TIME_US)
- {
- return val*1000000;
- }
- if (precision == TIME_MILI_S)
- {
- return val*1000;
- }
- if (precision == TIME_S)
- {
- return val;
- }
- return val;
-}
-
-s32 platform_get_memory_size()
-{
- u64 result;
- GetPhysicallyInstalledSystemMemory(&result);
- return result;
-}
-
-s32 platform_get_cpu_count()
-{
- SYSTEM_INFO info;
- GetSystemInfo(&info);
-
- return info.dwNumberOfProcessors;
-}
-
-u64 string_to_u64(char *str)
-{
- return (u64)strtoull(str, 0, 10);
-}
-
-u32 string_to_u32(char *str)
-{
- return (u32)strtoul(str, 0, 10);
-}
-
-u16 string_to_u16(char *str)
-{
- return (u16)strtoul(str, 0, 10);
-}
-
-u8 string_to_u8(char *str)
-{
- return (u8)strtoul(str, 0, 10);
-}
-
-s64 string_to_s64(char *str)
-{
- return (s64)strtoull(str, 0, 10);
-}
-
-s32 string_to_s32(char *str)
-{
- return (s32)strtoul(str, 0, 10);
-}
-
-s16 string_to_s16(char *str)
-{
- return (s16)strtoul(str, 0, 10);
-}
-
-s8 string_to_s8(char *str)
-{
- return (s8)strtoul(str, 0, 10);
-} \ No newline at end of file
diff --git a/src/windows/thread.c b/src/windows/thread.c
deleted file mode 100644
index 9669158..0000000
--- a/src/windows/thread.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-* BSD 2-Clause “Simplified” License
-* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
-* All rights reserved.
-*/
-
-#include <synchapi.h>
-
-thread thread_start(void *(*start_routine) (void *), void *arg)
-{
- thread result;
- result.valid = false;
-
- result.thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine,
- arg, 0, NULL);
- result.valid = true;
-
- return result;
-}
-
-void thread_join(thread *thread)
-{
- if (thread->valid)
- {
- WaitForSingleObject(thread->thread, INFINITE);
- CloseHandle(thread->thread);
- }
-}
-
-bool thread_tryjoin(thread *thread)
-{
- if (thread->valid)
- {
- s32 result = WaitForSingleObject(thread->thread, 0);
- return result == WAIT_OBJECT_0;
- }
- return false;
-}
-
-void thread_detach(thread *thread)
-{
- if (thread->valid)
- {
- CloseHandle(thread->thread);
- }
-}
-
-void thread_stop(thread *thread)
-{
- if (thread->valid)
- {
- SuspendThread(thread->thread);
- }
-}
-
-u32 thread_get_id()
-{
- return GetCurrentThreadId();
-}
-
-void thread_sleep(u64 microseconds)
-{
- Sleep(microseconds/1000);
-}
-
-mutex mutex_create()
-{
- mutex result;
- result.mutex = CreateMutex(
- NULL, // default security attributes
- FALSE, // initially not owned
- NULL); // unnamed mutex
-
- return result;
-}
-
-mutex mutex_create_recursive()
-{
- return mutex_create();
-}
-
-void mutex_lock(mutex *mutex)
-{
- WaitForSingleObject(
- mutex->mutex, // handle to mutex
- INFINITE); // no time-out interval
-}
-
-bool mutex_trylock(mutex *mutex)
-{
- return WaitForSingleObject(mutex->mutex, 1) == WAIT_OBJECT_0;
-}
-
-void mutex_unlock(mutex *mutex)
-{
- ReleaseMutex(mutex->mutex);
-}
-
-void mutex_destroy(mutex *mutex)
-{
- CloseHandle(mutex->mutex);
-}