• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

The evil that is ".blp".

Status
Not open for further replies.

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
So I am working on a Java library containing service providers for the javax.imageio system to read/write (hopefully) ".blp" files. So far I have managed to get image (mipmap) extraction working for BLP1 files with content mode 0 (JPEG container). Although it currently does not load a buffered image of the contents, it does dump the file to disk for viewing. The files dumped appear valid which is good news.

However the JPEG structure produced seems to be unusual and poorly support. It appears to be produced by some imaging library from Intel.

For the high-resolution Gryphon Rider texture this is the dumped JPEG for mipmap level 0 (full size).
attachment.php

What it appears like to you is questionable and probably heavily influenced by display calibration. Poor browser implementations might show you nonsense. The thumbnail is also likely to appear garbage as I doubt the quality of the imaging libraries used by the server. However what it should be is the correct texture including alpha with exception of the red and blue colour channels being swapped (part of the blp specification). Alpha should also be producing some visual indicator that may or may not be correct (depends on blp specification of the meaning of alpha).

For comparison this is what GIMP sees it as after the colours are swapped. This image should not be subject to display calibration as GIMP appeared to use the intended profile to interpret the image. Notice that the alpha data appears to have been lost (it was loaded as a RGB image only).
attachment.php


This is how it looks if a RGB dump is made with BLPLab tool which should be interpreting the file correctly. Again the colour channels have been swapped for you. Obviously alpha is lost but the detail contained in the alpha is not (unlike the GIMP processed image).
attachment.php


The unprocessed JPEG dump is interpreted completely incorrectly by Paint Shop Pro. By the looks of the produced image it is not converting colour space correctly into RGB. This is likely the result of missing support or non-compliance with the JPEG standard. Hence why I used GIMP since it appears to be more compliant.

The big question is what is going on with the alpha channel. The dumped unprocessed image is a "JPEG File Interchange Format (JFIF)" compliant image. Decent implementations of JPEG such as used by GIMP do load the RGB colours correctly from it however the alpha information is lost.

I am still investigating the exact standards used by the dumped JPEG file, specifically how the alpha is stored. So far I suspect it uses a slightly different colour space from usual with it being either YCBCR or Y′CBCR (which ever of the two is not used by most JPEG files). The image is also generated by Intel imaging libraries according to meta data. It must also be using parts of the standard defined before 2004 for logical reasons.

The documentation of what JPEG format blp supports is very poor. If it was fully understood it may be possible to generate smaller blp files (useful for mappers) or higher quality blp files (lossless) than previously possible.

In any case feel free to mess around with the dumped image and see if you can make anything of it.
 

Attachments

  • imgdb.jpg
    imgdb.jpg
    199.3 KB · Views: 642
  • imgdb2.jpg
    imgdb2.jpg
    118.2 KB · Views: 674
  • imgdb-correct.jpg
    imgdb-correct.jpg
    67.1 KB · Views: 542
Level 29
Joined
Jul 29, 2007
Messages
5,174
It doesn't use anything fancy, it's just a normal 32bit BGRA format compressed as JPEG. It's non-standard, hence why software fails to load it properly. 4 channel JFIFs are only used by extensions as far as I know (Adobe and stuff like that), except for Blizzard of course.
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
Do you know the following?

The war3mapMap.blp file : The Minimap Image

The BLP file contain the JPEG header and the JPEG raw data separated.
BLP stands for "Blip" file which I guess is a "BLIzzard Picture".
There are two types of BLPs:
- JPG-BLPs: use JPG compression
- Paletted BLPs: use palettes and 1 or 2 bytes per pixel depending

The general format of JPG-BLPs:

Header:
char[4]: file ID ("BLP1")
int: 0 for JPG-BLP, 1 for Paletted
int: 0x00000008 = has alpha, 0x00000000 = no alpha
int: image width
int: image height
int: flag for alpha channel and team colors (usually 3, 4 or 5), 3 and 4 means color and alpha information for paletted files,
5 means only color information, if >=5 on 'unit' textures, it won't show the team color.
int: always 0x00000001, if 0x00000000 the model that uses this texture will be messy.
int[16]: mipmap offset (offset from the begining of the file)
int[16]: mipmap size (size of mipmaps)

If it's a JPG-BLP we go on with:
int: jpg header size (header size) "h" (usually 0x00000270)
byte[h]: header
followed by 0 bytes until the begining of the jpeg data, we can safely erase these 0 bytes if we fix the mipmap offset specified above
byte[16, mipmap size]: starting from each of the 16 mipmap offset addresses we read 'mipmap size' bytes raw jpeg data till the end of the file, having the header and the mipmap data we can process the picture like ordinary JPG files

If it's a paletted BLP we go here:
byte[4, 255]: the BGRA palette defining 256 colors by their BGRA values, each 1 byte
byte[width x height]: the ColorIndex of each pixel from top left to bottom right, ColorIndex refers to the above defined color palette
byte[width x height]: the AlphaIndex of each pixel on a standard greyscale palette for the alpha channel, where 0 is fully transparent and 255 is opaque,
if the picturetype flag is set to 5, the image doesn't have an alpha channel, so this section will be omitted

Or Magos' specification:

//+-----------------------------------------------------------------------------
//| Info
//+-----------------------------------------------------------------------------
The BLP file format!
Compiled by Magnus Ostberg (aka Magos)
[email protected]


//+-----------------------------------------------------------------------------
//| Data types
//+-----------------------------------------------------------------------------
CHAR - 8bit character
BYTE - 8bit unsigned integer
WORD - 16bit unsigned integer
DWORD - 32bit unsigned integer
FLOAT - 32bit floating point number
COLOR - 32bit color value of type RGBA, one byte per channel
X[n] - An n-dimensional vector of type X


//+-----------------------------------------------------------------------------
//| Descriptions
//+-----------------------------------------------------------------------------
[X | Y]; - Exactly one of the structures X and Y are present.

X; - A structure that must be present.

#X - A flag value, more than one can be combined.


//+-----------------------------------------------------------------------------
//| Notes
//+-----------------------------------------------------------------------------
- A full mipmap chain must be present. The last mipmap must be 1x1 (no larger).
If an image is 32x8 the mipmap chain must be 32x8, 16x4, 8x2, 4x1, 2x1, 1x1.
Sizes not of powers of 2 seems to work fine too, the same rules for mipmaps
still applies. Ex: 24x17, 12x8 (rounded down), 6x4, 3x2, 1x1 (rounded down).


//+-----------------------------------------------------------------------------
//| BLP structure
//+-----------------------------------------------------------------------------
struct Blp
{
DWORD 'BLP1';
DWORD Compression; //0 - Uses JPEG compression
//1 - Uses palettes (uncompressed)
DWORD Flags; //#8 - Uses alpha channel (?)
DWORD Width;
DWORD Height;
DWORD PictureType; //3 - Uncompressed index list + alpha list
//4 - Uncompressed index list + alpha list
//5 - Uncompressed index list
DWORD PictureSubType; //1 - ???
DWORD MipMapOffset[16];
DWORD MipMapSize[16];

[BlpJpeg | BlpUncompressed1 | BlpUncompressed2]
};


//+-----------------------------------------------------------------------------
//| BLP JPEG structure (Compression == 0)
//+-----------------------------------------------------------------------------
struct BlpJpeg
{
DWORD JpegHeaderSize;

BYTE[JpegHeaderSize] JpegHeader;

struct MipMap[16]
{
BYTE[???] JpegData;
};

// Up to 16 mipmaps can be stored in a blp image. 2^16 = 65536, so there's
// little risk it won't be enough. Each JPEG (JFIF to be more exact) image
// is constructed by merging the header with the mipmap (all mipmaps uses
// the same header. It seems like Warcraft 3 can handle JPEG header sizes
// of 0 (in case you have trouble generating JPEG images using the same
// header) however there are other fan tools that does not. Specifying a
// low number like 4 will work too as the only shared data are the initial
// JPEG markers.
//
// Each mipmap has a certain size and is located at a certain offset as
// specified in the main blp header. There can be (and sometimes are in
// Blizzard's images) unused space between the JPEG header and the JPEG
// data. Why this is I don't know!
//
// The JPEG header of Blizzard's images is usually 624 bytes long. This
// may or may not be true for your own generated images depending on how
// you generated them.
//
// The JPEG format is advanced so I won't go into detail here.
};


//+-----------------------------------------------------------------------------
//| BLP Uncompressed 1 structure (Compression == 1, PictureType == 3 or 4)
//+-----------------------------------------------------------------------------
struct BlpUncompressed1
{
COLOR[256] Palette;

struct MipMap[16]
{
BYTE IndexList[CurrentWidth * CurrentHeight];
BYTE AlphaList[CurrentWidth * CurrentHeight];
};

// CurrentWidth/CurrentHeight is the width/height for the current mipmap.
// Mipmap size/offset works the same as explained for JPEGs above.
//
// Each cell in the index list refers to a location in the palette where
// the corresponding RGB value is (the palette is still RGBA, but A is not
// used). The alpha list contains the alpha value for the pixel.
};


//+-----------------------------------------------------------------------------
//| BLP Uncompressed 2 structure (Compression == 1, PictureType == 5)
//+-----------------------------------------------------------------------------
struct BlpUncompressed2
{
COLOR[256] Palette;

struct MipMap[16]
{
BYTE IndexList[CurrentWidth * CurrentHeight];
};

// CurrentWidth/CurrentHeight is the width/height for the current mipmap.
// Mipmap size/offset works the same as explained for JPEGs above.
//
// Each cell in the index list refers to a location in the palette where
// the corresponding RGBA value is. The alpha value is inversed so the real
// alpha is "255 - alpha".
};
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
It doesn't use anything fancy, it's just a normal 32bit BGRA format compressed as JPEG. It's non-standard, hence why software fails to load it properly. 4 channel JFIFs are only used by extensions as far as I know (Adobe and stuff like that), except for Blizzard of course.
JFIF standard supports 4 channel RGBA under JPEG2000 compression according to some sources. However I am unsure when that was added to the standard.

What is interesting is how in Windows 10 the image display is affected by colour profiles. What should be black is not black (incorrect gamma?). It also appears to see the alpha channel information as it shows true black where their is alpha but not true black where there is no alpha and it is meant to be true black.
attachment.php

Next to that incorrect gamma and lack of forwarding alpha information it would appear to be interpreted correctly.

Do you know the following?
Or Magos' specification:
Both of those I know about and have read. Unfortunately does not help with the JPEG compression much.

EDIT:

After some messing around I have come up with the following details.
  • The JPEG images are ABGR encoded. If they appear messed up (eg black being green) that is because 3 of the channels (BGR?) are being treated as YCBCR colours so are being wrongly converted to another colour space.
  • The images fail metadata validation checks by some readers such as the JAVA standard JPEG plugin. I am currently unsure of how to pull meta data down short of a custom JPEG plugin or one from another party (a huge pain...).
  • I think alpha is not pre-multiplied, however that might also be due some processing by the reader. If I set the destination image to use pre-multiplied alpha artefacts appear.
  • To read the colour values correctly you need to bypass the YCBCR conversion most JPEG reader pipelines apply. This can be done by specifying a different colour profile (what Blizzard must have done with the Intel JPEG library) or in the case of JAVA simply accessing the rasterizer of the image reader (which reason for existence is specifically for situations like this).
  • In JAVA one should be able to manipulate the rasterizers such that the R and B colour channel swap is performed as the BufferedImage (or returned Rasterizer) is produced from the JPEG reader Rasterizer. This should produce the output image with correct colour channels in a single step.

EDIT:
And here is the result (uncompressed PNG) of processing the image with the JAVA standard JPEG plugin.
attachment.php

Colours should now be correct and alpha present. Not sure if the alpha is being correctly output though (although the data is there as one can see).
 

Attachments

  • imgdbstrange.jpg
    imgdbstrange.jpg
    23.5 KB · Views: 524
  • imgdb.png
    imgdb.png
    715.3 KB · Views: 509
Last edited:
Level 29
Joined
Jul 29, 2007
Messages
5,174
[*]The JPEG images are ABGR encoded. If they appear messed up (eg black being green) that is because 3 of the channels (BGR?) are being treated as YCBCR colours so are being wrongly converted to another colour space.

Yes, because like I said, 4 channel JFIF is used by actual registered extensions, not any company making random changes.
I don't think anyone in the world supports JPEG2000.

I too had to skip the color space transformations done by the JS JPG decoder I found.

Either way...good job? not sure why you are doing this, but whatever.
 
Status
Not open for further replies.
Top