diff options
| author | Aldrik Ramaekers <aldrik.ramaekers@protonmail.com> | 2020-02-05 18:58:55 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrik.ramaekers@protonmail.com> | 2020-02-05 18:58:55 +0100 |
| commit | 8c2f35bd1f18b62fff609f3a7d77d4e85b706916 (patch) | |
| tree | 93e504989bd6182b1b381f1c9bd1b46e0732169a | |
| parent | ec901c42d7d5dee13b4c69e4b65fc385d8ffd3a8 (diff) | |
refactor
45 files changed, 89 insertions, 26870 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9920522 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/project-base"] + path = src/project-base + url = https://github.com/aldrikboy/project-base 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, ¤t_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); -} |
