Jump to content


  • Posts

  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

AndersM's Achievements


Rookie (2/14)

  • Collaborator
  • One Month Later
  • Week One Done
  • First Post

Recent Badges



  1. That doesn't really answer my question 🙂 I'm not using PDN so I haven't got any samples of the BMPs it produce. Against better judgment I've now looked a bit more into this. As far as I can tell, when dealing with DIBs, many parts of Windows expect or produce a color table for BI_BITFIELDS DIBs - However not all. Notably, parts of the gdi, icm, and the imaging part of MS Paint, among others, expect a color table in v3 BI_BITFIELDS DIBs but explicitly not in v4 or v5. FWIW, this corresponds to my original implementation. I have no idea how all these different parts manage to interoperate when they can't agree on the data format.
  2. That was just a conincidence. I just picked one of the file sets that demonstrate the problem. v3, v4 and v5 all have it. I can read v1, v2, v3, v4, and v5 in BI_RGB and BI_BITFIELDS format but I only write v1 and v2 in BI_RGB format (since BI_BITFIELDS has no purpose in that version (because no alpha)), and I only write v3, v4, and v5 in BI_BITFIELDS format (since I need the alpha). v2 btw is an undocumented, unreleased format. It's only included for completeness. For some reason the Windows shell only supports alpha for v4 and later. Also, the Windows shell doesn't like the color table: For the clipboard only v1 and v5 are relevant. Okay, but have you tried opening a v5 BI_BITFIELDS bmp file produced by WIC in, for example, MS Paint? So far it appears that the clipboard expects a DIB with a color table but everything else expects one without. So far I haven't found a single application that can load the color table v4/v5 bmps correctly. Even online editors experience the problem (tested with both Firefox, Edge and Chrome).
  3. I think I'm going crazy. As you've seen I have implemented the change in Graphics32 and closed the issue there. Since many of the bitmaps I used for unit testing were made under the old assumption I decided to regenerate all the bitmaps. And now that I had the bitmaps I thought I would see how Windows Paint handled them - and this is where things got really depressing. I've attached a zip with two 16x16x32bit bitmaps in v4 BI_BITFIELDS format. One with the color table and one without. The bitmaps look like this (in my own bitmap editor): And this is what they look like in Paint when I load them from file: bgra_v4_bottomup.bmp (with color table) bgra_v4_bottomup_missing_color_table.bmp (without color table) Okay, I thought, I guess I was wrong about being wrong. But then I did a copy from my editor (which now produce CF_DIBV5 data with the color table) and pasted into Paint: (╯°□°)╯︵ ┻━┻ Anyway, I'm too busy with work and stuff to get to the bottom of this right now but I just wanted to give you a heads up. bgra_v4_bottomup.zip
  4. Alas, it seems I have to concede defeat on this issue. I was wrong. I noticed that the MSDN documentation on bitmap headers has once again been rephrased and now reads: The text that follows shows evidence of having been copied from somewhere else (it makes reference to the function which has no meaning in that context) but regardless of that it doesn't really leave room for ambiguity; The color masks must follow BITMAPV5HEADER. Since MSDN nowadays is semi-community-edited I didn't take the above as definite proof (I've seen the documentation on this topic change back and forth so many times). Instead I decided to consult "the source" (I'll leave it to you to guess what "the source" is). So I now have it on good authority that the following is how the Windows clipboard calculates the size of a DIB (brought to you in beautiful Pascal): Size of the color table if (BitmapInfoHeader.biCompression = BI_RGB) then begin if (BitmapInfoHeader.biClrUsed) then ColorTableSize := BitmapInfoHeader.biClrUsed * SizeOf(DWORD) else if (BitmapInfoHeader.biBitCount <= 8) then ColorTableSize := (1 shl BitmapInfoHeader.biBitCount) * SizeOf(RGBQUAD) else ColorTableSize := 0; end else if (BitmapInfoHeader.biCompression = BI_BITFIELDS) then begin if (BitmapInfoHeader.biBitCount = 16) or (BitmapInfoHeader.biBitCount = 32) then ColorTableSize := 3 * SizeOf(DWORD) else ColorTableSize := 0; end else if (BitmapInfoHeader.biCompression = BI_RLE4) { ColorTableSize := 16 * SizeOf(DWORD) else if (BitmapInfoHeader.biCompression = BI_RLE8) { ColorTableSize := 256 * SizeOf(DWORD) else ColorTableSize := 0; Size of the pixel data PixelDataSize := (((BitmapInfoheader.biWidth * BitmapInfoheader.biBitCount + 31) and not 31) shr 3) * Abs(BitmapInfoheader.biHeight); // 32 bit aligned Size of DIB DIBSize := BitmapInfoHeader.biSize + ColorTableSize + PixelDataSize; Notes: The above only applies for (BitmapInfoHeader.biSize >= SizeOf(BITMAPINFOHEADER)). The rules for BITMAPCOREHEADER (i.e an OS/2 DIB) are different. For BITMAPV5HEADER, when writing DIBs, the header color masks are duplicated into the color table. This is a requirement but I guess it's a good idea since we don't know which of the two set of masks the reader will use. My guess regarding the cause of this mess is that the DIB spec, internally at Microsoft, has evolved through time from the masks originally being included in the header to them being excluded because that was how most people implemented it. This would explain the ever changing documentation and the fact that different Microsoft tools has implemented different formats - and in the case of the clipboard, different formats within the same code. It's also interesting that so many Windows tools are able to correctly read both versions of the format...
  5. Btw, as far as I remember there is a bug, at least in Windows 10, where pasting CF_DIB will corrupt the CF_DIBV5 on the clipboard - or maybe vice versa. I can't remember which it is. Edit: I see that I already mentioned this in the original thread: The comment that mentions the clipboard bugs predates Windows 10 so I probably discovered them in Windows 7 (I only upgraded from Win7 to Win10 a few months ago).
  6. Don't go there; The clipboard will synthesize CF_DIBV5 from CF_DIB if only CF_DIB has been pasted.
  7. I just searched the Firefox source for CF_DIBV5, BITMAPV4HEADER and BITMAPV5HEADER in order to find out how they handle DIBs - and now I need to book an appointment with my therapist... As far as I can tell Firefox has used the renowned method of rolling a dice to decide what method to use when reading/writing V4/V5 bitmaps. In most places the DIB data does not contain the extra three color masks, but in a few places it does, and in one gloriously stupid case, when writing a bitmap file, the masks are included but only if the data is specified as having alpha. WTH?
  8. I didn't 🙂 - but I have now. I don't really have anything to add beyond what was discussed in the Graphics32 vs Paint.NET thread; My position is still that the three color masks are included in BITMAPV5HEADER and thus should not be added after the header. I.e. Paint.NET is correct.
  9. Your patience is admirable 🙂 I personally wouldn't try to handle broken input to that degree. Especially since it's impossible to guarantee that the fix wouldn't break valid input. For example, the following bitmap is valid and not premultiplied but all RGB <= A: Oh... It seems I can't paste BMPs. Anyway, you get the picture - so to say.
  10. I can't remember anymore and I'm afraid I don't have time to pursue it now. You can deduce it from the structure format though. The bV5RedMask, bV5GreenMask, and bV5BlueMask fields are only used with BI_BITFIELDS. It follows that the bV5AlphaMask field, which is the only method there is to specify where to find the alpha value in the color DWORDs, is also only used with BI_BITFIELDS. I think you'll find that you can't find any mention of alpha at all in the BMP documentation. There's nothing stopping you from putting alpha in a BI_RGB DWORD, but if the readers don't recognize it as alpha then what's the point? You can give it a try with Windows Explorer. The easiest test is probably just to place a 32-bit RGBA BMP on the desktop and see if it displays as opaque or transparent.
  11. Yep. Here's the code: // Work around BMPs with an extra color mask after the header: // If there's exactly 12 bytes too many then we assume that these are an extra // color mask and skip it. // For example the Windows resource compiler will add these 12 bytes to the header // of RT_BITMAP resources. This goes for both BI_RGB and BI_BITFIELDS. if (InfoHeaderVersion >= InfoHeaderVersion1) then begin ChunkSize := 3 * SizeOf(DWORD); if (Size = ChunkSize) then Stream.Seek(ChunkSize, soFromCurrent); end; Yes, with this it seems there's no "fixed". Just various degrees of broken. There's no point in using CF_DIBV5 unless you're also using BI_BITFIELDS as BI_BITFIELDS is the only way to specify that the bitmap contains alpha. Without BI_BITFIELDS you might as well just use CF_DIB. Graphics32 originally used CF_DIB + BI_RGB for both clipboard and BMP files and while this worked if the target assumed that the bitmaps contained alpha, many applications rightly treated the bitmaps as 24-bit RGB. For example Windows Explorer; Only a v3+header with BI_BITFIELDS will make Windows Explorer treat a BMP as a 32-bit RGBA bitmap. FWIW, the following comment from the Graphics32 bitmap/clipboard adapter code might provide some additional clues. The Windows clipboard isn't exactly makes things easier for us: (* ** We place the following data on the clipboard: ** ** - CF_BITMAP ** This is the source bitmap rendered onto a white background. ** Transparency is not retained. ** For use by applications that doesn't support Alpha. ** ** - CF_DIBV5 ** A 32 bit DIB with alpha. This alone can be used to recreate the original ** 32 bit bitmap, including alpha. ** This format provides round trip support. ** ** Since Windows can synthesize between any of CF_DIB, CF_BITMAP and CF_DIBV5 ** theoretically we could just supply the most capable format (CF_DIBV5) and ** let Windows supply the others. Unfortunately we need to supply both CF_DIBV5 ** and CF_BITMAP/CF_DIB in order to work around various Windows bugs: ** ** - When the clipboard synthesizes CF_DIBV5 from CF_DIB it uses BI_BITFIELDS. ** However, if the clipboard synthesizes CF_DIB from CF_DIBV5 with ** BI_BITFIELDS compression, the clipboard apparently forgets to take the ** extra 3 mask DWORDs into account which messes up the resulting DIB. ** ** - When the clipboard synthesizes CF_DIB or CF_BITMAP from CF_DIBV5 it ** appears to require 68 extra bytes after the bitmap header. ** Inserting these 68 bytes would fix that but would also make the bitmap ** stream invalid for everything else. ** FWIW, 68 = SizeOf(BITMAPV4HEADER)-SizeOf(BITMAPINFOHEADER)... ** ** As a bonus we get to control the background color of the CF_DIB/CF_BITMAP ** bitmap instead of the black one Windows would use. *)
  12. Hi, Anders Melander, the current Graphics32 maintainer here. The GDI documentation is correct, albeit very poorly worded, but from my POW it's your interpretation of bmiColors that's the problem. As I understand it, your assumption is that the bmiColors array should always follow the BITMAPV4HEADER structure. My claim is that it's part of the structure. If you look at the documentation, bmiColors comes from the BITMAPINFO structure; The first member of the structure is BITMAPINFOHEADER and the second member is the bmiColors DWORD array. Now if you look at all the different BMP header versions, they all extend on the previous version by adding fields to the end of it. We start with the v1 BITMAPINFOHEADER. The (undocumented) v2 header adds 3 DWORD RGB masks. The v3 header adds a DWORD alpha mask. the v4 adds gamma and color space stuff, etc. etc. The point here is that bmiColors really only applies when you are using a v1 header. All the later versions include bmiColors in their structure. A clue is that the documentation first state that bmiColors specify the color masks: and then later state (for BI_BITFIELDS): Ergo: bmiColors=bV4RedMask, bV4GreenMask, bV4BlueMask -> The 3 DWORDs are included in the header Now, even though I claim my interpretation of the structures to be "the right one" it can also be argued that they are both correct. The documentation has been changed so many times with regard to this and it's still broken, some parts of Windows write one variant, other parts write another and it can usually read them both. If possible, I suggest Paint.NET try to handle both variants (when reading). That's what I do at my end and it seems that that is also what Windows itself does.
  • Create New...