Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
imageProcessing
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ecole
imageProcessing
Commits
ed995b81
Commit
ed995b81
authored
Feb 13, 2018
by
BORNON Théophile
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Display++
parent
801cdd6e
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
244 additions
and
126 deletions
+244
-126
Display.cs
S04_Projet/Display.cs
+175
-0
MyImage.cs
S04_Projet/MyImage.cs
+33
-86
Pixel.cs
S04_Projet/Pixel.cs
+29
-0
Program.cs
S04_Projet/Program.cs
+6
-40
S04_Projet.csproj
S04_Projet/S04_Projet.csproj
+1
-0
No files found.
S04_Projet/Display.cs
0 → 100644
View file @
ed995b81
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.IO
;
using
System.Threading
;
namespace
S04_Projet
{
public
class
Display
{
/// <summary>
/// Liste des opérations possibles
/// </summary>
public
enum
Operation
{
Rotate90
,
Rotate180
,
Rotate270
,
GrayScaleLinear
,
GrayScaleLuminosity
,
Save
}
/// <summary>
/// Charge l'image
/// </summary>
/// <returns>Retourne l'objet contenant l'image chargée</returns>
public
static
MyImage
LoadImage
()
{
string
path
;
bool
validPath
=
false
;
byte
[]
file
=
new
byte
[
0
];
do
{
Console
.
WriteLine
(
"Veuillez saisir le nom du fichier à traiter :"
);
path
=
Console
.
ReadLine
();
try
{
file
=
File
.
ReadAllBytes
(
path
);
}
catch
(
Exception
e
)
{
Console
.
ForegroundColor
=
ConsoleColor
.
Red
;
Console
.
WriteLine
(
"Une erreur est survenue : "
+
e
.
Message
);
Console
.
ForegroundColor
=
ConsoleColor
.
White
;
}
if
(
file
.
Length
!=
0
)
validPath
=
true
;
}
while
(!
validPath
);
Console
.
Clear
();
MyImageThread
myImageThread
=
new
MyImageThread
(
file
);
Thread
imageThread
=
new
Thread
(
new
ThreadStart
(
myImageThread
.
ThreadLoop
));
imageThread
.
Start
();
Console
.
Write
(
"Traitement en cours de l'image."
);
while
(
imageThread
.
IsAlive
)
{
Thread
.
Sleep
(
250
);
Console
.
Write
(
"."
);
}
Console
.
Clear
();
Console
.
ForegroundColor
=
ConsoleColor
.
Green
;
Console
.
WriteLine
(
"Image chargée en {0}ms\n"
,
myImageThread
.
loadTime
);
Console
.
ForegroundColor
=
ConsoleColor
.
Cyan
;
Console
.
WriteLine
(
myImageThread
.
image
.
toString
());
Console
.
ForegroundColor
=
ConsoleColor
.
White
;
return
myImageThread
.
image
;
}
/// <summary>
/// Demande quelles opérations l'on souhaite effectuer sur l'image
/// </summary>
/// <returns>Retourne le type d'opération souhaitée</returns>
public
static
Operation
AskForOperation
()
{
int
choosenItem
=
0
;
string
[]
menuItems
=
new
string
[]
{
"Rotation à 90°"
,
"Rotation à 180°"
,
"Rotation à 270°"
,
"Passage à une image en nuances de gris (linéairement)"
,
"Passage à une image en nuances de gris (luminosité)"
,
"Sauvegarder"
};
for
(
int
i
=
0
;
i
<
menuItems
.
Length
;
i
++)
{
Console
.
WriteLine
(((
choosenItem
==
i
)
?
">> "
:
" "
)
+
menuItems
[
i
]);
}
ConsoleKeyInfo
key
;
do
{
key
=
Console
.
ReadKey
();
Console
.
SetCursorPosition
(
0
,
9
+
choosenItem
);
Console
.
Write
(
" "
);
if
(
key
.
Key
==
ConsoleKey
.
DownArrow
)
{
choosenItem
++;
if
(
choosenItem
==
menuItems
.
Length
)
choosenItem
=
0
;
}
else
if
(
key
.
Key
==
ConsoleKey
.
UpArrow
)
{
choosenItem
--;
if
(
choosenItem
==
-
1
)
choosenItem
=
menuItems
.
Length
-
1
;
}
Console
.
SetCursorPosition
(
0
,
9
+
choosenItem
);
Console
.
Write
(
">>"
);
}
while
(
key
.
Key
!=
ConsoleKey
.
Escape
&&
key
.
Key
!=
ConsoleKey
.
Enter
);
if
(
key
.
Key
==
ConsoleKey
.
Escape
)
Environment
.
Exit
(
0
);
return
(
Operation
)
choosenItem
;
}
/// <summary>
/// Effectue l'opération demandée sur l'image passée en paramètres
/// </summary>
/// <param name="ope">Opération désirée</param>
/// <param name="img">Image sur laquelle effectuer l'opération</param>
/// <returns>Image avec l'opération effectuée dessus</returns>
public
static
MyImage
PerformOperation
(
Operation
ope
,
MyImage
img
)
{
MyImage
output
=
null
;
Console
.
Clear
();
if
(
ope
==
Operation
.
Rotate90
)
output
=
img
.
Rotate90
();
else
if
(
ope
==
Operation
.
Rotate180
)
output
=
img
.
Rotate180
();
else
if
(
ope
==
Operation
.
Rotate270
)
output
=
img
.
Rotate270
();
else
if
(
ope
==
Operation
.
GrayScaleLinear
||
ope
==
Operation
.
GrayScaleLuminosity
)
{
bool
retry
=
false
;
int
input
;
do
{
if
(
retry
)
Console
.
WriteLine
(
"Saisie incorrecte"
);
Console
.
WriteLine
(
"Veuillez saisir le nombre de nuances de gris désirées (entre 2 et 255)"
);
retry
=
!
int
.
TryParse
(
Console
.
ReadLine
(),
out
input
);
}
while
(
retry
&&
input
>=
2
&&
input
<=
255
);
if
(
ope
==
Operation
.
GrayScaleLinear
)
output
=
img
.
ToGrayScale
((
byte
)
input
,
MyImage
.
grayFilterType
.
LINEAR
);
else
if
(
ope
==
Operation
.
GrayScaleLuminosity
)
output
=
img
.
ToGrayScale
((
byte
)
input
,
MyImage
.
grayFilterType
.
LUMINOSITY
);
}
else
if
(
ope
==
Operation
.
Save
)
{
string
path
;
bool
anotherTry
=
false
;
do
{
if
(
anotherTry
)
Console
.
WriteLine
(
"Le format du chemin est incorrect"
);
Console
.
WriteLine
(
"Veuillez saisir le nom du fichier en sortie"
);
path
=
Console
.
ReadLine
();
if
(!
path
.
EndsWith
(
".bmp"
))
path
+=
".bmp"
;
}
while
(
anotherTry
=
!
Uri
.
IsWellFormedUriString
(
path
,
UriKind
.
RelativeOrAbsolute
));
img
.
Save
(
path
);
}
else
{
throw
new
Exception
(
"Opération non reconnue"
);
}
return
output
;
}
}
}
S04_Projet/MyImage.cs
View file @
ed995b81
...
@@ -41,80 +41,23 @@ namespace S04_Projet
...
@@ -41,80 +41,23 @@ namespace S04_Projet
{
{
if
(
file
.
Length
!=
0
)
if
(
file
.
Length
!=
0
)
{
{
#
region
Header
FromFileToImage
(
file
);
opt
.
format
=
(
char
)
file
[
0
]
+
""
+
(
char
)
file
[
1
];
}
opt
.
fileSize
=
EndianToInt
(
file
,
2
,
6
);
}
opt
.
offset
=
EndianToInt
(
file
,
10
,
14
);
}
Console
.
WriteLine
(
"Format : {0}\nTaille : {1} octets"
,
opt
.
format
,
opt
.
fileSize
);
Console
.
WriteLine
(
"Offset : "
+
opt
.
offset
);
#
endregion
#
region
File
info
opt
.
fileInfoHeaderSize
=
EndianToInt
(
file
,
14
,
18
);
opt
.
width
=
EndianToInt
(
file
,
18
,
22
);
opt
.
height
=
EndianToInt
(
file
,
22
,
26
);
opt
.
colorPlanesNb
=
EndianToInt
(
file
,
26
,
28
);
opt
.
bitsPerPixel
=
EndianToInt
(
file
,
28
,
30
);
opt
.
compressionMethod
=
EndianToInt
(
file
,
30
,
34
);
opt
.
imgSize
=
EndianToInt
(
file
,
34
,
38
);
opt
.
horizontalRes
=
EndianToInt
(
file
,
38
,
42
);
opt
.
VerticalRes
=
EndianToInt
(
file
,
42
,
46
);
opt
.
nbColor
=
EndianToInt
(
file
,
46
,
50
);
opt
.
nbImportantColor
=
EndianToInt
(
file
,
50
,
54
);
Console
.
WriteLine
(
"File info header size : {0} octets"
,
opt
.
fileInfoHeaderSize
);
Console
.
WriteLine
(
"Size : {0}x{1}px"
,
opt
.
width
,
opt
.
height
);
Console
.
WriteLine
(
"Color planes number : "
+
opt
.
colorPlanesNb
);
Console
.
WriteLine
(
"Bits per pixel : "
+
opt
.
bitsPerPixel
);
Console
.
WriteLine
(
"Compression method : "
+
opt
.
compressionMethod
);
Console
.
WriteLine
(
"Image size : {0} octets"
,
opt
.
imgSize
);
Console
.
WriteLine
(
"Horizontal res : {0}\nVertical res : {1}"
,
opt
.
horizontalRes
,
opt
.
VerticalRes
);
Console
.
WriteLine
(
"Number of colors : "
+
opt
.
nbColor
);
Console
.
WriteLine
(
"Number of important colors : "
+
opt
.
nbImportantColor
);
#
endregion
#
region
Pixel
array
int
pixelNumber
=
opt
.
width
*
opt
.
height
;
Pixels
=
new
Pixel
[
opt
.
width
,
opt
.
height
];
Stopwatch
s
=
new
Stopwatch
();
#
region
Mono
public
MyImage
(
byte
[]
file
)
s
.
Start
();
for
(
int
i
=
0
;
i
<
pixelNumber
;
i
++)
{
{
int
x
=
i
%
opt
.
width
;
FromFileToImage
(
file
);
int
y
=
i
/
opt
.
width
;
byte
r
=
file
[
i
*
3
+
opt
.
offset
];
byte
g
=
file
[
i
*
3
+
opt
.
offset
+
1
];
byte
b
=
file
[
i
*
3
+
opt
.
offset
+
2
];
Pixels
[
x
,
y
]
=
new
Pixel
(
r
,
g
,
b
);
}
}
s
.
Stop
();
Console
.
WriteLine
(
s
.
ElapsedMilliseconds
);
#
endregion
#
region
Parallel
public
MyImage
(
Options
opt
,
Pixel
[,]
Pixels
)
/*
s.Start();
Parallel.For(0, pixelNumber, i =>
{
{
Pixels[i % opt.width, i / opt.width] = new Pixel(file[i * 3 + opt.offset],
this
.
opt
=
opt
;
file[i * 3 + opt.offset + 1],
this
.
Pixels
=
Pixels
;
file[i * 3 + opt.offset + 2]
);
});
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
*/
#
endregion
#
endregion
}
}
}
}
p
ublic
My
Image
(
byte
[]
file
)
p
rivate
void
FromFileTo
Image
(
byte
[]
file
)
{
{
opt
=
new
Options
();
opt
=
new
Options
();
#
region
Header
#
region
Header
...
@@ -142,10 +85,7 @@ namespace S04_Projet
...
@@ -142,10 +85,7 @@ namespace S04_Projet
int
pixelNumber
=
opt
.
width
*
opt
.
height
;
int
pixelNumber
=
opt
.
width
*
opt
.
height
;
Pixels
=
new
Pixel
[
opt
.
width
,
opt
.
height
];
Pixels
=
new
Pixel
[
opt
.
width
,
opt
.
height
];
Stopwatch
s
=
new
Stopwatch
();
#
region
Mono
#
region
Mono
s
.
Start
();
for
(
int
i
=
0
;
i
<
pixelNumber
;
i
++)
for
(
int
i
=
0
;
i
<
pixelNumber
;
i
++)
{
{
int
x
=
i
%
opt
.
width
;
int
x
=
i
%
opt
.
width
;
...
@@ -155,13 +95,10 @@ namespace S04_Projet
...
@@ -155,13 +95,10 @@ namespace S04_Projet
byte
b
=
file
[
i
*
3
+
opt
.
offset
+
2
];
byte
b
=
file
[
i
*
3
+
opt
.
offset
+
2
];
Pixels
[
x
,
y
]
=
new
Pixel
(
r
,
g
,
b
);
Pixels
[
x
,
y
]
=
new
Pixel
(
r
,
g
,
b
);
}
}
s
.
Stop
();
Console
.
WriteLine
(
s
.
ElapsedMilliseconds
);
#
endregion
#
endregion
#
region
Parallel
#
region
Parallel
/*
/*
s.Start();
Parallel.For(0, pixelNumber, i =>
Parallel.For(0, pixelNumber, i =>
{
{
Pixels[i % opt.width, i / opt.width] = new Pixel(file[i * 3 + opt.offset],
Pixels[i % opt.width, i / opt.width] = new Pixel(file[i * 3 + opt.offset],
...
@@ -169,8 +106,6 @@ namespace S04_Projet
...
@@ -169,8 +106,6 @@ namespace S04_Projet
file[i * 3 + opt.offset + 2]
file[i * 3 + opt.offset + 2]
);
);
});
});
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
*/
*/
#
endregion
#
endregion
...
@@ -187,12 +122,6 @@ namespace S04_Projet
...
@@ -187,12 +122,6 @@ namespace S04_Projet
#
endregion
#
endregion
}
}
public
MyImage
(
Options
opt
,
Pixel
[,]
Pixels
)
{
this
.
opt
=
opt
;
this
.
Pixels
=
Pixels
;
}
private
void
FromImageToFile
(
string
output
)
private
void
FromImageToFile
(
string
output
)
{
{
int
nbPixel
=
(
opt
.
height
*
opt
.
width
);
int
nbPixel
=
(
opt
.
height
*
opt
.
width
);
...
@@ -228,9 +157,9 @@ namespace S04_Projet
...
@@ -228,9 +157,9 @@ namespace S04_Projet
{
{
MixArrays
(
file
,
Pixels
[
i
%
opt
.
width
,
i
/
opt
.
width
].
getRGB
(),
54
+
(
i
*
3
));
MixArrays
(
file
,
Pixels
[
i
%
opt
.
width
,
i
/
opt
.
width
].
getRGB
(),
54
+
(
i
*
3
));
}
}
#
endregion
File
.
WriteAllBytes
(
output
,
file
);
File
.
WriteAllBytes
(
output
,
file
);
#
endregion
}
}
public
void
Save
(
string
output
)
public
void
Save
(
string
output
)
...
@@ -256,12 +185,30 @@ namespace S04_Projet
...
@@ -256,12 +185,30 @@ namespace S04_Projet
public
MyImage
Rotate180
()
public
MyImage
Rotate180
()
{
{
return
Rotate90
().
Rotate90
();
Options
options
=
opt
.
Copy
();
Pixel
[,]
PixelArr
=
new
Pixel
[
options
.
width
,
options
.
height
];
for
(
int
i
=
0
;
i
<
options
.
width
*
options
.
height
;
i
++)
{
int
x
=
i
%
opt
.
width
;
int
y
=
i
/
opt
.
width
;
PixelArr
[
options
.
width
-
x
-
1
,
options
.
height
-
y
-
1
]
=
Pixels
[
x
,
y
];
}
return
new
MyImage
(
options
,
PixelArr
);
}
}
public
MyImage
Rotate270
()
public
MyImage
Rotate270
()
{
{
return
Rotate180
().
Rotate90
();
Options
options
=
opt
.
Copy
();
options
.
width
=
opt
.
height
;
options
.
height
=
opt
.
width
;
Pixel
[,]
PixelArr
=
new
Pixel
[
options
.
width
,
options
.
height
];
for
(
int
i
=
0
;
i
<
options
.
width
*
options
.
height
;
i
++)
{
int
x
=
i
%
opt
.
width
;
int
y
=
i
/
opt
.
width
;
PixelArr
[
options
.
width
-
1
-
y
,
x
]
=
Pixels
[
x
,
y
];
}
return
new
MyImage
(
options
,
PixelArr
);
}
}
#
endregion
#
endregion
...
...
S04_Projet/Pixel.cs
View file @
ed995b81
...
@@ -12,6 +12,12 @@ namespace S04_Projet
...
@@ -12,6 +12,12 @@ namespace S04_Projet
public
byte
g
{
get
;
private
set
;
}
public
byte
g
{
get
;
private
set
;
}
public
byte
b
{
get
;
private
set
;
}
public
byte
b
{
get
;
private
set
;
}
/// <summary>
/// Prend les trois couleurs en paramètres
/// </summary>
/// <param name="r">Rouge</param>
/// <param name="g">Vert</param>
/// <param name="b">Bleu</param>
public
Pixel
(
byte
r
,
byte
g
,
byte
b
)
public
Pixel
(
byte
r
,
byte
g
,
byte
b
)
{
{
this
.
r
=
r
;
this
.
r
=
r
;
...
@@ -19,6 +25,10 @@ namespace S04_Projet
...
@@ -19,6 +25,10 @@ namespace S04_Projet
this
.
b
=
b
;
this
.
b
=
b
;
}
}
/// <summary>
/// Assigne une même valeur à toutes les couleurs
/// </summary>
/// <param name="unifiedColors">Valeur souhaitée pour toutes les couleurs</param>
public
Pixel
(
byte
unifiedColors
)
public
Pixel
(
byte
unifiedColors
)
{
{
r
=
unifiedColors
;
r
=
unifiedColors
;
...
@@ -26,6 +36,10 @@ namespace S04_Projet
...
@@ -26,6 +36,10 @@ namespace S04_Projet
b
=
unifiedColors
;
b
=
unifiedColors
;
}
}
/// <summary>
/// Créer un pixel à partir d'un tableau contenant les couleurs rouge, vert, bleu (respectivement)
/// </summary>
/// <param name="rgb">Tableau composé dans cet ordre des couleurs rouge, vert, bleu</param>
public
Pixel
(
byte
[]
rgb
)
public
Pixel
(
byte
[]
rgb
)
{
{
r
=
rgb
[
0
];
r
=
rgb
[
0
];
...
@@ -33,11 +47,20 @@ namespace S04_Projet
...
@@ -33,11 +47,20 @@ namespace S04_Projet
b
=
rgb
[
2
];
b
=
rgb
[
2
];
}
}
/// <summary>
/// Retourne un tableau contenant les valeurs RGB du pixel
/// </summary>
/// <returns>Composantes rouge, verte et bleue du pixel</returns>
public
byte
[]
getRGB
()
public
byte
[]
getRGB
()
{
{
return
new
byte
[]
{
r
,
g
,
b
};
return
new
byte
[]
{
r
,
g
,
b
};
}
}
/// <summary>
/// Retourne l'équavalent gris d'un pixel
/// </summary>
/// <param name="scale">Nombre de nuances de gris</param>
/// <returns>Pixel en échelle de gris</returns>
public
Pixel
getGrayScale
(
byte
scale
)
public
Pixel
getGrayScale
(
byte
scale
)
{
{
byte
total
=
(
byte
)((
r
+
g
+
b
)
/
3
);
byte
total
=
(
byte
)((
r
+
g
+
b
)
/
3
);
...
@@ -45,6 +68,12 @@ namespace S04_Projet
...
@@ -45,6 +68,12 @@ namespace S04_Projet
return
new
Pixel
(
total
);
return
new
Pixel
(
total
);
}
}
/// <summary>
/// Retourne l'équivalent gris d'un pixel en prenant en compte les coefficients de luminosité
/// des couleurs RGB
/// </summary>
/// <param name="scale"></param>
/// <returns></returns>
public
Pixel
getGrayScaleLuminosity
(
byte
scale
)
public
Pixel
getGrayScaleLuminosity
(
byte
scale
)
{
{
byte
total
=
(
byte
)(
0.21
*
r
+
0.72
*
g
+
0.07
*
b
);
byte
total
=
(
byte
)(
0.21
*
r
+
0.72
*
g
+
0.07
*
b
);
...
...
S04_Projet/Program.cs
View file @
ed995b81
...
@@ -13,53 +13,19 @@ namespace S04_Projet
...
@@ -13,53 +13,19 @@ namespace S04_Projet
{
{
static
void
Main
(
string
[]
args
)
static
void
Main
(
string
[]
args
)
{
{
//MyImage img = new MyImage("img/coco.bmp")
;
Console
.
ForegroundColor
=
ConsoleColor
.
White
;
Console
.
WriteLine
(
"#############################################################"
);
Console
.
WriteLine
(
"#############################################################"
);
Console
.
WriteLine
(
"### Bienvenue ###"
);
Console
.
WriteLine
(
"### Bienvenue ###"
);
Console
.
WriteLine
(
"#############################################################"
);
Console
.
WriteLine
(
"#############################################################"
);
Console
.
WriteLine
();
Console
.
WriteLine
();
string
path
;
MyImage
img
=
Display
.
LoadImage
()
;
bool
validPath
=
false
;
Display
.
Operation
ToDo
=
Display
.
AskForOperation
()
;
byte
[]
file
=
new
byte
[
0
]
;
Display
.
PerformOperation
(
ToDo
,
img
)
;
do
{
Console
.
WriteLine
(
"Veuillez saisir le nom du fichier à traiter :"
);
path
=
Console
.
ReadLine
();
try
{
file
=
File
.
ReadAllBytes
(
path
);
}
catch
(
Exception
e
)
{
Console
.
ForegroundColor
=
ConsoleColor
.
Red
;
Console
.
WriteLine
(
"Une erreur est survenue : "
+
e
.
Message
);
Console
.
ForegroundColor
=
ConsoleColor
.
White
;
}
if
(
file
.
Length
!=
0
)
validPath
=
true
;
}
while
(!
validPath
);
Console
.
Clear
();
MyImageThread
myImageThread
=
new
MyImageThread
(
file
);
Thread
imageThread
=
new
Thread
(
new
ThreadStart
(
myImageThread
.
ThreadLoop
));
imageThread
.
Start
();
while
(
imageThread
.
IsAlive
)
{
Console
.
Write
(
"Traitement en cours de l'image."
);
Thread
.
Sleep
(
250
);
Console
.
Write
(
"."
);
Thread
.
Sleep
(
250
);
Console
.
Write
(
"."
);
Thread
.
Sleep
(
250
);
Console
.
Clear
();
}
Console
.
WriteLine
(
"Image chargée en {0}ms\n"
,
myImageThread
.
loadTime
);
Console
.
WriteLine
(
myImageThread
.
image
.
toString
());
//Console.Clear();
//Console.Clear();
Console
.
Read
();
Console
.
Read
();
...
...
S04_Projet/S04_Projet.csproj
View file @
ed995b81
...
@@ -44,6 +44,7 @@
...
@@ -44,6 +44,7 @@
</ItemGroup>
</ItemGroup>
<ItemGroup>
<ItemGroup>
<Compile
Include=
"MyImage.cs"
/>
<Compile
Include=
"MyImage.cs"
/>
<Compile
Include=
"Display.cs"
/>
<Compile
Include=
"MyImageThread.cs"
/>
<Compile
Include=
"MyImageThread.cs"
/>
<Compile
Include=
"Options.cs"
/>
<Compile
Include=
"Options.cs"
/>
<Compile
Include=
"Pixel.cs"
/>
<Compile
Include=
"Pixel.cs"
/>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment