The png-16 image format stores imagery in 16-bit pixels. It is being developed to offer several unique benefits:Four formats are proposed: fc16, mc16, gc16, and ic16. The bit patterns for each are:
- losslessly compressed true-color imagery in smaller files then 24-bit color
- provide a display space for color imagery indistinguishable from 24-bit color
- provide an analytical and display space for grey imagery up to 12 bits per pixel
- allow deep grey and true color to co-exist in less than 48 bits per pixel
- add extra image features such as:
- positive and negative transparency
- extremely flexible color cycling
- 256-entry indexed palette
- provide a useful rgb format with close to the color precision of 18-bit RGB
- provide a primarily grey-scale format with the ability to contain color also
- handle every other color mapping not explicitly supported
- multiple 8-bit indexed palettes of 16-, 24-, and/or 48-bit pixels
fc16: ssss.iiiiii.hhhhhh saturation.intensity.hue (sih) mc16: rrrrr.ggggg.i.bbbbb red.green.intensity.blue (rgib) gc16: iiiiiiiiiiiiiii.0 (i15) rrrrr.ggggg.bbbbb.1 (rgb15) ic16: 0 .. 65535 (index)In each case, the high-order bit is on the left. Just as in PNG, network byte order is used to store the pixels, regardless of the native byte-order of the computer which generates the image.The file extension "pn6" is to be used by png-16 images. The pronunciation is "ping sixteen". A compliant png-16 program must handle both normal PNG images, as well as pn6 images. Once png-16 has proved itself in the real world, my hope is that it become a formal part of the PNG specification. Until then, it is simply a PNG-like image format.
The primary format of png-16 is fc16. In one sense it is a partial indexed representation of 24-bit color space. This is a special indexing, though, wherein most of the index values having an inherent and obvious relationship to the colors they represent. In another sense, fc16 is a superset, containing 12-bit grey in addition to true color. Although the format is basically consistent with itself, some of the features require a degree of unorthogonality. For example, absolute consistency would require that black exist 1024 times. Obviously one true black is enough.
Often the imagery used in the medical and NDT fields is too dark to
see. Useful information is stored on the film even in the darkest
regions. So when the film is digitized, the data is often modified
to enhance the dark end. Initially three scales of grey will be
documented and stored with the fc16 image. These are linear, log,
and square-root. Assuming that the image was digitized linearly at
16 bits per pixel, the formulas for these scales are as follows:
The scale types are of informational use only; they inform programs which analyze the imagery what the deep grey value represents. Most display programs will not care, and can assume that the greys map black to white in a linear fashion.
grey r: g: b comment 000 0: 0: 0, base intensity for 0 (black) 002 0: 0: 1, blue bit set 005 1: 0: 0, red bit set 007 1: 0: 1, red and blue bits set 009 0: 1: 0, green bit set 00b 0: 1: 1, green and blue bits set 00e 1: 1: 0, red and green bits set 010 1: 1: 1, base intensity for 1 012 1: 1: 2, extra blue 015 2: 1: 1, extra red 017 2: 1: 2, extra red and blue 019 1: 2: 1, extra green 01b 1: 2: 2, extra green and blue ... fde fe:fe:fd, extra red and green fe0 fe:fe:fe, base intensity for 254 fe4 fe:fe:ff, extra blue fea ff:fe:fe, extra red fee ff:fe:ff, extra red and blue ff2 fe:ff:fe, extra green ff6 fe:ff:ff, extra green and blue ffc ff:ff:fe, extra red and green fff ff:ff:ff, base intensity for 255 ("infinite")
For the most part, the fractional intensities (2/16 for blue, 5/16 for red, and 9/16 for green) correspond to their luma scale values of 0.114, 0.299, and 0.587. These are the ratios used to convert a color TV picture to its "black and white" form.
Most of the SIH colorspace is included within this color set. In general, this space may be thought of as a cylinder. The 64 hues are located around the circumference. If you think in degrees, this means there are generally 6 degrees per hue. The levels of saturation represent how far from a line going along the center of the cylinder the color is. The further away from the center, the greater the level of saturation. Finally, the height of the cylinder can be thought of as the color's intensity.
My recipe for shadow soup distorts this cylinder a bit. It removes some of the saturation oil out of the bottom colors. This is then used to make a nice topping of seven additional intensity layers. Once you add in the Pale Parables, 1 ½ more intensity levels are gained. So, when completely baked, the cylinder appears to be more of a cone. (...but it tastes great, and no cholesterol!)
From an algorithmic perspective, the SIH components are quite regular.
Note: Ok, ok. I know that intensity has a precise scientific meaning, and that I am not using it in that sense. Intensity is a mensurable quantity which is only loosely related to the way I use it herein. Those of you with one or more photometers by your monitor certainly have the right to point out that, say, just because "Rich's numeric intensity-thingy" doubles does not mean that true intensity doubles. The rest of you already understand my terminology, wherein "as intensity goes up, monitor brightness goes up".
A color is pure only when min(r,g,b) is 0.
from red
to yellow
to green
to cyan
to blue
to magenta (and back) to red. |
halfway between red and yellow | orange |
halfway between yellow and green | __________? |
halfway between green and cyan | __________? |
halfway between cyan and blue | __________? |
halfway between blue and magenta | purple |
halfway between magenta and red | __________? |
Even if I cannot name them, I can tabulate them. If you only have a 256-color display mode, you probably will not see the gradual transition between hues. But a 15-, 16-, or 24-bit display mode should show them just fine.
Hue Table with 100% Saturation -- max(r,g,b) = 241 (70/74) | |||||
100% red + | 100% green + | 100% blue + | 100% red + | ||
0% grn 10% grn 20% grn 30% grn 40% grn 50% grn 60% grn 70% grn 80% grn 90% grn100% grn |
100% red 90% red 80% red 70% red 60% red 50% red 40% red 30% red 20% red 10% red0% red |
0% blu 10% blu 20% blu 30% blu 40% blu 50% blu 60% blu 70% blu 80% blu 90% blu100% blu |
100% grn 90% grn 80% grn 70% grn 60% grn 50% grn 40% grn 30% grn 20% grn 10% grn0% grn |
0% red 10% red 20% red 30% red 40% red 50% red 60% red 70% red 80% red 90% red100% red |
100% blu 90% blu 80% blu 70% blu 60% blu 50% blu 40% blu 30% blu 20% blu 10% blu0% blu |
100% red + | 100% green + | 100% blue + | 100% red + | ||
0% grn 25% grn 50% grn 75% grn100% grn |
100% red 75% red 50% red 25% red0% red |
0% blu 25% blu 50% blu 75% blu100% blu |
100% grn 75% grn 50% grn 25% grn0% grn |
0% red 25% red 50% red 75% red100% red |
100% blu 75% blu 50% blu 25% blu0% blu |
Hue Table with 50% Saturation -- max(r,g,b) = 241 (70/74) |
So to build the scale of hues, the 160 is subtracted from 240 to give an intensity delta of 80. The percentage variations are based on this 80, but the intensity base is 160. A partial scale in decimal would be (r:g:b components) 240:160:160, 240:168:160, 240:176:160, 240:184:160, etc.  If you are really interested, examine the HTML source for the two tables. Or, now that it exists, go play with my color wheel.
SIH is able to efficiently store and to accurately restore the red, green, and blue components. 24-bit RGB contains millions of more colors (most of them visually indistinguishable), but SIH contains an excellent subset of visible colors.
Curiously, even though this SIH format contains many less colors than 24-bit RGB, some of the colors it does contain do not "fit" within 24-bit RGB. We have already established that the deep greys do not fit. Similarly, at the extremes of intensity, there are some hue:sat combinations which cannot exactly be expressed in the 24-bit RGB paradigm. Ideally, there would need to be at least 10 bits per RGB color component to maintain arithmetic precision. Thus, the png-16 infrastucture allows rendering programs to access RGB data in either 24- or 48-bit form. No claim is made that 16-bit SIH is somehow better than 24-bit RGB, which is obviously incorrect.
The paradigm for representing 192 levels of transparancy is that each level represents 2.5% of change in background intensity. This covers a range of 480%, which is shifted such that modifications from -200% to +280% are allowed.
Negative numbers represent inversion. -100% is an exact negative of the backdrop, while -200% is twice as "bright" as the negative. Positive numbers cover the other three effects. 100% is the backdrop shown as if the png-16 image were not there. Numbers from 2.5% to 97.5% represent various levels of subdual. (0% is not represented by the transparancy palette. If you wanted 0%, the background would be black, which already has a static value in the fc16 palette.) Numbers greater than 100% represent various levels of embossing.
Backdrop intensities are cropped at "max bright" and "max negative". I.e., if the backdrop is already fairly bright, multiplying it by 250% would have to either wrap around the bright intensities or have many of them be cropped to "max bright". Since cropping is visually less distracting, it is the method that has been chosen.
Amiga graphics used color-cycling (c-c) to provide great effects. It cheated -- c-c capability was built right into its graphics hardware. Mainstream PC's did not have this, and so c-c has not been common in the PC world. It is making a comeback, though. People are writing Java applets that make flames flicker and waves move atop the waters. It shouldn't be necessary to have an actual program to cause the waves to move; with color cycling, the effect can be put right back into the image itself, where it belongs.
What I propose goes beyond what the Amiga could do -- after all,
the Amiga's cpu and graphics chips were only running at 7 megahertz, and it
only had a maximum of 6 bits per pixel to work with at any one time.
Here are the color cycling features for fc16:
Using C code, here is a partial overview of the cCYC chunk:
typedef struct
{
u_char offset; // specified in dimeTime
u_char duration; // specified in dimeTime
u_char cycles; // cycle count
u_char slotBase; // starting offset into color cycle slots
short slots; // number of color-cycle slots (0 to 256)
short paletteBase; // starting offset into cycle palette (0 to 2047)
u_short stopCount; // completely stop cycling this group at stopCount
short reserve; // must be written as 0
wind_t box; // col, row, width, height of containing rectangle
char future[8]; // must be written as 0's
} cycGroup_t;
const char cycSig[4] = { 'c', 'C', 'Y', 'C' };
char numGroups; // 1 to 8 (don't include cCYC if 0!)
char cycReserve[3]; // must be written as 0's
cycGroup_t cg[8];
u_short cycPalette[2048];
char cycFuture[8]; // must be written as 0's
If your image uses pixel values corresponding to the c-c slots,
it should be handling them specifically within the cCYC chunk.
Just in case it does not do so, however, unused slots will be
rendered as black or, when defined, the backdrop color. Display programs
which do not support color cycling (grumble) must render
the first set of colors from cCYC.
What the heck is dimeTime?
It may well be that a give system cannot keep-up with the specified
cycling times, especially when large regions of an image are defined
for cycling.  However, it is essential that cycle sequencing order
be followed, even if it takes longer to do than the image designer would have
liked. So a png-16 image keeps track of cycle "clicks" in units of
dimeTime. Think of one dimeTime unit as a tenth of a second, but only
when possible. When not, the Dime Counter is not advanced until the
display program can handle the next cycling event. Thus time, in essence,
is stretched as necessary to meet the limitations of the user's computer.
Whenever possible, even on a slow computer, the dimeTimer is advanced 10
times per second. All cycling activity is based on the dimeTimer.
There are a maximum of 8 cycling groups. While not required by the
cycling infrastucture, it is recommended that no more than 32 colors be assigned
to one group. Similarly, it is recommended, but not required, that no more
8 cycles be associated with one group. Thus, the paradigm of 8 groups of
32 colors with 8 cycles per group exactly utilizes the 2048 colors of the
color cycling palette.
What exactly happens in a color cycle update? Let's take a simple case:
c-c group 0 with four members, 0x1000, 0x1001, 0x1002, and 0x1003. When
it comes time to update them:
Since transparent colors may be cycled as well, there may be a backdrop
lookup sequence between steps 1 and 2. If transparency were not 100%, there
would also be a calculation step between 2 and 3.
Crazy:
Any color is available to be used within this palette.
Transparent colors, deep grey colors,
even cycling colors -- they can all be used. Since all cycling
operations are based on dimeTime, the behavior of color cycling color-cycled
colors is well defined. The only awkward behaviour occurs when a given
cycling group chooses to use one of its own members as a cycling member.
I.e. It must both look up the current color of one of its members and write
over the color value of one of its members (maybe the same member). All
color look-ups must be done before any color changes, so the result will be
deterministic. However, don't complain to me when your brain barfs at
trying to grok the changing of changed things while they are changing.
Even crazier: The
8:32:8 paradigm described above is only a suggestion. Color cycling
groups can overlap in both the cycling colors they control and the
cycPalette[] entries they use. Both sets of tables can also wrap around,
so fc16 implementors must insure that they always use mod-256 arithmetic when
accessing the color cycle slots ((cg[x].slotBase + n) & 0x0ff).
They must also use mod-2048 arithmetic when accessing the
cycPalette[(cg[x].paletteBase + n) & 0x7ff]. And, because each
cycle group is associated with its own image region (cg[x].box]) effects can
geometrically overlap as well. Inversely, regions that do not geometrically
overlap may well temporally overlap if cycle groups contain common members.
Something sane and simple: The offset
element of cycGroup_t offers two nice features. It specifies how long
from dimeTime=0 until the first cycling should be done of that group. Thus,
all groups do not need to start at the same time. As a side effect, you
can permanently lock two (or more) cycle groups out of phase by giving them the
same cycle duration but different offsets.
Oh, yeah -- advertisers need not annoy forever. Setting the
stopCount to something other than 0 will eventually allow its
associated cycling group to become passive. The displayed colors at the
end of the last cycle are the ones which become locked in place.
Believe it or not, this sounds a lot more complex than it really is.
The most complex operation described is color cycling of transparent
colors, but this is potentially one of the neatest effects. And browsers
already have the technology to look up backdrop colors to support transparency
in GIF and PNG. Color cycling them simply means that the look-up function
will have to happen more than once. The real complexity lies with writing
a user interface for a drawing/graphics program that can take advantage of all
this without putting the artist into a state of shock. DeluxePaint
(as well as other drawing programs which were only available only on the Amiga)
has already demonstrated that it is possible to offer these tools to the artist,
and I anxiously await a version that supports fc16!
The png16 library will contain a procedure which will compare a 16-bit fc16 image with its 24-bit original. Then it will define up to 256 indexed colors to replace the SIH pixels that are most visibly different from the original image. This function will be slower than the conversion to SIH itself, and for most images it probably will not be visibly necessary. But it will be available for those willing to wait a bit in order to achieve the best possible 16-bit conversion.
Most people will not need the indices for 16-bit greyscale. For images scanned from x-ray film, it will be useful to preserve maximum contrast sensitivity at the dark (most dense) end of the film.
So for this design team sometime in the future, we will leave 256 color slots reserved for their imagination. (Don't worry, that still leaves 65,280 slots that will act the same no matter what the display hardware and display software actually are.) Each vendor is free to do anything they like with these values. Unimplemented slots should display as black, however.
These reserved slots are not only for hardware vendors, though. If Netscape or Microsoft had a great idea for some of the values, they could implement them in software. I do advise, though, that hardware vendors use values from the bottom of the pool of reserved slots, and software vendors use values from the top.
If you have a good monitor, you may be able to discern somewhere between 80 and 100 discrete contrast levels. It may be driven by 8-bit guns, but somewhere between the CRT phosphers and your eyes, many of those 256 theoretical intensities are lost. Still, there are more than the 64 levels that the pure SIH format offers.
So, if SIH is showing too many colors at the dark end, and not enough intensities overall, what can be done? Let's take our magic wand, go into the dark, and make some shadow soup. Here is the spell:
In the dark, it is difficult to discern changes in saturation. Hence the recipe for Shadow Soup. Similarly, at the palest saturation levels, it is difficult to discern changes in hue. In modern words we say Use it or lose it! So in fulfillment of the parable, the unused hues are taken away, and applied where they will be useful. For intensities greater than 16, this means adding a sixteenth saturation ring. (Even with Shadow Soup, there are only a maximum of 15 saturation rings.)
But what if it is dark and pale? Do we still want to add saturation rings that cannot be differentiated? No! Application of the Parables trades a brand new saturation ring for indiscernable pale hues. That saturation ring need not be applied at the same intensity level. So I choose to apply them to the creation of 1 ½ new intensity levels.
You read it correctly -- "1 ½". You probably are wondering what the heck one half of an intensity level is. The darkest, non-black, intensity corresponds to either 3 or 4 in a 255-entry 8-bit scale (depending on rounding.) Guess what? There is no way to represent 64 hues with a maximum intensity of 3/255. So I force a special rounding to the 4/255 level, and create a new intensity at "½" (2/255). Where does it come from? I take 12 hues from intensity 1 and give them to intensity ½. This means that the hues are 30° apart, but you cannot represent more than 12 hues with a maximum of 2/255 anyway. (Even at 3/255, there are considerably less than 64 hues.)
Except for intensities 1 and ½, the pattern to gain a new saturation ring is to have the innermost ring split in 2. The newly created innermost ring has 12 of the original hues, and the next outer ring as the remainder. For most intensities, this innermost ring corresponds to a saturation level of 1/16, and, as far as my eyes can see, 12 hues are plenty. This leaves either 52 (or 56 with augmentation) hues for the next outer ring. Let's see how this works:
Without Shadow Soup, the new intensity levels would not be possible.
The Pale Parables on their own would still grant the extra saturation ring
per intensity, but it would take too many rings to add any extra intensity
levels. The two effects combined, however, were able to grant the
1 ½ new intensities with just 11 saturation rings.
embarrassing note:
When the Pale Parables were first instituted, I had thought that they could
add 5 ½ intensity levels. In the tradition of overbalanced wheel
inventors everywhere, I became convinced that I could get "something for
nothing". But eventually I realized that there were only 1 ½ new
levels (72 ½ total) -- by then, though, I was spoiled with the concept of
enough levels to never have adjacent 4:4 jumps in the 8-bit intensity scale.
The minimum number of levels to avoid this is 74 (which gives a
255/73 ~= 3.49 jump in 8-bit intensity). Thus I forced this
to happen by "condensing" the Shadow Soup a bit. You can see this
in the repeated saturation levels.
Most of us are not used to thinking in terms of 75 ½ intensity levels.
What does that mean in a scale of 0 to 255 (or 0 to 4095)? See the table
below.
int saturation levels int:sat_slots
--- ----------------- -------------
0: 0 0: 0
½: 1 [well, part of 1] ( full) 1: 1 [only 12 hues]
1: 1 [well, most of 1] ( full) 1: 1 [minus 12 hues]
2: 2 (1/ 2, full) 1: 2
3: 2 (1/ 2, full) 1: 3
4: 3 (1/ 3, 2/ 3, full) 1: 4.. 5
5: 3 (1/ 3, 2/ 3, full) 1: 6.. 7
6: 4 (1/ 4, 2/ 4, 3/ 4, full) 1: 8..10
7: 4 (1/ 4, 2/ 4, 3/ 4, full) 1:11..13
8: 5 (1/ 5, 2/ 5, 4/ 5, full) 1:14..15, 2:1.. 2
9: 6 (1/ 6, 2/ 6, 5/ 6, full) 2: 3.. 7
10: 7 (1/ 7, 2/ 7, .., 6/ 7, full) 2: 8..13
11: 8 (1/ 8, 2/ 8, .., 7/ 8, full) 2:14..15, 3:1.. 5
12: 9 (1/ 9, 2/ 9, .., 8/ 9, full) 3: 6..13
13: 10 (1/10, 2/10, .., 9/10, full) 3:14..15, 4:1.. 7
14: 11 (1/11, 2/11, .., 10/11, full) 4: 8..15, 5:1.. 2
15: 12 (1/12, 2/12, .., 11/12, full) 5: 3..13
16: 13 (1/13, 2/13, .., 12/13, full) 5:14..15, 6:1..10
17: 13 (1/13, 2/13, .., 12/13, full) 6:11..15, 7:1.. 7
18: 14 (1/14, 2/14, .., 13/14, full) 7: 8..15, 8:1.. 5
19: 14 (1/14, 2/14, .., 13/14, full) 8: 6..15, 9:1.. 3
20: 15 (1/15, 2/15, .., 14/15, full) 9: 4..15, 10:1.. 2
21: 15 (1/15, 2/15, .., 14/15, full) 10: 3..15, 11:1
22: 15 (1/15, 2/15, .., 14/15, full) 11: 2..15
23: 16 (1/16, 2/16, .., 15/16, full) 12: 1..15
24: 16 (1/16, 2/16, .., 15/16, full) 13: 1..15
... ... ...
74: 16 (1/16, 2/16, .., 15/16, full) 63: 1..15
Intensity Table: max74=max255=max4095
0= 0= 0
½= 2= 28
1= 4= 55
2= 7= 111
3= 10= 166
4= 14= 221
5= 17= 277
6= 21= 332
7= 24= 387
8= 28= 443
9= 31= 498
10= 34= 553
11= 38= 609
12= 41= 664
13= 45= 719
14= 48= 775
15= 52= 830
16= 55= 885
17= 59= 941
18= 62= 996
19= 65=1051
20= 69=1107
21= 72=1162
22= 76=1217
23= 79=1273
24= 83=1328
25= 86=1383
26= 90=1439
27= 93=1494
28= 96=1549
29=100=1605
30=103=1660
31=107=1714
32=110=1771
33=114=1826
34=117=1881
35=121=1937
36=124=1992
37=127=2047
38=131=2103
39=134=2158
40=138=2214
41=141=2269
42=145=2324
43=148=2380
44=152=2435
45=155=2490
46=159=2546
47=162=2601
48=165=2656
49=169=2712
50=172=2767
51=176=2822
52=179=2878
53=183=2933
54=186=2988
55=190=3044
56=193=3099
57=196=3154
58=200=3210
59=203=3265
60=207=3320
61=210=3376
62=214=3431
63=217=3486
64=221=3542
65=224=3597
66=227=3652
67=231=3708
68=234=3763
69=238=3818
70=241=3874
71=245=3929
72=248=3984
73=252=4040
74=255=4095
Normal
Pale Parables
Shadow Soup
Take a look at the
SIH Wheel to see how all this fits.
You will see a color space that can be generally described as a cylinder, but
tapers to a cone at the dark end.
Red and cyan are chosen intentionally. The yellow and green domains need no augmentation, but green is the primary color that is most influential on perceived brightness. Since the red and cyan domains contain green while the blue and magenta domains do not, the former are always augmented.
But blue and magenta can be augmented, as a global option for the image. The trade-off is that you give up the 12-bit greyscale. The format, of course, still provides a full 8-bit greyscale. But enough slots are freed up to fully populate 4 more hue wedges, two of which are assigned to blue, and two to magenta.
Hue Mapping range is 0 to 63, plus potential augments 0, 1, 2, and 3 | ||
red domain | 0 to 11 | 5° increments |
yellow domain | 12 to 21 | 6° increments |
green domain | 22 to 31 | 6° increments |
cyan domain | 32 to 43 | 5° increments |
blue domain |
44
to
53
44 to 53, a0, a1 |
6° increments
5° increments |
magenta domain |
54
to
63
a2, a3, 54 to 63 |
6° increments
5° increments |
With this augmentation enabled, what does it do to the 4:6:6 bit-mapping
of the SIH color space? Here is the road map:
a) ssss:iiiiii:hhhhhh static colors (s > 0, i > 0) a) bbbb:000000:mmmmmm black magic colors (bbbb > 0) b) 0000:iiiiiiiiiiii 12-bit grey c) 0000:iiiiiiii:0000 8-bit grey c) 0000:000000:iiiiii 8+bit grey c) 0000:iiiiii:hh:ssss 4 augmented hues (s > 0, i > 0) a == always; b == color augmentation off; c == color augmentation on
There is a strange feature added by this road map. Just as in the black magic mapping, the 8-bit grey augmented mapping gives up some values to deep grey. In 8-bit grey, the first 4 intensities end up with extra precision of 1/16. Do not think of these low intensities in terms such as "59" of 4095. Instead, this example would be "3 11/16" of 255.
How could this be useful? Well, it would not be unless some of the
grey had originated in a 12-bit scanner. In the 12-bit realm, the scanner
may have been configured to the following characteristic curve:
intensity density 4000 1.2 400 2.2 40 3.2 4 4.2
(If you are not familiar with film and density values, just skim through this section -- you probably will not have any images that can use the extra dark values. As a reference point, ~0.07 is air, ~0.20 is clear film, and ~3.0 is "black" at normal room light.) Note that an entire decade of density lies between intensities 40 and 4. So if this 12-bit image were mapped down to simple 8-bit, not only would the contrast sensitivity of the image be degraded, but the image would actually lose a significant portion of its dynamic range. With the augmented 8-bit grey, however, the full dynamic range of the image can be preserved.
rrrrr.ggggg.i.bbbbb : choosing the i-bit | |
low-order bit r g b: i 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 1 0 0 0 1 0 1 1 1 1 0 1 1 1 1 1 |
As the table on the left shows, the sixth most significant bit (6msb)
is chosen essentially by a vote. When 0 or 1 of the input color
components have their 6msb set to 0, then i = 0.
Otherwise, when 2 or 3 of the color compenents have their 6msb set to 1,
then i = 1. Thus, no rgib pixel is more than 1 low-order-bit different than the pixel's 18-bit equivalent. |
The mc16 format is a simple subset of 24-bit RGB. I submit, though, that it is best possible RGB mapping that can be done in 16 bits. Examine the following table:
virtual bit format |
bit accuracy | accuracy wrt 18 bits |
accuracy wrt 24 bits |
15 bits rrrrr0.ggggg0.bbbbb0 |
15.125 / 18 | 84.03% | 63.02% |
'dumb' 16 bits rrrrr0.gggggg.bbbbb0 |
16.250 / 18 | 90.28% | 67.71% |
mc16 rrrrri.gggggi.bbbbbi |
17.250 / 18 | 95.83% | 71.88% |
18 bits rrrrrr.gggggg.bbbbbb |
18.000 / 18 | 100% | 75.06% |
"17.25 bits out of 18?? -- Are you on drugs? You only have 16 bits!"
My answer is that democracy is a wonderful thing. By having the
three 6msb's vote on the value of i, at least two of the bits will automatically
equal i. So the 15 stored bits plus the two 6msb bits that are known to be
correct sum up to 17 bits. Then if you look at the
choosing the i-bit table, you can see that
the third bit is statistically correct 2 times out of 8. I.e., 25% of
the time all bits are equal.
Unfortunately, many of the manufacturers of video boards chose to implement
the "dumb 16-bits" approach in their hardware. I can either
stubbornly refuse to accept this reality, or accept it as an unfortunate
fact of computer life. Emotionally, I would rather do the former, but
my logic says to submit to reality. Logic wins this round. So the
mc16 format has a de-optimize option that blindly uses the i-bit as the
sixth g-bit. This means there are 32 levels of grey, red, and blue,
but 64 levels of green. Sheesh!
(If you are a hardware designer about to build a new video board, please be
smart about 16-bit support, ok? All you have to do is design it
internally to be 18 bits, then feed the i-bit as the lower bit of rrrrri,
gggggi, and bbbbbi. ..two extra runs and two extra gates,
that's all!)
When transforming from 24-bit RGB to mc16, when would you de-optimize?
It depends on how you expect the imagery to be used.
If it is for games using the Microsoft DirectX infrastructure, then you might
as well de-optimize the image. However, if you want it to look as good as
possible on a 24-bit graphic display (and have religious reservations against
SIH), then choose the normal, optimized form with 64 levels of grey and all
the primary and secondary colors. Note that even on boards with dumb
16-bit mapping, an optimized mc16 image will look just fine.
This format can offer one special effect, 32 levels of transparency. When the low order bit specifies color, but the r, g, and b bits are all equal (specifying grey) then a shade of transparency can used instead. Four of these will be used for inverted (negative) levels of transparency: -25%, -50%, -75%, and -100%. The other 28 shades will provide an ascending scale, from 5% to 140% in 5% intervals. This scheme is not as flexible as the transparency levels offered by fc16, but the major effects of inversion, subdual, clear transparency, and embossing are all included.
By default, though, transparency is disabled, and the 32 values are simply interpreted as their expected grey levels.  A special chunk, gOPT, would have to be present and contain an enabling field. The reason for not making transparency the default is to simplify things for those who don't care about special effects.
There are three forms of indices. These are FALS, TRUE, and PLTE. The FALS type originated with the PNG Development Group, but there was insufficient support to make it part of the PNG specification. Since this wheel was already there, I chose not to re-invent it. FALS allows for efficient implementation of False Color imagery (also known as "pseudo-color"). This is a technique in image processing where colors are assigned to levels of grey in hopes of enhancing certain image details. It is also used just to help make an image more appealing to human eyes. Many of the "color" pictures you have seen from NASA of distant objects are actually greyscale images that have been enhanced with false color.
The TRUE and PLTE forms are used to generate "random" palettes. In this sense, random simply means there is not necessarily a relationship between adjacent palette entries. The image itself may be quite non-random! When there is a strong relationship between adjacent entries, then the FALS chunk is more efficient in storing the information. The name "TRUE" is basically a joke; since there was a FALS form, there needed to be a TRUE form also, right? But if you do not like jokes, then think of it as an indexed form for very precise true-color imagery. The RGB elements mapped by the TRUE indices have 16-bit (0..65535) precision (just like the FALS type). The PLTE form is an extension of the native PNG PLTE chunk type. The RGB elements only have 8-bit (0..255) precision, but instead of being limited to just 256 total indices, in png-16 there can be up to 65536.
In summary, the index forms are:
PLTE: maps up to 65536 RGB values with 24-bit (8:8:8) precision TRUE: maps up to 65536 RGB values with 48-bit (16:16:16) precision FALS: maps up to 65536 RGB values with 48-bit precision; linear interpolation is used to map between adjacent indexed values
Note that the whole set of 65536 potential values need not be mapped. Non-mapped values are treated as 16-bit greyscale numbers with an intensity equal to its index. The one exception is that non-mapped indices 0..255 are treated as 8-bit greyscale numbers. If you need these bottom values to be 16-bit (or 12-bit, etc) greyscale, then use a FALS chunk to map them as desired. It is recommended that all used values be mapped, but some default was necessary in the event of error or sloppiness by the image-generation software.
Also note that any of the three index chunks may appear multiple times. There is a base index element within each chunk type that defines the starting value, and consecutive values are used for the given chunk descriptor.
Since the png-16 infrastructure already allows for multiple levels of backdrop transparency, color cycling, and vendor options it would be an omission not to allow the ic16 type to access them. Each of these possiblities, when present, occupies 256 fixed slots of the ic16 map. They are high in the map, and each can be individually enabled. The mapping is:
backdrop transparancy: 0xfd00 - 0xfdff color cycling: 0xfe00 - 0xfeff vendor options: 0xff00 - 0xffff
Normal PNG transparency is available also. The difference between the two kinds of transparency is that PNG transparency involves another band of image data which specifies the mix ratio at each pixel between the foreground PNG (or png-16) image and the background. Backdrop transparency is a feature only of png-16, and it specifies that there is no foreground color. Instead the background color is used in some proportion of its normal brightness level.
Within the png-16 framework, ic16 can be used for "no-brainer mosaic's", where multiple 8-bit indexed images are displayed as a single image. This has traditionally been difficult to do, because an 8-bit palette that was right for one image could be a tremendous mismatch for another, and mosaicing them within a single indexed image resulted in compromised quality of the source images. However, when there are over 65,000 indices to choose from, each mosaiced sub-image is free to use its "ideal" set of 256 colors. (Any png-16 format can be used as the base mosaic type, but if the source imagery was indexed to begin with, then it will probably be easiest to use ic16.)
Bitmap of Indexed Elements in a Byte | |
bits bit-map of byte 1: p.p.p.p.p.p.p.p 2: pp.pp.pp.pp 3: 0.ppp.0.ppp 4: pppp.pppp 5: 000.ppppp 6: 00.pppppp 7: 0.ppppppp 8: pppppppp |
The table to the left shows the breakdown of various sizes of pixel indexes, and
how they are stored within a byte. The letter 'p' (and 'pp', etc.) represent
an indexed pixel. The numeral '0' is ignored, but should be written as a 0.
Pixels are extracted from each byte in left-to-right order (high-bit(s) is first pixel, next high bit(s) second, etc.). |
There is a chunk descriptor associated with each 8-bit indexed data set. It
looks something like:
Note that the 8-bit indexed pixels reference 16-bit pixels. Just to make life
interesting, the 16-bit pixels may well be from the 16-bit indexed palette.
(Well, only if the image type is ic16 and has a 16-bit indexed
palette...) Any (but only one) of the png-16 image types may be used in
an 8-bit indexed image. So if you want color-cycling, you would have to
choose either fc16 or ic16 as the base image type (the other types do not
support color-cycling).
const char i8Sig[4] = { 'i', 'd', 'x', '8' };
u_char i8_id; // 2 thru 254
char i8_bits; // 1 to 8
u_char i8_Reserve[2]; // must be written as 0's
short i8_lutSize; // 2 thru 2^i8_bits
u_short i8_LUT[i8_lutSize] =
{ /* pixels which make up the LUT */ };
char i8_Future[8]; // must be written as 0's
bytes | shorts | both 8- and 16-bit |
u_char |
u_short |
|
The reason for the data type of i16_8_t (which, for non-C programmers, is an overlapped union of an 8-bit entity and a 16-bit entity) is that png-16 natively supports pixels up to 16 bits in size per color component. Since a png-16 file uses network byte order through-out, the high-order byte of an 'i16' unit is the same as the 'i8' unit. If you want to access the RGB values of, say, pixel 0x32A5 (12965), it would be:
Color 8-bit values 16-bit values red lutRGB[12965*3 + 0].i8 lutRGB[12965*3 + 0].i16 green lutRGB[12965*3 + 1].i8 lutRGB[12965*3 + 1].i16 blue lutRGB[12965*3 + 2].i8 lutRGB[12965*3 + 2].i16
Note that a given application program need not initialize or use any of these LUT's. For the mc16 format especially, the 16-bit pixels can be expanded by the cpu "inline", and no LUT would be required. The gc16 format would take a little more calculation, but handling fc16 and ic16 pretty-much requires the existence of lutRGB[][].
typedef struct transp_t
{
float coefficient;
u_short index;
};
int transpElementCount;
transp_t lutTransp[256];
Image type fc16 has 201 elements, gc16 may have 33 elements, and ic16 may have 256 elements. For fc16 and gc16, one of the element indices points to black within the color space, but the ic16 case inherently contains black.
u_short lutCC[256];
A mosaic requires a degree of structure. For png-16, the overall format of a mosaic'd image is described by a canvass. A canvass has the following characteristics:
Notes:
Because this is still a network-graphic format, being able to render the image while it loads from the net is a prerequisite. How can this be done, when there are multiple image chunks to render? Simple. Only render one image.
Ok, two. If a backdrop image chunk is defined, it must precede all other image chunks. It is suggested that a backdrop image be small; it will be rendered with a display characteristics such as tile, center, or zoom-to-canvass. The backdrop chunk should not use any dynamic effects such as color cycling. Display programs are free to ignore these special effects for the backdrop, but are not required to do so. However, display programs must pass thru transparency; i.e. if both a mosaic source image and the backdrop choose transparency in a certain location, then the browser background would show thru. The backdrop should be rendered as it is transferred.
There is one case where a large backdrop image might make sense. If it were the primary image, and a small number of smaller 8-bit images were to be mosaic'd onto the primary image, then this usage is expressly condoned. Still, transient effects such as color-cycling should not be written to the backdrop when that portion of the backdrop has a mosaic'd element on top of it.
Now comes the problem of rendering multiple source images as a single mosaic in one pass. There are several complicating elements. A mosaic source stream is (probably) an 8-bit indexed image feeding a 16-bit stream. Each mosaic'd element has a different 8:16 LUT associated with it. And the geometry of how the mosaic'd images fit within the canvass vary. (Two images may be in the first row of the mosaic, three in the second, etc.)
All this can be solved by using a set of line descriptors. A line descriptor describes one (or more) lines of the canvass. It is specified in a set of chunk_id:count pairs (IDCP's):
Each line descriptor may be a different size, but the total count of pixels must sum up to the canvass width. Line descriptors should be thought of as rasterization initializers. Once the file has been decompressed, it is then rasterized to a pure 16-bit memory image (im16). Pixels are then extracted on demand from the im16 data, not from the complex mosaic'd storage format.
I had considered allowing 16-bit pieces to be mosaic'd-in as well. This would have allowed mixed 16-bit formats, such as fc16 and gc16 (as well as 8-bit data). Fortunately, common sense caught up with me, and I realized that featuritus was creeping in. Thus, all 16-bit imagery that makes up a png-16 image must be of the same image type. However, 8-, 4-, 2-, and 1-bit images can be mosaic'd together, along with imagery of a single 16-bit style.
When there is a canvass, the following summarizes the data stream elements: