1 module CPUblit.transform;
2 
3 import CPUblit.colorspaces;
4 import bitleveld.datatypes;
5 
6 /+/** 
7  * Horizontal scaling using nearest integer algorithm for per-line operations. Mainly created to solve problems in
8  * PixelPerfectEngine with sprite scaling
9  * Params:
10  *   src = Source of the line to be transformed. Must be large enough to support the destination output with the given
11  *   transformation params.
12  *   dest = Destination of the transformed line.
13  *   length = Length of the output. Must be less or eaqual than the dest buffer. Use function "scaleNearestLength" to 
14  *   calculate the needed output length.
15  *   trfmParam = Transformation parameter.
16  *   offset = Offset in the source line. This allows the source line to be offset by the given amount of fractional 
17  *   pixels. Default value is zero.
18  */
19 public void horizontalScaleNearest(T)(T* src, T* dest, sizediff_t length, int trfmParam, size_t offset = 0) 
20 		@nogc pure nothrow {
21 	if(trfmParam < 0){
22 		offset = (length<<10) - 1024 - offset;
23 	}
24 	while (length > 0) {
25 		*dest = src[offset>>>10];
26 		offset += trfmParam;
27 		length--;
28 		dest++;
29 	}
30 }+/
31 /** 
32  * Horizontal scaling using nearest integer algorithm for per-line operations. Intended to use with arrays that might
33  * contain elements less than 8 bit in length.
34  * Params:
35  *   src = Source of the line to be transformed. Must be large enough to support the destination output with the given
36  *   transformation params.
37  *   dest = Destination of the transformed line.
38  *   length = Length of the output. Must be less or eaqual than the dest buffer. Use function "scaleNearestLength" to 
39  *   calculate the needed output length.
40  *   trfmParam = Transformation parameter.
41  *   offset = Offset in the source line. This allows the source line to be offset by the given amount of fractional 
42  *   pixels. Default value is zero.
43  */
44 public void horizontalScaleNearest(ArrayType)(ArrayType src, ArrayType dest, sizediff_t length, int trfmParam, 
45 		sizediff_t offset = 0) @nogc pure nothrow {
46 	if(trfmParam < 0) {
47 		trfmParam *= -1;
48 		for (sizediff_t i ; i < length ; i++) {
49 			dest[i] = src[src.length - (offset>>>10) - 1];
50 			offset += trfmParam;
51 		}
52 	} else {
53 		for (sizediff_t i ; i < length ; i++) {
54 			dest[i] = src[offset>>>10];
55 			offset += trfmParam;
56 		}
57 	}
58 }
59 /+/** 
60  * Horizontal scaling using nearest integer algorithm for per-line operations. Uses pointers for both the palette and
61  * source. Intended for 8 bit or larger data.
62  * Params:
63  *   src = Source of the line to be transformed. Must be large enough to support the destination output with the given
64  *   transformation params.
65  *   dest = Destination of the transformed line.
66  *   palette = Palette. Should have enough elements for every index, or ensure that source wouldn't point that far.
67  *   length = Length of the output. Must be less or eaqual than the dest buffer. Use function "scaleNearestLength" to 
68  *   calculate the needed output length.
69  *   trfmParam = Transformation parameter.
70  *   offset = Offset in the source line. This allows the source line to be offset by the given amount of fractional 
71  *   pixels. Default value is zero.
72  */
73 public void horizontalScaleNearestAndCLU(T, U)(T* src, U* dest, U* palette, sizediff_t length, int trfmParam, 
74 		size_t offset = 0) @nogc pure nothrow {
75 	if(trfmParam < 0){
76 		offset += (length<<10) - 1024 - offset;
77 	}
78 	while (length > 0) {
79 		*dest = palette[src[offset>>>10]];
80 		offset += trfmParam;
81 		length--;
82 		dest++;
83 	}
84 }+/
85 /** 
86  * Horizontal scaling using nearest integer algorithm for per-line operations. Uses arrays for source (compatible with 
87  * data types smaller than 8 bit)
88  * Params:
89  *   src = Source of the line to be transformed. Must be large enough to support the destination output with the given
90  *   transformation params.
91  *   dest = Destination of the transformed line.
92  *   palette = Palette. Should have enough elements for every index, or ensure that source wouldn't point that far.
93  *   length = Length of the output. Must be less or eaqual than the dest buffer. Use function "scaleNearestLength" to 
94  *   calculate the needed output length.
95  *   trfmParam = Transformation parameter.
96  *   offset = Offset in the source line. This allows the source line to be offset by the given amount of fractional 
97  *   pixels. Default value is zero.
98  */
99 public void horizontalScaleNearestAndCLU(ArrayType, U)(ArrayType src, U* dest, U* palette, sizediff_t length, 
100 		int trfmParam, sizediff_t offset = 0) @nogc pure nothrow {
101 	if(trfmParam < 0) {
102 		trfmParam *= -1;
103 		for (sizediff_t i ; i < length ; i++) {
104 			dest[i] = palette[src[src.length - (offset>>>10) - 1]];
105 			offset += trfmParam;
106 		}
107 	} else {
108 		for (sizediff_t i ; i < length ; i++) {
109 			dest[i] = palette[src[offset>>>10]];
110 			offset += trfmParam;
111 		}
112 	}
113 }
114 /**
115  * Horizontal scaling using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
116  * Works with most datatypes. Use a separate one for 4 bit.
117  * Lenght determines the source's length.
118  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
119  */
120 public void _horizontalScaleNearest(T)(T* src, T* dest, sizediff_t length, int trfmParam) @nogc pure nothrow {
121 	int trfmParamA = trfmParam;
122 	sizediff_t offset;
123 	length <<= 10;
124 	if(trfmParam < 0){
125 		offset += length-1024;
126 		trfmParamA *= -1;
127 	}
128 	while(length > 0){
129 		*dest = src[offset>>>10];
130 		offset += trfmParam;
131 		length -= trfmParamA;
132 		dest++;
133 	}
134 }
135 /**
136  * Horizontal scaling and color lookup using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
137  * Works with most datatypes. Use a separate one for 4 bit.
138  * Lenght determines the source's length.
139  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
140  */
141 public void _horizontalScaleNearestAndCLU(T, U)(T* src, U* dest, U* palette, sizediff_t length, const int trfmParam)
142 		@nogc pure nothrow {
143 	int trfmParamA = trfmParam;
144 	sizediff_t offset;
145 	length <<= 10;
146 	if(trfmParam < 0){
147 		offset += length-1024;
148 		trfmParamA *= -1;
149 	}
150 	while(length > 0){
151 		*dest = palette[(src[offset>>>10])];
152 		offset += trfmParam;
153 		length -= trfmParamA;
154 		dest++;
155 	}
156 }
157 /**
158  * Horizontal scaling using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
159  * Works with 4 bit datatypes.
160  * Lenght determines the source's length.
161  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
162  */
163 public void _horizontalScaleNearest4Bit(ubyte* src, ubyte* dest, sizediff_t length, sizediff_t offset, 
164 		const int trfmParam) @nogc pure nothrow {
165 	int trfmParamA = trfmParam;
166 	length <<= 10;
167 	offset <<= 10;
168 	if(trfmParam < 0){
169 		offset += length-1024;
170 		trfmParamA *= -1;
171 	}
172 	while(length > 0){
173 		const ubyte temp = (offset>>>10) & 1 ?  src[offset>>>11] & 0x0F : src[offset>>>11] >> 4;
174 		*dest |= length & 1 ? temp : temp << 4;
175 		offset += trfmParam;
176 		length -= trfmParamA;
177 		dest++;
178 	}
179 }
180 /**
181  * Horizontal scaling using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
182  * Works with 16, 8, 4, and 2 bit datatypes.
183  * Lenght determines the source's length.
184  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
185  * `ArrayType` should be bitleveld's NibbleArray or QuadArray, but also works with regular D arrays.
186  */
187 public void _horizontalScaleNearest(ArrayType)(ArrayType src, ArrayType dest, sizediff_t length, sizediff_t offset, 
188 		const int trfmParam) @nogc pure nothrow {
189 	int trfmParamA = trfmParam;
190 	size_t destPtr;
191 	length <<= 10;
192 	offset <<= 10;
193 	if(trfmParam < 0){
194 		offset += length-1024;
195 		trfmParamA *= -1;
196 	}
197 	while(length > 0){
198 		dest[destPtr] = src[offset>>>10];
199 		offset += trfmParam;
200 		length -= trfmParamA;
201 		destPtr++;
202 	}
203 }
204 /**
205  * Horizontal scaling and color lookup using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
206  * Works with 4 bit datatypes.
207  * Lenght determines the source's length.
208  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
209  */
210 public void _horizontalScaleNearest4BitAndCLU(U)(ubyte* src, U* dest, U* palette, sizediff_t length, sizediff_t offset, 
211 		const int trfmParam) @nogc pure nothrow {
212 	int trfmParamA = trfmParam;
213 	length <<= 10;
214 	offset <<= 10;
215 	if(trfmParam < 0){
216 		offset += length-1024;
217 		trfmParamA *= -1;
218 	}
219 	while(length > 0){
220 		const ubyte temp = (offset>>>10) & 1 ?  src[offset>>>11] & 0x0F : src[offset>>>11] >> 4;
221 		*dest = palette[temp];
222 		offset += trfmParam;
223 		length -= trfmParam;
224 		dest++;
225 	}
226 }
227 /**
228  * Horizontal scaling and color lookup using nearest integer algorithm for per-line operations. (Old, might get deprecated later on)
229  * Works with 16, 8, 4, and 2 bit datatypes.
230  * Lenght determines the source's length.
231  * trfmParam describes how the transformation is done. 1024 results in the same exact line. Larger values cause shrinkage, smaller omes growth. Negative values cause reflections.
232  * `ArrayType` should be bitleveld's NibbleArray or QuadArray, but also works with regular D arrays.
233  */
234 public void _horizontalScaleNearestAndCLU(PaletteType, ArrayType)(ArrayType src, PaletteType* dest, PaletteType* palette, 
235 		sizediff_t length, sizediff_t offset, const int trfmParam) @nogc pure nothrow {
236 	int trfmParamA = trfmParam;
237 	length <<= 10;
238 	offset <<= 10;
239 	if(trfmParam < 0){
240 		offset += length-1024;
241 		trfmParamA *= -1;
242 	}
243 	while(length > 0){
244 		*dest = palette[src[offset>>>10]];
245 		offset += trfmParam;
246 		length -= trfmParamA;
247 		dest++;
248 	}
249 }
250 /**
251  * Returns the needed length of dest for the given trfmParam if the "nearest integer algorithm" used. (Old, might get deprecated later on)
252  * Works with both horizontal and vertical algorithms.
253  */
254 public size_t scaleNearestLength(size_t origLen, int trfmParam) @nogc @safe pure nothrow {
255 	if(trfmParam < 0)
256 		trfmParam *= -1;
257 	return cast(size_t)(cast(double)origLen * (1024.0 / cast(double)trfmParam));
258 }
259 pure nothrow unittest{
260 	import std.conv : to;
261 	uint[256] a, b;
262 	ubyte[256] c;
263 	NibbleArray d = NibbleArray(c[0..$], 512), f = NibbleArray(c[0..$], 512);
264 	QuadArray e = QuadArray(c[0..$], 1024), g = QuadArray(c[0..$], 1024);
265 	//old scalers
266 	_horizontalScaleNearest(a.ptr, b.ptr, 16, 2048);
267 	_horizontalScaleNearestAndCLU(c.ptr,a.ptr,b.ptr,16,2048);
268 	_horizontalScaleNearest4BitAndCLU(c.ptr,a.ptr,b.ptr,16,0,2048);
269 	_horizontalScaleNearestAndCLU(d, a.ptr, b.ptr, 256, 0, 2048);
270 	_horizontalScaleNearestAndCLU(e, a.ptr, b.ptr, 256, 0, 2048);
271 	
272 	horizontalScaleNearest(a, b, scaleNearestLength(100, 1000), 1000, 20);
273 	horizontalScaleNearest(a, b, scaleNearestLength(100, -1000), -1000, 20);
274 	horizontalScaleNearest(d, f, scaleNearestLength(200, 1000), 1000, 56);
275 	horizontalScaleNearest(d, f, scaleNearestLength(200, -1000), -1000, 58);
276 	horizontalScaleNearest(e, g, scaleNearestLength(200, 1100), 1100, 53);
277 	horizontalScaleNearest(e, g, scaleNearestLength(200, -1100), -1100, 53);
278 
279 	horizontalScaleNearestAndCLU(c, a.ptr, b.ptr, scaleNearestLength(130, 1200), 1200, 94);
280 	horizontalScaleNearestAndCLU(c, a.ptr, b.ptr, scaleNearestLength(130, -1200), -1200, 0);
281 	horizontalScaleNearestAndCLU(d, a.ptr, b.ptr, scaleNearestLength(130, 1200), 1200, 94);
282 	horizontalScaleNearestAndCLU(d, a.ptr, b.ptr, scaleNearestLength(130, -1200), -1200, 94);
283 	horizontalScaleNearestAndCLU(e, a.ptr, b.ptr, scaleNearestLength(130, 1200), 1200, 94);
284 	horizontalScaleNearestAndCLU(e, a.ptr, b.ptr, scaleNearestLength(130, -1200), -1200, 94);
285 	assert(20 == scaleNearestLength(10, 512), "Error while testing function `scaleNearestLength`. Expected value: 20 " ~
286 			"Returned value: " ~ to!string(scaleNearestLength(10, 512)));
287 	assert(20 == scaleNearestLength(10, -512), "Error while testing function `scaleNearestLength`. Expected value: 20 " ~
288 			"Returned value: " ~ to!string(scaleNearestLength(10, -512)));
289 }