From df9783ba9fac4db8bdc20b0725a0e20e680c7b0d Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 15 Sep 2018 11:01:33 +1000 Subject: [PATCH 01/32] COMMON: bump version --- configure.ac | 2 +- debian/changelog | 5 +++++ src/platform/android/app/build.gradle | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 9929192e..0c71066c 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [0.12.13]) +AC_INIT([smallbasic], [0.12.14]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET diff --git a/debian/changelog b/debian/changelog index 627431ba..a2711668 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +smallbasic (0.12.14) unstable; urgency=low + * Various see web site + + -- Chris Warren-Smith Sat, 15 Sept 2018 09:45:25 +1000 + smallbasic (0.12.13) unstable; urgency=low * Various see web site diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 7b29fac5..290216cb 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 15 targetSdkVersion 27 - versionCode 28 - versionName "0.12.13.1" + versionCode 29 + versionName "0.12.14" resConfigs "en" } From 6a50542c6456bb0b82fca68c1f7052b45f8c553a Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 15 Sep 2018 11:42:24 +1000 Subject: [PATCH 02/32] COMMON: fix crash when attempting to load an image from a failed network connection --- ChangeLog | 3 +++ src/languages/messages.en.h | 2 ++ src/ui/image.cpp | 27 +++++++++++++++++---------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index cca2dd9a..0ecab844 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2018-08-08 (0.12.14) + COMMON: fix crash when attempting to load an image from a failed network connection + 2018-08-08 (0.12.13) SDL: fix incorrect file loading with ALT+1-9 command diff --git a/src/languages/messages.en.h b/src/languages/messages.en.h index 93643f5b..ff020ddc 100644 --- a/src/languages/messages.en.h +++ b/src/languages/messages.en.h @@ -220,3 +220,5 @@ #define ERR_PACK_TOO_MANY "Too many values to unpack" #define ERR_PACK_TOO_FEW "Need more than %d values to unpack" #define ERR_MEMORY "Out of memory error" +#define ERR_NETWORK "Network error" +#define ERR_XPM_IMAGE "Invalid xpm image" diff --git a/src/ui/image.cpp b/src/ui/image.cpp index 9b295d88..559eff33 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -247,17 +247,22 @@ ImageBuffer *load_image(dev_file_t *filep) { unsigned w, h; unsigned char *image; unsigned error = 0; + unsigned network_error = 0; var_t *var_p; switch (filep->type) { case ft_http_client: // open "http://localhost/image1.gif" as #1 - var_p = v_new(); - http_read(filep, var_p); - error = lodepng_decode32(&image, &w, &h, (unsigned char *)var_p->v.p.ptr, - var_p->v.p.length); - v_free(var_p); - v_detach(var_p); + if (filep->handle == -1) { + network_error = 1; + } else { + var_p = v_new(); + http_read(filep, var_p); + error = lodepng_decode32(&image, &w, &h, (unsigned char *)var_p->v.p.ptr, + var_p->v.p.length); + v_free(var_p); + v_detach(var_p); + } break; case ft_stream: error = lodepng_decode32_file(&image, &w, &h, filep->name); @@ -266,7 +271,11 @@ ImageBuffer *load_image(dev_file_t *filep) { error = 1; break; } - if (!error) { + if (network_error) { + err_throw(ERR_IMAGE_LOAD, ERR_NETWORK); + } else if (error) { + err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error)); + } else { result = new ImageBuffer(); result->_bid = ++nextId; result->_width = w; @@ -274,8 +283,6 @@ ImageBuffer *load_image(dev_file_t *filep) { result->_filename = strdup(filep->name); result->_image = image; cache.add(result); - } else { - err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error)); } } return result; @@ -295,7 +302,7 @@ ImageBuffer *load_xpm_image(char **data) { result->_image = image; cache.add(result); } else { - err_throw(ERR_IMAGE_LOAD, "Invalid xpm image"); + err_throw(ERR_IMAGE_LOAD, ERR_XPM_IMAGE); } return result; } From c1bba736a59b649e6e486ae24eb31671b5e816ce Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Thu, 27 Sep 2018 21:50:29 +1000 Subject: [PATCH 03/32] ANDROID: update for latest tool refresh --- src/common/keymap.h | 12 ++++++------ src/platform/android/app/build.gradle | 2 +- src/platform/android/app/proguard-rules.pro | 2 ++ src/platform/android/build.gradle | 2 +- src/platform/android/jni/Application.mk | 3 +-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/common/keymap.h b/src/common/keymap.h index 439f00cb..e8d50d71 100644 --- a/src/common/keymap.h +++ b/src/common/keymap.h @@ -51,12 +51,12 @@ extern "C" { #define SB_KEY_SF(x) (0xFFE0+(x)) // Control & Alt keys (parameter = capital character) -#define SB_KEY_CTRL(c) (0xF1000000 + (c)) -#define SB_KEY_ALT(c) (0xF2000000 + (c)) -#define SB_KEY_CTRL_ALT(c) (0xF3000000 + (c)) -#define SB_KEY_SHIFT(c) (0xF4000000 + (c)) -#define SB_KEY_SHIFT_CTRL(c) (0xF5000000 + (c)) -#define SB_KEY_ALT_SHIFT(c) (0xF6000000 + (c)) +#define SB_KEY_CTRL(c) (0x71000000 + (c)) +#define SB_KEY_ALT(c) (0x72000000 + (c)) +#define SB_KEY_CTRL_ALT(c) (0x73000000 + (c)) +#define SB_KEY_SHIFT(c) (0x74000000 + (c)) +#define SB_KEY_SHIFT_CTRL(c) (0x75000000 + (c)) +#define SB_KEY_ALT_SHIFT(c) (0x76000000 + (c)) // keypad #define SB_KEY_KP_DIV 0xFFDA diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 290216cb..5180e829 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -7,7 +7,7 @@ android { // can override some attributes in main/AndroidManifest.xml defaultConfig { applicationId 'net.sourceforge.smallbasic' - minSdkVersion 15 + minSdkVersion 16 targetSdkVersion 27 versionCode 29 versionName "0.12.14" diff --git a/src/platform/android/app/proguard-rules.pro b/src/platform/android/app/proguard-rules.pro index bc528ca8..8406e402 100644 --- a/src/platform/android/app/proguard-rules.pro +++ b/src/platform/android/app/proguard-rules.pro @@ -26,3 +26,5 @@ -keepclasseswithmembernames class * { native ; } + +-printmapping build/outputs/mapping/release/mapping.txt diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index 8a72b6fe..21b3539c 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' + classpath 'com.android.tools.build:gradle:3.2.0' classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0" } } diff --git a/src/platform/android/jni/Application.mk b/src/platform/android/jni/Application.mk index d897604e..3d534433 100644 --- a/src/platform/android/jni/Application.mk +++ b/src/platform/android/jni/Application.mk @@ -1,3 +1,2 @@ -APP_PLATFORM := android-15 -APP_ABI := armeabi armeabi-v7a x86 +APP_ABI := all From 5433b73ff2e0a4612c919057750377ea39c7d72b Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Fri, 28 Sep 2018 14:42:58 +1000 Subject: [PATCH 04/32] COMMON: fix some intellij inspections --- configure.ac | 1 + src/common/bc.c | 2 +- src/common/bc.h | 2 +- src/common/blib.c | 20 +++++++-------- src/common/blib.h | 6 ----- src/common/blib_db.c | 30 +++++++++++----------- src/common/blib_graph.c | 2 +- src/common/blib_math.c | 2 +- src/common/blib_math.h | 1 - src/common/blib_sound.c | 4 +-- src/common/ceval.c | 2 +- src/common/device.c | 12 ++++----- src/common/device.h | 1 - src/common/eval.c | 4 +-- src/common/ffill.c | 4 +-- src/common/file.c | 2 -- src/common/hotspots.h | 4 +-- src/common/kw.h | 4 --- src/common/pfill.c | 9 ------- src/common/pproc.h | 10 -------- src/common/proc.c | 6 ++--- src/common/sberr.h | 2 -- src/common/scan.c | 12 ++++----- src/common/scan.h | 10 -------- src/common/screen.c | 2 -- src/common/smbas.h | 5 ---- src/common/str.c | 2 +- src/common/str.h | 57 ----------------------------------------- src/common/sys.h | 5 ---- src/common/tasks.h | 2 +- src/common/units.h | 3 +-- src/common/var.h | 1 - src/common/var_map.c | 2 -- 33 files changed, 56 insertions(+), 175 deletions(-) diff --git a/configure.ac b/configure.ac index 0c71066c..6ed1d6a9 100644 --- a/configure.ac +++ b/configure.ac @@ -198,6 +198,7 @@ function buildSDL() { esac PACKAGE_CFLAGS="${PACKAGE_CFLAGS} `sdl2-config --cflags` `freetype-config --cflags` -fno-exceptions" + CXXFLAGS="${CXXFLAGS} -fno-rtti -std=c++11" dnl preconfigured values for SDL build AC_DEFINE(_SDL, 1, [Defined when building SDL version]) diff --git a/src/common/bc.c b/src/common/bc.c index 063b0298..592d114f 100644 --- a/src/common/bc.c +++ b/src/common/bc.c @@ -50,7 +50,7 @@ void bc_resize(bc_t *bc, uint32_t new_size) { /* * add one command */ -void bc_add1(bc_t *bc, byte code) { +void bc_add1(bc_t *bc, char code) { if (bc->count + sizeof(byte) >= bc->size) { bc_resize(bc, bc->size + BC_ALLOC_INCR); } diff --git a/src/common/bc.h b/src/common/bc.h index 16115545..f6a5f0c5 100644 --- a/src/common/bc.h +++ b/src/common/bc.h @@ -77,7 +77,7 @@ void bc_resize(bc_t *bc, uint32_t newsize); * @param bc the bc structure * @param code the byte */ -void bc_add1(bc_t *bc, byte code); +void bc_add1(bc_t *bc, char code); /** * @ingroup scan diff --git a/src/common/blib.c b/src/common/blib.c index 17bb4bfe..d40685f2 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -588,7 +588,7 @@ void cmd_print(int output) { v_free(&var); return; } else { - build_format((char *) var.v.p.ptr); + build_format(var.v.p.ptr); v_free(&var); } } @@ -626,7 +626,7 @@ void cmd_print(int output) { if (use_format) { switch (var.type) { case V_STR: - fmt_printS((char *)var.v.p.ptr, output, handle); + fmt_printS(var.v.p.ptr, output, handle); break; case V_INT: fmt_printN(var.v.i, output, handle); @@ -748,7 +748,7 @@ void cmd_input(int input) { // "redo from start" if (input == PV_CONSOLE) { // prompt if (prompt.v.p.ptr) { - pv_write((char *) prompt.v.p.ptr, input, handle); + pv_write(prompt.v.p.ptr, input, handle); } } @@ -768,7 +768,7 @@ void cmd_input(int input) { break; case PV_STRING: // string (SINPUT) - inps = strdup((char *)vuser_p->v.p.ptr); + inps = strdup(vuser_p->v.p.ptr); break; case PV_FILE: // file (INPUT#) @@ -857,7 +857,7 @@ void cmd_input(int input) { *p = lc; // next pos - inp_p = p + ((next_is_const) ? strlen((char *) ptable[cur_par_idx].var->v.p.ptr) : 1); + inp_p = p + ((next_is_const) ? strlen(ptable[cur_par_idx].var->v.p.ptr) : 1); if (*p == '\0') { input_is_finished = 1; } @@ -1974,7 +1974,7 @@ void cmd_read() { vp->v.p.ptr = malloc(len + 1); vp->v.p.owner = 1; memcpy(vp->v.p.ptr, prog_source + prog_dp, len); - *((char *) (vp->v.p.ptr + len)) = '\0'; + *((vp->v.p.ptr + len)) = '\0'; vp->v.p.length = len; prog_dp += len; } @@ -2317,11 +2317,11 @@ void cmd_wjoin() { } len += el_len; - strcat((char *)str->v.p.ptr, (char *)e_str.v.p.ptr); + strcat(str->v.p.ptr, e_str.v.p.ptr); v_free(&e_str); if (i != var_p->v.p.length - 1) { - strcat((char *)str->v.p.ptr, (char *)del.v.p.ptr); + strcat(str->v.p.ptr, del.v.p.ptr); len += del_len; } } @@ -2365,7 +2365,7 @@ void cmd_datedmy() { eval(&arg); if (arg.type == V_STR) { - date_str2dmy((char *) arg.v.p.ptr, &d, &m, &y); + date_str2dmy(arg.v.p.ptr, &d, &m, &y); v_free(&arg); } else { // julian @@ -2423,7 +2423,7 @@ void cmd_timehms() { if (arg.type == V_STR) { // string - date_str2hms((char *) arg.v.p.ptr, &h, &m, &s); + date_str2hms(arg.v.p.ptr, &h, &m, &s); v_free(&arg); } else { // timer diff --git a/src/common/blib.h b/src/common/blib.h index 6b8465c5..3d76dd9c 100644 --- a/src/common/blib.h +++ b/src/common/blib.h @@ -69,11 +69,6 @@ void cmd_search(void); void cmd_swap(void); void cmd_chain(void); void cmd_run(int); -void cmd_poke(void); -void cmd_poke16(void); -void cmd_poke32(void); -void cmd_bcopy(void); -void cmd_calladr(void); void cmd_try(); void cmd_catch(); void cmd_end_try(); @@ -136,7 +131,6 @@ void cmd_intN(long, var_t*); void cmd_numN(long, var_t*); void cmd_genfunc(long, var_t*); var_num_t line_length(var_num_t Ax, var_num_t Ay, var_num_t Bx, var_num_t By); -var_num_t line_segangle(int type, var_num_t Adx, var_num_t Ady, var_num_t Bdx, var_num_t Bdy); void cmd_integral(void); void cmd_root(); diff --git a/src/common/blib_db.c b/src/common/blib_db.c index 02200f14..6861d58a 100644 --- a/src/common/blib_db.c +++ b/src/common/blib_db.c @@ -70,7 +70,7 @@ void cmd_fopen() { int handle = par_getint(); if (!prog_error) { if (dev_fstatus(handle) == 0) { - dev_fopen(handle, (char *)file_name.v.p.ptr, flags); + dev_fopen(handle, file_name.v.p.ptr, flags); } else { rt_raise("OPEN: FILE IS ALREADY OPENED"); } @@ -145,7 +145,7 @@ void write_encoded_var(int handle, var_t *var) { dev_fwrite(handle, (byte *)&var->v.n, fv.size); break; case V_STR: - fv.size = strlen((char *)var->v.p.ptr); + fv.size = strlen(var->v.p.ptr); dev_fwrite(handle, (byte *)&fv, sizeof(struct file_encoded_var)); dev_fwrite(handle, (byte *)var->v.p.ptr, fv.size); break; @@ -201,7 +201,7 @@ int read_encoded_var(int handle, var_t *var) { v_new_array(var, fv.size); // read additional data about array - dev_fread(handle, (byte *)&v_maxdim(var), 1); + dev_fread(handle, &v_maxdim(var), 1); for (int i = 0; i < v_maxdim(var); i++) { dev_fread(handle, (byte *)&v_lbound(var, i), sizeof(int)); dev_fread(handle, (byte *)&v_ubound(var, i), sizeof(int)); @@ -398,7 +398,7 @@ void cmd_flineinput() { v_free(var_p); var_p->type = V_STR; var_p->v.p.ptr = calloc(SB_TEXTLINE_SIZE + 1, 1); - dev_gets((char *)var_p->v.p.ptr, SB_TEXTLINE_SIZE); + dev_gets(var_p->v.p.ptr, SB_TEXTLINE_SIZE); var_p->v.p.length = strlen(var_p->v.p.ptr); dev_print("\n"); } @@ -417,8 +417,8 @@ void cmd_fkill() { if (prog_error) { return; } - if (dev_fexists((char *)file_name.v.p.ptr)) { - dev_fremove((char *)file_name.v.p.ptr); + if (dev_fexists(file_name.v.p.ptr)) { + dev_fremove(file_name.v.p.ptr); } v_free(&file_name); } @@ -447,11 +447,11 @@ void cmd_filecp(int mv) { return; } - if (dev_fexists((char *)src.v.p.ptr)) { + if (dev_fexists(src.v.p.ptr)) { if (!mv) { - dev_fcopy((char *)src.v.p.ptr, (char *)dst.v.p.ptr); + dev_fcopy(src.v.p.ptr, dst.v.p.ptr); } else { - dev_frename((char *)src.v.p.ptr, (char *)dst.v.p.ptr); + dev_frename(src.v.p.ptr, dst.v.p.ptr); } } else { rt_raise("COPY/RENAME: FILE DOES NOT EXIST"); @@ -472,7 +472,7 @@ void cmd_chdir() { if (prog_error) { return; } - dev_chdir((char *)dir.v.p.ptr); + dev_chdir(dir.v.p.ptr); v_free(&dir); } @@ -488,7 +488,7 @@ void cmd_rmdir() { if (prog_error) { return; } - dev_rmdir((char *)dir.v.p.ptr); + dev_rmdir(dir.v.p.ptr); v_free(&dir); } @@ -504,7 +504,7 @@ void cmd_mkdir() { if (prog_error) { return; } - dev_mkdir((char *)dir.v.p.ptr); + dev_mkdir(dir.v.p.ptr); v_free(&dir); } @@ -573,7 +573,7 @@ void cmd_floadln() { if (v_strlen(&file_name) == 0) { err_throw(FSERR_NOT_FOUND); } else { - dev_fopen(handle, (char *)file_name.v.p.ptr, flags); + dev_fopen(handle, file_name.v.p.ptr, flags); } v_free(&file_name); CHK_ERR(FSERR_GENERIC); @@ -710,7 +710,7 @@ void cmd_fsaveln() { return; } - int success = dev_fopen(handle, (char *)file_name.v.p.ptr, flags); + int success = dev_fopen(handle, file_name.v.p.ptr, flags); v_free(&file_name); CHK_ERR(FSERR_GENERIC); if (!success) { @@ -771,7 +771,7 @@ void cmd_chmod() { return; } - chmod((char *)str.v.p.ptr, mode); + chmod(str.v.p.ptr, mode); v_free(&str); } diff --git a/src/common/blib_graph.c b/src/common/blib_graph.c index 64c401a5..f56ee869 100644 --- a/src/common/blib_graph.c +++ b/src/common/blib_graph.c @@ -665,7 +665,7 @@ void cmd_draw() { if (prog_error) { return; } - p = (char *) var.v.p.ptr; + p = var.v.p.ptr; while (*p) { // 'N' command must affect only the next drawing command. diff --git a/src/common/blib_math.c b/src/common/blib_math.c index ad664410..36293d4b 100644 --- a/src/common/blib_math.c +++ b/src/common/blib_math.c @@ -41,7 +41,7 @@ int sgn(var_num_t x) { * ROUND(x, digits) */ var_num_t fround(var_num_t x, int dig) { - var_num_t result = 0.0; + var_num_t result; var_num_t m = floor(pow(10.0, dig)); if (x < 0.0) { diff --git a/src/common/blib_math.h b/src/common/blib_math.h index a6ca1801..28076d5a 100644 --- a/src/common/blib_math.h +++ b/src/common/blib_math.h @@ -21,7 +21,6 @@ var_num_t frac(var_num_t x); int sgn(var_num_t x); var_num_t fround(var_num_t x, int dig); -#define SEGLEN(Ax,Ay,Bx,By) line_length(Ax,Ay,Bx,By) #define PTSIGN(Ax,Ay,Bx,By,Qx,Qy) (ZSGN((Qx) * ((Ay) - (By)) + (Qy) * \ ((Bx) - (Ax)) + (Ax) * (By) - (Ay) * (Bx))) /**< sign of a point(Q) from a line-segment(A->B) @ingroup math */ diff --git a/src/common/blib_sound.c b/src/common/blib_sound.c index 91407343..ff48c81d 100644 --- a/src/common/blib_sound.c +++ b/src/common/blib_sound.c @@ -39,7 +39,7 @@ void cmd_beep() { // SOUND frq, dur [, vol] [BG] // void cmd_sound() { - int frq, ms = 250, vol = 100; + int frq, ms, vol = 100; int bg = 0; frq = par_getint(); @@ -113,7 +113,7 @@ void cmd_play() { str = (char *) malloc(var.v.p.length + 1); // copy without spaces - p = (char *) var.v.p.ptr; + p = var.v.p.ptr; s = str; while (*p) { if (*p > 32) { diff --git a/src/common/ceval.c b/src/common/ceval.c index d4e6a41b..c87cacf9 100644 --- a/src/common/ceval.c +++ b/src/common/ceval.c @@ -342,7 +342,7 @@ void cev_log(void) { cev_cmp(); // R = cev_cmp() IF_ERR_RTN; while (CODE(IP) == kwTYPE_LOGOPR) { - char op; + byte op; bcip_t shortcut; bcip_t shortcut_offs; diff --git a/src/common/device.c b/src/common/device.c index d6d64635..8bdea1e2 100644 --- a/src/common/device.c +++ b/src/common/device.c @@ -158,7 +158,7 @@ int dev_input_insert_char(int ch, char *dest, int pos, int replace_mode) { // store character into buffer if (replace_mode) { // overwrite mode - remain = strlen((char *)(dest + pos)); + remain = strlen((dest + pos)); buf = malloc(remain + 1); strcpy(buf, dest + pos); memcpy(dest + pos, cstr, count); @@ -170,17 +170,17 @@ int dev_input_insert_char(int ch, char *dest, int pos, int replace_mode) { count = 1; } if (buf[0]) { // not a '\0' - strcat((char *)dest, (char *)(buf + count)); + strcat(dest, (buf + count)); } free(buf); } else { // insert mode - remain = strlen((char *)(dest + pos)); + remain = strlen((dest + pos)); buf = malloc(remain + 1); strcpy(buf, dest + pos); memcpy(dest + pos, cstr, count); dest[pos + count] = '\0'; - strcat((char *)dest, (char *)buf); + strcat(dest, buf); free(buf); } @@ -201,12 +201,12 @@ int dev_input_remove_char(char *dest, int pos) { } else { count = 1; } - remain = strlen((char *)(dest + pos + 1)); + remain = strlen((dest + pos + 1)); buf = malloc(remain + 1); strcpy(buf, dest + pos + count); dest[pos] = '\0'; - strcat((char *)dest, (char *)buf); + strcat(dest, buf); free(buf); return count; } diff --git a/src/common/device.h b/src/common/device.h index 1116dd16..8fbe0ac9 100644 --- a/src/common/device.h +++ b/src/common/device.h @@ -782,7 +782,6 @@ int dev_filemtime(var_t *v, char **buffer); */ typedef enum { ft_stream, /**< simple file */ - ft_random, ft_serial_port, /**< COMx:speed, serial port */ ft_socket_client, /**< SCLT:address:port, socket client */ ft_socket_server, // SSVR:address:port diff --git a/src/common/eval.c b/src/common/eval.c index 47e8d4de..56d04d9a 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -281,12 +281,12 @@ int v_wc_match(var_t *vwc, var_t *v) { } } } else if (v->type == V_STR) { - ri = wc_match((char *)vwc->v.p.ptr, (char *)v->v.p.ptr); + ri = wc_match(vwc->v.p.ptr, v->v.p.ptr); } else if (v->type == V_NUM || v->type == V_INT) { var_t *vt = v_clone(v); v_tostr(vt); if (!prog_error) { - ri = wc_match((char *)vwc->v.p.ptr, (char *)vt->v.p.ptr); + ri = wc_match(vwc->v.p.ptr, vt->v.p.ptr); } V_FREE(vt); v_detach(vt); diff --git a/src/common/ffill.c b/src/common/ffill.c index 86111d11..2015d1f6 100644 --- a/src/common/ffill.c +++ b/src/common/ffill.c @@ -39,7 +39,7 @@ typedef struct tagQUEUE QUEUE; // Global variables of the module. static struct tagParams ff_buf1[QUEUESIZE]; static struct tagParams ff_buf2[QUEUESIZE]; -static long ucBorder, ucFill; +static long ucBorder; static QUEUE Qup; static QUEUE Qdn; static int scan_type; @@ -63,7 +63,6 @@ void dev_ffill(uint16_t x0, uint16_t y0, long fill_color, long border_color) { memset(&Qup, 0, sizeof(QUEUE)); memset(&Qdn, 0, sizeof(QUEUE)); ucBorder = 0; - ucFill = 0; scan_type = 0; pcolor = dev_fgcolor; @@ -91,7 +90,6 @@ void dev_ffill(uint16_t x0, uint16_t y0, long fill_color, long border_color) { // save the border and fill values in global variables ucBorder = border_color; - ucFill = fill_color; // initialize the queues Qup.pQ = &Qdn; // pointer to opposite queue diff --git a/src/common/file.c b/src/common/file.c index 08c519bf..d6dae52d 100644 --- a/src/common/file.c +++ b/src/common/file.c @@ -22,8 +22,6 @@ #include #endif -typedef int FileHand; - // drivers #include "common/fs_stream.h" #include "common/fs_serial.h" diff --git a/src/common/hotspots.h b/src/common/hotspots.h index 51af728a..7b2b98e4 100644 --- a/src/common/hotspots.h +++ b/src/common/hotspots.h @@ -71,7 +71,7 @@ static inline var_num_t v_getval(var_t *v) { case V_NUM: return v->v.n; case V_STR: - return numexpr_sb_strtof((char *) v->v.p.ptr); + return numexpr_sb_strtof(v->v.p.ptr); case V_PTR: return v->v.ap.p; case V_MAP: @@ -102,7 +102,7 @@ static inline var_int_t v_igetval(var_t *v) { case V_NUM: return v->v.n; case V_STR: - return numexpr_strtol((char *) v->v.p.ptr); + return numexpr_strtol(v->v.p.ptr); case V_PTR: return v->v.ap.p; case V_MAP: diff --git a/src/common/kw.h b/src/common/kw.h index 05aa5f30..fda521a1 100644 --- a/src/common/kw.h +++ b/src/common/kw.h @@ -164,7 +164,6 @@ enum keyword { // line 50 kwFILEREAD, kwCLOSE, kwSEEK, - kwTYPE, kwSPRINT, kwDO, kwOPTION, @@ -189,7 +188,6 @@ enum keyword { // line 50 */ enum proc_keywords { kwCLS = 0x100, // 256 (generic keywords) - kwSHELL, kwENVIRON, kwLOCATE, kwAT, @@ -394,7 +392,6 @@ enum func_keywords { kwBIN, kwENCLOSE, kwDISCLOSE, - kwSEARCHF, kwTRANSLATEF, kwCHOP, kwBGETC, @@ -473,7 +470,6 @@ int kw_getprocname(bid_t code, char *dest); int kw_noarg_func(bid_t code); #define OPTION_BASE 1 -#define OPTION_GRMODE 3 #define OPTION_MATCH 4 #if defined(__cplusplus) diff --git a/src/common/pfill.c b/src/common/pfill.c index 3e1a714e..0bb73227 100644 --- a/src/common/pfill.c +++ b/src/common/pfill.c @@ -11,15 +11,6 @@ #include "common/sys.h" #include "common/device.h" -struct HLine { - int XStart; /* X coordinate of leftmost pixel in line */ - int XEnd; /* X coordinate of rightmost pixel in line */ -}; - -struct HLineList { - struct HLine *HLinePtr; /* pointer to list of horz lines */ -}; - struct EdgeState { struct EdgeState *NextEdge; int X, StartY; diff --git a/src/common/pproc.h b/src/common/pproc.h index a0e6e492..cb55af57 100644 --- a/src/common/pproc.h +++ b/src/common/pproc.h @@ -42,8 +42,6 @@ extern "C" { /**< simple macro for free() 2 ptrs @ingroup par */ #define pfree3(a,b,c) { pfree2((a),(b)); pfree((c)); } /**< simple macro for free() 3 ptrs @ingroup par */ -#define pfree4(a,b,c,d) { pfree3((a),(b),(c)); pfree((d)); } -/**< simple macro for free() 4 ptrs @ingroup par */ /** * @ingroup exec @@ -64,14 +62,6 @@ void bc_loop(int isf); */ void eval(var_t *result); -/** - * @ingroup exec - * - * sets the data-p. the data-p is used for READ/DATA commands. - * actually it is points to the next position of which the READ will use. - */ -void set_dataip(uint16_t label_id); - /** * @ingroup exec * diff --git a/src/common/proc.c b/src/common/proc.c index d89d623f..faa94c29 100644 --- a/src/common/proc.c +++ b/src/common/proc.c @@ -155,10 +155,10 @@ void pv_write_str(char *str, var_t *vp) { if (vp->v.p.ptr == NULL) { vp->v.p.ptr = malloc(vp->v.p.length + 1); vp->v.p.owner = 1; - strcpy((char *)vp->v.p.ptr, str); + strcpy(vp->v.p.ptr, str); } else { vp->v.p.ptr = realloc(vp->v.p.ptr, vp->v.p.length + 1); - strcat((char *)vp->v.p.ptr, str); + strcat(vp->v.p.ptr, str); } } @@ -203,7 +203,7 @@ void pv_writevar(var_t *var, int method, intptr_t handle) { pv_write(tmpsb, method, handle); break; case V_STR: - pv_write((char *)var->v.p.ptr, method, handle); + pv_write(var->v.p.ptr, method, handle); break; case V_ARRAY: case V_MAP: diff --git a/src/common/sberr.h b/src/common/sberr.h index 0ca895a0..b41a8638 100644 --- a/src/common/sberr.h +++ b/src/common/sberr.h @@ -69,12 +69,10 @@ void err_fopen(void); void err_syntaxsep(const char *seps); void err_parsepoly(int idx, int mark); void err_bfn_err(long code); -void err_gpf(bcip_t addr, int bc); void err_pcode_err(long pcode); void err_const(void); void err_notavar(void); void err_run_err(const char *file); -void err_invkw(bcip_t addr, byte code); void err_ref_var(); void err_ref_circ_var(); void err_array(); diff --git a/src/common/scan.c b/src/common/scan.c index 2e46944b..fb984851 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -438,7 +438,7 @@ bid_t comp_add_udp(const char *proc_name) { /* * sets the IP of the user-defined-procedure (or function) */ -bid_t comp_udp_setip(const char *proc_name, bcip_t ip) { +bid_t comp_udp_setip(const char *proc_name) { bid_t idx; char *name = comp_bc_temp; @@ -473,7 +473,7 @@ bcip_t comp_udp_getip(const char *proc_name) { * parameters string-section */ char *get_param_sect(char *text, const char *delim, char *dest) { - char *p = (char *)text; + char *p = text; char *d = dest; int quotes = 0, level = 0, skip_ch = 0; int curley_brace = 0; @@ -1150,7 +1150,7 @@ char *comp_scan_json(char *json, bc_t *bc) { * scan expression */ void comp_expression(char *expr, byte no_parser) { - char *ptr = (char *)expr; + char *ptr = expr; int level = 0, check_udf = 0; int kw_exec_more = 0; var_int_t lv = 0; @@ -1678,7 +1678,7 @@ char *comp_getlist_insep(char *source, char_p_t *args, char *sep, int maxarg, in */ int comp_single_line_if(char *text) { // *text points to 'expr' - char *p = (char *)text; + char *p = text; char *pthen, *pelse; char buf[SB_SOURCELINE_SIZE + 1]; @@ -2142,9 +2142,9 @@ void comp_text_line_func(bid_t idx, int decl) { } else { // setup routine's address (and get an id) int pidx; - if ((pidx = comp_udp_setip(pname, comp_prog.count)) == -1) { + if ((pidx = comp_udp_setip(pname)) == -1) { pidx = comp_add_udp(pname); - comp_udp_setip(pname, comp_prog.count); + comp_udp_setip(pname); } // put JMP to the next command after the END // (now we just keep the rq space, pass2 will update that) diff --git a/src/common/scan.h b/src/common/scan.h index 9064ad29..5fc45af2 100644 --- a/src/common/scan.h +++ b/src/common/scan.h @@ -359,16 +359,6 @@ int comp_pass1(const char *section, const char *text); */ int comp_pass2(void); -/** - * @ingroup scan - * - * returns true if the SB-source file 'basfile' exists - * - * @param basfile the filename - * @return non-zero if SB-source file 'basfile' exists - */ -int comp_bas_exist(const char *basfile); - /** * @ingroup scan * diff --git a/src/common/screen.c b/src/common/screen.c index 10490bc8..0b43e17d 100644 --- a/src/common/screen.c +++ b/src/common/screen.c @@ -27,7 +27,6 @@ c |= ((y > dev_Vy2) << 3); } #define CLIPIN(c) ((c & 0xF) == 0) -uint32_t os_ver = 0x40000; uint32_t os_color_depth = 16; byte os_graphics = 0; // CONSOLE int os_graf_mx = 80; @@ -302,7 +301,6 @@ void dev_arc(int xc, int yc, double r, double start, double end, double aspect) */ void osd_line(int x1, int y1, int x2, int y2); - /** * draw rectangle (filled or not) */ diff --git a/src/common/smbas.h b/src/common/smbas.h index daf555da..59823acd 100644 --- a/src/common/smbas.h +++ b/src/common/smbas.h @@ -139,8 +139,6 @@ EXTERN char gsb_last_errmsg[SB_ERRMSG_SIZE + 1]; /**< last error message */ #define prog_error ctask->error #define comp_error ctask->error #define prog_file ctask->file -#define comp_file prog_file -#define comp_errmsg ctask->errmsg #define prog_errmsg ctask->errmsg #define prog_length ctask->sbe.exec.length #define prog_ip ctask->sbe.exec.ip @@ -195,7 +193,6 @@ EXTERN char gsb_last_errmsg[SB_ERRMSG_SIZE + 1]; /**< last error message */ #define comp_udptable ctask->sbe.comp.udptable #define comp_udpcount ctask->sbe.comp.udpcount #define comp_udpsize ctask->sbe.comp.udpsize -#define comp_next_field_id ctask->sbe.comp.next_field_id #define comp_use_global_vartable ctask->sbe.comp.use_global_vartable #define comp_stack ctask->sbe.comp.stack #define comp_sp ctask->sbe.comp.stack.count @@ -207,8 +204,6 @@ EXTERN char gsb_last_errmsg[SB_ERRMSG_SIZE + 1]; /**< last error message */ #define tlab prog_labtable #define tvar prog_vartable #define eval_size eval_stk_size -#define data_dp prog_dp -#define brun_first_data_ip data_org #define prog_stack_sp prog_sp #define prog_stack_count prog_stack_sp diff --git a/src/common/str.c b/src/common/str.c index dba65238..52af5462 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -288,7 +288,7 @@ char *strlower(char *str) { * Warning: octals are different from C (QB compatibility: 009 = 9) */ char *get_numexpr(char *text, char *dest, int *type, var_int_t *lv, var_num_t *dv) { - char *p = (char *)text; + char *p = text; char *d = dest; char *epos = NULL; byte base = 10; diff --git a/src/common/str.h b/src/common/str.h index cb7f4fea..988eaa0a 100644 --- a/src/common/str.h +++ b/src/common/str.h @@ -128,17 +128,6 @@ char *strupper(char *str); */ char *strlower(char *str); -/** - * @ingroup str - * - * stores the next keyword of text in dest and returns a pointer to the next position - * - * @param text the source string - * @param dest the buffer to store the keyword - * @return a pointer in 'text' that points to the next position - */ -char *get_keyword(char *text, char *dest); - /** * @ingroup str * @@ -212,7 +201,6 @@ long hextol(const char *str); * @return the number */ var_num_t sb_strtof(const char *str); -#define xsb_strtof(s) sb_strtof((s)) /** * @ingroup str @@ -257,20 +245,6 @@ char *ltostr(var_int_t num, char *dest); */ int strcaselessn(const char *s1, int s1n, const char *s2, int s2n); -/** - * @ingroup str - * - * locate the substring 's2' into 's1' and return a pointer of the - * first occurence (in 's1') or NULL if not found. - * - * the difference with strstr() is that the stristr do the search ignoring the case - * - * @param s1 the text - * @param s2 the substring - * @return on success a pointer to 's1' in the place which the 's2' is starting; otherwise NULL - */ -char *stristr(const char *s1, const char *s2); - /** * @ingroup str * @@ -377,33 +351,6 @@ char *transdup(const char *src, const char *what, const char *with, int ignore_c */ char *trimdup(const char *str); -/** - * @ingroup str - * - * with few words, the filename's playground. - * - * @param dest is the buffer to store the result - * @param source is the filename - * @param newdir if not NULL, the directory to replace the old-one (note the newdir must ends with directory-separator character) - * @param prefix if not NULL, the prefix to be used on the basename - * @param new_ext if not NULL, the new extension of the file - * @param suffix if not NULL, the suffix to be used on the basename (before the extension) - * @return a pointer to dest - */ -char *chgfilename(char *dest, char *source, char *newdir, char *prefix, char *new_ext, char *suffix); - -/** - * @ingroup str - * - * returns the basename of source. basename means the name without the directory. - * on error the dest will be empty. - * - * @param dest is the buffer to store the name - * @param source is the filename - * @return a pointer to dest - */ -char *xbasename(char *dest, const char *source); - /** * @ingroup str * @@ -436,10 +383,6 @@ char *bstrdup(const char *source); */ const char *baseof(const char *source, int delim); -/* - */ -char char_table_replace(const char *what_table, int ch, const char *replace_table); - /** * @ingroup sys * diff --git a/src/common/sys.h b/src/common/sys.h index 856badc3..b1d45a50 100644 --- a/src/common/sys.h +++ b/src/common/sys.h @@ -87,11 +87,6 @@ extern "C" { #define SWAP(a,b,c) ( (c) = (a), (a) = (b), (b) = (c) ) // swap values #define FLOOR(a) ((a)>0 ? (int)(a) : -(int)(-a)) // floor #define CEILING(a) ((a)==(int)(a) ? (a) : (a)>0 ? 1+(int)(a) : -(1+(int)(-a))) -#define ROUND(a) ((a)>0 ? (int)(a+0.5) : -(int)(0.5-a)) // round -#define ISWAP(a,b) ( a^=b, b^=a, a^=b ) // integer swap -#define I2MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) // min -#define I2MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) // max -#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v) // clamp to specified range #if !defined(NULL) #define NULL (void*)0L diff --git a/src/common/tasks.h b/src/common/tasks.h index 8118550f..ef0617fc 100644 --- a/src/common/tasks.h +++ b/src/common/tasks.h @@ -17,7 +17,7 @@ extern "C" { #endif typedef enum { - tsk_free, tsk_ready, tsk_nil + tsk_free, tsk_ready } task_status_t; typedef struct timer_s timer_s; diff --git a/src/common/units.h b/src/common/units.h index c5faee57..c24ea0d5 100644 --- a/src/common/units.h +++ b/src/common/units.h @@ -54,8 +54,7 @@ typedef struct { */ typedef enum { unit_undefined, /**< unused record */ - unit_loaded, /**< unit is loaded */ - unit_nil + unit_loaded /**< unit is loaded */ } unit_status_t; /** diff --git a/src/common/var.h b/src/common/var.h index 05984eb9..2041d6e9 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -315,7 +315,6 @@ stknode_t *code_stackpeek(); #define code_skipnext() prog_ip++ /**< IP ++; @ingroup exec */ #define code_skipnext16() prog_ip+=2 /**< IP += 2; @ingroup exec */ #define code_skipnext32() prog_ip+=4 /**< IP += 4; @ingroup exec */ -#define code_skipnext64() prog_ip+=8 /**< IP += 8; @ingroup exec */ #if defined(CPU_BIGENDIAN) #define code_getnext16() (prog_ip+=2, (prog_source[prog_ip-2]<<8)|prog_source[prog_ip-1]) diff --git a/src/common/var_map.c b/src/common/var_map.c index 6d9e36ad..b43f1afd 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -17,7 +17,6 @@ #define BUFFER_GROW_SIZE 64 #define BUFFER_PADDING 10 #define TOKEN_GROW_SIZE 16 -#define ARRAY_GROW_SIZE 8 /** * Container for map_from_str @@ -26,7 +25,6 @@ typedef struct JsonTokens { const char *js; jsmntok_t *tokens; int num_tokens; - int code_array; } JsonTokens; struct ArrayNode; From 110b5fd41c543471f719da5221bbab7da3e8865d Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Fri, 28 Sep 2018 15:34:13 +1000 Subject: [PATCH 05/32] COMMON: fix error message when command LINE INPUT attempts to read a non open file --- ChangeLog | 3 + src/common/blib_db.c | 143 +++++++++--------- src/languages/messages.en.h | 1 + .../android/app/src/main/assets/main.bas | 2 +- 4 files changed, 77 insertions(+), 72 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0ecab844..d455cb69 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2018-08-08 (0.12.14) + COMMON: fix error message when command "LINE INPUT" attempts to read a non open file + 2018-08-08 (0.12.14) COMMON: fix crash when attempting to load an image from a failed network connection diff --git a/src/common/blib_db.c b/src/common/blib_db.c index 6861d58a..2cc1baa8 100644 --- a/src/common/blib_db.c +++ b/src/common/blib_db.c @@ -18,11 +18,17 @@ #include +#define LDLN_INC 256 +#define GROW_SIZE 1024 +#define BUFMAX 256 +#define CHK_ERR_CLEANUP(s) if (err_handle_error(s, &file_name)) return; +#define CHK_ERR(s) if (err_handle_error(s, NULL)) return; + struct file_encoded_var { - byte sign; // always '$' - byte version; // - byte type; - uint32_t size; + byte sign; // always '$' + byte version; // + byte type; // + uint32_t size; // }; /* @@ -235,12 +241,13 @@ void cmd_fwrite() { if (!dev_fstatus(handle)) { // dev_fwrite(handle, "\n", 1); } else { - rt_raise("FIO: FILE IS NOT OPENED"); + rt_raise(ERR_FILE_NOT_OPEN); } return; } - par_getsep(); // allow commas + // allow commas + par_getsep(); if (!prog_error) { if (dev_fstatus(handle)) { @@ -271,7 +278,7 @@ void cmd_fwrite() { } } while (!exitf); } else { - rt_raise("FIO: FILE IS NOT OPENED"); + rt_raise(ERR_FILE_NOT_OPEN); } } } @@ -290,7 +297,8 @@ void cmd_fread() { return; } - par_getsep(); // allow commas + // allow commas + par_getsep(); if (prog_error) { return; } @@ -313,13 +321,55 @@ void cmd_fread() { // next byte code = code_peek(); if (code == kwTYPE_SEP) { - par_getsep(); // allow commas + // allow commas + par_getsep(); } else { break; } } while (1); } else { - rt_raise("FIO: FILE IS NOT OPENED"); + rt_raise(ERR_FILE_NOT_OPEN); + } + } +} + +void cmd_read_variable(int handle) { + byte code = code_peek(); + if (code != kwTYPE_VAR) { + err_syntax(kwLINEINPUT, "%P"); + } else { + var_t *var_p = code_getvarptr(); + if (!prog_error) { + v_free(var_p); + int size = BUFMAX; + int index = 0; + byte ch; + + var_p->type = V_STR; + var_p->v.p.ptr = malloc(size); + + // READ IT + while (!dev_feof(handle)) { + dev_fread(handle, &ch, 1); + if (prog_error) { + v_free(var_p); + var_p->type = V_INT; + var_p->v.i = -1; + return; + } else if (ch == '\n') { + break; + } else if (ch != '\r') { + // store char + if (index == (size - 1)) { + size += BUFMAX; + var_p->v.p.ptr = realloc(var_p->v.p.ptr, size); + } + var_p->v.p.ptr[index] = ch; + index++; + } + } + var_p->v.p.ptr[index] = '\0'; + var_p->v.p.length = index + 1; } } } @@ -332,59 +382,17 @@ void cmd_flineinput() { // // FILE OR DEVICE // - - // file handle par_getsharp(); if (!prog_error) { int handle = par_getint(); if (!prog_error) { - // par_getsemicolon(); - par_getsep(); // allow commas + // allow commas + par_getsep(); if (!prog_error) { if (dev_fstatus(handle)) { - // get the variable - byte code = code_peek(); - if (code != kwTYPE_VAR) { - err_syntax(kwLINEINPUT, "%P"); - return; - } - var_t *var_p = code_getvarptr(); - if (!prog_error) { - v_free(var_p); - int size = 256; - int index = 0; - byte ch; - - var_p->type = V_STR; - var_p->v.p.ptr = malloc(size); - - // READ IT - while (!dev_feof(handle)) { - dev_fread(handle, &ch, 1); - if (prog_error) { - v_free(var_p); - var_p->type = V_INT; - var_p->v.i = -1; - return; - } else if (ch == '\n') { - break; - } - else if (ch != '\r') { - // store char - if (index == (size - 1)) { - size += 256; - var_p->v.p.ptr = realloc(var_p->v.p.ptr, size); - } - var_p->v.p.ptr[index] = ch; - index++; - } - } - var_p->v.p.ptr[index] = '\0'; - var_p->v.p.length = index + 1; - } - else { - rt_raise("FIO: FILE IS NOT OPENED"); - } + cmd_read_variable(handle); + } else { + rt_raise(ERR_FILE_NOT_OPEN); } } } @@ -509,17 +517,11 @@ void cmd_mkdir() { } /* - * load text-file to string or to array - * Modified 2-May-2002 Chris Warren-Smith. Implemented buffered read + * load text-file to string or to array + * Modified 2-May-2002 Chris Warren-Smith. Implemented buffered read * - * TLOAD filename, variable [, type] + * TLOAD filename, variable [, type] */ -#define LDLN_INC 256 -#define GROW_SIZE 1024 -#define BUFMAX 256 -#define CHK_ERR_CLEANUP(s) if (err_handle_error(s, &file_name)) return; -#define CHK_ERR(s) if (err_handle_error(s, NULL)) return; - void cmd_floadln() { var_t file_name, *array_p = NULL, *var_p = NULL; int flags = DEV_FILE_INPUT; @@ -826,8 +828,7 @@ void dirwalk(char *dir, char *wc, bcip_t use_ip, int depth) { } if (strlen(dir) + strlen(dp->d_name) + 2 > OS_PATHNAME_SIZE) { rt_raise("DIRWALK: name %s/%s too long", dir, dp->d_name); - } - else { + } else { // check filename int callusr; int contf = 1; @@ -835,8 +836,7 @@ void dirwalk(char *dir, char *wc, bcip_t use_ip, int depth) { if (!wc) { callusr = 1; - } - else { + } else { callusr = wc_match(wc, dp->d_name); } @@ -920,7 +920,8 @@ void cmd_bputc() { if (prog_error) { return; } - par_getsep(); // allow commas + // allow commas + par_getsep(); if (prog_error) { return; } diff --git a/src/languages/messages.en.h b/src/languages/messages.en.h index ff020ddc..70dae434 100644 --- a/src/languages/messages.en.h +++ b/src/languages/messages.en.h @@ -222,3 +222,4 @@ #define ERR_MEMORY "Out of memory error" #define ERR_NETWORK "Network error" #define ERR_XPM_IMAGE "Invalid xpm image" +#define ERR_FILE_NOT_OPEN "IOError: File not open for reading" diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 81c0d49c..e6d1f3d9 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -111,7 +111,7 @@ sub do_about() print print "Copyright (c) 2002-2018 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nicholas Christopoulos" + chr(10) - print "https://smallbasic.sourceforge.io" + chr(10) + print "https://smallbasic.github.io" + chr(10) color colGrey,0 print "SmallBASIC comes with ABSOLUTELY NO WARRANTY. "; print "This program is free software; you can use it "; From 459686b3a5b5a77f29deab71637751f7e10dba85 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 30 Sep 2018 10:10:12 +1000 Subject: [PATCH 06/32] COMMON: added console build check for endianness --- configure.ac | 7 +++++++ src/common/fs_socket_client.c | 11 ++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 6ed1d6a9..277dc27c 100644 --- a/configure.ac +++ b/configure.ac @@ -242,6 +242,13 @@ function buildConsole() { win32="yes" esac + AC_C_BIGENDIAN( + AC_DEFINE(CPU_BIGENDIAN, 1, [machine is big-endian]), + AC_DEFINE(CPU_LITTLE_ENDIAN, 1, [machine is little-endian]), + AC_MSG_ERROR(unknown endianess), + AC_MSG_ERROR(universial endianess not supported) + ) + AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, test $win32 = yes) AC_DEFINE(BUILD_CONSOLE, 1, [Building a console based system.]) diff --git a/src/common/fs_socket_client.c b/src/common/fs_socket_client.c index 3d825268..24638db1 100644 --- a/src/common/fs_socket_client.c +++ b/src/common/fs_socket_client.c @@ -14,22 +14,19 @@ #include int sockcl_open(dev_file_t *f) { - char *p; - int port; - char server[129]; - // open "SOCL:smallbasic.sf.net:80" as #1 // open "SOCL:80" as #2 f->drv_dw[0] = 1; - p = strchr(f->name + 5, ':'); + char *p = strchr(f->name + 5, ':'); if (!p) { - port = xstrtol(f->name + 5); + int port = xstrtol(f->name + 5); f->handle = (int) net_listen(port); } else { *p = '\0'; + char server[255]; strlcpy(server, f->name + 5, sizeof(server)); *p = ':'; - port = xstrtol(p + 1); + int port = xstrtol(p + 1); f->handle = (int) net_connect(server, port); } From 9701c2ed81f6fbdf10cd08453c38df0d83a01059 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 30 Sep 2018 15:19:41 +1000 Subject: [PATCH 07/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- ChangeLog | 7 +++++-- src/common/bc.c | 6 ++++-- src/common/bc.h | 1 + src/common/scan.c | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index d455cb69..49488169 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,10 @@ -2018-08-08 (0.12.14) +2018-09-30 (0.12.14) + COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs (or kwTYPE_LINE) + +2018-09-23 (0.12.14) COMMON: fix error message when command "LINE INPUT" attempts to read a non open file -2018-08-08 (0.12.14) +2018-09-23 (0.12.14) COMMON: fix crash when attempting to load an image from a failed network connection 2018-08-08 (0.12.13) diff --git a/src/common/bc.c b/src/common/bc.c index 592d114f..e13adfde 100644 --- a/src/common/bc.c +++ b/src/common/bc.c @@ -21,6 +21,7 @@ void bc_create(bc_t *bc) { bc->size = BC_ALLOC_INCR; bc->count = 0; bc->cp = 0; + bc->eoc_position = 0; } /* @@ -245,8 +246,9 @@ char *bc_store_string(bc_t *bc, char *src) { */ void bc_eoc(bc_t *bc) { if (bc && bc->count && - (bc->ptr[bc->count - 1] != kwTYPE_LINE && - bc->ptr[bc->count - 1] != kwTYPE_EOC)) { + (bc->eoc_position == 0 || bc->eoc_position != bc->count - 1)) { + // avoid appending multiple kwTYPE_EOCs (or kwTYPE_LINE) + bc->eoc_position = bc->count; bc_add1(bc, kwTYPE_EOC); } } diff --git a/src/common/bc.h b/src/common/bc.h index f6a5f0c5..da8ac9e4 100644 --- a/src/common/bc.h +++ b/src/common/bc.h @@ -29,6 +29,7 @@ typedef struct { bcip_t cp; /**< current position (used by readers not writers) */ bcip_t size; /**< allocation size (optimization) */ bcip_t count; /**< current size (used by writers as the current position) */ + bcip_t eoc_position; /**< position of most recent kwTYPE_LINE or kwTYPE_EOC */ } bc_t; /** diff --git a/src/common/scan.c b/src/common/scan.c index fb984851..04fc55fb 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -2863,6 +2863,7 @@ void comp_text_line(char *text, int addLineNo) { } if (addLineNo) { // add debug info: line-number + comp_prog.eoc_position = comp_prog.count; bc_add_code(&comp_prog, kwTYPE_LINE); bc_add_addr(&comp_prog, comp_line); } From 3aaab3192611f3c92e6ab76375c4524efce2772c Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 30 Sep 2018 19:34:16 +1000 Subject: [PATCH 08/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- src/common/scan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/scan.c b/src/common/scan.c index 04fc55fb..52d10a1e 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3724,6 +3724,11 @@ void comp_optimise() { case kwAPPEND: ip = comp_optimise_let(ip, kwTYPE_SEP, ',', kwAPPEND_OPT); break; + case kwTYPE_EOC: + if (comp_prog.ptr[ip + 1] == kwTYPE_EOC) { + sc_raise(ERR_UNSUPPORTED); + } + break; default: break; } From f6bcf1099ea9b72ef4d3133115a3a681708a16d9 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Fri, 5 Oct 2018 16:58:25 +1000 Subject: [PATCH 09/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- src/common/bc.c | 1 + src/common/bc.h | 3 ++- src/common/inet2.c | 6 ++---- src/common/scan.c | 28 ++++++++++++++++++++-------- src/ui/ansiwidget.cpp | 2 +- src/ui/system.cpp | 18 +++++++++--------- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/common/bc.c b/src/common/bc.c index e13adfde..bc8b1616 100644 --- a/src/common/bc.c +++ b/src/common/bc.c @@ -22,6 +22,7 @@ void bc_create(bc_t *bc) { bc->count = 0; bc->cp = 0; bc->eoc_position = 0; + bc->line_position = 0; } /* diff --git a/src/common/bc.h b/src/common/bc.h index da8ac9e4..cdda1ebc 100644 --- a/src/common/bc.h +++ b/src/common/bc.h @@ -29,7 +29,8 @@ typedef struct { bcip_t cp; /**< current position (used by readers not writers) */ bcip_t size; /**< allocation size (optimization) */ bcip_t count; /**< current size (used by writers as the current position) */ - bcip_t eoc_position; /**< position of most recent kwTYPE_LINE or kwTYPE_EOC */ + bcip_t eoc_position; /**< position of most recent kwTYPE_EOC or kwTYPE_LINE*/ + bcip_t line_position; /**< position of most recent kwTYPE_LINE */ } bc_t; /** diff --git a/src/common/inet2.c b/src/common/inet2.c index c5b74c9f..b86fb32f 100644 --- a/src/common/inet2.c +++ b/src/common/inet2.c @@ -94,7 +94,6 @@ void net_printf(socket_t s, const char *fmt, ...) { int net_read(socket_t s, char *buf, int size) { fd_set readfds; struct timeval tv; - int rv; // clear the set FD_ZERO(&readfds); @@ -104,7 +103,7 @@ int net_read(socket_t s, char *buf, int size) { tv.tv_sec = 0; tv.tv_usec = BLOCK_INTERVAL; - rv = select(s + 1, &readfds, NULL, NULL, &tv); + int rv = select(s + 1, &readfds, NULL, NULL, &tv); if (rv == -1) { // an error occured return 0; @@ -204,7 +203,6 @@ socket_t net_connect(const char *server_name, int server_port) { socket_t sock; uint32_t inaddr; struct sockaddr_in ad; - struct hostent *hp; net_init(); @@ -212,7 +210,7 @@ socket_t net_connect(const char *server_name, int server_port) { ad.sin_family = AF_INET; if ((inaddr = inet_addr(server_name)) == INADDR_NONE) { - hp = gethostbyname(server_name); + struct hostent *hp = gethostbyname(server_name); if (hp == NULL) { return -1; } diff --git a/src/common/scan.c b/src/common/scan.c index 52d10a1e..6a44dc9e 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -48,6 +48,8 @@ extern void expr_parser(bc_t *bc); #define LEN_AUTOLOCAL STRLEN(LCN_AUTOLOCAL) #define LEN_AS_WRS STRLEN(LCN_AS_WRS) +#define KW_TYPE_LINE_BYTES 5 + #define SKIP_SPACES(p) \ while (*p == ' ' || *p == '\t') { \ p++; \ @@ -2379,9 +2381,8 @@ void comp_text_line_for() { * Insert the local variables detected during sub/func processing */ void comp_insert_locals() { - int i; int count_local = 0; - for (i = 0; i < comp_varcount; i++) { + for (int i = 0; i < comp_varcount; i++) { if (comp_vartable[i].local_id != -1 && comp_vartable[i].local_proc_level == comp_proc_level) { count_local++; @@ -2390,7 +2391,7 @@ void comp_insert_locals() { comp_pass_node_t *node; bcip_t pos_goto = INVALID_ADDR; - for (i = comp_stack.count - 1; i >= 0; i--) { + for (int i = comp_stack.count - 1; i >= 0; i--) { node = comp_stack.elem[i]; if (comp_prog.ptr[node->pos] == kwGOTO && node->block_id != -1 && @@ -2422,7 +2423,7 @@ void comp_insert_locals() { memcpy(comp_prog.ptr + pos_goto + 1, &ip, ADDRSZ); bc_add_code(&comp_prog, kwTYPE_CRVAR); bc_add_code(&comp_prog, count_local); - for (i = 0; i < comp_varcount; i++) { + for (int i = 0; i < comp_varcount; i++) { if (comp_vartable[i].local_id != -1 && comp_vartable[i].local_proc_level == comp_proc_level) { bc_add_addr(&comp_prog, comp_vartable[i].local_id); @@ -2863,9 +2864,19 @@ void comp_text_line(char *text, int addLineNo) { } if (addLineNo) { // add debug info: line-number - comp_prog.eoc_position = comp_prog.count; - bc_add_code(&comp_prog, kwTYPE_LINE); - bc_add_addr(&comp_prog, comp_line); + if (comp_prog.line_position == 0 || + comp_prog.line_position != (comp_prog.count - KW_TYPE_LINE_BYTES)) { + // not an adjoining kwTYPE_LINE + if (!opt_autolocal && comp_prog.eoc_position == comp_prog.count - 1) { + // overwrite any adjoining kwTYPE_EOC (can't do this with autolocal) + comp_prog.count--; + } + // prevent kwTYPE_EOC from being appended to this kwTYPE_LINE + comp_prog.eoc_position = comp_prog.count; + comp_prog.line_position = comp_prog.count; + bc_add_code(&comp_prog, kwTYPE_LINE); + bc_add_addr(&comp_prog, comp_line); + } } if (idx == -1) { idx = comp_is_proc(comp_bc_name); @@ -3725,7 +3736,8 @@ void comp_optimise() { ip = comp_optimise_let(ip, kwTYPE_SEP, ',', kwAPPEND_OPT); break; case kwTYPE_EOC: - if (comp_prog.ptr[ip + 1] == kwTYPE_EOC) { + if (!opt_autolocal && + (comp_prog.ptr[ip + 1] == kwTYPE_EOC || comp_prog.ptr[ip + 1] == kwTYPE_LINE)) { sc_raise(ERR_UNSUPPORTED); } break; diff --git a/src/ui/ansiwidget.cpp b/src/ui/ansiwidget.cpp index 1841a8a3..f2dc5558 100755 --- a/src/ui/ansiwidget.cpp +++ b/src/ui/ansiwidget.cpp @@ -273,7 +273,7 @@ void AnsiWidget::resize(int newWidth, int newHeight) { } void AnsiWidget::removeHover() { - if ( _hoverInput) { + if (_hoverInput) { int dx = _front->_x; int dy = _front->_y - _front->_scrollY; _hoverInput->drawHover(dx, dy, false); diff --git a/src/ui/system.cpp b/src/ui/system.cpp index ab745c7c..2295790c 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -46,10 +46,10 @@ #define MENU_SHORTCUT 22 #define MENU_SHARE 23 #define MENU_SIZE 24 -#define MENU_COMPETION_0 (MENU_SIZE + 1) -#define MENU_COMPETION_1 (MENU_SIZE + 2) -#define MENU_COMPETION_2 (MENU_SIZE + 3) -#define MENU_COMPETION_3 (MENU_SIZE + 4) +#define MENU_COMPLETION_0 (MENU_SIZE + 1) +#define MENU_COMPLETION_1 (MENU_SIZE + 2) +#define MENU_COMPLETION_2 (MENU_SIZE + 3) +#define MENU_COMPLETION_3 (MENU_SIZE + 4) #define MAX_COMPLETIONS 4 #define MAX_CACHE 8 #define CHANGE_WAIT_SLEEP 1000 @@ -377,16 +377,16 @@ void System::handleMenu(MAEvent &event) { share(_activeFile.c_str()); } break; - case MENU_COMPETION_0: + case MENU_COMPLETION_0: completeKeyword(0); break; - case MENU_COMPETION_1: + case MENU_COMPLETION_1: completeKeyword(1); break; - case MENU_COMPETION_2: + case MENU_COMPLETION_2: completeKeyword(2); break; - case MENU_COMPETION_3: + case MENU_COMPLETION_3: completeKeyword(3); break; } @@ -866,7 +866,7 @@ void System::showMenu() { #endif items->add(new String("Help")); for (int i = 0; i < completions; i++) { - _systemMenu[index++] = MENU_COMPETION_0 + i; + _systemMenu[index++] = MENU_COMPLETION_0 + i; } _systemMenu[index++] = MENU_UNDO; _systemMenu[index++] = MENU_REDO; From 1c38fc3c6f27f27520604417711ed0477a3f48a6 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Fri, 5 Oct 2018 19:41:20 +1000 Subject: [PATCH 10/32] Fix FUNC RETURN statement on linuxone --- src/common/scan.c | 53 +++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/common/scan.c b/src/common/scan.c index 6a44dc9e..36e16c0e 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -26,27 +26,27 @@ bcip_t comp_next_bc_cmd(bc_t *bc, bcip_t ip); extern void expr_parser(bc_t *bc); #define STRLEN(s) ((sizeof(s) / sizeof(s[0])) - 1) -#define LEN_OPTION STRLEN(LCN_OPTION) -#define LEN_IMPORT STRLEN(LCN_IMPORT_WRS) -#define LEN_UNIT STRLEN(LCN_UNIT_WRS) -#define LEN_SBASICPATH STRLEN(LCN_SBASICPATH) -#define LEN_INC STRLEN(LCN_INC) -#define LEN_SUB_WRS STRLEN(LCN_SUB_WRS) -#define LEN_FUNC_WRS STRLEN(LCN_FUNC_WRS) -#define LEN_DEF_WRS STRLEN(LCN_DEF_WRS) -#define LEN_END_WRS STRLEN(LCN_END_WRS) -#define LEN_END_SELECT STRLEN(LCN_END_SELECT) -#define LEN_END_TRY STRLEN(LCN_END_TRY) -#define LEN_PREDEF STRLEN(LCN_PREDEF) -#define LEN_QUIET STRLEN(LCN_QUIET) -#define LEN_GRMODE STRLEN(LCN_GRMODE) -#define LEN_TEXTMODE STRLEN(LCN_TEXTMODE) -#define LEN_COMMAND STRLEN(LCN_COMMAND) -#define LEN_SHOWPAGE STRLEN(LCN_SHOWPAGE) -#define LEN_ANTIALIAS STRLEN(LCN_ANTIALIAS) -#define LEN_LDMODULES STRLEN(LCN_LOAD_MODULES) -#define LEN_AUTOLOCAL STRLEN(LCN_AUTOLOCAL) -#define LEN_AS_WRS STRLEN(LCN_AS_WRS) +const int LEN_OPTION = STRLEN(LCN_OPTION); +const int LEN_IMPORT = STRLEN(LCN_IMPORT_WRS); +const int LEN_UNIT = STRLEN(LCN_UNIT_WRS); +const int LEN_SBASICPATH = STRLEN(LCN_SBASICPATH); +const int LEN_INC = STRLEN(LCN_INC); +const int LEN_SUB_WRS = STRLEN(LCN_SUB_WRS); +const int LEN_FUNC_WRS = STRLEN(LCN_FUNC_WRS); +const int LEN_DEF_WRS = STRLEN(LCN_DEF_WRS); +const int LEN_END_WRS = STRLEN(LCN_END_WRS); +const int LEN_END_SELECT = STRLEN(LCN_END_SELECT); +const int LEN_END_TRY = STRLEN(LCN_END_TRY); +const int LEN_PREDEF = STRLEN(LCN_PREDEF); +const int LEN_QUIET = STRLEN(LCN_QUIET); +const int LEN_GRMODE = STRLEN(LCN_GRMODE); +const int LEN_TEXTMODE = STRLEN(LCN_TEXTMODE); +const int LEN_COMMAND = STRLEN(LCN_COMMAND); +const int LEN_SHOWPAGE = STRLEN(LCN_SHOWPAGE); +const int LEN_ANTIALIAS = STRLEN(LCN_ANTIALIAS); +const int LEN_LDMODULES = STRLEN(LCN_LOAD_MODULES); +const int LEN_AUTOLOCAL = STRLEN(LCN_AUTOLOCAL); +const int LEN_AS_WRS = STRLEN(LCN_AS_WRS); #define KW_TYPE_LINE_BYTES 5 @@ -61,6 +61,13 @@ extern void expr_parser(bc_t *bc); #define GROWSIZE 128 #define MAX_PARAMS 256 +// the offset to a single byte stored in an 32 bit field +#if defined(CPU_BIGENDIAN) + #define BYTE_OFFSET_IN_32 4 +#else + #define BYTE_OFFSET_IN_32 1 +#endif + typedef struct { byte *code; uint32_t size; @@ -3397,7 +3404,7 @@ void comp_pass2_scan() { w = label->ip; // number of POPs - level = comp_prog.ptr[node->pos + (ADDRSZ + 1)]; + level = comp_prog.ptr[node->pos + (ADDRSZ + BYTE_OFFSET_IN_32)]; if (level >= label->level) { comp_prog.ptr[node->pos + (ADDRSZ + 1)] = level - label->level; } else { @@ -3638,7 +3645,7 @@ void comp_pass2_scan() { case kwFUNC_RETURN: // address for the FUNCs kwTYPE_RET - level = comp_prog.ptr[node->pos + 1]; + level = comp_prog.ptr[node->pos + BYTE_OFFSET_IN_32]; true_ip = comp_search_bc_stack(i + 1, kwTYPE_RET, level, -1); if (true_ip != INVALID_ADDR) { // otherwise error handled elsewhere From bf1a5078166388b93f6ef91ada408a7470aaa7b0 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 6 Oct 2018 08:37:34 +1000 Subject: [PATCH 11/32] ANDROID: remove incorrect CPU_BIGENDIAN define --- configure.ac | 1 - src/platform/android/jni/main.cpp | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 277dc27c..dca33692 100644 --- a/configure.ac +++ b/configure.ac @@ -224,7 +224,6 @@ function buildAndroid() { AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) AC_DEFINE(IMPL_DEV_DELAY, 1, [Driver implements dev_delay()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) - AC_DEFINE(CPU_BIGENDIAN, 1, [Android uses big-endian]) BUILD_SUBDIRS="src/platform/android" AC_SUBST(BUILD_SUBDIRS) diff --git a/src/platform/android/jni/main.cpp b/src/platform/android/jni/main.cpp index d85af85a..51fe7c93 100644 --- a/src/platform/android/jni/main.cpp +++ b/src/platform/android/jni/main.cpp @@ -8,6 +8,11 @@ #include "config.h" #include "platform/android/jni/runtime.h" +#include + +#if _BYTE_ORDER != _LITTLE_ENDIAN || defined (__BIG_ENDIAN_BITFIELD) + #error "Big-Endian Arch is not supported" +#endif void android_main(android_app *app) { logEntered(); From fc10ac377257dd0226bdf8b0ea5f070991edc7a1 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 6 Oct 2018 08:43:46 +1000 Subject: [PATCH 12/32] COMMON: make all test portable --- samples/distro-examples/tests/all.bas | 1 - samples/distro-examples/tests/output/all.out | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/distro-examples/tests/all.bas b/samples/distro-examples/tests/all.bas index c9dbe896..61f8e0d1 100644 --- a/samples/distro-examples/tests/all.bas +++ b/samples/distro-examples/tests/all.bas @@ -140,7 +140,6 @@ print "DEG:" + DEG (x) print "DETERM:"' + DETERM (A, 1) print "DISCLOSE:" + DISCLOSE("{debraceme}", "{}") print "ENCLOSE:" + ENCLOSE ("braceme", "{}") -print "ENV:" + ENV("DISPLAY") print "EOF:"' + EOF (fileN) print "EXIST:"' + EXIST (file) print "EXP:" + EXP (x) diff --git a/samples/distro-examples/tests/output/all.out b/samples/distro-examples/tests/output/all.out index 5892dde3..caca2746 100644 --- a/samples/distro-examples/tests/output/all.out +++ b/samples/distro-examples/tests/output/all.out @@ -125,7 +125,6 @@ DEG:704.73808801091252 DETERM: DISCLOSE:debraceme ENCLOSE:{braceme} -ENV::0.0 EOF: EXIST: EXP:219695.98867213790072 @@ -172,7 +171,7 @@ POINT:0 POLYAREA:0 POLYCENT: POW:12.3 -PROGLINE:190 +PROGLINE:189 PTDISTLN:0 PTDISTSEG:0 PTSIGN:0 From 1e3f4979a64917aa84c1f87d6f6a2df8c7f01a58 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 7 Oct 2018 07:07:40 +1000 Subject: [PATCH 13/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- samples/distro-examples/tests/ongoto.bas | 7 ++++--- src/common/scan.c | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/samples/distro-examples/tests/ongoto.bas b/samples/distro-examples/tests/ongoto.bas index 1daee8c6..f7fab9b4 100644 --- a/samples/distro-examples/tests/ongoto.bas +++ b/samples/distro-examples/tests/ongoto.bas @@ -1,3 +1,7 @@ +rem test "OUT OF ADDRESS SPACE" error with incorrect kw optimisation +goto 50 +a=b +50 a=b for i=0 to 5 on i gosub 10,20, 30 , 40 @@ -12,6 +16,3 @@ return return 40 ? "40" return - - - diff --git a/src/common/scan.c b/src/common/scan.c index 36e16c0e..b4467ebc 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3403,6 +3403,11 @@ void comp_pass2_scan() { label = comp_labtable.elem[label_id]; w = label->ip; + // adjust the address to compensate for optimisation to remove adjoining kwEOC + if (comp_prog.ptr[w] != kwTYPE_LINE && comp_prog.ptr[w - 1] == kwTYPE_LINE) { + w--; + } + // number of POPs level = comp_prog.ptr[node->pos + (ADDRSZ + BYTE_OFFSET_IN_32)]; if (level >= label->level) { From 97c33ba93164ae9948e96a748ef47ca92ebc0595 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 7 Oct 2018 11:54:17 +1000 Subject: [PATCH 14/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- samples/distro-examples/tests/ongoto.bas | 8 +++++++ src/common/brun.c | 27 +++++++++++++----------- src/common/scan.c | 5 +++++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/samples/distro-examples/tests/ongoto.bas b/samples/distro-examples/tests/ongoto.bas index f7fab9b4..cce73fdb 100644 --- a/samples/distro-examples/tests/ongoto.bas +++ b/samples/distro-examples/tests/ongoto.bas @@ -3,6 +3,14 @@ goto 50 a=b 50 a=b +rem a terrible alternative to select/case +a = 1 +100 +ON a GOTO 110,120,130 +110 a = 2:GOTO 100 +120 a = 3:GOTO 100 +130 + for i=0 to 5 on i gosub 10,20, 30 , 40 next i diff --git a/src/common/brun.c b/src/common/brun.c index 590614d7..e5def045 100644 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -694,6 +694,20 @@ static inline void bc_loop_end() { prog_error = errEnd; } +void bc_loop_goto() { + bcip_t next_ip = code_getaddr(); + + // clear the stack + byte pops = code_getnext(); + while (pops > 0) { + code_pop_and_free(); + pops--; + } + + // jump + prog_ip = next_ip; +} + /** * execute commands (loop) * @@ -703,7 +717,6 @@ static inline void bc_loop_end() { */ void bc_loop(int isf) { byte pops; - bcip_t next_ip; int i; int proc_level = 0; byte code = 0; @@ -781,17 +794,7 @@ void bc_loop(int isf) { cmd_packed_let(); break; case kwGOTO: - next_ip = code_getaddr(); - - // clear the stack (whatever you can) - pops = code_getnext(); - while (pops > 0) { - code_pop_and_free(); - pops--; - } - - // jump - prog_ip = next_ip; + bc_loop_goto(); continue; case kwGOSUB: cmd_gosub(); diff --git a/src/common/scan.c b/src/common/scan.c index b4467ebc..d79bf1ae 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3389,6 +3389,11 @@ void comp_pass2_scan() { memcpy(&label_id, comp_prog.ptr + node->pos + (j * ADDRSZ) + (ADDRSZ + ADDRSZ + 3), ADDRSZ); label = comp_labtable.elem[label_id]; w = label->ip; + + // adjust the address to compensate for optimisation to remove adjoining kwEOC + if (comp_prog.ptr[w] != kwTYPE_LINE && comp_prog.ptr[w - 1] == kwTYPE_LINE) { + w--; + } memcpy(comp_prog.ptr + node->pos + (j * ADDRSZ) + (ADDRSZ + ADDRSZ + 3), &w, ADDRSZ); } break; From 49b3f6ab406a8f2b4950d3a5107a1ab110699bcd Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Tue, 9 Oct 2018 07:17:34 +1000 Subject: [PATCH 15/32] COMMON: fix crash when passing zero as format argument to DATEFMT --- ChangeLog | 3 +++ samples/distro-examples/tests/all.bas | 2 +- samples/distro-examples/tests/unx_benchmarks.bas | 1 + src/common/blib_func.c | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 49488169..68ac47c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2018-10-09 (0.12.14) + COMMON: fix crash when passing zero as format argument to DATEFMT + 2018-09-30 (0.12.14) COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs (or kwTYPE_LINE) diff --git a/samples/distro-examples/tests/all.bas b/samples/distro-examples/tests/all.bas index 61f8e0d1..b237d708 100644 --- a/samples/distro-examples/tests/all.bas +++ b/samples/distro-examples/tests/all.bas @@ -134,7 +134,7 @@ print "CREAL:" + CREAL (x) print "CSC:" + CSC (x) print "CSCH:" + CSCH (x) print "DATE:"' + DATE -print "DATEFMT:" + DATEFMT("ddmmyy", 12345) + " " + DATEFMT("yyymmdd", d,m,y) +print "DATEFMT:" + DATEFMT("ddmmyy", 12345) + " " + DATEFMT("yyymmdd", d,m,y): xx=datefmt(0,date) print "DEFINEKEY:"' + DEFINEKEY k,sub print "DEG:" + DEG (x) print "DETERM:"' + DETERM (A, 1) diff --git a/samples/distro-examples/tests/unx_benchmarks.bas b/samples/distro-examples/tests/unx_benchmarks.bas index 4c09002e..b4e934a8 100644 --- a/samples/distro-examples/tests/unx_benchmarks.bas +++ b/samples/distro-examples/tests/unx_benchmarks.bas @@ -3,6 +3,7 @@ ' st=ticks +tickspersec=1000 for i=1 to 1000000:next et=ticks ? "FOR speed: "; ((et-st)/tickspersec); "sec "; round(1000000/((et-st)/tickspersec));" l/s" diff --git a/src/common/blib_func.c b/src/common/blib_func.c index 3fb00e57..da5e5e80 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -323,7 +323,7 @@ char *date_fmt(char *fmt, long d, long m, long y) { mc = 0; yc = 0; p = fmt; - if (!(*p)) { + if (p == NULL || !(*p)) { return str.buf; } while (1) { From 69a0a048a947ea8c47d8c9afddc90177bdbd49a5 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 13 Oct 2018 16:43:45 +1000 Subject: [PATCH 16/32] COMMON: fix about page logo --- .../android/app/src/main/assets/main.bas | 32 +++++++++++++------ src/platform/android/build.gradle | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index e6d1f3d9..8c60f316 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -78,14 +78,17 @@ func mk_scratch() return result end -sub do_okay_button() +sub do_okay_button(bn_extra) local frm, button button.label = "[Close]" button.x = (xmax - txtw(button.label)) / 2 - button.y = -1 + button.y = ypos * char_h button.backgroundColor = 0 button.color = 3 button.type = "link" + if (ismap(bn_extra)) then + frm.inputs << bn_extra + endif frm.inputs << button frm = form(frm) print @@ -98,12 +101,12 @@ sub do_about() if (char_w * 45 < xmax) then print " ____ _______ ___ _____________" print " / ____ _ ___ _/ / / _ )/ _ | / __/ _/ ___/" - print " _\ \/ ' / _ `/ / / _ / __ |_\ \_/ // /__ " - print "/___/_/_/_\_,_/_/_/____/_/ |_/___/___/\___/ " + print " _\\ \\/ ' / _ `/ / / _ / __ |_\\ \\_/ // /" + print "/___/_/_/_\\_,_/_/_/____/_/ |_/___/___/\\___/" else - print " __ _ ___ _" - print "(_ ._ _ _.|||_) /\ (_ |/ " - print "__)| | |(_||||_)/--\__)|\_" + print " __ _ ___ _ " + print "(_ ._ _ _.|||_) /\\ (_ |/ " + print "__)| | |(_||||_)/--\\__)|\\_" endif print color 7,0 @@ -111,7 +114,16 @@ sub do_about() print print "Copyright (c) 2002-2018 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nicholas Christopoulos" + chr(10) - print "https://smallbasic.github.io" + chr(10) + + local bn_home + bn_home.x = 2 + bn_home.y = ypos * char_h + bn_home.type = "link" + bn_home.isExternal = true + bn_home.label = "https://smallbasic.github.io" + bn_home.color = 3 + print:print + color colGrey,0 print "SmallBASIC comes with ABSOLUTELY NO WARRANTY. "; print "This program is free software; you can use it "; @@ -121,7 +133,7 @@ sub do_about() print color 7,0 server_info() - do_okay_button() + do_okay_button(bn_home) cls end @@ -501,7 +513,7 @@ sub manageFiles() for i = 0 to len_buffer print buffer(i) next i - do_okay_button + do_okay_button(nil) wnd.graphicsScreen1() f.value = selectedFile endIf diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index 21b3539c..ef83891a 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' + classpath 'com.android.tools.build:gradle:3.2.1' classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0" } } From aca49b372db2abb083f862988865b67b987a5c17 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 13 Oct 2018 16:44:24 +1000 Subject: [PATCH 17/32] COMMON: fix about page logo --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 68ac47c8..873d4a82 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2018-10-13 (0.12.14) + COMMON: fix about page logo + 2018-10-09 (0.12.14) COMMON: fix crash when passing zero as format argument to DATEFMT From cb4a0a4c1420772745b803a3717e23f217afe10b Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 14 Oct 2018 09:44:00 +1000 Subject: [PATCH 18/32] ANDROID: bump version --- src/platform/android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 5180e829..9771e0fb 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 16 targetSdkVersion 27 - versionCode 29 - versionName "0.12.14" + versionCode 30 + versionName "0.12.14.1" resConfigs "en" } From 964b0d9ed63b8a74c2218908554635d830b9e857 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Tue, 16 Oct 2018 19:36:04 +1000 Subject: [PATCH 19/32] COMMON: fix iOS compile error --- src/common/extlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/extlib.c b/src/common/extlib.c index 7b4777b5..51dc58fe 100644 --- a/src/common/extlib.c +++ b/src/common/extlib.c @@ -22,7 +22,7 @@ #define WIN_EXTLIB #define LIB_EXT ".dll" #define DEFAULT_PATH "c:/sbasic/lib" -#elif defined(__linux__) || defined(__midipix__) && defined(_UnixOS) +#elif (defined(__linux__) || defined(__midipix__) || defined(__MACH__)) && defined(_UnixOS) #include #define LNX_EXTLIB #define LIB_EXT ".so" From 60776ecd862875cf65fe003feff1b2a55e58995b Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 20 Oct 2018 14:21:46 +1000 Subject: [PATCH 20/32] UI: changed display of BLACK to be a slightly ligher onyx colour. --- ChangeLog | 5 +++++ src/common/inet2.c | 6 ++++-- src/platform/android/app/src/main/assets/main.bas | 3 ++- src/ui/inputs.cpp | 2 +- src/ui/inputs.h | 3 ++- src/ui/system.cpp | 7 +++++++ src/ui/utils.h | 2 +- 7 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 873d4a82..d1544dab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2018-10-20 (0.12.14) + UI: fix navigation when network access down then available + UI: changed display of "BLACK" to be a slightly ligher onyx colour. + ANDROID: update help tip in scratch window + 2018-10-13 (0.12.14) COMMON: fix about page logo diff --git a/src/common/inet2.c b/src/common/inet2.c index b86fb32f..dc764ae5 100644 --- a/src/common/inet2.c +++ b/src/common/inet2.c @@ -312,10 +312,12 @@ socket_t net_listen(int server_port) { * disconnect the given network connection */ void net_disconnect(socket_t s) { + if (s != -1) { #if defined(_Win32) - closesocket(s); + closesocket(s); #else - close(s); + close(s); #endif + } } diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 8c60f316..1fc00732 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -58,12 +58,13 @@ func mk_scratch() text << "rem Welcome to SmallBASIC" text << "rem" if (is_sdl) then - text << "rem Press F1 for keyword help." + text << "rem Press F1 or F2 for keyword help." text << "rem Press and hold Ctrl then press 'h' for editor help." text << "rem Press and hold Ctrl then press 'r' to RUN this program." text << "rem Click the right mouse button for menu options." else text << "rem Press the 3 vertical dots for menu options." + text << "rem Press and drag the line numbers to scroll." endif try tsave scratch_file, text diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index 9248133b..ad392970 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -56,7 +56,7 @@ int get_color(var_p_t value, int def) { } else if (n[0] == '#' && n[1]) { result = strtol(n + 1, NULL, 16); } else if (strcasecmp(n, "black") == 0) { - result = 0; + result = DEFAULT_BACKGROUND; } else if (strcasecmp(n, "red") == 0) { result = 0x800000; } else if (strcasecmp(n, "green") == 0) { diff --git a/src/ui/inputs.h b/src/ui/inputs.h index a787960f..077f13df 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -16,9 +16,10 @@ #include "ui/strlib.h" #include "ui/shape.h" #include "ui/image.h" +#include "ui/utils.h" const uint32_t colors[] = { - 0x000000, // 0 black + DEFAULT_BACKGROUND, // 0 black 0x000080, // 1 blue 0x008000, // 2 green 0x008080, // 3 cyan diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 2295790c..bd9a7496 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -491,6 +491,13 @@ char *System::loadResource(const char *fileName) { opt_file_permitted = 0; } } + if (buffer == NULL) { + // remove failed item from history + strlib::String *old = _history.peek(); + if (old && old->equals(fileName)) { + delete _history.pop(); + } + } return buffer; } diff --git a/src/ui/utils.h b/src/ui/utils.h index 0c492a28..de17c041 100644 --- a/src/ui/utils.h +++ b/src/ui/utils.h @@ -17,7 +17,7 @@ #endif #define DEFAULT_FOREGROUND 0xa1a1a1 -#define DEFAULT_BACKGROUND 0 +#define DEFAULT_BACKGROUND 0x121212 #define HANDLE_SCREEN_BUFFER HANDLE_SCREEN #define USER_MESSAGE_EXIT 1000 From f5f4773831cd305fd4f71c9701bdefd4e6c46d6e Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 20 Oct 2018 16:33:16 +1000 Subject: [PATCH 21/32] COMMON: fix backslash escape regression --- samples/distro-examples/tests/strings.bas | 5 +++++ src/common/scan.c | 11 +++++++---- src/platform/android/app/src/main/assets/main.bas | 10 +++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/samples/distro-examples/tests/strings.bas b/samples/distro-examples/tests/strings.bas index 3d8093e6..02e422e6 100644 --- a/samples/distro-examples/tests/strings.bas +++ b/samples/distro-examples/tests/strings.bas @@ -176,3 +176,8 @@ if expect != seq(0, 2*pi, 360/15+1) then throw "SEQ error" s="Hello\033There" if (27 != asc(mid(s, 6, 1))) then throw "err" +s="This\033T\ha\t\The\"Other" +rem ThisnT\ha\t\The"Other - non escaping '\' should appear verbatim +if len(s) != 21 then throw "escape error" +s="Hello\03There" +if len(s) != 13 then throw "escape error" diff --git a/src/common/scan.c b/src/common/scan.c index d79bf1ae..ac80f26a 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3924,18 +3924,21 @@ const char *format_numeric_text(const char *str, char **output) { int value = 0; int digits = 0; - while (isdigit(*str)) { - value = (value << 3) + (*str - '0'); + while (isdigit(*result)) { + value = (value << 3) + (*result - '0'); digits++; - str++; + result++; } if (digits == 3 && value > V_JOIN_LINE && value < 256) { **output = value; - (*output)++; + } else { + **output = *str; result = str; } + (*output)++; + return result; } diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 1fc00732..55cf116b 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -102,12 +102,12 @@ sub do_about() if (char_w * 45 < xmax) then print " ____ _______ ___ _____________" print " / ____ _ ___ _/ / / _ )/ _ | / __/ _/ ___/" - print " _\\ \\/ ' / _ `/ / / _ / __ |_\\ \\_/ // /" - print "/___/_/_/_\\_,_/_/_/____/_/ |_/___/___/\\___/" + print " _\ \/ ' / _ `/ / / _ / __ |_\ \_/ // /__ " + print "/___/_/_/_\_,_/_/_/____/_/ |_/___/___/\___/ " else - print " __ _ ___ _ " - print "(_ ._ _ _.|||_) /\\ (_ |/ " - print "__)| | |(_||||_)/--\\__)|\\_" + print " __ _ ___ _" + print "(_ ._ _ _.|||_) /\ (_ |/ " + print "__)| | |(_||||_)/--\__)|\_" endif print color 7,0 From 3a59e22d431ba08d6a9ab06fa64fd4418ca52f97 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 21 Oct 2018 08:12:58 +1000 Subject: [PATCH 22/32] COMMON: fix backslash escape regression --- samples/distro-examples/tests/strings.bas | 10 +++++----- src/common/scan.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/distro-examples/tests/strings.bas b/samples/distro-examples/tests/strings.bas index 02e422e6..73dd682e 100644 --- a/samples/distro-examples/tests/strings.bas +++ b/samples/distro-examples/tests/strings.bas @@ -176,8 +176,8 @@ if expect != seq(0, 2*pi, 360/15+1) then throw "SEQ error" s="Hello\033There" if (27 != asc(mid(s, 6, 1))) then throw "err" -s="This\033T\ha\t\The\"Other" -rem ThisnT\ha\t\The"Other - non escaping '\' should appear verbatim -if len(s) != 21 then throw "escape error" -s="Hello\03There" -if len(s) != 13 then throw "escape error" +rem Non escaping '\' should appear verbatim +s= "a\c\e" +if mid(s, 2, 1) != "\\" then throw s +if mid(s, 4, 1) != "\\" then throw s + diff --git a/src/common/scan.c b/src/common/scan.c index ac80f26a..b97ea9ae 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3920,7 +3920,7 @@ char *comp_load(const char *file_name) { } const char *format_numeric_text(const char *str, char **output) { - const char *result = str; + const char *result = str + 1; int value = 0; int digits = 0; @@ -3934,7 +3934,7 @@ const char *format_numeric_text(const char *str, char **output) { **output = value; } else { **output = *str; - result = str; + result = str + 1; } (*output)++; @@ -4151,7 +4151,7 @@ char *comp_format_text(const char *source) { // new line auto-ends the quoted string quotes = !quotes; } else if (*p == '\\') { - p = format_numeric_text(p + 1, &ps); + p = format_numeric_text(p, &ps); continue; } *ps++ = *p++; From f3ea9701a097e0c824e7e9d90a22ad8e986e8278 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Wed, 24 Oct 2018 21:22:56 +1000 Subject: [PATCH 23/32] UI: set widget default colours from output --- src/ui/form.cpp | 1 + src/ui/inputs.cpp | 11 +++++++++-- src/ui/inputs.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ui/form.cpp b/src/ui/form.cpp index 819036f4..ee3feac6 100644 --- a/src/ui/form.cpp +++ b/src/ui/form.cpp @@ -282,6 +282,7 @@ extern "C" void v_create_form(var_p_t var) { } if (hasInputs) { + set_input_defaults(out->getColor(), out->getBackgroundColor()); map_set(var, arg); var_p_t v_focus = map_get(var, FORM_FOCUS); unsigned i_focus = v_focus != NULL ? v_getint(v_focus) : -1; diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index ad392970..1ec34f40 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -21,6 +21,8 @@ extern System *g_system; FormList *activeList = NULL; FormInput *focusInput = NULL; FormEditInput *focusEdit = NULL; +int background = DEFAULT_BACKGROUND; +int foreground = DEFAULT_FOREGROUND; #define LINE_Y (_height - 2) #define LINE_W (_width - 2) @@ -39,6 +41,11 @@ bool form_ui::optionSelected(int index) { return result; } +void set_input_defaults(int fg, int bg) { + foreground = fg; + background = bg; +} + int get_color(var_p_t value, int def) { int result = def; if (value != NULL && value->type == V_INT) { @@ -107,8 +114,8 @@ FormInput::FormInput(int x, int y, int w, int h) : _visible(true), _noFocus(false), _resizable(false), - _bg(DEFAULT_BACKGROUND), - _fg(DEFAULT_FOREGROUND), + _bg(background), + _fg(foreground), _onclick(0) { } diff --git a/src/ui/inputs.h b/src/ui/inputs.h index 077f13df..521173dc 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -75,6 +75,7 @@ namespace form_ui { bool optionSelected(int index); }; +void set_input_defaults(int fg, int bg); int get_color(var_p_t value, int def); struct IFormWidgetListModel { From b5b794b6cfc9bccffeb16539c26fe4eb3a4ad1f8 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 28 Oct 2018 09:20:41 +1000 Subject: [PATCH 24/32] UI: fix colour handling --- .../android/app/src/main/assets/main.bas | 41 +++++++++++-------- src/ui/system.cpp | 11 ++++- src/ui/textedit.cpp | 6 +-- src/ui/utils.h | 2 +- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/platform/android/app/src/main/assets/main.bas b/src/platform/android/app/src/main/assets/main.bas index 55cf116b..262a0ef2 100644 --- a/src/platform/android/app/src/main/assets/main.bas +++ b/src/platform/android/app/src/main/assets/main.bas @@ -7,7 +7,10 @@ const char_w = txtw(".") const lineSpacing = 2 + char_h const idxEdit = 6 const idxFiles = 7 -const colGrey = rgb(100,100,100) +const colGrey = rgb(100, 100, 100) +const colBkGnd = rgb(31, 28, 31) +const colText = 2 +const colLink = 3 const menu_gap = -(char_w / 2) const is_sdl = instr(sbver, "SDL") != 0 const onlineUrl = "http://smallbasic.github.io/samples/index.bas" @@ -43,8 +46,7 @@ func mk_menu(value, lab, x) bn.y = ypos * char_h bn.value = value bn.label = "[" + lab + "]" - bn.color = 3 - bn.backgroundColor = 0 + bn.color = colLink bn.type = "link" mk_menu = bn end @@ -84,8 +86,7 @@ sub do_okay_button(bn_extra) button.label = "[Close]" button.x = (xmax - txtw(button.label)) / 2 button.y = ypos * char_h - button.backgroundColor = 0 - button.color = 3 + button.color = colLink button.type = "link" if (ismap(bn_extra)) then frm.inputs << bn_extra @@ -96,9 +97,14 @@ sub do_okay_button(bn_extra) frm.doEvents() end +sub clear_screen() + color colText, colBkGnd + cls +end + sub do_about() cls - color 2,0 + color colText if (char_w * 45 < xmax) then print " ____ _______ ___ _____________" print " / ____ _ ___ _/ / / _ )/ _ | / __/ _/ ___/" @@ -110,7 +116,7 @@ sub do_about() print "__)| | |(_||||_)/--\__)|\_" endif print - color 7,0 + color 7 print "Version "; sbver print print "Copyright (c) 2002-2018 Chris Warren-Smith" @@ -122,26 +128,26 @@ sub do_about() bn_home.type = "link" bn_home.isExternal = true bn_home.label = "https://smallbasic.github.io" - bn_home.color = 3 + bn_home.color = colLink print:print - color colGrey,0 + color colGrey print "SmallBASIC comes with ABSOLUTELY NO WARRANTY. "; print "This program is free software; you can use it "; print "redistribute it and/or modify it under the terms of the "; print "GNU General Public License version 2 as published by "; print "the Free Software Foundation." + chr(10) print - color 7,0 + color 7 server_info() do_okay_button(bn_home) - cls + clear_screen() end sub do_setup() local frm - color 3, 0 + color 3 cls print boldOn + "Setup web service port number." print boldOff @@ -163,7 +169,7 @@ sub do_setup() env("serverToken=" + token) endif - color 3, 0 + color 3 cls print "Web service port number: " + env("serverSocket") print @@ -187,8 +193,7 @@ sub do_setup() local msg = "You must restart SmallBASIC for this change to take effect." local wnd = window() wnd.alert(msg, "Restart required") - color 7, 0 - cls + clear_screen() end sub server_info() @@ -422,7 +427,7 @@ sub manageFiles() bn_files.y = bn_edit.y + char_h + 2 bn_files.height = ymax - bn_files.y bn_files.width = xmax - x1 - bn_files.color = 2 + bn_files.color = colText bn_files.type = "list" bn_files.resizable = TRUE bn_files.help = "No .bas files in " + cwd @@ -508,13 +513,14 @@ sub manageFiles() else tload selectedFile, buffer wnd.graphicsScreen2() + color 7 cls - color 7,0 len_buffer = len(buffer) - 1 for i = 0 to len_buffer print buffer(i) next i do_okay_button(nil) + clear_screen() wnd.graphicsScreen1() f.value = selectedFile endIf @@ -649,6 +655,7 @@ sub main path = backPath end + clear_screen() path = cwd frm = makeUI(path, sortDir) diff --git a/src/ui/system.cpp b/src/ui/system.cpp index bd9a7496..33c79b89 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -537,8 +537,15 @@ char *System::readSource(const char *fileName) { buffer = (char *)malloc(len + 1); len = read(h, buffer, len); buffer[len] = '\0'; - _activeFile = fileName; - _modifiedTime = getModifiedTime(); + _modifiedTime = st.st_mtime; + char fullPath[PATH_MAX + 1]; + char *path = realpath(fileName, fullPath); + if (path != NULL) { + // set full path for getModifiedTime() + _activeFile = fullPath; + } else { + _activeFile = fileName; + } } close(h); } diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index b61fac87..f371c138 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -978,11 +978,7 @@ void TextEditInput::updateField(var_p_t form) { bool TextEditInput::updateUI(var_p_t form, var_p_t field) { bool updated = (form && field) ? FormInput::updateUI(form, field) : false; if (!_theme) { - if (_fg == DEFAULT_FOREGROUND && _bg == DEFAULT_BACKGROUND) { - _theme = new EditTheme(); - } else { - _theme = new EditTheme(_fg, _bg); - } + _theme = new EditTheme(); updated = true; } return updated; diff --git a/src/ui/utils.h b/src/ui/utils.h index de17c041..0c492a28 100644 --- a/src/ui/utils.h +++ b/src/ui/utils.h @@ -17,7 +17,7 @@ #endif #define DEFAULT_FOREGROUND 0xa1a1a1 -#define DEFAULT_BACKGROUND 0x121212 +#define DEFAULT_BACKGROUND 0 #define HANDLE_SCREEN_BUFFER HANDLE_SCREEN #define USER_MESSAGE_EXIT 1000 From bcdfc01c284a514c2b21be05dd5ff9cca753c140 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Thu, 1 Nov 2018 05:26:28 +1000 Subject: [PATCH 25/32] COMMON: fix broken implementation to avoid appending multiple kwTYPE_EOCs --- samples/distro-examples/tests/output/uds.out | 1 + samples/distro-examples/tests/uds.bas | 25 ++++++++++- src/common/bc.c | 26 ++++++----- src/common/bc.h | 20 ++++----- src/common/blib.c | 6 +++ src/common/scan.c | 46 ++++++++++++-------- 6 files changed, 86 insertions(+), 38 deletions(-) diff --git a/samples/distro-examples/tests/output/uds.out b/samples/distro-examples/tests/output/uds.out index af45a2fd..01fafa41 100644 --- a/samples/distro-examples/tests/output/uds.out +++ b/samples/distro-examples/tests/output/uds.out @@ -10,3 +10,4 @@ a.xfish.small=small a.xfish.big=big 3 2 +10 diff --git a/samples/distro-examples/tests/uds.bas b/samples/distro-examples/tests/uds.bas index ee0ad301..22727a93 100644 --- a/samples/distro-examples/tests/uds.bas +++ b/samples/distro-examples/tests/uds.bas @@ -141,4 +141,27 @@ rem fill the var cache for testing in valgrind cache = {} for i = 0 to 8096 cache[i] = "." -next i \ No newline at end of file +next i + +' +' regression modifying kwTYPE_EOC +' +func GridClass() + sub setCellValue(a) + ? a + end + local result = {} + result.setCellValue = @setCellValue + return result +end +func Game() + sub start() + self.grid.setCellValue(10) + end + local result = {} + result.grid = GridClass() + result.start = @start + return result +end +g = Game() +g.start() diff --git a/src/common/bc.c b/src/common/bc.c index bc8b1616..c5498db8 100644 --- a/src/common/bc.c +++ b/src/common/bc.c @@ -9,9 +9,6 @@ #include "common/bc.h" #include "common/smbas.h" -#if defined(_UnixOS) -#include -#endif /* * Create a bytecode segment @@ -34,6 +31,8 @@ void bc_destroy(bc_t *bc) { bc->size = 0; bc->count = 0; bc->cp = 0; + bc->eoc_position = 0; + bc->line_position = 0; } /* @@ -60,13 +59,6 @@ void bc_add1(bc_t *bc, char code) { bc->count++; } -/* - * change one command - */ -void bc_store1(bc_t *bc, bcip_t offset, byte code) { - bc->ptr[offset] = code; -} - /* * add one uint32_t */ @@ -254,6 +246,20 @@ void bc_eoc(bc_t *bc) { } } +/* + * pops any EOC mark at the current position + */ +int bc_pop_eoc(bc_t *bc) { + int result; + if (bc->eoc_position > 0 && bc->eoc_position == bc->count - 1) { + bc->eoc_position = 0; + result = (bc->ptr[--bc->count] == kwTYPE_EOC); + } else { + result = 0; + } + return result; +} + /* * appends the src to dst */ diff --git a/src/common/bc.h b/src/common/bc.h index cdda1ebc..3ae11019 100644 --- a/src/common/bc.h +++ b/src/common/bc.h @@ -81,16 +81,6 @@ void bc_resize(bc_t *bc, uint32_t newsize); */ void bc_add1(bc_t *bc, char code); -/** - * @ingroup scan - * - * put 1 byte to specified offset - * - * @param bc the bc structure - * @param code the byte - */ -void bc_store1(bc_t *bc, bcip_t offset, byte code); - /** * @ingroup scan * @@ -128,6 +118,16 @@ char *bc_store_string(bc_t *bc, char *src); */ void bc_eoc(bc_t *bc); +/** + * @ingroup scan + * + * pops any EOC mark at the current position + * + * @param bc the bc segment + * @return whether kwTYPE_EOC was popped + */ +int bc_pop_eoc(bc_t *bc); + /** * @ingroup scan * diff --git a/src/common/blib.c b/src/common/blib.c index d40685f2..b724886e 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -1009,6 +1009,9 @@ bcip_t cmd_push_args(int cmd, bcip_t goto_addr, bcip_t rvid) { do { byte code = code_peek(); // get next BC switch (code) { + case kwTYPE_LINE: + ready = 1; // finish flag + break; case kwTYPE_EOC: // end of an expression (parameter) code_skipnext(); // ignore it break; @@ -1097,6 +1100,9 @@ void cmd_call_unit_udp(int cmd, int udp_tid, bcip_t goto_addr, bcip_t rvid) { do { byte code = code_peek(); // get next BC switch (code) { + case kwTYPE_LINE: + ready = 1; // finish flag + break; case kwTYPE_EOC: // end of an expression (parameter) code_skipnext(); // ignore it break; diff --git a/src/common/scan.c b/src/common/scan.c index b97ea9ae..63e1cbd5 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -1849,7 +1849,12 @@ char *comp_array_params(char *src, char exitChar) { *se = '\0'; bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); comp_expression(ss, 0); - bc_store1(&comp_prog, comp_prog.count - 1, kwTYPE_LEVEL_END); + // overwrite kwTYPE_EOC with kwTYPE_LEVEL_END + if (!bc_pop_eoc(&comp_prog)) { + sc_raise(ERR_UNSUPPORTED); + } + bc_add_code(&comp_prog, kwTYPE_LEVEL_END); + comp_prog.eoc_position = 0; *ss = ssSave; *se = seSave; ss = se = NULL; @@ -2812,6 +2817,24 @@ int comp_text_line_command(bid_t idx, int decl, int sharp, char *last_cmd) { return result; } +void add_line_no() { + if (comp_prog.line_position == 0 || + comp_prog.line_position != (comp_prog.count - KW_TYPE_LINE_BYTES)) { + // not an adjoining kwTYPE_LINE + if (!opt_autolocal && comp_prog.eoc_position == comp_prog.count - 1) { + // overwrite any adjoining kwTYPE_EOC (can't do this with autolocal) + if (!bc_pop_eoc(&comp_prog)) { + sc_raise(ERR_UNSUPPORTED); + } + } + + // prevent adjoining kwTYPE_LINEs + comp_prog.line_position = comp_prog.count; + bc_add_code(&comp_prog, kwTYPE_LINE); + bc_add_addr(&comp_prog, comp_line); + } +} + /* * Pass 1: scan source line */ @@ -2870,20 +2893,7 @@ void comp_text_line(char *text, int addLineNo) { return; } if (addLineNo) { - // add debug info: line-number - if (comp_prog.line_position == 0 || - comp_prog.line_position != (comp_prog.count - KW_TYPE_LINE_BYTES)) { - // not an adjoining kwTYPE_LINE - if (!opt_autolocal && comp_prog.eoc_position == comp_prog.count - 1) { - // overwrite any adjoining kwTYPE_EOC (can't do this with autolocal) - comp_prog.count--; - } - // prevent kwTYPE_EOC from being appended to this kwTYPE_LINE - comp_prog.eoc_position = comp_prog.count; - comp_prog.line_position = comp_prog.count; - bc_add_code(&comp_prog, kwTYPE_LINE); - bc_add_addr(&comp_prog, comp_line); - } + add_line_no(); } if (idx == -1) { idx = comp_is_proc(comp_bc_name); @@ -3719,13 +3729,15 @@ bcip_t comp_optimise_let(bcip_t ip, byte kw_opr, char sep, byte opt_kw) { bcip_t ip_next = ip + 1; if (comp_prog.ptr[ip_next] == kwTYPE_VAR) { ip_next += 1 + sizeof(bcip_t); - while (ip_next < comp_prog.count && comp_prog.ptr[ip_next] != kwTYPE_EOC) { + while (ip_next < comp_prog.count && comp_prog.ptr[ip_next] != kwTYPE_EOC + && comp_prog.ptr[ip_next] != kwTYPE_LINE) { if (comp_prog.ptr[ip_next] == kw_opr && comp_prog.ptr[ip_next + 1] == sep) { ip_next += 2; if (ip_next < comp_prog.count && comp_prog.ptr[ip_next] == kwTYPE_VAR && - comp_prog.ptr[ip_next + 1 + sizeof(bcip_t)] == kwTYPE_EOC) { + (comp_prog.ptr[ip_next + 1 + sizeof(bcip_t)] == kwTYPE_EOC || + comp_prog.ptr[ip_next + 1 + sizeof(bcip_t)] == kwTYPE_LINE)) { comp_prog.ptr[ip] = opt_kw; ip = ip_next; } From c6c50f7b35b2e4add0307a16669ebbfcbff5de30 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 10 Nov 2018 14:56:49 +1000 Subject: [PATCH 26/32] bump lodepng version --- src/lib/lodepng.c | 1010 ++++++++++++++++++++++++++++++++++++--------- src/lib/lodepng.h | 217 ++++++++-- 2 files changed, 995 insertions(+), 232 deletions(-) diff --git a/src/lib/lodepng.c b/src/lib/lodepng.c index 69989103..05287bbf 100644 --- a/src/lib/lodepng.c +++ b/src/lib/lodepng.c @@ -1,5 +1,5 @@ /* -LodePNG version 20180114 +LodePNG version 20180910 Copyright (c) 2005-2018 Lode Vandevenne @@ -39,7 +39,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ -const char* LODEPNG_VERSION_STRING = "20180114"; +const char* LODEPNG_VERSION_STRING = "20180910"; /* This source file is built up in the following large parts. The code sections @@ -62,11 +62,17 @@ from here.*/ #ifdef LODEPNG_COMPILE_ALLOCATORS static void* lodepng_malloc(size_t size) { +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif return malloc(size); } static void* lodepng_realloc(void* ptr, size_t new_size) { +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif return realloc(ptr, new_size); } @@ -86,6 +92,9 @@ void lodepng_free(void* ptr); /* ////////////////////////////////////////////////////////////////////////// */ /* ////////////////////////////////////////////////////////////////////////// */ +#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) + /* Often in case of an error a value is assigned to a variable and then it breaks out of a loop (to go to the cleanup phase of a function). This macro does that. @@ -275,42 +284,29 @@ static unsigned ucvector_push_back(ucvector* p, unsigned char c) #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -/*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned string_resize(char** out, size_t size) -{ - char* data = (char*)lodepng_realloc(*out, size + 1); - if(data) - { - data[size] = 0; /*null termination char*/ - *out = data; - } - return data != 0; -} -/*init a {char*, size_t} pair for use as string*/ -static void string_init(char** out) -{ - *out = NULL; - string_resize(out, 0); -} - -/*free the above pair again*/ +/*free string pointer and set it to NULL*/ static void string_cleanup(char** out) { lodepng_free(*out); *out = NULL; } -static void string_set(char** out, const char* in) +/* dynamically allocates a new string with a copy of the null terminated input text */ +static char* alloc_string(const char* in) { - size_t insize = strlen(in), i; - if(string_resize(out, insize)) + size_t insize = strlen(in); + char* out = (char*)lodepng_malloc(insize + 1); + if(out) { + size_t i; for(i = 0; i != insize; ++i) { - (*out)[i] = in[i]; + out[i] = in[i]; } + out[i] = 0; } + return out; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -402,7 +398,7 @@ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const FILE* file; file = fopen(filename, "wb" ); if(!file) return 79; - fwrite((char*)buffer , 1 , buffersize, file); + fwrite(buffer, 1, buffersize, file); fclose(file); return 0; } @@ -795,7 +791,7 @@ unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequen BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ - if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ + if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); if(!leaves) return 83; /*alloc fail*/ @@ -1455,11 +1451,11 @@ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned { hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; - hash->head[hashval] = wpos; + hash->head[hashval] = (int)wpos; hash->zeros[wpos] = numzeros; if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; - hash->headz[numzeros] = wpos; + hash->headz[numzeros] = (int)wpos; } /* @@ -1531,7 +1527,7 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, for(;;) { if(chainlength++ >= maxchainlength) break; - current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize; + current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ prev_offset = current_offset; @@ -2090,13 +2086,13 @@ static unsigned deflate(unsigned char** out, size_t* outsize, static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { - unsigned s1 = adler & 0xffff; - unsigned s2 = (adler >> 16) & 0xffff; + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; while(len > 0) { - /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ - unsigned amount = len > 5550 ? 5550 : len; + /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5552 ? 5552 : len; len -= amount; while(amount > 0) { @@ -2480,14 +2476,46 @@ void lodepng_chunk_generate_crc(unsigned char* chunk) unsigned char* lodepng_chunk_next(unsigned char* chunk) { - unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; - return &chunk[total_chunk_length]; + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return chunk + total_chunk_length; + } } const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) { - unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; - return &chunk[total_chunk_length]; + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return chunk + total_chunk_length; + } +} + +unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]) +{ + for(;;) + { + if(chunk + 12 >= end) return 0; + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next(chunk); + } +} + +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) +{ + for(;;) + { + if(chunk + 12 >= end) return 0; + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next_const(chunk); + } } unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) @@ -2609,6 +2637,15 @@ unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* return 0; } +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) +{ + LodePNGColorMode result; + lodepng_color_mode_init(&result); + result.colortype = colortype; + result.bitdepth = bitdepth; + return result; +} + static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { size_t i; @@ -2621,15 +2658,10 @@ static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColo if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } - /*if one of the palette sizes is 0, then we consider it to be the same as the - other: it means that e.g. the palette was not given by the user and should be - considered the same as the palette inside the PNG.*/ - if(1/*a->palettesize != 0 && b->palettesize != 0*/) { - if(a->palettesize != b->palettesize) return 0; - for(i = 0; i != a->palettesize * 4; ++i) - { - if(a->palette[i] != b->palette[i]) return 0; - } + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; } return 1; } @@ -2662,9 +2694,9 @@ unsigned lodepng_palette_add(LodePNGColorMode* info, return 0; } +/*calculate bits per pixel out of colortype and bitdepth*/ unsigned lodepng_get_bpp(const LodePNGColorMode* info) { - /*calculate bits per pixel out of colortype and bitdepth*/ return lodepng_get_bpp_lct(info->colortype, info->bitdepth); } @@ -2705,32 +2737,75 @@ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) || lodepng_has_palette_alpha(info); } -size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp(color); - size_t n = w * h; + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = (size_t)w * (size_t)h; return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } -size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { - /*will not overflow for any color type if roughly w * h < 268435455*/ - size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); - size_t n = w * h; - return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; + return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); } #ifdef LODEPNG_COMPILE_PNG #ifdef LODEPNG_COMPILE_DECODER -/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ + +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, +and in addition has one extra byte per line: the filter byte. So this gives a larger +result than lodepng_get_raw_size. */ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { - /*will not overflow for any color type if roughly w * h < 268435455*/ size_t bpp = lodepng_get_bpp(color); - size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; - return h * line; + /* + 1 for the filter byte, and possibly plus padding bits per line */ + size_t line = ((size_t)(w / 8) * bpp) + 1 + ((w & 7) * bpp + 7) / 8; + return (size_t)h * line; +} + +/* Safely check if multiplying two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_mulofl(size_t a, size_t b, size_t* result) +{ + *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ + return (a != 0 && *result / a != b); +} + +/* Safely check if adding two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_addofl(size_t a, size_t b, size_t* result) +{ + *result = a + b; /* Unsigned addition is well defined and safe in C90 */ + return *result < a; +} + +/*Safely checks whether size_t overflow can be caused due to amount of pixels. +This check is overcautious rather than precise. If this check indicates no overflow, +you can safely compute in a size_t (but not an unsigned): +-(size_t)w * (size_t)h * 8 +-amount of bytes in IDAT (including filter, padding and Adam7 bytes) +-amount of bytes in raw color model +Returns 1 if overflow possible, 0 if not. +*/ +static int lodepng_pixel_overflow(unsigned w, unsigned h, + const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) +{ + size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); + size_t numpixels, total; + size_t line; /* bytes per line in worst case */ + + if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; + if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ + + /* Bytes per scanline with the expression "(w / 8) * bpp) + ((w & 7) * bpp + 7) / 8" */ + if(lodepng_mulofl((size_t)(w / 8), bpp, &line)) return 1; + if(lodepng_addofl(line, ((w & 7) * bpp + 7) / 8, &line)) return 1; + + if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ + if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ + + return 0; /* no overflow */ } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -2825,11 +2900,8 @@ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) info->text_keys = new_keys; info->text_strings = new_strings; - string_init(&info->text_keys[info->text_num - 1]); - string_set(&info->text_keys[info->text_num - 1], key); - - string_init(&info->text_strings[info->text_num - 1]); - string_set(&info->text_strings[info->text_num - 1], str); + info->text_keys[info->text_num - 1] = alloc_string(key); + info->text_strings[info->text_num - 1] = alloc_string(str); return 0; } @@ -2904,19 +2976,41 @@ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langt info->itext_transkeys = new_transkeys; info->itext_strings = new_strings; - string_init(&info->itext_keys[info->itext_num - 1]); - string_set(&info->itext_keys[info->itext_num - 1], key); + info->itext_keys[info->itext_num - 1] = alloc_string(key); + info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); + info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); + info->itext_strings[info->itext_num - 1] = alloc_string(str); - string_init(&info->itext_langtags[info->itext_num - 1]); - string_set(&info->itext_langtags[info->itext_num - 1], langtag); + return 0; +} - string_init(&info->itext_transkeys[info->itext_num - 1]); - string_set(&info->itext_transkeys[info->itext_num - 1], transkey); +/* same as set but does not delete */ +static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) +{ + info->iccp_name = alloc_string(name); + info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size); - string_init(&info->itext_strings[info->itext_num - 1]); - string_set(&info->itext_strings[info->itext_num - 1], str); + if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/ - return 0; + memcpy(info->iccp_profile, profile, profile_size); + info->iccp_profile_size = profile_size; + + return 0; /*ok*/ +} + +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) +{ + if(info->iccp_name) lodepng_clear_icc(info); + + return lodepng_assign_icc(info, name, profile, profile_size); +} + +void lodepng_clear_icc(LodePNGInfo* info) +{ + string_cleanup(&info->iccp_name); + lodepng_free(info->iccp_profile); + info->iccp_profile = NULL; + info->iccp_profile_size = 0; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ @@ -2936,6 +3030,13 @@ void lodepng_info_init(LodePNGInfo* info) info->time_defined = 0; info->phys_defined = 0; + info->gama_defined = 0; + info->chrm_defined = 0; + info->srgb_defined = 0; + info->iccp_defined = 0; + info->iccp_name = NULL; + info->iccp_profile = NULL; + LodePNGUnknownChunks_init(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } @@ -2947,6 +3048,8 @@ void lodepng_info_cleanup(LodePNGInfo* info) LodePNGText_cleanup(info); LodePNGIText_cleanup(info); + lodepng_clear_icc(info); + LodePNGUnknownChunks_cleanup(info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } @@ -2961,6 +3064,10 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + if(source->iccp_defined) + { + CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size)); + } LodePNGUnknownChunks_init(dest); CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); @@ -2968,13 +3075,6 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) return 0; } -void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) -{ - LodePNGInfo temp = *a; - *a = *b; - *b = temp; -} - /* ////////////////////////////////////////////////////////////////////////// */ /*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ @@ -3462,7 +3562,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { size_t i; ColorTree tree; - size_t numpixels = w * h; + size_t numpixels = (size_t)w * (size_t)h; unsigned error = 0; if(lodepng_color_mode_equal(mode_out, mode_in)) @@ -3476,7 +3576,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { size_t palettesize = mode_out->palettesize; const unsigned char* palette = mode_out->palette; - size_t palsize = 1u << mode_out->bitdepth; + size_t palsize = (size_t)1u << mode_out->bitdepth; /*if the user specified output palette but did not give the values, assume they want the values of the input color type (assuming that one is palette). Note that we never create a new palette ourselves.*/ @@ -3484,13 +3584,22 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, { palettesize = mode_in->palettesize; palette = mode_in->palette; + /*if the input was also palette with same bitdepth, then the color types are also + equal, so copy literally. This to preserve the exact indices that were in the PNG + even in case there are duplicate colors in the palette.*/ + if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } } if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); for(i = 0; i != palsize; ++i) { const unsigned char* p = &palette[i * 4]; - color_tree_add(&tree, p[0], p[1], p[2], p[3], i); + color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); } } @@ -3530,6 +3639,79 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, return error; } + +/* Converts a single rgb color without alpha from one type to another, color bits truncated to +their bitdepth. In case of single channel (grey or palette), only the r channel is used. Slow +function, do not use to process all pixels of an image. Alpha channel not supported on purpose: +this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the +specification it looks like bKGD should ignore the alpha values of the palette since it can use +any palette index but doesn't have an alpha channel. Idem with ignoring color key. */ +unsigned lodepng_convert_rgb( + unsigned* r_out, unsigned* g_out, unsigned* b_out, + unsigned r_in, unsigned g_in, unsigned b_in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) +{ + unsigned r = 0, g = 0, b = 0; + unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/ + unsigned shift = 16 - mode_out->bitdepth; + + if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) + { + r = g = b = r_in * mul; + } + else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) + { + r = r_in * mul; + g = g_in * mul; + b = b_in * mul; + } + else if(mode_in->colortype == LCT_PALETTE) + { + if(r_in >= mode_in->palettesize) return 82; + r = mode_in->palette[r_in * 4 + 0] * 257u; + g = mode_in->palette[r_in * 4 + 1] * 257u; + b = mode_in->palette[r_in * 4 + 2] * 257u; + } + else + { + return 31; + } + + /* now convert to output format */ + if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) + { + *r_out = r >> shift ; + } + else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) + { + *r_out = r >> shift ; + *g_out = g >> shift ; + *b_out = b >> shift ; + } + else if(mode_out->colortype == LCT_PALETTE) + { + unsigned i; + /* a 16-bit color cannot be in the palette */ + if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82; + for(i = 0; i < mode_out->palettesize; i++) { + unsigned j = i * 4; + if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] && + (b >> 8) == mode_out->palette[j + 2]) + { + *r_out = i; + return 0; + } + } + return 82; + } + else + { + return 31; + } + + return 0; +} + #ifdef LODEPNG_COMPILE_ENCODER void lodepng_color_profile_init(LodePNGColorProfile* profile) @@ -3540,6 +3722,7 @@ void lodepng_color_profile_init(LodePNGColorProfile* profile) profile->alpha = 0; profile->numcolors = 0; profile->bits = 1; + profile->numpixels = 0; } /*function used for debug purposes with C++*/ @@ -3564,39 +3747,62 @@ static unsigned getValueRequiredBits(unsigned char value) return 8; } -/*profile must already have been inited with mode. +/*profile must already have been inited. It's ok to set some parameters of profile to done already.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* in, unsigned w, unsigned h, - const LodePNGColorMode* mode) + const LodePNGColorMode* mode_in) { unsigned error = 0; size_t i; ColorTree tree; - size_t numpixels = w * h; + size_t numpixels = (size_t)w * (size_t)h; - unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; - unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + /* mark things as done already if it would be impossible to have a more expensive case */ + unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1; unsigned numcolors_done = 0; - unsigned bpp = lodepng_get_bpp(mode); - unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned bpp = lodepng_get_bpp(mode_in); + unsigned bits_done = (profile->bits == 1 && bpp == 1) ? 1 : 0; + unsigned sixteen = 0; /* whether the input image is 16 bit */ unsigned maxnumcolors = 257; - unsigned sixteen = 0; - if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, profile->numcolors + (1u << bpp)); + + profile->numpixels += numpixels; color_tree_init(&tree); + /*If the profile was already filled in from previous data, fill its palette in tree + and mark things as done already if we know they are the most expensive case already*/ + if(profile->alpha) alpha_done = 1; + if(profile->colored) colored_done = 1; + if(profile->bits == 16) numcolors_done = 1; + if(profile->bits >= bpp) bits_done = 1; + if(profile->numcolors >= maxnumcolors) numcolors_done = 1; + + if(!numcolors_done) + { + for(i = 0; i < profile->numcolors; i++) + { + const unsigned char* color = &profile->palette[i * 4]; + color_tree_add(&tree, color[0], color[1], color[2], color[3], i); + } + } + /*Check if the 16-bit input is truly 16-bit*/ - if(mode->bitdepth == 16) + if(mode_in->bitdepth == 16 && !sixteen) { unsigned short r, g, b, a; for(i = 0; i != numpixels; ++i) { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { + profile->bits = 16; sixteen = 1; + bits_done = 1; + numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ break; } } @@ -3605,12 +3811,10 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, if(sixteen) { unsigned short r = 0, g = 0, b = 0, a = 0; - profile->bits = 16; - bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ for(i = 0; i != numpixels; ++i) { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if(!colored_done && (r != g || r != b)) { @@ -3649,7 +3853,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, { for(i = 0; i != numpixels; ++i) { - getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ @@ -3665,7 +3869,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, unsigned char r = 0, g = 0, b = 0, a = 0; for(i = 0; i != numpixels; ++i) { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); if(!bits_done && profile->bits < 8) { @@ -3735,7 +3939,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, { for(i = 0; i != numpixels; ++i) { - getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { /* Color key cannot be used if an opaque pixel also has that RGB color. */ @@ -3757,41 +3961,61 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, return error; } -/*Automatically chooses color type that gives smallest amount of bits in the -output image, e.g. grey if there are only greyscale pixels, palette if there -are less than 256 colors, ... -Updates values of mode with a potentially smaller color model. mode_out should -contain the user chosen color model, but will be overwritten with the new chosen one.*/ -unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, - const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in) +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*Adds a single color to the color profile. The profile must already have been inited. The color must be given as 16-bit +(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for +all pixels of an image but only for a few additional values. */ +static unsigned lodepng_color_profile_add(LodePNGColorProfile* profile, + unsigned r, unsigned g, unsigned b, unsigned a) { - LodePNGColorProfile prof; unsigned error = 0; - unsigned i, n, palettebits, palette_ok; + unsigned char image[8]; + LodePNGColorMode mode; + lodepng_color_mode_init(&mode); + image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g; + image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a; + mode.bitdepth = 16; + mode.colortype = LCT_RGBA; + error = lodepng_get_color_profile(profile, image, 1, 1, &mode); + lodepng_color_mode_cleanup(&mode); + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Autochoose color model given the computed profile. mode_in is to copy palette order from +when relevant.*/ +static unsigned auto_choose_color_from_profile(LodePNGColorMode* mode_out, + const LodePNGColorMode* mode_in, + const LodePNGColorProfile* prof) +{ + unsigned error = 0; + unsigned palettebits, palette_ok; + size_t i, n; + size_t numpixels = prof->numpixels; + + unsigned alpha = prof->alpha; + unsigned key = prof->key; + unsigned bits = prof->bits; - lodepng_color_profile_init(&prof); - error = lodepng_get_color_profile(&prof, image, w, h, mode_in); - if(error) return error; mode_out->key_defined = 0; - if(prof.key && w * h <= 16) + if(key && numpixels <= 16) { - prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ - prof.key = 0; - if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + key = 0; + if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } - n = prof.numcolors; + n = prof->numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); - palette_ok = n <= 256 && prof.bits <= 8; - if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ - if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + palette_ok = n <= 256 && bits <= 8; + if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(!prof->colored && bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ if(palette_ok) { - unsigned char* p = prof.palette; + const unsigned char* p = prof->palette; lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ - for(i = 0; i != prof.numcolors; ++i) + for(i = 0; i != prof->numcolors; ++i) { error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); if(error) break; @@ -3810,16 +4034,16 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, } else /*8-bit or 16-bit per channel*/ { - mode_out->bitdepth = prof.bits; - mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) - : (prof.colored ? LCT_RGB : LCT_GREY); + mode_out->bitdepth = bits; + mode_out->colortype = alpha ? (prof->colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof->colored ? LCT_RGB : LCT_GREY); - if(prof.key) + if(key) { unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ - mode_out->key_r = prof.key_r & mask; - mode_out->key_g = prof.key_g & mask; - mode_out->key_b = prof.key_b & mask; + mode_out->key_r = prof->key_r & mask; + mode_out->key_g = prof->key_g & mask; + mode_out->key_b = prof->key_b & mask; mode_out->key_defined = 1; } } @@ -3827,6 +4051,23 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, return error; } +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, color key if only single transparent color, ... +Updates values of mode with a potentially smaller color model. mode_out should +contain the user chosen color model, but will be overwritten with the new chosen one.*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) +{ + unsigned error = 0; + LodePNGColorProfile prof; + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + return auto_choose_color_from_profile(mode_out, mode_in, &prof); +} + #endif /* #ifdef LODEPNG_COMPILE_ENCODER */ /* @@ -3905,6 +4146,7 @@ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t fil unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize) { + unsigned width, height; LodePNGInfo* info = &state->info_png; if(insize == 0 || in == 0) { @@ -3916,6 +4158,7 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, } /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + /* TODO: remove this. One should use a new LodePNGState for new sessions */ lodepng_info_cleanup(info); lodepng_info_init(info); @@ -3934,19 +4177,22 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, } /*read the values given in the header*/ - *w = lodepng_read32bitInt(&in[16]); - *h = lodepng_read32bitInt(&in[20]); + width = lodepng_read32bitInt(&in[16]); + height = lodepng_read32bitInt(&in[20]); info->color.bitdepth = in[24]; info->color.colortype = (LodePNGColorType)in[25]; info->compression_method = in[26]; info->filter_method = in[27]; info->interlace_method = in[28]; - if(*w == 0 || *h == 0) + if(width == 0 || height == 0) { CERROR_RETURN_ERROR(state->error, 93); } + if(w) *w = width; + if(h) *h = height; + if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); @@ -4244,7 +4490,7 @@ static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* dat if(color->colortype == LCT_PALETTE) { /*error: more alpha values given than there are palette entries*/ - if(chunkLength > color->palettesize) return 38; + if(chunkLength > color->palettesize) return 39; for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } @@ -4281,6 +4527,9 @@ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, siz /*error: this chunk must be 1 byte for indexed color image*/ if(chunkLength != 1) return 43; + /*error: invalid palette index, or maybe this chunk appeared before PLTE*/ + if(data[0] >= info->color.palettesize) return 103; + info->background_defined = 1; info->background_r = info->background_g = info->background_b = data[0]; } @@ -4289,6 +4538,7 @@ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, siz /*error: this chunk must be 2 bytes for greyscale image*/ if(chunkLength != 2) return 44; + /*the values are truncated to bitdepth in the PNG file*/ info->background_defined = 1; info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; } @@ -4297,6 +4547,7 @@ static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, siz /*error: this chunk must be 6 bytes for greyscale image*/ if(chunkLength != 6) return 45; + /*the values are truncated to bitdepth in the PNG file*/ info->background_defined = 1; info->background_r = 256u * data[0] + data[1]; info->background_g = 256u * data[2] + data[3]; @@ -4331,7 +4582,7 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz string2_begin = length + 1; /*skip keyword null terminator*/ - length = chunkLength < string2_begin ? 0 : chunkLength - string2_begin; + length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); str = (char*)lodepng_malloc(length + 1); if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ @@ -4379,7 +4630,7 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting string2_begin = length + 2; if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ - length = chunkLength - string2_begin; + length = (unsigned)chunkLength - string2_begin; /*will fail if zlib error, e.g. if length is too small*/ error = zlib_decompress(&decoded.data, &decoded.size, (unsigned char*)(&data[string2_begin]), @@ -4408,7 +4659,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting unsigned length, begin, compressed; char *key = 0, *langtag = 0, *transkey = 0; ucvector decoded; - ucvector_init(&decoded); + ucvector_init(&decoded); /* TODO: only use in case of compressed text */ while(!error) /*not really a while loop, only used to break on error*/ { @@ -4459,7 +4710,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting /*read the actual text*/ begin += length + 1; - length = chunkLength < begin ? 0 : chunkLength - begin; + length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; if(compressed) { @@ -4518,7 +4769,168 @@ static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, siz return 0; /* OK */ } + +static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/ + + info->gama_defined = 1; + info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + + return 0; /* OK */ +} + +static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/ + + info->chrm_defined = 1; + info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3]; + info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7]; + info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11]; + info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15]; + info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19]; + info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23]; + info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27]; + info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31]; + + return 0; /* OK */ +} + +static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/ + + info->srgb_defined = 1; + info->srgb_intent = data[0]; + + return 0; /* OK */ +} + +static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + ucvector decoded; + + info->iccp_defined = 1; + if(info->iccp_name) lodepng_clear_icc(info); + + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/ + if(length < 1 || length > 79) return 89; /*keyword too short or long*/ + + info->iccp_name = (char*)lodepng_malloc(length + 1); + if(!info->iccp_name) return 83; /*alloc fail*/ + + info->iccp_name[length] = 0; + for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i]; + + if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + ucvector_init(&decoded); + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(!error) { + info->iccp_profile_size = decoded.size; + info->iccp_profile = (unsigned char*)lodepng_malloc(decoded.size); + if(info->iccp_profile) { + memcpy(info->iccp_profile, decoded.data, decoded.size); + } else { + error = 83; /* alloc fail */ + } + } + ucvector_cleanup(&decoded); + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize) +{ + const unsigned char* chunk = in + pos; + unsigned chunkLength; + const unsigned char* data; + unsigned unhandled = 0; + unsigned error = 0; + + if (pos + 4 > insize) return 30; + chunkLength = lodepng_chunk_length(chunk); + if(chunkLength > 2147483647) return 63; + data = lodepng_chunk_data_const(chunk); + if(data + chunkLength + 4 > in + insize) return 30; + + if(lodepng_chunk_type_equals(chunk, "PLTE")) + { + error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "tRNS")) + { + error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + else if(lodepng_chunk_type_equals(chunk, "bKGD")) + { + error = readChunk_bKGD(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "tEXt")) + { + error = readChunk_tEXt(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "zTXt")) + { + error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "iTXt")) + { + error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "tIME")) + { + error = readChunk_tIME(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "pHYs")) + { + error = readChunk_pHYs(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "gAMA")) + { + error = readChunk_gAMA(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "cHRM")) + { + error = readChunk_cHRM(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "sRGB")) + { + error = readChunk_sRGB(&state->info_png, data, chunkLength); + } + else if(lodepng_chunk_type_equals(chunk, "iCCP")) + { + error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + else + { + /* unhandled chunk is ok (is not an error) */ + unhandled = 1; + } + + if(!error && !unhandled && !state->decoder.ignore_crc) + { + if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/ + } + + return error; +} /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, @@ -4531,7 +4943,6 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, ucvector idat; /*the data from idat chunks*/ ucvector scanlines; size_t predict; - size_t numpixels; size_t outsize = 0; /*for unknown chunk order*/ @@ -4546,13 +4957,10 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; - numpixels = *w * *h; - - /*multiplication overflow*/ - if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); - /*multiplication overflow possible further below. Allows up to 2^31-1 pixel - bytes with 16-bit RGBA, the rest is room for filter bytes.*/ - if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) + { + CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ + } ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ @@ -4587,11 +4995,15 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, data = lodepng_chunk_data_const(chunk); + unknown = 0; + /*IDAT chunk, containing compressed image data*/ if(lodepng_chunk_type_equals(chunk, "IDAT")) { size_t oldsize = idat.size; - if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + size_t newsize; + if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/); for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; @@ -4611,7 +5023,9 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, critical_pos = 2; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ } - /*palette transparency chunk (tRNS)*/ + /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled + in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that + affects the alpha channel of pixels. */ else if(lodepng_chunk_type_equals(chunk, "tRNS")) { state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); @@ -4661,6 +5075,26 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, state->error = readChunk_pHYs(&state->info_png, data, chunkLength); if(state->error) break; } + else if(lodepng_chunk_type_equals(chunk, "gAMA")) + { + state->error = readChunk_gAMA(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "cHRM")) + { + state->error = readChunk_cHRM(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "sRGB")) + { + state->error = readChunk_sRGB(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "iCCP")) + { + state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { @@ -4694,21 +5128,20 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, If the decompressed size does not match the prediction, the image must be corrupt.*/ if(state->info_png.interlace_method == 0) { - /*The extra *h is added because this are the filter bytes every scanline starts with*/ - predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color); } else { /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ const LodePNGColorMode* color = &state->info_png.color; predict = 0; - predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); - predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); - if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); - predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); - if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); - predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color); } if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) @@ -5154,6 +5587,73 @@ static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) return error; } +static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->gama_gamma); + + error = addChunk(out, "gAMA", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->chrm_white_x); + lodepng_add32bitInt(&data, info->chrm_white_y); + lodepng_add32bitInt(&data, info->chrm_red_x); + lodepng_add32bitInt(&data, info->chrm_red_y); + lodepng_add32bitInt(&data, info->chrm_green_x); + lodepng_add32bitInt(&data, info->chrm_green_y); + lodepng_add32bitInt(&data, info->chrm_blue_x); + lodepng_add32bitInt(&data, info->chrm_blue_y); + + error = addChunk(out, "cHRM", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) +{ + unsigned char data = info->srgb_intent; + return addChunk(out, "sRGB", &data, 1); +} + +static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i; + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; info->iccp_name[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)info->iccp_name[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + info->iccp_profile, info->iccp_profile_size, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "iCCP", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, @@ -5415,7 +5915,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, { for(type = 0; type != 5; ++type) { - unsigned testsize = linebytes; + unsigned testsize = (unsigned)linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); @@ -5650,16 +6150,41 @@ static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t data } return 0; } + +static unsigned isGreyICCProfile(const unsigned char* profile, unsigned size) +{ + /* + It is a grey profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19 + are "RGB ". We do not perform any full parsing of the ICC profile here, other + than check those 4 bytes to grayscale profile. Other than that, validity of + the profile is not checked. This is needed only because the PNG specification + requires using a non-grey color model if there is an ICC profile with "RGB " + (sadly limiting compression opportunities if the input data is greyscale RGB + data), and requires using a grey color model if it is "GRAY". + */ + if(size < 20) return 0; + return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y'; +} + +static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) +{ + /* See comment in isGreyICCProfile*/ + if(size < 20) return 0; + return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' '; +} #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ unsigned lodepng_encode(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h, LodePNGState* state) { - LodePNGInfo info; - ucvector outv; unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ size_t datasize = 0; + ucvector outv; + LodePNGInfo info; + + ucvector_init(&outv); + lodepng_info_init(&info); /*provide some proper output values if error will happen*/ *out = 0; @@ -5670,50 +6195,114 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) { - CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ + state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ + goto cleanup; } if(state->encoder.zlibsettings.btype > 2) { - CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ + state->error = 61; /*error: unexisting btype*/ + goto cleanup; } if(state->info_png.interlace_method > 1) { - CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ + state->error = 71; /*error: unexisting interlace mode*/ + goto cleanup; } state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); - if(state->error) return state->error; /*error: unexisting color type given*/ + if(state->error) goto cleanup; /*error: unexisting color type given*/ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); - if(state->error) return state->error; /*error: unexisting color type given*/ + if(state->error) goto cleanup; /*error: unexisting color type given*/ /* color convert and compute scanline filter types */ - lodepng_info_init(&info); lodepng_info_copy(&info, &state->info_png); if(state->encoder.auto_convert) { - state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->info_png.background_defined) + { + unsigned bg_r = state->info_png.background_r; + unsigned bg_g = state->info_png.background_g; + unsigned bg_b = state->info_png.background_b; + unsigned r = 0, g = 0, b = 0; + LodePNGColorProfile prof; + LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16); + lodepng_convert_rgb(&r, &g, &b, bg_r, bg_g, bg_b, &mode16, &state->info_png.color); + lodepng_color_profile_init(&prof); + state->error = lodepng_get_color_profile(&prof, image, w, h, &state->info_raw); + if(state->error) goto cleanup; + lodepng_color_profile_add(&prof, r, g, b, 65535); + state->error = auto_choose_color_from_profile(&info.color, &state->info_raw, &prof); + if(state->error) goto cleanup; + if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b, + bg_r, bg_g, bg_b, &info.color, &state->info_png.color)) + { + state->error = 104; + goto cleanup; + } + } + else +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + { + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + if(state->error) goto cleanup; + } } - if (!state->error) +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->info_png.iccp_defined) { - if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + unsigned grey_icc = isGreyICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size); + unsigned grey_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA; + /* TODO: perhaps instead of giving errors or less optimal compression, we can automatically modify + the ICC profile here to say "GRAY" or "RGB " to match the PNG color type, unless this will require + non trivial changes to the rest of the ICC profile */ + if(!grey_icc && !isRGBICCProfile(state->info_png.iccp_profile, state->info_png.iccp_profile_size)) + { + state->error = 100; /* Disallowed profile color type for PNG */ + goto cleanup; + } + if(!state->encoder.auto_convert && grey_icc != grey_png) + { + /* Non recoverable: encoder not allowed to convert color type, and requested color type not + compatible with ICC color type */ + state->error = 101; + goto cleanup; + } + if(grey_icc && !grey_png) { - unsigned char* converted; - size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + /* Non recoverable: trying to set greyscale ICC profile while colored pixels were given */ + state->error = 102; + goto cleanup; + /* NOTE: this relies on the fact that lodepng_auto_choose_color never returns palette for greyscale pixels */ + } + if(!grey_icc && grey_png) + { + /* Recoverable but an unfortunate loss in compression density: We have greyscale pixels but + are forced to store them in more expensive RGB format that will repeat each value 3 times + because the PNG spec does not allow an RGB ICC profile with internal greyscale color data */ + if(info.color.colortype == LCT_GREY) info.color.colortype = LCT_RGB; + if(info.color.colortype == LCT_GREY_ALPHA) info.color.colortype = LCT_RGBA; + if(info.color.bitdepth < 8) info.color.bitdepth = 8; + } + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + { + unsigned char* converted; + size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; - converted = (unsigned char*)lodepng_malloc(size); - if(!converted && size) state->error = 83; /*alloc fail*/ - if(!state->error) - { - state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); - } - if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); - lodepng_free(converted); + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } - else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); + if(state->error) goto cleanup; } + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); /* output all PNG chunks */ - ucvector_init(&outv); - while(!state->error) /*while only executed once, to break on error*/ { #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS size_t i; @@ -5727,8 +6316,13 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(info.unknown_chunks_data[0]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); - if(state->error) break; + if(state->error) goto cleanup; } + /*color profile chunks must come before PLTE */ + if(info.iccp_defined) addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); + if(info.srgb_defined) addChunk_sRGB(&outv, &info); + if(info.gama_defined) addChunk_gAMA(&outv, &info); + if(info.chrm_defined) addChunk_cHRM(&outv, &info); #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*PLTE*/ if(info.color.colortype == LCT_PALETTE) @@ -5750,7 +6344,11 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, } #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*bKGD (must come between PLTE and the IDAt chunks*/ - if(info.background_defined) addChunk_bKGD(&outv, &info); + if(info.background_defined) + { + state->error = addChunk_bKGD(&outv, &info); + if(state->error) goto cleanup; + } /*pHYs (must come before the IDAT chunks)*/ if(info.phys_defined) addChunk_pHYs(&outv, &info); @@ -5758,12 +6356,12 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(info.unknown_chunks_data[1]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); - if(state->error) break; + if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /*IDAT (multiple IDAT chunks must be consecutive)*/ state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); - if(state->error) break; + if(state->error) goto cleanup; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /*tIME*/ if(info.time_defined) addChunk_tIME(&outv, &info.time); @@ -5773,12 +6371,12 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(strlen(info.text_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ - break; + goto cleanup; } if(strlen(info.text_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ - break; + goto cleanup; } if(state->encoder.text_compression) { @@ -5792,16 +6390,16 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, /*LodePNG version id in text chunk*/ if(state->encoder.add_id) { - unsigned alread_added_id_text = 0; + unsigned already_added_id_text = 0; for(i = 0; i != info.text_num; ++i) { if(!strcmp(info.text_keys[i], "LodePNG")) { - alread_added_id_text = 1; + already_added_id_text = 1; break; } } - if(alread_added_id_text == 0) + if(already_added_id_text == 0) { addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ } @@ -5812,12 +6410,12 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(strlen(info.itext_keys[i]) > 79) { state->error = 66; /*text chunk too large*/ - break; + goto cleanup; } if(strlen(info.itext_keys[i]) < 1) { state->error = 67; /*text chunk too small*/ - break; + goto cleanup; } addChunk_iTXt(&outv, state->encoder.text_compression, info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], @@ -5828,16 +6426,16 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(info.unknown_chunks_data[2]) { state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); - if(state->error) break; + if(state->error) goto cleanup; } #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ addChunk_IEND(&outv); - - break; /*this isn't really a while loop; no error happened so break out now!*/ } +cleanup: lodepng_info_cleanup(&info); lodepng_free(data); + /*instead of cleaning the vector up, give it to the output*/ *out = outv.data; *outsize = outv.size; @@ -5955,7 +6553,7 @@ const char* lodepng_error_text(unsigned code) case 36: return "illegal PNG filter type encountered"; case 37: return "illegal bit depth for this color type given"; case 38: return "the palette is too big"; /*more than 256 colors*/ - case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; + case 39: return "tRNS chunk before PLTE or has more entries than palette size"; case 40: return "tRNS chunk has wrong size for greyscale image"; case 41: return "tRNS chunk has wrong size for RGB image"; case 42: return "tRNS chunk appeared while it was not allowed for this color type"; @@ -6000,7 +6598,7 @@ const char* lodepng_error_text(unsigned code) case 79: return "failed to open file for writing"; case 80: return "tried creating a tree of 0 symbols"; case 81: return "lazy matching at pos 0 is impossible"; - case 82: return "color conversion to palette requested while a color isn't in palette"; + case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds"; case 83: return "memory allocation failed"; case 84: return "given image too small to contain all pixels to be encoded"; case 86: return "impossible offset in lz77 encoding (internal bug)"; @@ -6010,9 +6608,19 @@ const char* lodepng_error_text(unsigned code) /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; case 91: return "invalid decompressed idat size"; - case 92: return "too many pixels, not supported"; + case 92: return "integer overflow due to too many pixels"; case 93: return "zero width or height is invalid"; case 94: return "header chunk must have a size of 13 bytes"; + case 95: return "integer overflow with combined idat chunk size"; + case 96: return "invalid gAMA chunk size"; + case 97: return "invalid cHRM chunk size"; + case 98: return "invalid sRGB chunk size"; + case 99: return "invalid sRGB rendering intent"; + case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY"; + case 101: return "PNG specification does not allow RGB ICC profile on grey color types and vice versa"; + case 102: return "not allowed to set greyscale ICC profile with colored pixels by PNG specification"; + case 103: return "Invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?"; + case 104: return "Invalid bKGD color while encoding (e.g. palette index out of range)"; } return "unknown error code"; } diff --git a/src/lib/lodepng.h b/src/lib/lodepng.h index cf6667c8..2780e095 100644 --- a/src/lib/lodepng.h +++ b/src/lib/lodepng.h @@ -1,5 +1,5 @@ /* -LodePNG version 20180114 +LodePNG version 20180910 Copyright (c) 2005-2018 Lode Vandevenne @@ -255,7 +255,7 @@ const char* lodepng_error_text(unsigned code); typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; struct LodePNGDecompressSettings { - /* Check LodePNGDecoderSettings for more ignorable errors */ + /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ /*use custom zlib decoder instead of built in one (default: null)*/ @@ -360,6 +360,8 @@ void lodepng_color_mode_init(LodePNGColorMode* info); void lodepng_color_mode_cleanup(LodePNGColorMode* info); /*return value is error code (0 means no error)*/ unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); +/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */ +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth); void lodepng_palette_clear(LodePNGColorMode* info); /*add 1 color to the palette*/ @@ -410,23 +412,35 @@ typedef struct LodePNGInfo /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ unsigned compression_method;/*compression method of the original file. Always 0.*/ unsigned filter_method; /*filter method of the original file*/ - unsigned interlace_method; /*interlace method of the original file*/ + unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS /* - suggested background color chunk (bKGD) - This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. + Suggested background color chunk (bKGD) - For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding - the encoder writes the red one. For palette PNGs: When decoding, the RGB value - will be stored, not a palette index. But when encoding, specify the index of - the palette in background_r, the other two are then ignored. + This uses the same color mode and bit depth as the PNG (except no alpha channel), + with values truncated to the bit depth in the unsigned integer. - The decoder does not use this background color to edit the color of pixels. + For greyscale and palette PNGs, the value is stored in background_r. The values + in background_g and background_b are then unused. + + So when decoding, you may get these in a different color mode than the one you requested + for the raw pixels. + + When encoding with auto_convert, you must use the color model defined in info_png.color for + these values. The encoder normally ignores info_png.color when auto_convert is on, but will + use it to interpret these values (and convert copies of them to its chosen color model). + + When encoding, avoid setting this to an expensive color, such as a non-grey value + when the image is grey, or the compression will be worse since it will be forced to + write the PNG with a more expensive color mode (when auto_convert is on). + + The decoder does not use this background color to edit the color of pixels. This is a + completely optional metadata feature. */ unsigned background_defined; /*is a suggested background color given?*/ - unsigned background_r; /*red component of suggested background color*/ + unsigned background_r; /*red/grey/palette component of suggested background color*/ unsigned background_g; /*green component of suggested background color*/ unsigned background_b; /*blue component of suggested background color*/ @@ -437,6 +451,10 @@ typedef struct LodePNGInfo text_strings, while text_keys are keywords that give a short description what the actual text represents, e.g. Title, Author, Description, or anything else. + All the string fields below including keys, names and language tags are null terminated. + The PNG specification uses null characters for the keys, names and tags, and forbids null + characters to appear in the main text which is why we can use null termination everywhere here. + A keyword is minimum 1 character and maximum 79 characters long. It's discouraged to use a single line length longer than 79 characters for texts. @@ -469,11 +487,86 @@ typedef struct LodePNGInfo unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ /* - unknown chunks - There are 3 buffers, one for each position in the PNG where unknown chunks can appear - each buffer contains all unknown chunks for that position consecutively - The 3 buffers are the unknown chunks between certain critical chunks: - 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Color profile related chunks: gAMA, cHRM, sRGB, iCPP + + LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color + profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please + use these values with a color management library. + + See the PNG, ICC and sRGB specifications for more information about the meaning of these values. + */ + + /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */ + unsigned gama_gamma; /* Gamma exponent times 100000 */ + + /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */ + unsigned chrm_white_x; /* White Point x times 100000 */ + unsigned chrm_white_y; /* White Point y times 100000 */ + unsigned chrm_red_x; /* Red x times 100000 */ + unsigned chrm_red_y; /* Red y times 100000 */ + unsigned chrm_green_x; /* Green x times 100000 */ + unsigned chrm_green_y; /* Green y times 100000 */ + unsigned chrm_blue_x; /* Blue x times 100000 */ + unsigned chrm_blue_y; /* Blue y times 100000 */ + + /* + sRGB chunk: optional. May not appear at the same time as iCCP. + If gAMA is also present gAMA must contain value 45455. + If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000. + */ + unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */ + unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */ + + /* + iCCP chunk: optional. May not appear at the same time as sRGB. + + LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a + separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color + management and conversions. + + For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC + profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and + enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile. + + For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-grey + PNG color types and a "GRAY" profile for grey PNG color types. If you disable auto_convert, you must ensure + the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is + enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder + error if the pixel data has non-grey pixels for a GRAY profile, or a silent less-optimal compression of the pixel + data if the pixels could be encoded as greyscale but the ICC profile is RGB. + + To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so + make sure you compute it carefully to avoid the above problems. + */ + unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */ + char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */ + /* + The ICC profile in iccp_profile_size bytes. + Don't allocate this buffer yourself. Use the init/cleanup functions + correctly and use lodepng_set_icc and lodepng_clear_icc. + */ + unsigned char* iccp_profile; + unsigned iccp_profile_size; /* The size of iccp_profile in bytes */ + + /* End of color profile related chunks */ + + + /* + unknown chunks: chunks not known by LodePNG, passed on byte for byte. + + There are 3 buffers, one for each position in the PNG where unknown chunks can appear. + Each buffer contains all unknown chunks for that position consecutively. + The 3 positions are: + 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND. + + For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag + above in here, since the encoder will blindly follow this and could then encode an invalid PNG file + (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use + this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST), + or any non-standard PNG chunk. + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. */ @@ -489,12 +582,16 @@ void lodepng_info_cleanup(LodePNGInfo* info); unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS -void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ -void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ + +/*replaces if exists*/ +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size); +void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ /* @@ -521,10 +618,14 @@ typedef struct LodePNGDecoderSettings { LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ - /* Check LodePNGDecompressSettings for more ignorable errors */ + /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ unsigned ignore_crc; /*ignore CRC checksums*/ unsigned ignore_critical; /*ignore unknown critical chunks*/ unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ + /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable + errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some + strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters + in string keys, etc... */ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ @@ -558,8 +659,11 @@ typedef enum LodePNGFilterStrategy LFS_PREDEFINED } LodePNGFilterStrategy; -/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. -Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...), +which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms. +NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/... +fields in this header file.*/ typedef struct LodePNGColorProfile { unsigned colored; /*not greyscale*/ @@ -571,11 +675,14 @@ typedef struct LodePNGColorProfile unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ + size_t numpixels; } LodePNGColorProfile; void lodepng_color_profile_init(LodePNGColorProfile* profile); -/*Get a LodePNGColorProfile of the image.*/ +/*Get a LodePNGColorProfile of the image. The profile must already have been inited. +NOTE: This is not related to the ICC color profile, search "iccp_profile" instead to find the ICC/chromacity/... +fields in this header file.*/ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, const unsigned char* image, unsigned w, unsigned h, const LodePNGColorMode* mode_in); @@ -657,7 +764,7 @@ unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, /* Read the PNG header, but not the actual data. This returns only the information -that is in the header chunk of the PNG, such as width, height and color type. The +that is in the IHDR chunk of the PNG, such as width, height and color type. The information is placed in the info_png field of the LodePNGState. */ unsigned lodepng_inspect(unsigned* w, unsigned* h, @@ -665,6 +772,20 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, const unsigned char* in, size_t insize); #endif /*LODEPNG_COMPILE_DECODER*/ +/* +Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it +read in the state. Returns error code on failure. +Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const +to find the desired chunk type, and if non null use lodepng_inspect_chunk (with +chunk_pointer - start_of_file as pos). +Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...). +Ignores unsupported, unknown, non-metadata or IHDR chunks (without error). +Requirements: &in[pos] must point to start of a chunk, must use regular +lodepng_inspect first since format of most other chunks depends on IHDR, and if +there is a PLTE chunk, that one must be inspected before tRNS or bKGD. +*/ +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize); #ifdef LODEPNG_COMPILE_ENCODER /*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ @@ -678,11 +799,23 @@ The lodepng_chunk functions are normally not needed, except to traverse the unknown chunks stored in the LodePNGInfo struct, or add new ones to it. It also allows traversing the chunks of an encoded PNG file yourself. -PNG standard chunk naming conventions: -First byte: uppercase = critical, lowercase = ancillary -Second byte: uppercase = public, lowercase = private -Third byte: must be uppercase -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +The chunk pointer always points to the beginning of the chunk itself, that is +the first byte of the 4 length bytes. + +In the PNG file format, chunks have the following format: +-4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer) +-4 bytes chunk type (ASCII a-z,A-Z only, see below) +-length bytes of data (may be 0 bytes if length was 0) +-4 bytes of CRC, computed on chunk name + data + +The first chunk starts at the 8th byte of the PNG file, the entire rest of the file +exists out of concatenated chunks with the above format. + +PNG standard chunk ASCII naming conventions: +-First byte: uppercase = critical, lowercase = ancillary +-Second byte: uppercase = public, lowercase = private +-Third byte: must be uppercase +-Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ /* @@ -717,10 +850,24 @@ unsigned lodepng_chunk_check_crc(const unsigned char* chunk); /*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ void lodepng_chunk_generate_crc(unsigned char* chunk); -/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ +/* +Iterate to next chunks, allows iterating through all chunks of the PNG file. +Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call, +or the 8th byte of a PNG file which always has the first chunk), or alternatively may +point to the first byte of the PNG file (which is not a chunk but the magic header, the +function will then skip over it and return the first real chunk). +Expects at least 8 readable bytes of memory in the input pointer. +Will output pointer to the start of the next chunk or the end of the file if there +is no more chunk after this. Start this process at the 8th byte of the PNG file. +In a non-corrupt PNG file, the last chunk should have name "IEND". +*/ unsigned char* lodepng_chunk_next(unsigned char* chunk); const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); +/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/ +unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]); +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]); + /* Appends chunk to the data in out. The given chunk should already have its chunk header. The out variable and outlength are updated to reflect the new reallocated buffer. @@ -894,13 +1041,15 @@ unsigned compress(std::vector& out, const std::vector (2^31)-1 [ ] partial decoding (stream processing) [X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [ ] don't stop decoding on errors like 69, 57, 58 (make warnings) [ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] errors with line numbers (and version) [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes [ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... [ ] allow user to give data (void*) to custom allocator @@ -1614,6 +1763,12 @@ yyyymmdd. Some changes aren't backwards compatible. Those are indicated with a (!) symbol. +*) 10 sep 2018: added way to inspect metadata chunks without full decoding. +*) 19 aug 2018 (!): fixed color mode bKGD is encoded with and made it use + palette index in case of palette. +*) 10 aug 2018 (!): added support for gAMA, cHRM, sRGB and iCCP chunks. This + change is backwards compatible unless you relied on unknown_chunks for those. +*) 11 jun 2018: less restrictive check for pixel size integer overflow *) 14 jan 2018: allow optionally ignoring a few more recoverable errors *) 17 sep 2017: fix memory leak for some encoder input error cases *) 27 nov 2016: grey+alpha auto color model detection bugfix @@ -1765,5 +1920,5 @@ Domain: gmail dot com. Account: lode dot vandevenne. -Copyright (c) 2005-2017 Lode Vandevenne +Copyright (c) 2005-2018 Lode Vandevenne */ From 7d70a9b18159b6cbea4bb10851e3e5641a456a81 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 11 Nov 2018 05:12:33 +1000 Subject: [PATCH 27/32] CONSOLE: added support for IMAGE command --- src/common/device.c | 1 - src/languages/messages.en.h | 1 + src/platform/android/app/build.gradle | 4 +- src/platform/console/Makefile.am | 3 +- src/platform/console/image.cpp | 465 ++++++++++++++++++++++++++ src/ui/canvas.h | 8 +- src/ui/graphics.h | 34 +- src/ui/rgb.h | 57 ++++ 8 files changed, 530 insertions(+), 43 deletions(-) create mode 100644 src/platform/console/image.cpp create mode 100644 src/ui/rgb.h diff --git a/src/common/device.c b/src/common/device.c index 8bdea1e2..92918111 100644 --- a/src/common/device.c +++ b/src/common/device.c @@ -384,7 +384,6 @@ void log_printf(const char *format, ...) { } #if defined(BUILD_CONSOLE) -void v_create_image(var_p_t var) {} void v_create_form(var_p_t var) {} void v_create_window(var_p_t var) {} void dev_show_page() {} diff --git a/src/languages/messages.en.h b/src/languages/messages.en.h index 70dae434..dab8451c 100644 --- a/src/languages/messages.en.h +++ b/src/languages/messages.en.h @@ -215,6 +215,7 @@ #define ERR_NO_FUNC "Not a function" #define ERR_IMAGE_LOAD "Failed to load image: '%s'" #define ERR_IMAGE_SAVE "Failed to save image" +#define ERR_IMAGE_SAVE_ERR "Failed to save image: '%s'" #define ERR_FORM_INPUT "Form has no inputs" #define ERR_PARAM_NUM "Incorrect number of parameters: %d. Expected %d." #define ERR_PACK_TOO_MANY "Too many values to unpack" diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 9771e0fb..f144e62d 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 16 targetSdkVersion 27 - versionCode 30 - versionName "0.12.14.1" + versionCode 31 + versionName "0.12.14.2" resConfigs "en" } diff --git a/src/platform/console/Makefile.am b/src/platform/console/Makefile.am index cee12e43..79fb0f47 100644 --- a/src/platform/console/Makefile.am +++ b/src/platform/console/Makefile.am @@ -1,5 +1,5 @@ # SmallBASIC command line version -# Copyright(C) 2001-2012 Chris Warren-Smith. +# Copyright(C) 2001-2018 Chris Warren-Smith. # # This program is distributed under the terms of the GPL v2.0 or later # Download the GNU Public License (GPL) from www.gnu.org @@ -12,6 +12,7 @@ bin_PROGRAMS = sbasic sbasic_SOURCES = \ ../console/main.cpp \ ../console/device.cpp \ + ../console/image.cpp \ ../console/decomp.c sbasic_LDADD = -L$(top_srcdir)/src/common -lsb_common @PACKAGE_LIBS@ diff --git a/src/platform/console/image.cpp b/src/platform/console/image.cpp new file mode 100644 index 00000000..ce9ff08c --- /dev/null +++ b/src/platform/console/image.cpp @@ -0,0 +1,465 @@ +// This file is part of SmallBASIC +// +// Image handling +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2002-2018 Chris Warren-Smith. + +#define LODEPNG_NO_COMPILE_CPP +#define PIXELFORMAT_RGBA8888 +#define IMG_WIDTH "width" +#define IMG_HEIGHT "height" +#define IMG_ID "ID" +#define IMG_BID "BID" + +#include "config.h" +#include "common/sys.h" +#include "common/messages.h" +#include "common/pproc.h" +#include "common/fs_socket_client.h" +#include "ui/strlib.h" +#include "ui/utils.h" +#include "ui/rgb.h" + +extern "C" { +#include "lib/lodepng.h" + int xpm_decode32(uint8_t **image, unsigned *width, unsigned *height, const char *const *xpm); +} + +struct ImageBuffer { + ImageBuffer(); + ImageBuffer(ImageBuffer &imageBuffer); + virtual ~ImageBuffer(); + + unsigned _bid; + char *_filename; + unsigned char *_image; + int _width; + int _height; +}; + +unsigned nextId = 0; +strlib::List cache; + +void reset_image_cache() { + cache.removeAll(); +} + +ImageBuffer::ImageBuffer() : + _bid(0), + _filename(NULL), + _image(NULL), + _width(0), + _height(0) { +} + +ImageBuffer::ImageBuffer(ImageBuffer &o) : + _bid(o._bid), + _filename(o._filename), + _image(o._image), + _width(o._width), + _height(o._height) { +} + +ImageBuffer::~ImageBuffer() { + free(_filename); + free(_image); + _filename = NULL; + _image = NULL; +} + +void create_func(var_p_t map, const char *name, method cb) { + var_p_t v_func = map_add_var(map, name, 0); + v_func->type = V_FUNC; + v_func->v.fn.cb = cb; +} + +dev_file_t *eval_filep() { + dev_file_t *result = NULL; + code_skipnext(); + if (code_getnext() == '#') { + int handle = par_getint(); + if (!prog_error) { + result = dev_getfileptr(handle); + } + } + return result; +} + +ImageBuffer *load_image(int w) { + ImageBuffer *result = NULL; + if (!par_getsep()) { + err_throw(ERR_PARAM); + } else { + int h = par_getint(); + if (prog_error) { + err_throw(ERR_PARAM); + } else { + int size = w * h * 4; + unsigned char *image = (unsigned char *)calloc(size, 1); + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } + } + return result; +} + +// share image buffer from another image variable or array +ImageBuffer *load_image(var_t *var) { + ImageBuffer *result = NULL; + if (var->type == V_MAP) { + int bid = map_get_int(var, IMG_BID, -1); + if (bid != -1) { + List_each(ImageBuffer *, it, cache) { + ImageBuffer *next = (*it); + if (next->_bid == (unsigned)bid) { + result = next; + break; + } + } + } + } else if (var->type == V_ARRAY && v_maxdim(var) == 2) { + int w = ABS(v_lbound(var, 0) - v_ubound(var, 0)) + 1; + int h = ABS(v_lbound(var, 1) - v_ubound(var, 1)) + 1; + int size = w * h * 4; + unsigned char *image = (unsigned char *)malloc(size); + for (int y = 0; y < h; y++) { + int yoffs = (4 * y * w); + for (int x = 0; x < w; x++) { + int pos = y * w + x; + var_t *elem = v_elem(var, pos); + pixel_t px = v_getint(elem); + uint8_t r, g, b, a; + GET_ARGB(px, a, r, g, b); + int offs = yoffs + (4 * x); + image[offs + 0] = r; + image[offs + 1] = g; + image[offs + 2] = b; + image[offs + 3] = a; + } + } + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } + return result; +} + +ImageBuffer *load_image(const unsigned char* buffer, int32_t size) { + ImageBuffer *result = NULL; + unsigned w, h; + unsigned char *image; + unsigned error = 0; + + error = lodepng_decode32(&image, &w, &h, buffer, size); + if (!error) { + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } else { + err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error)); + } + return result; +} + +// +// png = image(#1) +// +ImageBuffer *load_image(dev_file_t *filep) { + ImageBuffer *result = NULL; + List_each(ImageBuffer *, it, cache) { + ImageBuffer *next = (*it); + if (next->_filename != NULL && strcmp(next->_filename, filep->name) == 0) { + result = next; + break; + } + } + + if (result == NULL) { + unsigned w, h; + unsigned char *image; + unsigned error = 0; + unsigned network_error = 0; + var_t *var_p; + + switch (filep->type) { + case ft_http_client: + // open "http://localhost/image1.gif" as #1 + if (filep->handle == -1) { + network_error = 1; + } else { + var_p = v_new(); + http_read(filep, var_p); + error = lodepng_decode32(&image, &w, &h, (unsigned char *)var_p->v.p.ptr, + var_p->v.p.length); + v_free(var_p); + v_detach(var_p); + } + break; + case ft_stream: + error = lodepng_decode32_file(&image, &w, &h, filep->name); + break; + default: + error = 1; + break; + } + if (network_error) { + err_throw(ERR_IMAGE_LOAD, ERR_NETWORK); + } else if (error) { + err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error)); + } else { + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = strdup(filep->name); + result->_image = image; + cache.add(result); + } + } + return result; +} + +ImageBuffer *load_xpm_image(char **data) { + unsigned w, h; + unsigned char *image; + unsigned error = xpm_decode32(&image, &w, &h, data); + ImageBuffer *result = NULL; + if (!error) { + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } else { + err_throw(ERR_IMAGE_LOAD, ERR_XPM_IMAGE); + } + return result; +} + +// +// png.save("horse1.png") +// png.save(#1) +// +void cmd_image_save(var_s *self) { + ImageBuffer *image = load_image(self); + dev_file_t *filep = NULL; + byte code = code_peek(); + int error = -1; + int w = image->_width; + int h = image->_height; + var_t var; + + if (!prog_error && image != NULL) { + switch (code) { + case kwTYPE_SEP: + filep = eval_filep(); + if (filep != NULL && filep->open_flags == DEV_FILE_OUTPUT) { + error = lodepng_encode32_file(filep->name, image->_image, w, h); + } + break; + default: + v_init(&var); + eval(&var); + if (var.type == V_STR && !prog_error) { + error = lodepng_encode32_file(var.v.p.ptr, image->_image, w, h); + } + v_free(&var); + break; + } + } + if (error == -1) { + err_throw(ERR_PARAM); + } else if (error != 0) { + err_throw(ERR_IMAGE_SAVE_ERR, lodepng_error_text(error)); + } +} + +// +// calls the supplied callback function on each pixel +// +// func colorToAlpha(x) +// return x +// end +// png.filter(use colorToAlpha(x)) +// +void cmd_image_filter(var_s *self) { + ImageBuffer *image_buffer = load_image(self); + if (code_peek() == kwUSE && image_buffer != NULL) { + code_skipnext(); + bcip_t use_ip = code_getaddr(); + bcip_t exit_ip = code_getaddr(); + int w = image_buffer->_width; + int h = image_buffer->_height; + unsigned char *image = image_buffer->_image; + var_t var; + v_init(&var); + for (int y = 0; y < h; y++) { + int yoffs = (4 * y * w); + for (int x = 0; x < w; x++) { + int offs = yoffs + (4 * x); + uint8_t r = image[offs + 0]; + uint8_t g = image[offs + 1]; + uint8_t b = image[offs + 2]; + uint8_t a = image[offs + 3]; + pixel_t px = SET_ARGB(a, r, g, b); + v_setint(&var, px); + exec_usefunc(&var, use_ip); + px = v_getint(&var); + GET_ARGB(px, a, r, g, b); + image[offs + 0] = r; + image[offs + 1] = g; + image[offs + 2] = b; + image[offs + 3] = a; + } + } + code_jump(exit_ip); + } else { + err_throw(ERR_PARAM); + } +} + +// +// png2 = image(w, h) +// png2.paste(png1, 0, 0, w, h) +// +void cmd_image_paste(var_s *self) { + var_int_t x, y, w, h; + var_t *var; + ImageBuffer *image = load_image(self); + int count = par_massget("Piiii", &var, &x, &y, &w, &h); + if (image != NULL && (count == 1 || count == 3 || count == 5)) { + ImageBuffer *srcImage = load_image(var); + if (srcImage == NULL) { + err_throw(ERR_PARAM); + } else { + if (count == 1) { + x = 0; + y = 0; + } + if (count < 5) { + w = image->_width; + h = image->_height; + } + int dw = image->_width; + int dh = image->_height; + int sw = MIN(w, srcImage->_width); + int sh = MIN(h, srcImage->_height); + unsigned char *src = srcImage->_image; + unsigned char *dst = image->_image; + for (int sy = 0, dy = y; sy < sh && dy < dh; sy++, dy++) { + int syoffs = (4 * sy * srcImage->_width); + int dyoffs = (4 * dy * dw); + for (int sx = 0, dx = x; sx < sw && dx < dw; sx++, dx++) { + int soffs = syoffs + (4 * sx); + int doffs = dyoffs + (4 * dx); + dst[doffs + 0] = src[soffs + 0]; + dst[doffs + 1] = src[soffs + 1]; + dst[doffs + 2] = src[soffs + 2]; + dst[doffs + 3] = src[soffs + 3]; + } + } + } + } else { + err_throw(ERR_PARAM); + } +} + +void create_image(var_p_t var, ImageBuffer *image) { + map_init(var); + map_add_var(var, IMG_ID, ++nextId); + map_add_var(var, IMG_WIDTH, image->_width); + map_add_var(var, IMG_HEIGHT, image->_height); + map_add_var(var, IMG_BID, image->_bid); + create_func(var, "filter", cmd_image_filter); + create_func(var, "paste", cmd_image_paste); + create_func(var, "save", cmd_image_save); +} + +// +// png = image(#1) +// png = image("file.png") +// png = image(array) +// png = image(png) +// png = image(10, 10) +// +extern "C" void v_create_image(var_p_t var) { + var_t arg; + ImageBuffer *image = NULL; + dev_file_t *filep = NULL; + + byte code = code_peek(); + switch (code) { + case kwTYPE_SEP: + filep = eval_filep(); + if (filep != NULL) { + image = load_image(filep); + } + break; + + case kwTYPE_LINE: + case kwTYPE_EOC: + break; + + default: + v_init(&arg); + eval(&arg); + if (arg.type == V_STR && !prog_error) { + dev_file_t file; + strlcpy(file.name, arg.v.p.ptr, sizeof(file.name)); + file.type = ft_stream; + image = load_image(&file); + } else if (arg.type == V_ARRAY && v_asize(&arg) > 0 && !prog_error) { + var_p_t elem0 = v_elem(&arg, 0); + if (elem0->type == V_STR) { + char **data = new char*[v_asize(&arg)]; + for (unsigned i = 0; i < v_asize(&arg); i++) { + var_p_t elem = v_elem(&arg, i); + data[i] = elem->v.p.ptr; + } + image = load_xpm_image(data); + delete [] data; + } else if (v_maxdim(&arg) == 2) { + // load from 2d array + image = load_image(&arg); + } else if (elem0->type == V_INT) { + unsigned char *data = new unsigned char[v_asize(&arg)]; + for (unsigned i = 0; i < v_asize(&arg); i++) { + var_p_t elem = v_elem(&arg, i); + data[i] = (unsigned char)elem->v.i; + } + image = load_image(data, v_asize(&arg)); + delete [] data; + } + } else if (arg.type == V_INT && !prog_error) { + image = load_image(arg.v.i); + } else { + image = load_image(&arg); + } + v_free(&arg); + break; + }; + + if (image != NULL) { + create_image(var, image); + } else { + err_throw(ERR_BAD_FILE_HANDLE); + } +} diff --git a/src/ui/canvas.h b/src/ui/canvas.h index 3fa71800..cdf97116 100644 --- a/src/ui/canvas.h +++ b/src/ui/canvas.h @@ -1,6 +1,6 @@ // This file is part of SmallBASIC // -// Copyright(C) 2001-2015 Chris Warren-Smith. +// Copyright(C) 2001-2018 Chris Warren-Smith. // // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org @@ -9,12 +9,6 @@ #ifndef UI_CANVAS #define UI_CANVAS -#if defined(PIXELFORMAT_RGB565) - typedef uint16_t pixel_t; -#else - typedef uint32_t pixel_t; -#endif - #if defined(_SDL) #include #define MAX_CANVAS_SIZE 20 diff --git a/src/ui/graphics.h b/src/ui/graphics.h index 3d2fec46..de48875a 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -1,6 +1,6 @@ // This file is part of SmallBASIC // -// Copyright(C) 2001-2015 Chris Warren-Smith. +// Copyright(C) 2001-2018 Chris Warren-Smith. // // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org @@ -11,6 +11,7 @@ #include "lib/maapi.h" #include "ui/strlib.h" +#include "ui/rgb.h" #include "ui/canvas.h" #include @@ -21,37 +22,6 @@ using namespace strlib; -inline void RGB888_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { - r = (c & 0xff0000) >> 16; - g = (c & 0xff00) >> 8; - b = (c & 0xff); -} - -inline pixel_t RGB888_to_RGBA8888(unsigned c) { - uint8_t r = (c & 0xff0000) >> 16; - uint8_t g = (c & 0xff00) >> 8; - uint8_t b = (c & 0xff); - return ((0xff000000) | (b << 16) | (g << 8) | (r)); -} - -inline void RGB888_BE_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { - b = (c & 0xff0000) >> 16; - g = (c & 0xff00) >> 8; - r = (c & 0xff); -} - -#if defined(PIXELFORMAT_RGBA8888) - #define SET_RGB(r, g, b) ((0xff000000) | (r << 16) | (g << 8) | (b)) - #define GET_RGB RGB888_to_RGB - #define GET_RGB2 RGB888_to_RGB - #define GET_FROM_RGB888 RGB888_to_RGBA8888 -#else - #define SET_RGB(r, g, b) ((r << 16) | (g << 8) | (b)) - #define GET_RGB RGB888_to_RGB - #define GET_RGB2 RGB888_to_RGB - #define GET_FROM_RGB888(c) (c) -#endif - namespace ui { struct Glyph { diff --git a/src/ui/rgb.h b/src/ui/rgb.h new file mode 100644 index 00000000..c78a5ce2 --- /dev/null +++ b/src/ui/rgb.h @@ -0,0 +1,57 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2018 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#ifndef UI_RGB +#define UI_RGB + +#if defined(PIXELFORMAT_RGB565) + typedef uint16_t pixel_t; +#else + typedef uint32_t pixel_t; +#endif + +inline void RGB888_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { + r = (c & 0xff0000) >> 16; + g = (c & 0xff00) >> 8; + b = (c & 0xff); +} + +inline pixel_t RGB888_to_RGBA8888(unsigned c) { + uint8_t r = (c & 0xff0000) >> 16; + uint8_t g = (c & 0xff00) >> 8; + uint8_t b = (c & 0xff); + return ((0xff000000) | (b << 16) | (g << 8) | (r)); +} + +inline void RGB888_BE_to_RGB(pixel_t c, uint8_t &r, uint8_t &g, uint8_t &b) { + b = (c & 0xff0000) >> 16; + g = (c & 0xff00) >> 8; + r = (c & 0xff); +} + +inline void GET_ARGB(pixel_t c, uint8_t &a, uint8_t &r, uint8_t &g, uint8_t &b) { + a = (c & 0xff000000) >> 24; + r = (c & 0x00ff0000) >> 16; + g = (c & 0x0000ff00) >> 8; + b = (c & 0x000000ff); +} + +#if defined(PIXELFORMAT_RGBA8888) + #define SET_RGB(r, g, b) ((0xff000000) | (r << 16) | (g << 8) | (b)) + #define SET_ARGB(a, r, g, b) (a << 24 | (r << 16) | (g << 8) | (b)) + #define GET_RGB RGB888_to_RGB + #define GET_RGB2 RGB888_to_RGB + #define GET_FROM_RGB888 RGB888_to_RGBA8888 +#else + #define SET_RGB(r, g, b) ((r << 16) | (g << 8) | (b)) + #define GET_RGB RGB888_to_RGB + #define GET_RGB2 RGB888_to_RGB +#define GET_FROM_RGB888(c) (c) +#endif + +#endif From edc0e7f1681c9f292fbe8df0b33e583b8fde5f74 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 11 Nov 2018 05:40:02 +1000 Subject: [PATCH 28/32] CONSOLE: added support for IMAGE command --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index d1544dab..4bc7758a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2018-11-11 (0.12.14) + CONSOLE: added support for IMAGE command + 2018-10-20 (0.12.14) UI: fix navigation when network access down then available UI: changed display of "BLACK" to be a slightly ligher onyx colour. From 2d2716a44fb7d5eb64f6ac7cad36a9a1269cab5c Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 11 Nov 2018 12:24:04 +1000 Subject: [PATCH 29/32] CONSOLE: added support for IMAGE command --- src/platform/console/image.cpp | 178 ++++++++++++++++++++------------- 1 file changed, 111 insertions(+), 67 deletions(-) diff --git a/src/platform/console/image.cpp b/src/platform/console/image.cpp index ce9ff08c..808d2577 100644 --- a/src/platform/console/image.cpp +++ b/src/platform/console/image.cpp @@ -20,7 +20,6 @@ #include "common/pproc.h" #include "common/fs_socket_client.h" #include "ui/strlib.h" -#include "ui/utils.h" #include "ui/rgb.h" extern "C" { @@ -35,7 +34,7 @@ struct ImageBuffer { unsigned _bid; char *_filename; - unsigned char *_image; + uint8_t *_image; int _width; int _height; }; @@ -70,13 +69,13 @@ ImageBuffer::~ImageBuffer() { _image = NULL; } -void create_func(var_p_t map, const char *name, method cb) { +void create_function(var_p_t map, const char *name, method cb) { var_p_t v_func = map_add_var(map, name, 0); v_func->type = V_FUNC; v_func->v.fn.cb = cb; } -dev_file_t *eval_filep() { +dev_file_t *get_file() { dev_file_t *result = NULL; code_skipnext(); if (code_getnext() == '#') { @@ -98,7 +97,7 @@ ImageBuffer *load_image(int w) { err_throw(ERR_PARAM); } else { int size = w * h * 4; - unsigned char *image = (unsigned char *)calloc(size, 1); + uint8_t *image = (uint8_t *)calloc(size, 1); result = new ImageBuffer(); result->_bid = ++nextId; result->_width = w; @@ -129,7 +128,7 @@ ImageBuffer *load_image(var_t *var) { int w = ABS(v_lbound(var, 0) - v_ubound(var, 0)) + 1; int h = ABS(v_lbound(var, 1) - v_ubound(var, 1)) + 1; int size = w * h * 4; - unsigned char *image = (unsigned char *)malloc(size); + uint8_t *image = (uint8_t *)malloc(size); for (int y = 0; y < h; y++) { int yoffs = (4 * y * w); for (int x = 0; x < w; x++) { @@ -156,10 +155,10 @@ ImageBuffer *load_image(var_t *var) { return result; } -ImageBuffer *load_image(const unsigned char* buffer, int32_t size) { +ImageBuffer *load_image(const uint8_t* buffer, int32_t size) { ImageBuffer *result = NULL; unsigned w, h; - unsigned char *image; + uint8_t *image; unsigned error = 0; error = lodepng_decode32(&image, &w, &h, buffer, size); @@ -192,7 +191,7 @@ ImageBuffer *load_image(dev_file_t *filep) { if (result == NULL) { unsigned w, h; - unsigned char *image; + uint8_t *image; unsigned error = 0; unsigned network_error = 0; var_t *var_p; @@ -205,8 +204,7 @@ ImageBuffer *load_image(dev_file_t *filep) { } else { var_p = v_new(); http_read(filep, var_p); - error = lodepng_decode32(&image, &w, &h, (unsigned char *)var_p->v.p.ptr, - var_p->v.p.length); + error = lodepng_decode32(&image, &w, &h, (uint8_t *)var_p->v.p.ptr, var_p->v.p.length); v_free(var_p); v_detach(var_p); } @@ -237,7 +235,7 @@ ImageBuffer *load_image(dev_file_t *filep) { ImageBuffer *load_xpm_image(char **data) { unsigned w, h; - unsigned char *image; + uint8_t *image; unsigned error = xpm_decode32(&image, &w, &h, data); ImageBuffer *result = NULL; if (!error) { @@ -255,45 +253,50 @@ ImageBuffer *load_xpm_image(char **data) { } // -// png.save("horse1.png") -// png.save(#1) +// Reduces the size of the image +// arguments: left, top, right, bottom // -void cmd_image_save(var_s *self) { +// png.clip(10, 10, 10, 10) +// +void cmd_image_clip(var_s *self) { + var_int_t left, top, right, bottom; ImageBuffer *image = load_image(self); - dev_file_t *filep = NULL; - byte code = code_peek(); - int error = -1; - int w = image->_width; - int h = image->_height; - var_t var; - - if (!prog_error && image != NULL) { - switch (code) { - case kwTYPE_SEP: - filep = eval_filep(); - if (filep != NULL && filep->open_flags == DEV_FILE_OUTPUT) { - error = lodepng_encode32_file(filep->name, image->_image, w, h); - } - break; - default: - v_init(&var); - eval(&var); - if (var.type == V_STR && !prog_error) { - error = lodepng_encode32_file(var.v.p.ptr, image->_image, w, h); + if (par_massget("iiii", &left, &top, &right, &bottom) == 4) { + int w = image->_width - (right + left); + int h = image->_height - (bottom + top); + int size = w * h * 4; + int oldSize = image->_width * image->_height * 4; + if (size > oldSize) { + err_throw(ERR_PARAM); + } else if (size != oldSize) { + uint8_t *dst = (uint8_t *)calloc(size, 1); + uint8_t *src = image->_image; + for (int y = 0; y < h; y++) { + int syoffs = (4 * (y + top) * image->_width); + int dyoffs = (4 * y * w); + for (int x = 0; x < w; x++) { + int spos = syoffs + (4 * (x + left)); + int dpos = dyoffs + (4 * x); + dst[dpos + 0] = src[spos + 0]; + dst[dpos + 1] = src[spos + 1]; + dst[dpos + 2] = src[spos + 2]; + dst[dpos + 3] = src[spos + 3]; + } } - v_free(&var); - break; + free(image->_image); + image->_image = dst; + image->_width = w; + image->_height = h; + map_set_int(self, IMG_WIDTH, w); + map_set_int(self, IMG_HEIGHT, h); } - } - if (error == -1) { + } else { err_throw(ERR_PARAM); - } else if (error != 0) { - err_throw(ERR_IMAGE_SAVE_ERR, lodepng_error_text(error)); } } // -// calls the supplied callback function on each pixel +// Calls the supplied callback function on each pixel // // func colorToAlpha(x) // return x @@ -308,7 +311,7 @@ void cmd_image_filter(var_s *self) { bcip_t exit_ip = code_getaddr(); int w = image_buffer->_width; int h = image_buffer->_height; - unsigned char *image = image_buffer->_image; + uint8_t *image = image_buffer->_image; var_t var; v_init(&var); for (int y = 0; y < h; y++) { @@ -336,16 +339,18 @@ void cmd_image_filter(var_s *self) { } } +// +// Paste the given image into this image at the given x, y location // // png2 = image(w, h) -// png2.paste(png1, 0, 0, w, h) +// png2.paste(png1, 0, 0) // void cmd_image_paste(var_s *self) { - var_int_t x, y, w, h; + var_int_t x, y; var_t *var; ImageBuffer *image = load_image(self); - int count = par_massget("Piiii", &var, &x, &y, &w, &h); - if (image != NULL && (count == 1 || count == 3 || count == 5)) { + int count = par_massget("Piiii", &var, &x, &y); + if (image != NULL && (count == 1 || count == 3)) { ImageBuffer *srcImage = load_image(var); if (srcImage == NULL) { err_throw(ERR_PARAM); @@ -354,26 +359,22 @@ void cmd_image_paste(var_s *self) { x = 0; y = 0; } - if (count < 5) { - w = image->_width; - h = image->_height; - } int dw = image->_width; int dh = image->_height; - int sw = MIN(w, srcImage->_width); - int sh = MIN(h, srcImage->_height); - unsigned char *src = srcImage->_image; - unsigned char *dst = image->_image; + int sw = srcImage->_width; + int sh = srcImage->_height; + uint8_t *src = srcImage->_image; + uint8_t *dst = image->_image; for (int sy = 0, dy = y; sy < sh && dy < dh; sy++, dy++) { int syoffs = (4 * sy * srcImage->_width); int dyoffs = (4 * dy * dw); for (int sx = 0, dx = x; sx < sw && dx < dw; sx++, dx++) { - int soffs = syoffs + (4 * sx); - int doffs = dyoffs + (4 * dx); - dst[doffs + 0] = src[soffs + 0]; - dst[doffs + 1] = src[soffs + 1]; - dst[doffs + 2] = src[soffs + 2]; - dst[doffs + 3] = src[soffs + 3]; + int spos = syoffs + (4 * sx); + int dpos = dyoffs + (4 * dx); + dst[dpos + 0] = src[spos + 0]; + dst[dpos + 1] = src[spos + 1]; + dst[dpos + 2] = src[spos + 2]; + dst[dpos + 3] = src[spos + 3]; } } } @@ -382,17 +383,60 @@ void cmd_image_paste(var_s *self) { } } +// +// Output the image to a PNG file +// +// png.save("horse1.png") +// png.save(#1) +// +void cmd_image_save(var_s *self) { + ImageBuffer *image = load_image(self); + dev_file_t *filep = NULL; + byte code = code_peek(); + int error = -1; + int w = image->_width; + int h = image->_height; + var_t var; + + if (!prog_error && image != NULL) { + switch (code) { + case kwTYPE_SEP: + filep = get_file(); + if (filep != NULL && filep->open_flags == DEV_FILE_OUTPUT) { + error = lodepng_encode32_file(filep->name, image->_image, w, h); + } + break; + default: + v_init(&var); + eval(&var); + if (var.type == V_STR && !prog_error) { + error = lodepng_encode32_file(var.v.p.ptr, image->_image, w, h); + } + v_free(&var); + break; + } + } + if (error == -1) { + err_throw(ERR_PARAM); + } else if (error != 0) { + err_throw(ERR_IMAGE_SAVE_ERR, lodepng_error_text(error)); + } +} + void create_image(var_p_t var, ImageBuffer *image) { map_init(var); map_add_var(var, IMG_ID, ++nextId); map_add_var(var, IMG_WIDTH, image->_width); map_add_var(var, IMG_HEIGHT, image->_height); map_add_var(var, IMG_BID, image->_bid); - create_func(var, "filter", cmd_image_filter); - create_func(var, "paste", cmd_image_paste); - create_func(var, "save", cmd_image_save); + create_function(var, "clip", cmd_image_clip); + create_function(var, "filter", cmd_image_filter); + create_function(var, "paste", cmd_image_paste); + create_function(var, "save", cmd_image_save); } +// +// Creates an image object // // png = image(#1) // png = image("file.png") @@ -408,7 +452,7 @@ extern "C" void v_create_image(var_p_t var) { byte code = code_peek(); switch (code) { case kwTYPE_SEP: - filep = eval_filep(); + filep = get_file(); if (filep != NULL) { image = load_image(filep); } @@ -440,10 +484,10 @@ extern "C" void v_create_image(var_p_t var) { // load from 2d array image = load_image(&arg); } else if (elem0->type == V_INT) { - unsigned char *data = new unsigned char[v_asize(&arg)]; + uint8_t *data = new uint8_t[v_asize(&arg)]; for (unsigned i = 0; i < v_asize(&arg); i++) { var_p_t elem = v_elem(&arg, i); - data[i] = (unsigned char)elem->v.i; + data[i] = (uint8_t)elem->v.i; } image = load_image(data, v_asize(&arg)); delete [] data; From 0bf5c928760bef9299a5abeee272d387d3bd6b5f Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 12 Nov 2018 06:16:41 +1000 Subject: [PATCH 30/32] CONSOLE: added support for IMAGE command --- src/platform/console/image.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platform/console/image.cpp b/src/platform/console/image.cpp index 808d2577..22a4eb21 100644 --- a/src/platform/console/image.cpp +++ b/src/platform/console/image.cpp @@ -11,6 +11,7 @@ #define PIXELFORMAT_RGBA8888 #define IMG_WIDTH "width" #define IMG_HEIGHT "height" +#define IMG_NAME "name" #define IMG_ID "ID" #define IMG_BID "BID" @@ -429,6 +430,10 @@ void create_image(var_p_t var, ImageBuffer *image) { map_add_var(var, IMG_WIDTH, image->_width); map_add_var(var, IMG_HEIGHT, image->_height); map_add_var(var, IMG_BID, image->_bid); + if (image->_filename != NULL) { + var_p_t value = map_add_var(var, IMG_NAME, 0); + v_setstr(value, image->_filename); + } create_function(var, "clip", cmd_image_clip); create_function(var, "filter", cmd_image_filter); create_function(var, "paste", cmd_image_paste); From 8c314f5a1d6a6592ba8184e3b8f689ad4061d40b Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 17 Nov 2018 10:50:50 +1000 Subject: [PATCH 31/32] Fix Win32 build --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index dca33692..44fc1460 100644 --- a/configure.ac +++ b/configure.ac @@ -161,6 +161,9 @@ function buildSDL() { dnl avoid using MSCRT versions of printf for long double PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -D__USE_MINGW_ANSI_STDIO" + AC_DEFINE(_USE_MATH_DEFINES, 1, [for M_PI in math.h]) + AC_DEFINE(realpath(P, R), _fullpath((char *)P, R, PATH_MAX), [add missing realpath for system.cpp]) + dnl do not depend on cygwin.dll under cygwin build PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -mms-bitfields" PACKAGE_LIBS="${PACKAGE_LIBS} -lwsock32 -lws2_32 -static-libgcc -static-libstdc++" From b423a4eee0cfed6c28dc4b719632c8232379ab71 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 19 Nov 2018 20:40:12 +1000 Subject: [PATCH 32/32] Fix Win32 build --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 44fc1460..f9af3299 100644 --- a/configure.ac +++ b/configure.ac @@ -162,7 +162,7 @@ function buildSDL() { PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -D__USE_MINGW_ANSI_STDIO" AC_DEFINE(_USE_MATH_DEFINES, 1, [for M_PI in math.h]) - AC_DEFINE(realpath(P, R), _fullpath((char *)P, R, PATH_MAX), [add missing realpath for system.cpp]) + AC_DEFINE(realpath(F, R), _fullpath(R, F, PATH_MAX), [add missing realpath for system.cpp]) dnl do not depend on cygwin.dll under cygwin build PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -mms-bitfields"