Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 05:27 25 Nov 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 : Displaying Bitmap Images

Author Message
crackerjack

Senior Member

Joined: 11/07/2011
Location: Australia
Posts: 164
Posted: 10:52am 09 Oct 2011
Copy link to clipboard 
Print this post

Hi All,

I have a small program that allows me to display bitmap images loaded from the SD card. Of course monochrome images no larger than 480x432 pixels are the limit (and it is painfully slow), but it was a fun exercise learning about the BMP format and how to read it from a file & decode it using MMBasic. Attached is a photo of a bitmap loaded from the SD Card (Don, I hope you don't mind me using the Duinomite logo as a test case). It may be of some use if you want to display small images as splash logos, etc.





If anybody is interested, I can post the code (I'll have to clean it up a bit first).

Cheers,

Lance.
 
Sumit

Newbie

Joined: 13/09/2011
Location:
Posts: 23
Posted: 03:10pm 09 Oct 2011
Copy link to clipboard 
Print this post

Hi,
Nice work. I did a similar thing but my code took up a good amount of RAM. I would like to see your code. Maybe yours is more efficient.

Sumit

 
donmck

Guru

Joined: 09/06/2011
Location: Australia
Posts: 1313
Posted: 07:19pm 09 Oct 2011
Copy link to clipboard 
Print this post

  crackerjack said  
(Don, I hope you don't mind me using the Duinomite logo as a test case). It may be of some use if you want to display small images as splash logos, etc.

Lance.


Actually it is an Olimex generated Logo, but I'll stick my neck out and say they are more than happy for you to use it.

Nice work.

Cheers Don...
https://www.dontronics.com
 
sparkey

Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 819
Posted: 05:20am 10 Oct 2011
Copy link to clipboard 
Print this post

heres a pic for you guy`s


technicians do it with least resistance
 
crackerjack

Senior Member

Joined: 11/07/2011
Location: Australia
Posts: 164
Posted: 02:24pm 10 Oct 2011
Copy link to clipboard 
Print this post

Below is the code to read a bitmap file in MMBasic. It could clearly be improved upon and made more efficient - in particular around the 3 subroutines that read double-words, words and bytes (a single routine given a number of bytes to read would do the trick). Also the "workhorse" section (lines 330 to 530) is a bit "ugly" for lack of a better term. I have tested it with various sizes of monochrome bitmap files, but there might be some sizes that may cause some display issues.

The program uses about 6% of program space and 7% of variable memory - the images are loaded into the display space, so there are no issues with RAM really (just speed!)

[code]
10 ' Display a monochrome bitmap (max size 480x432)
20 INPUT "Bitmap file: ", f$
30 OPEN f$ FOR input AS #1
40 ADDRESS = 0
50 IF EOF(#1) GOTO 540
60 IF INPUT$(2,#1) <> "BM" THEN
70 ? "Not a Bitmap File" : GOTO 540
80 ENDIF
90 ADDRESS = ADDRESS + 2
100 GOSUB 570 ' Get the next DWORD - FILESIZE
110 ? "File Size:"; DWORD
120 GOSUB 570 ' Skip next two WORDs - RESERVED DWORD = 2 x WORD
130 GOSUB 570 ' Get OFFSET
140 OFFSET = DWORD : ? "OFFSET:"; OFFSET
150 GOSUB 570 ' Skip the next DWORD - DIB HEADER INFO
160 GOSUB 570 : WIDTH = DWORD : ? "Width:", WIDTH
170 GOSUB 570 : HEIGHT = DWORD : ? "Height:", HEIGHT
180 IF HEIGHT > MM.VRES OR WIDTH > MM.HRES THEN
190 ? "Image exceeds Maximite resolution" : GOTO 540
200 ENDIF
210 GOSUB 650 ' Skip a word
220 GOSUB 650 : ? "Bits Per Pixel:"; word
230 IF WORD <> 1 THEN
240 ? "Only monochrome (1bpp) bitmaps supported" : GOTO 540
250 ENDIF
260 'Advance to OFFSET
270 CURRENT = ADDRESS
280 FOR A = CURRENT TO OFFSET
290 GOSUB 730
300 NEXT A
310 'CLS
330 ROWSIZE = FIX((WIDTH/32)+SGN((WIDTH/32)-(WIDTH\32))) * 4
340 FOR Y = HEIGHT TO 0 STEP -1
350 FOR Z = 1 TO ROWSIZE
360 IF EOF(#1) THEN GOTO 540
370 FOR B = 7 TO 0 STEP -1
380 PIXEL(X,Y) = SGN(BYTE AND (2^B))
390 X = X + 1
400 IF X = WIDTH THEN
410 X = 0
420 EXIT FOR
430 ENDIF
440 NEXT B
450 IF X = 0 THEN
460 PAD=ROWSIZE-FIX((WIDTH / 32) * 4) - 1
470 IF PAD < 0 THEN PAD = 0
480 FOR I=0 TO PAD:GOSUB 730: NEXT I
490 EXIT FOR
500 ENDIF
510 GOSUB 730
520 NEXT Z
530 NEXT Y
540 CLOSE #1
550 END
560 ' --------------------------------------------------------
570 ' _GETDWORD_
580 DWORD = 0
590 DWORD$ = INPUT$(4,#1)
600 FOR B = 4 TO 1 STEP -1
610 DWORD = DWORD + ASC(MID$(DWORD$,B,1)) * ((2^(B-1))^8)
620 NEXT B
630 ADDRESS = ADDRESS+4
640 RETURN
650 ' _GETWORD_
660 WORD = 0
670 WORD$ = INPUT$(2,#1)
680 FOR B = 2 TO 1 STEP -1
690 WORD = WORD + ASC(MID$(WORD$,B,1)) * ((2^(B-1))^8)
700 NEXT B
710 ADDRESS = ADDRESS+2
720 RETURN
730 ' _GETBYTE_
740 BYTE = ASC(INPUT$(1,1))
750 ADDRESS = ADDRESS+1
760 RETURN
[/code]
 
crackerjack

Senior Member

Joined: 11/07/2011
Location: Australia
Posts: 164
Posted: 08:31am 11 Oct 2011
Copy link to clipboard 
Print this post

Just for the sake of completeness - here is a final version - refactored somewhat now that I better understand the BMP format's padding spec.
There are some diagnostic details still printed by the code which could be removed to make it more compact (since the CLS at line 280 wipes them out). 5% of program memory is used by this version of the code.

I am thinking there may be a better, more quickly rendered format for raster graphics specifically for the Maximite... I may look into what this could be. Code follows along with another screenshot.

[code]
10 ' BITMAP.BAS v1.0 Display a monochrome bitmap (max size 480x432)
20 INPUT "Bitmap file: ", f$
30 OPEN f$ FOR input AS #1
40 ADDRESS = 0
50 IF EOF(#1) GOTO 570
60 IF INPUT$(2,#1) <> "BM" THEN
70 ? "Not a Bitmap File" : GOTO 570
80 ENDIF
90 ADDRESS = ADDRESS + 2
100 NO_BYTES = 4 : GOSUB 460 ' Get the next DWORD - FILESIZE
110 ? "File Size:"; BYTES
120 GOSUB 460 ' Skip next two WORDs - RESERVED DWORD = 2 x WORD
130 GOSUB 460 ' Get OFFSET
140 OFFSET = BYTES : ? "OFFSET:"; OFFSET
150 GOSUB 460 ' Skip the next DWORD - DIB HEADER INFO
160 GOSUB 460 : WIDTH = BYTES : ? "Width:", WIDTH
170 GOSUB 460 : HEIGHT = BYTES: ? "Height:", HEIGHT
180 IF HEIGHT > MM.VRES OR WIDTH > MM.HRES THEN
190 ? "Image exceeds Maximite resolution" : GOTO 570
200 ENDIF
210 NO_BYTES = 2 : GOSUB 460 ' Skip a word
220 GOSUB 460 : ? "Bits Per Pixel:"; BYTES
230 IF BYTES <> 1 THEN
240 ? "Only monochrome (1bpp) bitmaps supported" : GOTO 570
250 ENDIF
260 'Advance to OFFSET
270 NO_BYTES = OFFSET - ADDRESS : GOSUB 460
280 CLS
290 ROWSIZE = FIX((WIDTH/32)+SGN((WIDTH/32)-(WIDTH\32))) * 4
300 NO_BYTES = 1
310 GOSUB 460
320 FOR Y = HEIGHT TO 0 STEP -1
330 FOR Z = 1 TO ROWSIZE
340 FOR B = 7 TO 0 STEP -1
350 IF X <= WIDTH THEN
360 PIXEL(X,Y) = SGN(BYTES AND (2^B))
370 ENDIF
380 X = X + 1
390 IF X >= (ROWSIZE * 8) THEN X = 0
400 NEXT B
410 GOSUB 460
420 NEXT Z
430 NEXT Y
440 GOTO 570
450 ' --------------------------------------------------------
460 ' _GETBYTES_
470 BYTES = 0
480 BYTES$ = INPUT$(NO_BYTES,#1)
490 IF EOF(#1) THEN GOTO 440
500 FOR B = NO_BYTES TO 1 STEP -1
510 BYTES = BYTES + ASC(MID$(BYTES$,B,1)) * ((2^(B-1))^8)
520 NEXT B
530 ADDRESS = ADDRESS+NO_BYTES
540 RETURN
550 ' --------------------------------------------------------
560 ' _END_
570 CLOSE #1
580 END
[/code]



Edited by crackerjack 2011-10-12
 
Olimex
Senior Member

Joined: 02/10/2011
Location: Bulgaria
Posts: 226
Posted: 04:15pm 11 Oct 2011
Copy link to clipboard 
Print this post

did you check JPEG format too? as we have nice VGA camera with UEXT connector now and it sends JPEG compressed pictures over RS232 and would connect directly to DuinoMite with UEXT connector
 
crackerjack

Senior Member

Joined: 11/07/2011
Location: Australia
Posts: 164
Posted: 12:10pm 17 Oct 2011
Copy link to clipboard 
Print this post

Hi All,

Just in response to the Olimex post from a week or so back - I did look at JPEGs and very quickly decided to look no further. The JPEG compression algorithm is complex - vastly complex for MMBasic and the Maximite's processing power. Apart from that, the Maximite has 1-bit, 480x432 graphics resolution which could not really offer much in the way of rendering images from a JPEG which I don't believe goes much lower than 8x8 grayscale.

Since the Maximite can generate it's own Bitmaps to file with the SAVEBMP command, I thought I'd stick with that. My experiments at getting the Maximite to be used to display some basic graphics in the form or rendering monochrome Windows Bitmap (BMP) files showed that it was hopelessly slow rendering the bitmap pixel-by-pixel. I spent a little time thinking about it and decided there was possibly a way to render images in more reasonable time-frames. The following is what I have come up with. For those who feel less inclined to read about the details and just "have a play" - a zipped file with the MMBasic code and a README file can be downloaded here.

Essentially, by converting the BMP files into what I have dubbed the "Maximite Picture Format" (MPF), the results are encouraging and I think nifty Splash Screens or even games using bitmapped graphics might be feasible. Some stats to illustrate the results:

A single 400x328 pixel image (the "Face" image in my post above) previously rendered in over 50 seconds as a BMP, while as an MPF, the same image is rendered in slightly more than 1 second. The file size is reduced from around 18kB to just under 4kB.

The boring details follow (some may care to know)...

The 2-step process explained:

1 -> Convert the BMP files to a format more readily renderable in MMBasic. Maximite Picture Format (MPF) works as follows:

A program (BMP2MPF.BAS) validates and reads an ordinary monochrome Windows Bitmap file (of dimensions no greater than 480x432 pixels). The data is encoded using a modified Run Length Encoding algorithm such that horizontal "runs" of alike pixel "colours" (black or white) are counted and written out as count/colour pairs. For example, 10 pixels of white followed by 27 pixels of black would be encoded as (10)1(27)0. The count values are written as single byte ASCII characters rather than decimal numbers. Of course a maximum count of only 255 is achievable using this method, hence a modified algorithm to take this and the image width into account. The result is a vastly smaller file (in most cases) which really contains information about the image in a "horizontal vector" type of format. Actual image details will cause variation in file size differences, but the results I have had are MPF files in the range of 80% - 90% smaller than the original BMP file. It is of course possible that an MPF file may be larger than the original BMP file if the image is say of a grey dithered pattern. There is really nothing new about RLE.

2 -> Decode the MPF file for rendering, as follows:

The program MPFVIEW.BAS simply reads the Run Length Encoded data from an MPF file, and then using the MMBasic LINE(x1,y)-(x2,y),c statement, renders the vectors in a series of horizontal lines (bottom up) to create the image. MPFVIEW basically shows the decoding algorithm. It takes up less than 1KB of program space and is simple enough to use in any larger program to render previously converted images (either from SD card file of even from an in-memory array).

It's quick and pretty easy to use. Converting an image from BMP to MPF can take a while, but this only needs to be done once for an image.

I hope this is of some use to somebody and inspires some ideas as to what more this great little machine can do.

Cheers,

Lance.
 
brucepython

Regular Member

Joined: 19/06/2011
Location: Australia
Posts: 64
Posted: 03:05am 18 Oct 2011
Copy link to clipboard 
Print this post

Thanks Crackerjack. I've been working on the same challenge but had to press pause for a few weeks due to work and a trip away from home.
I got to the stage where I could display full-width BMPs but hadn't conquered those of less than full horizontal width. You sure get some spectacular op art when the code wanders off into its own little dimension!

The routine below as its stands draws a full screen in about 38 sec, which is painfully slow. The MPF approach sounds a good one. However, if it's going to become part of the Maximite system an encoding/decoding routine will probably need to be included in Maxi BASIC version umpteen at some stage. I suspect there will always be the need to display standard BMPs without going through the RLE utility first so I'll plug on regardless when time permits.

For what it's worth, here's my effort. Some of the comments may clarify what's going on at different stages.


10 ' BMPSHOW: displays a 1-bit BMP image
20 ' from Bruce Mitchell <brucepython@gmail.com>
30 '
40 ' BMP is a format with many variations and vagaries.
50 ' Don't be surprised if some images appear inverted, negative or
60 ' otherwise weird.
70 ' See en.wikipedia.ord/wiki/BMP_file_format for basic info and some
80 ' useful references.
90 ' Max image size displayed is 480 wide x 432 high.
100 'Larger images might load OK but only a corner will be shown.
110 'It's very slow!
120 '
130 'Possible enhancements might include choices to display the image as:
140 ' * inverted
150 ' * negative
160 ' * flipped horizontally
170 '
180 INPUT "BMP file to load: "; f$
190 ext$=UCASE$(RIGHT$(f$,4))
200 IF ext$<>".BMP" THEN f$=f$+".BMP"
210 OPEN f$ FOR input AS #1
220 '
230 FOR ptr=0 TO 9:a$=INPUT$(1,#1):NEXT: ' Skip the signature field.
240 '
250 'Get data offset.
260 GOSUB 650: offset = byte-1
270 FOR ptr=ptr TO ptr+5:a$=INPUT$(1,#1):NEXT
280 '
290 GOSUB 650:width=byte
300 FOR ptr=ptr TO ptr+1:a$=INPUT$(1,#1):NEXT
310 GOSUB 650:height=byte
320 ptr=ptr+8
330 '
340 ' Get colour depth in bits from &1C.
350 FOR ptr=ptr TO ptr+3:a$=INPUT$(1,#1)
360 NEXT
370 GOSUB 650: depth=byte
380 '
390 IF depth<>1 THEN PRINT "Only 1-bit BMP images are supported.":END
400 '
410 REM PRINT "Height=";height:' Remove REM if you need to check the image size.
420 REM PRINT "Width =";width:' See above.
430 '
440 CLOSE #1: OPEN f$ FOR input AS #1:' Reset to start of file.
450 FOR ptr=0 TO offset:a$=INPUT$(1,#1):NEXT:' ptr points to start of bitmap.
460 '
470 'Copy the bitmap to the screen.
480 CLS
490 FOR y=height-1 TO 0 STEP -1
500 FOR x=0 TO width-8 STEP 8
510 byte=ASC(INPUT$(1,#1))
520 FOR bit=7 TO 0 STEP -1
530 pxl=NOT(byte AND 2^bit): ' Remove the NOT to get a negative image.
540 PIXEL(x+7-bit,y)=pxl
550 NEXT bit
560 NEXT x
570 NEXT y
580 '
590 'Indicate job is done and wait for a keypress.
600 SOUND 400,100:PAUSE 150:SOUND 200,100
610 IF INKEY$="" THEN GOTO 610
620 END
630 '
640 '============ SUBROUTINE ===============
650 ' Get next two bytes from ptr, return as 'byte', then inc file ptr.
660 lsb=ASC(INPUT$(1,#1)):msb=ASC(INPUT$(1,#1))
670 byte=(msb * 256) + lsb
680 RETURN

I may get back to working on the rest of the challenge, but don't hold your breath!

I agree with the comments about JPEG images. They're a bit outside the Maximite's capabilities at present, and certainly not in BASIC. And, as you say, who's ever seen a 1-bit JPEG? Or needs one?

Bruce
 
brucepython

Regular Member

Joined: 19/06/2011
Location: Australia
Posts: 64
Posted: 03:28am 18 Oct 2011
Copy link to clipboard 
Print this post

The above code can be speeded up about 10% by combining lines 530 and 540 as follows:

Original:
530 pxl=NOT(byte AND 2^bit): ' Remove the NOT to get a negative image.
540 PIXEL(x+7-bit,y)=pxl

Replace with:
530 '
540 PIXEL(x+7-bit,y)=NOT(byte AND 2^bit): ' Remove the NOT to get a negative image.

Sorry about that - it's been a few weeks since I looked at it.

Bruce
 
brucepython

Regular Member

Joined: 19/06/2011
Location: Australia
Posts: 64
Posted: 08:38pm 21 Oct 2011
Copy link to clipboard 
Print this post

Here's an improved version of the above code: download here (about 270k) .
The ZIP file also includes some images to play with.

This version displays 1-bit BMPs of any width and height, but if the images are bigger than the screen only the top left corner will appear, and only after the rest of the image has been "plotted" off-screen so you'll have a long wait before anything appears.

Despite a considerable increase in plotting speed over the old version, this is still pretty slow.

Bruce
 
brucepython

Regular Member

Joined: 19/06/2011
Location: Australia
Posts: 64
Posted: 07:59pm 22 Nov 2011
Copy link to clipboard 
Print this post

A much faster version is now available at this address. Plotting time has been halved for most images, and anything below the display area of a standard MM screen is ignored.

BruceEdited by brucepython 2011-11-24
 
Nick

Guru

Joined: 09/06/2011
Location: Australia
Posts: 512
Posted: 09:17pm 14 Jan 2012
Copy link to clipboard 
Print this post


Edited by Nick 2012-01-16
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024