PictureToJPEGString Binary Read/Write

A bit of a redirection but same vicinity of my last post.
Part 1 - Creator program
I writing a binary file out with data plus at the end of some file writes a graphic is also written out. I load a picture (jpeg/png) using Xojo’s ClipArt = f.OpenAsPicture but save it out in the combo data file using PictureToJPEGStringMBS

Part 2 Player Program
In the player program the user can open the above file fine and once they start playing and they save their progress it becomes a player file with a different extension so that it is only associated to the player program. At this point it is saved similar to Part 1 which all seems fine until I load the game file back in but the graphic doesn’t show.

When I check while debugging the graphic data seems to be loading JPEGtoSTRINGMBS won’t convert the bytes to picture yet it did so fine when loading from the creator.

All sounds very confusing but finally a question or two

  • When reading a binary file if you attempt to read and there is no more data what happens?
  • the byte size of the picture seems to be different when read in to what is written out in subsequent saves why would this be?

Below example has some of the load save code
https://www.rushsoftware.com.au/Files/FileDropTest.zip

Based on this discussion Binarystream Text and Picture, it appears you are creating your own file format. That sort of thing can become fragile over time if you don’t do some sort of tag/value scheme. XML lends itself well to that. The main problem you may run into is if you are reading the file based on the way it was written out, you have to assume the order and size of the information. That’s the part that becomes brittle. It’s hard to add additional fields later on, because you have to worry about compatibility.

If an earlier version of the program reads the file, how will it handle new fields?
If a later version reads the file, how will it deal with missing fields?

That said,

You only get as many bytes as are left. Eg., if you have a 150 byte file and you’re currently at position 100, read(100) will return the remaining 50 bytes.

the byte size of the picture seems to be different when read in to what is written out in subsequent saves why would this be?
It could be variations in compression. If you were using Picture.ToData and Picture.FromData, (ie., no compression) it would be identical.

1 Like

Thanks Tim, I tried using memory block but get the same issue. Can you search the entire binary stream for ‘^^’ some characters that denote the start of the picture and read to the end of the file?
Would that capture the picture data? I have tried to do that but without success.

I also tried to check by doing the following in the open file method

b = ClozeIn.ReadInt32                                   'Read in the length
var pictureString as string =ClozeIn.Read(b)            'Read the picture data
Clipart = JPEGStringToPictureMBS(pictureString)         'Convert the picture data
//The above works fine
//Swap the loaded data into another picture property and check the size
var myclip as picture = Clipart
var myclipstring as string = PictureToJPEGStringMBS(myclip)
c = myclipstring.Bytes

c and b are different sizes. Not sure if that sheds any light

I stripped out the data before the picture so I just save out the picture in the user game file so there probably isn’t anything weird I am doing there. The data I am saving and loading before the picture looks right also. How can I double check I am up to reading the picture data? Is searching for some characters the right approach?

No. The JPEG stream may contain any character.
You should store somewhere earlier how many bytes the JPEG have.

Or use an existing format for your data like SQLite database, JSON or XML file. Then include the image inside. For JSON and XML, you may base64 encode it.

1 Like
  1. As per the docs if you try to read more bytes than are available in the file, you will only read to the end of the file.
  2. You are saving as JPG which is lossy, every time you load in the image, and save it out, you will change the image and potentially lose quality even if you set the quality to 100. You’d be better off using PNG instead which is lossless. While the file size may change slightly, this will be the difference between the algorithm the original image was saved with vs the algorithm xojo uses.

Here is some simple xojo code I threw together which loads in an image, saves it to a file, then loads it back in again.

Dim p1 As Picture = picture.Open(New FolderItem("c:\users\julian\desktop\smallimage.png"))
Dim mb As MemoryBlock = p1.ToData(picture.Formats.PNG) 'convert image to MemoryBlock
Dim yourData As String = "*PNG*" 'your data goes here

Dim bsSaveData As BinaryStream = BinaryStream.Create(New FolderItem("c:\users\julian\desktop\test.bin"), True)
bsSaveData.WriteUInt64(yourData.Length) 'save the data length so we can read it all
bsSaveData.Write(yourData) 'your custom data is written
bsSaveData.WriteUInt64(mb.Size) 'save the image size so we can easily read it back later
bsSaveData.Write(mb) 'the image is written
bsSaveData.Close


Dim bsReadData As BinaryStream = BinaryStream.Open(New FolderItem("c:\users\julian\desktop\test.bin"))
Dim dataSize As UInt64 = bsReadData.ReadUInt64() 'get the size of the data
Dim myData As String = bsReadData.Read(dataSize) 'read the data
Dim picSize As UInt64 = bsReadData.ReadUInt64() 'get the size of the image
Dim p2 As Picture = Picture.FromData(bsReadData.Read(picSize)) 'get the image
bsReadData.Close
1 Like

Thanks Julian that really helps explain what I thought were inconsistencies.

Usually, I write an Int8 at the beginning of my binary files, serving as a version number and check against it:

bs.WriteInt8 2

bs.WriteInt32 SomeNewFields

On reading:

Var Version as integer=bs.ReadInt8

if Version>2 then
MessageBox “This file is newer than what this application can open. Please use a more recent version of this application.”
return
end if

'< Read existing values here >

If Version>=2 then MyNewProperty=bs.ReadInt32

I’d start by comparing the data when you save it and when you read it. In the IDE, look at the string containing your data in the debugger (use the Binary pane there) and check that both your strings (at load and write time) start with the exact same bytes (and end with other same bytes too). If they don’t, it likely means you are reading more (or less) data prior to this one than what had be written originally (e.g. you wrote 2 int32 and your picture and read 3 int32 and your picture).

Examine the properties you read earlier, in your code: if you find one (or several) which looks weird (a negative integer, an odd string that should be human-readable but looks “garbage”, out-of-bounds values, etc.), you know you read something wrong in the stream before that point. Find the earliest weird property and you know the problem (or the first one, if there are several) lies before you read that odd property.

1 Like

First start and write a text on the beginning like your app name, so everyone, who looks on the file with text editor knows what it is.

1 Like

If you want to make it open-source or non-proprietary, only.

1 Like
Forum for Xojo Programming Language and IDE. Copyright © 2021 Xojo, Inc.