Ternary Raster Ops
Ternary raster operation codes define how the graphics device interface (GDI) combines the bits in a source bitmap with the bits in the destination bitmap.
Microsoft has some documentation on ternary raster operations ([1]), but it is incomplete. They list 256 valid ternary raster operations, each of which is composed of a 16 bit number in the high word, and an apparently random number in the low word.
While these are sufficient to specify any of the outcomes available to the raster operations, the bits in the low word are meaningful (although it seems Windows may no longer use them).
The high word is documented at the page shown above, so this page will deal with the low word.
This page assumes you have read and understand the Microsoft documentation linked above.
Contents
The bitwise format
Bits | Purpose | |
0-4 | Specify the order of arguments to the raster operation | |
5 | Used to apply the NOT operator to the results of the other operations | |
6-7 | The first boolean operator | |
8-9 | The second boolean operator | |
a-b | The third boolean operator | |
c-d | The fourth boolean operator | |
e-f | The fifth boolean operator |
The order of arguments
Each raster operation uses values for S, D and P, but because there are 5 boolean operators available, there may be 6 arguments such that any of S, D or P may be repeated to make up the 6 arguments.
In the following table, where a "dot" appears, the first boolean operator will appear in the postfix raster operation description
Order bits value | Order | |
0x00 | SSSSSS | |
0x01 | PPPPPP | |
0x02 | DDDDDD | |
0x03 | ||
0x04 | SPDSPD | |
0x05 | PDSPDS | |
0x06 | DSPDSP | |
0x07 | ||
0x08 | SDPSDP | |
0x09 | DPSDPS | |
0x0a | PSDPSD | |
0x0b | ||
0x0c | ||
0x0d | ||
0x0e | ||
0x0f | ||
0x10 | ||
0x11 | ||
0x12 | ||
0x13 | ||
0x14 | SSP.DS | |
0x15 | SP.DS | |
0x16 | ||
0x17 | ||
0x18 | SSP.PD | |
0x19 | SP.PD | |
0x1a | ||
0x1b | ||
0x1c | SSD.PD | |
0x1d | SD.PD | |
0x1e | ||
0x1f |
The boolean operators
The boolean operators are read from the value starting from the
Boolean operator bits | Operator | |
0x0 | Not (n) | |
0x1 | Xor (x) | |
0x2 | OR (o) | |
0x3 | AND (a) |
The final "Not" bit
The "Not" bit is actually an indicator of the number of boolean operations. If the number of boolean operations is even, this bit is set to 1. If the number of boolean operations is odd, this bit is set to zero.
When you read the boolean operations, you start at the first (in bits 6-7). As soon as you encounter one that is zero and is followed only by others that are zero, you stop. That gives you your provisional number of operations. So if all operations are zero, you have zero operations. If operations 2-5 are zero, you have one. If operations 3-5 are zero, you have two.
If the number of provisional operations does not correspond with the value of the "Not" bit, there must be another operation - that operation is the one with all zero bits, a "Not" operation.
Some examples
From the Microsoft page listed above we know that operation Dn has a low word value of 0x0009, made up as follows:
Bits 0-4 | 01001 | 0x9 | Sequence DPSDPS | |||
Bit 5 | 0 | 0 | Odd number of operations | |||
Bits 6-7 | 00 | 0x0 | (no operator) | |||
Bits 8-9 | 00 | 0x0 | (no operator) | |||
Bits a-b | 00 | 0x0 | (no operator) | |||
Bits c-d | 00 | 0x0 | (no operator) | |||
Bits e-f | 00 | 0x0 | (no operator) |
Since there must be an odd number of operations, but the number of operations in specified is zero, there must be an even number. Therefore we must have a "NOT" operation. We then take the arguments from the sequence as needed (in this case we only need 1), giving "Dn".
There is another way to create the "Dn" operation - 0x0006:
Bits 0-4 | 00110 | 0x6 | Sequence DSPDSP | |||
Bit 5 | 0 | 0 | Odd number of operations | |||
Bits 6-7 | 00 | 0x0 | (no operator) | |||
Bits 8-9 | 00 | 0x0 | (no operator) | |||
Bits a-b | 00 | 0x0 | (no operator) | |||
Bits c-d | 00 | 0x0 | (no operator) | |||
Bits e-f | 00 | 0x0 | (no operator) |
From the MSDN page, operation PDSPDaoxxn has the low word value 0x16c5, which decodes as follows:
Bits 0-4 | 00101 | 0x5 | Sequence PDSPDS | |||
Bit 5 | 0 | 0 | Odd number of operations | |||
Bits 6-7 | 11 | 0x3 | And | |||
Bits 8-9 | 10 | 0x2 | Or | |||
Bits a-b | 01 | 0x1 | Xor | |||
Bits c-d | 01 | 0x1 | Xor | |||
Bits e-f | 00 | 0x0 | (no operator) |
Four operators are specified, so we need a fifth (which must be Not) since the Not bit shows an odd number of operations. Our operations are therefore aoxxn. This requires 5 input values taken from the sequence, hence PDSPDaoxxn
From the MSDN page, operation SSPxDSxoxn has a low word value of 0x1954, which decodes as follows:
Bits 0-4 | 10100 | 0x14 | Sequence SSP.DS | |||
Bit 5 | 0 | 0 | Odd number of operations | |||
Bits 6-7 | 01 | 0x1 | Xor | |||
Bits 8-9 | 01 | 0x1 | Xor | |||
Bits a-b | 10 | 0x2 | Or | |||
Bits c-d | 01 | 0x1 | Xor | |||
Bits e-f | 00 | 0x0 | (no operator) |
Four operators are specified, so we need a fifth (which must be Not) since the Not bit shows an odd number of operations. Our operations are therefore xxoxn. This requires 5 values from the sequence. Note that the first operation goes where the "dot" is, giving us: SSPxDSxoxn.
From the MSDN page, operation PDSxnan has a low word value of 0x0c65, which decodes as follows:
Bits 0-4 | 00101 | 0x05 | Sequence PDSPDS | |||
Bit 5 | 1 | 1 | Even number of operations | |||
Bits 6-7 | 01 | 0x1 | Xor | |||
Bits 8-9 | 00 | 0x0 | Not | |||
Bits a-b | 11 | 0x3 | And | |||
Bits c-d | 00 | 0x0 | (no operator) | |||
Bits e-f | 00 | 0x0 | (no operator) |
Three operators are specified, so we need a fourth (which must be Not) since the Not bit shows an even number of operations. Our operations are therefore xnan. This requires 3 values from the sequence, giving PDSxnan.
Summary
As you can see the low word of the raster operations does have meaning and is not just a set of random numbers. Many raster operations can be represented in more than one way, although MSDN only documents one way.
If you are implementing the raster operations, then decoding the low 16 bit value may be better than having a huge switch statement that picks a set of operations based on one of the 256 valid high word values.