Browse Source

Moved image filters from the draw API and supported more image types.

David Piuva 5 years ago
parent
commit
47b77b90f9

+ 0 - 188
Source/DFPSR/api/drawAPI.cpp

@@ -24,11 +24,9 @@
 
 
 #define DFPSR_INTERNAL_ACCESS
 #define DFPSR_INTERNAL_ACCESS
 
 
-#include <cassert>
 #include "imageAPI.h"
 #include "imageAPI.h"
 #include "drawAPI.h"
 #include "drawAPI.h"
 #include "../image/draw.h"
 #include "../image/draw.h"
-#include "../image/ImageRgbaU8.h"
 #include "../image/PackOrder.h"
 #include "../image/PackOrder.h"
 #include "../image/internal/imageTemplate.h"
 #include "../image/internal/imageTemplate.h"
 #include "../image/internal/imageInternal.h"
 #include "../image/internal/imageInternal.h"
@@ -36,62 +34,6 @@
 using namespace dsr;
 using namespace dsr;
 
 
 
 
-// -------------------------------- Image generation and filtering --------------------------------
-
-
-static void mapRgbaU8(ImageRgbaU8Impl& target, const ImageGenRgbaU8& lambda, int startX, int startY) {
-	const int targetWidth = target.width;
-	const int targetHeight = target.height;
-	const int targetStride = target.stride;
-	// Use the output directly
-	SafePointer<Color4xU8> targetRow = imageInternal::getSafeData<Color4xU8>(target);
-	for (int y = startY; y < targetHeight + startY; y++) {
-		SafePointer<Color4xU8> targetPixel = targetRow;
-		for (int x = startX; x < targetWidth + startX; x++) {
-			*targetPixel = target.packRgba(lambda(x, y).saturate());
-			targetPixel += 1;
-		}
-		targetRow.increaseBytes(targetStride);
-	}
-}
-void dsr::filter_mapRgbaU8(ImageRgbaU8& target, const ImageGenRgbaU8& lambda, int startX, int startY) {
-	if (target) {
-		mapRgbaU8(*target, lambda, startX, startY);
-	}
-}
-ImageRgbaU8 dsr::filter_generateRgbaU8(int width, int height, const ImageGenRgbaU8& lambda, int startX, int startY) {
-	ImageRgbaU8 result = image_create_RgbaU8(width, height);
-	filter_mapRgbaU8(result, lambda, startX, startY);
-	return result;
-}
-
-static void mapF32(ImageF32Impl& target, const ImageGenF32& lambda, int startX, int startY) {
-	const int targetWidth = target.width;
-	const int targetHeight = target.height;
-	const int targetStride = target.stride;
-	// Use the output directly
-	SafePointer<float> targetRow = imageInternal::getSafeData<float>(target);
-	for (int y = startY; y < targetHeight + startY; y++) {
-		SafePointer<float> targetPixel = targetRow;
-		for (int x = startX; x < targetWidth + startX; x++) {
-			*targetPixel = lambda(x, y);
-			targetPixel += 1;
-		}
-		targetRow.increaseBytes(targetStride);
-	}
-}
-void dsr::filter_mapF32(ImageF32& target, const ImageGenF32& lambda, int startX, int startY) {
-	if (target) {
-		mapF32(*target, lambda, startX, startY);
-	}
-}
-ImageF32 dsr::filter_generateF32(int width, int height, const ImageGenF32& lambda, int startX, int startY) {
-	ImageF32 result = image_create_F32(width, height);
-	filter_mapF32(result, lambda, startX, startY);
-	return result;
-}
-
-
 // -------------------------------- Drawing shapes --------------------------------
 // -------------------------------- Drawing shapes --------------------------------
 
 
 
 
@@ -206,133 +148,3 @@ void dsr::draw_higher(ImageF32& targetHeight, const ImageF32& sourceHeight, Imag
 	}
 	}
 }
 }
 
 
-
-// -------------------------------- Resize --------------------------------
-
-
-static ImageRgbaU8Impl resizeToValue(const ImageRgbaU8Impl& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
-	ImageRgbaU8Impl resultImage = ImageRgbaU8Impl(newWidth, newHeight);
-	imageImpl_resizeToTarget(resultImage, image, interpolation == Sampler::Linear); // TODO: Pass Sampler to internal API if more modes are created
-	return resultImage;
-}
-
-static OrderedImageRgbaU8 resizeToRef(const ImageRgbaU8Impl& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
-	OrderedImageRgbaU8 resultImage = image_create_RgbaU8(newWidth, newHeight);
-	imageImpl_resizeToTarget(*resultImage, image, interpolation == Sampler::Linear); // TODO: Pass Sampler to internal API if more modes are created
-	return resultImage;
-}
-
-OrderedImageRgbaU8 dsr::filter_resize(const ImageRgbaU8& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
-	if (image) {
-		return resizeToRef(*image, interpolation, newWidth, newHeight);
-	} else {
-		return OrderedImageRgbaU8(); // Null gives null
-	}
-}
-
-void dsr::filter_blockMagnify(ImageRgbaU8& target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight) {
-	if (target && source) {
-		imageImpl_blockMagnify(*target, *source, pixelWidth, pixelHeight);
-	}
-}
-
-// Get RGBA sub-images without allocating heads on the heap
-static const ImageRgbaU8Impl getView(const ImageRgbaU8Impl& image, const IRect& region) {
-	assert(region.left() >= 0); assert(region.top() >= 0); assert(region.width() >= 1); assert(region.height() >= 1);
-	assert(region.right() <= image.width); assert(region.bottom() <= image.height);
-	intptr_t newOffset = image.startOffset + (region.left() * image.pixelSize) + (region.top() * image.stride);
-	return ImageRgbaU8Impl(region.width(), region.height(), image.stride, image.buffer, newOffset, image.packOrder);
-}
-
-OrderedImageRgbaU8 dsr::filter_resize3x3(const ImageRgbaU8& image, Sampler interpolation, int newWidth, int newHeight, int leftBorder, int topBorder, int rightBorder, int bottomBorder) {
-	if (image) {
-		// Get source dimensions
-		int sourceWidth = image->width;
-		int sourceHeight = image->height;
-
-		// Limit borders to a place near the center while leaving at least 2x2 pixels at the center for bilinear interpolation
-		int maxLeftBorder = std::min(sourceWidth, newWidth) / 2 - 1;
-		int maxTopBorder = std::min(sourceHeight, newHeight) / 2 - 1;
-		int maxRightBorder = maxLeftBorder;
-		int maxBottomBorder = maxTopBorder;
-		if (leftBorder > maxLeftBorder) leftBorder = maxLeftBorder;
-		if (topBorder > maxTopBorder) topBorder = maxTopBorder;
-		if (rightBorder > maxRightBorder) rightBorder = maxRightBorder;
-		if (bottomBorder > maxBottomBorder) bottomBorder = maxBottomBorder;
-		if (leftBorder < 0) leftBorder = 0;
-		if (topBorder < 0) topBorder = 0;
-		if (rightBorder < 0) rightBorder = 0;
-		if (bottomBorder < 0) bottomBorder = 0;
-
-		// Combine dimensions
-		// L_R T_B
-		int leftRightBorder = leftBorder + rightBorder;
-		int topBottomBorder = topBorder + bottomBorder;
-		// _C_
-		int targetCenterWidth = newWidth - leftRightBorder;
-		int targetCenterHeight = newHeight - topBottomBorder;
-		// LC_ RC_
-		int targetLeftAndCenter = newWidth - rightBorder;
-		int targetTopAndCenter = newHeight - bottomBorder;
-		// _C_
-		int sourceCenterWidth = sourceWidth - leftRightBorder;
-		int sourceCenterHeight = sourceHeight - topBottomBorder;
-		// LC_ RC_
-		int sourceLeftAndCenter = sourceWidth - rightBorder;
-		int sourceTopAndCenter = sourceHeight - bottomBorder;
-
-		// Allocate target image
-		OrderedImageRgbaU8 result = image_create_RgbaU8(newWidth, newHeight);
-		ImageRgbaU8Impl* target = result.get();
-
-		// Draw corners
-		if (leftBorder > 0 && topBorder > 0) {
-			imageImpl_drawCopy(*target, getView(*image, IRect(0, 0, leftBorder, topBorder)), 0, 0);
-		}
-		if (rightBorder > 0 && topBorder > 0) {
-			imageImpl_drawCopy(*target, getView(*image, IRect(sourceLeftAndCenter, 0, rightBorder, topBorder)), targetLeftAndCenter, 0);
-		}
-		if (leftBorder > 0 && bottomBorder > 0) {
-			imageImpl_drawCopy(*target, getView(*image, IRect(0, sourceTopAndCenter, leftBorder, bottomBorder)), 0, targetTopAndCenter);
-		}
-		if (rightBorder > 0 && bottomBorder > 0) {
-			imageImpl_drawCopy(*target, getView(*image, IRect(sourceLeftAndCenter, sourceTopAndCenter, rightBorder, bottomBorder)), targetLeftAndCenter, targetTopAndCenter);
-		}
-		// Resize and draw edges
-		if (targetCenterHeight > 0) {
-			if (leftBorder > 0) {
-				ImageRgbaU8Impl edgeSource = getView(*image, IRect(0, topBorder, leftBorder, sourceCenterHeight));
-				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, leftBorder, targetCenterHeight);
-				imageImpl_drawCopy(*target, stretchedEdge, 0, topBorder);
-			}
-			if (rightBorder > 0) {
-				ImageRgbaU8Impl edgeSource = getView(*image, IRect(sourceLeftAndCenter, topBorder, rightBorder, sourceCenterHeight));
-				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, rightBorder, targetCenterHeight);
-				imageImpl_drawCopy(*target, stretchedEdge, targetLeftAndCenter, topBorder);
-			}
-		}
-		if (targetCenterWidth > 0) {
-			if (topBorder > 0) {
-				ImageRgbaU8Impl edgeSource = getView(*image, IRect(leftBorder, 0, sourceCenterWidth, topBorder));
-				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, targetCenterWidth, topBorder);
-				imageImpl_drawCopy(*target, stretchedEdge, leftBorder, 0);
-			}
-			if (bottomBorder > 0) {
-				ImageRgbaU8Impl edgeSource = getView(*image, IRect(leftBorder, sourceTopAndCenter, sourceCenterWidth, bottomBorder));
-				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, targetCenterWidth, bottomBorder);
-				imageImpl_drawCopy(*target, stretchedEdge, leftBorder, targetTopAndCenter);
-			}
-		}
-		// Resize and draw center
-		if (targetCenterWidth > 0 && targetCenterHeight > 0) {
-			ImageRgbaU8Impl centerSource = getView(*image, IRect(leftBorder, topBorder, sourceCenterWidth, sourceCenterHeight));
-			ImageRgbaU8Impl stretchedCenter = resizeToValue(centerSource, interpolation, targetCenterWidth, targetCenterHeight);
-			imageImpl_drawCopy(*target, stretchedCenter, leftBorder, topBorder);
-		}
-		return result;
-	} else {
-		return OrderedImageRgbaU8(); // Null gives null
-	}
-
-}
-

+ 1 - 43
Source/DFPSR/api/drawAPI.h

@@ -1,7 +1,7 @@
 
 
 // zlib open source license
 // zlib open source license
 //
 //
-// Copyright (c) 2017 to 2019 David Forsgren Piuva
+// Copyright (c) 2017 to 2020 David Forsgren Piuva
 // 
 // 
 // This software is provided 'as-is', without any express or implied
 // This software is provided 'as-is', without any express or implied
 // warranty. In no event will the authors be held liable for any damages
 // warranty. In no event will the authors be held liable for any damages
@@ -26,7 +26,6 @@
 #define DFPSR_API_DRAW
 #define DFPSR_API_DRAW
 
 
 #include "types.h"
 #include "types.h"
-#include <functional>
 
 
 namespace dsr {
 namespace dsr {
 
 
@@ -122,47 +121,6 @@ namespace dsr {
 	// Draw a uniform color using a grayscale silhouette as the alpha channel
 	// Draw a uniform color using a grayscale silhouette as the alpha channel
 	void draw_silhouette(ImageRgbaU8& target, const ImageU8& silhouette, const ColorRgbaI32& color, int32_t left = 0, int32_t top = 0);
 	void draw_silhouette(ImageRgbaU8& target, const ImageU8& silhouette, const ColorRgbaI32& color, int32_t left = 0, int32_t top = 0);
 
 
-// TODO: Make a separate filter API
-
-// Image resizing
-	// The interpolate argument
-	//   Bi-linear interoplation is used when true
-	//   Nearest neighbor sampling is used when false
-	// Create a stretched version of the source image with the given dimensions and default RGBA pack order
-	OrderedImageRgbaU8 filter_resize(const ImageRgbaU8& image, Sampler interpolation, int32_t newWidth, int32_t newHeight);
-	// Resize with borders of pixels that aren't stretched
-	//   Using a larger border than half the size will be clamped, so that the center keeps at least 2x2 pixels
-	OrderedImageRgbaU8 filter_resize3x3(const ImageRgbaU8& image, Sampler interpolation, int newWidth, int newHeight, int leftBorder, int topBorder, int rightBorder, int bottomBorder);
-	// The source image is scaled by pixelWidth and pixelHeight from the upper left corner
-	// If source is too small, transparent black pixels (0, 0, 0, 0) fills the outside
-	// If source is too large, partial pixels will be cropped away completely and replaced by the black border
-	// Letting the images have the same pack order and be aligned to 16-bytes will increase speed
-	void filter_blockMagnify(ImageRgbaU8& target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight);
-
-// Image generation and filtering
-//   Create new images from Lambda expressions
-//   Useful for pre-generating images for reuse, reference implementations and fast prototyping
-	// Lambda expressions for generating integer images
-	using ImageGenRgbaU8 = std::function<ColorRgbaI32(int, int)>;
-	using ImageGenF32 = std::function<float(int, int)>;
-	// In-place image generation to an existing image
-	//   The pixel at the upper left corner gets (startX, startY) as x and y arguments to the function
-	void filter_mapRgbaU8(ImageRgbaU8& target, const ImageGenRgbaU8& lambda, int startX = 0, int startY = 0);
-	void filter_mapF32(ImageF32& target, const ImageGenF32& lambda, int startX = 0, int startY = 0);
-	// A simpler image generation that constructs the image as a result
-	// Example:
-	//     int width = 64;
-	//     int height = 64;
-	//     ImageRgbaU8 fadeImage = filter_generateRgbaU8(width, height, [](int x, int y)->ColorRgbaI32 {
-	//         return ColorRgbaI32(x * 4, y * 4, 0, 255);
-	//     });
-	//     ImageRgbaU8 brighterImage = filter_generateRgbaU8(width, height, [fadeImage](int x, int y)->ColorRgbaI32 {
-	//	       ColorRgbaI32 source = image_readPixel_clamp(fadeImage, x, y);
-	//	       return ColorRgbaI32(source.red * 2, source.green * 2, source.blue * 2, source.alpha);
-	//     });
-	ImageRgbaU8 filter_generateRgbaU8(int width, int height, const ImageGenRgbaU8& lambda, int startX = 0, int startY = 0);
-	ImageF32 filter_generateF32(int width, int height, const ImageGenF32& lambda, int startX = 0, int startY = 0);
-
 }
 }
 
 
 #endif
 #endif

+ 259 - 0
Source/DFPSR/api/filterAPI.cpp

@@ -0,0 +1,259 @@
+
+// zlib open source license
+//
+// Copyright (c) 2017 to 2019 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.
+
+#define DFPSR_INTERNAL_ACCESS
+
+#include <cassert>
+#include "imageAPI.h"
+#include "filterAPI.h"
+#include "../image/draw.h"
+#include "../image/PackOrder.h"
+#include "../image/internal/imageTemplate.h"
+#include "../image/internal/imageInternal.h"
+
+using namespace dsr;
+
+
+// -------------------------------- Image generation and filtering --------------------------------
+
+
+static void mapRgbaU8(ImageRgbaU8Impl& target, const ImageGenRgbaU8& lambda, int startX, int startY) {
+	const int targetWidth = target.width;
+	const int targetHeight = target.height;
+	const int targetStride = target.stride;
+	SafePointer<Color4xU8> targetRow = imageInternal::getSafeData<Color4xU8>(target);
+	for (int y = startY; y < targetHeight + startY; y++) {
+		SafePointer<Color4xU8> targetPixel = targetRow;
+		for (int x = startX; x < targetWidth + startX; x++) {
+			*targetPixel = target.packRgba(lambda(x, y).saturate());
+			targetPixel += 1;
+		}
+		targetRow.increaseBytes(targetStride);
+	}
+}
+void dsr::filter_mapRgbaU8(ImageRgbaU8 target, const ImageGenRgbaU8& lambda, int startX, int startY) {
+	if (target.get() != nullptr) {
+		mapRgbaU8(*target, lambda, startX, startY);
+	}
+}
+OrderedImageRgbaU8 dsr::filter_generateRgbaU8(int width, int height, const ImageGenRgbaU8& lambda, int startX, int startY) {
+	OrderedImageRgbaU8 result = image_create_RgbaU8(width, height);
+	filter_mapRgbaU8(result, lambda, startX, startY);
+	return result;
+}
+
+template <typename IMAGE_TYPE, typename PIXEL_TYPE, int MIN_VALUE, int MAX_VALUE>
+static void mapMonochrome(IMAGE_TYPE& target, const ImageGenI32& lambda, int startX, int startY) {
+	const int targetWidth = target.width;
+	const int targetHeight = target.height;
+	const int targetStride = target.stride;
+	SafePointer<PIXEL_TYPE> targetRow = imageInternal::getSafeData<PIXEL_TYPE>(target);
+	for (int y = startY; y < targetHeight + startY; y++) {
+		SafePointer<PIXEL_TYPE> targetPixel = targetRow;
+		for (int x = startX; x < targetWidth + startX; x++) {
+			int output = lambda(x, y);
+			if (output < MIN_VALUE) { output = MIN_VALUE; }
+			if (output > MAX_VALUE) { output = MAX_VALUE; }
+			*targetPixel = output;
+			targetPixel += 1;
+		}
+		targetRow.increaseBytes(targetStride);
+	}
+}
+void dsr::filter_mapU8(ImageU8 target, const ImageGenI32& lambda, int startX, int startY) {
+	if (target.get() != nullptr) {
+		mapMonochrome<ImageU8Impl, uint8_t, 0, 255>(*target, lambda, startX, startY);
+	}
+}
+AlignedImageU8 dsr::filter_generateU8(int width, int height, const ImageGenI32& lambda, int startX, int startY) {
+	AlignedImageU8 result = image_create_U8(width, height);
+	filter_mapU8(result, lambda, startX, startY);
+	return result;
+}
+void dsr::filter_mapU16(ImageU16 target, const ImageGenI32& lambda, int startX, int startY) {
+	if (target.get() != nullptr) {
+		mapMonochrome<ImageU16Impl, uint16_t, 0, 65535>(*target, lambda, startX, startY);
+	}
+}
+AlignedImageU16 dsr::filter_generateU16(int width, int height, const ImageGenI32& lambda, int startX, int startY) {
+	AlignedImageU16 result = image_create_U16(width, height);
+	filter_mapU16(result, lambda, startX, startY);
+	return result;
+}
+
+static void mapF32(ImageF32Impl& target, const ImageGenF32& lambda, int startX, int startY) {
+	const int targetWidth = target.width;
+	const int targetHeight = target.height;
+	const int targetStride = target.stride;
+	SafePointer<float> targetRow = imageInternal::getSafeData<float>(target);
+	for (int y = startY; y < targetHeight + startY; y++) {
+		SafePointer<float> targetPixel = targetRow;
+		for (int x = startX; x < targetWidth + startX; x++) {
+			*targetPixel = lambda(x, y);
+			targetPixel += 1;
+		}
+		targetRow.increaseBytes(targetStride);
+	}
+}
+void dsr::filter_mapF32(ImageF32 target, const ImageGenF32& lambda, int startX, int startY) {
+	if (target.get() != nullptr) {
+		mapF32(*target, lambda, startX, startY);
+	}
+}
+AlignedImageF32 dsr::filter_generateF32(int width, int height, const ImageGenF32& lambda, int startX, int startY) {
+	AlignedImageF32 result = image_create_F32(width, height);
+	filter_mapF32(result, lambda, startX, startY);
+	return result;
+}
+
+
+// -------------------------------- Resize --------------------------------
+
+
+static ImageRgbaU8Impl resizeToValue(const ImageRgbaU8Impl& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
+	ImageRgbaU8Impl resultImage = ImageRgbaU8Impl(newWidth, newHeight);
+	imageImpl_resizeToTarget(resultImage, image, interpolation == Sampler::Linear); // TODO: Pass Sampler to internal API if more modes are created
+	return resultImage;
+}
+
+static OrderedImageRgbaU8 resizeToRef(const ImageRgbaU8Impl& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
+	OrderedImageRgbaU8 resultImage = image_create_RgbaU8(newWidth, newHeight);
+	imageImpl_resizeToTarget(*resultImage, image, interpolation == Sampler::Linear); // TODO: Pass Sampler to internal API if more modes are created
+	return resultImage;
+}
+
+OrderedImageRgbaU8 dsr::filter_resize(const ImageRgbaU8& image, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
+	if (image) {
+		return resizeToRef(*image, interpolation, newWidth, newHeight);
+	} else {
+		return OrderedImageRgbaU8(); // Null gives null
+	}
+}
+
+void dsr::filter_blockMagnify(ImageRgbaU8& target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight) {
+	if (target && source) {
+		imageImpl_blockMagnify(*target, *source, pixelWidth, pixelHeight);
+	}
+}
+
+// Get RGBA sub-images without allocating heads on the heap
+static const ImageRgbaU8Impl getView(const ImageRgbaU8Impl& image, const IRect& region) {
+	assert(region.left() >= 0); assert(region.top() >= 0); assert(region.width() >= 1); assert(region.height() >= 1);
+	assert(region.right() <= image.width); assert(region.bottom() <= image.height);
+	intptr_t newOffset = image.startOffset + (region.left() * image.pixelSize) + (region.top() * image.stride);
+	return ImageRgbaU8Impl(region.width(), region.height(), image.stride, image.buffer, newOffset, image.packOrder);
+}
+
+OrderedImageRgbaU8 dsr::filter_resize3x3(const ImageRgbaU8& image, Sampler interpolation, int newWidth, int newHeight, int leftBorder, int topBorder, int rightBorder, int bottomBorder) {
+	if (image) {
+		// Get source dimensions
+		int sourceWidth = image->width;
+		int sourceHeight = image->height;
+
+		// Limit borders to a place near the center while leaving at least 2x2 pixels at the center for bilinear interpolation
+		int maxLeftBorder = std::min(sourceWidth, newWidth) / 2 - 1;
+		int maxTopBorder = std::min(sourceHeight, newHeight) / 2 - 1;
+		int maxRightBorder = maxLeftBorder;
+		int maxBottomBorder = maxTopBorder;
+		if (leftBorder > maxLeftBorder) leftBorder = maxLeftBorder;
+		if (topBorder > maxTopBorder) topBorder = maxTopBorder;
+		if (rightBorder > maxRightBorder) rightBorder = maxRightBorder;
+		if (bottomBorder > maxBottomBorder) bottomBorder = maxBottomBorder;
+		if (leftBorder < 0) leftBorder = 0;
+		if (topBorder < 0) topBorder = 0;
+		if (rightBorder < 0) rightBorder = 0;
+		if (bottomBorder < 0) bottomBorder = 0;
+
+		// Combine dimensions
+		// L_R T_B
+		int leftRightBorder = leftBorder + rightBorder;
+		int topBottomBorder = topBorder + bottomBorder;
+		// _C_
+		int targetCenterWidth = newWidth - leftRightBorder;
+		int targetCenterHeight = newHeight - topBottomBorder;
+		// LC_ RC_
+		int targetLeftAndCenter = newWidth - rightBorder;
+		int targetTopAndCenter = newHeight - bottomBorder;
+		// _C_
+		int sourceCenterWidth = sourceWidth - leftRightBorder;
+		int sourceCenterHeight = sourceHeight - topBottomBorder;
+		// LC_ RC_
+		int sourceLeftAndCenter = sourceWidth - rightBorder;
+		int sourceTopAndCenter = sourceHeight - bottomBorder;
+
+		// Allocate target image
+		OrderedImageRgbaU8 result = image_create_RgbaU8(newWidth, newHeight);
+		ImageRgbaU8Impl* target = result.get();
+
+		// Draw corners
+		if (leftBorder > 0 && topBorder > 0) {
+			imageImpl_drawCopy(*target, getView(*image, IRect(0, 0, leftBorder, topBorder)), 0, 0);
+		}
+		if (rightBorder > 0 && topBorder > 0) {
+			imageImpl_drawCopy(*target, getView(*image, IRect(sourceLeftAndCenter, 0, rightBorder, topBorder)), targetLeftAndCenter, 0);
+		}
+		if (leftBorder > 0 && bottomBorder > 0) {
+			imageImpl_drawCopy(*target, getView(*image, IRect(0, sourceTopAndCenter, leftBorder, bottomBorder)), 0, targetTopAndCenter);
+		}
+		if (rightBorder > 0 && bottomBorder > 0) {
+			imageImpl_drawCopy(*target, getView(*image, IRect(sourceLeftAndCenter, sourceTopAndCenter, rightBorder, bottomBorder)), targetLeftAndCenter, targetTopAndCenter);
+		}
+		// Resize and draw edges
+		if (targetCenterHeight > 0) {
+			if (leftBorder > 0) {
+				ImageRgbaU8Impl edgeSource = getView(*image, IRect(0, topBorder, leftBorder, sourceCenterHeight));
+				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, leftBorder, targetCenterHeight);
+				imageImpl_drawCopy(*target, stretchedEdge, 0, topBorder);
+			}
+			if (rightBorder > 0) {
+				ImageRgbaU8Impl edgeSource = getView(*image, IRect(sourceLeftAndCenter, topBorder, rightBorder, sourceCenterHeight));
+				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, rightBorder, targetCenterHeight);
+				imageImpl_drawCopy(*target, stretchedEdge, targetLeftAndCenter, topBorder);
+			}
+		}
+		if (targetCenterWidth > 0) {
+			if (topBorder > 0) {
+				ImageRgbaU8Impl edgeSource = getView(*image, IRect(leftBorder, 0, sourceCenterWidth, topBorder));
+				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, targetCenterWidth, topBorder);
+				imageImpl_drawCopy(*target, stretchedEdge, leftBorder, 0);
+			}
+			if (bottomBorder > 0) {
+				ImageRgbaU8Impl edgeSource = getView(*image, IRect(leftBorder, sourceTopAndCenter, sourceCenterWidth, bottomBorder));
+				ImageRgbaU8Impl stretchedEdge = resizeToValue(edgeSource, interpolation, targetCenterWidth, bottomBorder);
+				imageImpl_drawCopy(*target, stretchedEdge, leftBorder, targetTopAndCenter);
+			}
+		}
+		// Resize and draw center
+		if (targetCenterWidth > 0 && targetCenterHeight > 0) {
+			ImageRgbaU8Impl centerSource = getView(*image, IRect(leftBorder, topBorder, sourceCenterWidth, sourceCenterHeight));
+			ImageRgbaU8Impl stretchedCenter = resizeToValue(centerSource, interpolation, targetCenterWidth, targetCenterHeight);
+			imageImpl_drawCopy(*target, stretchedCenter, leftBorder, topBorder);
+		}
+		return result;
+	} else {
+		return OrderedImageRgbaU8(); // Null gives null
+	}
+
+}
+

+ 80 - 0
Source/DFPSR/api/filterAPI.h

@@ -0,0 +1,80 @@
+
+// zlib open source license
+//
+// Copyright (c) 2017 to 2020 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.
+
+#ifndef DFPSR_API_FILTER
+#define DFPSR_API_FILTER
+
+#include "types.h"
+#include <functional>
+
+namespace dsr {
+
+// Image resizing
+	// The interpolate argument
+	//   Bi-linear interoplation is used when true
+	//   Nearest neighbor sampling is used when false
+	// Create a stretched version of the source image with the given dimensions and default RGBA pack order
+	OrderedImageRgbaU8 filter_resize(const ImageRgbaU8& image, Sampler interpolation, int32_t newWidth, int32_t newHeight);
+	// Resize with borders of pixels that aren't stretched
+	//   Using a larger border than half the size will be clamped, so that the center keeps at least 2x2 pixels
+	OrderedImageRgbaU8 filter_resize3x3(const ImageRgbaU8& image, Sampler interpolation, int newWidth, int newHeight, int leftBorder, int topBorder, int rightBorder, int bottomBorder);
+	// The source image is scaled by pixelWidth and pixelHeight from the upper left corner
+	// If source is too small, transparent black pixels (0, 0, 0, 0) fills the outside
+	// If source is too large, partial pixels will be cropped away completely and replaced by the black border
+	// Letting the images have the same pack order and be aligned to 16-bytes will increase speed
+	void filter_blockMagnify(ImageRgbaU8& target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight);
+
+// Image generation and filtering
+//   Create new images from Lambda expressions
+//   Useful for pre-generating images for reference implementations, fast prototyping and texture generation
+	// Lambda expressions for generating integer images
+	using ImageGenRgbaU8 = std::function<ColorRgbaI32(int, int)>;
+	using ImageGenI32 = std::function<int32_t(int, int)>; // Used for U8 and U16 images using different saturations
+	using ImageGenF32 = std::function<float(int, int)>;
+	// In-place image generation to an existing image
+	//   The pixel at the upper left corner gets (startX, startY) as x and y arguments to the function
+	void filter_mapRgbaU8(ImageRgbaU8 target, const ImageGenRgbaU8& lambda, int startX = 0, int startY = 0);
+	void filter_mapU8(ImageU8 target, const ImageGenI32& lambda, int startX = 0, int startY = 0);
+	void filter_mapU16(ImageU16 target, const ImageGenI32& lambda, int startX = 0, int startY = 0);
+	void filter_mapF32(ImageF32 target, const ImageGenF32& lambda, int startX = 0, int startY = 0);
+	// A simpler image generation that constructs the image as a result
+	// Example:
+	//     int width = 64;
+	//     int height = 64;
+	//     ImageRgbaU8 fadeImage = filter_generateRgbaU8(width, height, [](int x, int y)->ColorRgbaI32 {
+	//         return ColorRgbaI32(x * 4, y * 4, 0, 255);
+	//     });
+	//     ImageRgbaU8 brighterImage = filter_generateRgbaU8(width, height, [fadeImage](int x, int y)->ColorRgbaI32 {
+	//	       ColorRgbaI32 source = image_readPixel_clamp(fadeImage, x, y);
+	//	       return ColorRgbaI32(source.red * 2, source.green * 2, source.blue * 2, source.alpha);
+	//     });
+	OrderedImageRgbaU8 filter_generateRgbaU8(int width, int height, const ImageGenRgbaU8& lambda, int startX = 0, int startY = 0);
+	AlignedImageU8 filter_generateU8(int width, int height, const ImageGenI32& lambda, int startX = 0, int startY = 0);
+	AlignedImageU16 filter_generateU16(int width, int height, const ImageGenI32& lambda, int startX = 0, int startY = 0);
+	AlignedImageF32 filter_generateF32(int width, int height, const ImageGenF32& lambda, int startX = 0, int startY = 0);
+
+}
+
+#endif
+

+ 1 - 1
Source/DFPSR/gui/DsrWindow.cpp

@@ -33,7 +33,7 @@
 #include "../math/scalar.h"
 #include "../math/scalar.h"
 #include "../math/IVector.h"
 #include "../math/IVector.h"
 #include "../api/imageAPI.h"
 #include "../api/imageAPI.h"
-#include "../api/drawAPI.h"
+#include "../api/filterAPI.h"
 
 
 using namespace dsr;
 using namespace dsr;
 
 

+ 1 - 0
Source/DFPSR/includeFramework.h

@@ -14,6 +14,7 @@
 	// 2D API
 	// 2D API
 	#include "api/imageAPI.h" // Creating images and modifying pixels
 	#include "api/imageAPI.h" // Creating images and modifying pixels
 	#include "api/drawAPI.h" // Efficient drawing on images
 	#include "api/drawAPI.h" // Efficient drawing on images
+	#include "api/filterAPI.h" // Efficient image generation, resizing and filtering
 	// 3D API
 	// 3D API
 	#include "api/modelAPI.h" // Polygon models for 3D rendering
 	#include "api/modelAPI.h" // Polygon models for 3D rendering
 	// GUI API
 	// GUI API