Initial commit.
This commit is contained in:
commit
b7eafbf244
13 changed files with 23276 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.cache
|
||||
306
cJSON.h
Normal file
306
cJSON.h
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
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 19
|
||||
|
||||
#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
|
||||
|
||||
/* Limits the length of circular references can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_CIRCULAR_LIMIT
|
||||
#define CJSON_CIRCULAR_LIMIT 10000
|
||||
#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);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* 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);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, 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 item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(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(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) 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(cJSON_bool) 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(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) 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(cJSON_bool) 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(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) 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 address 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))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
|
||||
/* 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
|
||||
7988
deps/stb_image.h
vendored
Normal file
7988
deps/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
10651
deps/stb_image_resize2.h
vendored
Normal file
10651
deps/stb_image_resize2.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
113
fetch.c
Normal file
113
fetch.c
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#include <curl/curl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
static char curl_errbuf[CURL_ERROR_SIZE] = {'\0'};
|
||||
|
||||
struct fetch *fetch_init(void) {
|
||||
CURLcode res;
|
||||
CURL *handle;
|
||||
struct fetch *fh = NULL;
|
||||
|
||||
/*
|
||||
* TODO: Not sure if calling this possibly more than once can cause
|
||||
* an issue or not.
|
||||
*/
|
||||
if ((res = curl_global_init(CURL_GLOBAL_DEFAULT)) != CURLE_OK) {
|
||||
fprintf(stderr, "Could not initialize curl: %s\n", curl_easy_strerror(res));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
handle = curl_easy_init();
|
||||
|
||||
if ((res = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_errbuf)) !=
|
||||
CURLE_OK) {
|
||||
fprintf(stderr, "Error setting up error buffer: %s\n",
|
||||
curl_easy_strerror(res));
|
||||
curl_easy_cleanup(handle);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((fh = malloc(sizeof *fh)) == NULL) {
|
||||
fputs("Out of memory.\n", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fh->handle = handle;
|
||||
|
||||
return fh;
|
||||
}
|
||||
|
||||
void fetch_cleanup(struct fetch *restrict f) {
|
||||
/* Don't even dignify this with a response */
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
curl_easy_cleanup(f->handle);
|
||||
free(f);
|
||||
}
|
||||
|
||||
size_t fetch_cb(void *ptr, size_t size, size_t nmemb, void *data) {
|
||||
struct response *resp = (struct response *)data;
|
||||
size_t real_size = size * nmemb;
|
||||
|
||||
if ((resp->data = realloc(resp->data, resp->size + real_size + 1)) == NULL) {
|
||||
fputs("Out of memory.\n", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Wrote %zd bytes\n", real_size);
|
||||
|
||||
memcpy(resp->data + resp->size, ptr, real_size);
|
||||
resp->size += real_size;
|
||||
resp->data[resp->size] = '\0';
|
||||
|
||||
return real_size;
|
||||
}
|
||||
|
||||
struct response *fetch(struct fetch *restrict f, CURLU *restrict url) {
|
||||
struct response *resp = NULL;
|
||||
|
||||
if (curl_easy_setopt(f->handle, CURLOPT_WRITEFUNCTION, fetch_cb) != CURLE_OK)
|
||||
goto call_fetch_fail;
|
||||
|
||||
if (curl_easy_setopt(f->handle, CURLOPT_CURLU, url) != CURLE_OK)
|
||||
goto call_fetch_fail;
|
||||
|
||||
if ((resp = malloc(sizeof *resp)) == NULL) {
|
||||
fputs("Out of memory.", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(resp, 0, sizeof *resp);
|
||||
|
||||
if (curl_easy_setopt(f->handle, CURLOPT_WRITEDATA, (void *)resp) != CURLE_OK)
|
||||
goto call_fetch_fail;
|
||||
|
||||
if (curl_easy_perform(f->handle) != CURLE_OK)
|
||||
goto call_fetch_fail;
|
||||
|
||||
if (curl_easy_getinfo(f->handle, CURLINFO_CONTENT_TYPE,
|
||||
&resp->content_type) != CURLE_OK)
|
||||
goto call_fetch_fail;
|
||||
|
||||
printf("CONTENT TYPE: %s\n", resp->content_type);
|
||||
|
||||
return resp;
|
||||
|
||||
call_fetch_fail:
|
||||
if (resp != NULL)
|
||||
free(resp);
|
||||
fputs(curl_errbuf, stderr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void response_cleanup(struct response *resp) {
|
||||
if (resp != NULL) {
|
||||
free(resp->data);
|
||||
free(resp);
|
||||
}
|
||||
}
|
||||
27
fetch.h
Normal file
27
fetch.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* NOT THREAD SAFE */
|
||||
|
||||
#ifndef _FETCH_H
|
||||
#define _FETCH_H
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct fetch {
|
||||
CURL *handle;
|
||||
};
|
||||
|
||||
struct response {
|
||||
char *data;
|
||||
size_t size;
|
||||
char *content_type; /* Don't free. Managed by libcurl. */
|
||||
};
|
||||
|
||||
typedef size_t (*CurlWriteCallback)(void *ptr, size_t size, size_t nmemb,
|
||||
void *userdata);
|
||||
|
||||
struct fetch *fetch_init(void);
|
||||
struct response *fetch(struct fetch *restrict f, CURLU *restrict url);
|
||||
void fetch_cleanup(struct fetch *f);
|
||||
void response_cleanup(struct response *resp);
|
||||
|
||||
#endif
|
||||
231
image.cpp
Normal file
231
image.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "tiv_lib.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include "stb_image_resize2.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Program exit code constants compatible with sysexits.h.
|
||||
#define EXITCODE_OK 0
|
||||
#define EXITCODE_COMMAND_LINE_USAGE_ERROR 64
|
||||
#define EXITCODE_DATA_FORMAT_ERROR 65
|
||||
#define EXITCODE_NO_INPUT_ERROR 66
|
||||
|
||||
void printTermColor(const int &flags, int r, int g, int b) {
|
||||
r = clamp_byte(r);
|
||||
g = clamp_byte(g);
|
||||
b = clamp_byte(b);
|
||||
|
||||
bool bg = (flags & FLAG_BG) != 0;
|
||||
|
||||
if ((flags & FLAG_MODE_256) == 0) {
|
||||
std::cout << (bg ? "\x1b[48;2;" : "\x1b[38;2;") << r << ';' << g << ';' << b
|
||||
<< 'm';
|
||||
return;
|
||||
}
|
||||
|
||||
int ri = best_index(r, COLOR_STEPS, COLOR_STEP_COUNT);
|
||||
int gi = best_index(g, COLOR_STEPS, COLOR_STEP_COUNT);
|
||||
int bi = best_index(b, COLOR_STEPS, COLOR_STEP_COUNT);
|
||||
|
||||
int rq = COLOR_STEPS[ri];
|
||||
int gq = COLOR_STEPS[gi];
|
||||
int bq = COLOR_STEPS[bi];
|
||||
|
||||
int gray =
|
||||
static_cast<int>(std::round(r * 0.2989f + g * 0.5870f + b * 0.1140f));
|
||||
|
||||
int gri = best_index(gray, GRAYSCALE_STEPS, GRAYSCALE_STEP_COUNT);
|
||||
int grq = GRAYSCALE_STEPS[gri];
|
||||
|
||||
int color_index;
|
||||
if (0.3 * sqr(rq - r) + 0.59 * sqr(gq - g) + 0.11 * sqr(bq - b) <
|
||||
0.3 * sqr(grq - r) + 0.59 * sqr(grq - g) + 0.11 * sqr(grq - b)) {
|
||||
color_index = 16 + 36 * ri + 6 * gi + bi;
|
||||
} else {
|
||||
color_index = 232 + gri; // 1..24 -> 232..255
|
||||
}
|
||||
std::cout << (bg ? "\x1B[48;5;" : "\u001B[38;5;") << color_index << "m";
|
||||
}
|
||||
|
||||
void printCodepoint(int codepoint) {
|
||||
if (codepoint < 128) {
|
||||
std::cout << static_cast<char>(codepoint);
|
||||
} else if (codepoint < 0x7ff) {
|
||||
std::cout << static_cast<char>(0xc0 | (codepoint >> 6));
|
||||
std::cout << static_cast<char>(0x80 | (codepoint & 0x3f));
|
||||
} else if (codepoint < 0xffff) {
|
||||
std::cout << static_cast<char>(0xe0 | (codepoint >> 12));
|
||||
std::cout << static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
|
||||
std::cout << static_cast<char>(0x80 | (codepoint & 0x3f));
|
||||
} else if (codepoint < 0x10ffff) {
|
||||
std::cout << static_cast<char>(0xf0 | (codepoint >> 18));
|
||||
std::cout << static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
|
||||
std::cout << static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
|
||||
std::cout << static_cast<char>(0x80 | (codepoint & 0x3f));
|
||||
} else {
|
||||
std::cerr << "ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
void printImage(const unsigned char *data, int width, int height, int channels,
|
||||
const int &flags) {
|
||||
GetPixelFunction get_pixel = [&](int x, int y) -> unsigned long {
|
||||
int index = (y * width + x) * channels;
|
||||
return (((unsigned long)data[index]) << 16) |
|
||||
(((unsigned long)data[index + 1]) << 8) |
|
||||
(((unsigned long)data[index + 2]));
|
||||
};
|
||||
|
||||
CharData lastCharData;
|
||||
for (int y = 0; y <= height - 8; y += 8) {
|
||||
for (int x = 0; x <= width - 4; x += 4) {
|
||||
CharData charData =
|
||||
flags & FLAG_NOOPT
|
||||
? createCharData(get_pixel, x, y, 0x2584, 0x0000ffff)
|
||||
: findCharData(get_pixel, x, y, flags);
|
||||
if (x == 0 || charData.bgColor != lastCharData.bgColor)
|
||||
printTermColor(flags | FLAG_BG, charData.bgColor[0],
|
||||
charData.bgColor[1], charData.bgColor[2]);
|
||||
if (x == 0 || charData.fgColor != lastCharData.fgColor)
|
||||
printTermColor(flags | FLAG_FG, charData.fgColor[0],
|
||||
charData.fgColor[1], charData.fgColor[2]);
|
||||
printCodepoint(charData.codePoint);
|
||||
lastCharData = charData;
|
||||
}
|
||||
std::cout << "\x1b[0m" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
struct size {
|
||||
size(unsigned int in_width, unsigned int in_height)
|
||||
: width(in_width), height(in_height) {}
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
size scaled(double scale) { return size(width * scale, height * scale); }
|
||||
size fitted_within(size container) {
|
||||
double scale = std::min(container.width / static_cast<double>(width),
|
||||
container.height / static_cast<double>(height));
|
||||
return scaled(scale);
|
||||
}
|
||||
};
|
||||
std::ostream &operator<<(std::ostream &stream, size sz) {
|
||||
stream << sz.width << "x" << sz.height;
|
||||
return stream;
|
||||
}
|
||||
|
||||
static void render_and_print_image(unsigned char *data, int width, int height,
|
||||
int channels) {
|
||||
int max_width = 80;
|
||||
int max_height = 24;
|
||||
struct winsize w;
|
||||
char err_buf[LINE_MAX];
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1) {
|
||||
strerror_r(errno, err_buf, sizeof err_buf);
|
||||
std::cerr << "Couldn't get terminal dimensions: " << err_buf << std::endl;
|
||||
} else {
|
||||
max_width = w.ws_col * 4;
|
||||
max_height = w.ws_row * 8;
|
||||
}
|
||||
|
||||
unsigned char *output_data = data;
|
||||
int output_width = width;
|
||||
int output_height = height;
|
||||
|
||||
std::vector<unsigned char> resized_data;
|
||||
|
||||
if (width > max_width || height > max_height) {
|
||||
size new_size =
|
||||
size(width, height).fitted_within(size(max_width, max_height));
|
||||
output_width = new_size.width;
|
||||
output_height = new_size.height;
|
||||
|
||||
resized_data.resize(output_width * output_height * channels);
|
||||
stbir_resize_uint8_linear(data, width, height, 0, resized_data.data(),
|
||||
output_width, output_height, 0,
|
||||
(stbir_pixel_layout)channels);
|
||||
output_data = resized_data.data();
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
printImage(output_data, output_width, output_height, channels, flags);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void process_image(const char *path) {
|
||||
int width, height, channels;
|
||||
unsigned char *data = stbi_load(path, &width, &height, &channels, 3);
|
||||
if (!data) {
|
||||
std::cerr << "Error: '" << path << "' could not be loaded" << std::endl;
|
||||
return;
|
||||
}
|
||||
// Force 3 channels for now
|
||||
channels = 3;
|
||||
|
||||
render_and_print_image(data, width, height, channels);
|
||||
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
void process_image_mem(const unsigned char *buffer, size_t len) {
|
||||
int width, height, channels;
|
||||
unsigned char *data =
|
||||
stbi_load_from_memory(buffer, (int)len, &width, &height, &channels, 3);
|
||||
if (!data) {
|
||||
std::cerr << "Error: Could not load image from memory" << std::endl;
|
||||
return;
|
||||
}
|
||||
// Force 3 channels for now
|
||||
channels = 3;
|
||||
|
||||
render_and_print_image(data, width, height, channels);
|
||||
|
||||
stbi_image_free(data);
|
||||
}
|
||||
}
|
||||
17
image.h
Normal file
17
image.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef _IMAGE_H
|
||||
#define _IMAGE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void process_image(const char *path);
|
||||
void process_image_mem(const unsigned char *buffer, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _IMAGE_H */
|
||||
37
meson.build
Normal file
37
meson.build
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
project(
|
||||
'omdb',
|
||||
'c', 'cpp',
|
||||
meson_version : '>= 1.3.0',
|
||||
version : '0.1',
|
||||
default_options : ['warning_level=3', 'cpp_std=c++17', 'c_std=c11'],
|
||||
)
|
||||
|
||||
curl = dependency('libcurl')
|
||||
|
||||
cpp_flags = []
|
||||
c_flags = ['-DPOSIXLY_CORRECT', '-Wno-c99-extensions']
|
||||
|
||||
dependencies = [
|
||||
curl
|
||||
]
|
||||
|
||||
sources = [
|
||||
'cJSON.c',
|
||||
'fetch.c',
|
||||
'image.cpp',
|
||||
'omdb.c',
|
||||
'tiv_lib.cpp'
|
||||
]
|
||||
|
||||
add_project_arguments(cpp_flags, language : 'cpp')
|
||||
add_project_arguments(c_flags, language : 'c')
|
||||
|
||||
exe = executable(
|
||||
'omdb',
|
||||
[sources],
|
||||
dependencies : dependencies,
|
||||
include_directories : include_directories('deps'),
|
||||
install : true,
|
||||
)
|
||||
|
||||
test('basic', exe)
|
||||
219
omdb.c
Normal file
219
omdb.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "fetch.h"
|
||||
#include "image.h"
|
||||
|
||||
#define UNUSED(x) (void)x
|
||||
|
||||
#define OMDB_URL "http://www.omdbapi.com"
|
||||
#define PROJECT_NAME "omdb"
|
||||
|
||||
/* Default to plain output. */
|
||||
static enum _output_format {
|
||||
OUTPUT_JSON,
|
||||
OUTPUT_PLAIN,
|
||||
OUTPUT_FANCY
|
||||
} output_format = OUTPUT_PLAIN;
|
||||
|
||||
static struct option opts[] = {{"id", no_argument, NULL, 'i'},
|
||||
{"json", no_argument, NULL, 'j'},
|
||||
{"api-key", required_argument, NULL, 'k'},
|
||||
{"search-type", required_argument, NULL, 't'},
|
||||
{NULL, 0, NULL, 0}};
|
||||
|
||||
void print_usage(void) {
|
||||
printf(
|
||||
PROJECT_NAME
|
||||
" [ikt] <TITLE-OR-ID>\n"
|
||||
"\t-i/--id: Search by ID rather than title. (optional)\n"
|
||||
"\t-j/--json: Output full JSON response.\n"
|
||||
"\t-k/--api-key: OMDB API Key. (required)\n"
|
||||
"\t-t/--search-type: Search type. (optional) [movie, series, episode]\n");
|
||||
}
|
||||
|
||||
/* Set a URL query parameter. */
|
||||
void set_param(CURLU *restrict url, const char *restrict key,
|
||||
char *restrict value) {
|
||||
CURLUcode err;
|
||||
char param[2048];
|
||||
|
||||
snprintf(param, sizeof param, "%s=%s", key, value);
|
||||
|
||||
if ((err = curl_url_set(url, CURLUPART_QUERY, param,
|
||||
CURLU_APPENDQUERY | CURLU_URLENCODE)) != CURLUE_OK) {
|
||||
fprintf(stderr, "Error setting url param '%s': %s\n", key,
|
||||
curl_url_strerror(err));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Just print the url if needed for debugging. */
|
||||
void print_url(CURLU *restrict url) {
|
||||
char *url_string = NULL;
|
||||
|
||||
curl_url_get(url, CURLUPART_URL, &url_string, 0);
|
||||
printf("%s\n", url_string);
|
||||
curl_free(url_string);
|
||||
}
|
||||
|
||||
/* Print entire JSON response as is. */
|
||||
int print_title_json(const cJSON *const json) {
|
||||
char *json_str = cJSON_Print(json);
|
||||
|
||||
if (!json_str) {
|
||||
fputs("Couldn't output formatted JSON.\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fputs(json_str, stdout);
|
||||
putchar('\n');
|
||||
free(json_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print some title information in plain text. */
|
||||
int print_title_plain(const cJSON *json) {
|
||||
cJSON *title = cJSON_GetObjectItem(json, "title");
|
||||
cJSON *imdb_rating = cJSON_GetObjectItem(json, "imdbRating");
|
||||
|
||||
if (title == NULL || imdb_rating == NULL) {
|
||||
printf("Could not find title or rating in response.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Found: '%s' with rating of %s\n", title->valuestring,
|
||||
imdb_rating->valuestring);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_response(struct response *restrict resp) {
|
||||
cJSON *json = NULL;
|
||||
|
||||
if (strstr(resp->content_type, "application/json") == NULL) {
|
||||
fprintf(stderr, "Unexpected response: %s\n", resp->content_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json = cJSON_ParseWithLength(resp->data, resp->size);
|
||||
|
||||
if (json == NULL) {
|
||||
const char *error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != NULL) {
|
||||
fprintf(stderr, "Error before: %s\n", error_ptr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (output_format) {
|
||||
case OUTPUT_PLAIN:
|
||||
print_title_plain(json);
|
||||
break;
|
||||
case OUTPUT_JSON:
|
||||
print_title_json(json);
|
||||
break;
|
||||
case OUTPUT_FANCY:
|
||||
/* Just print_title + image planned here for now */
|
||||
print_title_plain(json);
|
||||
break;
|
||||
}
|
||||
|
||||
cJSON_free(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ch;
|
||||
bool k_flag = false;
|
||||
char search_mode[8] = "movie";
|
||||
|
||||
if (argc == 0) {
|
||||
fputs("Argument required.\n", stderr);
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Just validate and add options directly to the request now. */
|
||||
CURLU *url = curl_url();
|
||||
CURLUcode ures;
|
||||
|
||||
if (url == NULL) {
|
||||
fputs("Out of memory.\n", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((ures = curl_url_set(url, CURLUPART_URL, OMDB_URL, 0)) != CURLUE_OK) {
|
||||
fprintf(stderr, "Couldn't set url: %s\n", curl_url_strerror(ures));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "hijk:t:", opts, NULL)) != -1) {
|
||||
switch (ch) {
|
||||
case 'i':
|
||||
/* Lookup by ID */
|
||||
set_param(url, "id", optarg);
|
||||
break;
|
||||
case 'j':
|
||||
output_format = OUTPUT_JSON;
|
||||
break;
|
||||
case 'k':
|
||||
/* API Key */
|
||||
k_flag = true;
|
||||
set_param(url, "apikey", optarg);
|
||||
break;
|
||||
case 't':
|
||||
/* Type: movie, series, episode */
|
||||
strlcpy(search_mode, optarg, sizeof search_mode);
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "Invalid option'%c'.\n", optopt);
|
||||
exit(EXIT_FAILURE);
|
||||
case 'h':
|
||||
default:
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
|
||||
if (!k_flag) {
|
||||
fputs("Missing required arguments.\n", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Looking for: %s\n", argv[optind]);
|
||||
|
||||
/* Set remaining params if any... */
|
||||
set_param(url, "type", search_mode);
|
||||
set_param(url, "r", "json");
|
||||
/* Just testing... */
|
||||
set_param(url, "t", argv[optind]);
|
||||
|
||||
/* Print it... */
|
||||
print_url(url);
|
||||
|
||||
/*
|
||||
* All of the call setup should be done via the URL so all that's left is to
|
||||
* send the request and conditionally handle any response.
|
||||
*/
|
||||
struct fetch *f = fetch_init(); /* Check this */
|
||||
struct response *resp = fetch(f, url);
|
||||
handle_response(resp);
|
||||
process_image("./test.jpg");
|
||||
free(resp->data);
|
||||
free(resp);
|
||||
curl_url_cleanup(url);
|
||||
fetch_cleanup(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
394
tiv_lib.cpp
Normal file
394
tiv_lib.cpp
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tiv_lib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
const int END_MARKER = 0;
|
||||
|
||||
// An interleaved map of 4x8 bit character bitmaps (each hex digit represents a
|
||||
// row) to the corresponding Unicode character code point.
|
||||
constexpr unsigned int BITMAPS[] = {
|
||||
0x00000000, 0x00a0, 0,
|
||||
|
||||
// Block graphics
|
||||
// 0xffff0000, 0x2580, 0, // upper 1/2; redundant with inverse lower 1/2
|
||||
|
||||
0x0000000f, 0x2581, 0, // lower 1/8
|
||||
0x000000ff, 0x2582, 0, // lower 1/4
|
||||
0x00000fff, 0x2583, 0,
|
||||
0x0000ffff, 0x2584, 0, // lower 1/2
|
||||
0x000fffff, 0x2585, 0,
|
||||
0x00ffffff, 0x2586, 0, // lower 3/4
|
||||
0x0fffffff, 0x2587, 0,
|
||||
// 0xffffffff, 0x2588, // full; redundant with inverse space
|
||||
|
||||
0xeeeeeeee, 0x258a, 0, // left 3/4
|
||||
0xcccccccc, 0x258c, 0, // left 1/2
|
||||
0x88888888, 0x258e, 0, // left 1/4
|
||||
|
||||
0x0000cccc, 0x2596, 0, // quadrant lower left
|
||||
0x00003333, 0x2597, 0, // quadrant lower right
|
||||
0xcccc0000, 0x2598, 0, // quadrant upper left
|
||||
// 0xccccffff, 0x2599, // 3/4 redundant with inverse 1/4
|
||||
0xcccc3333, 0x259a, 0, // diagonal 1/2
|
||||
// 0xffffcccc, 0x259b, // 3/4 redundant
|
||||
// 0xffff3333, 0x259c, // 3/4 redundant
|
||||
0x33330000, 0x259d, 0, // quadrant upper right
|
||||
// 0x3333cccc, 0x259e, // 3/4 redundant
|
||||
// 0x3333ffff, 0x259f, // 3/4 redundant
|
||||
|
||||
// Line drawing subset: no double lines, no complex light lines
|
||||
|
||||
0x000ff000, 0x2501, 0, // Heavy horizontal
|
||||
0x66666666, 0x2503, 0, // Heavy vertical
|
||||
|
||||
0x00077666, 0x250f, 0, // Heavy down and right
|
||||
0x000ee666, 0x2513, 0, // Heavy down and left
|
||||
0x66677000, 0x2517, 0, // Heavy up and right
|
||||
0x666ee000, 0x251b, 0, // Heavy up and left
|
||||
|
||||
0x66677666, 0x2523, 0, // Heavy vertical and right
|
||||
0x666ee666, 0x252b, 0, // Heavy vertical and left
|
||||
0x000ff666, 0x2533, 0, // Heavy down and horizontal
|
||||
0x666ff000, 0x253b, 0, // Heavy up and horizontal
|
||||
0x666ff666, 0x254b, 0, // Heavy cross
|
||||
|
||||
0x000cc000, 0x2578, 0, // Bold horizontal left
|
||||
0x00066000, 0x2579, 0, // Bold horizontal up
|
||||
0x00033000, 0x257a, 0, // Bold horizontal right
|
||||
0x00066000, 0x257b, 0, // Bold horizontal down
|
||||
|
||||
0x06600660, 0x254f, 0, // Heavy double dash vertical
|
||||
|
||||
0x000f0000, 0x2500, 0, // Light horizontal
|
||||
0x0000f000, 0x2500, 0, //
|
||||
0x44444444, 0x2502, 0, // Light vertical
|
||||
0x22222222, 0x2502, 0,
|
||||
|
||||
0x000e0000, 0x2574, 0, // light left
|
||||
0x0000e000, 0x2574, 0, // light left
|
||||
0x44440000, 0x2575, 0, // light up
|
||||
0x22220000, 0x2575, 0, // light up
|
||||
0x00030000, 0x2576, 0, // light right
|
||||
0x00003000, 0x2576, 0, // light right
|
||||
0x00004444, 0x2577, 0, // light down
|
||||
0x00002222, 0x2577, 0, // light down
|
||||
|
||||
// Misc technical
|
||||
|
||||
0x44444444, 0x23a2, 0, // [ extension
|
||||
0x22222222, 0x23a5, 0, // ] extension
|
||||
|
||||
0x0f000000, 0x23ba, 0, // Horizontal scanline 1
|
||||
0x00f00000, 0x23bb, 0, // Horizontal scanline 3
|
||||
0x00000f00, 0x23bc, 0, // Horizontal scanline 7
|
||||
0x000000f0, 0x23bd, 0, // Horizontal scanline 9
|
||||
|
||||
// Geometrical shapes. Tricky because some of them are too wide.
|
||||
|
||||
// 0x00ffff00, 0x25fe, 0, // Black medium small square
|
||||
0x00066000, 0x25aa, 0, // Black small square
|
||||
|
||||
// 0x11224488, 0x2571, 0, // diagonals
|
||||
// 0x88442211, 0x2572, 0,
|
||||
// 0x99666699, 0x2573, 0,
|
||||
// 0x000137f0, 0x25e2, 0, // Triangles
|
||||
// 0x0008cef0, 0x25e3, 0,
|
||||
// 0x000fec80, 0x25e4, 0,
|
||||
// 0x000f7310, 0x25e5, 0,
|
||||
|
||||
// Teletext / legacy graphics 3x2 block character codes.
|
||||
// Using a 3-2-3 pattern consistently, perhaps we should create automatic
|
||||
// variations....
|
||||
|
||||
0xccc00000, 0xfb00, FLAG_TELETEXT,
|
||||
0x33300000, 0xfb01, FLAG_TELETEXT,
|
||||
0xfff00000, 0xfb02, FLAG_TELETEXT,
|
||||
0x000cc000, 0xfb03, FLAG_TELETEXT,
|
||||
0xccccc000, 0xfb04, FLAG_TELETEXT,
|
||||
0x333cc000, 0xfb05, FLAG_TELETEXT,
|
||||
0xfffcc000, 0xfb06, FLAG_TELETEXT,
|
||||
0x00033000, 0xfb07, FLAG_TELETEXT,
|
||||
0xccc33000, 0xfb08, FLAG_TELETEXT,
|
||||
0x33333000, 0xfb09, FLAG_TELETEXT,
|
||||
0xfff33000, 0xfb0a, FLAG_TELETEXT,
|
||||
0x000ff000, 0xfb0b, FLAG_TELETEXT,
|
||||
0xcccff000, 0xfb0c, FLAG_TELETEXT,
|
||||
0x333ff000, 0xfb0d, FLAG_TELETEXT,
|
||||
0xfffff000, 0xfb0e, FLAG_TELETEXT,
|
||||
0x00000ccc, 0xfb0f, FLAG_TELETEXT,
|
||||
|
||||
0xccc00ccc, 0xfb10, FLAG_TELETEXT,
|
||||
0x33300ccc, 0xfb11, FLAG_TELETEXT,
|
||||
0xfff00ccc, 0xfb12, FLAG_TELETEXT,
|
||||
0x000ccccc, 0xfb13, FLAG_TELETEXT,
|
||||
0x333ccccc, 0xfb14, FLAG_TELETEXT,
|
||||
0xfffccccc, 0xfb15, FLAG_TELETEXT,
|
||||
0x00033ccc, 0xfb16, FLAG_TELETEXT,
|
||||
0xccc33ccc, 0xfb17, FLAG_TELETEXT,
|
||||
0x33333ccc, 0xfb18, FLAG_TELETEXT,
|
||||
0xfff33ccc, 0xfb19, FLAG_TELETEXT,
|
||||
0x000ffccc, 0xfb1a, FLAG_TELETEXT,
|
||||
0xcccffccc, 0xfb1b, FLAG_TELETEXT,
|
||||
0x333ffccc, 0xfb1c, FLAG_TELETEXT,
|
||||
0xfffffccc, 0xfb1d, FLAG_TELETEXT,
|
||||
0x00000333, 0xfb1e, FLAG_TELETEXT,
|
||||
0xccc00333, 0xfb1f, FLAG_TELETEXT,
|
||||
|
||||
0x33300333, 0x1b20, FLAG_TELETEXT,
|
||||
0xfff00333, 0x1b21, FLAG_TELETEXT,
|
||||
0x000cc333, 0x1b22, FLAG_TELETEXT,
|
||||
0xccccc333, 0x1b23, FLAG_TELETEXT,
|
||||
0x333cc333, 0x1b24, FLAG_TELETEXT,
|
||||
0xfffcc333, 0x1b25, FLAG_TELETEXT,
|
||||
0x00033333, 0x1b26, FLAG_TELETEXT,
|
||||
0xccc33333, 0x1b27, FLAG_TELETEXT,
|
||||
0xfff33333, 0x1b28, FLAG_TELETEXT,
|
||||
0x000ff333, 0x1b29, FLAG_TELETEXT,
|
||||
0xcccff333, 0x1b2a, FLAG_TELETEXT,
|
||||
0x333ff333, 0x1b2b, FLAG_TELETEXT,
|
||||
0xfffff333, 0x1b2c, FLAG_TELETEXT,
|
||||
0x00000fff, 0x1b2d, FLAG_TELETEXT,
|
||||
0xccc00fff, 0x1b2e, FLAG_TELETEXT,
|
||||
0x33300fff, 0x1b2f, FLAG_TELETEXT,
|
||||
|
||||
0xfff00fff, 0x1b30, FLAG_TELETEXT,
|
||||
0x000ccfff, 0x1b31, FLAG_TELETEXT,
|
||||
0xcccccfff, 0x1b32, FLAG_TELETEXT,
|
||||
0x333ccfff, 0x1b33, FLAG_TELETEXT,
|
||||
0xfffccfff, 0x1b34, FLAG_TELETEXT,
|
||||
0x00033fff, 0x1b35, FLAG_TELETEXT,
|
||||
0xccc33fff, 0x1b36, FLAG_TELETEXT,
|
||||
0x33333fff, 0x1b37, FLAG_TELETEXT,
|
||||
0xfff33fff, 0x1b38, FLAG_TELETEXT,
|
||||
0x000fffff, 0x1b39, FLAG_TELETEXT,
|
||||
0xcccfffff, 0x1b3a, FLAG_TELETEXT,
|
||||
0x333fffff, 0x1b3b, FLAG_TELETEXT,
|
||||
|
||||
0, END_MARKER, 0 // End marker
|
||||
};
|
||||
|
||||
// The channel indices are 0, 1, 2 for R, G, B
|
||||
unsigned char get_channel(unsigned long rgb, int index) {
|
||||
return (unsigned char) ((rgb >> ((2 - index) * 8)) & 255);
|
||||
}
|
||||
|
||||
CharData createCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
int codepoint, int pattern) {
|
||||
CharData result;
|
||||
result.codePoint = codepoint;
|
||||
int fg_count = 0;
|
||||
int bg_count = 0;
|
||||
unsigned int mask = 0x80000000;
|
||||
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
int *avg;
|
||||
if (pattern & mask) {
|
||||
avg = result.fgColor.data();
|
||||
fg_count++;
|
||||
} else {
|
||||
avg = result.bgColor.data();
|
||||
bg_count++;
|
||||
}
|
||||
long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
avg[i] += get_channel(rgb, i);
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the average color value for each bucket
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (bg_count != 0) {
|
||||
result.bgColor[i] /= bg_count;
|
||||
}
|
||||
if (fg_count != 0) {
|
||||
result.fgColor[i] /= fg_count;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CharData findCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
const int &flags) {
|
||||
int min[3] = {255, 255, 255};
|
||||
int max[3] = {0};
|
||||
std::map<long, int> count_per_color;
|
||||
|
||||
// Determine the minimum and maximum value for each color channel
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
long color = 0;
|
||||
long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int d = get_channel(rgb, i);
|
||||
min[i] = std::min(min[i], d);
|
||||
max[i] = std::max(max[i], d);
|
||||
color = (color << 8) | d;
|
||||
}
|
||||
count_per_color[color]++;
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<int, long> color_per_count;
|
||||
for (auto i = count_per_color.begin(); i != count_per_color.end(); ++i) {
|
||||
color_per_count.insert(std::pair<int, long>(i->second, i->first));
|
||||
}
|
||||
|
||||
auto iter = color_per_count.rbegin();
|
||||
int count2 = iter->first;
|
||||
long max_count_color_1 = iter->second;
|
||||
long max_count_color_2 = max_count_color_1;
|
||||
if ((++iter) != color_per_count.rend()) {
|
||||
count2 += iter->first;
|
||||
max_count_color_2 = iter->second;
|
||||
}
|
||||
|
||||
unsigned int bits = 0;
|
||||
bool direct = count2 > (8 * 4) / 2;
|
||||
|
||||
if (direct) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
int d1 = 0;
|
||||
int d2 = 0;
|
||||
unsigned long rgb = get_pixel(x0 + x, y0 + y);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
int c1 = (max_count_color_1 >> shift) & 255;
|
||||
int c2 = (max_count_color_2 >> shift) & 255;
|
||||
int c = get_channel(rgb, i);
|
||||
d1 += (c1 - c) * (c1 - c);
|
||||
d2 += (c2 - c) * (c2 - c);
|
||||
}
|
||||
if (d1 > d2) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determine the color channel with the greatest range.
|
||||
int splitIndex = 0;
|
||||
int bestSplit = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (max[i] - min[i] > bestSplit) {
|
||||
bestSplit = max[i] - min[i];
|
||||
splitIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// We just split at the middle of the interval instead of computing the
|
||||
// median.
|
||||
int splitValue = min[splitIndex] + bestSplit / 2;
|
||||
|
||||
// Compute a bitmap using the given split and sum the color values for
|
||||
// both buckets.
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
bits = bits << 1;
|
||||
if (get_channel(get_pixel(x0 + x, y0 + y),
|
||||
splitIndex) > splitValue) {
|
||||
bits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the best bitmap match by counting the bits that don't match,
|
||||
// including the inverted bitmaps.
|
||||
int best_diff = 8;
|
||||
unsigned int best_pattern = 0x0000ffff;
|
||||
int codepoint = 0x2584;
|
||||
bool inverted = false;
|
||||
for (int i = 0; BITMAPS[i + 1] != END_MARKER; i += 3) {
|
||||
if ((BITMAPS[i + 2] & flags) != BITMAPS[i + 2]) {
|
||||
continue;
|
||||
}
|
||||
unsigned int pattern = BITMAPS[i];
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int diff = (std::bitset<32>(pattern ^ bits)).count();
|
||||
if (diff < best_diff) {
|
||||
best_pattern = BITMAPS[i]; // pattern might be inverted.
|
||||
codepoint = BITMAPS[i + 1];
|
||||
best_diff = diff;
|
||||
inverted = best_pattern != pattern;
|
||||
}
|
||||
pattern = ~pattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (direct) {
|
||||
CharData result;
|
||||
if (inverted) {
|
||||
long tmp = max_count_color_1;
|
||||
max_count_color_1 = max_count_color_2;
|
||||
max_count_color_2 = tmp;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int shift = 16 - 8 * i;
|
||||
result.fgColor[i] = (max_count_color_2 >> shift) & 255;
|
||||
result.bgColor[i] = (max_count_color_1 >> shift) & 255;
|
||||
result.codePoint = codepoint;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return createCharData(get_pixel, x0, y0, codepoint, best_pattern);
|
||||
}
|
||||
|
||||
int clamp_byte(int value) {
|
||||
return value < 0 ? 0 : (value > 255 ? 255 : value);
|
||||
}
|
||||
|
||||
double sqr(double n) { return n * n; }
|
||||
|
||||
int best_index(int value, const int STEPS[], int count) {
|
||||
int best_diff = std::abs(STEPS[0] - value);
|
||||
int result = 0;
|
||||
for (int i = 1; i < count; i++) {
|
||||
int diff = std::abs(STEPS[i] - value);
|
||||
if (diff < best_diff) {
|
||||
result = i;
|
||||
best_diff = diff;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
101
tiv_lib.h
Normal file
101
tiv_lib.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2017-2023, Stefan Haustein, Aaron Liu
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Alternatively, you may copy, redistribute and/or modify this file under
|
||||
* the terms of the Apache License, version 2.0:
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TIV_LIB_H
|
||||
#define TIV_LIB_H
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
// Implementation of flag representation for flags in the main() method
|
||||
constexpr int FLAG_FG = 1;
|
||||
constexpr int FLAG_BG = 2;
|
||||
constexpr int FLAG_MODE_256 = 4; // Limit colors to 256-color mode
|
||||
constexpr int FLAG_24BIT = 8; // 24-bit color mode
|
||||
constexpr int FLAG_NOOPT = 16; // Only use the same half-block character
|
||||
constexpr int FLAG_TELETEXT = 32; // Use teletext characters
|
||||
|
||||
|
||||
// Color saturation value steps from 0 to 255
|
||||
constexpr int COLOR_STEP_COUNT = 6;
|
||||
constexpr int COLOR_STEPS[COLOR_STEP_COUNT] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||
|
||||
// Grayscale saturation value steps from 0 to 255
|
||||
constexpr int GRAYSCALE_STEP_COUNT = 24;
|
||||
constexpr int GRAYSCALE_STEPS[GRAYSCALE_STEP_COUNT] = {
|
||||
0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76,
|
||||
0x80, 0x8a, 0x94, 0x9e, 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee};
|
||||
|
||||
|
||||
typedef std::function<unsigned long(int, int)> GetPixelFunction;
|
||||
|
||||
unsigned char get_channel(unsigned long rgb, int index);
|
||||
|
||||
int clamp_byte(int value);
|
||||
|
||||
int best_index(int value, const int STEPS[], int count);
|
||||
|
||||
double sqr(double n);
|
||||
|
||||
/**
|
||||
* @brief Struct to represent a character to be drawn.
|
||||
* @param fgColor RGB
|
||||
* @param bgColor RGB
|
||||
* @param codePoint The code point of the character to be drawn.
|
||||
*/
|
||||
struct CharData {
|
||||
std::array<int, 3> fgColor = std::array<int, 3>{0, 0, 0};
|
||||
std::array<int, 3> bgColor = std::array<int, 3>{0, 0, 0};
|
||||
int codePoint;
|
||||
};
|
||||
|
||||
// Return a CharData struct with the given code point and corresponding averag
|
||||
// fg and bg colors.
|
||||
CharData createCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
int codepoint, int pattern);
|
||||
|
||||
/**
|
||||
* @brief Find the best character and colors
|
||||
* for a 4x8 part of the image at the given position
|
||||
*
|
||||
* @param image
|
||||
* @param x0
|
||||
* @param y0
|
||||
* @param flags
|
||||
* @return CharData
|
||||
*/
|
||||
CharData findCharData(GetPixelFunction get_pixel, int x0, int y0,
|
||||
const int &flags);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue