|
|
Line 1: |
Line 1: |
− | The TI.Image format is a type of image used in several places in the Nspire documents, such as Lua scripts. | + | The TI.image format is used by TI-Nspire Lua to display images. It uses a bitmap based format with no compression at all. The image data consists out of a header section and a data section. |
| | | |
− | The format is some form of bitmap, with almost no compression at all. | + | ==The header== |
| + | The header consists out of 20 bytes of data arranged as presented in the following table. All fields are little endian integers. |
| | | |
− | The examples here, as the encoded strings visible in .lua files sometimes show letters and symbols instead of expected "\xxx" characters. This is because the "\xxx" is actually a representation of a non-printable character, and when, by chance, the character is printable the editor displays its value, which can be a letter, a symbol etc. | + | {| class="wikitable" |
| + | |- |
| + | ! Offset !! Width (bytes) !! Contents |
| + | |- |
| + | | 0 || 4 || Pixel width of image |
| + | |- |
| + | | 4 || 4 || Pixel height of image |
| + | |- |
| + | | 8 || 1 || Image alignment (0) |
| + | |- |
| + | | 9 || 1 || Flags (0) |
| + | |- |
| + | | 10 || 2 || Padding (0) |
| + | |- |
| + | | 12 || 4 || The number of bytes between successive raster lines (generally 2*width) |
| + | |- |
| + | | 16 || 2 || The number of bits per pixel (16) |
| + | |- |
| + | | 18 || 2 || Planes per bit (1) |
| + | |} |
| | | |
− | ==Bytes and Data sections== | + | ==Image data== |
| | | |
− | All the data in a Ti.image is formatted in what we call bytes.
| + | The image data immediately follows the header. Pixels are arranged in rows. Each pixel is a little endian 16-bit integer with five bits for each color red, green and blue. The top bit determines if the pixel is drawn. If its zero (0), the pixel is not drawn (alpha). If it is one (1), the pixel is drawn in the RGB color of the remaining 15 bits. |
− | A byte can look like this (without the quotes): "<tt>\255</tt>", "<tt>\129</tt>", "<tt>\045</tt>" or "<tt>.</tt>". The byte number ranges from \000 to \255
| |
− | If the byte number is reprecentable in ascii (and the nspire supports that char), it is allowed to just use the ascii version as byte, to lower the size of the image. For example, you can change "<tt>\046</tt>" to "<tt>.</tt>". | |
| | | |
− | A data section is a group of bytes used to represent some header data or a pixel.
| + | Example, converting a RGB color: |
− | For example, it might look like this: "<tt>\255\123</tt>".
| |
− | What is very important to understand is that the data is not written logically, in fact, the position of the bytes is swapped.
| |
− | For example, let's take a picture with the width 320, and you want to convert the width size to put in your header:
| |
| | | |
− | 1. You first convert the number to binary: <tt>320 --> 101000000</tt>.<br> | + | R=255 → R = 31 (because each pixel can only have 5 bits per color) |
− | 2.Then you have to add the extra zeros to make it fit in the header (width is 4 bytes):<br>
| + | G=012 → G = 1 |
− | <tt>00000000000000000000000101000000</tt><br>
| + | B=123 → B = 15 |
− | 3.This is then splited in four (4 bytes):<br>
| + | A= 1 (alpha flag, make that the pixel is visible) |
− | <tt>00000000 - 00000000 - 00000001 - 01000000</tt><br>
| |
− | 4.Then converted back to decimal:<br>
| |
− | <tt>000 - 000 - 001 - 064</tt><br>
| |
| | | |
− | You would expect to put this then as "<tt>\000\000\001\064</tt>" in the header, but no, you have to swap it: "<tt>\064\001\000\000</tt>"<br>(Little Endian)<br>
| + | To make things easy, we will fist convert it to binary in this form: |
− | You can also change the "<tt>\064</tt>" in the header to "<tt>@</tt>", since that is its ASCII representation.
| + | A RRRRR GGGGG BBBBB |
− | Finally, the data would look like this: "<tt>@\001\000\000</tt>"
| + | With the above data: |
| + | 1 11111 00001 01111 |
| | | |
− | ==Header==
| + | But, since the data needs to be stored in little endian, it has to be in this form: |
− | The header is 20 "bytes" long, and can be devided in the following 6 data sections:<br>
| + | GGGBBBBB ARRRRRGG |
− | <tt>[IMAGE WIDTH, 4 bytes][IMAGE HEIGHT, 4 bytes][EMPTY, 4 bytes][IMAGE BUFFER SIZE, 4 bytes][IMAGE DEPTH, 2 bytes][OTHER, 2 bytes]</tt>
| + | Quite easy to fix, as we only need to swap both halves: |
− |
| + | 00101111 11111100 |
− | The header is the first data of the Ti.image format.
| |
− |
| |
− | <tt>[IMAGE WIDTH, 4 bytes]</tt> : This is the width of your image, and is four bytes long.<br>
| |
− | <tt>[IMAGE HEIGHT, 4 bytes]</tt> : This is the height of your image, and is four bytes long.<br>
| |
− | <tt>[EMPTY, 4 bytes]</tt> : This space is just filled with zeroes ("\000\000\000\000"), and is four bytes long<br>
| |
− | <tt>[IMAGE BUFFER SIZE, 4 bytes]</tt> : Amount of bytes in one buffer row , this is normally 2*width. 4 bytes long.<br>
| |
− | <tt>[IMAGE DEPTH, 2 bytes]</tt> : This is the image, normally just 16 ,two bytes long (actaully the image is 15 bit, but if you count the alpha channel it is 16)<br>
| |
− | <tt>[UNKNOWN, 2 bytes]</tt> : Unknown, maybe compression type? Two bytes long, and has 1 as default value.<bt>
| |
| | | |
| + | Ok, now we have our pixel data! |
| + | This data can then be put inside a string using escape characters (8 bit per escape character), for example: |
| + | 00101111 becomes “\047” and 11111100 becomes “\252”. If a escape character is ASCII UTF-8 representable, you can replace it with the equivalent char (“\122” can be replaced by “z”). |
| | | |
− | Example header and description:<br> | + | Example implementation in a program: |
− | <tt>.\000\000\000\018\000\000\000\000\000\000\000\092\000\000\000\016\000\001\000</tt>
| |
| | | |
− | The first four bytes ("<tt>.\000\000\000</tt>") shows that the width is 46px.<br>
| + | <source> |
− | The next four bytes ("<tt>\018\000\000\000</tt>") shows that the height 18px.<br>
| + | str = "\007\000\000\000\008\000\000\000\000\000\000\000\014\000\000\000\016\000\001\000alalalalalalalal\000\244\000\244al\000\244\000\244al\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244al\000\244\000\244\000\244\000\244\000\244alalal\000\244\000\244\000\244alalalalal\000\244alalalalalalalalalal5" |
− | The next four bytes is empty.<br>
| |
− | The four bytes ("<tt>\092\000\000\000</tt>") after this is the buffer row size (2*width)<br>
| |
− | The next two bytes ("<tt>\016\000</tt>") is the image depth.
| |
− | The next two bytes ("<tt>\001\000</tt>") are unkown.
| |
| | | |
− | ==Pixel data== | + | img = image.new(str) |
− | The pixel data comes directly after the header.
| |
− | Pixels are arranged in rows.
| |
− | Each pixel has a data section of two bytes.
| |
− | It contains the rgb values, and the alpha channel.
| |
− |
| |
− | Here is how it is structured (in binary):
| |
− |
| |
− | <tt>A - RRRRR - GGGGG - BBBBB</tt><br>
| |
− | Example : <tt>1 - 11111 - 10001 - 00000</tt><br>
| |
− | A is the alpha channel, 1 for not transparent, 0 for fully transparent. R, G, and B or the color levels.
| |
− |
| |
− | To convert this to valid pixel data it has to be cut in two:
| |
− |
| |
− | ARRRRRGG - GGGBBBBB
| |
− | 11111110 - 00100000
| |
− |
| |
− | || || Converter to decimal
| |
− | \/ \/
| |
− |
| |
− | 254 032
| |
− |
| |
− | And finally, swapped:
| |
− |
| |
− | "\032\254"
| |
− |
| |
− | This is then added to the data buffer.
| |
| | | |
− | ==Examples==
| + | function on.paint(gc) |
− | | + | gc:drawImage(img,10,10) |
− | <tt>'''str = "\007\000\000\000\008\000\000\000\000\000\000\000\014\000\000\000\016\000\001\000'''alalalalalalalal\000\244\000\244al\000\244\000\244al\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244al\000\244\000\244\000\244\000\244\000\244alalal\000\244\000\244\000\244alalalalal\000\244alalalalalalalalalal5"</tt>
| + | end |
− | | + | </source> |
− | You then need to use the function ''[[image.new]](str)''.
| |
− | | |
− | ==External Links==
| |
− | | |
− | [[User:Adriweb|Adriweb]] made [http://www.youtube.com/watch?v=YQhrMHNkL3A a video] showing some direct manipulations of the TI-Image, in order to understand better the format in general.
| |
− | | |
− | [[User:jimbauwens|Jimbauwens]] made [http://bwns.be/jim/tiviewer.html a image previewer], [http://bwns.be/jim/sprite.html a sprite creator] and a basic [http://bwns.be/jim/convert.py python image converter] (you need PythonMagick to run this)
| |
− | | |
− | These tools are not official and we'll update this as soon as we can / are allowed to.
| |
− | | |
− | In the official Nspire Lua toolkit, TI included in their software a feature that converts an image (with a common format such as jpg, png etc.) into a TI.Image format.
| |
− | | |
− | ==References==
| |
− | http://en.wikipedia.org/wiki/Highcolor
| |
− | | |
− | | |
− | [[Category:image]]
| |
The TI.image format is used by TI-Nspire Lua to display images. It uses a bitmap based format with no compression at all. The image data consists out of a header section and a data section.
The header consists out of 20 bytes of data arranged as presented in the following table. All fields are little endian integers.
Offset |
Width (bytes) |
Contents
|
0 |
4 |
Pixel width of image
|
4 |
4 |
Pixel height of image
|
8 |
1 |
Image alignment (0)
|
9 |
1 |
Flags (0)
|
10 |
2 |
Padding (0)
|
12 |
4 |
The number of bytes between successive raster lines (generally 2*width)
|
16 |
2 |
The number of bits per pixel (16)
|
18 |
2 |
Planes per bit (1)
|
Image data
The image data immediately follows the header. Pixels are arranged in rows. Each pixel is a little endian 16-bit integer with five bits for each color red, green and blue. The top bit determines if the pixel is drawn. If its zero (0), the pixel is not drawn (alpha). If it is one (1), the pixel is drawn in the RGB color of the remaining 15 bits.
Example, converting a RGB color:
R=255 → R = 31 (because each pixel can only have 5 bits per color)
G=012 → G = 1
B=123 → B = 15
A= 1 (alpha flag, make that the pixel is visible)
To make things easy, we will fist convert it to binary in this form:
A RRRRR GGGGG BBBBB
With the above data:
1 11111 00001 01111
But, since the data needs to be stored in little endian, it has to be in this form:
GGGBBBBB ARRRRRGG
Quite easy to fix, as we only need to swap both halves:
00101111 11111100
Ok, now we have our pixel data!
This data can then be put inside a string using escape characters (8 bit per escape character), for example:
00101111 becomes “\047” and 11111100 becomes “\252”. If a escape character is ASCII UTF-8 representable, you can replace it with the equivalent char (“\122” can be replaced by “z”).
Example implementation in a program:
str = "\007\000\000\000\008\000\000\000\000\000\000\000\014\000\000\000\016\000\001\000alalalalalalalal\000\244\000\244al\000\244\000\244al\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244\000\244al\000\244\000\244\000\244\000\244\000\244alalal\000\244\000\244\000\244alalalalal\000\244alalalalalalalalalal5"
img = image.new(str)
function on.paint(gc)
gc:drawImage(img,10,10)
end