Ver código fonte

Made a basic image theme for the Wizard application.

David Piuva 3 anos atrás
pai
commit
9111a57006

+ 14 - 8
Source/DFPSR/api/filterAPI.cpp

@@ -131,21 +131,27 @@ AlignedImageF32 dsr::filter_generateF32(int width, int height, const ImageGenF32
 // -------------------------------- Resize --------------------------------
 // -------------------------------- Resize --------------------------------
 
 
 
 
-static OrderedImageRgbaU8 resizeToRef(const ImageRgbaU8Impl& source, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
-	OrderedImageRgbaU8 resultImage = image_create_RgbaU8(newWidth, newHeight);
-	imageImpl_resizeToTarget(*resultImage, source, interpolation == Sampler::Linear);
-	return resultImage;
+OrderedImageRgbaU8 dsr::filter_resize(const ImageRgbaU8 &source, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
+	if (source.get() != nullptr) {
+		OrderedImageRgbaU8 resultImage = image_create_RgbaU8(newWidth, newHeight);
+		imageImpl_resizeToTarget(*resultImage, *source, interpolation == Sampler::Linear);
+		return resultImage;
+	} else {
+		return OrderedImageRgbaU8(); // Null gives null
+	}
 }
 }
 
 
-OrderedImageRgbaU8 dsr::filter_resize(ImageRgbaU8 source, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
+AlignedImageU8 dsr::filter_resize(const ImageU8 &source, Sampler interpolation, int32_t newWidth, int32_t newHeight) {
 	if (source.get() != nullptr) {
 	if (source.get() != nullptr) {
-		return resizeToRef(*source, interpolation, newWidth, newHeight);
+		AlignedImageU8 resultImage = image_create_U8(newWidth, newHeight);
+		imageImpl_resizeToTarget(*resultImage, *source, interpolation == Sampler::Linear);
+		return resultImage;
 	} else {
 	} else {
-		return OrderedImageRgbaU8(); // Null gives null
+		return AlignedImageU8(); // Null gives null
 	}
 	}
 }
 }
 
 
-void dsr::filter_blockMagnify(ImageRgbaU8 target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight) {
+void dsr::filter_blockMagnify(ImageRgbaU8 &target, const ImageRgbaU8& source, int pixelWidth, int pixelHeight) {
 	if (target.get() != nullptr && source.get() != nullptr) {
 	if (target.get() != nullptr && source.get() != nullptr) {
 		imageImpl_blockMagnify(*target, *source, pixelWidth, pixelHeight);
 		imageImpl_blockMagnify(*target, *source, pixelWidth, pixelHeight);
 	}
 	}

+ 16 - 18
Source/DFPSR/api/filterAPI.h

@@ -31,32 +31,30 @@
 namespace dsr {
 namespace dsr {
 
 
 // Image resizing
 // 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(ImageRgbaU8 source, Sampler interpolation, int32_t newWidth, int32_t newHeight);
-	// The nearest-neighbor resize used for up-scaling the window canvas
-	//   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);
+	// Create a stretched version of the source image with the given dimensions and default RGBA pack order.
+	OrderedImageRgbaU8 filter_resize(const ImageRgbaU8 &source, Sampler interpolation, int32_t newWidth, int32_t newHeight);
+	AlignedImageU8     filter_resize(const ImageU8 &source,     Sampler interpolation, int32_t newWidth, int32_t newHeight);
+	// The nearest-neighbor resize used for up-scaling the window canvas.
+	//   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
 // 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
+//   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 ImageGenRgbaU8 = std::function<ColorRgbaI32(int, int)>;
-	using ImageGenI32 = std::function<int32_t(int, int)>; // Used for U8 and U16 images using different saturations
+	using ImageGenI32 = std::function<int32_t(int, int)>; // Used for U8 and U16 images using different saturations.
 	using ImageGenF32 = std::function<float(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
+	// 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_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_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_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);
 	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
+	// A simpler image generation that constructs the image as a result.
 	// Example:
 	// Example:
 	//     int width = 64;
 	//     int width = 64;
 	//     int height = 64;
 	//     int height = 64;

+ 106 - 0
Source/DFPSR/api/mediaMachineAPI.cpp

@@ -510,7 +510,58 @@ static const InsSig mediaMachineInstructions[] = {
 		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
 		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
 		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
 		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
 	),
 	),
+	InsSig::create(U"RESIZE_BILINEAR", 1,
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = filter_resize(IMAGE_U8_REF(3), Sampler::Linear, INT_VALUE(1), INT_VALUE(2));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"NewWidth", true, DataType_FixedPoint),
+		ArgSig(U"NewHeight", true, DataType_FixedPoint),
+		ArgSig(U"Source", true, DataType_ImageU8)
+	),
+	InsSig::create(U"RESIZE_BILINEAR", 1,
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_RGBAU8_REF(0) = filter_resize(IMAGE_RGBAU8_REF(3), Sampler::Linear, INT_VALUE(1), INT_VALUE(2));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageRgbaU8),
+		ArgSig(U"NewWidth", true, DataType_FixedPoint),
+		ArgSig(U"NewHeight", true, DataType_FixedPoint),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8)
+	),
+	InsSig::create(U"RESIZE_BILINEAR", 1,
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = filter_resize(image_getSubImage(IMAGE_U8_REF(3), IRect(INT_VALUE(4), INT_VALUE(5), INT_VALUE(6), INT_VALUE(7))), Sampler::Linear, INT_VALUE(1), INT_VALUE(2));
+			NEXT_INSTRUCTION
+		},
+		// TODO: Prevent aliasing
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"NewWidth", true, DataType_FixedPoint),
+		ArgSig(U"NewHeight", true, DataType_FixedPoint),
+		ArgSig(U"Source", true, DataType_ImageU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
+	InsSig::create(U"RESIZE_BILINEAR", 1,
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_RGBAU8_REF(0) = filter_resize(image_getSubImage(IMAGE_RGBAU8_REF(3), IRect(INT_VALUE(4), INT_VALUE(5), INT_VALUE(6), INT_VALUE(7))), Sampler::Linear, INT_VALUE(1), INT_VALUE(2));
+			NEXT_INSTRUCTION
+		},
+		// TODO: Prevent aliasing
+		ArgSig(U"Target", false, DataType_ImageRgbaU8),
+		ArgSig(U"NewWidth", true, DataType_FixedPoint),
+		ArgSig(U"NewHeight", true, DataType_FixedPoint),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
 	InsSig::create(U"GET_RED", 1,
 	InsSig::create(U"GET_RED", 1,
+		// Getting red channel of an image.
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 			IMAGE_U8_REF(0) = image_get_red(IMAGE_RGBAU8_REF(1));
 			IMAGE_U8_REF(0) = image_get_red(IMAGE_RGBAU8_REF(1));
 			NEXT_INSTRUCTION
 			NEXT_INSTRUCTION
@@ -518,7 +569,21 @@ static const InsSig mediaMachineInstructions[] = {
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 	),
 	),
+	InsSig::create(U"GET_RED", 1,
+		// Getting red channel of a source region in the image.
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = image_get_red(image_getSubImage(IMAGE_RGBAU8_REF(1), IRect(INT_VALUE(2), INT_VALUE(3), INT_VALUE(4), INT_VALUE(5))));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
 	InsSig::create(U"GET_GREEN", 1,
 	InsSig::create(U"GET_GREEN", 1,
+		// Getting green channel of an image.
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 			IMAGE_U8_REF(0) = image_get_green(IMAGE_RGBAU8_REF(1));
 			IMAGE_U8_REF(0) = image_get_green(IMAGE_RGBAU8_REF(1));
 			NEXT_INSTRUCTION
 			NEXT_INSTRUCTION
@@ -526,7 +591,21 @@ static const InsSig mediaMachineInstructions[] = {
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 	),
 	),
+	InsSig::create(U"GET_GREEN", 1,
+		// Getting green channel of a source region in the image.
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = image_get_green(image_getSubImage(IMAGE_RGBAU8_REF(1), IRect(INT_VALUE(2), INT_VALUE(3), INT_VALUE(4), INT_VALUE(5))));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
 	InsSig::create(U"GET_BLUE", 1,
 	InsSig::create(U"GET_BLUE", 1,
+		// Getting blue channel of an image.
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 			IMAGE_U8_REF(0) = image_get_blue(IMAGE_RGBAU8_REF(1));
 			IMAGE_U8_REF(0) = image_get_blue(IMAGE_RGBAU8_REF(1));
 			NEXT_INSTRUCTION
 			NEXT_INSTRUCTION
@@ -534,7 +613,21 @@ static const InsSig mediaMachineInstructions[] = {
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 	),
 	),
+	InsSig::create(U"GET_BLUE", 1,
+		// Getting blue channel of a source region in the image.
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = image_get_blue(image_getSubImage(IMAGE_RGBAU8_REF(1), IRect(INT_VALUE(2), INT_VALUE(3), INT_VALUE(4), INT_VALUE(5))));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
 	InsSig::create(U"GET_ALPHA", 1,
 	InsSig::create(U"GET_ALPHA", 1,
+		// Getting alpha channel of an image.
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 			IMAGE_U8_REF(0) = image_get_alpha(IMAGE_RGBAU8_REF(1));
 			IMAGE_U8_REF(0) = image_get_alpha(IMAGE_RGBAU8_REF(1));
 			NEXT_INSTRUCTION
 			NEXT_INSTRUCTION
@@ -542,6 +635,19 @@ static const InsSig mediaMachineInstructions[] = {
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Target", false, DataType_ImageU8),
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 		ArgSig(U"Source", true, DataType_ImageRgbaU8)
 	),
 	),
+	InsSig::create(U"GET_ALPHA", 1,
+		// Getting alpha channel of a source region in the image.
+		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
+			IMAGE_U8_REF(0) = image_get_alpha(image_getSubImage(IMAGE_RGBAU8_REF(1), IRect(INT_VALUE(2), INT_VALUE(3), INT_VALUE(4), INT_VALUE(5))));
+			NEXT_INSTRUCTION
+		},
+		ArgSig(U"Target", false, DataType_ImageU8),
+		ArgSig(U"Source", true, DataType_ImageRgbaU8),
+		ArgSig(U"SourceLeft", true, DataType_FixedPoint),
+		ArgSig(U"SourceTop", true, DataType_FixedPoint),
+		ArgSig(U"SourceWidth", true, DataType_FixedPoint),
+		ArgSig(U"SourceHeight", true, DataType_FixedPoint)
+	),
 	InsSig::create(U"PACK_RGBA", 1,
 	InsSig::create(U"PACK_RGBA", 1,
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 		[](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
 			IMAGE_RGBAU8_REF(0) = image_pack(IMAGE_U8_REF(1), INT_VALUE(2), INT_VALUE(3), INT_VALUE(4));
 			IMAGE_RGBAU8_REF(0) = image_pack(IMAGE_U8_REF(1), INT_VALUE(2), INT_VALUE(3), INT_VALUE(4));

+ 33 - 72
Source/DFPSR/gui/VisualTheme.cpp

@@ -27,6 +27,7 @@
 #include "../api/drawAPI.h"
 #include "../api/drawAPI.h"
 #include "../api/mediaMachineAPI.h"
 #include "../api/mediaMachineAPI.h"
 #include "../api/configAPI.h"
 #include "../api/configAPI.h"
+#include "../persistent/atomic/PersistentImage.h"
 
 
 namespace dsr {
 namespace dsr {
 
 
@@ -36,27 +37,27 @@ static const ReadableString defaultMediaMachineCode =
 UR"QUOTE(
 UR"QUOTE(
 # Helper methods
 # Helper methods
 BEGIN: generate_rounded_rectangle
 BEGIN: generate_rounded_rectangle
-	# Dimensions of the result image
+	# Dimensions of the result image.
 	INPUT: FixedPoint, width
 	INPUT: FixedPoint, width
 	INPUT: FixedPoint, height
 	INPUT: FixedPoint, height
-	# The subtracted offset from the radius to create a border on certain channels
+	# The subtracted offset from the radius to create a border on certain channels.
 	INPUT: FixedPoint, border
 	INPUT: FixedPoint, border
-	# The whole pixel radius from center points to the end of the image
+	# The whole pixel radius from center points to the end of the image.
 	INPUT: FixedPoint, rounding
 	INPUT: FixedPoint, rounding
-	# Create the result image
+	# Create the result image.
 	OUTPUT: ImageU8, resultImage
 	OUTPUT: ImageU8, resultImage
 	CREATE: resultImage, width, height
 	CREATE: resultImage, width, height
-	# Limit outer radius to half of the image's minimum dimension
+	# Limit outer radius to half of the image's minimum dimension.
 	MIN: radius<FixedPoint>, width, height
 	MIN: radius<FixedPoint>, width, height
 	MUL: radius, radius, 0.5
 	MUL: radius, radius, 0.5
 	MIN: radius, radius, rounding
 	MIN: radius, radius, rounding
 	ROUND: radius, radius
 	ROUND: radius, radius
-	# Place the inner radius for drawing
+	# Place the inner radius for drawing.
 	SUB: innerRadius<FixedPoint>, rounding, border
 	SUB: innerRadius<FixedPoint>, rounding, border
-	# Use +- 0.5 pixel offsets for fake anti-aliasing
+	# Use +- 0.5 pixel offsets for fake anti-aliasing.
 	ADD: radiusOut<FixedPoint>, innerRadius, 0.5
 	ADD: radiusOut<FixedPoint>, innerRadius, 0.5
 	ADD: radiusIn<FixedPoint>, innerRadius, -0.5
 	ADD: radiusIn<FixedPoint>, innerRadius, -0.5
-	# Calculate dimensions for drawing
+	# Calculate dimensions for drawing.
 	SUB: w2<FixedPoint>, width, radius
 	SUB: w2<FixedPoint>, width, radius
 	SUB: w3<FixedPoint>, w2, radius
 	SUB: w3<FixedPoint>, w2, radius
 	SUB: w4<FixedPoint>, width, border
 	SUB: w4<FixedPoint>, width, border
@@ -64,7 +65,7 @@ BEGIN: generate_rounded_rectangle
 	SUB: h2<FixedPoint>, height, radius
 	SUB: h2<FixedPoint>, height, radius
 	SUB: h3<FixedPoint>, h2, radius
 	SUB: h3<FixedPoint>, h2, radius
 	SUB: r2<FixedPoint>, radius, border
 	SUB: r2<FixedPoint>, radius, border
-	# Draw
+	# Draw.
 	FADE_REGION_RADIAL: resultImage,   0,  0,  radius, radius,  radius, radius,  radiusIn, 255,  radiusOut, 0
 	FADE_REGION_RADIAL: resultImage,   0,  0,  radius, radius,  radius, radius,  radiusIn, 255,  radiusOut, 0
 	FADE_REGION_RADIAL: resultImage,  w2,  0,  radius, radius,       0, radius,  radiusIn, 255,  radiusOut, 0
 	FADE_REGION_RADIAL: resultImage,  w2,  0,  radius, radius,       0, radius,  radiusIn, 255,  radiusOut, 0
 	FADE_REGION_RADIAL: resultImage,   0, h2,  radius, radius,  radius,      0,  radiusIn, 255,  radiusOut, 0
 	FADE_REGION_RADIAL: resultImage,   0, h2,  radius, radius,  radius,      0,  radiusIn, 255,  radiusOut, 0
@@ -84,7 +85,7 @@ BEGIN: generate_rounded_button
 	INPUT: FixedPoint, border
 	INPUT: FixedPoint, border
 	INPUT: FixedPoint, rounding
 	INPUT: FixedPoint, rounding
 	OUTPUT: ImageRgbaU8, resultImage
 	OUTPUT: ImageRgbaU8, resultImage
-	# Scale by 2 / 255 so that 127.5 represents full intensity in patternImage
+	# Scale by 2 / 255 so that 127.5 represents full intensity in patternImage.
 	MUL: normRed<FixedPoint>, red, 0.007843138
 	MUL: normRed<FixedPoint>, red, 0.007843138
 	MUL: normGreen<FixedPoint>, green, 0.007843138
 	MUL: normGreen<FixedPoint>, green, 0.007843138
 	MUL: normBlue<FixedPoint>, blue, 0.007843138
 	MUL: normBlue<FixedPoint>, blue, 0.007843138
@@ -131,7 +132,7 @@ BEGIN: ListBox
 	RECTANGLE: colorImage, border, border, w2, h2, red, green, blue, 255
 	RECTANGLE: colorImage, border, border, w2, h2, red, green, blue, 255
 END:
 END:
 
 
-BEGIN: VerticalScrollBackground
+BEGIN: VerticalScrollList
 	INPUT: FixedPoint, width
 	INPUT: FixedPoint, width
 	INPUT: FixedPoint, height
 	INPUT: FixedPoint, height
 	INPUT: FixedPoint, red
 	INPUT: FixedPoint, red
@@ -173,11 +174,11 @@ UR"QUOTE(
 		method = "ListBox"
 		method = "ListBox"
 	[VerticalScrollKnob]
 	[VerticalScrollKnob]
 		rounding = 8
 		rounding = 8
-	[VerticalScrollBackground]
-		method = "VerticalScrollBackground"
-	[ScrollTop]
+	[VerticalScrollList]
+		method = "VerticalScrollList"
+	[ScrollUp]
 		rounding = 5
 		rounding = 5
-	[ScrollBottom]
+	[ScrollDown]
 		rounding = 5
 		rounding = 5
 	[Panel]
 	[Panel]
 		border = 1
 		border = 1
@@ -193,7 +194,6 @@ struct KeywordEntry {
 };
 };
 
 
 #define FOR_EACH_COLLECTION(LOCATION, MACRO_NAME) \
 #define FOR_EACH_COLLECTION(LOCATION, MACRO_NAME) \
-	MACRO_NAME(LOCATION monochromeImages) \
 	MACRO_NAME(LOCATION colorImages) \
 	MACRO_NAME(LOCATION colorImages) \
 	MACRO_NAME(LOCATION scalars) \
 	MACRO_NAME(LOCATION scalars) \
 	MACRO_NAME(LOCATION strings)
 	MACRO_NAME(LOCATION strings)
@@ -206,11 +206,9 @@ struct KeywordEntry {
 	}
 	}
 struct ClassSettings {
 struct ClassSettings {
 	String className; // Following a line with [className] in the *.ini configuration file.
 	String className; // Following a line with [className] in the *.ini configuration file.
-	List<KeywordEntry<AlignedImageU8>> monochromeImages;
-	List<KeywordEntry<OrderedImageRgbaU8>> colorImages;
+	List<KeywordEntry<PersistentImage>> colorImages;
 	List<KeywordEntry<FixedPoint>> scalars;
 	List<KeywordEntry<FixedPoint>> scalars;
 	List<KeywordEntry<String>> strings;
 	List<KeywordEntry<String>> strings;
-	// TODO: Store strings for paths and method names.
 	ClassSettings(const ReadableString &className)
 	ClassSettings(const ReadableString &className)
 	: className(className) {}
 	: className(className) {}
 	bool keyExists(const ReadableString &key) {
 	bool keyExists(const ReadableString &key) {
@@ -219,17 +217,23 @@ struct ClassSettings {
 	}
 	}
 	void setVariable(const ReadableString &key, const ReadableString &value) {
 	void setVariable(const ReadableString &key, const ReadableString &value) {
 		if (this->keyExists(key)) { throwError(U"The property ", key, U" was defined multiple times in ", className, U"\n"); }
 		if (this->keyExists(key)) { throwError(U"The property ", key, U" was defined multiple times in ", className, U"\n"); }
-		// TODO: Check what type the value has and select the correct list for it.
-		//       Try to be consistent with how images are embedded into the layout files, but allow embedding and loading monochrome images as well.
-		//       Let the parsing of settings have a temporary pool of RGBA images,
-		//       so that one does not have to load the same image multiple times when extracting channels,
-		//       and so that one can reuse memory when different components are using parts of the same atlas image.
-		//this->monochromeImages.pushConstruct(key, value);
-		//this->colorImages.pushConstruct(key, value);
-		if (value[0] == U'\"') {
+		DsrChar firstCharacter = value[0];
+		if (firstCharacter == U'\"') {
+			// Key = "text"
 			this->strings.pushConstruct(key, string_unmangleQuote(value));
 			this->strings.pushConstruct(key, string_unmangleQuote(value));
 		} else {
 		} else {
-			this->scalars.pushConstruct(key, FixedPoint::fromText(value));
+			int64_t pipeIndex = string_findFirst(value, U'|');
+			if (pipeIndex > -1 && string_caseInsensitiveMatch(string_before(value, pipeIndex), U"RGBA")) {
+				// Key = RGBA|File:Path
+				// Key = RGBA|WxH:Hexadecimals
+				PersistentImage newImage;
+				newImage.assignValue(string_after(value, pipeIndex));
+				this->colorImages.pushConstruct(key, newImage);
+			} else {
+				// Key = Integer
+				// Key = Integer.Decimals
+				this->scalars.pushConstruct(key, FixedPoint::fromText(value));
+			}
 		}
 		}
 	}
 	}
 	// Post-condition: Returns true iff the key was found for the expected type.
 	// Post-condition: Returns true iff the key was found for the expected type.
@@ -266,40 +270,6 @@ public:
 	virtual ~VisualThemeImpl() {}
 	virtual ~VisualThemeImpl() {}
 };
 };
 
 
-/* TODO: Find a better way to debug the content of a theme that can actually show the images.
-static String& string_toStreamIndented(String &target, const ImageU8 &image, const ReadableString &indentation) {
-	string_append(target, indentation, U"ImageU8 of ", image_getWidth(image), U"x", image_getHeight(image), U" pixels\n");
-	return target;
-}
-
-static String& string_toStreamIndented(String &target, const ImageRgbaU8 &image, const ReadableString &indentation) {
-	string_append(target, indentation, U"ImageRgbaU8 of ", image_getWidth(image), U"x", image_getHeight(image), U" pixels\n");
-	return target;
-}
-
-#define PRINT_SETTINGS(COLLECTION) \
-	for (int64_t i = 0; i < COLLECTION.length(); i++) { \
-		string_append(target, indentation, U"\t", COLLECTION[i].key, U" = ", COLLECTION[i].value, U"\n"); \
-	}
-static String& string_toStreamIndented(String &target, const ClassSettings &settings, const ReadableString &indentation) {
-	string_append(target, indentation, U"[", settings.className, U"]\n");
-	FOR_EACH_COLLECTION(settings., PRINT_SETTINGS)
-	return target;
-}
-
-static String& string_toStreamIndented(String &target, const VisualTheme &theme, const ReadableString &indentation) {
-	if (!theme.get()) {
-		string_append(target, indentation, U"Non-existing visual theme");
-	} else {
-		string_append(target, indentation, U"Theme:\n");
-		for (int c = 0; c < theme->settings.length(); c++) {
-			string_toStreamIndented(target, theme->settings[c], indentation + "\t");
-		}
-	}
-	return target;
-}
-*/
-
 static VisualTheme defaultTheme;
 static VisualTheme defaultTheme;
 VisualTheme theme_getDefault() {
 VisualTheme theme_getDefault() {
 	if (!(defaultTheme.get())) {
 	if (!(defaultTheme.get())) {
@@ -318,7 +288,6 @@ MediaMethod theme_getScalableImage(const VisualTheme &theme, const ReadableStrin
 	}
 	}
 	int classIndex = theme->getClassIndex(className);
 	int classIndex = theme->getClassIndex(className);
 	if (classIndex == -1) {
 	if (classIndex == -1) {
-		//printText(theme, U"\n");
 		throwError(U"theme_getScalableImage: Can't find any style class named ", className, U" in the given theme!\n");
 		throwError(U"theme_getScalableImage: Can't find any style class named ", className, U" in the given theme!\n");
 	}
 	}
 	// Try to get the method's name from the component's class settings,
 	// Try to get the method's name from the component's class settings,
@@ -326,7 +295,6 @@ MediaMethod theme_getScalableImage(const VisualTheme &theme, const ReadableStrin
 	String methodName;
 	String methodName;
 	if (!theme->settings[classIndex].getString(methodName, U"method")) {
 	if (!theme->settings[classIndex].getString(methodName, U"method")) {
 		if (!theme->settings[0].getString(methodName, U"method")) {
 		if (!theme->settings[0].getString(methodName, U"method")) {
-			//printText(theme, U"\n");
 			throwError(U"The property \"method\" could not be found from the style class ", className, U", nor in the default settings!\n");
 			throwError(U"The property \"method\" could not be found from the style class ", className, U", nor in the default settings!\n");
 		}
 		}
 	}
 	}
@@ -334,17 +302,10 @@ MediaMethod theme_getScalableImage(const VisualTheme &theme, const ReadableStrin
 }
 }
 
 
 static bool assignMediaMachineArguments(ClassSettings settings, MediaMachine &machine, int methodIndex, int inputIndex, const ReadableString &argumentName) {
 static bool assignMediaMachineArguments(ClassSettings settings, MediaMachine &machine, int methodIndex, int inputIndex, const ReadableString &argumentName) {
-	// Search for argumentName in monochromeImages.
-	for (int64_t i = 0; i < settings.monochromeImages.length(); i++) {
-		if (string_caseInsensitiveMatch(settings.monochromeImages[i].key, argumentName)) {
-			machine_setInputByIndex(machine, methodIndex, inputIndex, settings.monochromeImages[i].value);
-			return true;
-		}
-	}
 	// Search for argumentName in colorImages.
 	// Search for argumentName in colorImages.
 	for (int64_t i = 0; i < settings.colorImages.length(); i++) {
 	for (int64_t i = 0; i < settings.colorImages.length(); i++) {
 		if (string_caseInsensitiveMatch(settings.colorImages[i].key, argumentName)) {
 		if (string_caseInsensitiveMatch(settings.colorImages[i].key, argumentName)) {
-			machine_setInputByIndex(machine, methodIndex, inputIndex, settings.colorImages[i].value);
+			machine_setInputByIndex(machine, methodIndex, inputIndex, settings.colorImages[i].value.value);
 			return true;
 			return true;
 		}
 		}
 	}
 	}

+ 3 - 3
Source/DFPSR/gui/components/ListBox.cpp

@@ -220,10 +220,10 @@ void ListBox::receiveKeyboardEvent(const KeyboardEvent& event) {
 
 
 void ListBox::loadTheme(VisualTheme theme) {
 void ListBox::loadTheme(VisualTheme theme) {
 	this->scalableImage_listBox = theme_getScalableImage(theme, U"ListBox");
 	this->scalableImage_listBox = theme_getScalableImage(theme, U"ListBox");
-	this->scalableImage_scrollTop = theme_getScalableImage(theme, U"ScrollTop");
-	this->scalableImage_scrollBottom = theme_getScalableImage(theme, U"ScrollBottom");
+	this->scalableImage_scrollTop = theme_getScalableImage(theme, U"ScrollUp");
+	this->scalableImage_scrollBottom = theme_getScalableImage(theme, U"ScrollDown");
 	this->scalableImage_verticalScrollKnob = theme_getScalableImage(theme, U"VerticalScrollKnob");
 	this->scalableImage_verticalScrollKnob = theme_getScalableImage(theme, U"VerticalScrollKnob");
-	this->scalableImage_verticalScrollBackground = theme_getScalableImage(theme, U"VerticalScrollBackground");
+	this->scalableImage_verticalScrollBackground = theme_getScalableImage(theme, U"VerticalScrollList");
 	// Generate fixed size buttons for the scroll buttons (because their size is currently given by constants)
 	// Generate fixed size buttons for the scroll buttons (because their size is currently given by constants)
 	ColorRgbI32 color = this->color.value;
 	ColorRgbI32 color = this->color.value;
 	this->generateImage(this->scalableImage_scrollTop,    scrollWidth, scrollEndHeight, color.red, color.green, color.blue, 0)(this->scrollButtonTopImage_normal);
 	this->generateImage(this->scalableImage_scrollTop,    scrollWidth, scrollEndHeight, color.red, color.green, color.blue, 0)(this->scrollButtonTopImage_normal);

+ 92 - 69
Source/DFPSR/image/draw.cpp

@@ -823,21 +823,21 @@ static inline ColorRgbaI32 U32x4_to_ColorRgbaI32(const U32x4& color) {
 static inline U32x4 mixColorsUniform(const U32x4 &colorA, const U32x4 &colorB, uint32_t fineRatio) {
 static inline U32x4 mixColorsUniform(const U32x4 &colorA, const U32x4 &colorB, uint32_t fineRatio) {
 	uint16_t ratio = fineRatio >> 8;
 	uint16_t ratio = fineRatio >> 8;
 	uint16_t invRatio = 256 - ratio;
 	uint16_t invRatio = 256 - ratio;
-	ALIGN16 U16x8 weightA = U16x8(invRatio);
-	ALIGN16 U16x8 weightB = U16x8(ratio);
-	ALIGN16 U32x4 lowMask(0x00FF00FFu);
-	ALIGN16 U16x8 lowColorA = U16x8(colorA & lowMask);
-	ALIGN16 U16x8 lowColorB = U16x8(colorB & lowMask);
-	ALIGN16 U32x4 highMask(0xFF00FF00u);
-	ALIGN16 U16x8 highColorA = U16x8((colorA & highMask) >> 8);
-	ALIGN16 U16x8 highColorB = U16x8((colorB & highMask) >> 8);
-	ALIGN16 U32x4 lowColor = (((lowColorA * weightA) + (lowColorB * weightB))).get_U32();
-	ALIGN16 U32x4 highColor = (((highColorA * weightA) + (highColorB * weightB))).get_U32();
+	U16x8 weightA = U16x8(invRatio);
+	U16x8 weightB = U16x8(ratio);
+	U32x4 lowMask(0x00FF00FFu);
+	U16x8 lowColorA = U16x8(colorA & lowMask);
+	U16x8 lowColorB = U16x8(colorB & lowMask);
+	U32x4 highMask(0xFF00FF00u);
+	U16x8 highColorA = U16x8((colorA & highMask) >> 8);
+	U16x8 highColorB = U16x8((colorB & highMask) >> 8);
+	U32x4 lowColor = (((lowColorA * weightA) + (lowColorB * weightB))).get_U32();
+	U32x4 highColor = (((highColorA * weightA) + (highColorB * weightB))).get_U32();
 	return (((lowColor >> 8) & lowMask) | (highColor & highMask));
 	return (((lowColor >> 8) & lowMask) | (highColor & highMask));
 }
 }
 
 
-#define READ_CLAMP(X,Y) ImageRgbaU8Impl::unpackRgba(ImageRgbaU8Impl::readPixel_clamp(source, X, Y), source.packOrder)
-#define READ_CLAMP_SIMD(X,Y) ColorRgbaI32_to_U32x4(READ_CLAMP(X,Y))
+#define READ_RGBAU8_CLAMP(X,Y) ImageRgbaU8Impl::unpackRgba(ImageRgbaU8Impl::readPixel_clamp(source, X, Y), source.packOrder)
+#define READ_RGBAU8_CLAMP_SIMD(X,Y) ColorRgbaI32_to_U32x4(READ_RGBAU8_CLAMP(X,Y))
 
 
 // Fixed-precision decimal system with 16-bit indices and 16-bit sub-pixel weights
 // Fixed-precision decimal system with 16-bit indices and 16-bit sub-pixel weights
 static const uint32_t interpolationFullPixel = 65536;
 static const uint32_t interpolationFullPixel = 65536;
@@ -845,14 +845,51 @@ static const uint32_t interpolationHalfPixel = interpolationFullPixel / 2;
 // Modulo mask for values greater than or equal to 0 and lesser than interpolationFullPixel
 // Modulo mask for values greater than or equal to 0 and lesser than interpolationFullPixel
 static const uint32_t interpolationWeightMask = interpolationFullPixel - 1;
 static const uint32_t interpolationWeightMask = interpolationFullPixel - 1;
 
 
+template <bool BILINEAR>
+static uint32_t samplePixel(const ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, uint32_t leftX, uint32_t upperY, uint32_t rightRatio, uint32_t lowerRatio) {
+	if (BILINEAR) {
+		uint32_t upperRatio = 65536 - lowerRatio;
+		uint32_t leftRatio = 65536 - rightRatio;
+		U32x4 vUpperLeftColor = READ_RGBAU8_CLAMP_SIMD(leftX, upperY);
+		U32x4 vUpperRightColor = READ_RGBAU8_CLAMP_SIMD(leftX + 1, upperY);
+		U32x4 vLowerLeftColor = READ_RGBAU8_CLAMP_SIMD(leftX, upperY + 1);
+		U32x4 vLowerRightColor = READ_RGBAU8_CLAMP_SIMD(leftX + 1, upperY + 1);
+		U32x4 vLeftRatio = U32x4(leftRatio);
+		U32x4 vRightRatio = U32x4(rightRatio);
+		U32x4 vUpperColor = ((vUpperLeftColor * vLeftRatio) + (vUpperRightColor * vRightRatio)) >> 16;
+		U32x4 vLowerColor = ((vLowerLeftColor * vLeftRatio) + (vLowerRightColor * vRightRatio)) >> 16;
+		U32x4 vCenterColor = ((vUpperColor * upperRatio) + (vLowerColor * lowerRatio)) >> 16;
+		return (target.packRgba(U32x4_to_ColorRgbaI32(vCenterColor))).packed;
+	} else {
+		return (target.packRgba(READ_RGBAU8_CLAMP(leftX, upperY))).packed;
+	}
+}
+
+template <bool BILINEAR>
+static uint8_t samplePixel(const ImageU8Impl& target, const ImageU8Impl& source, uint32_t leftX, uint32_t upperY, uint32_t rightRatio, uint32_t lowerRatio) {
+	if (BILINEAR) {
+		uint32_t upperRatio = 65536 - lowerRatio;
+		uint32_t leftRatio = 65536 - rightRatio;
+		uint32_t upperLeftLuma = ImageU8Impl::readPixel_clamp(source, leftX, upperY);
+		uint32_t upperRightLuma = ImageU8Impl::readPixel_clamp(source, leftX + 1, upperY);
+		uint32_t lowerLeftLuma = ImageU8Impl::readPixel_clamp(source, leftX, upperY + 1);
+		uint32_t lowerRightLuma = ImageU8Impl::readPixel_clamp(source, leftX + 1, upperY + 1);
+		uint32_t upperLuma = ((upperLeftLuma * leftRatio) + (upperRightLuma * rightRatio)) >> 16;
+		uint32_t lowerLuma = ((lowerLeftLuma * leftRatio) + (lowerRightLuma * rightRatio)) >> 16;
+		return ((upperLuma * upperRatio) + (lowerLuma * lowerRatio)) >> 16;
+	} else {
+		return ImageU8Impl::readPixel_clamp(source, leftX, upperY);
+	}
+}
+
 // BILINEAR: Enables linear interpolation
 // BILINEAR: Enables linear interpolation
 // scaleRegion:
 // scaleRegion:
 //     The stretched location of the source image in the target image
 //     The stretched location of the source image in the target image
 //     Making it smaller than the target image will fill the outside with stretched pixels
 //     Making it smaller than the target image will fill the outside with stretched pixels
 //     Allowing the caller to crop away parts of the source image that aren't interesting
 //     Allowing the caller to crop away parts of the source image that aren't interesting
 //     Can be used to round the region to a multiple of the input size for a fixed pixel size
 //     Can be used to round the region to a multiple of the input size for a fixed pixel size
-template <bool BILINEAR>
-static void resize_reference(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, const IRect& scaleRegion) {
+template <bool BILINEAR, typename IMAGE_TYPE, typename PIXEL_TYPE>
+static void resize_reference(IMAGE_TYPE& target, const IMAGE_TYPE& source, const IRect& scaleRegion) {
 	// Reference implementation
 	// Reference implementation
 
 
 	// Offset in source pixels per target pixel
 	// Offset in source pixels per target pixel
@@ -864,42 +901,23 @@ static void resize_reference(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& sou
 		startX -= interpolationHalfPixel;
 		startX -= interpolationHalfPixel;
 		startY -= interpolationHalfPixel;
 		startY -= interpolationHalfPixel;
 	}
 	}
-	SafePointer<uint32_t> targetRow = imageInternal::getSafeData<uint32_t>(target);
+	SafePointer<PIXEL_TYPE> targetRow = imageInternal::getSafeData<PIXEL_TYPE>(target);
 	int32_t readY = startY;
 	int32_t readY = startY;
 	for (int32_t y = 0; y < target.height; y++) {
 	for (int32_t y = 0; y < target.height; y++) {
 		int32_t naturalY = readY;
 		int32_t naturalY = readY;
 		if (naturalY < 0) { naturalY = 0; }
 		if (naturalY < 0) { naturalY = 0; }
 		uint32_t sampleY = (uint32_t)naturalY;
 		uint32_t sampleY = (uint32_t)naturalY;
 		uint32_t upperY = sampleY >> 16;
 		uint32_t upperY = sampleY >> 16;
-		uint32_t lowerY = upperY + 1;
 		uint32_t lowerRatio = sampleY & interpolationWeightMask;
 		uint32_t lowerRatio = sampleY & interpolationWeightMask;
-		uint32_t upperRatio = 65536 - lowerRatio;
-		SafePointer<uint32_t> targetPixel = targetRow;
+		SafePointer<PIXEL_TYPE> targetPixel = targetRow;
 		int32_t readX = startX;
 		int32_t readX = startX;
 		for (int32_t x = 0; x < target.width; x++) {
 		for (int32_t x = 0; x < target.width; x++) {
 			int32_t naturalX = readX;
 			int32_t naturalX = readX;
 			if (naturalX < 0) { naturalX = 0; }
 			if (naturalX < 0) { naturalX = 0; }
 			uint32_t sampleX = (uint32_t)naturalX;
 			uint32_t sampleX = (uint32_t)naturalX;
 			uint32_t leftX = sampleX >> 16;
 			uint32_t leftX = sampleX >> 16;
-			uint32_t rightX = leftX + 1;
 			uint32_t rightRatio = sampleX & interpolationWeightMask;
 			uint32_t rightRatio = sampleX & interpolationWeightMask;
-			uint32_t leftRatio = 65536 - rightRatio;
-			ColorRgbaI32 finalColor;
-			if (BILINEAR) {
-				ALIGN16 U32x4 vUpperLeftColor = READ_CLAMP_SIMD(leftX, upperY);
-				ALIGN16 U32x4 vUpperRightColor = READ_CLAMP_SIMD(rightX, upperY);
-				ALIGN16 U32x4 vLowerLeftColor = READ_CLAMP_SIMD(leftX, lowerY);
-				ALIGN16 U32x4 vLowerRightColor = READ_CLAMP_SIMD(rightX, lowerY);
-				ALIGN16 U32x4 vLeftRatio = U32x4(leftRatio);
-				ALIGN16 U32x4 vRightRatio = U32x4(rightRatio);
-				ALIGN16 U32x4 vUpperColor = ((vUpperLeftColor * vLeftRatio) + (vUpperRightColor * vRightRatio)) >> 16;
-				ALIGN16 U32x4 vLowerColor = ((vLowerLeftColor * vLeftRatio) + (vLowerRightColor * vRightRatio)) >> 16;
-				ALIGN16 U32x4 vCenterColor = ((vUpperColor * upperRatio) + (vLowerColor * lowerRatio)) >> 16;
-				finalColor = U32x4_to_ColorRgbaI32(vCenterColor);
-			} else {
-				finalColor = READ_CLAMP(leftX, upperY);
-			}
-			*targetPixel = target.packRgba(finalColor).packed;
+			*targetPixel = samplePixel<BILINEAR>(target, source, leftX, upperY, rightRatio, lowerRatio);
 			targetPixel += 1;
 			targetPixel += 1;
 			readX += offsetX;
 			readX += offsetX;
 		}
 		}
@@ -957,8 +975,8 @@ static void resize_optimized(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& sou
 					}
 					}
 				} else {
 				} else {
 					for (int32_t x = 0; x < target.width; x++) {
 					for (int32_t x = 0; x < target.width; x++) {
-						ALIGN16 U32x4 vUpperColor = READ_CLAMP_SIMD(x, upperY);
-						ALIGN16 U32x4 vLowerColor = READ_CLAMP_SIMD(x, lowerY);
+						ALIGN16 U32x4 vUpperColor = READ_RGBAU8_CLAMP_SIMD(x, upperY);
+						ALIGN16 U32x4 vLowerColor = READ_RGBAU8_CLAMP_SIMD(x, lowerY);
 						ALIGN16 U32x4 vCenterColor = ((vUpperColor * upperRatio) + (vLowerColor * lowerRatio)) >> 16;
 						ALIGN16 U32x4 vCenterColor = ((vUpperColor * upperRatio) + (vLowerColor * lowerRatio)) >> 16;
 						ColorRgbaI32 finalColor = U32x4_to_ColorRgbaI32(vCenterColor);
 						ColorRgbaI32 finalColor = U32x4_to_ColorRgbaI32(vCenterColor);
 						*targetPixel = target.packRgba(finalColor).packed;
 						*targetPixel = target.packRgba(finalColor).packed;
@@ -996,12 +1014,12 @@ static void resize_optimized(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& sou
 				uint32_t leftRatio = 65536 - rightRatio;
 				uint32_t leftRatio = 65536 - rightRatio;
 				ColorRgbaI32 finalColor;
 				ColorRgbaI32 finalColor;
 				if (BILINEAR) {
 				if (BILINEAR) {
-					ALIGN16 U32x4 vLeftColor = READ_CLAMP_SIMD(leftX, y);
-					ALIGN16 U32x4 vRightColor = READ_CLAMP_SIMD(rightX, y);
+					ALIGN16 U32x4 vLeftColor = READ_RGBAU8_CLAMP_SIMD(leftX, y);
+					ALIGN16 U32x4 vRightColor = READ_RGBAU8_CLAMP_SIMD(rightX, y);
 					ALIGN16 U32x4 vCenterColor = ((vLeftColor * leftRatio) + (vRightColor * rightRatio)) >> 16;
 					ALIGN16 U32x4 vCenterColor = ((vLeftColor * leftRatio) + (vRightColor * rightRatio)) >> 16;
 					finalColor = U32x4_to_ColorRgbaI32(vCenterColor);
 					finalColor = U32x4_to_ColorRgbaI32(vCenterColor);
 				} else {
 				} else {
-					finalColor = READ_CLAMP(leftX, y);
+					finalColor = READ_RGBAU8_CLAMP(leftX, y);
 				}
 				}
 				*targetPixel = target.packRgba(finalColor).packed;
 				*targetPixel = target.packRgba(finalColor).packed;
 				targetPixel += 1;
 				targetPixel += 1;
@@ -1011,7 +1029,7 @@ static void resize_optimized(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& sou
 		}
 		}
 	} else {
 	} else {
 		// Call the reference implementation
 		// Call the reference implementation
-		resize_reference<BILINEAR>(target, source, scaleRegion);
+		resize_reference<BILINEAR, ImageRgbaU8Impl, uint32_t>(target, source, scaleRegion);
 	}
 	}
 }
 }
 
 
@@ -1025,14 +1043,14 @@ static bool imageIs16ByteAligned(const ImageImpl& image) {
 static void resize_aux(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate, bool paddWrite, const IRect& scaleRegion) {
 static void resize_aux(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate, bool paddWrite, const IRect& scaleRegion) {
 	// If writing to padding is allowed and both images are 16-byte aligned with the same pack order
 	// If writing to padding is allowed and both images are 16-byte aligned with the same pack order
 	if (paddWrite && imageIs16ByteAligned(source) && imageIs16ByteAligned(target)) {
 	if (paddWrite && imageIs16ByteAligned(source) && imageIs16ByteAligned(target)) {
-		// Optimized resize allowed
+		// SIMD resize allowed
 		if (interpolate) {
 		if (interpolate) {
 			resize_optimized<true, true>(target, source, scaleRegion);
 			resize_optimized<true, true>(target, source, scaleRegion);
 		} else {
 		} else {
 			resize_optimized<false, true>(target, source, scaleRegion);
 			resize_optimized<false, true>(target, source, scaleRegion);
 		}
 		}
 	} else {
 	} else {
-		// Non-optimized resize
+		// Non-SIMD resize
 		if (interpolate) {
 		if (interpolate) {
 			resize_optimized<true, false>(target, source, scaleRegion);
 			resize_optimized<true, false>(target, source, scaleRegion);
 		} else {
 		} else {
@@ -1041,41 +1059,47 @@ static void resize_aux(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, b
 	}
 	}
 }
 }
 
 
-void dsr::imageImpl_resizeInPlace(ImageRgbaU8Impl& target, ImageRgbaU8Impl* wideTempImage, const ImageRgbaU8Impl& source, bool interpolate, const IRect& scaleRegion) {
+// TODO: Optimize monochrome resizing.
+static void resize_aux(ImageU8Impl& target, const ImageU8Impl& source, bool interpolate, bool paddWrite, const IRect& scaleRegion) {
+	if (interpolate) {
+		resize_reference<true, ImageU8Impl, uint8_t>(target, source, scaleRegion);
+	} else {
+		resize_reference<false, ImageU8Impl, uint8_t>(target, source, scaleRegion);
+	}
+}
+
+// Creating an image to replacedImage with the same pack order as originalImage when applicable to the image format.
+static ImageRgbaU8Impl createWithSamePackOrder(const ImageRgbaU8Impl& originalImage, int32_t width, int32_t height) {
+	return ImageRgbaU8Impl(width, height, originalImage.packOrder.packOrderIndex);
+}
+static ImageU8Impl createWithSamePackOrder(const ImageU8Impl& originalImage, int32_t width, int32_t height) {
+	return ImageU8Impl(width, height);
+}
+
+template <typename IMAGE_TYPE>
+void resizeToTarget(IMAGE_TYPE& target, const IMAGE_TYPE& source, bool interpolate) {
+	IRect scaleRegion = imageInternal::getBound(target);
 	if (target.width != source.width && target.height > source.height) {
 	if (target.width != source.width && target.height > source.height) {
 		// Upscaling is faster in two steps by both reusing the horizontal interpolation and vectorizing the vertical interpolation.
 		// Upscaling is faster in two steps by both reusing the horizontal interpolation and vectorizing the vertical interpolation.
 		int tempWidth = target.width;
 		int tempWidth = target.width;
 		int tempHeight = source.height;
 		int tempHeight = source.height;
-		PackOrderIndex tempPackOrder = target.packOrder.packOrderIndex;
 		IRect tempScaleRegion = IRect(scaleRegion.left(), 0, scaleRegion.width(), source.height);
 		IRect tempScaleRegion = IRect(scaleRegion.left(), 0, scaleRegion.width(), source.height);
-		if (wideTempImage == nullptr
-		 || wideTempImage->width != tempWidth
-		 || wideTempImage->height != tempHeight
-		 || wideTempImage->packOrder.packOrderIndex != tempPackOrder) {
-			// Performance warnings
-			// TODO: Make optional
-			if (wideTempImage != nullptr) {
-				if (wideTempImage->width != tempWidth) { printText("Ignored temp buffer of wrong width! Found ", wideTempImage->width, " instead of ", tempWidth, "\n"); }
-				if (wideTempImage->height != tempHeight) { printText("Ignored temp buffer of wrong height! Found ", wideTempImage->height, " instead of ", tempHeight, "\n"); }
-				if (wideTempImage->packOrder.packOrderIndex != tempPackOrder) { printText("Ignored temp buffer of wrong pack order!\n"); }
-			}
-			// Create a new buffer
-			ImageRgbaU8Impl newTempImage = ImageRgbaU8Impl(tempWidth, tempHeight, tempPackOrder);
-			resize_aux(newTempImage, source, interpolate, true, tempScaleRegion);
-			resize_aux(target, newTempImage, interpolate, true, scaleRegion);
-		} else {
-			// Use existing buffer
-			resize_aux(*wideTempImage, source, interpolate, true, tempScaleRegion);
-			resize_aux(target, *wideTempImage, interpolate, true, scaleRegion);
-		}
+		// Create a temporary buffer.
+		IMAGE_TYPE newTempImage = createWithSamePackOrder(target, tempWidth, tempHeight);
+		resize_aux(newTempImage, source, interpolate, true, tempScaleRegion);
+		resize_aux(target, newTempImage, interpolate, true, scaleRegion);
 	} else {
 	} else {
-		// Downscaling or only changing one dimension is faster in one step
+		// Downscaling or only changing one dimension is faster in one step.
 		resize_aux(target, source, interpolate, true, scaleRegion);
 		resize_aux(target, source, interpolate, true, scaleRegion);
 	}
 	}
 }
 }
 
 
 void dsr::imageImpl_resizeToTarget(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate) {
 void dsr::imageImpl_resizeToTarget(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate) {
-	imageImpl_resizeInPlace(target, nullptr, source, interpolate, imageInternal::getBound(target));
+	resizeToTarget<ImageRgbaU8Impl>(target, source, interpolate);
+}
+
+void dsr::imageImpl_resizeToTarget(ImageU8Impl& target, const ImageU8Impl& source, bool interpolate) {
+	resizeToTarget<ImageU8Impl>(target, source, interpolate);
 }
 }
 
 
 template <bool CONVERT_COLOR>
 template <bool CONVERT_COLOR>
@@ -1089,7 +1113,6 @@ static inline Color4xU8 convertRead(const ImageRgbaU8Impl& target, const ImageRg
 
 
 // Used for drawing large pixels
 // Used for drawing large pixels
 static inline void fillRectangle(ImageRgbaU8Impl& target, int pixelLeft, int pixelRight, int pixelTop, int pixelBottom, const Color4xU8& packedColor) {
 static inline void fillRectangle(ImageRgbaU8Impl& target, int pixelLeft, int pixelRight, int pixelTop, int pixelBottom, const Color4xU8& packedColor) {
-	// TODO: Get target pointer in advance and add the correct offsets
 	SafePointer<Color4xU8> targetRow = imageInternal::getSafeData<Color4xU8>(target, pixelTop) + pixelLeft;
 	SafePointer<Color4xU8> targetRow = imageInternal::getSafeData<Color4xU8>(target, pixelTop) + pixelLeft;
 	for (int y = pixelTop; y < pixelBottom; y++) {
 	for (int y = pixelTop; y < pixelBottom; y++) {
 		SafePointer<Color4xU8> targetPixel = targetRow;
 		SafePointer<Color4xU8> targetPixel = targetRow;

+ 2 - 7
Source/DFPSR/image/draw.h

@@ -76,20 +76,15 @@ void imageImpl_drawHigher(ImageF32Impl& targetHeight, const ImageF32Impl& source
   ImageRgbaU8Impl& targetB, const ImageRgbaU8Impl& sourceB, int32_t left = 0, int32_t top = 0, float sourceHeightOffset = 0);
   ImageRgbaU8Impl& targetB, const ImageRgbaU8Impl& sourceB, int32_t left = 0, int32_t top = 0, float sourceHeightOffset = 0);
 
 
 // Pre-conditions:
 // Pre-conditions:
-//     * wideTempImage should be one of the following:
-//        * A nullptr (for allocating it automatically when needed)
-//          Can be preferred when down-scaling, because the two-step resize is only used when width changes and height increases
-//        * An image of dimensions target.width x source.height and the same pack order as target
-//          Wrong dimensions or pack order for wideTempImage is equivalent to passing nullptr
 //     * target must own its padding
 //     * target must own its padding
 //       This is automatically true for aligned images
 //       This is automatically true for aligned images
-//       If broken, visible pixels in a parent image may change outside of the sub-image's region
+//       If the target does not own its padding, any pixels being treated as padding at the end of each line may become visible artifacts in another image sharing the buffer.
 // Side-effects:
 // Side-effects:
 //     * Writes a resized version of source to target, including padding
 //     * Writes a resized version of source to target, including padding
 //     * May also write to any pixels in wideTempImage, including padding
 //     * May also write to any pixels in wideTempImage, including padding
 //     * May also change the pack order of wideTempImage
 //     * May also change the pack order of wideTempImage
-void imageImpl_resizeInPlace(ImageRgbaU8Impl& target, ImageRgbaU8Impl* wideTempImage, const ImageRgbaU8Impl& source, bool interpolate, const IRect& scaleRegion);
 void imageImpl_resizeToTarget(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate);
 void imageImpl_resizeToTarget(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, bool interpolate);
+void imageImpl_resizeToTarget(ImageU8Impl& target, const ImageU8Impl& source, bool interpolate);
 void imageImpl_blockMagnify(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, int pixelWidth, int pixelHeight);
 void imageImpl_blockMagnify(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, int pixelWidth, int pixelHeight);
 void imageImpl_blockMagnify_aligned(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, int pixelWidth, int pixelHeight);
 void imageImpl_blockMagnify_aligned(ImageRgbaU8Impl& target, const ImageRgbaU8Impl& source, int pixelWidth, int pixelHeight);
 
 

+ 117 - 1
Source/tools/wizard/main.cpp

@@ -17,7 +17,122 @@ using namespace dsr;
 bool running = true;
 bool running = true;
 Window window;
 Window window;
 
 
-String interfaceContent =
+static const ReadableString mediaMachineCode =
+UR"QUOTE(
+BEGIN: Button
+	INPUT: FixedPoint, width
+	INPUT: FixedPoint, height
+	INPUT: FixedPoint, red
+	INPUT: FixedPoint, green
+	INPUT: FixedPoint, blue
+	INPUT: FixedPoint, pressed
+	INPUT: FixedPoint, sourceLeft
+	INPUT: FixedPoint, sourceTop
+	INPUT: FixedPoint, sourceWidth
+	INPUT: FixedPoint, sourceHeight
+	INPUT: FixedPoint, pressOffsetX
+	INPUT: FixedPoint, pressOffsetY
+	INPUT: ImageRgbaU8, atlas
+	OUTPUT: ImageRgbaU8, colorImage
+	# Scale by 1 / 255 so that 255 represents full intensity in atlas.
+	MUL: normRed<FixedPoint>, red, 0.00392156862745
+	MUL: normGreen<FixedPoint>, green, 0.00392156862745
+	MUL: normBlue<FixedPoint>, blue, 0.00392156862745
+	# Calculate the final source location to read.
+	MUL: sourceOffsetX<FixedPoint>, pressed, pressOffsetX
+	MUL: sourceOffsetY<FixedPoint>, pressed, pressOffsetY
+	ADD: adjustedSourceLeft<FixedPoint>, sourceLeft, sourceOffsetX
+	ADD: adjustedSourceTop<FixedPoint>, sourceTop, sourceOffsetY
+	# Resize source region from the atlas.
+	RESIZE_BILINEAR: sourceImage<ImageRgbaU8>, width, height, atlas, adjustedSourceLeft, adjustedSourceTop, sourceWidth, sourceHeight
+	GET_RED: diffuseMap<ImageU8>, sourceImage
+	GET_GREEN: specularMap<ImageU8>, sourceImage
+	GET_ALPHA: visibilityMap<ImageU8>, sourceImage
+	MUL: redImage<ImageU8>, diffuseMap, normRed
+	MUL: greenImage<ImageU8>, diffuseMap, normGreen
+	MUL: blueImage<ImageU8>, diffuseMap, normBlue
+	ADD: redImage, redImage, specularMap
+	ADD: greenImage, greenImage, specularMap
+	ADD: blueImage, blueImage, specularMap
+	PACK_RGBA: colorImage, redImage, greenImage, blueImage, visibilityMap
+END:
+
+BEGIN: ListBox
+	INPUT: FixedPoint, width
+	INPUT: FixedPoint, height
+	INPUT: FixedPoint, red
+	INPUT: FixedPoint, green
+	INPUT: FixedPoint, blue
+	INPUT: FixedPoint, border
+	OUTPUT: ImageRgbaU8, colorImage
+	CREATE: colorImage, width, height
+	ADD: b2<FixedPoint>, border, border
+	SUB: w2<FixedPoint>, width, b2
+	SUB: h2<FixedPoint>, height, b2
+	RECTANGLE: colorImage, border, border, w2, h2, red, green, blue, 255
+END:
+
+BEGIN: VerticalScrollList
+	INPUT: FixedPoint, width
+	INPUT: FixedPoint, height
+	INPUT: FixedPoint, red
+	INPUT: FixedPoint, green
+	INPUT: FixedPoint, blue
+	OUTPUT: ImageRgbaU8, colorImage
+	CREATE: visImage<ImageU8>, width, height
+	CREATE: lumaImage<ImageU8>, width, height
+	FADE_LINEAR: visImage, 0, 0, 128, width, 0, 0
+	PACK_RGBA: colorImage, 0, 0, 0, visImage
+END:
+
+BEGIN: Panel
+	INPUT: FixedPoint, width
+	INPUT: FixedPoint, height
+	INPUT: FixedPoint, red
+	INPUT: FixedPoint, green
+	INPUT: FixedPoint, blue
+	INPUT: FixedPoint, border
+	OUTPUT: ImageRgbaU8, colorImage
+	CREATE: colorImage, width, height
+	ADD: b2<FixedPoint>, border, border
+	SUB: w2<FixedPoint>, width, b2
+	SUB: h2<FixedPoint>, height, b2
+	RECTANGLE: colorImage, border, border, w2, h2, red, green, blue, 255
+END:
+)QUOTE";
+
+static const ReadableString styleSettings =
+UR"QUOTE(
+	border = 2
+	atlas = RGBA|File:media/Style.png
+	; Image location in the atlas
+	sourceLeft = 0
+	sourceTop = 0
+	sourceWidth = 64
+	sourceHeight = 64
+	; How the image location moves when pressed increases
+	pressOffsetX = 64
+	pressOffsetY = 0
+	; Fall back on the Button method if a component's class could not be recognized.
+	method = "Button"
+	[Button]
+		rounding = 12
+	[ListBox]
+		method = "ListBox"
+	[VerticalScrollKnob]
+		rounding = 8
+	[VerticalScrollList]
+		method = "VerticalScrollList"
+	[ScrollUp]
+		rounding = 5
+	[ScrollDown]
+		rounding = 5
+	[Panel]
+		border = 1
+		method = "Panel"
+)QUOTE";
+
+static const ReadableString interfaceContent =
 UR"QUOTE(
 UR"QUOTE(
 Begin : Panel
 Begin : Panel
 	Name = "mainPanel"
 	Name = "mainPanel"
@@ -211,6 +326,7 @@ void dsrMain(List<String> args) {
 	// Create a window
 	// Create a window
 	window = window_create(U"DFPSR wizard application", 800, 600);
 	window = window_create(U"DFPSR wizard application", 800, 600);
 	window_loadInterfaceFromString(window, interfaceContent);
 	window_loadInterfaceFromString(window, interfaceContent);
+	window_applyTheme(window, theme_create(mediaMachineCode, styleSettings));
 
 
 	// Find components
 	// Find components
 	projectList = window_findComponentByName(window, U"projectList");
 	projectList = window_findComponentByName(window, U"projectList");

BIN
Source/tools/wizard/media/Style.png


BIN
Source/tools/wizard/media/Style.xcf