Jump to content

Headerless Bitmap Reconstructor.


JrMasterModelBuilder
 Share

Recommended Posts

JrMasterModelBuilder

Well, lately I've been trying to figure out if the .BMP files in the LEGO.JAM file for LEGO Racers are headerless BMP files, which they appear to be.To try to accomplish it, I created a utility to read .BMP hex and convert it to pixel data with the width and height provided.

While I haven't had any luck with the LEGO Racers .bmp files I though maybe someone else would find it useful, so here it is.

I made it in Flash so unfortunately it gets very slow on larger images (I would redo it in C++ if I knew how). It works by taking hex you paste in, spliting it around spaces, reordering it into arrays, and setting a bunch of 1px/1px movie clips to the pixel colors. Not the prettiest thing I've ever done, but it works. I included a sample bitmap file (64x64) and a text file containing the hex that must be entered to recreate it.

If you guys want, I can post source code.

VERSION 2 (approx. 4x faster):

http://www.mediafire.com/?ot5kbu2ihsv6xiz

VERSION 1:

http://www.mediafire.com/?c5hdh7pnjmd80kh

Link to comment
Share on other sites

Neat, yea it doesn't work with the Lego Races BMP's. Maybe helpful someday.

@TMLC, yes, you have to know the size.

Link to comment
Share on other sites

OK so I programmed it in python using pygame. It failed because there is extraneous data in your apparently headerless BMP. Its filesize in bytes should be 12288 or 16384 depending on pixel format, but it's 12344. So I assume there's a footer left or you missed out part of the header. In any case, here's the source code, it's an ugly hack and I only really made it as a test, bla bla copyright is mine..


import pygame

import time


fname = raw_input("Filename? ")

f = open(fname, "r")

bindata = f.read()

w = int(raw_input("X size? "))

h = int(raw_input("Y size? "))

fmt = raw_input("""Pixel Format? "

	# RGB, 24bit image

	# RGBX, 32bit image with unused space

	# RGBA, 32bit image with an alpha channel

	# ARGB, 32bit image with alpha channel first

	# RGBA_PREMULT, 32bit image with colors scaled by alpha channel

	# ARGB_PREMULT, 32bit image with colors scaled by alpha channel, alpha channel first 

Choice: """)


pygame.init()

img = pygame.image.fromstring(bindata, (w, h), fmt)

screen = pygame.display.set_mode((w, h))

screen.blit((0, 0), img)

pygame.display.flip()

time.sleep(2)

So yeah, as I said this is hackish, but should work on correctly truncated BMPs.

Link to comment
Share on other sites

Because of the way the data is stored you have to know the correct size; just giving it extra room won't work because then it basically takes the next row and puts it in the first until it runs out of space in the first.

Thus you need the size.

Link to comment
Share on other sites

I wondered what would happen when you DIDN'T know the size and made sure that you had enough room for it.

The pixels are stored in a simple stream and the rows and columns are defined only by the size usually given in the header, so depending on the error, the image will be distorted or completely dead.

EDIT: dammit acmex ninja'd me and stopped the posts from merging.

EDIT2: Also my program will error if you give it the wrong info. The only thing it's lenient on is pixel format IF you only got the order wrong, it will display in unpredictable colours but not complain.

Link to comment
Share on other sites

gallery_1300_107_439.png

Hmmm, will this always do that when you oversize the input?

Oh. So that WASN'T Spam... My deepest apologies, Tracker.

Link to comment
Share on other sites

JrMasterModelBuilder

OK so I programmed it in python using pygame. It failed because there is extraneous data in your apparently headerless BMP. Its filesize in bytes should be 12288 or 16384 depending on pixel format, but it's 12344. So I assume there's a footer left or you missed out part of the header. In any case, here's the source code, it's an ugly hack and I only really made it as a test, bla bla copyright is mine..


import pygame

import time


fname = raw_input("Filename? ")

f = open(fname, "r")

bindata = f.read()

w = int(raw_input("X size? "))

h = int(raw_input("Y size? "))

fmt = raw_input("""Pixel Format? "

	# RGB, 24bit image

	# RGBX, 32bit image with unused space

	# RGBA, 32bit image with an alpha channel

	# ARGB, 32bit image with alpha channel first

	# RGBA_PREMULT, 32bit image with colors scaled by alpha channel

	# ARGB_PREMULT, 32bit image with colors scaled by alpha channel, alpha channel first 

Choice: """)


pygame.init()

img = pygame.image.fromstring(bindata, (w, h), fmt)

screen = pygame.display.set_mode((w, h))

screen.blit((0, 0), img)

pygame.display.flip()

time.sleep(2)

So yeah, as I said this is hackish, but should work on correctly truncated BMPs.
Yeah, the bitmap I gave has both the header and the footer. The text doc has the hex that would have to be entered. To get the raw pixel data, you have to cut off the first 36 and the last 2 bytes (which I think are always 00 00, just like in the LEGO Racers not bitmaps). Anyway, I rewrote it today in Actionscript 3 (it was in AS2). Now it executes about 4 times faster. I also included a headerless copy of the BMP file: http://www.mediafire.com/?ot5kbu2ihsv6xiz For those trying to rewrite it in other languages, here's source code for both versions:

EDIT: My program only supports 24-bit BMP files at the moment. I'm looking to add 16-bit BMP support, but I'm having some trouble figuring out the file format.


//BMPreconstructV2 in AS3.

//JrMasterModelBuilder

function createImage():void

{

	var input:String = inputArray_txt.text;


	var imgWidth:int = Number(inputWidth_txt.text);

	var imgHeight:int = Number(inputHeight_txt.text);


	//Split up the hex.

	var inputArray:Array = input.split(" ");


	//Make new array by taking the hex in 3 sections of the old array.

	var colorArray:Array = new Array();

	var arrayCounter:int = 0;

	for(var i:int = 0; i < inputArray.length; i = i+3)

	{

		colorArray[arrayCounter] = "0x" + inputArray[i+2] + inputArray[i+1] + inputArray[i];

		arrayCounter++;

	}


	//Remove old if existing.

	if(memory.removeOld)

	{

		removeChild(getChildByName("imgContainer_mc"));

	}

	//Make new container.

	var imgContainer_mc:MovieClip = new MovieClip();

	imgContainer_mc.x = 0;

	imgContainer_mc.y = 0;

	imgContainer_mc.name = "imgContainer_mc";

	addChild(imgContainer_mc);

	//Remember there already is one.

	memory.removeOld = true;


	//Counter for each mc.

	var counter:int = 0;

	//Varaible for height placement, starting bottom to top.

	var i3:int = imgHeight - 1;

	//Loop through the height.

	for(var i1:int = 0; i1 < imgHeight; i1++)

	{

		//And the width.

		for(var i2:int = 0; i2 < imgWidth; i2++)

		{

			var mc:MovieClip = new MovieClip();

			mc.name = "draw" + counter + "_mc";

			mc.graphics.beginFill(colorArray[counter]);

			mc.graphics.drawRect(0, 0, 1, 1);

			mc.graphics.endFill();

			mc.x = i2;

			mc.y = i3;

			imgContainer_mc.addChild(mc);

			counter++;

		}

		//Decrease so as to move up.

		i3--;

	}

}


function enter_btn_f(e:MouseEvent):void

{

	createImage()

}

enter_btn.addEventListener(MouseEvent.MOUSE_UP,enter_btn_f);


function wInc_btn_f(e:MouseEvent):void

{

	var number = Number(inputWidth_txt.text) + 1;

	inputWidth_txt.text = number;

	createImage()

}

wInc_btn.addEventListener(MouseEvent.MOUSE_UP,wInc_btn_f);


function wDec_btn_f(e:MouseEvent):void

{

	var number = Number(inputWidth_txt.text) - 1;

	inputWidth_txt.text = number;

	createImage()

}

wDec_btn.addEventListener(MouseEvent.MOUSE_UP,wDec_btn_f);


function hInc_btn_f(e:MouseEvent):void

{

	var number = Number(inputHeight_txt.text) + 1;

	inputHeight_txt.text = number;

	createImage()

}

hInc_btn.addEventListener(MouseEvent.MOUSE_UP,hInc_btn_f);


function hDec_btn_f(e:MouseEvent):void

{

	var number = Number(inputHeight_txt.text) - 1;

	inputHeight_txt.text = number;

	createImage()

}

hDec_btn.addEventListener(MouseEvent.MOUSE_UP,hDec_btn_f);


//Create object to store variables.

var memory:Object = new Object();

//Blank the input field.

inputArray_txt.text = "";


//BMPreconstruct in AS2.

//JrMasterModelBuilder

createImage = function()

{

	input = inputArray_txt.text;


	imgWidth = int(inputWidth_txt.text);

	imgHeight = int(inputHeight_txt.text);


	//Split up the hex.

	inputArray = input.split(" ");


	//Make new array by taking the hex in 3 sections of the old array.

	colorArray = new Array();

	arrayCounter = 0;

	for(var i = 0; i < inputArray.length; i = i+3)

	{

		colorArray[arrayCounter] = inputArray[i+2] + inputArray[i+1] + inputArray[i];

		arrayCounter++;

	}


	//Remove old if existing.

	imgContainer_mc.removeMovieClip();

	//Make new container.

	createEmptyMovieClip("imgContainer_mc", 10);


	//Counter for each mc.

	counter = 0;

	//Varaible for height placement, starting bottom to top.

	var i3 = imgHeight - 1;

	//Loop through the height.

	for(var i = 0; i < imgHeight; i++)

	{

		//And the width.

		for(var i2 = 0; i2 < imgWidth; i2++)

		{

			imgContainer_mc.attachMovie("draw_mc", "draw" + counter + "_mc", 10 + counter);

			imgContainer_mc["draw" + counter + "_mc"]._x = i2;

			imgContainer_mc["draw" + counter + "_mc"]._y = i3;		

			counter++;

		}

		//Decrease so as to move up.

		i3--;

	}


	//Loop through the new coloring array setting the pixel colors.

	for(var i = 0; i < colorArray.length; i++)

	{

		recolor = new Color(imgContainer_mc["draw" + i + "_mc"]);

		recolor.setRGB("0x" + colorArray[i]);

	}

}

enter_btn.onRelease = function()

{

	createImage();

}

wInc_btn.onRelease = function()

{

	inputWidth_txt.text = int(inputWidth_txt.text) + 1;

	createImage();

}

wDec_btn.onRelease = function()

{

	inputWidth_txt.text = int(inputWidth_txt.text) - 1;

	createImage();

}

hInc_btn.onRelease = function()

{

	inputHeight_txt.text = int(inputHeight_txt.text) + 1;

	createImage();

}

hDec_btn.onRelease = function()

{

	inputHeight_txt.text = int(inputHeight_txt.text) - 1;

	createImage();

}

inputArray_txt.text = "";

Link to comment
Share on other sites

import pygame

import time


fname = raw_input("Filename? ")

f = open(fname, "r")

bindata = f.read()

w = int(raw_input("X size? "))

h = int(raw_input("Y size? "))

fmt = raw_input("""Pixel Format? "

        # RGB, 24bit image

        # RGBX, 32bit image with unused space

        # RGBA, 32bit image with an alpha channel

        # ARGB, 32bit image with alpha channel first

        # RGBA_PREMULT, 32bit image with colors scaled by alpha channel

        # ARGB_PREMULT, 32bit image with colors scaled by alpha channel, alpha channel first 

Choice: """)


pygame.init()

img = pygame.image.fromstring(bindata, (w, h), fmt)

screen = pygame.display.set_mode((w, h))

screen.blit(img, (0, 0))

pygame.display.flip()

time.sleep(2)

Oops I made a mistake. It works now. Possible/likely/definite bugs:

  • Images being displayed upside down (definitely)
  • Images being displayed in the wrong colours (likely)

Any other errors are probably your fault or the fault of the person who gave you the file.

Link to comment
Share on other sites

JrMasterModelBuilder

import pygame

import time


fname = raw_input("Filename? ")

f = open(fname, "r")

bindata = f.read()

w = int(raw_input("X size? "))

h = int(raw_input("Y size? "))

fmt = raw_input("""Pixel Format? "

        # RGB, 24bit image

        # RGBX, 32bit image with unused space

        # RGBA, 32bit image with an alpha channel

        # ARGB, 32bit image with alpha channel first

        # RGBA_PREMULT, 32bit image with colors scaled by alpha channel

        # ARGB_PREMULT, 32bit image with colors scaled by alpha channel, alpha channel first 

Choice: """)


pygame.init()

img = pygame.image.fromstring(bindata, (w, h), fmt)

screen = pygame.display.set_mode((w, h))

screen.blit(img, (0, 0))

pygame.display.flip()

time.sleep(2)
Oops I made a mistake. It works now. Possible/likely/definite bugs:
  • Images being displayed upside down (definitely)
  • Images being displayed in the wrong colours (likely)
Any other errors are probably your fault or the fault of the person who gave you the file.
I haven't tried your code yet, but I think you might be having the same problems I had. For upside-down images: Bitmaps don't go top left to bottom right as I expected, but bottom left to top right, like so:

[08][09][10][11]

[04][05][06][07]

[00][01][02][03]

As for the wrong image colors, the RGB places are in reverse (BGRs I guess :P ). 125689 = 895612 in a 24-bit bitmap.

Link to comment
Share on other sites

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.