02-23-2024, 05:25 PM
Here are a few routines I wrote to store and retrieve a string that can be embedded within an image.
Think of this as writing a description on the back of a photograph. The image itself will contain its own description.
The embedded string can be anything; text, another image, even another file. There are limits on the string size though (see comments in code).
The code below includes sample code that will load 9 photos that have already had string embedded into them. The photos will display a caption that was decoded from the embedded string. The 9 photos are in the ZIP file below.
This code is just a proof on concept for me. It's highly inefficient but extremely fast. Go ahead and see what you can do, or modify, with it.
Think of this as writing a description on the back of a photograph. The image itself will contain its own description.
The embedded string can be anything; text, another image, even another file. There are limits on the string size though (see comments in code).
The code below includes sample code that will load 9 photos that have already had string embedded into them. The photos will display a caption that was decoded from the embedded string. The 9 photos are in the ZIP file below.
This code is just a proof on concept for me. It's highly inefficient but extremely fast. Go ahead and see what you can do, or modify, with it.
Code: (Select All)
'
' Encode/Decode Routines
' by Terry Ritchie 02/23/24
'
'+-----------------------------------------------------------------------------------------------+
'| Routines to encode and decode a string within an image. |
'| |
'| Possible uses: |
'| |
'| - Embed a description of the image into the image, like writing on the back of a photograph. |
'| - Secret spy stuff, pass messages embedded in images to friends. |
'| - Embed another file or image within an image. |
'| |
'| Notes: |
'| |
'| - These routines only work with 32bit images. |
'| - It's best to use images that are opague (all alpha values = 255 such as photographs) |
'| - The formula to determine the maximum string length an image can hold is: |
'| |
'| String_Size = (Image_Width x Image_Height \ 8) - 6 |
'| |
'+-----------------------------------------------------------------------------------------------+
OPTION _EXPLICIT ' declare those variables!
CONST SWIDTH = 800 ' screen width
CONST SHEIGHT = 600 ' screen height
DIM Image AS LONG ' image to load
DIM DecodedText AS STRING ' decoded string from image
DIM p AS INTEGER ' photo counter
'**
'** BEGIN EXAMPLE CODE: cycle through 9 photos that have encoded strings within them
'**
SCREEN _NEWIMAGE(SWIDTH, SHEIGHT, 32)
FOR p = 1 TO 9
Image = _LOADIMAGE("Photo" + _TRIM$(STR$(p)) + ".png", 32) ' load the image
DecodedText = DecodeImage(Image) ' decode string inside image
Display_Image Image, DecodedText, 1 ' show image and decoded caption
_FREEIMAGE Image ' free the image
NEXT p
SYSTEM
SUB Display_Image (i AS LONG, s AS STRING, p AS INTEGER)
CLS
_PUTIMAGE ((SWIDTH - _WIDTH(i)) \ 2, (SHEIGHT - _HEIGHT(i)) \ 2), i
LOCATE 2, ((SWIDTH \ 8) - LEN(s)) \ 2
PRINT s;
IF p THEN
LOCATE (SHEIGHT \ 16) - 1, ((SWIDTH \ 8) - 11) \ 2
PRINT "PRESS A KEY";
SLEEP
END IF
END SUB
'**
'** END EXAMPLE CODE
'**
'**
'** EXAMPLE: Saving an entire file within an image.
'**
'ff = FREEFILE ' get a free file handle
'OPEN "readme.md" FOR BINARY AS #ff ' open text file
'Text$ = SPACE$(LOF(ff)) ' create a string the size of file
'GET #ff, , Text$' get text as one string
'CLOSE #ff ' close text file
'Image& = _LOADIMAGE("photo.png", 32) ' load image to encode string into
'EncodedImage& = EncodeImage(Image&, Text$) ' encode string within image
'_SAVEIMAGE "EncodedPhoto.png", EncodedImage& ' save encoded image (use a non lossy format!)
'_FREEIMAGE Image& ' remove image from memory
'_FREEIMAGE EncodedImage& ' remove encoded image from memory
' ______________________________________________________________________________________________________
'/ \
FUNCTION EncodeImage& (i AS LONG, st AS STRING) ' EncodeImage& |
' __________________________________________________________________________________________________|____
'/ \
'| Embeds a string within an image. |
'| |
'| i - image to encode |
'| st - string to encode within image |
'| |
'| Returns: handle of new encoded image (-2 or less) |
'| -1 if an error occurred |
'| |
'| Note: Only works with 32 bit images |
'| |
'| The string is converted to a series of 1's and 0's that will be used to determine the alpha level of |
'| each pixel within the image. Therefore, each character in the string (a byte) will need 8 pixels to |
'| store the equivalent binary value. A binary value of 1 will set a pixel's alpha level to 255 and a |
'| binary value of 0 will set a pixel's alpha level to 254. This very slight variation in alpha levels |
'| will be imperceivable to the naked eye when viewing the image. |
'| A header and footer of 3 bytes is added to beginning and end of the string data. The header is used |
'| to identify that encoded information is contained within the image. The footer identifies the end of |
'| the string data contained within the image. |
'| |
'| The size of an image will determine the maximum size of the encoded string it can hold. |
'| |
'| String_Size = (Image_Width x Image_Height \ 8) - 6 |
'\_______________________________________________________________________________________________________/
DIM m AS _MEM ' memory block of image
DIM em AS _MEM ' memory block of image to encode
DIM ei AS LONG ' copy of image to encode
DIM s AS STRING ' string to encode within image
DIM p AS LONG ' position within string
DIM o AS _OFFSET ' pixel offset within memory block
DIM c AS _UNSIGNED _BYTE ' single character value within string
DIM a AS _UNSIGNED _BYTE ' pixel alpha value to write
DIM b AS _BYTE ' bit counter
DIM b(7) AS _UNSIGNED _BYTE ' place value of bit counter
DIM HeadFoot AS STRING * 3 ' string header and footer
'+-------------------------------------------------------------------------------------------------------+
'| Check for a valid image before proceeding. |
'| ========================================== |
'+------------------------------+ |
EncodeImage& = -1 ' | assume this image is invalid |
IF i < -1 THEN ' | is this a valid image handle? |
IF _PIXELSIZE(i) = 4 THEN ' | is this a 32 bit color image? |
' +------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Store binary place holder values |
'| ================================ |
'+-----------+ |
b(0) = 1 ' | define 8 bit binary place values |
b(1) = 2 ' | |
b(2) = 4 ' | |
b(3) = 8 ' | |
b(4) = 16 ' | |
b(5) = 32 ' | |
b(6) = 64 ' | |
b(7) = 128 ' | |
' +-----------------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Add an identifying header and footer to string Note: The possibility of this header/ |
'| ============================================== footer combination found in a |
'+-------------------------------+ string will be very low but not |
HeadFoot = "U" + CHR$(0) + "U" ' | 010101010000000001010101 a zero chance. May want to scan |
s = HeadFoot + st + HeadFoot ' | add header and footer before proceeding. ( use INSTR ) |
' +---------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Use image memory manipulation for speed |
'| ======================================= |
'+-----------------+ |
m = _MEMIMAGE(i) ' | create image memory block |
' +-----------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Ensure that the string will fit inside the image |
'| ================================================ |
'+-----------------------------+ |
IF LEN(s) > m.SIZE \ 32 THEN ' | will string fit into image? |
_MEMFREE m ' | no, free image memory block |
s = "" ' | clear variable |
ELSE ' | Yes, the string will fit |
' +-----------------------------------------------------------------+
'+-------------------------------------------------------------------------------------------+
'| Turn error checking off for speed |
'+-------------------------------------------------------------------------------------------+
$CHECKING:OFF
'+-------------------------------------------------------------------------------------------+
'| Use image memory manipulation for speed |
'| ======================================= |
'+-------------------+ |
ei = _COPYIMAGE(i) ' | create image to encode |
em = _MEMIMAGE(ei) ' | create image to encode memory block |
' +-----------------------------------------------------------------------+
'+-------------------------------------------------------------------------------------------+
'| Endcode string into image |
'| ========================= |
'+------------------------------------------------+ |
p = 1 ' | start at text position 1 |
o = 0 ' | reset pixel offset location |
DO ' | begin encoding loop |
c = ASC(s, p) ' | get next character value in string |
b = 0 ' | reset bit counter |
DO ' | begin bit identification loop |
IF c AND b(b) THEN a = 255 ELSE a = 254 ' | 255 = 1, 254 = 0 |
_MEMPUT em, em.OFFSET + o + 3, a ' | adjust alpha value in encoded image |
o = o + 4 ' | next pixel position in memory block |
b = b + 1 ' | next bit in character |
LOOP UNTIL b = 8 ' | leave when all 8 bits processed |
p = p + 1 ' | next character in string |
LOOP UNTIL p > LEN(s) ' | leave when string exhausted |
_MEMFREE m ' | free image memory block |
_MEMFREE em ' | free encoded image memory block |
' +------------------------------------------+
'+-------------------------------------------------------------------------------------------+
'| Turn error checking back on |
'+-------------------------------------------------------------------------------------------+
$CHECKING:ON
'+-------------------------------------------------------------------------------------------+
'| Return handle of encoded image |
'| ============================== |
'+------------------+ |
EncodeImage& = ei ' | return handle of encoded image |
' +------------------------------------------------------------------------+
END IF
END IF
END IF
END FUNCTION
' ______________________________________________________________________________________________________
'/ \
FUNCTION DecodeImage$ (i AS LONG) ' DecodeImage$ |
' __________________________________________________________________________________________________|____
'/ \
'| Extracts a coded string from within an image. |
'| |
'| i - image to decode |
'| |
'| Returns: string extracted from image |
'| null if no coded string was found within image or an error occurred |
'| |
'| Note: Only works with 32 bit images |
'\_______________________________________________________________________________________________________/
DIM m AS _MEM ' memory block of image
DIM e AS _OFFSET ' end of memory block
DIM s AS STRING ' string containing decoded data
DIM c AS _UNSIGNED _BYTE ' single character value
DIM o AS _OFFSET ' pixel offset within memory block
DIM a AS _UNSIGNED _BYTE ' pixel alpha value
DIM b AS _BYTE ' bit counter
DIM b(7) AS _UNSIGNED _BYTE ' place value of bit counter
DIM HeadFoot AS STRING * 3 ' string header and footer
DIM Done AS _BYTE ' finished processing flag
'+-------------------------------------------------------------------------------------------------------+
'| Check for a valid image before proceeding. |
'| ========================================== |
'+------------------------------+ |
s = "" ' | assume this image is invalid |
IF i < -1 THEN ' | is this a valid image handle? |
IF _PIXELSIZE(i) = 4 THEN ' | is this a 32 bit color image? |
' +------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Store binary place holder values |
'| ================================ |
'+-----------+ |
b(0) = 1 ' | define 8 bit binary place values |
b(1) = 2 ' | |
b(2) = 4 ' | |
b(3) = 8 ' | |
b(4) = 16 ' | |
b(5) = 32 ' | |
b(6) = 64 ' | |
b(7) = 128 ' | |
' +-----------------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Use image memory manipulation for speed |
'| ======================================= |
'+----------------------+ |
m = _MEMIMAGE(i) ' | create image memory block |
e = m.OFFSET + m.SIZE ' | end of memory block |
' +------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Turn error checking off for speed |
'+-----------------------------------------------------------------------------------------------+
$CHECKING:OFF
'+-----------------------------------------------------------------------------------------------+
'| Extract string from image |
'| ========================= |
'+---------------------------------------+ |
HeadFoot = "U" + CHR$(0) + "U" ' | 010101010000000001010101 string header and footer |
Done = 0 ' | reset finished flag |
s = "" ' | reset string |
o = 0 ' | reset pixel offset |
DO ' | cycle through image pixels |
b = 0 ' | reset bit count |
c = 0 ' | reset character value |
DO ' | cycle through character bits |
_MEMGET m, m.OFFSET + o + 3, a ' | get alpha value of pixel |
IF a = 255 THEN c = c + b(b) ' | if 1 then add place value |
b = b + 1 ' | increment bit counter |
o = o + 4 ' | increment to next image pixel |
LOOP UNTIL b = 8 ' | leave when 8 bits examined |
s = s + CHR$(c) ' | add character to string |
'+-----------------------------------+ |
'| Search for string header |
'| ======================== |
'+------------------------------------+ |
IF o = 96 THEN ' | first 3 characters extracted |
IF s <> HeadFoot THEN Done = -1 ' | report encoded string not found if no header |
s = "" ' | remove header (null return for no header found) |
ELSE ' | beyond first 3 characters |
'+--------------------------------+ |
'| Search for string footer |
'| ======================== |
'+--------------------------------+ |
IF RIGHT$(s, 3) = HeadFoot THEN ' | check for footer (end of string) |
Done = -1 ' | report encoded string found |
s = LEFT$(s, LEN(s) - 3) ' | remove footer |
END IF ' | |
END IF ' | |
LOOP UNTIL (o = e) OR Done ' | leave at end of memory block or done processing |
_MEMFREE m ' | free memory block |
' +------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Set string to null if a footer was not found (image corruption perhaps?) |
'| ============================================ |
'+------------------------+ |
IF Done = 0 THEN s = "" ' | a footer was not found |
' +----------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Turn error checking back on |
'+-----------------------------------------------------------------------------------------------+
$CHECKING:ON
END IF
END IF
'+-------------------------------------------------------------------------------------------------------+
'| Return string found |
'| =================== |
'+-----------------+ |
DecodeImage$ = s ' | return string |
' +-------------------------------------------------------------------------------------+
END FUNCTION
' ______________________________________________________________________________________________________
'/ \
FUNCTION EncodedStringExists% (i AS LONG) ' EncodedStringExists% |
' __________________________________________________________________________________________________|____
'/ \
'| Tests an image for an encoded string. |
'| |
'| i - image to test for encoded string |
'| |
'| Returns: 0 if no encoded string found |
'| -1 if an encoded string was found |
'\_______________________________________________________________________________________________________/
DIM m AS _MEM ' memory block of image
DIM e AS _OFFSET ' end of memory block
DIM s AS STRING ' string containing decoded data
DIM c AS _UNSIGNED _BYTE ' single character value
DIM o AS _OFFSET ' pixel offset within memory block
DIM a AS _UNSIGNED _BYTE ' pixel alpha value
DIM b AS _BYTE ' bit counter
DIM b(7) AS _UNSIGNED _BYTE ' place value of bit counter
DIM HeadFoot AS STRING * 3 ' string header and footer
'+-------------------------------------------------------------------------------------------------------+
'| Check for a valid image before proceeding. |
'| ========================================== |
'+------------------------------+ |
EncodedStringExists% = 0 ' | assume this image is invalid |
IF i < -1 THEN ' | is this a valid image handle? |
IF _PIXELSIZE(i) = 4 THEN ' | is this a 32 bit color image? |
' +------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Turn error checking off for speed |
'+-----------------------------------------------------------------------------------------------+
$CHECKING:OFF
'+-----------------------------------------------------------------------------------------------+
'| Store binary place holder values |
'| ================================ |
'+-----------+ |
b(0) = 1 ' | define 8 bit binary place values |
b(1) = 2 ' | |
b(2) = 4 ' | |
b(3) = 8 ' | |
b(4) = 16 ' | |
b(5) = 32 ' | |
b(6) = 64 ' | |
b(7) = 128 ' | |
' +-----------------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Use image memory manipulation for speed |
'| ======================================= |
'+----------------------+ |
m = _MEMIMAGE(i) ' | create image memory block |
e = m.OFFSET + m.SIZE ' | end of memory block |
' +------------------------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Extract header from image |
'| ========================= |
'+---------------------------------------+ |
HeadFoot = "U" + CHR$(0) + "U" ' | 010101010000000001010101 string header and footer |
s = "" ' | reset string |
o = 0 ' | reset pixel offset |
DO ' | cycle through image pixels |
b = 0 ' | reset bit count |
c = 0 ' | reset character value |
DO ' | cycle through character bits |
_MEMGET m, m.OFFSET + o + 3, a ' | get alpha value of pixel |
IF a = 255 THEN c = c + b(b) ' | if 1 then add place value |
b = b + 1 ' | increment bit counter |
o = o + 4 ' | increment to next image pixel |
LOOP UNTIL b = 8 ' | leave when 8 bits examined |
s = s + CHR$(c) ' | add character to string |
LOOP UNTIL (o = e) OR (o = 96) ' | leave at end of memory block or 3 characters retrieved|
_MEMFREE m ' | free memory block |
' +-------------------------------------------------------+
'+-----------------------------------------------------------------------------------------------+
'| Turn error checking back on |
'+-----------------------------------------------------------------------------------------------+
$CHECKING:ON
'+-----------------------------------------------------------------------------------------------+
'| Return status of string within image |
'| ==================================== |
'+-----------------------------------------------+ |
IF s = HeadFoot THEN EncodedStringExists% = -1 ' | return -1 if header found |
' +-----------------------------------------------+
END IF
END IF
END FUNCTION