From 20ce465eb5a25dbc318ef6872bc3c8137954c463 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 3 Dec 2021 21:44:21 +1100 Subject: [PATCH 1/5] WIP on implementing system fonts in webclient Although either memory or canvas gets corrupted.. still trying to work out what's going on there --- src/Drawer2D.c | 29 ++++++++++++++++++--- src/Menus.c | 5 ---- src/interop_web.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/Drawer2D.c b/src/Drawer2D.c index ae5e448db..8b4d385fc 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -684,18 +684,39 @@ cc_string Font_Lookup(const cc_string* fontName, int flags) { return String_Empt cc_result Font_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) { desc->size = size; desc->flags = flags; - desc->height = 0; + desc->height = size; + + desc->handle = Mem_TryAlloc(fontName->length + 1, 1); + if (!desc->handle) return 0; + + String_CopyToRaw(desc->handle, fontName->length + 1, fontName); return 0; } -void Font_MakeDefault(struct FontDesc* desc, int size, int flags) { Font_Make(desc, NULL, size, flags); } +void Font_MakeDefault(struct FontDesc* desc, int size, int flags) { + font_candidates[0].length = 0; + String_AppendConst(&font_candidates[0], "serif"); + Font_Make(desc, &font_candidates[0], size, flags); +} void Font_Free(struct FontDesc* desc) { + Mem_Free(desc->handle); + desc->handle = NULL; desc->size = 0; } void SysFonts_Register(const cc_string* path) { } -static int Font_SysTextWidth(struct DrawTextArgs* args) { return 0; } -static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) { } +extern int interop_TextWidth(const char* font, int size, const char* text, const int len); +extern void interop_TextDraw(const char* font, int size, const char* text, const int len, + struct Bitmap* bmp, int x, int y); + +static int Font_SysTextWidth(struct DrawTextArgs* args) { + struct FontDesc* font = args->font; + return interop_TextWidth(font->handle, font->size, args->text.buffer, args->text.length); +} +static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) { + struct FontDesc* font = args->font; + interop_TextDraw(font->handle, font->size, args->text.buffer, args->text.length, bmp, x, y); +} #else #include "freetype/ft2build.h" #include "freetype/freetype.h" diff --git a/src/Menus.c b/src/Menus.c index 1b6cec38c..86775ddca 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -2934,11 +2934,6 @@ static void GuiOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { s->numCore = 7; s->maxVertices += 7 * BUTTONWIDGET_MAX; MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); -#ifdef CC_BUILD_WEB - /* TODO: Support system fonts in webclient */ - s->buttons[5].disabled = true; - s->buttons[6].disabled = true; -#endif } void GuiOptionsScreen_Show(void) { diff --git a/src/interop_web.js b/src/interop_web.js index 877e6f133..fc7bc19e1 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -815,4 +815,68 @@ mergeInto(LibraryManager.library, { var str = AUDIO.errors[errCode - 1]; return stringToUTF8(str, buffer, bufferLen); }, + + +//######################################################################################################################## +//-----------------------------------------------------------Font--------------------------------------------------------- +//######################################################################################################################## + interop_TextInit: function(font, size) { + if (!window.FONT_CANVAS) { + window.FONT_CANVAS = document.createElement('canvas'); + window.FONT_CONTEXT = window.FONT_CANVAS.getContext('2d'); + } + + var ctx = window.FONT_CONTEXT; + ctx.font = size + 'px ' + font; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + return ctx; + }, + interop_TextWidth__deps: ['interop_TextInit'], + interop_TextWidth: function(fontStr, fontSize, textStr, textLen) { + var font = UTF8ToString(fontStr); + var text = UTF8ArrayToString(HEAPU8, textStr, textLen); + + var ctx = _interop_TextInit(font, fontSize); + var data = ctx.measureText(text); + return Math.ceil(data.width)|0; + }, + interop_TextDraw__deps: ['interop_TextInit'], + interop_TextDraw: function(fontStr, fontSize, textStr, textLen, bmp, dstX, dstY) { + var font = UTF8ToString(fontStr); + var text = UTF8ArrayToString(HEAPU8, textStr, textLen); + + var ctx = _interop_TextInit(font, fontSize); + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.fillStyle = "#000000"; + ctx.fillText(text, 0, 0); + + var dst_pixels = HEAP32[(bmp|0 + 0)>>2]; + var dst_width = HEAP32[(bmp|0 + 4)>>2]; + var dst_height = HEAP32[(bmp|0 + 8)>>2]; + + // TODO not all of it + var src = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); + var src_pixels = src.data; + var src_width = src.width|0; + var src_height = src.height|0; + + var img_width = Math.min(src_width, dst_width); + var img_height = Math.min(src_height, dst_height); + + for (var y = 0; y < img_height; y++) + { + var src_row = (y*(src_width << 2))|0; + var dst_row = dst_pixels + (y*(dst_width << 2))|0; + + for (var x = 0; x < img_width; x++) + { + HEAPU8[dst_row + (x<<2)+0] = src_pixels[src_row + (x<<2)+0]; + HEAPU8[dst_row + (x<<2)+1] = src_pixels[src_row + (x<<2)+1]; + HEAPU8[dst_row + (x<<2)+2] = src_pixels[src_row + (x<<2)+2]; + HEAPU8[dst_row + (x<<2)+3] = 255; + } + } + }, }); \ No newline at end of file From 7477751096279bd4b2ca83fca9618dedddc4cd9b Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 3 Dec 2021 22:48:14 +1100 Subject: [PATCH 2/5] Webclient: Add list of common sytem fonts --- src/Drawer2D.c | 28 +++++++++++++++++++++++----- src/Drawer2D.h | 2 -- src/Menus.c | 33 +++------------------------------ src/String.c | 26 ++++++++++++++++++++++++++ src/String.h | 2 ++ 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/Drawer2D.c b/src/Drawer2D.c index 8b4d385fc..a23d250b3 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -678,8 +678,20 @@ struct IGameComponent Drawer2D_Component = { *#########################################################################################################################*/ #ifdef CC_BUILD_WEB const cc_string* Font_UNSAFE_GetDefault(void) { return &font_candidates[0]; } -void Font_GetNames(struct StringsBuffer* buffer) { } -cc_string Font_Lookup(const cc_string* fontName, int flags) { return String_Empty; } + +void Font_GetNames(struct StringsBuffer* buffer) { + static const char* font_names[] = { + "Arial", "Arial Black", "Courier New", "Comic Sans MS", "Georgia", "Garamond", + "Helvetica", "Impact", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana", + "cursive", "fantasy", "monospace", "sans-serif", "serif", "system-ui" + }; + int i; + + for (i = 0; i < Array_Elems(font_names); i++) { + cc_string str = String_FromReadonly(font_names[i]); + StringsBuffer_Add(buffer, &str); + } +} cc_result Font_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) { desc->size = size; @@ -692,10 +704,13 @@ cc_result Font_Make(struct FontDesc* desc, const cc_string* fontName, int size, String_CopyToRaw(desc->handle, fontName->length + 1, fontName); return 0; } + void Font_MakeDefault(struct FontDesc* desc, int size, int flags) { - font_candidates[0].length = 0; - String_AppendConst(&font_candidates[0], "serif"); - Font_Make(desc, &font_candidates[0], size, flags); + cc_string str = font_candidates[0]; + /* Fallback to arial */ + if (!str.length) str = font_candidates[1]; + + Font_Make(desc, &str, size, flags); } void Font_Free(struct FontDesc* desc) { @@ -727,6 +742,8 @@ static FT_Library ft_lib; static struct FT_MemoryRec_ ft_mem; static struct StringsBuffer font_list; static cc_bool fonts_changed; +/* Finds the path and face number of the given system font, with closest matching style */ +static cc_string Font_Lookup(const cc_string* fontName, int flags); struct SysFont { FT_Face face; @@ -973,6 +990,7 @@ void Font_GetNames(struct StringsBuffer* buffer) { name.length -= 2; StringsBuffer_Add(buffer, &name); } + StringsBuffer_Sort(buffer); } static cc_string Font_LookupOf(const cc_string* fontName, const char type) { diff --git a/src/Drawer2D.h b/src/Drawer2D.h index ae7811c9f..fa81c22a4 100644 --- a/src/Drawer2D.h +++ b/src/Drawer2D.h @@ -109,8 +109,6 @@ CC_API void Font_GetNames(struct StringsBuffer* buffer); /* Sets padding for a bitmapped font */ void Font_SetPadding(struct FontDesc* desc, int amount); -/* Finds the path and face number of the given system font, with closest matching style */ -cc_string Font_Lookup(const cc_string* fontName, int flags); /* Allocates a new system font from the given arguments */ cc_result Font_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags); /* Allocates a new system font from the given arguments using default system font */ diff --git a/src/Menus.c b/src/Menus.c index 86775ddca..27436ecd5 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -289,32 +289,6 @@ static void ListScreen_MoveForwards(void* screen, void* b) { ListScreen_PageClick(s, true); } -static void ListScreen_QuickSort(int left, int right) { - struct StringsBuffer* buffer = &ListScreen.entries; - cc_uint32* keys = buffer->flagsBuffer; cc_uint32 key; - - while (left < right) { - int i = left, j = right; - cc_string pivot = StringsBuffer_UNSAFE_Get(buffer, (i + j) >> 1); - cc_string strI, strJ; - - /* partition the list */ - while (i <= j) { - while ((strI = StringsBuffer_UNSAFE_Get(buffer, i), String_Compare(&pivot, &strI)) > 0) i++; - while ((strJ = StringsBuffer_UNSAFE_Get(buffer, j), String_Compare(&pivot, &strJ)) < 0) j--; - QuickSort_Swap_Maybe(); - } - /* recurse into the smaller subset */ - QuickSort_Recurse(ListScreen_QuickSort) - } -} - -CC_NOINLINE static void ListScreen_Sort(struct ListScreen* s) { - if (s->entries.count) { - ListScreen_QuickSort(0, s->entries.count - 1); - } -} - static cc_string ListScreen_UNSAFE_GetCur(struct ListScreen* s, void* widget) { int i = Screen_Index(s, widget); return ListScreen_UNSAFE_Get(s, s->currentIndex + i); @@ -1583,7 +1557,7 @@ static void TexturePackScreen_FilterFiles(const cc_string* path, void* obj) { static void TexturePackScreen_LoadEntries(struct ListScreen* s) { static const cc_string path = String_FromConst(TEXPACKS_DIR); Directory_Enum(&path, &s->entries, TexturePackScreen_FilterFiles); - ListScreen_Sort(s); + StringsBuffer_Sort(&s->entries); } #ifdef CC_BUILD_WEB @@ -1648,7 +1622,6 @@ static void FontListScreen_UpdateEntry(struct ListScreen* s, struct ButtonWidget static void FontListScreen_LoadEntries(struct ListScreen* s) { Font_GetNames(&s->entries); - ListScreen_Sort(s); ListScreen_Select(s, Font_UNSAFE_GetDefault()); } @@ -1720,7 +1693,7 @@ static void HotkeyListScreen_LoadEntries(struct ListScreen* s) { /* Placeholder for 'add new hotkey' */ StringsBuffer_Add(&s->entries, &String_Empty); - ListScreen_Sort(s); + StringsBuffer_Sort(&s->entries); } static void HotkeyListScreen_UpdateEntry(struct ListScreen* s, struct ButtonWidget* button, const cc_string* text) { @@ -1774,7 +1747,7 @@ static void LoadLevelScreen_FilterFiles(const cc_string* path, void* obj) { static void LoadLevelScreen_LoadEntries(struct ListScreen* s) { static const cc_string path = String_FromConst("maps"); Directory_Enum(&path, &s->entries, LoadLevelScreen_FilterFiles); - ListScreen_Sort(s); + StringsBuffer_Sort(&s->entries); } #ifdef CC_BUILD_WEB diff --git a/src/String.c b/src/String.c index 8ddf7ac72..f5da50533 100644 --- a/src/String.c +++ b/src/String.c @@ -872,6 +872,32 @@ void StringsBuffer_Remove(struct StringsBuffer* buffer, int index) { buffer->totalLength -= len; } +static struct StringsBuffer* sort_buffer; +static void StringsBuffer_QuickSort(int left, int right) { + struct StringsBuffer* buffer = sort_buffer; + cc_uint32* keys = buffer->flagsBuffer; cc_uint32 key; + + while (left < right) { + int i = left, j = right; + cc_string pivot = StringsBuffer_UNSAFE_Get(buffer, (i + j) >> 1); + cc_string strI, strJ; + + /* partition the list */ + while (i <= j) { + while ((strI = StringsBuffer_UNSAFE_Get(buffer, i), String_Compare(&pivot, &strI)) > 0) i++; + while ((strJ = StringsBuffer_UNSAFE_Get(buffer, j), String_Compare(&pivot, &strJ)) < 0) j--; + QuickSort_Swap_Maybe(); + } + /* recurse into the smaller subset */ + QuickSort_Recurse(StringsBuffer_QuickSort) + } +} + +void StringsBuffer_Sort(struct StringsBuffer* buffer) { + sort_buffer = buffer; + StringsBuffer_QuickSort(0, buffer->count - 1); +} + /*########################################################################################################################* *------------------------------------------------------Word wrapper-------------------------------------------------------* diff --git a/src/String.h b/src/String.h index 0453f8de1..93c0606ff 100644 --- a/src/String.h +++ b/src/String.h @@ -241,6 +241,8 @@ STRING_REF void StringsBuffer_UNSAFE_GetRaw(struct StringsBuffer* buffer, int i, CC_API void StringsBuffer_Add(struct StringsBuffer* buffer, const cc_string* str); /* Removes the i'th string from the given buffer, shifting following strings downwards */ CC_API void StringsBuffer_Remove(struct StringsBuffer* buffer, int index); +/* Sorts all the entries in the given buffer using String_Compare */ +void StringsBuffer_Sort(struct StringsBuffer* buffer); /* Performs line wrapping on the given string. */ /* e.g. "some random tex|t* (| is lineLen) becomes "some random" "text" */ From ecd733cd31e58bca16ba83e779d18eb043f38a9b Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Wed, 8 Dec 2021 12:22:26 +1100 Subject: [PATCH 3/5] What's operator precedence anyways? Fixes game crashing due to heap corruption due to reading dst_height from wrong address (which would set dst_height to e.g. 7387141), which would then cause img_height to be set to 150, which would hence write to memory used by other data (as destination bitmap was actually only 64 pixels tall) --- src/interop_web.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/interop_web.js b/src/interop_web.js index fc7bc19e1..9b3bdcfd5 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -847,14 +847,15 @@ mergeInto(LibraryManager.library, { var text = UTF8ArrayToString(HEAPU8, textStr, textLen); var ctx = _interop_TextInit(font, fontSize); - ctx.fillStyle = "#ffffff"; + ctx.fillStyle = "#ffffff"; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.fillStyle = "#000000"; + ctx.fillStyle = "#000000"; ctx.fillText(text, 0, 0); - var dst_pixels = HEAP32[(bmp|0 + 0)>>2]; - var dst_width = HEAP32[(bmp|0 + 4)>>2]; - var dst_height = HEAP32[(bmp|0 + 8)>>2]; + bmp = bmp|0; + var dst_pixels = HEAP32[(bmp + 0|0)>>2]; + var dst_width = HEAP32[(bmp + 4|0)>>2]; + var dst_height = HEAP32[(bmp + 8|0)>>2]; // TODO not all of it var src = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); From c18e8e9649210f1f2ae60332ebf15d097927a2fd Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 10 Dec 2021 22:01:54 +1100 Subject: [PATCH 4/5] Text now renders somewhat passibly --- src/interop_web.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/interop_web.js b/src/interop_web.js index 9b3bdcfd5..c0b6eea97 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -845,11 +845,16 @@ mergeInto(LibraryManager.library, { interop_TextDraw: function(fontStr, fontSize, textStr, textLen, bmp, dstX, dstY) { var font = UTF8ToString(fontStr); var text = UTF8ArrayToString(HEAPU8, textStr, textLen); + var ctx = _interop_TextInit(font, fontSize); - var ctx = _interop_TextInit(font, fontSize); + // resize canvas if necessary so test fits + var data = ctx.measureText(text); + var text_width = Math.ceil(data.width)|0; + if (text_width > ctx.canvas.width) + ctx.canvas.width = text_width; + + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.fillStyle = "#ffffff"; - ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.fillStyle = "#000000"; ctx.fillText(text, 0, 0); bmp = bmp|0; @@ -876,7 +881,7 @@ mergeInto(LibraryManager.library, { HEAPU8[dst_row + (x<<2)+0] = src_pixels[src_row + (x<<2)+0]; HEAPU8[dst_row + (x<<2)+1] = src_pixels[src_row + (x<<2)+1]; HEAPU8[dst_row + (x<<2)+2] = src_pixels[src_row + (x<<2)+2]; - HEAPU8[dst_row + (x<<2)+3] = 255; + HEAPU8[dst_row + (x<<2)+3] = src_pixels[src_row + (x<<2)+3]; } } }, From a3a0e305925207de1f7fee162c63d4eadba1271f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 11 Dec 2021 09:22:19 +1100 Subject: [PATCH 5/5] Implement system font position, bold, non-ascii characters --- src/Drawer2D.c | 19 ++++++++++++----- src/interop_web.js | 52 +++++++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/Drawer2D.c b/src/Drawer2D.c index a23d250b3..11da59a02 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -720,17 +720,26 @@ void Font_Free(struct FontDesc* desc) { } void SysFonts_Register(const cc_string* path) { } -extern int interop_TextWidth(const char* font, int size, const char* text, const int len); -extern void interop_TextDraw(const char* font, int size, const char* text, const int len, - struct Bitmap* bmp, int x, int y); +extern void interop_SetFont(const char* font, int size, int flags); +extern int interop_TextWidth(const char* text, const int len); +extern void interop_TextDraw(const char* text, const int len, struct Bitmap* bmp, int x, int y, cc_bool shadow); static int Font_SysTextWidth(struct DrawTextArgs* args) { struct FontDesc* font = args->font; - return interop_TextWidth(font->handle, font->size, args->text.buffer, args->text.length); + char buffer[NATIVE_STR_LEN]; + int len = Platform_EncodeUtf8(buffer, &args->text); + + interop_SetFont(font->handle, font->size, font->flags); + return interop_TextWidth(buffer, len); } + static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) { struct FontDesc* font = args->font; - interop_TextDraw(font->handle, font->size, args->text.buffer, args->text.length, bmp, x, y); + char buffer[NATIVE_STR_LEN]; + int len = Platform_EncodeUtf8(buffer, &args->text); + + interop_SetFont(font->handle, font->size, font->flags); + interop_TextDraw(buffer, len, bmp, x, y, shadow); } #else #include "freetype/ft2build.h" diff --git a/src/interop_web.js b/src/interop_web.js index c0b6eea97..545255436 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -820,45 +820,53 @@ mergeInto(LibraryManager.library, { //######################################################################################################################## //-----------------------------------------------------------Font--------------------------------------------------------- //######################################################################################################################## - interop_TextInit: function(font, size) { + interop_SetFont: function(fontStr, size, flags) { if (!window.FONT_CANVAS) { window.FONT_CANVAS = document.createElement('canvas'); window.FONT_CONTEXT = window.FONT_CANVAS.getContext('2d'); } - var ctx = window.FONT_CONTEXT; - ctx.font = size + 'px ' + font; + var prefix = ''; + if (flags & 1) prefix += 'Bold '; + + var font = UTF8ToString(fontStr); + var ctx = window.FONT_CONTEXT; + ctx.font = prefix + size + 'px ' + font; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; return ctx; }, - interop_TextWidth__deps: ['interop_TextInit'], - interop_TextWidth: function(fontStr, fontSize, textStr, textLen) { - var font = UTF8ToString(fontStr); + interop_TextWidth: function(textStr, textLen) { var text = UTF8ArrayToString(HEAPU8, textStr, textLen); - - var ctx = _interop_TextInit(font, fontSize); + var ctx = window.FONT_CONTEXT; var data = ctx.measureText(text); return Math.ceil(data.width)|0; }, - interop_TextDraw__deps: ['interop_TextInit'], - interop_TextDraw: function(fontStr, fontSize, textStr, textLen, bmp, dstX, dstY) { - var font = UTF8ToString(fontStr); + interop_TextDraw: function(textStr, textLen, bmp, dstX, dstY, shadow) { var text = UTF8ArrayToString(HEAPU8, textStr, textLen); - var ctx = _interop_TextInit(font, fontSize); + var ctx = window.FONT_CONTEXT; // resize canvas if necessary so test fits var data = ctx.measureText(text); var text_width = Math.ceil(data.width)|0; if (text_width > ctx.canvas.width) - ctx.canvas.width = text_width; + ctx.canvas.width = text_width; + + var text_offset = 0.0; + ctx.fillStyle = "#ffffff"; + if (shadow) { + text_offset = 1.3; + ctx.fillStyle = "#7f7f7f"; + } ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.fillStyle = "#ffffff"; - ctx.fillText(text, 0, 0); + ctx.fillText(text, text_offset, text_offset); - bmp = bmp|0; - var dst_pixels = HEAP32[(bmp + 0|0)>>2]; + bmp = bmp|0; + dstX = dstX|0; + dstY = dstY|0; + + var dst_pixels = HEAP32[(bmp + 0|0)>>2] + (dstX << 2); var dst_width = HEAP32[(bmp + 4|0)>>2]; var dst_height = HEAP32[(bmp + 8|0)>>2]; @@ -873,11 +881,17 @@ mergeInto(LibraryManager.library, { for (var y = 0; y < img_height; y++) { - var src_row = (y*(src_width << 2))|0; - var dst_row = dst_pixels + (y*(dst_width << 2))|0; + var yy = y + dstY; + if (yy < 0 || yy >= dst_height) continue; + + var src_row = (y *(src_width << 2))|0; + var dst_row = dst_pixels + (yy*(dst_width << 2))|0; for (var x = 0; x < img_width; x++) { + var xx = x + dstX; + if (xx < 0 || xx >= dst_width) continue; + HEAPU8[dst_row + (x<<2)+0] = src_pixels[src_row + (x<<2)+0]; HEAPU8[dst_row + (x<<2)+1] = src_pixels[src_row + (x<<2)+1]; HEAPU8[dst_row + (x<<2)+2] = src_pixels[src_row + (x<<2)+2];