From ec89d477e114aef12fc5e84600af5be0b6dff6b1 Mon Sep 17 00:00:00 2001 From: Jay Dolan Date: Sun, 6 Sep 2026 16:48:34 -0400 Subject: [PATCH 1/4] Close the loop dropped from applyTransform in #59 The squash of #59 lost the for loop's closing brace, so main did not compile. Co-Authored-By: Claude Fable 5.1 --- Sources/ObjectivelyMVC/Text.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/ObjectivelyMVC/Text.c b/Sources/ObjectivelyMVC/Text.c index 32ce9c45..543727c8 100644 --- a/Sources/ObjectivelyMVC/Text.c +++ b/Sources/ObjectivelyMVC/Text.c @@ -357,6 +357,7 @@ static void applyTransform(Text *self) { } wordStart = SDL_isspace((unsigned char) *c) != 0; + } } /** From 823ff4fee9bc470b59a5f7df12f1ff3793155dc2 Mon Sep 17 00:00:00 2001 From: Jay Dolan Date: Sun, 6 Sep 2026 16:48:34 -0400 Subject: [PATCH 2/4] Load SVG images, rasterized at a scale Image recognizes SVG by declared type or by sniffing and rasterizes it through SDL_image's nanosvg. Image::imageWithSVG and initWithSVG take a scale, pixels per point such as the window's pixel density, so a vector asset drawn into a frame of its intrinsic size stays sharp on a high density display; Image::scale records it and Image::size reports points, so views sized to an image are unaffected. Raster images keep a scale of 1. Co-Authored-By: Claude Fable 5.1 --- Sources/ObjectivelyMVC/Image.c | 92 +++++++++++++- Sources/ObjectivelyMVC/Image.h | 33 +++++ Tests/ObjectivelyMVC/Image | 210 +++++++++++++++++++++++++++++++ Tests/ObjectivelyMVC/Image.c | 122 ++++++++++++++++++ Tests/ObjectivelyMVC/Makefile.am | 1 + 5 files changed, 457 insertions(+), 1 deletion(-) create mode 100755 Tests/ObjectivelyMVC/Image create mode 100644 Tests/ObjectivelyMVC/Image.c diff --git a/Sources/ObjectivelyMVC/Image.c b/Sources/ObjectivelyMVC/Image.c index c348dade..4f306ebd 100644 --- a/Sources/ObjectivelyMVC/Image.c +++ b/Sources/ObjectivelyMVC/Image.c @@ -81,12 +81,44 @@ static Image *imageWithSurface(SDL_Surface *surface) { return $(alloc(Image), initWithSurface, surface); } +/** + * @fn Image *Image::imageWithSVG(const uint8_t *bytes, size_t length, float scale) + * @memberof Image + */ +static Image *imageWithSVG(const uint8_t *bytes, size_t length, float scale) { + return $(alloc(Image), initWithSVG, bytes, length, scale); +} + +/** + * @return True if `bytes` are an SVG document, by declared type or by sniffing. + */ +static bool isSVG(const Image *self, const uint8_t *bytes, size_t length) { + + if (self->type && SDL_strcasecmp(self->type, "svg") == 0) { + return true; + } + + bool svg = false; + + SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length); + if (stream) { + svg = IMG_isSVG(stream); + SDL_CloseIO(stream); + } + + return svg; +} + /** * @fn Image *Image::initWithBytes(Image *self, const uint8_t *bytes, size_t length) * @memberof Image */ static Image *initWithBytes(Image *self, const uint8_t *bytes, size_t length) { + if (isSVG(self, bytes, length)) { + return $(self, initWithSVG, bytes, length, 1.f); + } + SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length); if (stream) { SDL_Surface *surface = IMG_LoadTyped_IO(stream, 0, self->type); @@ -150,6 +182,58 @@ static Image *initWithResourceName(Image *self, const char *name) { return self; } +/** + * @brief Rasterizes `bytes` as SVG at `size` pixels, or at its intrinsic size when `size` is zero. + */ +static SDL_Surface *rasterizeSVG(const uint8_t *bytes, size_t length, SDL_Size size) { + + SDL_Surface *surface = NULL; + + SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length); + if (stream) { + surface = IMG_LoadSizedSVG_IO(stream, size.w, size.h); + SDL_CloseIO(stream); + } + + return surface; +} + +/** + * @fn Image *Image::initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) + * @memberof Image + */ +static Image *initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) { + + assert(scale > 0.f); + + SDL_Surface *surface = rasterizeSVG(bytes, length, MakeSize(0, 0)); + + if (surface && scale != 1.f) { + const SDL_Size size = MakeSize( + (int) SDL_roundf(surface->w * scale), + (int) SDL_roundf(surface->h * scale) + ); + + SDL_DestroySurface(surface); + surface = rasterizeSVG(bytes, length, size); + } + + if (surface) { + self = $(self, initWithSurface, surface); + SDL_DestroySurface(surface); + + if (self) { + self->type = "svg"; + self->scale = scale; + } + } else { + MVC_LogWarn("%s\n", SDL_GetError()); + self = release(self); + } + + return self; +} + /** * @fn Image *Image::initWithSurface(Image *self, SDL_Surface *surface) * @memberof Image @@ -158,6 +242,7 @@ static Image *initWithSurface(Image *self, SDL_Surface *surface) { self = (Image *) super(Object, self, init); if (self) { + self->scale = 1.f; if (surface) { if (surface->format != SDL_PIXELFORMAT_RGBA32) { @@ -179,7 +264,10 @@ static Image *initWithSurface(Image *self, SDL_Surface *surface) { * @memberof Image */ static SDL_Size size(const Image *self) { - return MakeSize(self->surface->w, self->surface->h); + return MakeSize( + (int) SDL_roundf(self->surface->w / self->scale), + (int) SDL_roundf(self->surface->h / self->scale) + ); } #pragma mark - Class lifecycle @@ -196,11 +284,13 @@ static void initialize(Class *clazz) { ((ImageInterface *) clazz->interface)->imageWithResource = imageWithResource; ((ImageInterface *) clazz->interface)->imageWithResourceName = imageWithResourceName; ((ImageInterface *) clazz->interface)->imageWithSurface = imageWithSurface; + ((ImageInterface *) clazz->interface)->imageWithSVG = imageWithSVG; ((ImageInterface *) clazz->interface)->initWithBytes = initWithBytes; ((ImageInterface *) clazz->interface)->initWithData = initWithData; ((ImageInterface *) clazz->interface)->initWithResource = initWithResource; ((ImageInterface *) clazz->interface)->initWithResourceName = initWithResourceName; ((ImageInterface *) clazz->interface)->initWithSurface = initWithSurface; + ((ImageInterface *) clazz->interface)->initWithSVG = initWithSVG; ((ImageInterface *) clazz->interface)->size = size; } diff --git a/Sources/ObjectivelyMVC/Image.h b/Sources/ObjectivelyMVC/Image.h index 01015d02..6a5f8243 100644 --- a/Sources/ObjectivelyMVC/Image.h +++ b/Sources/ObjectivelyMVC/Image.h @@ -33,6 +33,9 @@ typedef struct ImageInterface ImageInterface; /** * @brief Image loading. + * @details Raster formats load at their native size. SVG, recognized by type or by sniffing, + * rasterizes at its intrinsic size times a `scale`, so that a vector asset drawn into a frame + * of its intrinsic size stays sharp at any pixel density; Image::size reports points. * @extends Object */ struct Image { @@ -48,6 +51,11 @@ struct Image { */ ImageInterface *interface[0]; + /** + * @brief Pixels of `surface` per point: `1` for raster images, the requested scale for SVG. + */ + float scale; + /** * @brief The backing surface. */ @@ -110,6 +118,18 @@ struct ImageInterface { */ Image *(*imageWithResourceName)(const char *name); + /** + * @static + * @fn Image *Image::imageWithSVG(const uint8_t *bytes, size_t length, float scale) + * @brief Instantiates an Image by rasterizing the specified SVG. + * @param bytes The SVG document. + * @param length The length of `bytes`. + * @param scale Pixels per point, e.g. the window's pixel density. + * @return The new Image, or `NULL` on error. + * @memberof Image + */ + Image *(*imageWithSVG)(const uint8_t *bytes, size_t length, float scale); + /** * @static * @fn Image *Image::imageWithSurface(SDL_Surface *surface) @@ -161,6 +181,19 @@ struct ImageInterface { */ Image *(*initWithResourceName)(Image *self, const char *name); + /** + * @fn Image *Image::initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) + * @brief Initializes this Image by rasterizing the specified SVG at its intrinsic size times + * `scale`. + * @param self The Image. + * @param bytes The SVG document. + * @param length The length of `bytes`. + * @param scale Pixels per point, e.g. the window's pixel density. + * @return The initialized Image, or `NULL` on error. + * @memberof Image + */ + Image *(*initWithSVG)(Image *self, const uint8_t *bytes, size_t length, float scale); + /** * @fn Image *Image::initWithSurface(Image *self, SDL_Surface *surface) * @brief Initializes this Image with the given surface. diff --git a/Tests/ObjectivelyMVC/Image b/Tests/ObjectivelyMVC/Image new file mode 100755 index 00000000..cedd1870 --- /dev/null +++ b/Tests/ObjectivelyMVC/Image @@ -0,0 +1,210 @@ +#! /bin/sh + +# Image - temporary wrapper script for .libs/Image +# Generated by libtool (GNU libtool) 2.6.2 +# +# The Image program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.6.2' + notinst_deplibs=' ../../Sources/ObjectivelyMVC/libObjectivelyMVC.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ../../libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "Image:Image:$LINENO: libtool wrapper (GNU libtool) 2.6.2" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "Image:Image:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "Image:Image:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw/windows when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='Image' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to DYLD_LIBRARY_PATH + DYLD_LIBRARY_PATH="/Users/jdolan/Coding/ObjectivelyMVC-svg/Sources/ObjectivelyMVC/.libs:$DYLD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + DYLD_LIBRARY_PATH=`$ECHO "$DYLD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'` + + export DYLD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/Tests/ObjectivelyMVC/Image.c b/Tests/ObjectivelyMVC/Image.c new file mode 100644 index 00000000..5a36627e --- /dev/null +++ b/Tests/ObjectivelyMVC/Image.c @@ -0,0 +1,122 @@ +/* + * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C. + * Copyright (C) 2014 Jay Dolan + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + */ + +#include +#include + +#include "ObjectivelyMVC.h" + +static const char *svg = + "" + ""; + +static void assertCenterIsRed(const Image *image) { + + SDL_Surface *surface = image->surface; + + Uint8 r, g, b, a; + ck_assert(SDL_ReadSurfacePixel(surface, surface->w / 2, surface->h / 2, &r, &g, &b, &a)); + ck_assert_int_eq(255, r); + ck_assert_int_eq(0, g); + ck_assert_int_eq(0, b); + ck_assert_int_eq(255, a); +} + +START_TEST(svgLoadsAtIntrinsicSize) { + + Image *image = $$(Image, imageWithBytes, (const uint8_t *) svg, strlen(svg)); + ck_assert_ptr_nonnull(image); + + ck_assert_str_eq("svg", image->type); + ck_assert_float_eq(1.f, image->scale); + ck_assert_int_eq(32, image->surface->w); + ck_assert_int_eq(16, image->surface->h); + + const SDL_Size size = $(image, size); + ck_assert_int_eq(32, size.w); + ck_assert_int_eq(16, size.h); + + assertCenterIsRed(image); + + release(image); + +} END_TEST + +START_TEST(svgRasterizesAtScale) { + + Image *image = $$(Image, imageWithSVG, (const uint8_t *) svg, strlen(svg), 2.f); + ck_assert_ptr_nonnull(image); + + ck_assert_float_eq(2.f, image->scale); + ck_assert_int_eq(64, image->surface->w); + ck_assert_int_eq(32, image->surface->h); + + // Points, not pixels, so a View sized to the image is the intrinsic size + const SDL_Size size = $(image, size); + ck_assert_int_eq(32, size.w); + ck_assert_int_eq(16, size.h); + + assertCenterIsRed(image); + + release(image); + +} END_TEST + +START_TEST(rasterKeepsUnitScale) { + + SDL_Surface *surface = SDL_CreateSurface(8, 4, SDL_PIXELFORMAT_RGBA32); + Image *image = $$(Image, imageWithSurface, surface); + SDL_DestroySurface(surface); + + ck_assert_float_eq(1.f, image->scale); + + const SDL_Size size = $(image, size); + ck_assert_int_eq(8, size.w); + ck_assert_int_eq(4, size.h); + + release(image); + +} END_TEST + +START_TEST(garbageFails) { + + const char *garbage = "not an image"; + Image *image = $$(Image, imageWithSVG, (const uint8_t *) garbage, strlen(garbage), 1.f); + ck_assert_ptr_null(image); + +} END_TEST + +int main(int argc, char **argv) { + + TCase *tcase = tcase_create("Image"); + tcase_add_test(tcase, svgLoadsAtIntrinsicSize); + tcase_add_test(tcase, svgRasterizesAtScale); + tcase_add_test(tcase, rasterKeepsUnitScale); + tcase_add_test(tcase, garbageFails); + + Suite *suite = suite_create("Image"); + suite_add_tcase(suite, tcase); + + SRunner *runner = srunner_create(suite); + + srunner_run_all(runner, CK_VERBOSE); + int failed = srunner_ntests_failed(runner); + + srunner_free(runner); + + return failed; +} diff --git a/Tests/ObjectivelyMVC/Makefile.am b/Tests/ObjectivelyMVC/Makefile.am index 670734e7..64f5b349 100644 --- a/Tests/ObjectivelyMVC/Makefile.am +++ b/Tests/ObjectivelyMVC/Makefile.am @@ -7,6 +7,7 @@ DEFAULT_INCLUDES = \ TESTS = \ Font+Bitmap \ + Image \ ImageAtlas \ Selector \ Style \ From 7dddb0f67bfd5a8520c90edc87af89f8bfc7f274 Mon Sep 17 00:00:00 2001 From: Jay Dolan Date: Sun, 6 Sep 2026 20:06:21 -0400 Subject: [PATCH 3/4] Remove versioned test. --- Tests/ObjectivelyMVC/.gitignore | 1 + Tests/ObjectivelyMVC/Image | 210 -------------------------------- 2 files changed, 1 insertion(+), 210 deletions(-) delete mode 100755 Tests/ObjectivelyMVC/Image diff --git a/Tests/ObjectivelyMVC/.gitignore b/Tests/ObjectivelyMVC/.gitignore index 078c2227..bc0e68c8 100644 --- a/Tests/ObjectivelyMVC/.gitignore +++ b/Tests/ObjectivelyMVC/.gitignore @@ -2,6 +2,7 @@ *.trs Constraint Font+Bitmap +Image ImageAtlas Selector Style diff --git a/Tests/ObjectivelyMVC/Image b/Tests/ObjectivelyMVC/Image deleted file mode 100755 index cedd1870..00000000 --- a/Tests/ObjectivelyMVC/Image +++ /dev/null @@ -1,210 +0,0 @@ -#! /bin/sh - -# Image - temporary wrapper script for .libs/Image -# Generated by libtool (GNU libtool) 2.6.2 -# -# The Image program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -sed_quote_subst='s|\([`"$\\]\)|\\\1|g' - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command="" - -# This environment variable determines our operation mode. -if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then - # install mode needs the following variables: - generated_by_libtool_version='2.6.2' - notinst_deplibs=' ../../Sources/ObjectivelyMVC/libObjectivelyMVC.la' -else - # When we are sourced in execute mode, $file and $ECHO are already set. - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - file="$0" - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' -} - ECHO="printf %s\\n" - fi - -# Very basic option parsing. These options are (a) specific to -# the libtool wrapper, (b) are identical between the wrapper -# /script/ and the wrapper /executable/ that is used only on -# windows platforms, and (c) all begin with the string --lt- -# (application programs are unlikely to have options that match -# this pattern). -# -# There are only two supported options: --lt-debug and -# --lt-dump-script. There is, deliberately, no --lt-help. -# -# The first argument to this parsing function should be the -# script's ../../libtool value, followed by no. -lt_option_debug= -func_parse_lt_options () -{ - lt_script_arg0=$0 - shift - for lt_opt - do - case "$lt_opt" in - --lt-debug) lt_option_debug=1 ;; - --lt-dump-script) - lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` - test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. - lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'` - cat "$lt_dump_D/$lt_dump_F" - exit 0 - ;; - --lt-*) - $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 - exit 1 - ;; - esac - done - - # Print the debug banner immediately: - if test -n "$lt_option_debug"; then - echo "Image:Image:$LINENO: libtool wrapper (GNU libtool) 2.6.2" 1>&2 - fi -} - -# Used when --lt-debug. Prints its arguments to stdout -# (redirection is the responsibility of the caller) -func_lt_dump_args () -{ - lt_dump_args_N=1; - for lt_arg - do - $ECHO "Image:Image:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" - lt_dump_args_N=`expr $lt_dump_args_N + 1` - done -} - -# Core function for launching the target application -func_exec_program_core () -{ - - if test -n "$lt_option_debug"; then - $ECHO "Image:Image:$LINENO: newargv[0]: $progdir/$program" 1>&2 - func_lt_dump_args ${1+"$@"} 1>&2 - fi - exec "$progdir/$program" ${1+"$@"} - - $ECHO "$0: cannot exec $program $*" 1>&2 - exit 1 -} - -# A function to encapsulate launching the target application -# Strips options in the --lt-* namespace from $@ and -# launches target application with the remaining arguments. -func_exec_program () -{ - case " $* " in - *\ --lt-*) - for lt_wr_arg - do - case $lt_wr_arg in - --lt-*) ;; - *) set x "$@" "$lt_wr_arg"; shift;; - esac - shift - done ;; - esac - func_exec_program_core ${1+"$@"} -} - - # Parse options - func_parse_lt_options "$0" ${1+"$@"} - - # Find the directory that this script lives in. - thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` - test "x$thisdir" = "x$file" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'` - while test -n "$file"; do - destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` - - # If there was a directory component, then change thisdir. - if test "x$destdir" != "x$file"; then - case "$destdir" in - [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; - *) thisdir="$thisdir/$destdir" ;; - esac - fi - - file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'` - file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'` - done - - # Usually 'no', except on cygwin/mingw/windows when embedded into - # the cwrapper. - WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no - if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then - # special case for '.' - if test "$thisdir" = "."; then - thisdir=`pwd` - fi - # remove .libs from thisdir - case "$thisdir" in - *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;; - .libs ) thisdir=. ;; - esac - fi - - # Try to get the absolute directory name. - absdir=`cd "$thisdir" && pwd` - test -n "$absdir" && thisdir="$absdir" - - program='Image' - progdir="$thisdir/.libs" - - - if test -f "$progdir/$program"; then - # Add our own library path to DYLD_LIBRARY_PATH - DYLD_LIBRARY_PATH="/Users/jdolan/Coding/ObjectivelyMVC-svg/Sources/ObjectivelyMVC/.libs:$DYLD_LIBRARY_PATH" - - # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH - # The second colon is a workaround for a bug in BeOS R4 sed - DYLD_LIBRARY_PATH=`$ECHO "$DYLD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'` - - export DYLD_LIBRARY_PATH - - if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then - # Run the actual program with our arguments. - func_exec_program ${1+"$@"} - fi - else - # The program doesn't exist. - $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 - $ECHO "This script is just a wrapper for $program." 1>&2 - $ECHO "See the libtool documentation for more information." 1>&2 - exit 1 - fi -fi From cf75f2ee9b4196fce891b28e4bcba183857e37ca Mon Sep 17 00:00:00 2001 From: Jay Dolan Date: Sun, 6 Sep 2026 20:13:50 -0400 Subject: [PATCH 4/4] PR feedback --- Sources/ObjectivelyMVC/Image.c | 20 ++++++++++---------- Sources/ObjectivelyMVC/Image.h | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Sources/ObjectivelyMVC/Image.c b/Sources/ObjectivelyMVC/Image.c index 4f306ebd..e6923896 100644 --- a/Sources/ObjectivelyMVC/Image.c +++ b/Sources/ObjectivelyMVC/Image.c @@ -82,11 +82,11 @@ static Image *imageWithSurface(SDL_Surface *surface) { } /** - * @fn Image *Image::imageWithSVG(const uint8_t *bytes, size_t length, float scale) + * @fn Image *Image::imageWithSvg(const uint8_t *bytes, size_t length, float scale) * @memberof Image */ -static Image *imageWithSVG(const uint8_t *bytes, size_t length, float scale) { - return $(alloc(Image), initWithSVG, bytes, length, scale); +static Image *imageWithSvg(const uint8_t *bytes, size_t length, float scale) { + return $(alloc(Image), initWithSvg, bytes, length, scale); } /** @@ -116,7 +116,7 @@ static bool isSVG(const Image *self, const uint8_t *bytes, size_t length) { static Image *initWithBytes(Image *self, const uint8_t *bytes, size_t length) { if (isSVG(self, bytes, length)) { - return $(self, initWithSVG, bytes, length, 1.f); + return $(self, initWithSvg, bytes, length, 1.f); } SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length); @@ -128,11 +128,11 @@ static Image *initWithBytes(Image *self, const uint8_t *bytes, size_t length) { } else { self = release(self); } + SDL_CloseIO(stream); } else { self = release(self); } - SDL_CloseIO(stream); return self; } @@ -199,12 +199,12 @@ static SDL_Surface *rasterizeSVG(const uint8_t *bytes, size_t length, SDL_Size s } /** - * @fn Image *Image::initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) + * @fn Image *Image::initWithSvg(Image *self, const uint8_t *bytes, size_t length, float scale) * @memberof Image */ -static Image *initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) { +static Image *initWithSvg(Image *self, const uint8_t *bytes, size_t length, float scale) { - assert(scale > 0.f); + scale = scale > 0.f ?: 1.f; SDL_Surface *surface = rasterizeSVG(bytes, length, MakeSize(0, 0)); @@ -284,13 +284,13 @@ static void initialize(Class *clazz) { ((ImageInterface *) clazz->interface)->imageWithResource = imageWithResource; ((ImageInterface *) clazz->interface)->imageWithResourceName = imageWithResourceName; ((ImageInterface *) clazz->interface)->imageWithSurface = imageWithSurface; - ((ImageInterface *) clazz->interface)->imageWithSVG = imageWithSVG; + ((ImageInterface *) clazz->interface)->imageWithSvg = imageWithSvg; ((ImageInterface *) clazz->interface)->initWithBytes = initWithBytes; ((ImageInterface *) clazz->interface)->initWithData = initWithData; ((ImageInterface *) clazz->interface)->initWithResource = initWithResource; ((ImageInterface *) clazz->interface)->initWithResourceName = initWithResourceName; ((ImageInterface *) clazz->interface)->initWithSurface = initWithSurface; - ((ImageInterface *) clazz->interface)->initWithSVG = initWithSVG; + ((ImageInterface *) clazz->interface)->initWithSvg = initWithSvg; ((ImageInterface *) clazz->interface)->size = size; } diff --git a/Sources/ObjectivelyMVC/Image.h b/Sources/ObjectivelyMVC/Image.h index 6a5f8243..aa1fe40c 100644 --- a/Sources/ObjectivelyMVC/Image.h +++ b/Sources/ObjectivelyMVC/Image.h @@ -120,7 +120,7 @@ struct ImageInterface { /** * @static - * @fn Image *Image::imageWithSVG(const uint8_t *bytes, size_t length, float scale) + * @fn Image *Image::imageWithSvg(const uint8_t *bytes, size_t length, float scale) * @brief Instantiates an Image by rasterizing the specified SVG. * @param bytes The SVG document. * @param length The length of `bytes`. @@ -128,7 +128,7 @@ struct ImageInterface { * @return The new Image, or `NULL` on error. * @memberof Image */ - Image *(*imageWithSVG)(const uint8_t *bytes, size_t length, float scale); + Image *(*imageWithSvg)(const uint8_t *bytes, size_t length, float scale); /** * @static @@ -182,7 +182,7 @@ struct ImageInterface { Image *(*initWithResourceName)(Image *self, const char *name); /** - * @fn Image *Image::initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) + * @fn Image *Image::initWithSvg(Image *self, const uint8_t *bytes, size_t length, float scale) * @brief Initializes this Image by rasterizing the specified SVG at its intrinsic size times * `scale`. * @param self The Image. @@ -192,7 +192,7 @@ struct ImageInterface { * @return The initialized Image, or `NULL` on error. * @memberof Image */ - Image *(*initWithSVG)(Image *self, const uint8_t *bytes, size_t length, float scale); + Image *(*initWithSvg)(Image *self, const uint8_t *bytes, size_t length, float scale); /** * @fn Image *Image::initWithSurface(Image *self, SDL_Surface *surface)