aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Bodenmann <joel@embedded.pro>2017-08-27 13:43:53 +0200
committerJoel Bodenmann <joel@embedded.pro>2017-08-27 13:43:53 +0200
commit285c6528e65c3fa9a898e03b261fcaf8121308cd (patch)
tree0895be81ed272a7e32bb1ed4df273a488ccce8ed
parentf9494c44a2406b4b7dd753076f1dfc8380e19500 (diff)
parentdb7971734fc940851b6d91a77a60906622f6c491 (diff)
downloaduGFX-285c6528e65c3fa9a898e03b261fcaf8121308cd.tar.gz
uGFX-285c6528e65c3fa9a898e03b261fcaf8121308cd.tar.bz2
uGFX-285c6528e65c3fa9a898e03b261fcaf8121308cd.zip
Merge branch 'master' of git.ugfx.io:uGFX/uGFX
-rw-r--r--boards/base/OSX/example/Makefile4
-rw-r--r--changelog.txt5
-rw-r--r--src/gdisp/gdisp_image_jpg.c1148
-rw-r--r--src/ginput/ginput_keyboard.c26
-rw-r--r--src/gos/gos_chibios.c4
-rw-r--r--src/gwin/gwin_image.c6
-rw-r--r--src/gwin/gwin_textedit.c190
-rw-r--r--src/gwin/gwin_textedit.h26
-rw-r--r--src/gwin/gwin_widget.h14
-rw-r--r--tools/gmake_scripts/os_osx.mk6
10 files changed, 1322 insertions, 107 deletions
diff --git a/boards/base/OSX/example/Makefile b/boards/base/OSX/example/Makefile
index cce88c13..5cc39d15 100644
--- a/boards/base/OSX/example/Makefile
+++ b/boards/base/OSX/example/Makefile
@@ -26,7 +26,7 @@
ARCH =
SRCFLAGS = -ggdb -O0
-CFLAGS = `sdl2-config --libs --cflags`
+CFLAGS = $(sdl2-config --cflags)
CXXFLAGS =
ASFLAGS =
LDFLAGS =
@@ -34,7 +34,7 @@ LDFLAGS =
SRC =
OBJS =
DEFS =
-LIBS =
+LIBS = $(sdl2-config --libs)
INCPATH =
LIBPATH =
diff --git a/changelog.txt b/changelog.txt
index 4bc85283..cfcb932f 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -44,6 +44,11 @@ FIX: Fixed to allow gwinSetText with static text on a TextEdit control
FIX: Fixed to ChibiOS realloc on a TextEdit control
FEATURE: Added support for CMSIS V2.0 operating systems (eg RTX5)
REMOVED: Removed long deprecated functions gfxSemCounter() and gfxSemCounterI()
+FIX: gwinDetachToggle() is now a visible part of the API
+CHANGE: Update OSX makefiles (allows for 64bit building)
+FIX: Fixed resetting a timer on gwinImage objects when using animated GIFs
+FEATURE: Added gwinTextEditSendKey() and gwinTextEditSendSpecialKey()
+FEATURE: Implemented the JPG image decoder
*** Release 2.7 ***
diff --git a/src/gdisp/gdisp_image_jpg.c b/src/gdisp/gdisp_image_jpg.c
index 9a10f215..9c149357 100644
--- a/src/gdisp/gdisp_image_jpg.c
+++ b/src/gdisp/gdisp_image_jpg.c
@@ -3,14 +3,1160 @@
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.org/license.html
+ *
+ * Note: part of this file is based on TJpgDec - Tiny JPEG Decompressor R0.01b (C)ChaN, 2012
+ * with modifications for uGFX. The copyright notice for that code is in this file
+ * when that code begins.
*/
#include "../../gfx.h"
#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG
+#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
+ #warning "GDISP JPG DECODER: This decoder is completly untested. It also currently has the downside that it always caches the complete image into RAM"
+#elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
+ COMPILER_WARNING("GDISP JPG DECODER: This decoder is completly untested. It also currently has the downside that it always caches the complete image into RAM")
+#endif
+
#include "gdisp_image_support.h"
-#error "JPG support not implemented yet"
+#define JD_SZBUF 512 /* Size of stream input buffer */
+#define JD_USE_SCALE 0 /* Use descaling feature for output */
+#define JD_TBLCLIP 0 /* Use table for saturation (might be a bit faster but increases 1K bytes of code size) */
+
+#define JD_WORKSZ (JD_SZBUF+2580+8) /* The extra 8 bytes just for safety */
+
+typedef struct {
+ coord_t left, right, top, bottom;
+} JRECT;
+/* Decompressor object structure */
+typedef struct JDEC {
+ unsigned dctr; /* Number of bytes available in the input buffer */
+ uint8_t* dptr; /* Current data read ptr */
+ uint8_t* inbuf; /* Bit stream input buffer */
+ uint8_t dmsk; /* Current bit in the current read byte */
+ uint8_t scale; /* Output scaling ratio */
+ uint8_t msx, msy; /* MCU size in unit of block (width, height) */
+ uint8_t qtid[3]; /* Quantization table ID of each component */
+ int16_t dcv[3]; /* Previous DC element of each component */
+ uint16_t nrst; /* Restart inverval */
+ unsigned width, height; /* Size of the input image (pixel) */
+ uint8_t* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */
+ uint16_t* huffcode[2][2]; /* Huffman code word tables [id][dcac] */
+ uint8_t* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */
+ int32_t* qttbl[4]; /* Dequaitizer tables [id] */
+ void* workbuf; /* Working buffer for IDCT and RGB output */
+ uint8_t* mcubuf; /* Working buffer for the MCU */
+ void* pool; /* Pointer to available memory pool */
+ unsigned sz_pool; /* Size of momory pool (bytes available) */
+ gdispImage* img; /* Pointer to I/O device identifiler for the session */
+ } JDEC;
+
+/* TJpgDec API functions */
+gdispImageError jd_prepare(JDEC*, void*, gdispImage*);
+gdispImageError jd_decomp(JDEC*, unsigned(*)(gdispImage*,void*,JRECT*), uint8_t);
+
+/*---------------------------------------------------------------------------*/
+typedef struct gdispImagePrivate_JPG {
+ pixel_t *frame0cache;
+ } gdispImagePrivate_JPG;
+
+gdispImageError gdispImageOpen_JPG(gdispImage *img){
+ gdispImagePrivate_JPG *priv;
+ uint8_t hdr[4];
+ unsigned len;
+
+ /* Read the file identifier */
+ if (gfileRead(img->f, hdr, 2) != 2)
+ return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
+
+ /* Test the JPEG header structure */
+ if (hdr[0] != 0xff || hdr[1] != 0xd8)
+ return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
+
+ /* We know we are a JPG format image */
+ img->type = GDISP_IMAGE_TYPE_JPG;
+ img->flags = 0;
+
+ /* Process Start of frame segments */
+ while(1) {
+
+ // Get a JPEG marker
+ if (gfileRead(img->f, hdr, 4) != 4 || hdr[0] != 0xFF)
+ return GDISP_IMAGE_ERR_BADDATA;
+
+ switch (hdr[1]) {
+ case 0xC0: // SOF0
+ gfileRead(img->f, hdr, 4);
+ img->height = gdispImageGetAlignedBE16(hdr, 0);
+ img->width = gdispImageGetAlignedBE16(hdr, 2);
+
+ /* Allocate our private area */
+ if (!(img->priv = gdispImageAlloc(img, sizeof(gdispImagePrivate_JPG))))
+ return GDISP_IMAGE_ERR_NOMEMORY;
+
+ /* Initialise the essential bits in the private area */
+ priv = (gdispImagePrivate_JPG *)img->priv;
+ priv->frame0cache = 0;
+
+ return GDISP_IMAGE_ERR_OK;
+
+ case 0xD9: // EOI
+ return GDISP_IMAGE_ERR_UNSUPPORTED; /* Unsuppoted JPEG standard (may be progressive JPEG) */
+
+ default:
+ // Other SOFn markers are unsupported
+ if (hdr[1] >= 0xC1 && hdr[1] <= 0xCF)
+ return GDISP_IMAGE_ERR_UNSUPPORTED;
+
+ // Skip segment data
+ len = gdispImageGetAlignedBE16(hdr, 2);
+ if (len <= 2) return GDISP_IMAGE_ERR_BADDATA;
+ gfileSetPos(img->f, gfileGetPos(img->f)+len-2);
+ }
+ }
+}
+
+void gdispImageClose_JPG(gdispImage *img){
+ gdispImagePrivate_JPG *priv = (gdispImagePrivate_JPG *)img->priv;
+ if(priv){
+ if (priv->frame0cache){
+ gdispImageFree(img, (void *)priv->frame0cache, img->width * img->height * sizeof(pixel_t));
+ }
+ gdispImageFree(img, (void*) priv, sizeof(gdispImagePrivate_JPG));
+ }
+}
+
+static unsigned gdispImage_JPG_WriteToCache(gdispImage *img, void *bitmap, JRECT *rect)
+{
+ gdispImagePrivate_JPG *priv;
+ uint8_t *in;
+ pixel_t *out;
+ coord_t x, y;
+
+ priv = (gdispImagePrivate_JPG *)img->priv;
+ in = (unsigned char *)bitmap;
+
+ for (y = rect->top; y <= rect->bottom; y++) {
+ out = priv->frame0cache + ((img->width * (unsigned)y) + rect->left);
+ for(x = rect->left; x <= rect->right; x++, in += 3)
+ *out++ = RGB2COLOR(in[0], in[1], in[2]);
+ }
+ return 1;
+}
+
+gdispImageError gdispImageCache_JPG(gdispImage *img) {
+ gdispImagePrivate_JPG *priv;
+ JDEC *jd;
+ gdispImageError r;
+
+ /* If we are already cached - just return OK */
+ priv = (gdispImagePrivate_JPG *)img->priv;
+ if (priv->frame0cache)
+ return GDISP_IMAGE_ERR_OK;
+
+ /* Otherwise start a new decode */
+ priv->frame0cache = (pixel_t *)gdispImageAlloc(img, img->width * img->height * sizeof(pixel_t));
+ if (!priv->frame0cache)
+ return GDISP_IMAGE_ERR_NOMEMORY;
+
+ if (!(jd = gdispImageAlloc(img, sizeof(JDEC)+JD_WORKSZ)))
+ return GDISP_IMAGE_ERR_NOMEMORY;
+
+ gfileSetPos(img->f, 0);
+
+ if(!(r = jd_prepare(jd, jd+1, img))
+ && !(r = jd_decomp(jd, gdispImage_JPG_WriteToCache, 0)))
+ r = GDISP_IMAGE_ERR_OK;
+
+ gdispImageFree(img, jd, sizeof(JDEC)+JD_WORKSZ);
+
+ return r;
+}
+
+gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy){
+ gdispImagePrivate_JPG * priv;
+
+ priv = (gdispImagePrivate_JPG *)img->priv;
+
+ /* Check some reasonableness */
+ if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK;
+ if (sx + cx > img->width) cx = img->width - sx;
+ if (sy + cy > img->height) cy = img->height - sy;
+
+ /* Cache the image if not already cached */
+ if (!priv->frame0cache) {
+ gdispImageError err = gdispImageCache_JPG(img);
+ if(err){
+ return err;
+ }
+ }
+ gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache);
+
+ return GDISP_IMAGE_ERR_OK;
+}
+
+delaytime_t gdispImageNext_JPG(gdispImage *img) {
+ (void) img;
+
+ /* No more frames/pages */
+ return TIME_INFINITE;
+}
+
+/*----------------------------------------------------------------------------/
+/ TJpgDec - Tiny JPEG Decompressor R0.01b (C)ChaN, 2012
+/-----------------------------------------------------------------------------/
+/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/ Copyright (C) 2012, ChaN, all right reserved.
+/
+/ * The TJpgDec module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Oct 04,'11 R0.01 First release.
+/ Feb 19,'12 R0.01a Fixed decompression fails when scan starts with an escape seq.
+/ Sep 03,'12 R0.01b Added JD_TBLCLIP option.
+/ Aug 26,'17 Modified and optimised for uGFX
+/----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------*/
+/* Zigzag-order to raster-order conversion table */
+/*-----------------------------------------------*/
+
+#define ZIG(n) Zig[n]
+
+static
+const uint8_t Zig[64] = { /* Zigzag-order to raster-order conversion table */
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+
+
+/*-------------------------------------------------*/
+/* Input scale factor of Arai algorithm */
+/* (scaled up 16 bits for fixed point operations) */
+/*-------------------------------------------------*/
+
+#define IPSF(n) Ipsf[n]
+
+static
+const uint16_t Ipsf[64] = { /* See also aa_idct.png */
+ (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192),
+ (uint16_t)(1.38704*8192), (uint16_t)(1.92388*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.08979*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.38268*8192),
+ (uint16_t)(1.30656*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.70711*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.36048*8192),
+ (uint16_t)(1.17588*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.38268*8192), (uint16_t)(1.17588*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.32442*8192),
+ (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192),
+ (uint16_t)(0.78570*8192), (uint16_t)(1.08979*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.61732*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.21677*8192),
+ (uint16_t)(0.54120*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.29290*8192), (uint16_t)(0.14932*8192),
+ (uint16_t)(0.27590*8192), (uint16_t)(0.38268*8192), (uint16_t)(0.36048*8192), (uint16_t)(0.32442*8192), (uint16_t)(0.27590*8192), (uint16_t)(0.21678*8192), (uint16_t)(0.14932*8192), (uint16_t)(0.07612*8192)
+};
+
+
+
+/*---------------------------------------------*/
+/* Conversion table for fast clipping process */
+/*---------------------------------------------*/
+
+#if JD_TBLCLIP
+
+#define BYTECLIP(v) Clip8[(unsigned)(v) & 0x3FF]
+
+static
+const uint8_t Clip8[1024] = {
+ /* 0..255 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ /* 256..511 */
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ /* -512..-257 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* -256..-1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#else /* JD_TBLCLIP */
+
+inline
+uint8_t BYTECLIP (
+ int val
+)
+{
+ if (val < 0) val = 0;
+ if (val > 255) val = 255;
+
+ return (uint8_t)val;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Allocate a memory block from memory pool */
+/*-----------------------------------------------------------------------*/
+
+static
+void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */
+ JDEC* jd, /* Pointer to the decompressor object */
+ unsigned nd /* Number of bytes to allocate */
+)
+{
+ char *rp = 0;
+
+
+ nd = (nd + 3) & ~3; /* Align block size to the word boundary */
+
+ if (jd->sz_pool >= nd) {
+ jd->sz_pool -= nd;
+ rp = (char*)jd->pool; /* Get start of available memory pool */
+ jd->pool = (void*)(rp + nd); /* Allocate requierd bytes */
+ }
+
+ return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create de-quantization and prescaling tables with a DQT segment */
+/*-----------------------------------------------------------------------*/
+
+static
+unsigned create_qt_tbl ( /* 0:OK, !0:Failed */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const uint8_t* data, /* Pointer to the quantizer tables */
+ unsigned ndata /* Size of input data */
+)
+{
+ unsigned i;
+ uint8_t d, z;
+ int32_t *pb;
+
+
+ while (ndata) { /* Process all tables in the segment */
+ if (ndata < 65) return GDISP_IMAGE_ERR_BADDATA; /* Err: table size is unaligned */
+ ndata -= 65;
+ d = *data++; /* Get table property */
+ if (d & 0xF0) return GDISP_IMAGE_ERR_BADDATA; /* Err: not 8-bit resolution */
+ i = d & 3; /* Get table ID */
+ pb = alloc_pool(jd, 64 * sizeof (int32_t));/* Allocate a memory block for the table */
+ if (!pb) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+ jd->qttbl[i] = pb; /* Register the table */
+ for (i = 0; i < 64; i++) { /* Load the table */
+ z = ZIG(i); /* Zigzag-order to raster-order conversion */
+ pb[z] = (int32_t)((uint32_t)*data++ * IPSF(z)); /* Apply scale factor of Arai algorithm to the de-quantizers */
+ }
+ }
+
+ return GDISP_IMAGE_ERR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create huffman code tables with a DHT segment */
+/*-----------------------------------------------------------------------*/
+
+static
+unsigned create_huffman_tbl ( /* 0:OK, !0:Failed */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const uint8_t* data, /* Pointer to the packed huffman tables */
+ unsigned ndata /* Size of input data */
+)
+{
+ unsigned i, j, b, np, cls, num;
+ uint8_t d, *pb, *pd;
+ uint16_t hc, *ph;
+
+
+ while (ndata) { /* Process all tables in the segment */
+ if (ndata < 17) return GDISP_IMAGE_ERR_BADDATA; /* Err: wrong data size */
+ ndata -= 17;
+ d = *data++; /* Get table number and class */
+ cls = (d >> 4); num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */
+ if (d & 0xEE) return GDISP_IMAGE_ERR_BADDATA; /* Err: invalid class/number */
+ pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */
+ if (!pb) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+ jd->huffbits[num][cls] = pb;
+ for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */
+ pb[i] = b = *data++;
+ np += b; /* Get sum of code words for each code */
+ }
+
+ ph = alloc_pool(jd, np * sizeof (uint16_t));/* Allocate a memory block for the code word table */
+ if (!ph) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+ jd->huffcode[num][cls] = ph;
+ hc = 0;
+ for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */
+ b = pb[i];
+ while (b--) ph[j++] = hc++;
+ hc <<= 1;
+ }
+
+ if (ndata < np) return GDISP_IMAGE_ERR_BADDATA; /* Err: wrong data size */
+ ndata -= np;
+ pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */
+ if (!pd) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+ jd->huffdata[num][cls] = pd;
+ for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code ward */
+ d = *data++;
+ if (!cls && d > 11) return GDISP_IMAGE_ERR_BADDATA;
+ *pd++ = d;
+ }
+ }
+
+ return GDISP_IMAGE_ERR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Extract N bits from input stream */
+/*-----------------------------------------------------------------------*/
+
+static
+int bitext ( /* >=0: extracted data, <0: error code */
+ JDEC* jd, /* Pointer to the decompressor object */
+ unsigned nbit /* Number of bits to extract (1 to 11) */
+)
+{
+ uint8_t msk, s, *dp;
+ unsigned dc, v, f;
+
+
+ msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
+ s = *dp; v = f = 0;
+ do {
+ if (!msk) { /* Next byte? */
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf; /* Top of input buffer */
+ dc = gfileRead(jd->img->f, dp, JD_SZBUF);
+ if (!dc) return 0 - (int)GDISP_IMAGE_ERR_BADDATA; /* Err: read error or wrong stream termination */
+ } else {
+ dp++; /* Next data ptr */
+ }
+ dc--; /* Decrement number of available bytes */
+ if (f) { /* In flag sequence? */
+ f = 0; /* Exit flag sequence */
+ if (*dp != 0) return 0 - (int)GDISP_IMAGE_ERR_BADDATA; /* Err: unexpected flag is detected (may be collapted data) */
+ *dp = s = 0xFF; /* The flag is a data 0xFF */
+ } else {
+ s = *dp; /* Get next data byte */
+ if (s == 0xFF) { /* Is start of flag sequence? */
+ f = 1; continue; /* Enter flag sequence */
+ }
+ }
+ msk = 0x80; /* Read from MSB */
+ }
+ v <<= 1; /* Get a bit */
+ if (s & msk) v++;
+ msk >>= 1;
+ nbit--;
+ } while (nbit);
+ jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
+
+ return (int)v;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Extract a huffman decoded data from input stream */
+/*-----------------------------------------------------------------------*/
+
+static
+int huffext ( /* >=0: decoded data, <0: error code */
+ JDEC* jd, /* Pointer to the decompressor object */
+ const uint8_t* hbits, /* Pointer to the bit distribution table */
+ const uint16_t* hcode, /* Pointer to the code word table */
+ const uint8_t* hdata /* Pointer to the data table */
+)
+{
+ uint8_t msk, s, *dp;
+ unsigned dc, v, f, bl, nd;
+
+
+ msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */
+ s = *dp; v = f = 0;
+ bl = 16; /* Max code length */
+ do {
+ if (!msk) { /* Next byte? */
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf; /* Top of input buffer */
+ dc = gfileRead(jd->img->f, dp, JD_SZBUF);
+ if (!dc) return 0 - (int)GDISP_IMAGE_ERR_BADDATA; /* Err: read error or wrong stream termination */
+ } else {
+ dp++; /* Next data ptr */
+ }
+ dc--; /* Decrement number of available bytes */
+ if (f) { /* In flag sequence? */
+ f = 0; /* Exit flag sequence */
+ if (*dp != 0)
+ return 0 - (int)GDISP_IMAGE_ERR_BADDATA; /* Err: unexpected flag is detected (may be collapted data) */
+ *dp = s = 0xFF; /* The flag is a data 0xFF */
+ } else {
+ s = *dp; /* Get next data byte */
+ if (s == 0xFF) { /* Is start of flag sequence? */
+ f = 1; continue; /* Enter flag sequence, get trailing byte */
+ }
+ }
+ msk = 0x80; /* Read from MSB */
+ }
+ v <<= 1; /* Get a bit */
+ if (s & msk) v++;
+ msk >>= 1;
+
+ for (nd = *hbits++; nd; nd--) { /* Search the code word in this bit length */
+ if (v == *hcode++) { /* Matched? */
+ jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp;
+ return *hdata; /* Return the decoded data */
+ }
+ hdata++;
+ }
+ bl--;
+ } while (bl);
+
+ return 0 - (int)GDISP_IMAGE_ERR_BADDATA; /* Err: code not found (may be collapted data) */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */
+/*-----------------------------------------------------------------------*/
+
+static
+void block_idct (
+ int32_t* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */
+ uint8_t* dst /* Pointer to the destination to store the block as byte array */
+)
+{
+ const int32_t M13 = (int32_t)(1.41421*4096), M2 = (int32_t)(1.08239*4096), M4 = (int32_t)(2.61313*4096), M5 = (int32_t)(1.84776*4096);
+ int32_t v0, v1, v2, v3, v4, v5, v6, v7;
+ int32_t t10, t11, t12, t13;
+ unsigned i;
+
+ /* Process columns */
+ for (i = 0; i < 8; i++) {
+ v0 = src[8 * 0]; /* Get even elements */
+ v1 = src[8 * 2];
+ v2 = src[8 * 4];
+ v3 = src[8 * 6];
+
+ t10 = v0 + v2; /* Process the even elements */
+ t12 = v0 - v2;
+ t11 = (v1 - v3) * M13 >> 12;
+ v3 += v1;
+ t11 -= v3;
+ v0 = t10 + v3;
+ v3 = t10 - v3;
+ v1 = t11 + t12;
+ v2 = t12 - t11;
+
+ v4 = src[8 * 7]; /* Get odd elements */
+ v5 = src[8 * 1];
+ v6 = src[8 * 5];
+ v7 = src[8 * 3];
+
+ t10 = v5 - v4; /* Process the odd elements */
+ t11 = v5 + v4;
+ t12 = v6 - v7;
+ v7 += v6;
+ v5 = (t11 - v7) * M13 >> 12;
+ v7 += t11;
+ t13 = (t10 + t12) * M5 >> 12;
+ v4 = t13 - (t10 * M2 >> 12);
+ v6 = t13 - (t12 * M4 >> 12) - v7;
+ v5 -= v6;
+ v4 -= v5;
+
+ src[8 * 0] = v0 + v7; /* Write-back transformed values */
+ src[8 * 7] = v0 - v7;
+ src[8 * 1] = v1 + v6;
+ src[8 * 6] = v1 - v6;
+ src[8 * 2] = v2 + v5;
+ src[8 * 5] = v2 - v5;
+ src[8 * 3] = v3 + v4;
+ src[8 * 4] = v3 - v4;
+
+ src++; /* Next column */
+ }
+
+ /* Process rows */
+ src -= 8;
+ for (i = 0; i < 8; i++) {
+ v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */
+ v1 = src[2];
+ v2 = src[4];
+ v3 = src[6];
+
+ t10 = v0 + v2; /* Process the even elements */
+ t12 = v0 - v2;
+ t11 = (v1 - v3) * M13 >> 12;
+ v3 += v1;
+ t11 -= v3;
+ v0 = t10 + v3;
+ v3 = t10 - v3;
+ v1 = t11 + t12;
+ v2 = t12 - t11;
+
+ v4 = src[7]; /* Get odd elements */
+ v5 = src[1];
+ v6 = src[5];
+ v7 = src[3];
+
+ t10 = v5 - v4; /* Process the odd elements */
+ t11 = v5 + v4;
+ t12 = v6 - v7;
+ v7 += v6;
+ v5 = (t11 - v7) * M13 >> 12;
+ v7 += t11;
+ t13 = (t10 + t12) * M5 >> 12;
+ v4 = t13 - (t10 * M2 >> 12);
+ v6 = t13 - (t12 * M4 >> 12) - v7;
+ v5 -= v6;
+ v4 -= v5;
+
+ dst[0] = BYTECLIP((v0 + v7) >> 8); /* Descale the transformed values 8 bits and output */
+ dst[7] = BYTECLIP((v0 - v7) >> 8);
+ dst[1] = BYTECLIP((v1 + v6) >> 8);
+ dst[6] = BYTECLIP((v1 - v6) >> 8);
+ dst[2] = BYTECLIP((v2 + v5) >> 8);
+ dst[5] = BYTECLIP((v2 - v5) >> 8);
+ dst[3] = BYTECLIP((v3 + v4) >> 8);
+ dst[4] = BYTECLIP((v3 - v4) >> 8);
+ dst += 8;
+
+ src += 8; /* Next row */
+ }
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load all blocks in the MCU into working buffer */
+/*-----------------------------------------------------------------------*/
+
+static
+gdispImageError mcu_load (
+ JDEC* jd /* Pointer to the decompressor object */
+)
+{
+ int32_t *tmp = (int32_t*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */
+ unsigned blk, nby, nbc, i, z, id, cmp;
+ int b, d, e;
+ uint8_t *bp;
+ const uint8_t *hb, *hd;
+ const uint16_t *hc;
+ const int32_t *dqf;
+
+
+ nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */
+ nbc = 2; /* Number of C blocks (2) */
+ bp = jd->mcubuf; /* Pointer to the first block */
+
+ for (blk = 0; blk < nby + nbc; blk++) {
+ cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */
+ id = cmp ? 1 : 0; /* Huffman table ID of the component */
+
+ /* Extract a DC element from input stream */
+ hb = jd->huffbits[id][0]; /* Huffman table for the DC element */
+ hc = jd->huffcode[id][0];
+ hd = jd->huffdata[id][0];
+ b = huffext(jd, hb, hc, hd); /* Extract a huffman coded data (bit length) */
+ if (b < 0) return 0 - b; /* Err: invalid code or input */
+ d = jd->dcv[cmp]; /* DC value of previous block */
+ if (b) { /* If there is any difference from previous block */
+ e = bitext(jd, b); /* Extract data bits */
+ if (e < 0) return 0 - e; /* Err: input */
+ b = 1 << (b - 1); /* MSB position */
+ if (!(e & b)) e -= (b << 1) - 1; /* Restore sign if needed */
+ d += e; /* Get current value */
+ jd->dcv[cmp] = (int16_t)d; /* Save current DC value for next block */
+ }
+ dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */
+ tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
+
+ /* Extract following 63 AC elements from input stream */
+ for (i = 1; i < 64; i++) tmp[i] = 0; /* Clear rest of elements */
+ hb = jd->huffbits[id][1]; /* Huffman table for the AC elements */
+ hc = jd->huffcode[id][1];
+ hd = jd->huffdata[id][1];
+ i = 1; /* Top of the AC elements */
+ do {
+ b = huffext(jd, hb, hc, hd); /* Extract a huffman coded value (zero runs and bit length) */
+ if (b == 0) break; /* EOB? */
+ if (b < 0) return 0 - b; /* Err: invalid code or input error */
+ z = (unsigned)b >> 4; /* Number of leading zero elements */
+ if (z) {
+ i += z; /* Skip zero elements */
+ if (i >= 64) return GDISP_IMAGE_ERR_BADDATA; /* Too long zero run */
+ }
+ if (b &= 0x0F) { /* Bit length */
+ d = bitext(jd, b); /* Extract data bits */
+ if (d < 0) return 0 - d; /* Err: input device */
+ b = 1 << (b - 1); /* MSB position */
+ if (!(d & b)) d -= (b << 1) - 1;/* Restore negative value if needed */
+ z = ZIG(i); /* Zigzag-order to raster-order converted index */
+ tmp[z] = d * dqf[z] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */
+ }
+ } while (++i < 64); /* Next AC element */
+
+ if (JD_USE_SCALE && jd->scale == 3)
+ *bp = (*tmp / 256) + 128; /* If scale ratio is 1/8, IDCT can be ommited and only DC element is used */
+ else
+ block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */
+
+ bp += 64; /* Next block */
+ }
+
+ return GDISP_IMAGE_ERR_OK; /* All blocks have been loaded successfully */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */
+/*-----------------------------------------------------------------------*/
+
+static
+gdispImageError mcu_output (
+ JDEC* jd, /* Pointer to the decompressor object */
+ unsigned (*outfunc)(gdispImage*, void*, JRECT*), /* RGB output function */
+ unsigned x, /* MCU position in the image (left of the MCU) */
+ unsigned y /* MCU position in the image (top of the MCU) */
+)
+{
+ const int CVACC = (sizeof (int) > 2) ? 1024 : 128;
+ unsigned ix, iy, mx, my, rx, ry;
+ int yy, cb, cr;
+ uint8_t *py, *pc, *rgb24;
+ JRECT rect;
+
+
+ mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */
+ rx = (x + mx <= jd->width) ? mx : jd->width - x; /* Output rectangular size (it may be clipped at right/bottom end) */
+ ry = (y + my <= jd->height) ? my : jd->height - y;
+ if (JD_USE_SCALE) {
+ rx >>= jd->scale; ry >>= jd->scale;
+ if (!rx || !ry) return GDISP_IMAGE_ERR_OK; /* Skip this MCU if all pixel is to be rounded off */
+ x >>= jd->scale; y >>= jd->scale;
+ }
+ rect.left = x; rect.right = x + rx - 1; /* Rectangular area in the frame buffer */
+ rect.top = y; rect.bottom = y + ry - 1;
+
+
+ if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */
+
+ /* Build an RGB MCU from discrete comopnents */
+ rgb24 = (uint8_t*)jd->workbuf;
+ for (iy = 0; iy < my; iy++) {
+ pc = jd->mcubuf;
+ py = pc + iy * 8;
+ if (my == 16) { /* Double block height? */
+ pc += 64 * 4 + (iy >> 1) * 8;
+ if (iy >= 8) py += 64;
+ } else { /* Single block height */
+ pc += mx * 8 + iy * 8;
+ }
+ for (ix = 0; ix < mx; ix++) {
+ cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
+ cr = pc[64] - 128;
+ if (mx == 16) { /* Double block width? */
+ if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */
+ pc += ix & 1; /* Increase chroma pointer every two pixels */
+ } else { /* Single block width */
+ pc++; /* Increase chroma pointer every pixel */
+ }
+ yy = *py++; /* Get Y component */
+
+ /* Convert YCbCr to RGB */
+ *rgb24++ = /* R */ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* G */ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* B */ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb) / CVACC);
+ }
+ }
+
+ /* Descale the MCU rectangular if needed */
+ if (JD_USE_SCALE && jd->scale) {
+ unsigned x, y, r, g, b, s, w, a;
+ uint8_t *op;
+
+ /* Get averaged RGB value of each square correcponds to a pixel */
+ s = jd->scale * 2; /* Bumber of shifts for averaging */
+ w = 1 << jd->scale; /* Width of square */
+ a = (mx - w) * 3; /* Bytes to skip for next line in the square */
+ op = (uint8_t*)jd->workbuf;
+ for (iy = 0; iy < my; iy += w) {
+ for (ix = 0; ix < mx; ix += w) {
+ rgb24 = (uint8_t*)jd->workbuf + (iy * mx + ix) * 3;
+ r = g = b = 0;
+ for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */
+ for (x = 0; x < w; x++) {
+ r += *rgb24++;
+ g += *rgb24++;
+ b += *rgb24++;
+ }
+ rgb24 += a;
+ } /* Put the averaged RGB value as a pixel */
+ *op++ = (uint8_t)(r >> s);
+ *op++ = (uint8_t)(g >> s);
+ *op++ = (uint8_t)(b >> s);
+ }
+ }
+ }
+
+ } else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */
+
+ /* Build a 1/8 descaled RGB MCU from discrete comopnents */
+ rgb24 = (uint8_t*)jd->workbuf;
+ pc = jd->mcubuf + mx * my;
+ cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */
+ cr = pc[64] - 128;
+ for (iy = 0; iy < my; iy += 8) {
+ py = jd->mcubuf;
+ if (iy == 8) py += 64 * 2;
+ for (ix = 0; ix < mx; ix += 8) {
+ yy = *py; /* Get Y component */
+ py += 64;
+
+ /* Convert YCbCr to RGB */
+ *rgb24++ = /* R */ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr / CVACC));
+ *rgb24++ = /* G */ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC);
+ *rgb24++ = /* B */ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb / CVACC));
+ }
+ }
+ }
+
+ /* Squeeze up pixel table if a part of MCU is to be truncated */
+ mx >>= jd->scale;
+ if (rx < mx) {
+ uint8_t *s, *d;
+ unsigned x, y;
+
+ s = d = (uint8_t*)jd->workbuf;
+ for (y = 0; y < ry; y++) {
+ for (x = 0; x < rx; x++) { /* Copy effective pixels */
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ s += (mx - rx) * 3; /* Skip truncated pixels */
+ }
+ }
+
+#if 0
+ /* Convert RGB888 to RGB565 if needed */
+ if (JD_FORMAT == 1) {
+ uint8_t *s = (uint8_t*)jd->workbuf;
+ uint16_t w, *d = (uint16_t*)s;
+ unsigned n = rx * ry;
+
+ do {
+ w = (*s++ & 0xF8) << 8; /* RRRRR----------- */
+ w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */
+ w |= *s++ >> 3; /* -----------BBBBB */
+ *d++ = w;
+ } while (--n);
+ }
+#endif
+
+ /* Output the RGB rectangular */
+ return outfunc(jd->img, jd->workbuf, &rect) ? GDISP_IMAGE_ERR_OK : GDISP_IMAGE_ERR_BADDATA;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Process restart interval */
+/*-----------------------------------------------------------------------*/
+
+static
+gdispImageError restart (
+ JDEC* jd, /* Pointer to the decompressor object */
+ uint16_t rstn /* Expected restert sequense number */
+)
+{
+ unsigned i, dc;
+ uint16_t d;
+ uint8_t *dp;
+
+
+ /* Discard padding bits and get two bytes from the input stream */
+ dp = jd->dptr; dc = jd->dctr;
+ d = 0;
+ for (i = 0; i < 2; i++) {
+ if (!dc) { /* No input data is available, re-fill input buffer */
+ dp = jd->inbuf;
+ dc = gfileRead(jd->img->f, dp, JD_SZBUF);
+ if (!dc) return GDISP_IMAGE_ERR_BADDATA;
+ } else {
+ dp++;
+ }
+ dc--;
+ d = (d << 8) | *dp; /* Get a byte */
+ }
+ jd->dptr = dp; jd->dctr = dc; jd->dmsk = 0;
+
+ /* Check the marker */
+ if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7))
+ return GDISP_IMAGE_ERR_BADDATA; /* Err: expected RSTn marker is not detected (may be collapted data) */
+
+ /* Reset DC offset */
+ jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0;
+
+ return GDISP_IMAGE_ERR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Analyze the JPEG image and Initialize decompressor object */
+/*-----------------------------------------------------------------------*/
+
+gdispImageError jd_prepare (
+ JDEC* jd, /* Blank decompressor object */
+ void* pool, /* Working buffer for the decompression session */
+ gdispImage* img /* I/O device identifier for the session */
+)
+{
+ uint8_t *seg, b;
+ uint16_t marker;
+ uint32_t ofs;
+ unsigned n, i, j, len;
+ gdispImageError rc;
+
+
+ jd->pool = pool; /* Work memroy */
+ jd->sz_pool = JD_WORKSZ; /* Size of given work memory */
+ jd->img = img; /* I/O device identifier */
+ jd->nrst = 0; /* No restart interval (default) */
+
+ for (i = 0; i < 2; i++) { /* Nulls pointers */
+ for (j = 0; j < 2; j++) {
+ jd->huffbits[i][j] = 0;
+ jd->huffcode[i][j] = 0;
+ jd->huffdata[i][j] = 0;
+ }
+ }
+ for (i = 0; i < 4; i++) jd->qttbl[i] = 0;
+
+ jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */
+ if (!seg) return GDISP_IMAGE_ERR_NOMEMORY;
+
+ if (gfileRead(jd->img->f, seg, 2) != 2) return GDISP_IMAGE_ERR_BADDATA;/* Check SOI marker */
+ if (gdispImageGetAlignedBE16(seg, 0) != 0xFFD8) return GDISP_IMAGE_ERR_BADDATA; /* Err: SOI is not detected */
+ ofs = 2;
+
+ for (;;) {
+ /* Get a JPEG marker */
+ if (gfileRead(jd->img->f, seg, 4) != 4) return GDISP_IMAGE_ERR_BADDATA;
+ marker = gdispImageGetAlignedBE16(seg, 0); /* Marker */
+ len = gdispImageGetAlignedBE16(seg, 2); /* Length field */
+ if (len <= 2 || (marker >> 8) != 0xFF) return GDISP_IMAGE_ERR_BADDATA;
+ len -= 2; /* Content size excluding length field */
+ ofs += 4 + len; /* Number of bytes loaded */
+
+ switch (marker & 0xFF) {
+ case 0xC0: /* SOF0 (baseline JPEG) */
+ /* Load segment data */
+ if (len > JD_SZBUF) return GDISP_IMAGE_ERR_NOMEMORY;
+ if (gfileRead(jd->img->f, seg, len) != len) return GDISP_IMAGE_ERR_BADDATA;
+
+ jd->width = gdispImageGetBE16(seg,3); /* Image width in unit of pixel */
+ jd->height = gdispImageGetBE16(seg,1); /* Image height in unit of pixel */
+ if (seg[5] != 3) return GDISP_IMAGE_ERR_BADDATA; /* Err: Supports only Y/Cb/Cr format */
+
+ /* Check three image components */
+ for (i = 0; i < 3; i++) {
+ b = seg[7 + 3 * i]; /* Get sampling factor */
+ if (!i) { /* Y component */
+ if (b != 0x11 && b != 0x22 && b != 0x21)/* Check sampling factor */
+ return GDISP_IMAGE_ERR_BADDATA; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */
+ jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */
+ } else { /* Cb/Cr component */
+ if (b != 0x11) return GDISP_IMAGE_ERR_BADDATA; /* Err: Sampling factor of Cr/Cb must be 1 */
+ }
+ b = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */
+ if (b > 3) return GDISP_IMAGE_ERR_BADDATA; /* Err: Invalid ID */
+ jd->qtid[i] = b;
+ }
+ break;
+
+ case 0xDD: /* DRI */
+ /* Load segment data */
+ if (len > JD_SZBUF) return GDISP_IMAGE_ERR_NOMEMORY;
+ if (gfileRead(jd->img->f, seg, len) != len) return GDISP_IMAGE_ERR_BADDATA;
+
+ /* Get restart interval (MCUs) */
+ jd->nrst = gdispImageGetAlignedBE16(seg, 0);
+ break;
+
+ case 0xC4: /* DHT */
+ /* Load segment data */
+ if (len > JD_SZBUF) return GDISP_IMAGE_ERR_NOMEMORY;
+ if (gfileRead(jd->img->f, seg, len) != len) return GDISP_IMAGE_ERR_BADDATA;
+
+ /* Create huffman tables */
+ rc = create_huffman_tbl(jd, seg, len);
+ if (rc) return rc;
+ break;
+
+ case 0xDB: /* DQT */
+ /* Load segment data */
+ if (len > JD_SZBUF) return GDISP_IMAGE_ERR_NOMEMORY;
+ if (gfileRead(jd->img->f, seg, len) != len) return GDISP_IMAGE_ERR_BADDATA;
+
+ /* Create de-quantizer tables */
+ rc = create_qt_tbl(jd, seg, len);
+ if (rc) return rc;
+ break;
+
+ case 0xDA: /* SOS */
+ /* Load segment data */
+ if (len > JD_SZBUF) return GDISP_IMAGE_ERR_NOMEMORY;
+ if (gfileRead(jd->img->f, seg, len) != len) return GDISP_IMAGE_ERR_BADDATA;
+
+ if (!jd->width || !jd->height) return GDISP_IMAGE_ERR_BADDATA; /* Err: Invalid image size */
+
+ if (seg[0] != 3) return GDISP_IMAGE_ERR_BADDATA; /* Err: Supports only three color components format */
+
+ /* Check if all tables corresponding to each components have been loaded */
+ for (i = 0; i < 3; i++) {
+ b = seg[2 + 2 * i]; /* Get huffman table ID */
+ if (b != 0x00 && b != 0x11) return GDISP_IMAGE_ERR_BADDATA; /* Err: Different table number for DC/AC element */
+ b = i ? 1 : 0;
+ if (!jd->huffbits[b][0] || !jd->huffbits[b][1]) /* Check huffman table for this component */
+ return GDISP_IMAGE_ERR_BADDATA; /* Err: Huffman table not loaded */
+ if (!jd->qttbl[jd->qtid[i]]) return GDISP_IMAGE_ERR_BADDATA; /* Err: Dequantizer table not loaded */
+ }
+
+ /* Allocate working buffer for MCU and RGB */
+ n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */
+ if (!n) return GDISP_IMAGE_ERR_BADDATA; /* Err: SOF0 has not been loaded */
+ len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */
+ if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */
+ jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */
+ if (!jd->workbuf) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+ jd->mcubuf = alloc_pool(jd, (n + 2) * 64); /* Allocate MCU working buffer */
+ if (!jd->mcubuf) return GDISP_IMAGE_ERR_NOMEMORY; /* Err: not enough memory */
+
+ /* Pre-load the JPEG data to extract it from the bit stream */
+ jd->dptr = seg; jd->dctr = 0; jd->dmsk = 0; /* Prepare to read bit stream */
+ if (ofs %= JD_SZBUF) { /* Align read offset to JD_SZBUF */
+ jd->dctr = gfileRead(jd->img->f, seg + ofs, JD_SZBUF - (unsigned)ofs);
+ jd->dptr = seg + ofs - 1;
+ }
+
+ return GDISP_IMAGE_ERR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */
+
+ case 0xC1: /* SOF1 */
+ case 0xC2: /* SOF2 */
+ case 0xC3: /* SOF3 */
+ case 0xC5: /* SOF5 */
+ case 0xC6: /* SOF6 */
+ case 0xC7: /* SOF7 */
+ case 0xC9: /* SOF9 */
+ case 0xCA: /* SOF10 */
+ case 0xCB: /* SOF11 */
+ case 0xCD: /* SOF13 */
+ case 0xCE: /* SOF14 */
+ case 0xCF: /* SOF15 */
+ case 0xD9: /* EOI */
+ return GDISP_IMAGE_ERR_UNSUPPORTED; /* Unsuppoted JPEG standard (may be progressive JPEG) */
+
+ default: /* Unknown segment (comment, exif or etc..) */
+ /* Skip segment data */
+ gfileSetPos(jd->img->f, gfileGetPos(jd->img->f)+len);
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------*/
+/* Start to decompress the JPEG picture */
+/*-----------------------------------------------------------------------*/
+
+gdispImageError jd_decomp (
+ JDEC* jd, /* Initialized decompression object */
+ unsigned (*outfunc)(gdispImage*, void*, JRECT*), /* RGB output function */
+ uint8_t scale /* Output de-scaling factor (0 to 3) */
+)
+{
+ unsigned x, y, mx, my;
+ uint16_t rst, rsc;
+ gdispImageError rc;
+
+
+ if (scale > (JD_USE_SCALE ? 3 : 0)) return GDISP_IMAGE_ERR_UNSUPPORTED;
+ jd->scale = scale;
+
+ mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */
+
+ jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */
+ rst = rsc = 0;
+
+ rc = GDISP_IMAGE_ERR_OK;
+ for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */
+ for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */
+ if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */
+ rc = restart(jd, rsc++);
+ if (rc != GDISP_IMAGE_ERR_OK) return rc;
+ rst = 1;
+ }
+ rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream and apply IDCT) */
+ if (rc != GDISP_IMAGE_ERR_OK) return rc;
+ rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (color space conversion, scaling and output) */
+ if (rc != GDISP_IMAGE_ERR_OK) return rc;
+ }
+ }
+
+ return rc;
+}
+
#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG */
diff --git a/src/ginput/ginput_keyboard.c b/src/ginput/ginput_keyboard.c
index 0018dafb..141bdd2b 100644
--- a/src/ginput/ginput_keyboard.c
+++ b/src/ginput/ginput_keyboard.c
@@ -471,7 +471,7 @@ void _gkeyboardInit(void) {
static const GKeyboardVMT * const dclist[] = {GINPUT_KEYBOARD_DRIVER_LIST};
for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) {
- if (!(dclist[i]->d.flags & GKEYBOARD_VFLG_DYNAMICONLY))
+ if (!(dclist[i]->d.flags & GKEYBOARD_VFLG_DYNAMICONLY))
gdriverRegister(&dclist[i]->d, 0);
}
}
@@ -481,8 +481,8 @@ void _gkeyboardInit(void) {
{
extern const GKeyboardVMT const GKEYBOARDVMT_OnlyOne[1];
- if (!(GKEYBOARDVMT_OnlyOne->d.flags & GKEYBOARD_VFLG_DYNAMICONLY))
- gdriverRegister(&GKEYBOARDVMT_OnlyOne->d, 0);
+ if (!(GKEYBOARDVMT_OnlyOne->d.flags & GKEYBOARD_VFLG_DYNAMICONLY))
+ gdriverRegister(&GKEYBOARDVMT_OnlyOne->d, 0);
}
#endif
@@ -493,43 +493,43 @@ void _gkeyboardDeinit(void) {
}
bool_t _gkeyboardInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) {
- #define k ((GKeyboard *)g)
+ #define k ((GKeyboard *)g)
(void) param;
- (void) systeminstance;
+ (void) systeminstance;
// The initial keyboard layout comes from the VMT
k->pLayout = gkvmt(k)->defLayout;
// Init the mouse
- if (!gkvmt(k)->init((GKeyboard *)g, driverinstance))
- return FALSE;
+ if (!gkvmt(k)->init((GKeyboard *)g, driverinstance))
+ return FALSE;
// Ensure the Poll timer is started
if (!gtimerIsActive(&KeyboardTimer))
gtimerStart(&KeyboardTimer, KeyboardPoll, 0, TRUE, GINPUT_KEYBOARD_POLL_PERIOD);
- return TRUE;
+ return TRUE;
- #undef k
+ #undef k
}
void _gkeyboardPostInitDriver(GDriver *g) {
- #define k ((GKeyboard *)g)
+ #define k ((GKeyboard *)g)
// Run the init sequence from the layout microcode.
microengine(k, 0, FLAG_INIT);
- #undef k
+ #undef k
}
void _gkeyboardDeInitDriver(GDriver *g) {
- (void) g;
+ (void) g;
}
GSourceHandle ginputGetKeyboard(unsigned instance) {
if (instance == GKEYBOARD_ALL_INSTANCES)
return (GSourceHandle)&KeyboardTimer;
- return (GSourceHandle)gdriverGetInstance(GDRIVER_TYPE_KEYBOARD, instance);
+ return (GSourceHandle)gdriverGetInstance(GDRIVER_TYPE_KEYBOARD, instance);
}
bool_t ginputGetKeyboardStatus(unsigned instance, GEventKeyboard *pe) {
diff --git a/src/gos/gos_chibios.c b/src/gos/gos_chibios.c
index 8bad8ed6..cb2193fe 100644
--- a/src/gos/gos_chibios.c
+++ b/src/gos/gos_chibios.c
@@ -141,10 +141,10 @@ bool_t gfxSemWaitI(gfxSem *psem)
{
#if (CH_KERNEL_MAJOR == 2) || (CH_KERNEL_MAJOR == 3)
if (psem->sem.s_cnt <= 0)
- return GFalse;
+ return FALSE;
#elif (CH_KERNEL_MAJOR == 4)
if (psem->sem.cnt <= 0)
- return GFalse;
+ return FALSE;
#endif
chSemFastWaitI(&psem->sem);
return TRUE;
diff --git a/src/gwin/gwin_image.c b/src/gwin/gwin_image.c
index 900e7f61..855eecc9 100644
--- a/src/gwin/gwin_image.c
+++ b/src/gwin/gwin_image.c
@@ -19,6 +19,10 @@
#define gw ((GImageObject *)gh)
static void ImageDestroy(GWindowObject *gh) {
+ // Stop the timer
+ #if GWIN_NEED_IMAGE_ANIMATION
+ gtimerStop(&gw->timer);
+ #endif
if (gdispImageIsOpen(&gw->image))
gdispImageClose(&gw->image);
}
@@ -129,7 +133,7 @@ GHandle gwinGImageCreate(GDisplay *g, GImageObject *gobj, GWindowInit *pInit) {
#if GWIN_NEED_IMAGE_ANIMATION
gtimerInit(&gobj->timer);
#endif
-
+
gwinSetVisible((GHandle)gobj, pInit->show);
return (GHandle)gobj;
diff --git a/src/gwin/gwin_textedit.c b/src/gwin/gwin_textedit.c
index 8e639300..61c28c66 100644
--- a/src/gwin/gwin_textedit.c
+++ b/src/gwin/gwin_textedit.c
@@ -26,54 +26,54 @@
#define gh2obj ((GTexteditObject *)gh)
#define gw2obj ((GTexteditObject *)gw)
-static void TextEditRemoveChar(GWidgetObject* gw) {
+static void TextEditRemoveChar(GHandle gh) {
char *p;
const char *q;
unsigned sz;
unsigned pos;
- sz = strlen(gw->text);
- pos = gw2obj->cursorPos;
- q = gw->text+pos;
+ sz = strlen(gh2obj->w.text);
+ pos = gh2obj->cursorPos;
+ q = gh2obj->w.text+pos;
- if (!(gw->g.flags & GWIN_FLG_ALLOCTXT)) {
+ if (!(gh->flags & GWIN_FLG_ALLOCTXT)) {
// Allocate and then copy
if (!(p = gfxAlloc(sz)))
return;
if (pos)
- memcpy(p, gw->text, pos);
+ memcpy(p, gh2obj->w.text, pos);
memcpy(p+pos, q+1, sz-pos);
- gw->g.flags |= GWIN_FLG_ALLOCTXT;
+ gh->flags |= GWIN_FLG_ALLOCTXT;
} else {
// Copy and then reallocate
memcpy((char *)q, q+1, sz-pos);
- if (!(p = gfxRealloc((char *)gw->text, sz+1, sz))) // This should never fail as we are making it smaller
+ if (!(p = gfxRealloc((char *)gh2obj->w.text, sz+1, sz))) // This should never fail as we are making it smaller
return;
}
- gw->text = p;
+ gh2obj->w.text = p;
}
-static bool_t TextEditAddChars(GWidgetObject* gw, unsigned cnt) {
+static bool_t TextEditAddChars(GHandle gh, unsigned cnt) {
char *p;
const char *q;
unsigned sz;
unsigned pos;
// Get the size of the text buffer
- sz = strlen(gw->text)+1;
- pos = gw2obj->cursorPos;
+ sz = strlen(gh2obj->w.text)+1;
+ pos = gh2obj->cursorPos;
- if (!(gw->g.flags & GWIN_FLG_ALLOCTXT)) {
+ if (!(gh->flags & GWIN_FLG_ALLOCTXT)) {
if (!(p = gfxAlloc(sz+cnt)))
return FALSE;
- memcpy(p, gw->text, pos);
- memcpy(p+pos+cnt, gw->text+pos, sz-pos);
- gw->g.flags |= GWIN_FLG_ALLOCTXT;
- gw->text = p;
+ memcpy(p, gh2obj->w.text, pos);
+ memcpy(p+pos+cnt, gh2obj->w.text+pos, sz-pos);
+ gh->flags |= GWIN_FLG_ALLOCTXT;
+ gh2obj->w.text = p;
} else {
- if (!(p = gfxRealloc((char *)gw->text, sz, sz+cnt)))
+ if (!(p = gfxRealloc((char *)gh2obj->w.text, sz, sz+cnt)))
return FALSE;
- gw->text = p;
+ gh2obj->w.text = p;
q = p+pos;
p += sz;
while(--p >= q)
@@ -117,76 +117,13 @@ static void TextEditMouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
// Is it a special key?
if (pke->keystate & GKEYSTATE_SPECIAL) {
-
// Arrow keys to move the cursor
- switch ((uint8_t)pke->c[0]) {
- case GKEY_LEFT:
- if (!gw2obj->cursorPos)
- return;
- gw2obj->cursorPos--;
- break;
- case GKEY_RIGHT:
- if (!gw->text[gw2obj->cursorPos])
- return;
- gw2obj->cursorPos++;
- break;
- case GKEY_HOME:
- if (!gw2obj->cursorPos)
- return;
- gw2obj->cursorPos = 0;
- break;
- case GKEY_END:
- if (!gw->text[gw2obj->cursorPos])
- return;
- gw2obj->cursorPos = strlen(gw->text);
- break;
- default:
- return;
- }
-
- } else {
+ gwinTextEditSendSpecialKey(&gw->g, (uint8_t)pke->c[0]);
+ return;
- // Normal key press
- switch((uint8_t)pke->c[0]) {
- case GKEY_BACKSPACE:
- // Backspace
- if (!gw2obj->cursorPos)
- return;
- gw2obj->cursorPos--;
- TextEditRemoveChar(gw);
- break;
- case GKEY_TAB:
- case GKEY_LF:
- case GKEY_CR:
- // Move to the next field
- _gwinMoveFocus();
- return;
- case GKEY_DEL:
- // Delete
- if (!gw->text[gw2obj->cursorPos])
- return;
- TextEditRemoveChar(gw);
- break;
- default:
- // Ignore any other control characters
- if ((uint8_t)pke->c[0] < GKEY_SPACE)
- return;
-
- // Keep the edit length to less than the maximum
- if (gw2obj->maxSize && strlen(gw->text)+pke->bytecount > gw2obj->maxSize)
- return;
-
- // Make space
- if (TextEditAddChars(gw, pke->bytecount)) {
- // Insert the characters
- memcpy((char *)gw->text+gw2obj->cursorPos, pke->c, pke->bytecount);
- gw2obj->cursorPos += pke->bytecount;
- }
- break;
- }
}
- _gwinUpdate((GHandle)gw);
+ gwinTextEditSendKey(&gw->g, pke->c, pke->bytecount);
}
#endif
@@ -246,6 +183,89 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit
return (GHandle)wt;
}
+#if (GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD) || GWIN_NEED_KEYBOARD
+ void gwinTextEditSendSpecialKey(GHandle gh, uint8_t key) {
+ // Is it a valid handle?
+ if (gh->vmt != (gwinVMT*)&texteditVMT)
+ return;
+
+ // Arrow keys to move the cursor
+ switch (key) {
+ case GKEY_LEFT:
+ if (!gh2obj->cursorPos)
+ return;
+ gh2obj->cursorPos--;
+ break;
+ case GKEY_RIGHT:
+ if (!gh2obj->w.text[gh2obj->cursorPos])
+ return;
+ gh2obj->cursorPos++;
+ break;
+ case GKEY_HOME:
+ if (!gh2obj->cursorPos)
+ return;
+ gh2obj->cursorPos = 0;
+ break;
+ case GKEY_END:
+ if (!gh2obj->w.text[gh2obj->cursorPos])
+ return;
+ gh2obj->cursorPos = strlen(gh2obj->w.text);
+ break;
+ default:
+ return;
+ }
+
+ _gwinUpdate(gh);
+ }
+
+ void gwinTextEditSendKey(GHandle gh, char *key, unsigned len) {
+ // Is it a valid handle?
+ if (gh->vmt != (gwinVMT*)&texteditVMT || !key || !len)
+ return;
+
+ // Normal key press
+ switch((uint8_t)key[0]) {
+ case GKEY_BACKSPACE:
+ // Backspace
+ if (!gh2obj->cursorPos)
+ return;
+ gh2obj->cursorPos--;
+ TextEditRemoveChar(gh);
+ break;
+ case GKEY_TAB:
+ case GKEY_LF:
+ case GKEY_CR:
+ // Move to the next field
+ _gwinMoveFocus();
+ return;
+ case GKEY_DEL:
+ // Delete
+ if (!gh2obj->w.text[gh2obj->cursorPos])
+ return;
+ TextEditRemoveChar(gh);
+ break;
+ default:
+ // Ignore any other control characters
+ if ((uint8_t)key[0] < GKEY_SPACE)
+ return;
+
+ // Keep the edit length to less than the maximum
+ if (gh2obj->maxSize && strlen(gh2obj->w.text)+len > gh2obj->maxSize)
+ return;
+
+ // Make space
+ if (TextEditAddChars(gh, len)) {
+ // Insert the characters
+ memcpy((char *)gh2obj->w.text+gh2obj->cursorPos, key, len);
+ gh2obj->cursorPos += len;
+ }
+ break;
+ }
+
+ _gwinUpdate(gh);
+ }
+#endif
+
void gwinTexteditDefaultDraw(GWidgetObject* gw, void* param)
{
const char* p;
diff --git a/src/gwin/gwin_textedit.h b/src/gwin/gwin_textedit.h
index ed2f634d..8cf6c03e 100644
--- a/src/gwin/gwin_textedit.h
+++ b/src/gwin/gwin_textedit.h
@@ -64,6 +64,32 @@ GHandle gwinGTexteditCreate(GDisplay* g, GTexteditObject* wt, GWidgetInit* pInit
#define gwinTexteditCreate(wt, pInit, maxSize) gwinGTexteditCreate(GDISP, wt, pInit, maxSize)
/**
+ * @brief Send a special key to the textedit such as GKEY_LEFT, GKEY_RIGHT, GKEY_HOME, GKEY_END
+ *
+ * @param[in] gh The window handle (must be a textedit window)
+ * @param[in] key The special key to send.
+ * @pre Requires GINPUT_NEED_KEYBOARD or GWIN_NEED_KEYBOARD to be on
+ * @api
+ */
+void gwinTextEditSendSpecialKey(GHandle gh, uint8_t key);
+
+/**
+ * @brief Send a normal utf8 character to the textedit
+ *
+ * @param[in] gh The window handle (must be a textedit window)
+ * @param[in] pkey The pointer to the utf8 character to send.
+ * @param[in] len The length of the utf8 character in bytes.
+ * @note This must ONLY be called with a single utf8 character at a time. Don't attempt to
+ * send a string of characters in the one call.
+ * @note Characters are interpreted as if they came directly from a keyboard ie a backspace
+ * character will perform the backspace operation, a tab will send the focus to the next
+ * control etc.
+ * @pre Requires GINPUT_NEED_KEYBOARD or GWIN_NEED_KEYBOARD to be on
+ * @api
+ */
+void gwinTextEditSendKey(GHandle gh, char *pkey, unsigned len);
+
+/**
* @defgroup Renderings_Textedit Renderings
*
* @brief Built-in rendering functions for the textedit widget.
diff --git a/src/gwin/gwin_widget.h b/src/gwin/gwin_widget.h
index ece98a06..ce7490cd 100644
--- a/src/gwin/gwin_widget.h
+++ b/src/gwin/gwin_widget.h
@@ -356,6 +356,20 @@ bool_t gwinAttachListener(GListener *pl);
* @api
*/
bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance);
+ /**
+ * @brief Detach a toggle from a widget
+ * @return TRUE on success
+ *
+ * @param[in] gh The widget handle
+ * @param[in] role The function the toggle will perform for the widget
+ *
+ * @note See the documentation on the specific widget to see the possible
+ * values for the role parameter. If it is out of range, this function
+ * will return FALSE
+ *
+ * @api
+ */
+ bool_t gwinDetachToggle(GHandle gh, uint16_t role);
#endif
#if (GFX_USE_GINPUT && GINPUT_NEED_DIAL) || defined(__DOXYGEN__)
diff --git a/tools/gmake_scripts/os_osx.mk b/tools/gmake_scripts/os_osx.mk
index 9bfc7024..b5f99496 100644
--- a/tools/gmake_scripts/os_osx.mk
+++ b/tools/gmake_scripts/os_osx.mk
@@ -9,10 +9,10 @@
# Requirements:
#
-# OSX_SDK The location of the SDK eg. OSX_SDK = /Developer/SDKs/MacOSX10.7.sdk
-# OSX_ARCH The architecture flags eg. OSX_ARCH = -mmacosx-version-min=10.3
+# OSX_SDK The location of the SDK eg. OSX_SDK = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk
+# OSX_ARCH The architecture flags eg. OSX_ARCH = -mmacosx-version-min=10.12
#
SRCFLAGS += -isysroot $(OSX_SDK) $(OSX_ARCH)
LDFLAGS += -pthread -Wl,-syslibroot,$(OSX_SDK) $(OSX_ARCH)
-OPT_CPU = x86
+OPT_CPU = x64