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; } } }