Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 16:34 28 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CMM2: I can't work out how to save and then load 8-bit graphics

     Page 1 of 2    
Author Message
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 12:55am 04 Feb 2024
Copy link to clipboard 
Print this post

I'm struggling with how to save and reload an 8-bit image with a custom palette. I've gone through the docs and tried a bunch of different things but I can't work out how to do it.

If I enter the following code, it assigns custom colours (using the MAP command) to the first 150 or so colours in the 8 bit palette. It then draws a line for each colour and saves it.

My idea was that I would use this standard palette on all of the sprite pages of my new game so that they shared the same 256 colours.


OPTION EXPLICIT
OPTION DEFAULT NONE
MODE 7,8      ' 320x240 with 256 colours
SetColourPalette()
Create8BitImage()


SUB Create8BitImage
 LOCAL INTEGER X%, T%
 X%=0
 FOR T%=0 TO 255
   LINE T%,0,T%,15,1,MAP(T%)
 NEXT T%
 SAVE IMAGE "PALETTE.BMP"
 DO WHILE 1=1: LOOP
END SUB

SUB SetColourPalette
 LOCAL INTEGER X%, T%, COLOURINCREMENT%, COLOURCOUNT%

 ' Define our reserved colours first
 MAP(0)=RGB(0,0,0) ' 0 is black, 1 is almost black
 MAP(1)=RGB(1,1,1)
 X%=31   ' We already have black defined, so this is the colour component we will start at
         '(i.e. (31,0,0) or (31,31,31) etc.
 COLOURINCREMENT%=20   ' We will increment the colour component by 20 each time
 COLOURCOUNT%=11 ' We will have 11 distinct colours for each combination
 FOR T%=0 to COLOURCOUNT%-1  
   MAP(2+T%+COLOURCOUNT%*0)=RGB(X%,0,0)
   MAP(2+T%+COLOURCOUNT%*1)=RGB(0,X%,0)
   MAP(2+T%+COLOURCOUNT%*2)=RGB(0,0,X%)
   MAP(2+T%+COLOURCOUNT%*3)=RGB(X%,X%,0)
   MAP(2+T%+COLOURCOUNT%*4)=RGB(X%,0,X%)
   MAP(2+T%+COLOURCOUNT%*5)=RGB(0,X%,X%)
   MAP(2+T%+COLOURCOUNT%*6)=RGB(X%,X%,X%)
   MAP(2+T%+COLOURCOUNT%*7)=RGB(X%,X%/2,0)
   MAP(2+T%+COLOURCOUNT%*8)=RGB(X%/2,X%,0)
   MAP(2+T%+COLOURCOUNT%*9)=RGB(X%,0,X%/2)
   MAP(2+T%+COLOURCOUNT%*10)=RGB(X%/2,0,X%)
   MAP(2+T%+COLOURCOUNT%*11)=RGB(0,X%/2,X%)
   MAP(2+T%+COLOURCOUNT%*12)=RGB(0,X%,X%/2)
   MAP(2+T%+COLOURCOUNT%*13)=RGB(X%/2,X%,X%)
   MAP(2+T%+COLOURCOUNT%*14)=RGB(X%,X%/2,X%)
   MAP(2+T%+COLOURCOUNT%*15)=RGB(X%,X%/2,X%/2)
   X%=X%+COLOURINCREMENT%
 NEXT T%
 MAP SET
END SUB


This draws the following image, and saves it to "PALETTE.BMP"



If I then try and reload the image using the following code, it comes back corrupted.

OPTION EXPLICIT
OPTION DEFAULT NONE
MODE 7,8      ' 320x240 with 256 colours
SetColourPalette()
'Create8BitImage()   <- Commented out
LOAD BMP "PALETTE.BMP"
DO WHILE 1=1: LOOP





Note: The outcome doesn't change if I comment out the SetColourPalette() subroutine.
I'm running Firmware 5.07.01

I've been wracking my brains over this one. What am I doing wrong please? How can I save the image so that my 8 bit sprites share the same palette across multiple pages/BMP files.
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5905
Posted: 02:13am 04 Feb 2024
Copy link to clipboard 
Print this post

MMBasic saves images in 24 bit format.
There are some versions that have a compressed mode as well but I don't think the CMM2 has it.

You might be able to reduce the 24 bit image to 8 bit with an external program.

Jim
VK7JH
MMedit   MMBasic Help
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5905
Posted: 03:09am 04 Feb 2024
Copy link to clipboard 
Print this post

THis is one result of reducing the palate.

Effected_PALETTE.zip
VK7JH
MMedit   MMBasic Help
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 03:54am 04 Feb 2024
Copy link to clipboard 
Print this post

  TassyJim said  MMBasic saves images in 24 bit format.
There are some versions that have a compressed mode as well but I don't think the CMM2 has it.

You might be able to reduce the 24 bit image to 8 bit with an external program.

Jim


Thanks Jim. I did try a number of different file formats, but even loading the image into Paint.Net as 24 bit shows some corrupted colours (although not as bad as when you reload).

But armed with the knowledge you provided, I looked at a Hex dump of the file and we might have two separate issues.

The first is a potential corruption of the 24-bit image file during save.



The first two numbers are mapped correctly (0,0,0) and (1,1,1). Then it goes into the loop and the next sequence of 10 numbers should be (0,0,0x1F), (0,0,0x33) (0,0,0x47) (0,0,0x58) (0,0,0x6F) (0,0,0x83) (0,0,0x97) (0,0,0xAB) (0,0,0xBF) (0,0,0xD3) (0,0,0xE7)

(In decimal those number are: 31,51,71,91,111,131,151,171,191,211,231)

That first number 0x1F is 4 more than it should be in the raw file dump. Then next one is 7 more. The next is 9 more, and the gap keeps getting larger until we get to the final number (0,0,0xE7) which looks like it has overflowed 255 and reverted to 6.

That might account for why the image won't load (uncorrupted) into Paint.Net. But also, I don't want to jump to conclusions - does anyone have any decent knowledge of the BMP file format that might confirm or refute my hypothesis? Or (more likely) is there a flaw in my program logic and I'm miscalculating the numbers?

And the second issue is then that we appear to have no way of storing an image with MAP-ed colours. I now realise that this is not a problem. I can write my own file save and load routine for that. The CMM2 can write a full screen of data pixel by pixel in about 600ms, so it's easily fast enough to rebuild an image from file. Especially given that was just using the PIXEL command. If I loaded an array of bytes and dumped it directly into the memory locations I'm sure it would be much faster. I really just needed a sanity check to see if I wasn't missing something fundamental (which I might still be).

I might even still roll my own file load now that I think about it. It would allow me to intersperse the loading with an animated file screen (so load a bit of data for 100ms, colour swap the title screen to make something move, load the next 100ms of data, animate and so forth). It could be quite a cool effect.
Edited 2024-02-04 13:55 by PeteCotton
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5905
Posted: 04:51am 04 Feb 2024
Copy link to clipboard 
Print this post

When you use 8 bit modes the colours are mapped to 3bit,3bit,2bit so what you read may not be what you wrote.

mode 7,8
CLS
FOR n = 0 TO 255
PIXEL 10,10,RGB(n,n,n)
PRINT HEX$(n,2),HEX$(PIXEL(10,10),6)
NEXT n



As a start, you have to stay with the available colours for your chosen mode.

Jim
VK7JH
MMedit   MMBasic Help
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 05:53am 04 Feb 2024
Copy link to clipboard 
Print this post

  TassyJim said  When you use 8 bit modes the colours are mapped to 3bit,3bit,2bit so what you read may not be what you wrote.

Jim


Thanks, that is an excellent point, and I thought that was the case as well, but a 2 bit blue would only give us four possible values for the blue component, yet the above file has 13 discreet values, so I'm not sure that's it.

  TassyJim said  
As a start, you have to stay with the available colours for your chosen mode.

Jim


Never Sir! Mere chains cannot hold me  



Edited 2024-02-04 16:42 by PeteCotton
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5905
Posted: 07:16am 04 Feb 2024
Copy link to clipboard 
Print this post

I didn't consider what MAP does to things.

Jim
VK7JH
MMedit   MMBasic Help
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1783
Posted: 08:06am 04 Feb 2024
Copy link to clipboard 
Print this post

I don't know if this is relevant, long ago I dissected BMP files byte by byte to make a OCR program for punched paper tape.
Here is a bit of what I found.

In 24 bit format basic BMP files are the simplest and use 3 bytes / pixel. The only wrinkle is that each line must be a multiple of 32 bits, so there may need to be padding bytes at the end of each line to make that happen.

It should be possible to see if that is being dealt with by making a test image whose width is an exact multiple of 12 pixels and comparing with one that is 1 pixel wider.
The decoder should remove the padding bits, if not thing go wonky.
If the first decodes correctly and the second doesn't the decoder has a problem.

For BMPs of 1 to 8 bits / pixel the the header will contain a palette of 2 to 256 32 bit words (top 8 bits not used) to define how the bitmap is read.
Again each line is padded to a multiple of 32 bits.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 09:16am 04 Feb 2024
Copy link to clipboard 
Print this post

Sounds like a bug/lack of feature in that save image isn't respecting a changed map. Leave it with me and I'll post a new CMM2 beta when fixed

UPDATE

This is properly hard. I would need to write a compressed 8-bit BMP file which isn't currently supported on any platform and also a new image read which understands the 8-bit image file and also automatically sets the colour map.

Not going to be done soon I'm afraid
Edited 2024-02-04 20:07 by matherp
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 04:49pm 04 Feb 2024
Copy link to clipboard 
Print this post

  phil99 said  In 24 bit format basic BMP files are the simplest and use 3 bytes / pixel. The only wrinkle is that each line must be a multiple of 32 bits, so there may need to be padding bytes at the end of each line to make that happen.


Ah - I wonder if that's the issue I'm seeing in the hex dump.

  matherp said  Sounds like a bug/lack of feature in that save image isn't respecting a changed map. Leave it with me and I'll post a new CMM2 beta when fixed

UPDATE

This is properly hard. I would need to write a compressed 8-bit BMP file which isn't currently supported on any platform and also a new image read which understands the 8-bit image file and also automatically sets the colour map.

Not going to be done soon I'm afraid


Not to worry - I was just worried that I was missing something simple. I think I have a manual work around - but once again, thank you for looking at it. I know we say this a lot (but not nearly as much as is deserved), I do appreciate the amount of work you've already put into this amazing computer.
Edited 2024-02-05 02:52 by PeteCotton
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 06:55pm 04 Feb 2024
Copy link to clipboard 
Print this post

For anyone else facing the same issue, here's a quick and dirty work around.
Note: This is not a .BMP file format, it's just a raw memory dump and load. You still have to set the colour palette each time, and you can't open it in a picture editor. But it does allow you to save and load 8 bit images to and from your graphics pages.
SUB Save8BitPage FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%=0
 SIZE%=WIDTH%*HEIGHT%
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 SAVE DATA FILENAME$, ADDRESS%, SIZE%
END SUB

SUB Load8BitImage FILENAME$, PAGENUM%
 LOCAL INTEGER ADDRESS%
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 LOAD DATA FILENAME$, ADDRESS%
END SUB


This can be used as follows with the original code
OPTION EXPLICIT
OPTION DEFAULT NONE
OPTION EXPLICIT
OPTION DEFAULT NONE
MODE 7,8      ' 320x240 with 256 colours
SetColourPalette()
Create8BitImage()
Save8BitPage "IMAGE1.DAT", 0, 320, 240

The Save8BitPage function takes the following parameters:
- Filename (I've used the .DAT file extension but it can be anything)
- Page Number to save
- Width of image in pixels
- Height of image in pixels

Note: This only works with 8 bit images.

Then when you want to load your image you can use the following:
OPTION EXPLICIT
OPTION DEFAULT NONE
OPTION EXPLICIT
OPTION DEFAULT NONE
MODE 7,8      ' 320x240 with 256 colours
Load8BitImage "IMAGE1.DAT", 0
DO WHILE 1=1: LOOP


The Load8BitImage function only needs the filename and the Page Number that you want to load the image into.
The function expects the graphics mode to be set to the same width/height as the original image was saved at, and once again, it only works with 8 bit images.

Rather shockingly, this image loads in 64 milliseconds. Which is extremely quick. This is because it's not doing any processing (just reading/writing directly from the ram) - so I'm inclined to think that even once I've got my sprites done in a graphics program, I will then convert them to this memory dump format and use that for loading them.

The colour map still needs to be set within the code.
Edited 2024-02-05 04:56 by PeteCotton
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 09:38pm 04 Feb 2024
Copy link to clipboard 
Print this post

Here's a couple of functions to write and read 8 bit Bitmap files.

SUB Save8BitBMP FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%, OFFSET%, T%
 LOCAL STRING FILESIZEHEX$, WIDTHHEX$, HEIGHTHEX$
 
 SIZE%=WIDTH%*HEIGHT%
 LOCAL INTEGER BYTES(1078 + SIZE%)
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 ' Header
 BYTES(0) = 66: BYTES(1) = 77   ' BM
 FILESIZEHEX$ = HEX$(1078+SIZE%,8) ' Filesize in bytes (Hex - Littleendian)
 BYTES(2)= VAL("&H" + MID$(FILESIZEHEX$,7,2))
 BYTES(3)= VAL("&H" + MID$(FILESIZEHEX$,5,2))
 BYTES(4)= VAL("&H" + MID$(FILESIZEHEX$,3,2))
 BYTES(5)= VAL("&H" + MID$(FILESIZEHEX$,1,2))
 BYTES(6)=0:BYTES(7)=0:BYTES(8)=0:BYTES(9)=0   ' Unused
 ' The offset to the start of data is 1078, which is 0x436
 ' so in little endian it's 0x36 0x04 0x00 0x00
 BYTES(10)=54:BYTES(11)=4:BYTES(12)=0:BYTES(13)=0
 BYTES(14)=40:BYTES(15)=0:BYTES(16)=0:BYTES(17)=0    ' Size of info header (littleendian)
 WIDTHHEX$=HEX$(WIDTH%,8)        ' Width in pixels (Hex - littlendian)
 BYTES(18)= VAL("&H" + MID$(WIDTHHEX$,7,2))
 BYTES(19)= VAL("&H" + MID$(WIDTHHEX$,5,2))
 BYTES(20)= VAL("&H" + MID$(WIDTHHEX$,3,2))
 BYTES(21)= VAL("&H" + MID$(WIDTHHEX$,1,2))
 HEIGHTHEX$=HEX$(HEIGHT%,8)       ' Height in pixels (Hex - littlendian)
 BYTES(22)= VAL("&H" + MID$(HEIGHTHEX$,7,2))
 BYTES(23)= VAL("&H" + MID$(HEIGHTHEX$,5,2))
 BYTES(24)= VAL("&H" + MID$(HEIGHTHEX$,3,2))
 BYTES(25)= VAL("&H" + MID$(HEIGHTHEX$,1,2))
 BYTES(26)=1:BYTES(27)=0          ' Number of planes (hex - little endian)
 BYTES(28)=8:BYTES(29)=0           ' Bits per pixel
 BYTES(30)=0:BYTES(31)=0:BYTES(32)=0:BYTES(33)=0       ' Compression - none
 BYTES(34)=0:BYTES(35)=0:BYTES(36)=0:BYTES(37)=0       ' Compressed file size - not valid
 BYTES(38)=196:BYTES(39)=14:BYTES(40)=0:BYTES(41)=0    ' XPixels Per M
 BYTES(42)=196:BYTES(43)=14:BYTES(44)=0:BYTES(45)=0    ' YPixels Per M
 BYTES(46)=0:BYTES(47)=1:BYTES(48)=0:BYTES(49)=0       ' 256 colours (Hex 0x100)
 BYTES(50)=0:BYTES(51)=0:BYTES(52)=0:BYTES(53)=0       ' Number of important colours 0=all
 ' Colour table (Stored in BGR0 format)
 FOR T%=0 TO 255
   BYTES(54 + T*4 + 0) = MAPBLUE(T%)
   BYTES(54 + T*4 + 1) = MAPGREEN%(T%)
   BYTES(54 + T*4 + 2) = MAPRED%(T%)
   BYTES(54 + T*4 + 3) = 0
 NEXT T%
 ' Pixel data
 FOR T%=0 TO SIZE%-1
   BYTES(1078+T%)=PEEK(BYTE ADDRESS%+T%)
 NEXT T%
 
 OPEN FILENAME$ FOR OUTPUT AS #1
 FOR T%=0 TO 1078+SIZE%-1
   PRINT #1, CHR$(BYTES(T%));
 NEXT T%
 CLOSE #1
END SUB

SUB Load8BitBMP FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%, OFFSET%, T%, BYTE%
 LOCAL STRING FILESIZEHEX$, WIDTHHEX$, HEIGHTHEX$, CHARACTER$
 SIZE%=WIDTH%*HEIGHT%
 
 LOCAL INTEGER BYTES(1078 + SIZE%)
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 
 OPEN FILENAME$ FOR INPUT AS #1
 FOR T%=0 TO 1078+SIZE%-1
   CHARACTER$ = INPUT$(1,#1)
   BYTES(T%) = ASC(CHARACTER$)
 NEXT T%
 CLOSE #1
 ' Decode the image
 ' Lets jump straight to the colour mapping data
 FOR T%=0 to 255
   MAP(T%)=RGB(BYTES(54 + T*4 + 2),BYTES(54 + T*4 + 1),BYTES(54 + T*4 + 0))
 NEXT T%
 ' Now lets do the pixel data
 FOR T%=0 TO SIZE%-1
   POKE BYTE ADDRESS%+T%, BYTES(1078+T%)
   'BYTES(1078+T%)=PEEK(BYTE ADDRESS%+T%)
 NEXT T%
END SUB


I'm not sure the logic is resilient enough to be added to the operating system - as there is no error checking, but it is small enough for people to paste into their code.

I couldn't find a way to read the mapped colour RGB values. So, when I'm generating my palette in code, I also save the RGB values to the following arrays
DIM INTEGER MAPRED%(256),MAPGREEN%(256),MAPBLUE%(256)


And then when I'm creating the colour palette in my program, I use this sub SETMAP to also populate those arrays (for saving the file).

SUB SetColourPalette
LOCAL INTEGER X%, T%, COLOURINCREMENT%, COLOURCOUNT%

' Define our reserved colours first
SETMAP 0,0,0,0  ' 0 is black, 1 is almost black
SETMAP 1,1,1,1
X%=31   ' We already have black defined, so this is the colour component we will start at
        '(i.e. (31,0,0) or (31,31,31) etc.
COLOURINCREMENT%=20   ' We will increment the colour component by 20 each time
COLOURCOUNT%=11 ' We will have 11 distinct colours for each combination
FOR T%=0 to COLOURCOUNT%-1  
  SETMAP 2+T%+COLOURCOUNT%*0,X%,0,0
  SETMAP 2+T%+COLOURCOUNT%*1,0,X%,0
  SETMAP 2+T%+COLOURCOUNT%*2,0,0,X%
  SETMAP 2+T%+COLOURCOUNT%*3,X%,X%,0
  SETMAP 2+T%+COLOURCOUNT%*4,X%,0,X%
  SETMAP 2+T%+COLOURCOUNT%*5,0,X%,X%
  SETMAP 2+T%+COLOURCOUNT%*6,X%,X%,X%
  SETMAP 2+T%+COLOURCOUNT%*7,X%,X%/2,0
  SETMAP 2+T%+COLOURCOUNT%*8,X%/2,X%,0
  SETMAP 2+T%+COLOURCOUNT%*9,X%,0,X%/2
  SETMAP 2+T%+COLOURCOUNT%*10,X%/2,0,X%
  SETMAP 2+T%+COLOURCOUNT%*11,0,X%/2,X%
  SETMAP 2+T%+COLOURCOUNT%*12,0,X%,X%/2
  SETMAP 2+T%+COLOURCOUNT%*13,X%/2,X%,X%
  SETMAP 2+T%+COLOURCOUNT%*14,X%,X%/2,X%
  SETMAP 2+T%+COLOURCOUNT%*15,X%,X%/2,X%/2
  X%=X%+COLOURINCREMENT%
NEXT T%
MAP SET
END SUB

SUB SETMAP COLOURINDEX%, RED%, GREEN%, BLUE%
 MAP(COLOURINDEX%)=RGB(RED%,GREEN%,BLUE%)
 MAPRED%(COLOURINDEX%)=RED%
 MAPGREEN%(COLOURINDEX%)=GREEN%
MAPBLUE%(COLOURINDEX%)=BLUE%
END SUB


I used this document for the file format, plus manually hex editing some files saved from paint programs (in 8 bit format). What that document fails to mention is all numbers are Little-endian and the colour format is actually stored as BGR not RGB.

https://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/2003_w/misc/bmp_file_format/bmp_file_format.htm

It works well - but it is relatively slow to load (2.7 seconds). So I would suggest the following work methodology.

- Generate the colour palette you want to use in code (using MAP)
- Use Save8BitBMP to save the file for loading into Paint.NET (or whatever you use). This will preserve the colour palette in the file and ensure all sprite sheets use the same colour map.
- Create your sprites in Paint.NET - and save them as 8-bit BMP files.
- Use Load8BitBMP to load your sprite sheets into CMM2 memory.

Then when you are finished development/editing sprite sheets:
- Use the Save method (Save8BitPage) from the previous post (which is just a straight memory dump) to create data files of the sprite sheets
- Ignore the 8 bit BMP file format and just use the fast load (Load8BitImage) from the previous post (Read from disk direct to memory) - which is x40 times faster than the BMP load code.

So use 8 bit BMP when developing and use the 8BitPage format when releasing (because the 8BitPage method is not editable, but is much faster).
Edited 2024-02-05 07:40 by PeteCotton
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 10:14pm 04 Feb 2024
Copy link to clipboard 
Print this post

Duplicate post - removed
Edited 2024-02-05 08:15 by PeteCotton
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 05:17pm 05 Feb 2024
Copy link to clipboard 
Print this post

Woops - testing last night showed that there's a slight bug with the colours and order of pixels. I'll fix it tonight and republish.
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 06:02pm 05 Feb 2024
Copy link to clipboard 
Print this post

Okay - this code works better - I was reading the scanlines in the wrong order and didn't take into account that Paint.NET might save the bitmap with a different number of colours.

SUB Save8BitBMP FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%, OFFSET%, T%, SCANLINE%, X%
 LOCAL STRING FILESIZEHEX$, WIDTHHEX$, HEIGHTHEX$
 
 SIZE%=WIDTH%*HEIGHT%
 LOCAL INTEGER BYTES(1078 + SIZE%)
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 ' Header
 BYTES(0) = 66: BYTES(1) = 77   ' BM
 FILESIZEHEX$ = HEX$(1078+SIZE%,8) ' Filesize in bytes (Hex - Littleendian)
 BYTES(2)= VAL("&H" + MID$(FILESIZEHEX$,7,2))
 BYTES(3)= VAL("&H" + MID$(FILESIZEHEX$,5,2))
 BYTES(4)= VAL("&H" + MID$(FILESIZEHEX$,3,2))
 BYTES(5)= VAL("&H" + MID$(FILESIZEHEX$,1,2))
 BYTES(6)=0:BYTES(7)=0:BYTES(8)=0:BYTES(9)=0   ' Unused
 ' The offset to the start of data is 1078, which is 0x436
 ' so in little endian it's 0x36 0x04 0x00 0x00
 BYTES(10)=54:BYTES(11)=4:BYTES(12)=0:BYTES(13)=0
 BYTES(14)=40:BYTES(15)=0:BYTES(16)=0:BYTES(17)=0    ' Size of info header (littleendian)
 WIDTHHEX$=HEX$(WIDTH%,8)        ' Width in pixels (Hex - littlendian)
 BYTES(18)= VAL("&H" + MID$(WIDTHHEX$,7,2))
 BYTES(19)= VAL("&H" + MID$(WIDTHHEX$,5,2))
 BYTES(20)= VAL("&H" + MID$(WIDTHHEX$,3,2))
 BYTES(21)= VAL("&H" + MID$(WIDTHHEX$,1,2))
 HEIGHTHEX$=HEX$(HEIGHT%,8)       ' Height in pixels (Hex - littlendian)
 BYTES(22)= VAL("&H" + MID$(HEIGHTHEX$,7,2))
 BYTES(23)= VAL("&H" + MID$(HEIGHTHEX$,5,2))
 BYTES(24)= VAL("&H" + MID$(HEIGHTHEX$,3,2))
 BYTES(25)= VAL("&H" + MID$(HEIGHTHEX$,1,2))
 BYTES(26)=1:BYTES(27)=0          ' Number of planes (hex - little endian)
 BYTES(28)=8:BYTES(29)=0           ' Bits per pixel
 BYTES(30)=0:BYTES(31)=0:BYTES(32)=0:BYTES(33)=0       ' Compression - none
 BYTES(34)=0:BYTES(35)=0:BYTES(36)=0:BYTES(37)=0       ' Compressed file size - not valid
 BYTES(38)=196:BYTES(39)=14:BYTES(40)=0:BYTES(41)=0    ' XPixels Per M
 BYTES(42)=196:BYTES(43)=14:BYTES(44)=0:BYTES(45)=0    ' YPixels Per M
 BYTES(46)=0:BYTES(47)=1:BYTES(48)=0:BYTES(49)=0       ' 256 colours (Hex 0x100)
 BYTES(50)=0:BYTES(51)=0:BYTES(52)=0:BYTES(53)=0       ' Number of important colours 0=all
 ' Colour table (Stored in BGR0 format)
 FOR T%=0 TO 255
   BYTES(54 + T*4 + 0) = MAPBLUE(T%)
   BYTES(54 + T*4 + 1) = MAPGREEN%(T%)
   BYTES(54 + T*4 + 2) = MAPRED%(T%)
   BYTES(54 + T*4 + 3) = 0
 NEXT T%
 ' Pixel data
 ' Data is stored bottom scanline to top
 ' Each scanline is stored left to right
 T%=0
 FOR SCANLINE% = HEIGHT%-1 TO 0 STEP -1
   FOR X%=0 TO WIDTH%-1
     BYTES(1078+(SCANLINE%*WIDTH%) + X%)=PEEK(BYTE ADDRESS%+T%)
     T%=T%+1
   NEXT X%
 NEXT SCANLINE%
 
 OPEN FILENAME$ FOR OUTPUT AS #1
 FOR T%=0 TO 1078+SIZE%-1
   PRINT #1, CHR$(BYTES(T%));
 NEXT T%
 CLOSE #1
END SUB

SUB Load8BitBMP FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%, OFFSET%, T%, BYTE%, SCANLINE%, X%, COLOURCOUNT%
 LOCAL STRING FILESIZEHEX$, WIDTHHEX$, HEIGHTHEX$, CHARACTER$
 SIZE%=WIDTH%*HEIGHT%
 
 LOCAL INTEGER BYTES(1078 + SIZE%)
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 
 OPEN FILENAME$ FOR INPUT AS #1
 FOR T%=0 TO 1078+SIZE%-1
   CHARACTER$ = INPUT$(1,#1)
   BYTES(T%) = ASC(CHARACTER$)
 NEXT T%
 CLOSE #1
 ' Decode the image
 COLOURCOUNT% = GetFromHex(BYTES(46),BYTES(47),BYTES(48),BYTES(49))

 ' Colour mapping data
 FOR T%=0 to COLOURCOUNT%
   MAP(T%)=RGB(BYTES(54 + T*4 + 2),BYTES(54 + T*4 + 1),BYTES(54 + T*4 + 0))
 NEXT T%
 MAP SET
 ' Pixel data
 ' Data is stored bottom scanline to top
 ' Each scanline is stored left to right
 T%=0
 FOR SCANLINE% = HEIGHT%-1 TO 0 STEP -1
   FOR X%=0 TO WIDTH%-1
     POKE BYTE ADDRESS%+T%, BYTES(54+(COLOURCOUNT%*4)+(SCANLINE%*WIDTH%) + X%)
     T%=T%+1
   NEXT X%
 NEXT SCANLINE%
END SUB

FUNCTION GetFromHex(B1%, B2%, B3%, B4%) AS INTEGER
 LOCAL STRING HEXVAL = HEX$(B4%,2) + HEX$(B3%,2) + HEX$(B2%,2) + HEX$(B1%,2)
 GetFromHex = VAL ("&H" + HEXVAL)
END FUNCTION
 
Nautilus
Newbie

Joined: 01/01/2021
Location: Germany
Posts: 16
Posted: 06:09pm 05 Feb 2024
Copy link to clipboard 
Print this post

I have solved exactly this problem by using JASC-Pal files. Have a look there:

https://www.thebackshed.com/forum/ViewTopic.php?TID=13648&PID=166583#166583

... was a very long time ago  
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 06:37pm 05 Feb 2024
Copy link to clipboard 
Print this post

  Nautilus said  I have solved exactly this problem by using JASC-Pal files.


Nice! I had never heard of JASC-Pal, that's very interesting.

I do remember reading your updates on Mapster at the time, but hadn't delved into 8 bit  modes yet - so didn't appreciate the problems involved in saving/loading tile sets. Are you still developing Mapster - it's a very cool project.
Edited 2024-02-06 04:38 by PeteCotton
 
PeteCotton

Guru

Joined: 13/08/2020
Location: Canada
Posts: 316
Posted: 05:14am 06 Feb 2024
Copy link to clipboard 
Print this post

Another quick update. When saving the bitmap from GIMP it jams a bunch of extra data in there which messes up the previous load logic. This version is more refined and can handle different size headers.

SUB Load8BitBMP FILENAME$, PAGENUM%, WIDTH%, HEIGHT%
 LOCAL INTEGER ADDRESS%, SIZE%, OFFSET%, T%, BYTE%, SCANLINE%, X%, COLOURCOUNT%
 LOCAL INTEGER STARTOFCOLOURDATA%, STARTOFPIXELDATA%
 LOCAL STRING FILESIZEHEX$, WIDTHHEX$, HEIGHTHEX$, CHARACTER$
 SIZE%=WIDTH%*HEIGHT%
 ADDRESS%=MM.INFO(PAGE ADDRESS PAGENUM%)
 OPEN FILENAME$ FOR INPUT AS #1
 LOCAL INTEGER BYTES(LOF(#1))
 FOR T%=0 TO 1078+SIZE%-1
   CHARACTER$ = INPUT$(1,#1)
   BYTES(T%) = ASC(CHARACTER$)
 NEXT T%
 CLOSE #1
 ' Decode the image
 STARTOFCOLOURDATA% = 14+GetFromHex(BYTES(14),BYTES(15),BYTES(16),BYTES(17))
 COLOURCOUNT% = GetFromHex(BYTES(46),BYTES(47),BYTES(48),BYTES(49))
 STARTOFPIXELDATA% = GetFromHex(BYTES(10),BYTES(11),BYTES(12),BYTES(13)) ' Offset
 
 ' Colour mapping data
 LOCAL INTEGER MEMINDEX%=0
 FOR T%=0 to COLOURCOUNT%-1
   MEMINDEX% = STARTOFCOLOURDATA% + T*4
   MAP(T%)=RGB(BYTES(MEMINDEX% + 2),BYTES(MEMINDEX% + 1),BYTES(MEMINDEX% + 0))
 NEXT T%
 MAP SET
 ' Pixel data
 ' Data is stored bottom scanline to top
 ' Each scanline is stored left to right
 T%=0
 FOR SCANLINE% = HEIGHT%-1 TO 0 STEP -1
   FOR X%=0 TO WIDTH%-1
     POKE BYTE ADDRESS%+T%, BYTES(STARTOFPIXELDATA%+(SCANLINE%*WIDTH%) + X%)
     T%=T%+1
   NEXT X%
 NEXT SCANLINE%
END SUB

FUNCTION GetFromHex(B1%, B2%, B3%, B4%) AS INTEGER
 LOCAL STRING HEXVAL = HEX$(B4%,2) + HEX$(B3%,2) + HEX$(B2%,2) + HEX$(B1%,2)
 GetFromHex = VAL ("&H" + HEXVAL)
END FUNCTION
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1783
Posted: 06:26am 06 Feb 2024
Copy link to clipboard 
Print this post

Gimp has possibly added it's own 256 word palette.
From memory the header has a word that tells at what byte number the bitmap data starts. If you can find that the changed header length won't matter.

From the "BMP File Format" link it's at offset &h000A.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 08:19am 06 Feb 2024
Copy link to clipboard 
Print this post

Pete

There is a bug in the current version of ReadBuffer which is used both by the pixel function and the save image command. If the R, G, or B value of the pixel is > 224 then the read will wrap due to a scaling error. I think you picked this up earlier. I'll post a fix on the CMM2 beta thread later today
 
     Page 1 of 2    
Print this page
© JAQ Software 2024