Re: .NET bitmap loaded from 1bpp png stored as 32bpp
I was a bit intrigued by this thread, so I tried to reproduce the described behavior, and couldn't do it no matter what.
For me, all the images load correctly, even the problematic one attached here, and Bitmap::Clone works perfect and successfully converts to 1bpp format.
I attached a really simple test project, in a rar file that also contains a compiled release version. Could one of you two compile the project on your machine and try to run the two exe-s (yours and the one in the rar), and see if there is any difference?
There's absolutely nothing fancy about the project, it uses the same things you tried, so I expect that once compiled on one of your machines, the bug will still be there. All the functionality is in Form1.h in the button's event handler.
It may be a low level thing... Are you both running Windows XP?
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
That means that it failed on the Clone call. Weird...
Really weird, but as I've said, expected, considering that there's nothing different in the code. This probably means that it's not framework- or compiler-related, but that it has something to do with how GDI+ uses GDI internally (or whatever other OS resources might be involved).
I compiled that with VS 2008 on W7, but I'll try and do some more tests, and report back if I find out anything significant.
I guess we'll have to wait for Microsoft to respond, if that happens anytime soon...
Please keep us informed.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Okay, I'll definitely post back if Microsoft contacts me.
Thanks for your help. The issue is kind of resolved on my part due to (drastic) changes in my code, but I am still interested in finding out why this is happening.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
My test results are basically identical to those Amil got. I tried it on XP Pro SP3. What did you run it on, Amil?
And I could only reproduce that with the .exe I got out of your .rar file beause project conversion failed for me, complaining about a corrupt .vcproj file. The messages I got are at least partially related to your project containing x64 config settings, and I don't have the x64 stuff installed here. (But these particular messages may have been just warnings, I'm not sure.)
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Actually, this appears to be even more of a pinch than it seemed initially.
Although MSDN's (rather short) description of the Bitmap(int, int, PixelFormat) constructor doesn't spend a single word to mention this, there seems to be no way to construct a Bitmap instance with an indexed pixel format except (1) loading it from a file, (2) using the Clone(Rectangle, PixelFormat) method to convert it which seems to be possible only on Win7 or to (3) use the Bitmap(int, int, int, PixelFormat, IntPtr) constructor with pre-prepared raw bitmap data which would either require a significant effort on the side of the developer or a Win-API-based approach like the one linked to by Amil in post #3.
Although not mentioned in the constructor documentation linked to above, constructing any Bitmap with an indexed pixel format seems to be useless, if not entirely impossible, unless you load it from a file or Clone() it. That's because the Bitmap::Palette property is writable, but the ColorPalette class has no public constructor, so where to get an instance of it from except from loading an already index-colored image file? (This at least applies to the scenario we have here. Maybe there are others that don't come to my mind at the moment.)
Therefore even the inefficient pixel-wise conversion approach I proposed in post #15 seems to be impossible...
Ok, there still is the "programmatic hacking" approach of manipulating the PNG file directly, but that's definitely not the .NET way of doing things...
Last edited by Eri523; June 4th, 2011 at 10:33 PM.
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Originally Posted by Amil
Well I'll post back when/if Microsoft contacts me.
Thanks.
This might take a while, so maybe you guys could just follow this thread by e-mail?
I'm not sure about TheGreatCthulhu, but I have set up the forum software to automatically subscribe to any thread I post in and notify me via e-mail of incoming posts. (However, these mails don't contain the post's text, just thread title, section and poster, so I still need to visit the forum to read them.) Also, this is one of the sections I monitor regularly anyway.
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Update after some more thoughts and experiments...
Originally Posted by Amil
Eri, I think you are right on all of those points...
Well, not quite, actually.
Originally Posted by Eri523
Although not mentioned in the constructor documentation linked to above, constructing any Bitmap with an indexed pixel format seems to be useless, if not entirely impossible, unless you load it from a file or Clone() it. That's because the Bitmap::Palette property is writable, but the ColorPalette class has no public constructor, so where to get an instance of it from except from loading an already index-colored image file? [...]
Therefore even the inefficient pixel-wise conversion approach I proposed in post #15 seems to be impossible...
Wrong. I can constuct a Bitmap object with an indexed color space using the Bitmap(int, int, PixelFormat) constructor on my system. (And it's getting constructed with a default palette that's at least reasonable for 1bpp images. Didn't try 8bpp yet.) But trying to use the SetPixel() method on that throws an exception (IIRC of type NotSupportedException) telling me that the method is not supported for indexed color spaces. Well, this is actually documented (somehow): MSDN says that the method throws an exception of type Exception if "The operation failed". (This may work on Win 7, just like the cloning.)
Honestly, I really love those .NET (framework) exceptions: They usually carry a message in their Message member that clearly tells what's wrong.
But I actually can write to the bitmap by directly manipulating the bitmap data. This is the conversion routine I currently have in my experimental app:
Code:
// The code in the following function is VERY roughly based on the MSDN sample at
// http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx
System::Void Form1::bnConvert_Click(System::Object^ sender, System::EventArgs^ e)
{
const float fBrightnessThreshold = 0.5;
Stopwatch ^stw = Stopwatch::StartNew();
Bitmap ^bmpNew = gcnew Bitmap(m_bmp->Width, m_bmp->Height, PixelFormat::Format1bppIndexed);
BitmapData ^bdDestination = bmpNew->LockBits(Rectangle(0, 0, bmpNew->Width, bmpNew->Height),
ImageLockMode::WriteOnly, PixelFormat::Format1bppIndexed);
int nStride = Math::Abs(bdDestination->Stride);
array<Byte> ^abyBuffer = gcnew array<Byte>(nStride * bmpNew->Height);
for (int y = 0; y < m_bmp->Height; ++y) {
int nRowIndex = y * nStride;
for (int x = 0; x < m_bmp->Width; x += 8) { // Make byte-size steps
Byte byTemp = 0;
int nBitIndex;
for (nBitIndex = 0; nBitIndex < 8 && x + nBitIndex < m_bmp->Width; ++nBitIndex) {
byTemp <<= 1;
if (m_bmp->GetPixel(x + nBitIndex, y).GetBrightness() > fBrightnessThreshold)
byTemp |= 1;
}
if (nBitIndex < 8) {
SByte sbyPad = 0x80; // For simplicity always assume white background
sbyPad >>= 8 - (nBitIndex - 1);
byTemp |= sbyPad;
}
abyBuffer[nRowIndex + x / 8] = byTemp;
}
}
Marshal::Copy(abyBuffer, 0, bdDestination->Scan0, abyBuffer->Length);
bmpNew->UnlockBits(bdDestination);
delete abyBuffer;
delete m_bmp;
m_bmp = bmpNew;
stw->Stop();
pictureBox1->Image = m_bmp;
label1->Text = String::Format("Pixel format: {0}", m_bmp->PixelFormat);
label3->Text = String::Format("Conversion took {0} ms", stw->ElapsedMilliseconds);
}
Note that this code makes even more assumptions for simplicity than mentioned in the comments. In particular I use GetPixel() to read from the source bitmap which is painfully slow but allows for any bitmap format (well, anyone that's supported by this method... ) as input without accounting for the different internal representations. (Well, that's not really an assumption, rather a generalization at the cost of efficiency.)
And, well, it is slow: On my system (2.2 GHz Athlon X2 dual core) it takes around 85 ms for the sample image you posted and 950 ms for an 800 x 600 24bpp (simply grabbed my desktop wallpaper as the guinea pig).
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Yeah, I guess it is allowed to construct bitmaps with indexed formats. I didn't check this, but just assumed you were correct.
Yeah, GetPixel() is very slow due to the generality...
Either way, I already found a nice, fast conversion algorithm and that's what I am using in my code. I mentioned it earlier, but here is the link again: http://www.wischik.com/lu/programmer/1bpp.html
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Originally Posted by Amil
Either way, I already found a nice, fast conversion algorithm and that's what I am using in my code. I mentioned it earlier, but here is the link again: http://www.wischik.com/lu/programmer/1bpp.html
Yeah, I already read that link and commented on it in post #5. However, I wasn't aware that it already had solved your problem at that point.
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
Re: .NET bitmap loaded from 1bpp png stored as 32bpp
Yeah, I saw their mysterious "Resolved-External" tag too some days ago. However, I was really uncertain about what they actually mean by that. They didn't post a link to any external source, so my best guess is that they mean this very CodeGuru thread by the term "external solution". However, if they really mean that, I consider the "solution" to effectively boil down to "if you encounter this problem, buy Windows 7"... That's not really what I, personally, would call a solution.
At any rate, thanks for informing us. So maybe it's time you mark the thread [RESOLVED]. It's commonly considered good style here on CodeGuru to do that when a problem is solved. Since I know you already solved your concrete problem way up this thread, you may want to do so. However, I, personally, probably would not, but that's mainly due to the fact that I'm using XP...
Last edited by Eri523; October 1st, 2011 at 11:00 AM.
I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.
This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.