I have this C++ function (unmanaged code) in a C++/CLI-project.
BMP Image;
Image.ReadFromFile(filePath);
HsvColor hsvPixel;
RGBApixel startPixel;
for (int i = 0; i < Image.TellWidth(); i++) {
for (int j = 0; j < Image.TellHeight(); j++) {
startPixel = *(Image(i, j));
hsvPixel = RgbToHsv(startPixel);
RGBApixel finalPixel = HsvToRgb(hsvPixel);
*(Image(i, j)) = finalPixel;
}
}
(HsvColor and RGBApixel are both structs out of unsigned chars. Image(i, j) returns a pointer to a RGBApixel.)
The problematic line is hsvPixel = RgbToHsv(startPixel);
I have two problems with this line:
It does sometimes cause an System.AccessViolationException, but not always. The error message also says that it was attempted to write into protected memory. When I execute the code step by step with the debugger, the RgbToHsv function returns a value, then it goes to some system function I can't see, and then it crashes before it goes back to the code block posted here.
It doesn't initialize the hsvPixel with the correct value. I followed it in debug mode and it does return an HsvColor object with correct values, but after the line has been executed the unsigned chars of the HsvColor struct are set to completely different value than the one returned by the function.
I would be very grateful for some help.
I also tried to execute only the RgbToHsv method alone without doing any assignments. When I do this, the loop runs through fine most of the times, but crashes sometimes too with System.AccessViolationException, only that that is much rarer than when doing the assignment to hsvPixel.
Code for RGBAPixel:
typedef struct RGBApixel {
ebmpBYTE Blue;
ebmpBYTE Green;
ebmpBYTE Red;
ebmpBYTE Alpha;
} RGBApixel;
An ebmpBYTE is a typedef-ed unsigned char.
Code for both methods:
RGBApixel HsvToRgb(HsvColor hsv)
{
RGBApixel rgb;
unsigned char region, remainder, p, q, t;
if (hsv.Saturation == 0)
{
rgb.Red = hsv.Value;
rgb.Green = hsv.Value;
rgb.Blue = hsv.Value;
rgb.Alpha = 1;
return rgb;
}
region = hsv.Hue / 43;
remainder = (hsv.Hue - (region * 43)) * 6;
p = (hsv.Value * (255 - hsv.Saturation)) >> 8;
q = (hsv.Value * (255 - ((hsv.Saturation * remainder) >> 8))) >> 8;
t = (hsv.Value * (255 - ((hsv.Saturation * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.Red = hsv.Value; rgb.Green = t; rgb.Blue = p;
break;
case 1:
rgb.Red = q; rgb.Green = hsv.Value; rgb.Blue = p;
break;
case 2:
rgb.Red = p; rgb.Green = hsv.Value; rgb.Blue = t;
break;
case 3:
rgb.Red = p; rgb.Green = q; rgb.Blue = hsv.Value;
break;
case 4:
rgb.Red = t; rgb.Green = p; rgb.Blue = hsv.Value;
break;
default:
rgb.Red = hsv.Value; rgb.Green = p; rgb.Blue = q;
break;
}
return rgb;
}
HsvColor RgbToHsv(RGBApixel rgb)
{
HsvColor hsv;
unsigned char rgbMin, rgbMax;
rgbMin = rgb.Red < rgb.Green ? (rgb.Red < rgb.Blue ? rgb.Red : rgb.Blue) : (rgb.Green < rgb.Blue ? rgb.Green : rgb.Blue);
rgbMax = rgb.Red > rgb.Green ? (rgb.Red > rgb.Blue ? rgb.Red : rgb.Blue) : (rgb.Green > rgb.Blue ? rgb.Green : rgb.Blue);
hsv.Value = rgbMax;
if (hsv.Value == 0)
{
hsv.Hue = 0;
hsv.Saturation = 0;
return hsv;
}
hsv.Saturation = 255 * long(rgbMax - rgbMin) / hsv.Value;
if (hsv.Saturation == 0)
{
hsv.Hue = 0;
return hsv;
}
if (rgbMax == rgb.Red)
hsv.Hue = 0 + 43 * (rgb.Green - rgb.Blue) / (rgbMax - rgbMin);
else if (rgbMax == rgb.Green)
hsv.Hue = 85 + 43 * (rgb.Blue - rgb.Red) / (rgbMax - rgbMin);
else
hsv.Hue = 171 + 43 * (rgb.Red - rgb.Green) / (rgbMax - rgbMin);
return hsv;
}
Implementation of Image(i, j):
RGBApixel* BMP::operator()(int i, int j)
{
using namespace std;
bool Warn = false;
if( i >= Width )
{ i = Width-1; Warn = true; }
if( i < 0 )
{ i = 0; Warn = true; }
if( j >= Height )
{ j = Height-1; Warn = true; }
if( j < 0 )
{ j = 0; Warn = true; }
if( Warn && EasyBMPwarnings )
{
cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
<< " Truncating request to fit in the range [0,"
<< Width-1 << "] x [0," << Height-1 << "]." << endl;
}
return &(Pixels[i][j]);
}
The declaration of Pixels (from the EasyBMP library):
RGBApixel** Pixels;
Edit: I used a debugger to look at how the RgbToHsv function executes. The startPixel is always the same the first time the loop runs (as it should be). But when I click "step into" in Visual Studio and see how the code executes in the function, the parameter ("rgb") is always completely different to startPixel! I am new to debugging, so I might be interpreting things incorrectly. But now I am even more confused. Here is a picture.
I also should mention the result of the code when it's run. It outputs the Image, but the output picture is just a random single color (entirely blue for example), whereas it should be identical to the input picture.