TSLtoRGB colorspace conversion - math

Someone knows the correct formula for RGBtoTSL ?
Wikipedia says that the conversion between RGB and TSL is made like this:
But....the reverse transformation presented on Wikipedia https://en.wikipedia.org/wiki/TSL_color_space is incorrect. The results presented by their reverted formula are not correct.
What is the proper formula to revert the transformation back ? I.e: transforming from TSL to RGB ?
Serg, here is some attempts we are making to try the revert operation :) Examples in Asm (RosAsm assembler).
; used variables
[Float_YIQ_Red_M1: R$ 0.29889531
Float_YIQ_Green_M2: R$ 0.58662247
Float_YIQ_Blue_M3: R$ 0.11448223]
[Float_minusOneThird: R$ (-1/3)] ; error in RosAsm. It can´ see "-(1/3)"
[r1Factor: R$ 0]
[g1Factor: R$ 0]
[rFactor: R$ 0]
[gFactor: R$ 0]
[TmpFloat: R$ 0]
[Float_Var95: R$ (9/5)]
[Float_Var14: R$ (1/4)]
[Float_Var34: R$ (3/4)]
[Float_VarOne2Pi: R$ 0.159154943091895335768883763372514362034459645740456448747] ; 1/(2*pi)
[xFactor: R$ 0]
[kFactor: R$ 0]
[Float_Half: R$ 0.5]
[Float_Var59: R$ (5/9)]
[Float_Var53SquareRoot: R$ 0.745355992499929898803057889577092078480206119870508574756] ; sqrt(5)/3
[Float_OneThird: R$ (1/3)]
[FloatTSLVar1: R$ 0.184413]
[FloatTSLVar2: R$ 0.4721403]
[Float_TempRed: R$ 0]
[Float_TempGreen: R$ 0]
[Float_TempBlue: R$ 0]
[Float_Two_PI: R$ 6.283185307179586476925286766559005768394338798750211641948]
[Float_Five: R$ 5.0]
; -----------------------------------------------------------
Proc RGBtoTSL2a:
Arguments #PixelSrc, #Tint, #Saturation, #Light
Local #TempRed, #TempGreen, #TempBlue, #TempCmax, #TempCmin, #TempDelta_Max, #TempVar
Uses eax, ecx, esi, edi, edx, ebx
finit
; RGB from 0 to 255
; get RED
mov esi D#PixelSrc | movzx edi B$esi+ARGB.RedDis | lea ecx D#TempRed | mov D$ecx edi | mov eax edi
; get GREEN
mov esi D#PixelSrc | movzx edi B$esi+ARGB.GreenDis | lea ecx D#TempGreen | mov D$ecx edi | add eax edi
; get BLUE
mov esi D#PixelSrc | movzx edi B$esi+ARGB.BlueDis | lea ecx D#TempBlue | mov D$ecx edi | add eax edi
If eax = 0
fldz
mov esi D#Light | fst R$esi
mov esi D#Saturation | fst R$esi
mov esi D#Tint | fstp R$esi
ExitP
End_If
; Get Min and Max of RGB (just to get MIn/Max RGB to check for grey
lea ebx D#TempCmin | mov D$ebx 0
lea eax D#TempCmax | mov D$eax 0
call GetRGB_MinMax esi, ebx, eax
; Get Delta
; max = edx
; min = ecx
mov edx D#TempCmax | lea eax D#TempCmax | mov D$eax edx
mov ecx D#TempCmin | lea eax D#TempCmin | mov D$eax ecx
lea ebx D#TempDelta_Max | mov D$ebx 0; cmax-cmin
mov eax D#TempCmax | sub eax D#TempCMin | mov D$ebx eax
; 1st compute Light (Normalized)
mov esi D#Light
fild F#TempRed | fmul R$Float_YIQ_Red_M1
fild F#TempGreen | fmul R$Float_YIQ_Green_M2
faddp ST1 ST0
fild F#TempBlue | fmul R$Float_YIQ_Blue_M3
faddp ST1 ST0 | fmul R$FloatOne_255 | fstp R$esi
..If D#TempDelta_Max = 0; This is gray, no tint, no saturation
fldz
mov esi D#Saturation | fst R$esi
mov esi D#Tint | fstp R$esi
..Else
; 2nd compute Saturation
; get rfactor
lea edi D#TempVar
fild F#TempRed | fiadd F#TempGreen | fiadd F#TempBlue | fistp F$edi
fild F#TempRed | fidiv F$edi | fadd R$Float_minusOneThird | fstp R$rFactor
; get gfactor
fild F#TempGreen | fidiv F$edi | fadd R$Float_minusOneThird | fstp R$gFactor
mov esi D#Saturation
fld R$rFactor | fmul ST0 ST0
fld R$gFactor | fmul ST0 ST0
faddp ST1 ST0 | fmul R$Float_Var95
fsqrt | fstp R$esi
; 3rd compute Tint
mov esi D#Tint
fldz | fstp R$esi
fld R$rFactor | fdiv R$gFactor | fstp R$TmpFloat
Fpu_If R$gFactor > R$Float_Zero
fld R$TmpFloat | fld1 | fpatan | fmul R$Float_VarOne2Pi | fadd R$Float_Var14 | fstp R$esi ; result in radian
Fpu_Else_If R$gFactor < R$Float_Zero
fld R$TmpFloat | fld1 | fpatan | fmul R$Float_VarOne2Pi | fadd R$Float_Var34 | fstp R$esi ; result in radian
Fpu_End_If
..End_If
EndP
and the reverted function
Proc TSLtoRGB2a:
Arguments #Tint, #Saturation, #Light, #Red, #Green, #Blue
Local #TempRed, #TempGreen, #TempBlue
Uses eax, ecx, esi, edi, ebx
; check for light conditions
mov esi D#Light
Fpu_If R$esi = R$Float_Zero
mov edi D#Red | mov D$edi 0
mov edi D#Green | mov D$edi 0
mov edi D#Blue | mov D$edi 0
ExitP
Fpu_End_If
mov edi D#Saturation
mov esi D#Tint
;.Fpu_If_Or R$esi = R$Float_Zero, R$edi = R$Float_Zero ; Grey color found
; Not true. Tint = 0 not necessarily means grey. What means grey is
; saturation = 0
; see Serg examples:(44, 22, 0) and (0, 32, 64)
.Fpu_If R$edi = R$Float_Zero ; Grey color found. Seems to be only this case for finding grey.
mov ebx D#Light
fld R$ebx | fst R$Float_TempRed | fst R$Float_TempGreen | fstp R$Float_TempBlue
fld1 | fstp R$kFactor
.Fpu_Else
; compute -1 * cot(2*pi*Tint)
fld R$esi
fmul R$Float_Two_PI
fptan
fdivrp ST0 ST1
fmul R$Float_Minus_one
fstp R$xFactor
fld1 | fld R$xFactor | fmul ST0 ST0 | fadd R$Float_One | fdivp ST0 ST1 | fmul R$Float_Five
fsqrt | fmul R$Float_OneThird | fmul R$edi | fstp R$g1Factor
Fpu_If R$esi > R$Float_Half
fld R$g1Factor | fmul R$Float_Minus_One | fstp R$g1Factor
Fpu_End_If
fld R$g1Factor | fmul R$xFactor | fstp R$r1Factor
fld R$r1Factor | fadd R$Float_OneThird | fstp R$Float_TempRed
fld R$g1Factor | fadd R$Float_OneThird | fstp R$Float_TempGreen
fld1 | fsub R$Float_TempRed | fsub R$Float_TempGreen | fstp R$Float_TempBlue
; Compte KFactor
mov ebx D#Light
fld R$ebx
fld R$Float_TempRed | fmul R$FloatTSLVar1
fld R$Float_TempGreen | fmul R$FloatTSLVar2
faddp ST1 ST0 | fadd R$Float_YIQ_Blue_M3
fdivp ST1 ST0
fstp R$kFactor
.Fpu_End_If
mov edi D#Red | fld R$kFactor | fmul R$Float_TempRed | fmul R$Float255 | fistp F$edi
If D$edi <s 0
mov D$edi 0
Else_If D$edi > 255
mov D$edi 255
End_If
mov edi D#Green | fld R$kFactor | fmul R$Float_TempGreen | fmul R$Float255 | fistp F$edi
If D$edi <s 0
mov D$edi 0
Else_If D$edi > 255
mov D$edi 255
End_If
mov edi D#Blue | fld R$kFactor | fmul R$Float_TempBlue | fmul R$Float255 | fistp F$edi
If D$edi <s 0
mov D$edi 0
Else_If D$edi > 255 ; something still is incorrect.
; See what happens when:
; Tint = 0.85764542031
; Saturation: 0.0315199340953953
; Light = 0.8826082107058823529411764705882352941176
mov D$edi 255
End_If
EndP

Code was updated with a "negative zero" trick to handle T = 0 case
It is too big for a comment so I'll put it as an answer. I think that the formula at the wiki is correct and works OK in cases except 2 * G == R + B (i.e. except g' == 0 or in other words T == 0) in which case backward transformation is not unique mathematically. But in practice since we have both positive and negative zero on our hardware, we can use them do distinguish those bad cases.
Here is almost literal transformation of that formula into a JS:
function rgbFromTsl(T, S, L) {
if (arguments.length == 1) {
T = arguments[0][0];
S = arguments[0][1];
L = arguments[0][2];
}
if (L == 0)
return [0, 0, 0];
var r1, g1, x;
var r, g, b;
// For T == 0 reverse solution is not unique
// and we use a trick with "negative zero" to distinguish them
if (isNegativeZero(T)) {
g1 = 0;
r1 = -Math.sqrt(5.0) / 3.0 * S;
}
else if (T == 0) {
g1 = 0;
r1 = Math.sqrt(5.0) / 3.0 * S;
}
else {
x = -1.0 / Math.tan(2 * Math.PI * T);
g1 = Math.sqrt(5.0 / (1 + x * x)) / 3.0 * S;
if (T > 0.5)
g1 = -g1;
r1 = x * g1;
}
r = r1 + 1.0 / 3;
g = g1 + 1.0 / 3;
b = (1 - r - g);
var k = L / (0.185 * r + 0.473 * g + 0.114);
return [k * r, k * g, k * b];
}
and for the example you provided
try inputing this: Red = 128, Green = 22, Blue = 37 and once you find the TSL, try the reverse formula and you will see that it produced incorrect values (one of them negative, btw, which is impossible on rgb).
it seems to produce back the original R, G, B values with a quite good precision.
Here is a full runnable code that you can run just in place and play with some values. Let me know if you find some other bad cases except for 2*G = R+B aka T = 0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TSL/RGB</title>
<style>
div {
padding: 5px;
}
#container {
width: 550px;
}
input {
width: 150px;
}
#lc, #rc {
width: 220px;
border: dashed;
}
#lc {
float: left;
}
#rc {
float: right;
}
</style>
</head>
<body>
<div id="container">
<div id="lc">
<h1>RGB to TSL</h1>
<form name="RGBsrc">
<div id="RGBsrc">
<div>
<label for="Rsrc">R=</label> <input type="number" id="Rsrc" value="128">
</div>
<div>
<label for="Gsrc">G=</label> <input type="number" id="Gsrc" value="22">
</div>
<div>
<label for="Bsrc">B=</label> <input type="number" id="Bsrc" value="37">
</div>
</div>
<div id="TSLdst">
<div>
<label for="Tdst">T=</label> <input type="number" id="Tdst" disabled="disabled">
</div>
<div>
<label for="Sdst">S=</label> <input type="number" id="Sdst" disabled="disabled">
</div>
<div>
<label for="Ldst">L=</label> <input type="number" id="Ldst" disabled="disabled">
</div>
</div>
<div><button id="btnRgbToTsl">RGB to TSL</button></div>
<div><button id="btnCopyTsl">Copy result to TSL</button></div>
</form>
</div>
<div id="rc">
<h1>TSL to RGB</h1>
<form name="TSLsrc">
<div id="TSLsrc">
<div>
<label for="Tsrc">T=</label> <input type="number" id="Tsrc" value="0.5876633198969483">
</div>
<div>
<label for="Ssrc">S=</label> <input type="number" id="Ssrc" value="0.552900835318196">
</div>
<div>
<label for="Lsrc">L=</label> <input type="number" id="Lsrc" value="55.403999999999996">
</div>
</div>
<div id="RGBdst">
<div>
<label for="Rdst">R=</label> <input type="number" id="Rdst" disabled="disabled">
</div>
<div>
<label for="Gdst">G=</label> <input type="number" id="Gdst" disabled="disabled">
</div>
<div>
<label for="Bdst">B=</label> <input type="number" id="Bdst" disabled="disabled">
</div>
</div>
<div>
<label for="roundRgbDst">Round RGB</label> <input type="checkbox" id="roundRgbDst" checked="checked" style="width: 50px;" >
</div>
<button id="btnTslToRgb">TSL to RGB</button>
</form>
</div>
</div>
<script>
function isNegativeZero(v) {
return 1 / v === -Infinity;
}
function floatToStrSmart(v) {
if (isNegativeZero(v))
return "-0.0";
else
return "" + v;
}
function parseFloatSmart(s) {
if ((s === "-0") || (s === "-0.0"))
return -0.0;
else
return parseFloat(s)
}
function tslFromRgb(R, G, B) {
if (arguments.length == 1) {
R = arguments[0][0];
G = arguments[0][1];
B = arguments[0][2];
}
if ((R == 0) && (G == 0) && (B == 0)) {
return [0, 0, 0];
}
var L = 0.299 * R + 0.587 * G + 0.114 * B;
var r1 = R / (R + G + B) - 1.0 / 3;
var g1 = G / (R + G + B) - 1.0 / 3;
var S = Math.sqrt(9.0 / 5 * (r1 * r1 + g1 * g1));
var T;
if (g1 == 0) {
if (R < B)
T = -0.0;
else
T = 0.0
}
else {
T = Math.atan(r1 / g1) / Math.PI / 2 + 0.25;
if (g1 < 0)
T += 0.5;
}
return [T, S, L]
}
function rgbFromTsl(T, S, L) {
if (arguments.length == 1) {
T = arguments[0][0];
S = arguments[0][1];
L = arguments[0][2];
}
if (L == 0)
return [0, 0, 0];
var r1, g1, x;
var r, g, b;
// For T == 0 reverse solution is not unique
// and we use a trick with "negative zero" to distinguish them
if (isNegativeZero(T)) {
g1 = 0;
r1 = -Math.sqrt(5.0) / 3.0 * S;
}
else if (T == 0) {
g1 = 0;
r1 = Math.sqrt(5.0) / 3.0 * S;
}
else {
x = -1.0 / Math.tan(2 * Math.PI * T);
g1 = Math.sqrt(5.0 / (1 + x * x)) / 3.0 * S;
if (T > 0.5)
g1 = -g1;
r1 = x * g1;
}
r = r1 + 1.0 / 3;
g = g1 + 1.0 / 3;
b = (1 - r - g);
var k = L / (0.185 * r + 0.473 * g + 0.114);
return [k * r, k * g, k * b];
}
document.getElementById('btnRgbToTsl').addEventListener('click', function (e) {
e.preventDefault();
var r = parseInt(document.getElementById('Rsrc').value);
var g = parseInt(document.getElementById('Gsrc').value);
var b = parseInt(document.getElementById('Bsrc').value);
document.getElementById('Rsrc').value = r;
document.getElementById('Gsrc').value = g;
document.getElementById('Bsrc').value = b;
var tsl = tslFromRgb(r, g, b);
var t = tsl[0];
var s = tsl[1];
var l = tsl[2];
document.getElementById('Tdst').value = floatToStrSmart(t);
document.getElementById('Sdst').value = s;
document.getElementById('Ldst').value = l;
});
document.getElementById('btnCopyTsl').addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('Tsrc').value = document.getElementById('Tdst').value;
document.getElementById('Ssrc').value = document.getElementById('Sdst').value;
document.getElementById('Lsrc').value = document.getElementById('Ldst').value;
});
document.getElementById('btnTslToRgb').addEventListener('click', function (e) {
e.preventDefault();
var t = parseFloatSmart(document.getElementById('Tsrc').value);
var s = parseFloat(document.getElementById('Ssrc').value);
var l = parseFloat(document.getElementById('Lsrc').value);
document.getElementById('Tsrc').value = floatToStrSmart(t);
document.getElementById('Ssrc').value = s;
document.getElementById('Lsrc').value = l;
var rgb = rgbFromTsl(t, s, l);
var r = rgb[0];
var g = rgb[1];
var b = rgb[2];
if (document.getElementById('roundRgbDst').checked) {
document.getElementById('Rdst').value = (r + .1) | 0; //round to int
document.getElementById('Gdst').value = (g + .1) | 0; //round to int;
document.getElementById('Bdst').value = (b + .1) | 0; //round to int;;
}
else {
document.getElementById('Rdst').value = r;
document.getElementById('Gdst').value = g;
document.getElementById('Bdst').value = b;
}
});
</script>
</body>
</html>

Related

Array of pointers, VirtualAlloc and RtlMoveMemory. MASM, some kind of problem

Does anybody know how to fix the addElement function so it ends up as another element in the array. The idea is a dynamic array, where arrayPtr is a pointer to the first element, then new elements can be added dynamically and kept track of by increasing the arrayPtr value. So in-fact I think what it would end up being is an array of pointers to DbRecord structs in memory. Allocated by VirtualAlloc and copied by RtlMoveMemory. I am kinda of hung up on RtlMoveMemeory line. I feel like my line of thinking is correct.
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
addElement PROTO: ptr DbRecord
.data?
DbRecord struct
Id dd ?
WordOne db 32 dup(?) ; db is define byte, set value of byte
WordTwo db 32 dup(?)
WordThree db 32 dup(?)
Year dd ?
DbRecord ends
arrayPtr dd ? ; pointer in memory to start of array
newElementPointer DbRecord <>
hStdOut dd ?
bytesWritten dd ?
.data
arrayCount dd 0
hello db 'Hello World!', 0
.code
main proc
LOCAL DbRecord01:DbRecord
mov [DbRecord01.Id], 1;
; any other way than one character at a time?
mov byte ptr [DbRecord01.WordOne], 'D'
mov byte ptr [DbRecord01.WordOne + 1], 'o'
mov byte ptr [DbRecord01.WordOne + 2], 'g'
mov byte ptr [DbRecord01.WordOne + 3], 0
mov byte ptr [DbRecord01.WordTwo], 'C'
mov byte ptr [DbRecord01.WordTwo + 1], 'a'
mov byte ptr [DbRecord01.WordTwo + 2], 't'
mov byte ptr [DbRecord01.WordTwo + 3], 0
mov byte ptr [DbRecord01.WordThree], 'E'
mov byte ptr [DbRecord01.WordThree + 1], 'y'
mov byte ptr [DbRecord01.WordThree + 2], 'e'
mov byte ptr [DbRecord01.WordThree + 3], 0
mov [DbRecord01.Year], 2022;
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov [hStdOut], eax
invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL
invoke addElement, addr DbRecord01
ret
main endp
addElement proc DbRecordPointer: ptr DbRecord
invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE ; I beleive store a memory address in eax
invoke RtlMoveMemory, DbRecord ptr [eax], DbRecordPointer, sizeof DbRecord ; but how to use that memory address here?
ret
addElement endp
end main
EDIT/Update:
So yes part of the answer is just passing in eax.
I am here now
How do I get the value of eax ("memory location from VirtualAlloc",) where data was copied into arrayPtr (arrayPtr + count * sizeof DbRecord)
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
addElement PROTO: ptr DbRecord
.data?
DbRecord struct
Id dd ?
WordOne db 32 dup(?) ; db is define byte, set value of byte
WordTwo db 32 dup(?)
WordThree db 32 dup(?)
Year dd ?
DbRecord ends
arrayPtr dword ? ; pointer in memory to start of array
; newElementPointer DbRecord <>
hStdOut dd ?
bytesWritten dd ?
.data
arrayCount dd 0
hello db 'Hello World!', 0
.code
main proc
LOCAL DbRecord01:DbRecord
mov [DbRecord01.Id], 1;
; any other way than one character at a time?
mov byte ptr [DbRecord01.WordOne], 'D'
mov byte ptr [DbRecord01.WordOne + 1], 'o'
mov byte ptr [DbRecord01.WordOne + 2], 'g'
mov byte ptr [DbRecord01.WordOne + 3], 0
mov byte ptr [DbRecord01.WordTwo], 'C'
mov byte ptr [DbRecord01.WordTwo + 1], 'a'
mov byte ptr [DbRecord01.WordTwo + 2], 't'
mov byte ptr [DbRecord01.WordTwo + 3], 0
mov byte ptr [DbRecord01.WordThree], 'E'
mov byte ptr [DbRecord01.WordThree + 1], 'y'
mov byte ptr [DbRecord01.WordThree + 2], 'e'
mov byte ptr [DbRecord01.WordThree + 3], 0
mov [DbRecord01.Year], 2022;
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov [hStdOut], eax
invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL
invoke addElement, addr DbRecord01
ret
main endp
addElement proc uses edx DbRecordPointer: ptr DbRecord
Local newElementPointer: Dword
invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE ; I beleive store a memory address in eax
mov newElementPointer, eax
;invoke RtlMoveMemory, newElementPointer , DbRecordPointer, sizeof DbRecord ; but how to use that memory address here?
invoke RtlMoveMemory, eax , DbRecordPointer, sizeof DbRecord
mov edx, arrayCount
inc edx
mov arrayCount, edx
;mov dword ptr [arrayPtr+arrayCount], eax
ret
addElement endp
end main

Swapping Elements of a Slice (in-place)

I have posted my solution in the answers below.
The question will not be updated with even more code to not further increase clutter.
I'm trying to rotate all elements in a Vec<Vec<T>> clockwise.
The vector is guaranteed to be square, as in v.len() == v[0].len().
The idea is to
find all elements that are equivalent under rotational symmetry to
v's center
swap these elements in place, using std::mem::swap
My current code does not change the state of the vec. How do I fix this?
fn rotate<T>(v: &mut Vec<Vec<T>>) {
// swap elements equivalent to position i on each ring r
// limit l = side length of current ring
//
// + 0 - - - - + r = 0 -> l = 6
// | + 1 - - + | r = 1 -> l = 4
// | | + 2 + | | r = 2 -> l = 2
// | | | | | |
// | | + - + | | swap:
// | + - - - + | a b c d
// + - - - - - + > b a c d
// > c a b d
// > d a b c
for r in 0..((v.len() + 1) / 2 {
let l = v.len() - 1 - r;
for i in r..l {
let mut a = & pieces[ r ][ r+i ];
let mut b = & pieces[ r+i ][ l-r ];
let mut c = & pieces[ l-r ][l-r-i];
let mut d = & pieces[l-r-i][ r ];
_rot_cw(&mut a, &mut b, &mut c, &mut d)},
}
}
fn _rot_cw<T>(a: &mut T, b: &mut T, c: &mut T, d: &mut T) {
//rotates a->b, b->c, c->d, d->a
std::mem::swap(a, b);
std::mem::swap(a, c);
std::mem::swap(a, d);
}
}
Edit:
Fixed minor issues in the original code above, thanks to #Jmb.
Here's my current code, again running into borrowing issues:
fn rotate_square_slice<T>(slice: &mut Vec<T>, rows: usize) {
for r in 0..(slice.len()+1)/2 {
let l = slice.len() -1 - r;
for i in r..l {
let a = &mut slice.get_mut(rows * r + r+i ).unwrap();
let b = &mut slice.get_mut(rows * (r+i) + l-r ).unwrap();
let c = &mut slice.get_mut(rows * (l-r) + l-r-i).unwrap();
let d = &mut slice.get_mut(rows * (l-r-i) + r ).unwrap();
std::mem::swap(a, b);
std::mem::swap(a, c);
std::mem::swap(a, d);
}
}
}
Swapping elements in a slice can be done by using the slice's swap() method.
Solving that problem, the code now looks like this:
fn rotate_square_slice<T>(slice: &mut [T], size: usize) {
for r in 0..(size + 1) / 2 {
let l = size - 1 - r;
for i in r..l {
// b, c & d are the indices with rotational symmetry to a,
// shifted by 90°, 180° & 270° respectively
let a = size * r + r+i ;
let b = size * (r+i) + l-r ;
let c = size * (l-r) + l-r-i;
let d = size * (l-r-i) + r ;
slice.swap(a, b);
slice.swap(a, c);
slice.swap(a, d);
}
}
}
I have, however, run into an issue with correctly indexing the slice. The question can be found here:
Rotational Symmetry Indexing in a 1D "Square" Array

Returning a value pointed to by a pointer in x86 NASM

I'm trying to write a function in x86 NASM assembly that takes a pointer to a structure (structure contains pointer to a buffer) and 2 ints (x,y) which then computes the address of the byte containing (x,y) and returns the value in this address. (The buffer contains a bmp file) I have this function written in C and it works fine.
C function
int loadByte(imgInfo* pImg, int x, int y)
{
unsigned char *pPix = pImg->pImg + (((pImg->width + 31) >> 5) << 2) * y + (x >> 3);
return *pPix;
}
x86 function
load_byte:
push ebp ; prologue
mov ebp, esp
lea ecx, [ebp + 8]
mov ecx, [ecx] ; ecx = &imgInfo
mov eax, [ecx+0] ; eax = width
add eax, 31 ; eax = width + 31
sar eax, 5 ; eax = (width + 31) >> 5
sal eax, 2 ; eax = ((width + 31) >> 5) << 2
mul DWORD [ebp+16] ; eax * y
mov edx, [ebp+12] ; edx = x
sar edx, 3 ; edx = x>>3
add eax, edx ; eax = ((width + 31) >> 5) << 2 * y + (x >> 3)
mov edx, [ecx+8] ; edx = &pImg
add eax, edx
mov eax, [eax]
pop ebp ; epilogue
ret
I tried checking if the address computed in both functions is the same so I changed the return of C to return pPix and commented the line mov eax, [eax] in x86 and to my surprise both functions returned the same number but in the unchanged form (as in the code above) the x86 function always returns -1 for some reason. Is return *pPix not equivalent to mov eax, [eax]? What is wrong with my reasoning?
imgInfo struct
typedef struct
{
int width, height;
unsigned char* pImg; //buffer
int cX, cY;
int col;
} imgInfo;
load_byte C declaration
extern int load_byte(imgInfo* pInfo, int x, int y);

I have written a path tracer using julia programming language but i think it is slow

I have changed my post and posted the whole of my code! Could someone tell me how can I optimize it?
import Base: *, +, -, /, ^
using Images
const Π = convert(Float64, π)
#define vector
mutable struct Vec3
x::Float64
y::Float64
z::Float64
end
function +(u::Vec3, v::Vec3)
Vec3(u.x+v.x, u.y+v.y, u.z+v.z)
end
function -(u::Vec3, v::Vec3)
Vec3(u.x-v.x, u.y-v.y, u.z-v.z)
end
function /(u::Vec3, v::Float64)
Vec3(u.x/v, u.y/v, u.z/v)
end
function *(u, v::Vec3)
if typeof(u) == Float64
Vec3(u*v.x, u*v.y, u*v.z)
elseif typeof(u) == Vec3
Vec3(u.x*v.x, u.y*v.y, u.z*v.z)
end
end
function ^(u::Vec3, v::Float64)
Vec3(u.x^v, u.y^v, u.z^v)
end
function dot(u::Vec3, v::Vec3)
u.x*v.x + u.y*v.y + u.z*v.z
end
function normalize(u::Vec3)
u/sqrt(dot(u,u))
end
function cross(u::Vec3, v::Vec3)
Vec3(u.y*v.z - v.y*u.z, u.z*v.x - v.z*u.x, u.x*v.y - v.x*u.y)
end
function gamma(u::Vec3)
Vec3(u.x^(1/2.2), u.y^(1/2.2), u.z^(1/2.2))
end
function clamp(u::Vec3)
u.x = u.x <= 1 ? u.x : 1
u.y = u.y <= 1 ? u.y : 1
u.z = u.z <= 1 ? u.z : 1
u
end
#define ray
struct Ray
s::Vec3
d::Vec3
end
#define planes
struct xyRect
z; x1; x2; y1; y2::Float64
normal; emittance; reflectance::Vec3
isLight::Bool
end
struct xzRect
y; x1; x2; z1; z2::Float64
normal; emittance; reflectance::Vec3
isLight::Bool
end
struct yzRect
x; y1; y2; z1; z2::Float64
normal; emittance; reflectance::Vec3
isLight::Bool
end
#define sphere
mutable struct Sphere
radius::Float64
center; normal; emittance; reflectance::Vec3
isLight::Bool
end
#define empty object
struct Empty
normal; emittance; reflectance::Vec3
end
#define surfaces
Surfaces = Union{xyRect, xzRect, yzRect, Sphere}
#define intersection function
function intersect(surface::Surfaces, ray::Ray)
if typeof(surface) == xyRect
t = (surface.z - ray.s.z)/ray.d.z
if surface.x1 < ray.s.x + t*ray.d.x < surface.x2 && surface.y1 < ray.s.y + t*ray.d.y < surface.y2 && t > 0
t
else
Inf
end
elseif typeof(surface) == xzRect
t = (surface.y - ray.s.y)/ray.d.y
if surface.x1 < ray.s.x + t*ray.d.x < surface.x2 && surface.z1 < ray.s.z + t*ray.d.z < surface.z2 && t > 0
t
else
Inf
end
elseif typeof(surface) == yzRect
t = (surface.x - ray.s.x)/ray.d.x
if surface.y1 < ray.s.y + t*ray.d.y < surface.y2 && surface.z1 < ray.s.z + t*ray.d.z < surface.z2 && t > 0
t
else
Inf
end
elseif typeof(surface) == Sphere
a = dot(ray.d, ray.d)
b = 2dot(ray.d, ray.s - surface.center)
c = dot(ray.s - surface.center, ray.s - surface.center) - surface.radius*surface.radius
Δ = b*b - 4*a*c
if Δ > 0
Δ = sqrt(Δ)
t1 = 0.5(-b-Δ)/a
t2 = 0.5(-b+Δ)/a
if t1 > 0
surface.normal = normalize(ray.s + t1*ray.d - surface.center)
t1
elseif t2 > 0
surface.normal = normalize(ray.s + t2*ray.d - surface.center)
t2
else
Inf
end
else
Inf
end
end
end
#define nearest function
function nearest(surfaces::Array{Surfaces, 1}, ray::Ray, tMin::Float64)
hitSurface = Empty(Vec3(0,0,0), Vec3(0,0,0), Vec3(0,0,0))
for surface in surfaces
t = intersect(surface, ray)
if t < tMin
tMin = t
hitSurface = surface
end
end
tMin, hitSurface
end
#cosine weighted sampling of hemisphere
function hemiRand(n::Vec3)
ξ1 = rand()
ξ2 = rand()
x = cos(2π*ξ2)*sqrt(ξ1)
y = sin(2π*ξ2)*sqrt(ξ1)
z = sqrt(1-ξ1)
r = normalize(Vec3(2rand()-1, 2rand()-1, 2rand()-1))
b = cross(n,r)
t = cross(n,b)
Vec3(x*t.x + y*b.x + z*n.x, x*t.y + y*b.y + z*n.y, x*t.z + y*b.z + z*n.z)
end
#trace the path
function trace(surfaces::Array{Surfaces, 1}, ray::Ray, depth::Int64, maxDepth::Int64)
if depth >= maxDepth
return Vec3(0,0,0)
end
t, material = nearest(surfaces, ray, Inf)
if typeof(material) == Empty
return Vec3(0,0,0)
end
if material.isLight == true
return material.emittance
end
ρ = material.reflectance
BRDF = ρ/Π
n = material.normal
R = hemiRand(n)
In = trace(surfaces, Ray(ray.s + t*ray.d, R), depth+1, maxDepth)
return Π*BRDF*In
end
#define camera
struct Camera
eye; v_up; N::Vec3
fov; aspect; distance::Float64
end
#render function
function render(surfaces::Array{Surfaces,1},camera::Camera,xRes::Int64,yRes::Int64,numSamples::Int64,maxDepth::Int64)
n = normalize(camera.N)
e = camera.eye
c = e - camera.distance*n
θ = camera.fov*(π/180)
H = 2*camera.distance*tan(θ/2)
W = H*camera.aspect
u = normalize(cross(camera.v_up,n))
v = cross(n,u)
img = zeros(3, xRes, yRes)
pixHeight = H/yRes
pixWidth = W/xRes
L = c - 0.5*W*u - 0.5*H*v
for i=1:xRes
for j=1:yRes
cl = Vec3(0,0,0)
for s=1:numSamples
pt = L + (i-rand())*pixWidth*u + (yRes-j+rand())*pixHeight*v
cl = cl + trace(surfaces, Ray(e, pt-e), 0, maxDepth)
end
cl = gamma(clamp(cl/convert(Float64, numSamples)))
img[:,j,i] = [cl.x, cl.y, cl.z]
end
end
img
end
#the scene
p1 = xzRect(1.,0.,1.,-1.,0.,Vec3(0,-1,0),Vec3(0,0,0),Vec3(0.75,0.75,0.75),false)
p2 = xzRect(0.,0.,1.,-1.,0.,Vec3(0,1,0),Vec3(0,0,0),Vec3(0.75,0.75,0.75),false)
p3 = xyRect(-1.,0.,1.,0.,1.,Vec3(0,0,1),Vec3(0,0,0),Vec3(0.75,0.75,0.75),false)
p4 = yzRect(0.,0.,1.,-1.,0.,Vec3(1,0,0),Vec3(0,0,0),Vec3(0.75,0.25,0.25),false)
p5 = yzRect(1.,0.,1.,-1.,0.,Vec3(-1,0,0),Vec3(0,0,0),Vec3(0.25,0.25,0.75),false)
p6 = xzRect(0.999,0.35,0.65,-0.65,-0.35,Vec3(0,-1,0),Vec3(18,18,18),Vec3(0,0,0),true)
s1 = Sphere(0.15,Vec3(0.3,0.15,-0.6),Vec3(0,0,0),Vec3(0,0,0),Vec3(0.75,0.75,0.75),false)
surfs = Surfaces[p1,p2,p3,p4,p5,p6,s1]
cam = Camera(Vec3(0.5,0.5,2),Vec3(0,1,0),Vec3(0,0,1),28.07,1,2)
#time image = render(surfs, cam, 400, 400, 1, 4);
colorview(RGB, image)
I need to know why my code is bad and slow. I am a beginner programmer and I don't have enough experience. My path tracer scene contains 7 objects, its maximum depth is 4, and it takes more than 2 seconds to generate an image of size 400*400. I think It shouldn't be that slow because my cpu is core i7 4770.
Sorry for changing my post.
To start with,
struct yzRect
x; y1; y2; z1; z2::Float64
normal; emittance; reflectance::Vec3
isLight::Bool
end
ends up only applying the type to the last variable for each line:
julia> fieldtypes(yzRect)
(Any, Any, Any, Any, Float64, Any, Any, Vec3, Bool)
so Julia will basically not know about any types in your structs which slows things down.
Also, your Vec3 should really be immutable and you then just create new instances of it when you want to "modify" the Vector.
There might be many more issues but those are two standing out.
Reading through https://docs.julialang.org/en/v1/manual/performance-tips/index.html and applying the guidelines in there is strongly recommended when analyzing performance.

How to decrypt a shifted message with XOR?

I have a message m that I encrypt by the code
c = m xor [m<<6] xor [m<<10]
(m<<x means that I shift it by x bits - eg 1001<<2 = 0100)
I need to decrypt this message.
I have tried code like:
c xor [c<<6] xor [c<<10]
OR
c xor [c>>6] xor [c>>10]
but none of them worked.
The function c = m xor [m<<6] xor [m<<10] is a bijection (i.e. reversible on a given modulo 2n). So you're good to go...
Just piece-wise de-XOR the value by 6 and 10 bits, starting from the lowest bits.
int main() {
uint32_t n = 0xfffffff;
for (uint32_t m = 0; m < n; ++m) {
uint32_t c = m ^ (m << 6) ^ (m << 10); // c = encrypted value
uint32_t x = c;
x ^= ((x & 0x3F) << 6);
x ^= ((x & 0x3FF) << 10);
x ^= ((x & 0xFC0) << 6);
x ^= ((x & 0x3F000) << 6);
x ^= ((x & 0xFFC00) << 10);
x ^= ((x & 0xFC0000) << 6);
x ^= ((x & 0x3F000000) << 6);
x ^= ((x & 0x3FF00000) << 10);
if (m != x) {
printf("Mismatch: %X > %X > %X\n", m, c, x); // should never happen
break;
}
}
}

Resources