| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
-
- // zlib open source license
- //
- // Copyright (c) 2017 to 2025 David Forsgren Piuva
- //
- // 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.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- // Everything stored directly in the image types is immutable to allow value types to behave like reference types using the data that they point to.
- // Image types can not be dynamically casted, because the inheritance is entirely static without any virtual functions.
- #ifndef DFPSR_API_IMAGE
- #define DFPSR_API_IMAGE
- #include "../image/Image.h"
- #include "../image/Color.h"
- #include "../base/heap.h"
- #include "../math/scalar.h"
- namespace dsr {
- // Constructors
- // Pre-conditions:
- // 1 <= width <= 65536
- // 1 <= height <= 65536
- // Post-condition:
- // Returns a new image of width x height pixels, or an empty image on failure.
- AlignedImageU8 image_create_U8(int32_t width, int32_t height);
- AlignedImageU16 image_create_U16(int32_t width, int32_t height);
- AlignedImageF32 image_create_F32(int32_t width, int32_t height);
- OrderedImageRgbaU8 image_create_RgbaU8(int32_t width, int32_t height);
- AlignedImageRgbaU8 image_create_RgbaU8_native(int32_t width, int32_t height, PackOrderIndex packOrderIndex);
- // Properties
- // Returns image's width in pixels, or 0 from an empty image
- inline int32_t image_getWidth(const Image& image) { return image.impl_dimensions.getWidth(); }
- // Returns image's height in pixels, or 0 from an empty image
- inline int32_t image_getHeight(const Image& image) { return image.impl_dimensions.getHeight(); }
- // Stride is the offset from the beginning of one row to another.
- // May be larger than the image's width to align with cache lines or share pixel data with a wider image.
- // When you add a variable offset to a pointer in C++, the added offset is multiplied by the element size because the address is always stored in bytes.
- // Because all pixels have a power of two size, the multiplication can be optimized into a bit shift.
- // On ARM, adding whole elements to a pointer is just as fast as adding bytes, by shifting and adding in the same instruction.
- // On Intel/AMD, shifting and adding needs two instructions, so then it makes sense to pre-calculate the stride as bytes and cast to uint8_t* when adding.
- // Returns image's stride in whole pixels, or 0 from an empty image
- // Used when incrementing indices instead of pointers.
- inline int32_t image_getPixelStride(const Image& image) { return (intptr_t)image.impl_dimensions.getPixelStride(); }
- // Returns image's stride in bytes, or 0 from an empty image.
- inline int32_t image_getStride(const Image& image) { return (image_getPixelStride(image) << (uintptr_t)image.impl_dimensions.getLog2PixelSize()); }
- inline int32_t image_getStride(const ImageU8& image) { return image_getPixelStride(image); } // pixelStride * sizeof(uint8_t )
- inline int32_t image_getStride(const ImageU16& image) { return image_getPixelStride(image) << 1; } // pixelStride * sizeof(uint16_t)
- inline int32_t image_getStride(const ImageF32& image) { return image_getPixelStride(image) << 2; } // pixelStride * sizeof(float )
- inline int32_t image_getStride(const ImageRgbaU8& image) { return image_getPixelStride(image) << 2; } // pixelStride * sizeof(uint32_t)
- // Returns image's offset from the allocation start in whole pixels, or 0 from an empty image
- inline int64_t image_getPixelStartOffset(const Image& image) { return (int64_t)image.impl_dimensions.getPixelStartOffset(); }
- // Returns image's offset from the allocation start in bytes, or 0 from an empty image
- inline int64_t image_getStartOffset(const ImageU8& image) { return image_getPixelStartOffset(image); } // pixelStartOffset * sizeof(uint8_t )
- inline int64_t image_getStartOffset(const ImageU16& image) { return image_getPixelStartOffset(image) << 1; } // pixelStartOffset * sizeof(uint16_t)
- inline int64_t image_getStartOffset(const ImageF32& image) { return image_getPixelStartOffset(image) << 2; } // pixelStartOffset * sizeof(float )
- inline int64_t image_getStartOffset(const ImageRgbaU8& image) { return image_getPixelStartOffset(image) << 2; } // pixelStartOffset * sizeof(uint32_t)
- // Get a rectangle from the image's dimensions with the top left corner set to (0, 0).
- // Useful for clipping to an image's bounds or subdividing space for a graphical user interface.
- // Returns IRect(0, 0, 0, 0) for empty images.
- inline IRect image_getBound(const Image& image) { return IRect(0, 0, image.impl_dimensions.getWidth(), image.impl_dimensions.getHeight()); }
- // Returns false on null, true otherwise.
- inline bool image_exists(const Image& image) { return image.impl_buffer.isNotNull(); }
- // TODO: Rename into image_getUseCount for easier use.
- // Returns the number of handles to the image.
- // References to a handle doesn't count, only when a handle is stored by value.
- inline uintptr_t image_useCount(const Image& image) { return image.impl_buffer.getUseCount(); }
- // Returns the image's pack order index.
- inline PackOrderIndex image_getPackOrderIndex(const ImageRgbaU8& image) { return image.impl_dimensions.getPackOrderIndex(); }
- // Returns the image's pack order, containing bit masks and offsets needed to pack and unpack colors.
- inline PackOrder image_getPackOrder(const ImageRgbaU8& image) { return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()); };
- // Returns true iff the pixel at (x, y) is inside of image.
- inline bool image_isPixelInside(const Image& image, int32_t x, int32_t y) {
- return x >= 0 && x < image_getWidth(image) && y >= 0 && y < image_getHeight(image);
- }
- // Returns the size of one pixel in bytes dynamically by looking it up.
- inline int32_t image_getPixelSize(const Image& image) { return image.impl_dimensions.getPixelSize(); }
- // Returns the size of one pixel in bytes statically from the type.
- template <typename T> int32_t image_getPixelSize() { return T::impl_pixelSize; }
- // Channel packing
- // Extract one channel
- AlignedImageU8 image_get_red (const ImageRgbaU8& image);
- AlignedImageU8 image_get_green(const ImageRgbaU8& image);
- AlignedImageU8 image_get_blue (const ImageRgbaU8& image);
- AlignedImageU8 image_get_alpha(const ImageRgbaU8& image);
- // Pack one channel
- OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, int32_t blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, int32_t blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, const ImageU8& blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, int32_t blue, const ImageU8& alpha);
- // Pack two channels
- OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, int32_t blue, const ImageU8& alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, int32_t alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, int32_t blue, const ImageU8& alpha);
- OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, const ImageU8& blue, const ImageU8& alpha);
- // Pack three channels
- OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha);
- OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, const ImageU8& alpha);
- OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, const ImageU8& alpha);
- OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, int32_t alpha);
- // Pack four channels
- OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha);
- // Pack a color to draw with using the image's pack order, as it would be represented as a pixel in the buffer.
- inline uint32_t image_pack(const ImageRgbaU8& image, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
- return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).packRgba(red, green, blue, alpha);
- }
- // Saturate and pack a color for an image's pack order, as it would be represented as a pixel in the buffer.
- inline uint32_t image_saturateAndPack(const ImageRgbaU8& image, const ColorRgbaI32& color) {
- return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).saturateAndPackRgba(color);
- }
- // Truncate and pack a color for an image's pack order, as it would be represented as a pixel in the buffer.
- inline uint32_t image_truncateAndPack(const ImageRgbaU8& image, const ColorRgbaI32& color) {
- return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).truncateAndPackRgba(color);
- }
- // Unpack a color back into an expanded and ordered RGBA format.
- // packedColor is expressed in image's pack order.
- inline ColorRgbaI32 image_unpack(const ImageRgbaU8& image, uint32_t packedColor) {
- return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).unpackRgba(packedColor);
- }
- // Pixel access
- // Pre-condition:
- // The pixel at (x, y) must exist within the image, or else the program may crash.
- // image_isPixelInsize(image, x, y)
- // Post-condition:
- // Returns a reference to the pixel at (x, y) in image.
- inline uint8_t &image_accessPixel(const ImageU8& image, int32_t x, int32_t y) {
- uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
- return *(buffer_getSafeData<uint8_t>(image.impl_buffer, "ImageU8 pixel access buffer") + pixelOffset);
- }
- inline uint16_t &image_accessPixel(const ImageU16& image, int32_t x, int32_t y) {
- uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
- return *(buffer_getSafeData<uint16_t>(image.impl_buffer, "ImageU16 pixel access buffer") + pixelOffset);
- }
- inline float &image_accessPixel(const ImageF32& image, int32_t x, int32_t y) {
- uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
- return *(buffer_getSafeData<float>(image.impl_buffer, "ImageF32 pixel access buffer") + pixelOffset);
- }
- inline uint32_t &image_accessPixel(const ImageRgbaU8& image, int32_t x, int32_t y) {
- uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
- return *(buffer_getSafeData<uint32_t>(image.impl_buffer, "ImageRgbaU8 pixel access buffer") + pixelOffset);
- }
- // Write a pixel to an image.
- // Out of bound is ignored silently without writing.
- // Empty images will be ignored safely.
- // Packed is faster if the color can be packed in advance for multiple pixels or comes directly from an image of the same rgba order.
- // Saturated to 0..255
- inline void image_writePixel(const ImageU8& image, int32_t x, int32_t y, int32_t color) {
- if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = clamp(0, color, 255);
- }
- // Saturated to 0..65535
- inline void image_writePixel(const ImageU16& image, int32_t x, int32_t y, int32_t color) {
- if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = clamp(0, color, 65535);
- }
- // No saturation needed
- inline void image_writePixel(const ImageF32& image, int32_t x, int32_t y, float color) {
- if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = color;
- }
- // Saturated to 0..255 in all channels
- inline void image_writePixel(const ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& color) {
- if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = image_saturateAndPack(image, color);
- }
- // Pre-packed color using image_saturateAndPack to create the pixel in advance.
- inline void image_writePixel(const ImageRgbaU8& image, int32_t x, int32_t y, uint32_t packedColor) {
- if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = packedColor;
- }
- // Read a pixel from an image with a solid border outside.
- // Out of bound will return the border color.
- // The border color does not have to be constrained to the limits of pixel storage.
- // Empty images will return zero.
- inline int32_t image_readPixel_border(const ImageU8& image, int32_t x, int32_t y, int32_t border = 0) {
- if (!image_exists(image)) {
- return 0;
- } else if (image_isPixelInside(image, x, y)) {
- return image_accessPixel(image, x, y);
- } else {
- return border;
- }
- }
- // The packed version is identical to the unpacked version, so we make a wrapper for template functions to call.
- //inline int32_t image_readPixel_border_packed(const ImageU8& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
- inline int32_t image_readPixel_border(const ImageU16& image, int32_t x, int32_t y, int32_t border = 0) {
- if (!image_exists(image)) {
- return 0;
- } else if (image_isPixelInside(image, x, y)) {
- return image_accessPixel(image, x, y);
- } else {
- return border;
- }
- }
- //inline int32_t image_readPixel_border_packed(const ImageU16& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
- inline float image_readPixel_border(const ImageF32& image, int32_t x, int32_t y, float border = 0.0f) {
- if (!image_exists(image)) {
- return 0.0f;
- } else if (image_isPixelInside(image, x, y)) {
- return image_accessPixel(image, x, y);
- } else {
- return border;
- }
- }
- //inline float image_readPixel_border_packed(const ImageF32& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
- inline ColorRgbaI32 image_readPixel_border(const ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& border = ColorRgbaI32()) {
- if (!image_exists(image)) {
- return ColorRgbaI32(0, 0, 0, 0);
- } else if (image_isPixelInside(image, x, y)) {
- return image_unpack(image, image_accessPixel(image, x, y));
- } else {
- return border;
- }
- }
- // Read the color directly as it is packed in image's pack order.
- inline uint32_t image_readPixel_border_packed(const ImageRgbaU8& image, int32_t x, int32_t y, uint32_t border = 0) {
- if (!image_exists(image)) {
- return 0;
- } else if (image_isPixelInside(image, x, y)) {
- return image_accessPixel(image, x, y);
- } else {
- return border;
- }
- }
- // Read a pixel from an image stretched edges.
- // Out of bound will return the closest pixel.
- // Empty images will return zero.
- inline uint8_t image_readPixel_clamp(const ImageU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
- } else {
- return 0;
- }
- }
- //inline uint8_t image_readPixel_clamp_packed(const ImageU8& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
- inline uint16_t image_readPixel_clamp(const ImageU16& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
- } else {
- return 0;
- }
- }
- //inline uint16_t image_readPixel_clamp_packed(const ImageU16& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
- inline float image_readPixel_clamp(const ImageF32& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
- } else {
- return 0.0f;
- }
- }
- //inline float image_readPixel_clamp_packed(const ImageF32& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
- inline ColorRgbaI32 image_readPixel_clamp(const ImageRgbaU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_unpack(image, image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1)));
- } else {
- return ColorRgbaI32();
- }
- }
- // Read the color directly as it is packed in image's pack order.
- inline uint32_t image_readPixel_clamp_packed(const ImageRgbaU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
- } else {
- return 0;
- }
- }
- // Read a pixel from an image with tiling.
- // Out of bound will take the coordinates in modulo of the size.
- // Empty images will return zero.
- inline uint8_t image_readPixel_tile(const ImageU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
- } else {
- return 0;
- }
- }
- //inline uint8_t image_readPixel_tile_packed(const ImageU8& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
- inline uint16_t image_readPixel_tile(const ImageU16& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
- } else {
- return 0;
- }
- }
- //inline uint16_t image_readPixel_tile_packed(const ImageU16& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
- inline float image_readPixel_tile(const ImageF32& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
- } else {
- return 0.0f;
- }
- }
- //inline float image_readPixel_tile_packed(const ImageF32& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
- inline ColorRgbaI32 image_readPixel_tile(const ImageRgbaU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_unpack(image, image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image))));
- } else {
- return ColorRgbaI32();
- }
- }
- // Read the color directly as it is packed in image's pack order.
- inline uint32_t image_readPixel_tile_packed(const ImageRgbaU8& image, int32_t x, int32_t y) {
- if (image_exists(image)) {
- return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
- } else {
- return 0;
- }
- }
- // Loading
- // Load an image from a file by giving the filename including folder path and extension.
- // If mustExist is true, an exception will be raised on failure.
- // If mustExist is false, failure will return an empty handle.
- OrderedImageRgbaU8 image_load_RgbaU8(const String& filename, bool mustExist = true);
- // Load an image from a memory buffer, which can be loaded with file_loadBuffer to get the same result as loading directly from the file.
- // A convenient way of loading compressed images from larger files.
- // Failure will return an empty handle.
- OrderedImageRgbaU8 image_decode_RgbaU8(const Buffer& fileContent);
- // A faster and more flexible way to load compressed images from memory.
- // If you just want to point directly to a memory location to avoid allocating many small buffers, you can use a safe pointer and a size in bytes.
- // Failure will return an empty handle.
- OrderedImageRgbaU8 image_decode_RgbaU8(SafePointer<const uint8_t> data, int size);
- // Saving
- // Save the image to the path specified by filename and return true iff the operation was successful.
- // The file extension is case insensitive after the last dot in filename.
- // Accepted file extensions:
- // *.jpg or *.jpeg
- // *.png
- // *.tga or *.targa
- // *.bmp
- // If mustWork is true, an exception will be raised on failure.
- // If mustWork is false, failure will return false.
- // The optional quality setting goes from 1% to 100% and is at the maximum by default.
- bool image_save(const ImageRgbaU8 &image, const String& filename, bool mustWork = true, int quality = 100);
- // Save the image to a memory buffer.
- // Post-condition: Returns a buffer with the encoded image format as it would be saved to a file, or empty on failure.
- // No exceptions will be raised on failure, because an error message without a filename would not explain much.
- // The optional quality setting goes from 1% to 100% and is at the maximum by default.
- Buffer image_encode(const ImageRgbaU8 &image, ImageFileFormat format, int quality = 90);
- // Fill all pixels with a uniform color
- void image_fill(const ImageU8& image, int32_t color);
- void image_fill(const ImageU16& image, int32_t color);
- void image_fill(const ImageF32& image, float color);
- void image_fill(const ImageRgbaU8& image, const ColorRgbaI32& color);
- // Clone
- // Get a deep clone of an image's content while discarding any pack order, padding and texture pyramids.
- // If the input image had a different pack order, it will automatically be converted into RGBA to preserve the colors.
- AlignedImageU8 image_clone(const ImageU8& image);
- AlignedImageU16 image_clone(const ImageU16& image);
- AlignedImageF32 image_clone(const ImageF32& image);
- OrderedImageRgbaU8 image_clone(const ImageRgbaU8& image);
- // Returns a copy of the image without any padding, which means that alignment cannot be guaranteed.
- // The pack order is the same as the input, becuase it just copies the memory one row at a time to be fast.
- // Used when external image libraries don't allow giving stride as a separate argument.
- ImageRgbaU8 image_removePadding(const ImageRgbaU8& image);
- // Ascii images
- String image_toAscii(const ImageU8& image, const String &alphabet);
- String image_toAscii(const ImageU8& image);
- AlignedImageU8 image_fromAscii(const String &content);
- // Comparisons
- // Get the maximum pixelwise difference between two images of the same format, or the highest possible value on failure
- // Useful for regression tests
- uint8_t image_maxDifference(const ImageU8& imageA, const ImageU8& imageB);
- uint16_t image_maxDifference(const ImageU16& imageA, const ImageU16& imageB);
- float image_maxDifference(const ImageF32& imageA, const ImageF32& imageB);
- uint8_t image_maxDifference(const ImageRgbaU8& imageA, const ImageRgbaU8& imageB);
- // TODO: Create sub-image constructors in the image types.
- // Sub-images are read/write views to a smaller region of the same pixel data.
- // Get a sub-image sharing buffer and side-effects with the parent image
- // Returns the overlapping region if out of bound
- // Returns a null image if there are no overlapping pixels to return
- inline ImageU8 image_getSubImage(const ImageU8& image, const IRect& region) {
- static_assert(sizeof(ImageU8) == sizeof(Image), "ImageU8 must have the same size as Image, to prevent slicing in assignments!");
- return ImageU8(image, region);
- }
- inline ImageU16 image_getSubImage(const ImageU16& image, const IRect& region) {
- static_assert(sizeof(ImageU16) == sizeof(Image), "ImageU16 must have the same size as Image, to prevent slicing in assignments!");
- return ImageU16(image, region);
- }
- inline ImageF32 image_getSubImage(const ImageF32& image, const IRect& region) {
- static_assert(sizeof(ImageF32) == sizeof(Image), "ImageF32 must have the same size as Image, to prevent slicing in assignments!");
- return ImageF32(image, region);
- }
- inline ImageRgbaU8 image_getSubImage(const ImageRgbaU8& image, const IRect& region) {
- static_assert(sizeof(ImageRgbaU8) == sizeof(Image), "ImageRgbaU8 must have the same size as Image, to prevent slicing in assignments!");
- return ImageRgbaU8(image, region);
- }
- // Check dynamically if the image was created as a sub-image.
- // Returns true if the image is a sub-image, created using image_getSubImage.
- // Returns false if the image is not a sub-image, created using image_create or default constructed as an empty image.
- inline bool image_isSubImage(const Image& image) {
- return image.impl_dimensions.isSubImage();
- }
- // Bound-checked pointer access (relatively safe compared to a raw pointer)
- // Returns a bound-checked pointer to the first pixel.
- template <typename T = uint8_t>
- inline SafePointer<uint8_t> image_getSafePointer(const ImageU8& image) {
- return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageU8 pixels").increaseBytes(image_getStartOffset(image));
- }
- // Returns a bound-checked pointer to the first pixel at rowIndex.
- template <typename T = uint8_t>
- inline SafePointer<uint8_t> image_getSafePointer(const ImageU8& image, int32_t rowIndex) {
- return image_getSafePointer(image).increaseBytes(image_getStride(image) * rowIndex);
- }
- // Returns a bound-checked pointer to the first pixel.
- template <typename T = uint16_t>
- inline SafePointer<T> image_getSafePointer(const ImageU16& image) {
- return image.impl_buffer.getSafe<T>("Pointer to ImageU16 pixels").increaseBytes(image_getStartOffset(image));
- }
- // Returns a bound-checked pointer to the first pixel at rowIndex.
- template <typename T = uint16_t>
- inline SafePointer<T> image_getSafePointer(const ImageU16& image, int32_t rowIndex) {
- return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
- }
- // Returns a bound-checked pointer to the first pixel.
- template <typename T = float>
- inline SafePointer<T> image_getSafePointer(const ImageF32& image) {
- return image.impl_buffer.getSafe<T>("Pointer to ImageF32 pixels").increaseBytes(image_getStartOffset(image));
- }
- // Returns a bound-checked pointer to the first pixel at rowIndex.
- template <typename T = float>
- inline SafePointer<T> image_getSafePointer(const ImageF32& image, int32_t rowIndex) {
- return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
- }
- // Returns a bound-checked pointer to the first pixel.
- template <typename T = uint32_t>
- inline SafePointer<T> image_getSafePointer(const ImageRgbaU8& image) {
- return image.impl_buffer.getSafe<T>("Pointer to ImageRgbaU8 pixels").increaseBytes(image_getStartOffset(image));
- }
- // Returns a bound-checked pointer to the first pixel at rowIndex.
- template <typename T = uint32_t>
- inline SafePointer<T> image_getSafePointer(const ImageRgbaU8& image, int32_t rowIndex) {
- return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
- }
- // Returns a bound-checked pointer to the first channel in the first pixel.
- inline SafePointer<uint8_t> image_getSafePointer_channels(const ImageRgbaU8& image) {
- return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageRgbaU8 channels").increaseBytes(image_getStartOffset(image));
- }
- // Returns a bound-checked pointer to the first channel in the first pixel at rowIndex.
- inline SafePointer<uint8_t> image_getSafePointer_channels(const ImageRgbaU8& image, int32_t rowIndex) {
- return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageRgbaU8 channels").increaseBytes(image_getStartOffset(image)).increaseBytes(image_getStride(image) * rowIndex);
- }
- // The dangerous image API
- // Use of these methods can be spotted using a search for "_dangerous_" in your code
- // Replaces the destructor in image's buffer, which.
- // newDestructor should not free the given data, only invoke destruction of any external resources that may depend on it before the data is freed automatically.
- inline void image_dangerous_replaceDestructor(ImageU8& image, const HeapDestructor &newDestructor) {
- if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
- }
- inline void image_dangerous_replaceDestructor(ImageU16& image, const HeapDestructor &newDestructor) {
- if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
- }
- inline void image_dangerous_replaceDestructor(ImageF32& image, const HeapDestructor &newDestructor) {
- if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
- }
- inline void image_dangerous_replaceDestructor(ImageRgbaU8& image, const HeapDestructor &newDestructor) {
- if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
- }
- // Returns a pointer to the image's pixels
- // Warning! Reading elements larger than 8 bits will have lower and higher bytes stored based on local endianness
- // Warning! Using bytes outside of the [0 .. stride * height - 1] range may cause crashes and undefined behaviour
- // Warning! Using the pointer after the image's lifetime may cause crashes from trying to access freed memory
- inline uint8_t* image_dangerous_getData(const ImageU8& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
- inline uint8_t* image_dangerous_getData(const ImageU16& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
- inline uint8_t* image_dangerous_getData(const ImageF32& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
- inline uint8_t* image_dangerous_getData(const ImageRgbaU8& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
- }
- #endif
|