From 685b8c82d0cb9d3942ae20ae12af0c1aa5ebc2c2 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 23 Apr 2021 11:58:53 +0200 Subject: [PATCH 01/12] WIP: testing usage of SDL renderer It does segfault. --- dev-utils/macos-retina-display.md | 97 +++++++++++++++++++++++++++++++ src/main.c | 3 + src/renderer.c | 69 ++++++++++++++++++++-- 3 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 dev-utils/macos-retina-display.md diff --git a/dev-utils/macos-retina-display.md b/dev-utils/macos-retina-display.md new file mode 100644 index 00000000..ae464dc4 --- /dev/null +++ b/dev-utils/macos-retina-display.md @@ -0,0 +1,97 @@ +## Window creation for Retina displays + +The file info.plist sets NSHighResolutionCapable to true. This is fine for High-DPI +and retina displays. + +The `SDL_CreateWindow` is called with the flag `SDL_WINDOW_ALLOW_HIGHDPI`. +On Mac OS it means that, in source file `video/cocoa/SDL_cocoawindow.m`, from +function `Cocoa_CreateWindow`, SDL calls: + +```objc +/* highdpi will be TRUE below */ +BOOL highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; +[contentView setWantsBestResolutionOpenGLSurface:highdpi] +``` + +Documentation for `setWantsBestResolutionOpenGLSurface`: + +https://developer.apple.com/documentation/appkit/nsview/1414938-wantsbestresolutionopenglsurface + +with more details in "OpenGL Programming Guide for Mac", chapter +"Optimizing OpenGL for High Resolution": + +https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/EnablingOpenGLforHighResolution/EnablingOpenGLforHighResolution.html#//apple_ref/doc/uid/TP40001987-CH1001-SW4 + +Citation from the official documentation: + +You can opt in to high resolution by calling the method +`setWantsBestResolutionOpenGLSurface:` when you initialize the view, and +supplying YES as an argument: + +```objc +[self setWantsBestResolutionOpenGLSurface:YES]; +``` + +If you don’t opt in, the system magnifies the rendered results. + +The wantsBestResolutionOpenGLSurface property is relevant only for views to +which an NSOpenGLContext object is bound. Its value does not affect the behavior +of other views. For compatibility, wantsBestResolutionOpenGLSurface defaults to +NO, providing a 1-pixel-per-point framebuffer regardless of the backing scale +factor for the display the view occupies. Setting this property to YES for a +given view causes AppKit to allocate a higher-resolution framebuffer when +appropriate for the backing scale factor and target display. + +To function correctly with wantsBestResolutionOpenGLSurface set to YES, a view +must perform correct conversions between view units (points) and pixel units as +needed. For example, the common practice of passing the width and height of +[self bounds] to glViewport() will yield incorrect results at high resolution, +because the parameters passed to the glViewport() function must be in pixels. As +a result, you’ll get only partial instead of complete coverage of the render +surface. Instead, use the backing store bounds: + +```objc +[self convertRectToBacking:[self bounds]]; +``` + +## Coordinates + +The SDL function `SDL_GL_GetDrawableSize` will provide the size of the underlying drawable +in pixels. From the `SDL_video.h` header. + +```c +/** + * \brief Get the size of a window's underlying drawable in pixels (for use + * with glViewport). + * + * \param window Window from which the drawable size should be queried + * \param w Pointer to variable for storing the width in pixels, may be NULL + * \param h Pointer to variable for storing the height in pixels, may be NULL + * + * This may differ from SDL_GetWindowSize() if we're rendering to a high-DPI + * drawable, i.e. the window was created with SDL_WINDOW_ALLOW_HIGHDPI on a + * platform with high-DPI support (Apple calls this "Retina"), and not disabled + * by the SDL_HINT_VIDEO_HIGHDPI_DISABLED hint. + * + * \sa SDL_GetWindowSize() + * \sa SDL_CreateWindow() + */ +extern DECLSPEC void SDLCALL SDL_GL_GetDrawableSize(SDL_Window * window, int *w, + int *h); +``` + +In turns it calls `Cocoa_GL_GetDrawableSize` from source file +`video/cocoa/SDL_cocoaopengl.m`. The function use the method +`[contentView convertRectToBacking:viewport]`: + +```objc + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + /* This gives us the correct viewport for a Retina-enabled view, only + * supported on 10.7+. */ + if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { + viewport = [contentView convertRectToBacking:viewport]; + } + } +``` + +to give back the sizes in pixels. diff --git a/src/main.c b/src/main.c index e2ae548b..98dd6eb3 100644 --- a/src/main.c +++ b/src/main.c @@ -125,6 +125,9 @@ int main(int argc, char **argv) { window = SDL_CreateWindow( "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); + + fprintf(stderr, "New window: %p\n", window); fflush(stderr); + init_window_icon(); ren_init(window); diff --git a/src/renderer.c b/src/renderer.c index 36a76604..da406c20 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -33,6 +33,11 @@ struct RenFont { static SDL_Window *window; +static SDL_Renderer *window_renderer = NULL; +static SDL_Texture *window_texture = NULL; +static SDL_Surface *window_surface = NULL; +static int window_w = -1, window_h = -1; + static FR_Clip_Area clip; static void* check_alloc(void *ptr) { @@ -61,6 +66,30 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { } +static SDL_Surface *get_window_surface(SDL_Window *this_window) { + int w, h; + // fprintf(stderr, "get_window_surface: %p\n", this_window); fflush(stderr); + SDL_GL_GetDrawableSize(this_window, &w, &h); + // FIXME: check for errors ? + if (window_surface && w == window_w && h == window_h) { + fprintf(stderr, "get_window_surface: return current surface: %p\n", window_surface); fflush(stderr); + return window_surface; + } + if (window_surface) { + fprintf(stderr, "going to free: %p\n", window_surface); fflush(stderr); + SDL_FreeSurface(window_surface); + } + window_surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 24, SDL_PIXELFORMAT_BGR24); + + fprintf(stderr, "NEW surface: %p, size: %d %d\n", window_surface, w, h); fflush(stderr); + + window_w = w; + window_h = h; + + return window_surface; +} + + void ren_cp_replace_init(CPReplaceTable *rep_table) { rep_table->size = 0; rep_table->replacements = NULL; @@ -95,18 +124,42 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char * void ren_init(SDL_Window *win) { assert(win); window = win; - SDL_Surface *surf = SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); + fprintf(stderr, "New surface %p\n", surf); fflush(stderr); ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } ); } void ren_update_rects(RenRect *rects, int count) { +#if 0 SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count); +#endif + fprintf(stderr, "ren_update_rects\n"); fflush(stderr); static bool initial_frame = true; if (initial_frame) { SDL_ShowWindow(window); initial_frame = false; } + + int w, h; + SDL_GL_GetDrawableSize(window, &w, &h); + + if (window_renderer && (w != window_w || h != window_h)) { + SDL_DestroyTexture(window_texture); + SDL_DestroyRenderer(window_renderer); + window_renderer = NULL; + } + + if (!window_renderer) { + window_renderer = SDL_CreateRenderer(window, -1, 0); + // SDL_CreateTextureFromSurface(sdlRenderer, mySurface); + window_texture = SDL_CreateTexture(window_renderer, SDL_PIXELFORMAT_BGR24, SDL_TEXTUREACCESS_STREAMING, w, h); + fprintf(stderr, "got new renderer and texture: %p %p\n", window_renderer, window_texture); fflush(stderr); + } + // FIXME: we ignore the rects here. + SDL_UpdateTexture(window_texture, NULL, window_surface->pixels, window_w * 3); + SDL_RenderCopy(window_renderer, window_texture, NULL, NULL); + SDL_RenderPresent(window_renderer); } @@ -119,7 +172,7 @@ void ren_set_clip_rect(RenRect rect) { void ren_get_size(int *x, int *y) { - SDL_Surface *surf = SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); *x = surf->w; *y = surf->h; } @@ -278,7 +331,10 @@ void ren_draw_rect(RenRect rect, RenColor color) { x2 = x2 > clip.right ? clip.right : x2; y2 = y2 > clip.bottom ? clip.bottom : y2; - SDL_Surface *surf = SDL_GetWindowSurface(window); + // fprintf(stderr, "ren_draw_rect: clipped rect: (%d, %d) (%d, %d)\n", x1, y1, x2, y2); + + // SDL_Surface *surf = SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); RenColor *d = (RenColor*) surf->pixels; d += x1 + y1 * surf->w; int dr = surf->w - (x2 - x1); @@ -290,6 +346,8 @@ void ren_draw_rect(RenRect rect, RenColor color) { } } +// FIXME: this function is never used +#if 0 void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) { if (color.a == 0) { return; } @@ -304,7 +362,7 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) } /* draw */ - SDL_Surface *surf = SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); RenColor *s = image->pixels; RenColor *d = (RenColor*) surf->pixels; s += sub->x + sub->y * image->width; @@ -322,6 +380,7 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) s += sr; } } +#endif static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { for (int i = 0; i < rep_table->size; i++) { @@ -340,7 +399,7 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int { const char *p = text; unsigned codepoint; - SDL_Surface *surf = SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); // SDL_GetWindowSurface(window); const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; while (*p) { FR_Color color_rep; From 33fe7295c085d3111236693da46410ba9656cb94 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 23 Apr 2021 14:54:25 +0200 Subject: [PATCH 02/12] First working implemention Cleanup also debug messages --- src/api/system.c | 6 ++- src/main.c | 5 +- src/renderer.c | 121 +++++++++++++++++------------------------------ src/renderer.h | 3 +- 4 files changed, 50 insertions(+), 85 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index 99180ad8..be5fdea5 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -109,9 +109,11 @@ top: case SDL_WINDOWEVENT: if (e.window.event == SDL_WINDOWEVENT_RESIZED) { + int w = e.window.data1, h = e.window.data2; + ren_resize(w, h); lua_pushstring(L, "resized"); - lua_pushnumber(L, e.window.data1); - lua_pushnumber(L, e.window.data2); + lua_pushnumber(L, w); + lua_pushnumber(L, h); return 3; } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { rencache_invalidate(); diff --git a/src/main.c b/src/main.c index 98dd6eb3..43652fbc 100644 --- a/src/main.c +++ b/src/main.c @@ -125,9 +125,6 @@ int main(int argc, char **argv) { window = SDL_CreateWindow( "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); - - fprintf(stderr, "New window: %p\n", window); fflush(stderr); - init_window_icon(); ren_init(window); @@ -203,7 +200,7 @@ init_lua: } lua_close(L); - SDL_DestroyWindow(window); + ren_free_window_resources(); return EXIT_SUCCESS; } diff --git a/src/renderer.c b/src/renderer.c index da406c20..145e3f47 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -66,26 +66,17 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { } -static SDL_Surface *get_window_surface(SDL_Window *this_window) { - int w, h; - // fprintf(stderr, "get_window_surface: %p\n", this_window); fflush(stderr); - SDL_GL_GetDrawableSize(this_window, &w, &h); - // FIXME: check for errors ? - if (window_surface && w == window_w && h == window_h) { - fprintf(stderr, "get_window_surface: return current surface: %p\n", window_surface); fflush(stderr); - return window_surface; - } +static void init_window_surface() { if (window_surface) { - fprintf(stderr, "going to free: %p\n", window_surface); fflush(stderr); SDL_FreeSurface(window_surface); } - window_surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 24, SDL_PIXELFORMAT_BGR24); + SDL_GL_GetDrawableSize(window, &window_w, &window_h); + window_surface = SDL_CreateRGBSurfaceWithFormat(0, window_w, window_h, 32, SDL_PIXELFORMAT_BGRA32); + ren_set_clip_rect( (RenRect) { 0, 0, window_w, window_h } ); +} - fprintf(stderr, "NEW surface: %p, size: %d %d\n", window_surface, w, h); fflush(stderr); - - window_w = w; - window_h = h; +static SDL_Surface *get_window_surface() { return window_surface; } @@ -120,44 +111,56 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char * rep_table->size = table_size + 1; } +void ren_free_window_resources() { + SDL_DestroyWindow(window); + SDL_DestroyRenderer(window_renderer); + SDL_DestroyTexture(window_texture); + window = NULL; + window_renderer = NULL; +} + +static void setup_renderer(int w, int h) { + /* Note that w and h here should always be in pixels and obtained from + a call to SDL_GL_GetDrawableSize(). */ + if (window_renderer) { + SDL_DestroyRenderer(window_renderer); + SDL_DestroyTexture(window_texture); + } + window_renderer = SDL_CreateRenderer(window, -1, 0); + // May be we could use: SDL_CreateTextureFromSurface(sdlRenderer, mySurface); + window_texture = SDL_CreateTexture(window_renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); +} void ren_init(SDL_Window *win) { assert(win); window = win; - SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); - fprintf(stderr, "New surface %p\n", surf); fflush(stderr); - ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } ); + init_window_surface(); +} + + +void ren_resize(int w, int h) { + int new_w, new_h; + SDL_GL_GetDrawableSize(window, &new_w, &new_h); + /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ + if (new_w != window_h || new_h != window_h) { + init_window_surface(); + setup_renderer(new_w, new_h); + } } void ren_update_rects(RenRect *rects, int count) { -#if 0 - SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count); -#endif - fprintf(stderr, "ren_update_rects\n"); fflush(stderr); static bool initial_frame = true; if (initial_frame) { + int w, h; SDL_ShowWindow(window); + SDL_GL_GetDrawableSize(window, &w, &h); + setup_renderer(w, h); initial_frame = false; } - int w, h; - SDL_GL_GetDrawableSize(window, &w, &h); - - if (window_renderer && (w != window_w || h != window_h)) { - SDL_DestroyTexture(window_texture); - SDL_DestroyRenderer(window_renderer); - window_renderer = NULL; - } - - if (!window_renderer) { - window_renderer = SDL_CreateRenderer(window, -1, 0); - // SDL_CreateTextureFromSurface(sdlRenderer, mySurface); - window_texture = SDL_CreateTexture(window_renderer, SDL_PIXELFORMAT_BGR24, SDL_TEXTUREACCESS_STREAMING, w, h); - fprintf(stderr, "got new renderer and texture: %p %p\n", window_renderer, window_texture); fflush(stderr); - } // FIXME: we ignore the rects here. - SDL_UpdateTexture(window_texture, NULL, window_surface->pixels, window_w * 3); + SDL_UpdateTexture(window_texture, NULL, window_surface->pixels, window_w * 4); SDL_RenderCopy(window_renderer, window_texture, NULL, NULL); SDL_RenderPresent(window_renderer); } @@ -172,7 +175,7 @@ void ren_set_clip_rect(RenRect rect) { void ren_get_size(int *x, int *y) { - SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); *x = surf->w; *y = surf->h; } @@ -331,9 +334,6 @@ void ren_draw_rect(RenRect rect, RenColor color) { x2 = x2 > clip.right ? clip.right : x2; y2 = y2 > clip.bottom ? clip.bottom : y2; - // fprintf(stderr, "ren_draw_rect: clipped rect: (%d, %d) (%d, %d)\n", x1, y1, x2, y2); - - // SDL_Surface *surf = SDL_GetWindowSurface(window); SDL_Surface *surf = get_window_surface(window); RenColor *d = (RenColor*) surf->pixels; d += x1 + y1 * surf->w; @@ -346,41 +346,6 @@ void ren_draw_rect(RenRect rect, RenColor color) { } } -// FIXME: this function is never used -#if 0 -void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) { - if (color.a == 0) { return; } - - int n; - if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } - if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; x += n; } - if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } - if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } - - if (sub->width <= 0 || sub->height <= 0) { - return; - } - - /* draw */ - SDL_Surface *surf = get_window_surface(window); //SDL_GetWindowSurface(window); - RenColor *s = image->pixels; - RenColor *d = (RenColor*) surf->pixels; - s += sub->x + sub->y * image->width; - d += x + y * surf->w; - int sr = image->width - sub->width; - int dr = surf->w - sub->width; - - for (int j = 0; j < sub->height; j++) { - for (int i = 0; i < sub->width; i++) { - *d = blend_pixel2(*d, *s, color); - d++; - s++; - } - d += dr; - s += sr; - } -} -#endif static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { for (int i = 0; i < rep_table->size; i++) { @@ -399,7 +364,7 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int { const char *p = text; unsigned codepoint; - SDL_Surface *surf = get_window_surface(window); // SDL_GetWindowSurface(window); + SDL_Surface *surf = get_window_surface(window); const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; while (*p) { FR_Color color_rep; diff --git a/src/renderer.h b/src/renderer.h index 2b1f29d3..1ab82206 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -36,9 +36,11 @@ typedef struct CPReplaceTable CPReplaceTable; void ren_init(SDL_Window *win); +void ren_resize(int w, int h); void ren_update_rects(RenRect *rects, int count); void ren_set_clip_rect(RenRect rect); void ren_get_size(int *x, int *y); +void ren_free_window_resources(); RenImage* ren_new_image(int width, int height); void ren_free_image(RenImage *image); @@ -53,7 +55,6 @@ int ren_get_font_subpixel_scale(RenFont *font); int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); void ren_draw_rect(RenRect rect, RenColor color); -void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color); void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); From 18bcfa7e54cce42154a2ea5276b8f0b07bee12c7 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 23 Apr 2021 07:05:57 -0700 Subject: [PATCH 03/12] Call package macos in build script and do not create disk image --- build-packages.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/build-packages.sh b/build-packages.sh index f2a57397..6fd6a0c4 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -84,11 +84,11 @@ lite_build_package_windows () { rm -fr ".package-build" echo "created package $package_name" } - -lite_build_package_macosx () { + +lite_build_package_macos () { local build="$1" local arch="$2" - local os="macosx" + local os="macos" local appdir=".package-build/lite-xl.app" local bindir="$appdir/Contents/MacOS" @@ -110,10 +110,7 @@ lite_build_package_macosx () { mv "$package_name" .. popd rm -fr ".package-build" - local dmg_name="lite-xl-$os-$arch.dmg" - rm -f "$dmg_name" && hdiutil create -volname lite-xl -srcfolder lite-xl.app -ov -format UDBZ "$dmg_name" echo "created package $package_name" - echo "created disk image $dmg_name" } lite_build_package_linux () { @@ -161,7 +158,7 @@ lite_build_package () { if [[ "$OSTYPE" == msys || "$OSTYPE" == win32 ]]; then lite_build_package_windows "$@" elif [[ "$OSTYPE" == "darwin"* ]]; then - lite_build_package_macosx "$@" + lite_build_package_macos "$@" elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then lite_build_package_linux "$@" else From 57e6de978b382a7be29f34f3f5708fadfd56ca84 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 23 Apr 2021 07:09:50 -0700 Subject: [PATCH 04/12] Fix error with missing ren_resize call --- src/api/system.c | 10 ++++++---- src/renderer.c | 8 ++++---- src/renderer.h | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index be5fdea5..826ad5c1 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -109,11 +109,12 @@ top: case SDL_WINDOWEVENT: if (e.window.event == SDL_WINDOWEVENT_RESIZED) { - int w = e.window.data1, h = e.window.data2; - ren_resize(w, h); + ren_resize(); lua_pushstring(L, "resized"); - lua_pushnumber(L, w); - lua_pushnumber(L, h); + /* The size below can be wrong on Retina display by a multiplicative factor + but the size reported below is not currently used. */ + lua_pushnumber(L, e.window.data1); + lua_pushnumber(L, e.window.data2); return 3; } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { rencache_invalidate(); @@ -318,6 +319,7 @@ static int f_set_window_size(lua_State *L) { double y = luaL_checknumber(L, 4); SDL_SetWindowSize(window, w, h); SDL_SetWindowPosition(window, x, y); + ren_resize(); return 0; } diff --git a/src/renderer.c b/src/renderer.c index 145e3f47..b48bf3d2 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -138,7 +138,7 @@ void ren_init(SDL_Window *win) { } -void ren_resize(int w, int h) { +void ren_resize() { int new_w, new_h; SDL_GL_GetDrawableSize(window, &new_w, &new_h); /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ @@ -175,7 +175,7 @@ void ren_set_clip_rect(RenRect rect) { void ren_get_size(int *x, int *y) { - SDL_Surface *surf = get_window_surface(window); + SDL_Surface *surf = get_window_surface(); *x = surf->w; *y = surf->h; } @@ -334,7 +334,7 @@ void ren_draw_rect(RenRect rect, RenColor color) { x2 = x2 > clip.right ? clip.right : x2; y2 = y2 > clip.bottom ? clip.bottom : y2; - SDL_Surface *surf = get_window_surface(window); + SDL_Surface *surf = get_window_surface(); RenColor *d = (RenColor*) surf->pixels; d += x1 + y1 * surf->w; int dr = surf->w - (x2 - x1); @@ -364,7 +364,7 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int { const char *p = text; unsigned codepoint; - SDL_Surface *surf = get_window_surface(window); + SDL_Surface *surf = get_window_surface(); const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; while (*p) { FR_Color color_rep; diff --git a/src/renderer.h b/src/renderer.h index 1ab82206..f11d8e64 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -36,7 +36,7 @@ typedef struct CPReplaceTable CPReplaceTable; void ren_init(SDL_Window *win); -void ren_resize(int w, int h); +void ren_resize(); void ren_update_rects(RenRect *rects, int count); void ren_set_clip_rect(RenRect rect); void ren_get_size(int *x, int *y); From 46c3bdea677b096fd4cf2f834c54f7ad1e9b4db2 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sat, 24 Apr 2021 10:21:34 +0200 Subject: [PATCH 05/12] First implementation of scaling for retina display Introduce a new approach that discriminate coordinates in points and pixels. Now all the logic from the Lua side and in rencache is to always use points. The coordinates are converted to pixels only within the renderer, in the file renderer.c. In this way the application logic does not need to care about the scaling of the retina displays. For non-retina display the scaling between points and pixels is equal to one so nothing will change. There is nevertheless a change that leak into the Lua side. The subpixel coordinates are in sub-pixel, not sub-points so they are scaled by the retina scaling factor. But no change in the code is required because the subpixel scaling factor take into account the retina scaling, when present. Because the retina scaling factor is not know when the application starts but only when a window is actually available we introduce a mechanism to render the font with a given scaling factor only from the renderer when they are needed. We use therefore FontDesc to describe the font information but without actually rasterizing the font at a given scale. --- src/api/renderer.c | 8 +- src/api/renderer_font.c | 55 +++++++----- src/api/system.c | 7 +- src/fontdesc.c | 53 +++++++++++ src/fontdesc.h | 33 +++++++ src/meson.build | 1 + src/rencache.c | 26 +++--- src/rencache.h | 5 +- src/renderer.c | 192 ++++++++++++++++++++++++---------------- src/renderer.h | 18 ++-- 10 files changed, 272 insertions(+), 126 deletions(-) create mode 100644 src/fontdesc.c create mode 100644 src/fontdesc.h diff --git a/src/api/renderer.c b/src/api/renderer.c index 6e4ebf40..c6883a95 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -72,9 +72,9 @@ static int f_draw_rect(lua_State *L) { } static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { - RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); + FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - /* The coordinate below will be in subpixels iff draw_subpixel is true. + /* The coordinate below will be in subpixel iff draw_subpixel is true. Otherwise it will be in pixels. */ int x_subpixel = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); @@ -90,7 +90,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { replace_color = (RenColor) {0}; } - x_subpixel = rencache_draw_text(*font, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); + x_subpixel = rencache_draw_text(font_desc, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); lua_pushnumber(L, x_subpixel); return 1; } @@ -114,7 +114,7 @@ static const luaL_Reg lib[] = { { "draw_rect", f_draw_rect }, { "draw_text", f_draw_text }, { "draw_text_subpixel", f_draw_text_subpixel }, - { NULL, NULL } + { NULL, NULL } }; diff --git a/src/api/renderer_font.c b/src/api/renderer_font.c index dd5069bc..5cc8b400 100644 --- a/src/api/renderer_font.c +++ b/src/api/renderer_font.c @@ -1,8 +1,8 @@ #include "api.h" +#include "fontdesc.h" #include "renderer.h" #include "rencache.h" - static int f_load(lua_State *L) { const char *filename = luaL_checkstring(L, 1); float size = luaL_checknumber(L, 2); @@ -40,57 +40,72 @@ static int f_load(lua_State *L) { } lua_pop(L, 1); } - RenFont **self = lua_newuserdata(L, sizeof(*self)); + + if (ren_verify_font(filename)) { + luaL_error(L, "failed to load font"); + } + + FontDesc *font_desc = lua_newuserdata(L, sizeof(FontDesc)); + // FIXME: implement font_desc initialization in fontdesc.c + int filename_sz = strlen(filename) + 1; + font_desc->filename = malloc(filename_sz); + memcpy(font_desc->filename, filename, filename_sz); + font_desc->size = size; + font_desc->options = font_options; + font_desc->tab_size = 4; + font_desc->fonts_scale_length = 0; + font_desc->recent_font_scale_index = 0; /* No need to initialize. */ + luaL_setmetatable(L, API_TYPE_FONT); - *self = ren_load_font(filename, size, font_options); - if (!*self) { luaL_error(L, "failed to load font"); } return 1; } static int f_set_tab_size(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); int n = luaL_checknumber(L, 2); - ren_set_font_tab_size(*self, n); + font_desc_set_tab_size(self, n); return 0; } static int f_gc(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); - if (*self) { rencache_free_font(*self); } + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); + rencache_free_font(self); return 0; } - static int f_get_width(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - int subpixel_scale; - int w = ren_get_font_width(*self, text, &subpixel_scale); - lua_pushnumber(L, ren_font_subpixel_round(w, subpixel_scale, 0)); + /* By calling ren_get_font_width with NULL as third arguments + we will obtain the width in points. */ + int w = ren_get_font_width(self, text, NULL); + lua_pushnumber(L, w); return 1; } static int f_subpixel_scale(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_subpixel_scale(*self)); + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_get_font_subpixel_scale(self)); return 1; } - static int f_get_width_subpixel(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - lua_pushnumber(L, ren_get_font_width(*self, text, NULL)); + int subpixel_scale; + /* We need to pass a non-null subpixel_scale pointer to force + subpixel width calculation. */ + lua_pushnumber(L, ren_get_font_width(self, text, &subpixel_scale)); return 1; } static int f_get_height(lua_State *L) { - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_height(*self) ); + FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_get_font_height(self) ); return 1; } diff --git a/src/api/system.c b/src/api/system.c index 826ad5c1..8fd00056 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -109,10 +109,9 @@ top: case SDL_WINDOWEVENT: if (e.window.event == SDL_WINDOWEVENT_RESIZED) { - ren_resize(); + ren_setup_renderer(); lua_pushstring(L, "resized"); - /* The size below can be wrong on Retina display by a multiplicative factor - but the size reported below is not currently used. */ + /* The size below will be in points. */ lua_pushnumber(L, e.window.data1); lua_pushnumber(L, e.window.data2); return 3; @@ -319,7 +318,7 @@ static int f_set_window_size(lua_State *L) { double y = luaL_checknumber(L, 4); SDL_SetWindowSize(window, w, h); SDL_SetWindowPosition(window, x, y); - ren_resize(); + ren_setup_renderer(); return 0; } diff --git a/src/fontdesc.c b/src/fontdesc.c new file mode 100644 index 00000000..8414a73d --- /dev/null +++ b/src/fontdesc.c @@ -0,0 +1,53 @@ +#include "fontdesc.h" +#include "renderer.h" + +void font_desc_set_tab_size(FontDesc *font_desc, int tab_size) { + font_desc->tab_size = tab_size; + for (int i = 0; i < font_desc->fonts_scale_length; i++) { + ren_set_font_tab_size(font_desc->fonts_scale[i].font, tab_size); + } +} + +int font_desc_get_tab_size(FontDesc *font_desc) { + return font_desc->tab_size; +} + +void font_desc_free(FontDesc *font_desc) { + for (int i = 0; i < font_desc->fonts_scale_length; i++) { + ren_free_font(font_desc->fonts_scale[i].font); + } + font_desc->fonts_scale_length = 0; + free(font_desc->filename); +} + +static void load_scaled_font(FontDesc *font_desc, int index, int scale) { + RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options); + font_desc->fonts_scale[index].font = font; + font_desc->fonts_scale[index].scale = scale; +} + +RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale) { + int index = -1; + for (int i = 0; i < font_desc->fonts_scale_length; i++) { + if (font_desc->fonts_scale[i].scale == scale) { + index = i; + break; + } + } + if (index < 0) { + index = font_desc->fonts_scale_length; + if (index < FONT_SCALE_ARRAY_MAX) { + load_scaled_font(font_desc, index, scale); + font_desc->fonts_scale_length = index + 1; + } else { + // FIXME: should not print into the stderr or stdout. + fprintf(stderr, "Warning: max array of font scale reached.\n"); + index = (font_desc->recent_font_scale_index == 0 ? 1 : 0); + ren_free_font(font_desc->fonts_scale[index].font); + load_scaled_font(font_desc, index, scale); + } + } + font_desc->recent_font_scale_index = index; + return font_desc->fonts_scale[index].font; +} + diff --git a/src/fontdesc.h b/src/fontdesc.h new file mode 100644 index 00000000..d0326ca4 --- /dev/null +++ b/src/fontdesc.h @@ -0,0 +1,33 @@ +#ifndef FONT_DESC_H +#define FONT_DESC_H + +typedef struct RenFont RenFont; + +// FIXME: find a better name for the struct below +struct FontScaled { + RenFont *font; + short int scale; +}; +typedef struct FontScaled FontScaled; + +#define FONT_SCALE_ARRAY_MAX 2 + +struct FontDesc { + char *filename; + float size; + unsigned int options; + short int tab_size; +// FIXME: find a better name for the array below + FontScaled fonts_scale[FONT_SCALE_ARRAY_MAX]; + short int fonts_scale_length; + short int recent_font_scale_index; /* More recently scale used. */ +}; +typedef struct FontDesc FontDesc; + +int font_desc_get_tab_size(FontDesc *font_desc); +void font_desc_set_tab_size(FontDesc *font_desc, int tab_size); +void font_desc_free(FontDesc *font_desc); +RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale); + +#endif + diff --git a/src/meson.build b/src/meson.build index 2800d3d4..ed287bb8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ lite_sources = [ 'api/renderer_font.c', 'api/system.c', 'renderer.c', + 'fontdesc.c', 'rencache.c', 'main.c', ] diff --git a/src/rencache.c b/src/rencache.c index 3d607094..36734b24 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -24,7 +24,7 @@ typedef struct { int32_t size; RenRect rect; RenColor color; - RenFont *font; + FontDesc *font_desc; CPReplaceTable *replacements; RenColor replace_color; char text[0]; @@ -115,9 +115,9 @@ void rencache_show_debug(bool enable) { } -void rencache_free_font(RenFont *font) { +void rencache_free_font(FontDesc *font_desc) { Command *cmd = push_command(FREE_FONT, COMMAND_BARE_SIZE); - if (cmd) { cmd->font = font; } + if (cmd) { cmd->font_desc = font_desc; } } @@ -136,17 +136,17 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -int rencache_draw_text(RenFont *font, +int rencache_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color) { int subpixel_scale; - int w_subpixel = ren_get_font_width(font, text, &subpixel_scale); + int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale); RenRect rect; rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); rect.y = y; rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); - rect.height = ren_get_font_height(font); + rect.height = ren_get_font_height(font_desc); if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; @@ -154,11 +154,11 @@ int rencache_draw_text(RenFont *font, if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; - cmd->font = font; + cmd->font_desc = font_desc; cmd->rect = rect; cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1); cmd->x_subpixel_offset = x - subpixel_scale * rect.x; - cmd->tab_size = ren_get_font_tab_size(font); + cmd->tab_size = font_desc_get_tab_size(font_desc); cmd->replacements = replacements; cmd->replace_color = replace_color; } @@ -272,13 +272,13 @@ void rencache_end_frame(void) { ren_draw_rect(cmd->rect, cmd->color); break; case DRAW_TEXT: - ren_set_font_tab_size(cmd->font, cmd->tab_size); - ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, + font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); + ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, cmd->replacements, cmd->replace_color); break; case DRAW_TEXT_SUBPIXEL: - ren_set_font_tab_size(cmd->font, cmd->tab_size); - ren_draw_text_subpixel(cmd->font, cmd->text, + font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); + ren_draw_text_subpixel(cmd->font_desc, cmd->text, cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color, cmd->replacements, cmd->replace_color); break; @@ -301,7 +301,7 @@ void rencache_end_frame(void) { cmd = NULL; while (next_command(&cmd)) { if (cmd->type == FREE_FONT) { - ren_free_font(cmd->font); + font_desc_free(cmd->font_desc); } } } diff --git a/src/rencache.h b/src/rencache.h index a532c9b5..b5c241ab 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -5,10 +5,11 @@ #include "renderer.h" void rencache_show_debug(bool enable); -void rencache_free_font(RenFont *font); +void rencache_free_font(FontDesc *font_desc); void rencache_set_clip_rect(RenRect rect); void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); +int rencache_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, + bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); void rencache_invalidate(void); void rencache_begin_frame(void); void rencache_end_frame(void); diff --git a/src/renderer.c b/src/renderer.c index b48bf3d2..e4e9dcbf 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -32,13 +32,16 @@ struct RenFont { }; -static SDL_Window *window; -static SDL_Renderer *window_renderer = NULL; -static SDL_Texture *window_texture = NULL; -static SDL_Surface *window_surface = NULL; -static int window_w = -1, window_h = -1; +struct Renderer { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *texture; + SDL_Surface *surface; + FR_Clip_Area clip; /* Clipping rect in pixel coordinates. */ + int surface_scale; +}; -static FR_Clip_Area clip; +static struct Renderer renderer = {0}; static void* check_alloc(void *ptr) { if (!ptr) { @@ -66,18 +69,30 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { } -static void init_window_surface() { - if (window_surface) { - SDL_FreeSurface(window_surface); - } - SDL_GL_GetDrawableSize(window, &window_w, &window_h); - window_surface = SDL_CreateRGBSurfaceWithFormat(0, window_w, window_h, 32, SDL_PIXELFORMAT_BGRA32); - ren_set_clip_rect( (RenRect) { 0, 0, window_w, window_h } ); +static int get_surface_scale() { + int w_pixels, h_pixels; + int w_points, h_points; + SDL_GL_GetDrawableSize(renderer.window, &w_pixels, &h_pixels); + SDL_GetWindowSize(renderer.window, &w_points, &h_points); + // FIXME: this assert is too harsh. + assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points); + return w_pixels / w_points; } -static SDL_Surface *get_window_surface() { - return window_surface; +static FR_Clip_Area scaled_clip(const RenRect rect, const int scale) { + return (FR_Clip_Area) {rect.x * scale, rect.y * scale, (rect.x + rect.width) * scale, (rect.y + rect.height) * scale}; +} + + +static void init_window_surface() { + if (renderer.surface) { + SDL_FreeSurface(renderer.surface); + } + int w, h; + SDL_GL_GetDrawableSize(renderer.window, &w, &h); + renderer.surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32); + renderer.clip = scaled_clip((RenRect) { 0, 0, w, h }, 1); } @@ -112,37 +127,39 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char * } void ren_free_window_resources() { - SDL_DestroyWindow(window); - SDL_DestroyRenderer(window_renderer); - SDL_DestroyTexture(window_texture); - window = NULL; - window_renderer = NULL; + SDL_DestroyWindow(renderer.window); + SDL_DestroyRenderer(renderer.renderer); + SDL_DestroyTexture(renderer.texture); + renderer.window = NULL; + renderer.renderer = NULL; } static void setup_renderer(int w, int h) { /* Note that w and h here should always be in pixels and obtained from a call to SDL_GL_GetDrawableSize(). */ - if (window_renderer) { - SDL_DestroyRenderer(window_renderer); - SDL_DestroyTexture(window_texture); + if (renderer.renderer) { + SDL_DestroyRenderer(renderer.renderer); + SDL_DestroyTexture(renderer.texture); } - window_renderer = SDL_CreateRenderer(window, -1, 0); + renderer.renderer = SDL_CreateRenderer(renderer.window, -1, 0); // May be we could use: SDL_CreateTextureFromSurface(sdlRenderer, mySurface); - window_texture = SDL_CreateTexture(window_renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); + renderer.texture = SDL_CreateTexture(renderer.renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); + renderer.surface_scale = get_surface_scale(); } void ren_init(SDL_Window *win) { assert(win); - window = win; + renderer.window = win; init_window_surface(); + renderer.surface_scale = get_surface_scale(); } -void ren_resize() { +void ren_setup_renderer() { int new_w, new_h; - SDL_GL_GetDrawableSize(window, &new_w, &new_h); + SDL_GL_GetDrawableSize(renderer.window, &new_w, &new_h); /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ - if (new_w != window_h || new_h != window_h) { + if (new_w != renderer.surface->w || new_h != renderer.surface->h) { init_window_surface(); setup_renderer(new_w, new_h); } @@ -153,31 +170,28 @@ void ren_update_rects(RenRect *rects, int count) { static bool initial_frame = true; if (initial_frame) { int w, h; - SDL_ShowWindow(window); - SDL_GL_GetDrawableSize(window, &w, &h); + SDL_ShowWindow(renderer.window); + SDL_GL_GetDrawableSize(renderer.window, &w, &h); setup_renderer(w, h); initial_frame = false; } // FIXME: we ignore the rects here. - SDL_UpdateTexture(window_texture, NULL, window_surface->pixels, window_w * 4); - SDL_RenderCopy(window_renderer, window_texture, NULL, NULL); - SDL_RenderPresent(window_renderer); + SDL_UpdateTexture(renderer.texture, NULL, renderer.surface->pixels, renderer.surface->w * 4); + SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL); + SDL_RenderPresent(renderer.renderer); } void ren_set_clip_rect(RenRect rect) { - clip.left = rect.x; - clip.top = rect.y; - clip.right = rect.x + rect.width; - clip.bottom = rect.y + rect.height; + renderer.clip = scaled_clip(rect, renderer.surface_scale); } void ren_get_size(int *x, int *y) { - SDL_Surface *surf = get_window_surface(); - *x = surf->w; - *y = surf->h; + const int scale = renderer.surface_scale; + *x = renderer.surface->w / scale; + *y = renderer.surface->h / scale; } @@ -214,6 +228,17 @@ static GlyphSet* get_glyphset(RenFont *font, int codepoint) { } +int ren_verify_font(const char *filename) { + RenFont font[1]; + font->renderer = FR_Renderer_New(0); + if (FR_Load_Font(font->renderer, filename)) { + return 1; + } + FR_Renderer_Free(font->renderer); + return 0; +} + + RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags) { RenFont *font = NULL; @@ -274,25 +299,34 @@ int ren_get_font_tab_size(RenFont *font) { } -int ren_get_font_width(RenFont *font, const char *text, int *subpixel_scale) { +/* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will + return width in subpixels. */ +int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale) { int x = 0; const char *p = text; unsigned codepoint; + const int surface_scale = renderer.surface_scale; + RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); while (*p) { p = utf8_to_codepoint(p, &codepoint); GlyphSet *set = get_glyphset(font, codepoint); FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; x += g->xadvance; } + /* At this point here x is in subpixel units */ + const int x_scale_to_points = FR_Subpixel_Scale(font->renderer) * surface_scale; if (subpixel_scale) { - *subpixel_scale = FR_Subpixel_Scale(font->renderer); + *subpixel_scale = x_scale_to_points; + return x; } - return x; + return (x + x_scale_to_points / 2) / x_scale_to_points; } -int ren_get_font_height(RenFont *font) { - return font->height; +int ren_get_font_height(FontDesc *font_desc) { + const int surface_scale = renderer.surface_scale; + RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); + return (font->height + surface_scale / 2) / surface_scale; } @@ -305,16 +339,6 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) { } -static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) { - src.a = (src.a * color.a) >> 8; - int ia = 0xff - src.a; - dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8); - dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8); - dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8); - return dst; -} - - #define rect_draw_loop(expr) \ for (int j = y1; j < y2; j++) { \ for (int i = x1; i < x2; i++) { \ @@ -327,17 +351,24 @@ static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) void ren_draw_rect(RenRect rect, RenColor color) { if (color.a == 0) { return; } - int x1 = rect.x < clip.left ? clip.left : rect.x; - int y1 = rect.y < clip.top ? clip.top : rect.y; + const int surface_scale = renderer.surface_scale; + + /* transforms coordinates in pixels. */ + rect.x *= surface_scale; + rect.y *= surface_scale; + rect.width *= surface_scale; + rect.height *= surface_scale; + + int x1 = rect.x < renderer.clip.left ? renderer.clip.left : rect.x; + int y1 = rect.y < renderer.clip.top ? renderer.clip.top : rect.y; int x2 = rect.x + rect.width; int y2 = rect.y + rect.height; - x2 = x2 > clip.right ? clip.right : x2; - y2 = y2 > clip.bottom ? clip.bottom : y2; + x2 = x2 > renderer.clip.right ? renderer.clip.right : x2; + y2 = y2 > renderer.clip.bottom ? renderer.clip.bottom : y2; - SDL_Surface *surf = get_window_surface(); - RenColor *d = (RenColor*) surf->pixels; - d += x1 + y1 * surf->w; - int dr = surf->w - (x2 - x1); + RenColor *d = (RenColor*) renderer.surface->pixels; + d += x1 + y1 * renderer.surface->w; + int dr = renderer.surface->w - (x2 - x1); if (color.a == 0xff) { rect_draw_loop(color); @@ -358,13 +389,12 @@ static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { return 0; } - -void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color, +static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color, CPReplaceTable *replacements, RenColor replace_color) { + SDL_Surface *surf = renderer.surface; const char *p = text; unsigned codepoint; - SDL_Surface *surf = get_window_surface(); const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; while (*p) { FR_Color color_rep; @@ -381,18 +411,29 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int color_rep = color_fr; } if (color.a != 0) { - FR_Blend_Glyph(font->renderer, &clip, - x_subpixel, y, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); + FR_Blend_Glyph(font->renderer, &renderer.clip, + x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); } x_subpixel += xadvance_original_cp; } } -void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, + +void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color) { - const int subpixel_scale = FR_Subpixel_Scale(font->renderer); - ren_draw_text_subpixel(font, text, subpixel_scale * x, y, color, replacements, replace_color); + const int surface_scale = renderer.surface_scale; + RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); + draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color); +} + +void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, + CPReplaceTable *replacements, RenColor replace_color) +{ + const int surface_scale = renderer.surface_scale; + RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); + const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer); + draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color); } // Could be declared as static inline @@ -409,7 +450,8 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) { } -int ren_get_font_subpixel_scale(RenFont *font) { - return FR_Subpixel_Scale(font->renderer); +int ren_get_font_subpixel_scale(FontDesc *font_desc) { + const int surface_scale = renderer.surface_scale; + RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); + return FR_Subpixel_Scale(font->renderer) * surface_scale; } - diff --git a/src/renderer.h b/src/renderer.h index f11d8e64..7638466b 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,9 +3,9 @@ #include #include +#include "fontdesc.h" typedef struct RenImage RenImage; -typedef struct RenFont RenFont; enum { RenFontAntialiasingMask = 1, @@ -36,27 +36,29 @@ typedef struct CPReplaceTable CPReplaceTable; void ren_init(SDL_Window *win); -void ren_resize(); +void ren_setup_renderer(); void ren_update_rects(RenRect *rects, int count); void ren_set_clip_rect(RenRect rect); -void ren_get_size(int *x, int *y); +void ren_get_size(int *x, int *y); /* Reports the size in points. */ void ren_free_window_resources(); RenImage* ren_new_image(int width, int height); void ren_free_image(RenImage *image); RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags); +int ren_verify_font(const char *filename); void ren_free_font(RenFont *font); void ren_set_font_tab_size(RenFont *font, int n); int ren_get_font_tab_size(RenFont *font); -int ren_get_font_width(RenFont *font, const char *text, int *subpixel_scale); -int ren_get_font_height(RenFont *font); -int ren_get_font_subpixel_scale(RenFont *font); + +int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale); +int ren_get_font_height(FontDesc *font_desc); +int ren_get_font_subpixel_scale(FontDesc *font_desc); int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); void ren_draw_rect(RenRect rect, RenColor color); -void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); -void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); +void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); +void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); void ren_cp_replace_init(CPReplaceTable *rep_table); void ren_cp_replace_free(CPReplaceTable *rep_table); From f2a33a567bf067b74f618209109f90ac51116d08 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Mon, 26 Apr 2021 15:56:18 +0200 Subject: [PATCH 06/12] Cleanup FontDesc struct and implementation --- src/api/renderer_font.c | 13 ++-------- src/fontdesc.c | 57 ++++++++++++++++++++++++++--------------- src/fontdesc.h | 18 ++++++------- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/api/renderer_font.c b/src/api/renderer_font.c index 5cc8b400..3d85597b 100644 --- a/src/api/renderer_font.c +++ b/src/api/renderer_font.c @@ -45,17 +45,8 @@ static int f_load(lua_State *L) { luaL_error(L, "failed to load font"); } - FontDesc *font_desc = lua_newuserdata(L, sizeof(FontDesc)); - // FIXME: implement font_desc initialization in fontdesc.c - int filename_sz = strlen(filename) + 1; - font_desc->filename = malloc(filename_sz); - memcpy(font_desc->filename, filename, filename_sz); - font_desc->size = size; - font_desc->options = font_options; - font_desc->tab_size = 4; - font_desc->fonts_scale_length = 0; - font_desc->recent_font_scale_index = 0; /* No need to initialize. */ - + FontDesc *font_desc = lua_newuserdata(L, font_desc_alloc_size(filename)); + font_desc_init(font_desc, filename, size, font_options); luaL_setmetatable(L, API_TYPE_FONT); return 1; } diff --git a/src/fontdesc.c b/src/fontdesc.c index 8414a73d..56244af8 100644 --- a/src/fontdesc.c +++ b/src/fontdesc.c @@ -1,10 +1,33 @@ +#include + #include "fontdesc.h" #include "renderer.h" + +int font_desc_alloc_size(const char *filename) { + return offsetof(FontDesc, filename) + strlen(filename) + 1; +} + +void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options) { + memcpy(font_desc->filename, filename, strlen(filename) + 1); + font_desc->size = size; + font_desc->options = font_options; + font_desc->tab_size = 4; + font_desc->cache_length = 0; + font_desc->cache_last_index = 0; /* Normally no need to initialize. */ +} + +void font_desc_free(FontDesc *font_desc) { + for (int i = 0; i < font_desc->cache_length; i++) { + ren_free_font(font_desc->cache[i].font); + } + font_desc->cache_length = 0; +} + void font_desc_set_tab_size(FontDesc *font_desc, int tab_size) { font_desc->tab_size = tab_size; - for (int i = 0; i < font_desc->fonts_scale_length; i++) { - ren_set_font_tab_size(font_desc->fonts_scale[i].font, tab_size); + for (int i = 0; i < font_desc->cache_length; i++) { + ren_set_font_tab_size(font_desc->cache[i].font, tab_size); } } @@ -12,42 +35,34 @@ int font_desc_get_tab_size(FontDesc *font_desc) { return font_desc->tab_size; } -void font_desc_free(FontDesc *font_desc) { - for (int i = 0; i < font_desc->fonts_scale_length; i++) { - ren_free_font(font_desc->fonts_scale[i].font); - } - font_desc->fonts_scale_length = 0; - free(font_desc->filename); -} - static void load_scaled_font(FontDesc *font_desc, int index, int scale) { RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options); - font_desc->fonts_scale[index].font = font; - font_desc->fonts_scale[index].scale = scale; + font_desc->cache[index].font = font; + font_desc->cache[index].scale = scale; } RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale) { int index = -1; - for (int i = 0; i < font_desc->fonts_scale_length; i++) { - if (font_desc->fonts_scale[i].scale == scale) { + for (int i = 0; i < font_desc->cache_length; i++) { + if (font_desc->cache[i].scale == scale) { index = i; break; } } if (index < 0) { - index = font_desc->fonts_scale_length; - if (index < FONT_SCALE_ARRAY_MAX) { + index = font_desc->cache_length; + if (index < FONT_CACHE_ARRAY_MAX) { load_scaled_font(font_desc, index, scale); - font_desc->fonts_scale_length = index + 1; + font_desc->cache_length = index + 1; } else { // FIXME: should not print into the stderr or stdout. fprintf(stderr, "Warning: max array of font scale reached.\n"); - index = (font_desc->recent_font_scale_index == 0 ? 1 : 0); - ren_free_font(font_desc->fonts_scale[index].font); + index = (font_desc->cache_last_index == 0 ? 1 : 0); + ren_free_font(font_desc->cache[index].font); load_scaled_font(font_desc, index, scale); } } - font_desc->recent_font_scale_index = index; - return font_desc->fonts_scale[index].font; + font_desc->cache_last_index = index; + return font_desc->cache[index].font; } diff --git a/src/fontdesc.h b/src/fontdesc.h index d0326ca4..2f4702ab 100644 --- a/src/fontdesc.h +++ b/src/fontdesc.h @@ -3,27 +3,27 @@ typedef struct RenFont RenFont; -// FIXME: find a better name for the struct below -struct FontScaled { +struct FontInstance { RenFont *font; short int scale; }; -typedef struct FontScaled FontScaled; +typedef struct FontInstance FontInstance; -#define FONT_SCALE_ARRAY_MAX 2 +#define FONT_CACHE_ARRAY_MAX 2 struct FontDesc { - char *filename; float size; unsigned int options; short int tab_size; -// FIXME: find a better name for the array below - FontScaled fonts_scale[FONT_SCALE_ARRAY_MAX]; - short int fonts_scale_length; - short int recent_font_scale_index; /* More recently scale used. */ + FontInstance cache[FONT_CACHE_ARRAY_MAX]; + short int cache_length; + short int cache_last_index; /* More recently used instance. */ + char filename[0]; }; typedef struct FontDesc FontDesc; +void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options); +int font_desc_alloc_size(const char *filename); int font_desc_get_tab_size(FontDesc *font_desc); void font_desc_set_tab_size(FontDesc *font_desc, int tab_size); void font_desc_free(FontDesc *font_desc); From 8b9fbecd74e339db01fb46fb24040ea414b2144e Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 27 Apr 2021 09:52:02 +0200 Subject: [PATCH 07/12] Ensure we update only modified rects --- src/renderer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index e4e9dcbf..3bd237c0 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -176,9 +176,12 @@ void ren_update_rects(RenRect *rects, int count) { initial_frame = false; } - // FIXME: we ignore the rects here. SDL_UpdateTexture(renderer.texture, NULL, renderer.surface->pixels, renderer.surface->w * 4); - SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL); + for (int i = 0; i < count; i++) { + const RenRect *r = &rects[i]; + SDL_Rect sr = {.x = r->x, .y = r->y, .w = r->width, .h = r->height}; + SDL_RenderCopy(renderer.renderer, renderer.texture, &sr, &sr); + } SDL_RenderPresent(renderer.renderer); } From 0fe8415bb433dc5880dbd87595e08ce96b5bc7cb Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 27 Apr 2021 11:56:02 +0200 Subject: [PATCH 08/12] Add assert if font loading fails during rendering --- src/fontdesc.c | 8 ++++++++ src/renderer.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/fontdesc.c b/src/fontdesc.c index 56244af8..d1d0825f 100644 --- a/src/fontdesc.c +++ b/src/fontdesc.c @@ -1,4 +1,5 @@ #include +#include #include "fontdesc.h" #include "renderer.h" @@ -37,6 +38,13 @@ int font_desc_get_tab_size(FontDesc *font_desc) { static void load_scaled_font(FontDesc *font_desc, int index, int scale) { RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options); + if (!font) { + /* The font was able to load when initially loaded using renderer.load.font. + If now is no longer available we just abort the application. */ + fprintf(stderr, "Fatal error: unable to load font %s. Application will abort.\n", + font_desc->filename); + exit(1); + } font_desc->cache[index].font = font; font_desc->cache[index].scale = scale; } diff --git a/src/renderer.c b/src/renderer.c index 3bd237c0..b2adc044 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -74,7 +74,8 @@ static int get_surface_scale() { int w_points, h_points; SDL_GL_GetDrawableSize(renderer.window, &w_pixels, &h_pixels); SDL_GetWindowSize(renderer.window, &w_points, &h_points); - // FIXME: this assert is too harsh. + /* We consider that the ratio pixel/point will always be an integer and + it is the same along the x and the y axis. */ assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points); return w_pixels / w_points; } From 67f431c69cd6a1de926183d8ba379bd1b5714045 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 27 Apr 2021 14:04:02 +0200 Subject: [PATCH 09/12] Alternative texture update scheme --- src/renderer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index b2adc044..b7163572 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -177,10 +177,11 @@ void ren_update_rects(RenRect *rects, int count) { initial_frame = false; } - SDL_UpdateTexture(renderer.texture, NULL, renderer.surface->pixels, renderer.surface->w * 4); for (int i = 0; i < count; i++) { const RenRect *r = &rects[i]; SDL_Rect sr = {.x = r->x, .y = r->y, .w = r->width, .h = r->height}; + int32_t *pixels = ((int32_t *) renderer.surface->pixels) + r->x + renderer.surface->w * r->y; + SDL_UpdateTexture(renderer.texture, &sr, pixels, renderer.surface->w * 4); SDL_RenderCopy(renderer.renderer, renderer.texture, &sr, &sr); } SDL_RenderPresent(renderer.renderer); From 94869400824925733c14b30cdd12a9b123c1b900 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 27 Apr 2021 15:21:23 +0200 Subject: [PATCH 10/12] Update the whole texture with RenderCopy --- src/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index b7163572..c0f7e479 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -182,8 +182,8 @@ void ren_update_rects(RenRect *rects, int count) { SDL_Rect sr = {.x = r->x, .y = r->y, .w = r->width, .h = r->height}; int32_t *pixels = ((int32_t *) renderer.surface->pixels) + r->x + renderer.surface->w * r->y; SDL_UpdateTexture(renderer.texture, &sr, pixels, renderer.surface->w * 4); - SDL_RenderCopy(renderer.renderer, renderer.texture, &sr, &sr); } + SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL); SDL_RenderPresent(renderer.renderer); } From 7c79105d2f2af3a38776f48721ddb86e05e42762 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 27 Apr 2021 07:29:13 -0700 Subject: [PATCH 11/12] Fix missing scaling for update rects in renderer --- src/renderer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index c0f7e479..31349e44 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -177,10 +177,13 @@ void ren_update_rects(RenRect *rects, int count) { initial_frame = false; } + const int scale = renderer.surface_scale; for (int i = 0; i < count; i++) { const RenRect *r = &rects[i]; - SDL_Rect sr = {.x = r->x, .y = r->y, .w = r->width, .h = r->height}; - int32_t *pixels = ((int32_t *) renderer.surface->pixels) + r->x + renderer.surface->w * r->y; + const int x = scale * r->x, y = scale * r->y; + const int w = scale * r->width, h = scale * r->height; + const SDL_Rect sr = {.x = x, .y = y, .w = w, .h = h}; + int32_t *pixels = ((int32_t *) renderer.surface->pixels) + x + renderer.surface->w * y; SDL_UpdateTexture(renderer.texture, &sr, pixels, renderer.surface->w * 4); } SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL); From 3d84fe54882b3960f2b3e47e9eceaee3e65ea780 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 29 Apr 2021 14:15:24 +0200 Subject: [PATCH 12/12] Make usage of SDL renderer optional --- meson.build | 5 ++ meson_options.txt | 1 + src/api/system.c | 4 +- src/meson.build | 1 + src/renderer.c | 145 +++++++++++++--------------------------------- src/renderer.h | 2 +- src/renwindow.c | 125 +++++++++++++++++++++++++++++++++++++++ src/renwindow.h | 25 ++++++++ 8 files changed, 201 insertions(+), 107 deletions(-) create mode 100644 src/renwindow.c create mode 100644 src/renwindow.h diff --git a/meson.build b/meson.build index 1f31b509..5b9576d4 100644 --- a/meson.build +++ b/meson.build @@ -40,6 +40,11 @@ if host_machine.system() == 'windows' lite_rc += windows.compile_resources('res.rc') endif +# On macos we need to use the SDL renderer to support retina displays +if get_option('renderer') or host_machine.system() == 'darwin' + lite_cargs += '-DLITE_USE_SDL_RENDERER' +endif + subdir('lib/font_renderer') subdir('src') diff --git a/meson_options.txt b/meson_options.txt index 2714cdf4..73a542f2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,3 @@ option('portable', type : 'boolean', value : false, description: 'Portable install') +option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer') diff --git a/src/api/system.c b/src/api/system.c index 8fd00056..1b15a2ec 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -109,7 +109,7 @@ top: case SDL_WINDOWEVENT: if (e.window.event == SDL_WINDOWEVENT_RESIZED) { - ren_setup_renderer(); + ren_resize_window(); lua_pushstring(L, "resized"); /* The size below will be in points. */ lua_pushnumber(L, e.window.data1); @@ -318,7 +318,7 @@ static int f_set_window_size(lua_State *L) { double y = luaL_checknumber(L, 4); SDL_SetWindowSize(window, w, h); SDL_SetWindowPosition(window, x, y); - ren_setup_renderer(); + ren_resize_window(); return 0; } diff --git a/src/meson.build b/src/meson.build index ed287bb8..881014be 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ lite_sources = [ 'api/renderer_font.c', 'api/system.c', 'renderer.c', + 'renwindow.c', 'fontdesc.c', 'rencache.c', 'main.c', diff --git a/src/renderer.c b/src/renderer.c index 31349e44..b08c25d9 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -2,8 +2,9 @@ #include #include #include -#include "renderer.h" #include "font_renderer.h" +#include "renderer.h" +#include "renwindow.h" #define MAX_GLYPHSET 256 #define REPLACEMENT_CHUNK_SIZE 8 @@ -31,17 +32,7 @@ struct RenFont { FR_Renderer *renderer; }; - -struct Renderer { - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Texture *texture; - SDL_Surface *surface; - FR_Clip_Area clip; /* Clipping rect in pixel coordinates. */ - int surface_scale; -}; - -static struct Renderer renderer = {0}; +static RenWindow window_renderer = {0}; static void* check_alloc(void *ptr) { if (!ptr) { @@ -69,34 +60,6 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { } -static int get_surface_scale() { - int w_pixels, h_pixels; - int w_points, h_points; - SDL_GL_GetDrawableSize(renderer.window, &w_pixels, &h_pixels); - SDL_GetWindowSize(renderer.window, &w_points, &h_points); - /* We consider that the ratio pixel/point will always be an integer and - it is the same along the x and the y axis. */ - assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points); - return w_pixels / w_points; -} - - -static FR_Clip_Area scaled_clip(const RenRect rect, const int scale) { - return (FR_Clip_Area) {rect.x * scale, rect.y * scale, (rect.x + rect.width) * scale, (rect.y + rect.height) * scale}; -} - - -static void init_window_surface() { - if (renderer.surface) { - SDL_FreeSurface(renderer.surface); - } - int w, h; - SDL_GL_GetDrawableSize(renderer.window, &w, &h); - renderer.surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32); - renderer.clip = scaled_clip((RenRect) { 0, 0, w, h }, 1); -} - - void ren_cp_replace_init(CPReplaceTable *rep_table) { rep_table->size = 0; rep_table->replacements = NULL; @@ -128,78 +91,43 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char * } void ren_free_window_resources() { - SDL_DestroyWindow(renderer.window); - SDL_DestroyRenderer(renderer.renderer); - SDL_DestroyTexture(renderer.texture); - renderer.window = NULL; - renderer.renderer = NULL; -} - -static void setup_renderer(int w, int h) { - /* Note that w and h here should always be in pixels and obtained from - a call to SDL_GL_GetDrawableSize(). */ - if (renderer.renderer) { - SDL_DestroyRenderer(renderer.renderer); - SDL_DestroyTexture(renderer.texture); - } - renderer.renderer = SDL_CreateRenderer(renderer.window, -1, 0); - // May be we could use: SDL_CreateTextureFromSurface(sdlRenderer, mySurface); - renderer.texture = SDL_CreateTexture(renderer.renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); - renderer.surface_scale = get_surface_scale(); + renwin_free(&window_renderer); } void ren_init(SDL_Window *win) { assert(win); - renderer.window = win; - init_window_surface(); - renderer.surface_scale = get_surface_scale(); + window_renderer.window = win; + renwin_init_surface(&window_renderer); + renwin_clip_to_surface(&window_renderer); } -void ren_setup_renderer() { - int new_w, new_h; - SDL_GL_GetDrawableSize(renderer.window, &new_w, &new_h); - /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ - if (new_w != renderer.surface->w || new_h != renderer.surface->h) { - init_window_surface(); - setup_renderer(new_w, new_h); - } +void ren_resize_window() { + renwin_resize_surface(&window_renderer); } void ren_update_rects(RenRect *rects, int count) { static bool initial_frame = true; if (initial_frame) { - int w, h; - SDL_ShowWindow(renderer.window); - SDL_GL_GetDrawableSize(renderer.window, &w, &h); - setup_renderer(w, h); + renwin_show_window(&window_renderer); initial_frame = false; } - - const int scale = renderer.surface_scale; - for (int i = 0; i < count; i++) { - const RenRect *r = &rects[i]; - const int x = scale * r->x, y = scale * r->y; - const int w = scale * r->width, h = scale * r->height; - const SDL_Rect sr = {.x = x, .y = y, .w = w, .h = h}; - int32_t *pixels = ((int32_t *) renderer.surface->pixels) + x + renderer.surface->w * y; - SDL_UpdateTexture(renderer.texture, &sr, pixels, renderer.surface->w * 4); - } - SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL); - SDL_RenderPresent(renderer.renderer); + renwin_update_rects(&window_renderer, rects, count); } void ren_set_clip_rect(RenRect rect) { - renderer.clip = scaled_clip(rect, renderer.surface_scale); + renwin_set_clip_rect(&window_renderer, rect); } void ren_get_size(int *x, int *y) { - const int scale = renderer.surface_scale; - *x = renderer.surface->w / scale; - *y = renderer.surface->h / scale; + RenWindow *ren = &window_renderer; + const int scale = renwin_surface_scale(ren); + SDL_Surface *surface = renwin_get_surface(ren); + *x = surface->w / scale; + *y = surface->h / scale; } @@ -313,7 +241,7 @@ int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scal int x = 0; const char *p = text; unsigned codepoint; - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); while (*p) { p = utf8_to_codepoint(p, &codepoint); @@ -332,7 +260,7 @@ int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scal int ren_get_font_height(FontDesc *font_desc) { - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); return (font->height + surface_scale / 2) / surface_scale; } @@ -359,7 +287,7 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) { void ren_draw_rect(RenRect rect, RenColor color) { if (color.a == 0) { return; } - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); /* transforms coordinates in pixels. */ rect.x *= surface_scale; @@ -367,16 +295,18 @@ void ren_draw_rect(RenRect rect, RenColor color) { rect.width *= surface_scale; rect.height *= surface_scale; - int x1 = rect.x < renderer.clip.left ? renderer.clip.left : rect.x; - int y1 = rect.y < renderer.clip.top ? renderer.clip.top : rect.y; + const RenRect clip = window_renderer.clip; + int x1 = rect.x < clip.x ? clip.x : rect.x; + int y1 = rect.y < clip.y ? clip.y : rect.y; int x2 = rect.x + rect.width; int y2 = rect.y + rect.height; - x2 = x2 > renderer.clip.right ? renderer.clip.right : x2; - y2 = y2 > renderer.clip.bottom ? renderer.clip.bottom : y2; + x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2; + y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2; - RenColor *d = (RenColor*) renderer.surface->pixels; - d += x1 + y1 * renderer.surface->w; - int dr = renderer.surface->w - (x2 - x1); + SDL_Surface *surface = renwin_get_surface(&window_renderer); + RenColor *d = (RenColor*) surface->pixels; + d += x1 + y1 * surface->w; + int dr = surface->w - (x2 - x1); if (color.a == 0xff) { rect_draw_loop(color); @@ -397,10 +327,17 @@ static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { return 0; } + +static FR_Clip_Area clip_area_from_rect(const RenRect r) { + return (FR_Clip_Area) {r.x, r.y, r.x + r.width, r.y + r.height}; +} + + static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color, CPReplaceTable *replacements, RenColor replace_color) { - SDL_Surface *surf = renderer.surface; + SDL_Surface *surf = renwin_get_surface(&window_renderer); + FR_Clip_Area clip = clip_area_from_rect(window_renderer.clip); const char *p = text; unsigned codepoint; const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; @@ -419,7 +356,7 @@ static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int color_rep = color_fr; } if (color.a != 0) { - FR_Blend_Glyph(font->renderer, &renderer.clip, + FR_Blend_Glyph(font->renderer, &clip, x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); } x_subpixel += xadvance_original_cp; @@ -430,7 +367,7 @@ static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color) { - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color); } @@ -438,7 +375,7 @@ void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixe void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color) { - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer); draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color); @@ -459,7 +396,7 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) { int ren_get_font_subpixel_scale(FontDesc *font_desc) { - const int surface_scale = renderer.surface_scale; + const int surface_scale = renwin_surface_scale(&window_renderer); RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); return FR_Subpixel_Scale(font->renderer) * surface_scale; } diff --git a/src/renderer.h b/src/renderer.h index 7638466b..bab059bb 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -36,7 +36,7 @@ typedef struct CPReplaceTable CPReplaceTable; void ren_init(SDL_Window *win); -void ren_setup_renderer(); +void ren_resize_window(); void ren_update_rects(RenRect *rects, int count); void ren_set_clip_rect(RenRect rect); void ren_get_size(int *x, int *y); /* Reports the size in points. */ diff --git a/src/renwindow.c b/src/renwindow.c new file mode 100644 index 00000000..0800f7c8 --- /dev/null +++ b/src/renwindow.c @@ -0,0 +1,125 @@ +#include +#include "renwindow.h" + +#ifdef LITE_USE_SDL_RENDERER +static int query_surface_scale(RenWindow *ren) { + int w_pixels, h_pixels; + int w_points, h_points; + SDL_GL_GetDrawableSize(ren->window, &w_pixels, &h_pixels); + SDL_GetWindowSize(ren->window, &w_points, &h_points); + /* We consider that the ratio pixel/point will always be an integer and + it is the same along the x and the y axis. */ + assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points); + return w_pixels / w_points; +} +#endif + + +void renwin_init_surface(RenWindow *ren) { +#ifdef LITE_USE_SDL_RENDERER + if (ren->surface) { + SDL_FreeSurface(ren->surface); + } + int w, h; + SDL_GL_GetDrawableSize(ren->window, &w, &h); + ren->surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32); + ren->surface_scale = query_surface_scale(ren); +#endif +} + +int renwin_surface_scale(RenWindow *ren) { +#ifdef LITE_USE_SDL_RENDERER + return ren->surface_scale; +#else + return 1; +#endif +} + + +static RenRect scaled_rect(const RenRect rect, const int scale) { + return (RenRect) {rect.x * scale, rect.y * scale, rect.width * scale, rect.height * scale}; +} + + +void renwin_clip_to_surface(RenWindow *ren) { + SDL_Surface *surface = renwin_get_surface(ren); + ren->clip = (RenRect) {0, 0, surface->w, surface->h}; +} + + +void renwin_set_clip_rect(RenWindow *ren, RenRect rect) { + ren->clip = scaled_rect(rect, renwin_surface_scale(ren)); +} + + +SDL_Surface *renwin_get_surface(RenWindow *ren) { +#ifdef LITE_USE_SDL_RENDERER + return ren->surface; +#else + return SDL_GetWindowSurface(ren->window); +#endif +} + +#ifdef LITE_USE_SDL_RENDERER +static void setup_renderer(RenWindow *ren, int w, int h) { + /* Note that w and h here should always be in pixels and obtained from + a call to SDL_GL_GetDrawableSize(). */ + if (ren->renderer) { + SDL_DestroyRenderer(ren->renderer); + SDL_DestroyTexture(ren->texture); + } + ren->renderer = SDL_CreateRenderer(ren->window, -1, 0); + ren->texture = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); + ren->surface_scale = query_surface_scale(ren); +} +#endif + +void renwin_resize_surface(RenWindow *ren) { +#ifdef LITE_USE_SDL_RENDERER + int new_w, new_h; + SDL_GL_GetDrawableSize(ren->window, &new_w, &new_h); + /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ + if (new_w != ren->surface->w || new_h != ren->surface->h) { + renwin_init_surface(ren); + renwin_clip_to_surface(ren); + setup_renderer(ren, new_w, new_h); + } +#endif +} + +void renwin_show_window(RenWindow *ren) { + SDL_ShowWindow(ren->window); +#ifdef LITE_USE_SDL_RENDERER + int w, h; + SDL_GL_GetDrawableSize(ren->window, &w, &h); + setup_renderer(ren, w, h); +#endif +} + +void renwin_update_rects(RenWindow *ren, RenRect *rects, int count) { +#ifdef LITE_USE_SDL_RENDERER + const int scale = ren->surface_scale; + for (int i = 0; i < count; i++) { + const RenRect *r = &rects[i]; + const int x = scale * r->x, y = scale * r->y; + const int w = scale * r->width, h = scale * r->height; + const SDL_Rect sr = {.x = x, .y = y, .w = w, .h = h}; + int32_t *pixels = ((int32_t *) ren->surface->pixels) + x + ren->surface->w * y; + SDL_UpdateTexture(ren->texture, &sr, pixels, ren->surface->w * 4); + } + SDL_RenderCopy(ren->renderer, ren->texture, NULL, NULL); + SDL_RenderPresent(ren->renderer); +#else + SDL_UpdateWindowSurfaceRects(ren->window, (SDL_Rect*) rects, count); +#endif +} + +void renwin_free(RenWindow *ren) { + SDL_DestroyWindow(ren->window); + ren->window = NULL; +#ifdef LITE_USE_SDL_RENDERER + SDL_DestroyRenderer(ren->renderer); + SDL_DestroyTexture(ren->texture); + SDL_FreeSurface(ren->surface); +#endif +} diff --git a/src/renwindow.h b/src/renwindow.h new file mode 100644 index 00000000..8edc03d8 --- /dev/null +++ b/src/renwindow.h @@ -0,0 +1,25 @@ +#include +#include "renderer.h" + +struct RenWindow { + SDL_Window *window; + RenRect clip; /* Clipping rect in pixel coordinates. */ +#ifdef LITE_USE_SDL_RENDERER + SDL_Renderer *renderer; + SDL_Texture *texture; + SDL_Surface *surface; + int surface_scale; +#endif +}; +typedef struct RenWindow RenWindow; + +void renwin_init_surface(RenWindow *ren); +int renwin_surface_scale(RenWindow *ren); +void renwin_clip_to_surface(RenWindow *ren); +void renwin_set_clip_rect(RenWindow *ren, RenRect rect); +void renwin_resize_surface(RenWindow *ren); +void renwin_show_window(RenWindow *ren); +void renwin_update_rects(RenWindow *ren, RenRect *rects, int count); +void renwin_free(RenWindow *ren); +SDL_Surface *renwin_get_surface(RenWindow *ren); +