From 2efa449f6d05d2a55bb6b283e39e18a5a622ae44 Mon Sep 17 00:00:00 2001 From: squid233 <60126026+squid233@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:06:34 +0800 Subject: [PATCH] [STB] Added STBImageResize2; rewriting STBVorbis --- .../src/main/java/overrungl/OverrunGL.java | 2 +- .../overrungl/stb/STBIRInputCallback.java | 80 +++++++ .../overrungl/stb/STBIRKernelCallback.java | 46 ++++ .../overrungl/stb/STBIROutputCallback.java | 56 +++++ .../main/java/overrungl/stb/STBIRRESIZE.java | 111 +++++++++ .../overrungl/stb/STBIRSupportCallback.java | 46 ++++ .../java/overrungl/stb/STBImageResize2.java | 217 ++++++++++++++++- .../main/java/overrungl/stb/STBVorbis.java | 226 +++++++++--------- 8 files changed, 674 insertions(+), 110 deletions(-) create mode 100644 modules/overrungl.stb/src/main/java/overrungl/stb/STBIRInputCallback.java create mode 100644 modules/overrungl.stb/src/main/java/overrungl/stb/STBIRKernelCallback.java create mode 100644 modules/overrungl.stb/src/main/java/overrungl/stb/STBIROutputCallback.java create mode 100644 modules/overrungl.stb/src/main/java/overrungl/stb/STBIRRESIZE.java create mode 100644 modules/overrungl.stb/src/main/java/overrungl/stb/STBIRSupportCallback.java diff --git a/modules/overrungl.core/src/main/java/overrungl/OverrunGL.java b/modules/overrungl.core/src/main/java/overrungl/OverrunGL.java index f7a005e0..cdb16ab0 100644 --- a/modules/overrungl.core/src/main/java/overrungl/OverrunGL.java +++ b/modules/overrungl.core/src/main/java/overrungl/OverrunGL.java @@ -41,7 +41,7 @@ public final class OverrunGL { /** * The version of STB native libraries. */ - public static final String STB_VERSION = "0.1.0.0"; + public static final String STB_VERSION = "0.1.0.1"; private static final Consumer DEFAULT_LOGGER = System.err::println; private static Consumer apiLogger = DEFAULT_LOGGER; diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRInputCallback.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRInputCallback.java new file mode 100644 index 00000000..1871fd31 --- /dev/null +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRInputCallback.java @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2024 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrungl.stb; + +import overrun.marshal.Upcall; +import overrungl.NativeType; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +/** + * INPUT CALLBACK: this callback is used for input scanlines + *

+ * The input callback is super flexible - it calls you with the input address + * (based on the stride and base pointer), it gives you an optional_output + * pointer that you can fill, or you can just return your own pointer into + * your own data. + *

+ * You can also do conversion from non-supported data types if necessary - in + * this case, you ignore the input_ptr and just use the x and y parameters to + * calculate your own input_ptr based on the size of each non-supported pixel. + * (Something like the third example below.) + *

+ * You can also install just an input or just an output callback by setting the + * callback that you don't want to zero. + *

+ * First example, progress: (getting a callback that you can monitor the progress): + *

{@code void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
+ * {
+ *    percentage_done = y / input_height;
+ *    return input_ptr;  // use buffer from call
+ * }}
+ *

+ * Next example, copying: (copy from some other buffer or stream): + *

{@code void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
+ * {
+ *    CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes );
+ *    return optional_output;  // return the optional buffer that we filled
+ * }}
+ *

+ * Third example, input another buffer without copying: (zero-copy from other buffer): + *

{@code void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context )
+ * {
+ *    void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes );
+ *    return pixels;       // return pointer to your data without copying
+ * }}
+ * + * @author squid233 + * @since 0.1.0 + */ +@FunctionalInterface +public interface STBIRInputCallback extends Upcall { + /** + * the type + */ + Type TYPE = Upcall.type(); + + @NativeType("void const *") + @Stub + MemorySegment invoke(@NativeType("void *") MemorySegment optional_output, @NativeType("void const *") MemorySegment input_ptr, int num_pixels, int x, int y, @NativeType("void *") MemorySegment context); + + @Override + default MemorySegment stub(Arena arena) { + return TYPE.of(arena, this); + } +} diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRKernelCallback.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRKernelCallback.java new file mode 100644 index 00000000..242c7f82 --- /dev/null +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRKernelCallback.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2024 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrungl.stb; + +import overrun.marshal.Upcall; +import overrungl.NativeType; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +/** + * callbacks for user installed filters + * + * @author squid233 + * @since 0.1.0 + */ +@FunctionalInterface +public interface STBIRKernelCallback extends Upcall { + /** + * the type + */ + Type TYPE = Upcall.type(); + + // centered at zero + @Stub + float invoke(float x, float scale, @NativeType("void *") MemorySegment user_data); + + @Override + default MemorySegment stub(Arena arena) { + return TYPE.of(arena, this); + } +} diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBIROutputCallback.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIROutputCallback.java new file mode 100644 index 00000000..95b86874 --- /dev/null +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIROutputCallback.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2024 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrungl.stb; + +import overrun.marshal.Upcall; +import overrungl.NativeType; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +/** + * OUTPUT CALLBACK: this callback is used for output scanlines + *

+ * The output callback is considerably simpler - it just calls you so that you can dump + * out each scanline. You could even directly copy out to disk if you have a simple format + * like TGA or BMP. You can also convert to other output types here if you want. + *

+ * Simple example: + *

{@code void const * my_output( void * output_ptr, int num_pixels, int y, void * context )
+ * {
+ *    percentage_done = y / output_height;
+ *    fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file );
+ * }}
+ * + * @author squid233 + * @since 0.1.0 + */ +@FunctionalInterface +public interface STBIROutputCallback extends Upcall { + /** + * the type + */ + Type TYPE = Upcall.type(); + + @Stub + void invoke(@NativeType("void const *") MemorySegment output_ptr, int num_pixels, int y, @NativeType("void *") MemorySegment context); + + @Override + default MemorySegment stub(Arena arena) { + return TYPE.of(arena, this); + } +} diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRRESIZE.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRRESIZE.java new file mode 100644 index 00000000..88540f6e --- /dev/null +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRRESIZE.java @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2024 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrungl.stb; + +import overrun.marshal.struct.Struct; + +import java.lang.foreign.*; + +import static java.lang.foreign.ValueLayout.*; + +/** + * {@code STBIR_RESIZE} + * + * @author squid233 + * @since 0.1.0 + */ +public final class STBIRRESIZE extends Struct { + /** + * Layout + */ + public static final StructLayout LAYOUT = MemoryLayout.structLayout( + ADDRESS.withName("user_data"), + ADDRESS.withName("input_pixels"), + JAVA_INT.withName("input_w"), + JAVA_INT.withName("input_h"), + JAVA_DOUBLE.withName("input_s0"), + JAVA_DOUBLE.withName("input_t0"), + JAVA_DOUBLE.withName("input_s1"), + JAVA_DOUBLE.withName("input_t1"), + ADDRESS.withName("input_cb"), + ADDRESS.withName("output_pixels"), + JAVA_INT.withName("output_w"), + JAVA_INT.withName("output_h"), + JAVA_INT.withName("output_subx"), + JAVA_INT.withName("output_suby"), + JAVA_INT.withName("output_subw"), + JAVA_INT.withName("output_subh"), + ADDRESS.withName("output_cb"), + JAVA_INT.withName("input_stride_in_bytes"), + JAVA_INT.withName("output_stride_in_bytes"), + JAVA_INT.withName("splits"), + JAVA_INT.withName("fast_alpha"), + JAVA_INT.withName("needs_rebuild"), + JAVA_INT.withName("called_alloc"), + JAVA_INT.withName("input_pixel_layout_public"), + JAVA_INT.withName("output_pixel_layout_public"), + JAVA_INT.withName("input_data_type"), + JAVA_INT.withName("output_data_type"), + JAVA_INT.withName("horizontal_filter"), + JAVA_INT.withName("vertical_filter"), + JAVA_INT.withName("horizontal_edge"), + JAVA_INT.withName("vertical_edge"), + ADDRESS.withName("horizontal_filter_kernel"), + ADDRESS.withName("horizontal_filter_support"), + ADDRESS.withName("vertical_filter_kernel"), + ADDRESS.withName("vertical_filter_support"), + ADDRESS.withName("samplers") + ); + + /** + * Creates a struct with the given layout. + * + * @param segment the segment + * @param elementCount the element count + */ + public STBIRRESIZE(MemorySegment segment, long elementCount) { + super(segment, elementCount, LAYOUT); + } + + /** + * Allocates a struct with the given layout. + * + * @param allocator the allocator + * @param elementCount the element count + */ + public STBIRRESIZE(SegmentAllocator allocator, long elementCount) { + super(allocator, elementCount, LAYOUT); + } + + /** + * Creates a struct with the given layout. + * + * @param segment the segment + */ + public STBIRRESIZE(MemorySegment segment) { + super(segment, LAYOUT); + } + + /** + * Allocates a struct with the given layout. + * + * @param allocator the allocator + */ + public STBIRRESIZE(SegmentAllocator allocator) { + super(allocator, LAYOUT); + } +} diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRSupportCallback.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRSupportCallback.java new file mode 100644 index 00000000..97c3fd8d --- /dev/null +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBIRSupportCallback.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2024 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrungl.stb; + +import overrun.marshal.Upcall; +import overrungl.NativeType; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +/** + * callbacks for user installed filters + * + * @author squid233 + * @since 0.1.0 + */ +@FunctionalInterface +public interface STBIRSupportCallback extends Upcall { + /** + * the type + */ + Type TYPE = Upcall.type(); + + // centered at zero + @Stub + float invoke(float scale, @NativeType("void *") MemorySegment user_data); + + @Override + default MemorySegment stub(Arena arena) { + return TYPE.of(arena, this); + } +} diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBImageResize2.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBImageResize2.java index fd229e71..ae8a6b3c 100644 --- a/modules/overrungl.stb/src/main/java/overrungl/stb/STBImageResize2.java +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBImageResize2.java @@ -70,10 +70,10 @@ MemorySegment resizeFloatLinear(@NativeType("const float *") MemorySegment input // medium api @Entrypoint("stbir_resize") @NativeType("void *") - MemorySegment resize(@NativeType("const void *") MemorySegment input_pixels , int input_w , int input_h, int input_stride_in_bytes, + MemorySegment resize(@NativeType("const void *") MemorySegment input_pixels, int input_w, int input_h, int input_stride_in_bytes, @NativeType("void *") MemorySegment output_pixels, int output_w, int output_h, int output_stride_in_bytes, STBIRPixelLayout pixel_layout, STBIRDatatype data_type, - STBIREdge edge, STBIRFilter filter ); + STBIREdge edge, STBIRFilter filter); //=============================================================== // Extended-complexity API @@ -98,4 +98,217 @@ MemorySegment resize(@NativeType("const void *") MemorySegment input_pixels , in // 4) Resample by calling stbir_resize_extended(). // 5) Call stbir_free_samplers() if you called stbir_build_samplers() //-------------------------------- + + /** + * First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls! + * + * @param resize resize + * @param input_pixels input_pixels + * @param input_w input_w + * @param input_h input_h + * @param input_stride_in_bytes input_stride_in_bytes, stride can be zero + * @param output_pixels output_pixels + * @param output_w output_w + * @param output_h output_h + * @param output_stride_in_bytes output_stride_in_bytes, stride can be zero + * @param pixel_layout pixel_layout + * @param data_type data_type + */ + @Entrypoint("stbir_resize_init") + void resizeInit(STBIRRESIZE resize, + @NativeType("const void *") MemorySegment input_pixels, int input_w, int input_h, int input_stride_in_bytes, + @NativeType("void *") MemorySegment output_pixels, int output_w, int output_h, int output_stride_in_bytes, + STBIRPixelLayout pixel_layout, STBIRDatatype data_type); + + @Entrypoint("stbir_set_datatypes") + void setDatatypes(STBIRRESIZE resize, STBIRDatatype input_type, STBIRDatatype output_type); + + /** + * no callbacks by default + * + * @param resize resize + * @param input_cb input_cb + * @param output_cb output_cb + */ + @Entrypoint("stbir_set_pixel_callbacks") + void setPixelCallbacks(STBIRRESIZE resize, STBIRInputCallback input_cb, STBIROutputCallback output_cb); + + /** + * pass back STBIR_RESIZE* by default + * + * @param resize resize + * @param user_data user_data + */ + @Entrypoint("stbir_set_user_data") + void setUserData(STBIRRESIZE resize, @NativeType("void *") MemorySegment user_data); + + @Entrypoint("stbir_set_buffer_ptrs") + void setBufferPtrs(STBIRRESIZE resize, @NativeType("const void *") MemorySegment input_pixels, int input_stride_in_bytes, @NativeType("void *") MemorySegment output_pixels, int output_stride_in_bytes); + + //=============================================================== + // If you call any of these functions, you will trigger a sampler rebuild! + //-------------------------------- + + /** + * sets new buffer layouts + * + * @param resize resize + * @param input_pixel_layout input_pixel_layout + * @param output_pixel_layout output_pixel_layout + * @return int + */ + @Entrypoint("stbir_set_pixel_layouts") + int setPixelLayouts(STBIRRESIZE resize, STBIRPixelLayout input_pixel_layout, STBIRPixelLayout output_pixel_layout); + + /** + * CLAMP by default + * + * @param resize resize + * @param horizontal_edge horizontal_edge + * @param vertical_edge vertical_edge + * @return int + */ + @Entrypoint("stbir_set_edgemodes") + int setEdgemodes(STBIRRESIZE resize, STBIREdge horizontal_edge, STBIREdge vertical_edge); + + /** + * STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default + * + * @param resize resize + * @param horizontal_filter horizontal_filter + * @param vertical_filter vertical_filter + * @return int + */ + @Entrypoint("stbir_set_filters") + int setFilters(STBIRRESIZE resize, STBIRFilter horizontal_filter, STBIRFilter vertical_filter); + + @Entrypoint("stbir_set_filter_callbacks") + int setFilterCallbacks(STBIRRESIZE resize, STBIRKernelCallback horizontal_filter, STBIRSupportCallback horizontal_support, STBIRKernelCallback vertical_filter, STBIRSupportCallback vertical_support); + + /** + * sets both sub-regions (full regions by default) + * + * @param resize resize + * @param subx subx + * @param suby suby + * @param subw subw + * @param subh subh + * @return int + */ + @Entrypoint("stbir_set_pixel_subrect") + int setPixelSubrect(STBIRRESIZE resize, int subx, int suby, int subw, int subh); + + /** + * sets input sub-region (full region by default) + * + * @param resize resize + * @param s0 s0 + * @param t0 t0 + * @param s1 s1 + * @param t1 t1 + * @return int + */ + @Entrypoint("stbir_set_input_subrect") + int setInputSubrect(STBIRRESIZE resize, double s0, double t0, double s1, double t1); + + /** + * sets output sub-region (full region by default) + * + * @param resize resize + * @param subx subx + * @param suby suby + * @param subw subw + * @param subh subh + * @return int + */ + @Entrypoint("stbir_set_output_pixel_subrect") + int setOutputPixelSubrect(STBIRRESIZE resize, int subx, int suby, int subw, int subh); + + /** + * when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique + * that fills the zero alpha pixel's RGB values with something plausible. + * If you don't care about areas of + * zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA + * types of resizes. + * + * @param resize resize + * @param non_pma_alpha_speed_over_quality non_pma_alpha_speed_over_quality + * @return int + */ + @Entrypoint("stbir_set_non_pm_alpha_speed_over_quality") + int setNonPmAlphaSpeedOverQuality(STBIRRESIZE resize, int non_pma_alpha_speed_over_quality); + + //=============================================================== + // You can call build_samplers to prebuild all the internal data we need to resample. + // Then, if you call resize_extended many times with the same resize, you only pay the + // cost once. + // If you do call build_samplers, you MUST call free_samplers eventually. + //-------------------------------- + + /** + * This builds the samplers and does one allocation + * + * @param resize resize + * @return int + */ + @Entrypoint("stbir_build_samplers") + int buildSamplers(STBIRRESIZE resize); + + /** + * You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits + * + * @param resize resize + */ + @Entrypoint("stbir_free_samplers") + void freeSamplers(STBIRRESIZE resize); + + /** + * And this is the main function to perform the resize synchronously on one thread. + * + * @param resize resize + * @return int + */ + @Entrypoint("stbir_resize_extended") + int resizeExtended(STBIRRESIZE resize); + + //=============================================================== + // Use these functions for multithreading. + // 1) You call stbir_build_samplers_with_splits first on the main thread + // 2) Then stbir_resize_with_split on each thread + // 3) stbir_free_samplers when done on the main thread + //-------------------------------- + + /** + * This will build samplers for threading. + * You can pass in the number of threads you'd like to use (try_splits). + * It returns the number of splits (threads) that you can call it with. + * It might be less if the image resize can't be split up that many ways. + * + * @param resize resize + * @param try_splits the number of threads you'd like to use + * @return the number of splits (threads) that you can call it with + */ + @Entrypoint("stbir_build_samplers_with_splits") + int buildSamplersWithSplits(STBIRRESIZE resize, int try_splits); + + /** + * This function does a split of the resizing (you call this fuction for each + * split, on multiple threads). + * A split is a piece of the output resize pixel space. + *

+ * Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split! + *

+ * Usually, you will always call stbir_resize_split with split_start as the thread_index + * and "1" for the split_count. + * But, if you have a weird situation where you MIGHT want 8 threads, but sometimes + * only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the + * split_count each time to turn in into a 4 thread resize. (This is unusual). + * + * @param resize resize + * @param split_start split_start + * @param split_count split_count + * @return int + */ + @Entrypoint("stbir_resize_extended_split") + int resizeExtendedSplit(STBIRRESIZE resize, int split_start, int split_count); } diff --git a/modules/overrungl.stb/src/main/java/overrungl/stb/STBVorbis.java b/modules/overrungl.stb/src/main/java/overrungl/stb/STBVorbis.java index 22ad0104..59434a0c 100644 --- a/modules/overrungl.stb/src/main/java/overrungl/stb/STBVorbis.java +++ b/modules/overrungl.stb/src/main/java/overrungl/stb/STBVorbis.java @@ -16,120 +16,132 @@ package overrungl.stb; +import overrun.marshal.ByValue; +import overrun.marshal.gen.Entrypoint; import overrungl.NativeType; -import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; -import java.lang.foreign.ValueLayout; -import java.lang.invoke.MethodHandle; - -import static overrungl.FunctionDescriptors.*; -import static overrungl.stb.Handles.downcall; /** + * normally stb_vorbis uses malloc() to allocate memory at startup, + * and alloca() to allocate temporary memory during a frame on the + * stack. + * (Memory consumption will depend on the amount of setup + * data in the file and how you set the compile flags for speed + * vs. size. In my test files the maximal-size usage is ~150KB.) + *

+ * You can modify the wrapper functions in the source (setup_malloc, + * setup_temp_malloc, temp_malloc) to change this behavior, or you + * can use a simpler allocation model: you pass in a buffer from + * which stb_vorbis will allocate _all_ its memory (including the + * temp memory). + * "open" may fail with a VORBIS_outofmem if you + * do not pass in enough data; there is no way to determine how + * much you do need except to succeed (at which point you can + * query get_info to find the exact amount required. yes I know + * this is lame). + *

+ * If you pass in a non-NULL buffer of the type below, allocation + * will occur from it as described above. + * Otherwise just pass NULL + * to use malloc()/alloca() + * * @author squid233 * @since 0.1.0 */ -public final class STBVorbis { - private static MethodHandle - stb_vorbis_get_info, stb_vorbis_get_comment, stb_vorbis_get_error, stb_vorbis_close, stb_vorbis_get_sample_offset, stb_vorbis_get_file_offset, - stb_vorbis_open_pushdata, stb_vorbis_decode_frame_pushdata, stb_vorbis_flush_pushdata, stb_vorbis_decode_filename, stb_vorbis_decode_memory, stb_vorbis_open_memory, - stb_vorbis_open_filename, stb_vorbis_open_file, stb_vorbis_open_file_section, stb_vorbis_seek_frame, stb_vorbis_seek, stb_vorbis_seek_start, - stb_vorbis_stream_length_in_samples, stb_vorbis_stream_length_in_seconds, stb_vorbis_get_frame_float, stb_vorbis_get_frame_short_interleaved, stb_vorbis_get_frame_short, stb_vorbis_get_samples_float_interleaved, - stb_vorbis_get_samples_float, stb_vorbis_get_samples_short_interleaved, stb_vorbis_get_samples_short; - - static { - create(); - } - - private static void create() { - stb_vorbis_get_info = downcall("stb_vorbis_get_info", FunctionDescriptor.of(STBVorbisInfo.LAYOUT, ValueLayout.ADDRESS)); - stb_vorbis_get_comment = downcall("stb_vorbis_get_comment", FunctionDescriptor.of(STBVorbisComment.LAYOUT, ValueLayout.ADDRESS)); - stb_vorbis_get_error = downcall("stb_vorbis_get_error", fd_PI); - stb_vorbis_close = downcall("stb_vorbis_close", PV); - stb_vorbis_get_sample_offset = downcall("stb_vorbis_get_sample_offset", fd_PI); - stb_vorbis_get_file_offset = downcall("stb_vorbis_get_file_offset", fd_PI); - stb_vorbis_open_pushdata = downcall("stb_vorbis_open_pushdata", PIPPPP); - stb_vorbis_decode_frame_pushdata = downcall("stb_vorbis_decode_frame_pushdata", PPIPPPI); - stb_vorbis_flush_pushdata = downcall("stb_vorbis_flush_pushdata", PV); - stb_vorbis_decode_filename = downcall("stb_vorbis_decode_filename", PPPPI); - stb_vorbis_decode_memory = downcall("stb_vorbis_decode_memory", PIPPPI); - stb_vorbis_open_memory = downcall("stb_vorbis_open_memory", PIPPP); - stb_vorbis_open_filename = downcall("stb_vorbis_open_filename", PPPP); - stb_vorbis_open_file = downcall("stb_vorbis_open_file", PIPPP); - stb_vorbis_open_file_section = downcall("stb_vorbis_open_file_section", PIPPIP); - stb_vorbis_seek_frame = downcall("stb_vorbis_seek_frame", PII); - stb_vorbis_seek = downcall("stb_vorbis_seek", PII); - stb_vorbis_seek_start = downcall("stb_vorbis_seek_start", fd_PI); - stb_vorbis_stream_length_in_samples = downcall("stb_vorbis_stream_length_in_samples", fd_PI); - stb_vorbis_stream_length_in_seconds = downcall("stb_vorbis_stream_length_in_seconds", PF); - stb_vorbis_get_frame_float = downcall("stb_vorbis_get_frame_float", PPPI); - stb_vorbis_get_frame_short_interleaved = downcall("stb_vorbis_get_frame_short_interleaved", PIPII); - stb_vorbis_get_frame_short = downcall("stb_vorbis_get_frame_short", PIPII); - stb_vorbis_get_samples_float_interleaved = downcall("stb_vorbis_get_samples_float_interleaved", PIPII); - stb_vorbis_get_samples_float = downcall("stb_vorbis_get_samples_float", PIPII); - stb_vorbis_get_samples_short_interleaved = downcall("stb_vorbis_get_samples_short_interleaved", PIPII); - stb_vorbis_get_samples_short = downcall("stb_vorbis_get_samples_short", PIPII); - } - - private STBVorbis() { - //no instance - } - - public static @NativeType("stb_vorbis_info") MemorySegment ngetInfo(SegmentAllocator allocator, @NativeType("stb_vorbis*") MemorySegment f) { - try { - return (MemorySegment) stb_vorbis_get_info.invokeExact(allocator, f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - - public static STBVorbisInfo getInfo(SegmentAllocator allocator, @NativeType("stb_vorbis*") MemorySegment f) { - return new STBVorbisInfo(ngetInfo(allocator, f)); - } - - public static @NativeType("stb_vorbis_comment") MemorySegment ngetComment(SegmentAllocator allocator, @NativeType("stb_vorbis*") MemorySegment f) { - try { - return (MemorySegment) stb_vorbis_get_comment.invokeExact(allocator, f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - - public static STBVorbisComment getComment(SegmentAllocator allocator, @NativeType("stb_vorbis*") MemorySegment f) { - return new STBVorbisComment(ngetComment(allocator, f)); - } - - public static int getError(@NativeType("stb_vorbis*") MemorySegment f) { - try { - return (int) stb_vorbis_get_error.invokeExact(f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - - public static void close(@NativeType("stb_vorbis*") MemorySegment f) { - try { - stb_vorbis_close.invokeExact(f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - - public static int getSampleOffset(@NativeType("stb_vorbis*") MemorySegment f) { - try { - return (int) stb_vorbis_get_sample_offset.invokeExact(f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } - - public static int getFileOffset(@NativeType("stb_vorbis*") MemorySegment f) { - try { - return (int) stb_vorbis_get_file_offset.invokeExact(f); - } catch (Throwable e) { - throw new AssertionError("should not reach here", e); - } - } +public interface STBVorbis { + /** + * get general information about the file + * + * @param allocator allocator + * @param f f + * @return general information about the file + */ + @ByValue + @Entrypoint("stb_vorbis_get_info") + STBVorbisInfo getInfo(SegmentAllocator allocator, @NativeType("stb_vorbis *") MemorySegment f); + + /** + * get ogg comments + * + * @param allocator allocator + * @param f f + * @return ogg comments + */ + @ByValue + @Entrypoint("stb_vorbis_get_comment") + STBVorbisComment getComment(SegmentAllocator allocator, @NativeType("stb_vorbis *") MemorySegment f); + + /** + * get the last error detected (clears it, too) + * + * @param f f + * @return the last error detected + */ + @Entrypoint("stb_vorbis_get_error") + int getError(@NativeType("stb_vorbis *") MemorySegment f); + + /** + * close an ogg vorbis file and free all memory in use + * + * @param f f + */ + @Entrypoint("stb_vorbis_close") + void close(@NativeType("stb_vorbis *") MemorySegment f); + + /** + * this function returns the offset (in samples) from the beginning of the + * file that will be returned by the next decode, if it is known, or -1 + * otherwise. + * after a flush_pushdata() call, this may take a while before + * it becomes valid again. + * NOT WORKING YET after a seek with PULLDATA API + * + * @param f f + * @return the offset + */ + @Entrypoint("stb_vorbis_get_sample_offset") + int getSampleOffset(@NativeType("stb_vorbis *") MemorySegment f); + + /** + * {@return the current seek point within the file, or offset from the beginning + * of the memory buffer} + * In pushdata mode it returns 0. + * + * @param f f + */ + @Entrypoint("stb_vorbis_get_file_offset") + int getFileOffset(@NativeType("stb_vorbis *") MemorySegment f); + + // this API allows you to get blocks of data from any source and hand + // them to stb_vorbis. you have to buffer them; stb_vorbis will tell + // you how much it used, and you have to give it the rest next time; + // and stb_vorbis may not have enough data to work with and you will + // need to give it the same data again PLUS more. Note that the Vorbis + // specification does not bound the size of an individual frame. + + /** + * create a vorbis decoder by passing in the initial data block containing + * the ogg&vorbis headers (you don't need to do parse them, just provide + * the first N bytes of the file--you're told if it's not enough, see below) + * + * @param datablock datablock + * @param datablock_length_in_bytes datablock_length_in_bytes + * @param datablock_memory_consumed_in_bytes datablock_memory_consumed_in_bytes + * @param error error + * @param alloc_buffer alloc_buffer + * @return on success, returns an stb_vorbis *, does not set error, returns the amount of + * data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; + * on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed + * if returns NULL and *error is VORBIS_need_more_data, then the input block was + * incomplete and you need to pass in a larger block from the start of the file + */ + @Entrypoint("stb_vorbis_open_pushdata") + @NativeType("stb_vorbis *") + MemorySegment openPushdata( + @NativeType("const unsigned char *") MemorySegment datablock, int datablock_length_in_bytes, + @NativeType("int *") MemorySegment datablock_memory_consumed_in_bytes, + @NativeType("int *") MemorySegment error, + STBVorbisAlloc alloc_buffer); }