diff --git a/CHANGES b/CHANGES index e7af312c53aad3740eaeab7d675daccb8e9abae7..c752f07ae64fe84ac76281c440ccc77b430ef7e9 100644 --- a/CHANGES +++ b/CHANGES @@ -37,6 +37,8 @@ Incompatible changes New modules and functions ------------------------- +o Support for decoding the AVIF image format through the new Image.AVIF module. + New features ------------ diff --git a/src/post_modules/_Image_AVIF/Makefile.in b/src/post_modules/_Image_AVIF/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..7f9c90cce970ca1a115bbcc4cbc6aebc6dd8aed8 --- /dev/null +++ b/src/post_modules/_Image_AVIF/Makefile.in @@ -0,0 +1,10 @@ +@make_variables@ +VPATH=@srcdir@ +OBJS=image_avif.o +MODULE_LDFLAGS=@LDFLAGS@ @LIBS@ + +@dynamic_module_makefile@ + +image_avif.o : $(SRCDIR)/image_avif.c + +@dependencies@ diff --git a/src/post_modules/_Image_AVIF/acconfig.h b/src/post_modules/_Image_AVIF/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..3c9afa6e9d8d43f8b787f619e9daf2928c158373 --- /dev/null +++ b/src/post_modules/_Image_AVIF/acconfig.h @@ -0,0 +1,7 @@ +/* +|| This file is part of Pike. For copyright information see COPYRIGHT. +|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING +|| for more information. +*/ +@TOP@ +@BOTTOM@ diff --git a/src/post_modules/_Image_AVIF/config.h.in b/src/post_modules/_Image_AVIF/config.h.in new file mode 100644 index 0000000000000000000000000000000000000000..6bc5b6818f6fc5760308ee551345f7b8a95059b7 --- /dev/null +++ b/src/post_modules/_Image_AVIF/config.h.in @@ -0,0 +1,215 @@ +/* config.h.in. Generated from configure.in by autoheader. */ +/* +|| This file is part of Pike. For copyright information see COPYRIGHT. +|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING +|| for more information. +*/ + +/* Define to 1 if you have the <avif/avif.h> header file. */ +#undef HAVE_AVIF_AVIF_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define when the -lavif library is available */ +#undef HAVE_LIBAVIF + +/* Define to 1 if you have the <minix/config.h> header file. */ +#undef HAVE_MINIX_CONFIG_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the <wchar.h> header file. */ +#undef HAVE_WCHAR_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + + +#ifndef _DARWIN_C_SOURCE +/* Overrides disabling of non-posix symbols by _POSIX_C_SOURCE on Darwin. */ +#undef _DARWIN_C_SOURCE +#endif + +#ifndef POSIX_SOURCE + /* We must define this *always* */ +# define POSIX_SOURCE 1 +#endif +#ifndef _POSIX_C_SOURCE +#if defined(__APPLE__) && defined(HAVE_SYS_SOCKET_H) +/* The <sys/socket.h> headerfile is broken on MacOS X as is disregards + * _DARWIN_C_SOURCE with respect to the declaration of sendfile(2). + * We thus need to include <sys/socket.h> before we set the POSIX + * compatibility level. + */ +#include <sys/socket.h> +#endif + /* Version of POSIX that we want to support. + * Note that POSIX.1-2001 and later require C99, and the earlier + * require C89. + * undef Not POSIX. + * 1 POSIX.1-1990 + * 2 POSIX.2-1992 + * 199309L POSIX.1b-1993 (Real Time) + * 199506L POSIX.1c-1995 (POSIX Threads) + * 200112L POSIX.1-2001 (Austin Group Revision) + * 200809L POSIX.1-2008 + */ +# undef _POSIX_C_SOURCE +#endif +#ifndef _XOPEN_SOURCE + /* Version of XPG that we want to support. + * Note that this interacts with _POSIX_C_SOURCE above. + * undef Not XPG (X Open Group). + * 1 XPG 3 or 4 or 4v2 (see below). + * 500 XPG 5 (POSIX.1c-1995). + * 600 XPG 6 (POSIX.1-2001). + * 700 XPG 7 (POSIX.1-2008). + */ +# undef _XOPEN_SOURCE + +# if defined(_XOPEN_SOURCE) && ((_XOPEN_SOURCE + 0) < 500) + /* Define to 4 for XPG 4. NB: Overrides _XOPEN_SOURCE_EXTENDED below. */ +# undef _XOPEN_VERSION + + /* Define to 1 (and do NOT define _XOPEN_VERSION) for XPG 4v2. */ +# undef _XOPEN_SOURCE_EXTENDED +# endif +#endif +#ifndef _NETBSD_SOURCE +#undef _NETBSD_SOURCE +#endif +#ifndef __BSD_VISIBLE +#undef __BSD_VISIBLE +#endif + + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable general extensions on macOS. */ +#ifndef _DARWIN_C_SOURCE +# undef _DARWIN_C_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable X/Open compliant socket functions that do not require linking + with -lxnet on HP-UX 11.11. */ +#ifndef _HPUX_ALT_XOPEN_SOCKET_API +# undef _HPUX_ALT_XOPEN_SOCKET_API +#endif +/* Identify the host operating system as Minix. + This macro does not affect the system headers' behavior. + A future release of Autoconf may stop defining this macro. */ +#ifndef _MINIX +# undef _MINIX +#endif +/* Enable general extensions on NetBSD. + Enable NetBSD compatibility extensions on Minix. */ +#ifndef _NETBSD_SOURCE +# undef _NETBSD_SOURCE +#endif +/* Enable OpenBSD compatibility extensions on NetBSD. + Oddly enough, this does nothing on OpenBSD. */ +#ifndef _OPENBSD_SOURCE +# undef _OPENBSD_SOURCE +#endif +/* Define to 1 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_SOURCE +# undef _POSIX_SOURCE +#endif +/* Define to 2 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_1_SOURCE +# undef _POSIX_1_SOURCE +#endif +/* Enable POSIX-compatible threading on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ +#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ +#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ +# undef __STDC_WANT_IEC_60559_BFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ +#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ +# undef __STDC_WANT_IEC_60559_DFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ +#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ +# undef __STDC_WANT_IEC_60559_FUNCS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# undef __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ +#ifndef __STDC_WANT_LIB_EXT2__ +# undef __STDC_WANT_LIB_EXT2__ +#endif +/* Enable extensions specified by ISO/IEC 24747:2009. */ +#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ +# undef __STDC_WANT_MATH_SPEC_FUNCS__ +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable X/Open extensions. Define to 500 only if necessary + to make mbstate_t available. */ +#ifndef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif + diff --git a/src/post_modules/_Image_AVIF/configure.in b/src/post_modules/_Image_AVIF/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..06b25e567953970e97cf5f309e30c77d8436f910 --- /dev/null +++ b/src/post_modules/_Image_AVIF/configure.in @@ -0,0 +1,21 @@ +# +AC_INIT(image_avif.cmod) +AC_MODULE_INIT() +AC_CONFIG_HEADER(config.h) + +PIKE_FEATURE_NODEP(Image.AVIF) + +AC_CHECK_HEADERS(avif/avif.h) +if test $ac_cv_header_avif_avif_h = yes ; then + have_libavif=false + AC_CHECK_LIB([avif], [avifVersion], [ + LIBS="${LIBS-} -lavif" + have_libavif=true + ]) + if $have_libavif; then + AC_DEFINE(HAVE_LIBAVIF,[],[Define when the -lavif library is available]) + PIKE_FEATURE_OK(Image.AVIF) + fi +fi + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/post_modules/_Image_AVIF/image_avif.cmod b/src/post_modules/_Image_AVIF/image_avif.cmod new file mode 100644 index 0000000000000000000000000000000000000000..146ce7b830fef65bd1785f802355a96834e68e66 --- /dev/null +++ b/src/post_modules/_Image_AVIF/image_avif.cmod @@ -0,0 +1,195 @@ +/* -*- c -*- +|| This file is part of Pike. For copyright information see COPYRIGHT. +|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING +|| for more information. +*/ + +/* https://github.com/AOMediaCodec/libavif/tree/main/examples + */ + +#include "pike_macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "threads.h" +#include "array.h" +#include "builtin_functions.h" +#include "mapping.h" +#include "pike_error.h" +#include "operators.h" +#include "module_support.h" +#include "modules/Image/image.h" +#include "config.h" + +#ifdef HAVE_LIBAVIF +DECLARATIONS + +#include <avif/avif.h> + +static struct program *img_program; + +static struct program *image_program() +{ + if( !img_program ) + { + push_static_text("Image.Image"); + SAFE_APPLY_MASTER("resolv",1); + img_program = Pike_sp[-1].u.program; + Pike_sp--; + } + return img_program; +} + +static struct object *allocate_image(int w, int h ) +{ + push_int(w); + push_int(h); + return clone_object(image_program(),2); +} + +static inline unsigned char *image_ptr( struct object *img ) +{ + char *x; + if( !img ) return 0; + x = get_storage( img, image_program() ); + if( !x ) return 0; + return (unsigned char *)(((struct image *)x)->img); +} + +PIKEFUN mapping(string:object(Image.Image)|string) _decode( string data ) +{ + int width, height, npix; + uint8_t *ip, *ap; + uint8_t av; + struct object *i = NULL, *a = NULL; + + avifRGBImage rgb; + memset(&rgb, 0, sizeof(rgb)); + + avifDecoder *decoder = avifDecoderCreate(); + decoder->imageCountLimit = 1; + + avifResult result = avifDecoderSetIOMemory(decoder, (const uint8_t *)data->str, data->len); + if( result != AVIF_RESULT_OK ) { + goto cleanup; + } + + result = avifDecoderParse(decoder); + if( result != AVIF_RESULT_OK ) { + goto cleanup; + } + + if( avifDecoderNextImage(decoder) != AVIF_RESULT_OK ) { + goto cleanup; + } + + avifRGBImageSetDefaults(&rgb, decoder->image); + /*result =*/ avifRGBImageAllocatePixels(&rgb); + /*if( result != AVIF_RESULT_OK ) { + goto cleanup; + }*/ + + result = avifImageYUVToRGB(decoder->image, &rgb); + if( result != AVIF_RESULT_OK ) { + goto cleanup; + } + + pop_n_elems(args); + + width = decoder->image->width; + height = decoder->image->height; + + npix = width * height; + ip = ap = NULL; + + if( decoder->alphaPresent ) { + a = allocate_image( width, height ); + ap = image_ptr( a ); + } + i = allocate_image( width, height ); + ip = image_ptr( i ); + + if( rgb.depth > 8 ) { + uint16_t *rp = (uint16_t *)rgb.pixels; + int shift = rgb.depth - 8; + while( npix-- ) { + *(ip++) = (uint8_t)( *(rp++) >> shift ); // R + *(ip++) = (uint8_t)( *(rp++) >> shift ); // G + *(ip++) = (uint8_t)( *(rp++) >> shift ); // B + av = (uint8_t)( *(rp++) >> shift ); + + if( ap ) { + *(ap++) = av; // A + *(ap++) = av; + *(ap++) = av; + } + } + } else { + uint8_t *rp = rgb.pixels; + while( npix-- ) { + *(ip++) = *(rp++); // R + *(ip++) = *(rp++); // G + *(ip++) = *(rp++); // B + av = *(rp++); + + if( ap ) { + *(ap++) = av; // A + *(ap++) = av; + *(ap++) = av; + } + } + } + + push_static_text( "format" ); push_text( "image/avif" ); + push_static_text( "xsize" ); push_int( width ); + push_static_text( "ysize" ); push_int( height ); + push_static_text( "image" ); push_object( i ); + push_static_text( "alpha" ); + if( a ) + push_object( a ); + else + push_undefined( ); + + f_aggregate_mapping( 10 ); + + cleanup: + avifRGBImageFreePixels(&rgb); + avifDecoderDestroy(decoder); + if( result != AVIF_RESULT_OK ) { + Pike_error("Failed to decode image\n"); + } +} + +PIKEFUN object(Image.Image) decode( string data ) +{ + struct mapping *m; + struct svalue *p; + + apply(Pike_fp->current_object, "_decode", args); + + m = Pike_sp[-1].u.mapping; + + if( TYPEOF(Pike_sp[-1]) != PIKE_T_MAPPING ) + Pike_error("Decoding failed\n"); + + p = simple_mapping_string_lookup(m,"image"); + + if( !p || TYPEOF(*p) != PIKE_T_OBJECT ) + Pike_error("Decoding failed\n"); + + ref_push_object( p->u.object ); + stack_unlink(1); +} + +PIKE_MODULE_INIT +{ + INIT + add_integer_constant("version", AVIF_VERSION, 0); +} + +PIKE_MODULE_EXIT +{ + EXIT +} +#endif