| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.hwmf.record; |
| |
| import java.util.Arrays; |
| import java.util.Deque; |
| |
| /** |
| * Each ternary raster operation code represents a Boolean operation in which the values of the pixels in |
| * the source, the selected brush, and the destination are combined. Following are the three operands |
| * used in these operations. |
| * |
| * <table summary=""> |
| * <tr><th>Operand</th><th>Meaning</th></tr> |
| * <tr><td>D</td><td>Destination bitmap</td></tr> |
| * <tr><td>P</td><td>Selected brush (also called pattern)</td></tr> |
| * <tr><td>S</td><td>Source bitmap</td></tr> |
| * </table> |
| * |
| * Following are the Boolean operators used in these operations. |
| * <table summary=""> |
| * <tr><th>Operand</th><th>Meaning</th></tr> |
| * <tr><td>a</td><td>Bitwise AND</td></tr> |
| * <tr><td>n</td><td>Bitwise NOT (inverse)</td></tr> |
| * <tr><td>o</td><td>Bitwise OR</td></tr> |
| * <tr><td>x</td><td>Bitwise exclusive OR (XOR)</td></tr> |
| * </table> |
| * |
| * All Boolean operations are presented in reverse Polish notation. For example, the following operation |
| * replaces the values of the pixels in the destination bitmap with a combination of the pixel values of the |
| * source and brush: PSo. |
| * |
| * The following operation combines the values of the pixels in the source and brush with the pixel values |
| * of the destination bitmap: DPSoo (there are alternative spellings of some functions, so although a |
| * particular spelling MAY NOT be listed in the enumeration, an equivalent form SHOULD be). |
| * |
| * Each raster operation code is a 32-bit integer whose high-order word is a Boolean operation index and |
| * whose low-order word is the operation code. The 16-bit operation index is a zero-extended, 8-bit |
| * value that represents the result of the Boolean operation on predefined brush, source, and destination |
| * values. For example, the operation indexes for the PSo and DPSoo operations are shown in the |
| * following list. |
| * |
| * <table summary=""> |
| * <tr><th>P</th><th>S</th><th>D</th><th>DPo</th><th>DPan</th></tr> |
| * <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr> |
| * <tr><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td></tr> |
| * <tr><td>0</td><td>1</td><td>0</td><td>1</td><td>1</td></tr> |
| * <tr><td>0</td><td>1</td><td>1</td><td>1</td><td>1</td></tr> |
| * <tr><td>1</td><td>0</td><td>0</td><td>1</td><td>1</td></tr> |
| * <tr><td>1</td><td>0</td><td>1</td><td>1</td><td>1</td></tr> |
| * <tr><td>1</td><td>1</td><td>0</td><td>1</td><td>1</td></tr> |
| * <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr> |
| * </table> |
| * |
| * The operation indexes are determined by reading the binary values in a column of the table from the |
| * bottom up. For example, in the PSo column, the binary value is 11111100, which is equivalent to 00FC |
| * (hexadecimal is implicit for these values), which is the operation index for PSo. |
| * |
| * Using this method, DPSoo can be seen to have the operation index 00FE. Operation indexes define the |
| * locations of corresponding raster operation codes in the preceding enumeration. The PSo operation is |
| * in line 252 (0x00FC) of the enumeration; DPSoo is in line 254 (0x00FE). |
| * |
| * The most commonly used raster operations have been given explicit enumeration names, which |
| * SHOULD be used; examples are PATCOPY and WHITENESS. |
| * |
| * When the source and destination bitmaps are monochrome, a bit value of 0 represents a black pixel |
| * and a bit value of 1 represents a white pixel. When the source and the destination bitmaps are color, |
| * those colors are represented with red green blue (RGB) values. |
| */ |
| @SuppressWarnings("unused") |
| public enum HwmfTernaryRasterOp { |
| /** Fills the destination rectangle with black */ |
| BLACKNESS(0x00000042), |
| DPSOON(0x00010289), |
| DPSONA(0x00020C89), |
| PSON(0x000300AA), |
| SDPONA(0x00040C88), |
| DPON(0x000500A9), |
| PDSXNON(0x00060865), |
| PDSAON(0x000702C5), |
| SDPNAA(0x00080F08), |
| PDSXON(0x00090245), |
| DPNA(0x000A0329), |
| PSDNAON(0x000B0B2A), |
| SPNA(0x000C0324), |
| PDSNAON(0x000D0B25), |
| PDSONON(0x000E08A5), |
| PN(0x000F0001), |
| PDSONA(0x00100C85), |
| /** Fills the destination area with (not (Dst or Src)) */ |
| NOTSRCERASE(0x001100A6), |
| SDPXNON(0x00120868), |
| SDPAON(0x001302C8), |
| DPSXNON(0x00140869), |
| DPSAON(0x001502C9), |
| PSDPSANAXX(0x00165CCA), |
| SSPXDSXAXN(0x00171D54), |
| SPXPDXA(0x00180D59), |
| SDPSANAXN(0x00191CC8), |
| PDSPAOX(0x001A06C5), |
| SDPSXAXN(0x001B0768), |
| PSDPAOX(0x001C06CA), |
| DSPDXAXN(0x001D0766), |
| PDSOX(0x001E01A5), |
| PDSOAN(0x001F0385), |
| DPSNAA(0x00200F09), |
| SDPXON(0x00210248), |
| DSNA(0x00220326), |
| SPDNAON(0x00230B24), |
| SPXDSXA(0x00240D55), |
| PDSPANAXN(0x00251CC5), |
| SDPSAOX(0x002606C8), |
| SDPSXNOX(0x00271868), |
| DPSXA(0x00280369), |
| PSDPSAOXXN(0x002916CA), |
| DPSANA(0x002A0CC9), |
| SSPXPDXAXN(0x002B1D58), |
| SPDSOAX(0x002C0784), |
| PSDNOX(0x002D060A), |
| PSDPXOX(0x002E064A), |
| PSDNOAN(0x002F0E2A), |
| PSNA(0x0030032A), |
| SDPNAON(0x00310B28), |
| SDPSOOX(0x00320688), |
| /** Fills the destination area with (not Src) */ |
| NOTSRCCOPY(0x00330008), |
| SPDSAOX(0x003406C4), |
| SPDSXNOX(0x00351864), |
| SDPOX(0x003601A8), |
| SDPOAN(0x00370388), |
| PSDPOAX(0x0038078A), |
| SPDNOX(0x0390604), |
| SPDSXOX(0x003A0644), |
| SPDNOAN(0x003B0E24), |
| PSX(0x003C004A), |
| SPDSONOX(0x003D18A4), |
| SPDSNAOX(0x003E1B24), |
| PSAN(0x003F00EA), |
| PSDNAA(0x00400F0A), |
| DPSXON(0x00410249), |
| SDXPDXA(0x00420D5D), |
| SPDSANAXN(0x00431CC4), |
| /** Fills the destination area with ((not Dst) and Src) */ |
| SRCERASE(0x00440328), |
| DPSNAON(0x00450B29), |
| DSPDAOX(0x004606C6), |
| PSDPXAXN(0x0047076A), |
| SDPXA(0x00480368), |
| PDSPDAOXXN(0x004916C5), |
| DPSDOAX(0x004A0789), |
| PDSNOX(0x004B0605), |
| SDPANA(0x004C0CC8), |
| SSPXDSXOXN(0x004D1954), |
| PDSPXOX(0x004E0645), |
| PDSNOAN(0x004F0E25), |
| PDNA(0x00500325), |
| DSPNAON(0x00510B26), |
| DPSDAOX(0x005206C9), |
| SPDSXAXN(0x00530764), |
| DPSONON(0x005408A9), |
| /** Inverts the colors of the destination area */ |
| DSTINVERT(0x00550009), |
| DPSOX(0x005601A9), |
| DPSOAN(0x000570389), |
| PDSPOAX(0x00580785), |
| DPSNOX(0x00590609), |
| /** Fills the destination area with (Dst xor Pattern) */ |
| PATINVERT(0x005A0049), |
| DPSDONOX(0x005B18A9), |
| DPSDXOX(0x005C0649), |
| DPSNOAN(0x005D0E29), |
| DPSDNAOX(0x005E1B29), |
| DPAN(0x005F00E9), |
| PDSXA(0x00600365), |
| DSPDSAOXXN(0x006116C6), |
| DSPDOAX(0x00620786), |
| SDPNOX(0x00630608), |
| SDPSOAX(0x00640788), |
| DSPNOX(0x00650606), |
| /** Fills the destination area with (Dst xor Src) */ |
| SRCINVERT(0x00660046), |
| SDPSONOX(0x006718A8), |
| DSPDSONOXXN(0x006858A6), |
| PDSXXN(0x00690145), |
| DPSAX(0x006A01E9), |
| PSDPSOAXXN(0x006B178A), |
| SDPAX(0x006C01E8), |
| PDSPDOAXXN(0x006D1785), |
| SDPSNOAX(0x006E1E28), |
| PDSXNAN(0x006F0C65), |
| PDSANA(0x00700CC5), |
| SSDXPDXAXN(0x00711D5C), |
| SDPSXOX(0x00720648), |
| SDPNOAN(0x00730E28), |
| DSPDXOX(0x00740646), |
| DSPNOAN(0x00750E26), |
| SDPSNAOX(0x00761B28), |
| DSAN(0x007700E6), |
| PDSAX(0x007801E5), |
| DSPDSOAXXN(0x00791786), |
| DPSDNOAX(0x007A1E29), |
| SDPXNAN(0x007B0C68), |
| SPDSNOAX(0x007C1E24), |
| DPSXNAN(0x007D0C69), |
| SPXDSXO(0x007E0955), |
| DPSAAN(0x007F03C9), |
| DPSAA(0x008003E9), |
| SPXDSXON(0x00810975), |
| DPSXNA(0x00820C49), |
| SPDSNOAXN(0x00831E04), |
| SDPXNA(0x00840C48), |
| PDSPNOAXN(0x00851E05), |
| DSPDSOAXX(0x008617A6), |
| PDSAXN(0x008701C5), |
| /** Fills the destination area with (Dst and Src) */ |
| SRCAND(0x008800C6), |
| SDPSNAOXN(0x00891B08), |
| DSPNOA(0x008A0E06), |
| DSPDXOXN(0x008B0666), |
| SDPNOA(0x008C0E08), |
| SDPSXOXN(0x008D0668), |
| SSDXPDXAX(0x008E1D7C), |
| PDSANAN(0x008F0CE5), |
| PDSXNA(0x00900C45), |
| SDPSNOAXN(0x00911E08), |
| DPSDPOAXX(0x009217A9), |
| SPDAXN(0x009301C4), |
| PSDPSOAXX(0x009417AA), |
| DPSAXN(0x009501C9), |
| DPSXX(0x00960169), |
| PSDPSONOXX(0x0097588A), |
| SDPSONOXN(0x00981888), |
| DSXN(0x00990066), |
| DPSNAX(0x009A0709), |
| SDPSOAXN(0x009B07A8), |
| SPDNAX(0x009C0704), |
| DSPDOAXN(0x009D07A6), |
| DSPDSAOXX(0x009E16E6), |
| PDSXAN(0x009F0345), |
| DPA(0x00A000C9), |
| PDSPNAOXN(0x00A11B05), |
| DPSNOA(0x00A20E09), |
| DPSDXOXN(0x00A30669), |
| PDSPONOXN(0x00A41885), |
| PDXN(0x00A50065), |
| DSPNAX(0x00A60706), |
| PDSPOAXN(0x00A707A5), |
| DPSOA(0x00A803A9), |
| DPSOXN(0x00A90189), |
| D(0x00AA0029), |
| DPSONO(0x00AB0889), |
| SPDSXAX(0x00AC0744), |
| DPSDAOXN(0x00AD06E9), |
| DSPNAO(0x00AE0B06), |
| DPNO(0x00AF0229), |
| PDSNOA(0x00B00E05), |
| PDSPXOXN(0x00B10665), |
| SSPXDSXOX(0x00B21974), |
| SDPANAN(0x00B30CE8), |
| PSDNAX(0x00B4070A), |
| DPSDOAXN(0x00B507A9), |
| DPSDPAOXX(0x00B616E9), |
| SDPXAN(0x00B70348), |
| PSDPXAX(0x00B8074A), |
| DSPDAOXN(0x00B906E6), |
| DPSNAO(0x00BA0B09), |
| /** Fills the destination area with (Dst or not Src) */ |
| MERGEPAINT(0x00BB0226), |
| SPDSANAX(0x00BC1CE4), |
| SDXPDXAN(0x00BD0D7D), |
| DPSXO(0x00BE0269), |
| DPSANO(0x00BF08C9), |
| /** Fills the destination area with (Src and Pattern) */ |
| MERGECOPY(0x00C000CA), |
| SPDSNAOXN(0x00C11B04), |
| SPDSONOXN(0x00C21884), |
| PSXN(0x00C3006A), |
| SPDNOA(0x00C40E04), |
| SPDSXOXN(0x00C50664), |
| SDPNAX(0x00C60708), |
| PSDPOAXN(0x00C707AA), |
| SDPOA(0x00C803A8), |
| SPDOXN(0x00C90184), |
| DPSDXAX(0x00CA0749), |
| SPDSAOXN(0x00CB06E4), |
| /** Fills the destination area with Src */ |
| SRCCOPY(0x00CC0020), |
| SDPONO(0x00CD0888), |
| SDPNAO(0x00CE0B08), |
| SPNO(0x00CF0224), |
| PSDNOA(0x00D00E0A), |
| PSDPXOXN(0x00D1066A), |
| PDSNAX(0x00D20705), |
| SPDSOAXN(0x00D307A4), |
| SSPXPDXAX(0x00D41D78), |
| DPSANAN(0x00D50CE9), |
| PSDPSAOXX(0x00D616EA), |
| DPSXAN(0x00D70349), |
| PDSPXAX(0x00D80745), |
| SDPSAOXN(0x00D906E8), |
| DPSDANAX(0x00DA1CE9), |
| SPXDSXAN(0x00DB0D75), |
| SPDNAO(0x00DC0B04), |
| SDNO(0x00DD0228), |
| SDPXO(0x00DE0268), |
| SDPANO(0x00DF08C8), |
| PDSOA(0x00E003A5), |
| PDSOXN(0x00E10185), |
| DSPDXAX(0x00E20746), |
| PSDPAOXN(0x00E306EA), |
| SDPSXAX(0x00E40748), |
| PDSPAOXN(0x00E506E5), |
| SDPSANAX(0x00E61CE8), |
| SPXPDXAN(0x00E70D79), |
| SSPXDSXAX(0x00E81D74), |
| DSPDSANAXXN(0x00E95CE6), |
| DPSAO(0x00EA02E9), |
| DPSXNO(0x00EB0849), |
| SDPAO(0x00EC02E8), |
| SDPXNO(0x00ED0848), |
| /** Combines the colors of the source and the destination using the operator OR on each pixel */ |
| SRCPAINT(0x00EE0086), |
| SDPNOO(0x00EF0A08), |
| /** Fills the destination area with (Pattern) */ |
| PATCOPY(0x00F00021), |
| PDSONO(0x00F10885), |
| PDSNAO(0x00F20B05), |
| PSNO(0x00F3022A), |
| PSDNAO(0x00F40B0A), |
| PDNO(0x00F50225), |
| PDSXO(0x00F60265), |
| PDSANO(0x00F708C5), |
| PDSAO(0x00F802E5), |
| PDSXNO(0x00F90845), |
| DPO(0x00FA0089), |
| /** Fills the destination area with (Dst or (not Src) or Pattern) */ |
| PATPAINT(0x00FB0A09), |
| PSO(0x00FC008A), |
| PSDNOO(0x00FD0A0A), |
| DPSOO(0x00FE02A9), |
| /** Fills the destination rectangle with white */ |
| WHITENESS(0x00FF0062); |
| |
| |
| private static final String[] ARG_ORDER = { |
| "SSSSSS","PPPPPP","DDDDDD",null, |
| "SPDSPD","PDSPDS","DSPDSP",null, |
| "SDPSDP","DPSDPS","PSDPSD",null, |
| null, null, null, null, |
| null, null, null, null, |
| "SSP.DS", "SP.DS", null, null, |
| "SSP.PD", "SP.PD", null, null, |
| "SSD.PD", "SD.PD", null, null |
| }; |
| |
| private static final String OPS = "nxoa"; |
| |
| private final int opValue; |
| |
| HwmfTernaryRasterOp(int opValue) { |
| this.opValue=opValue; |
| } |
| |
| public static HwmfTernaryRasterOp valueOf(int opIndex) { |
| for (HwmfTernaryRasterOp bb : HwmfTernaryRasterOp.values()) { |
| if (bb.getOpIndex() == opIndex) { |
| return bb; |
| } |
| } |
| return null; |
| } |
| |
| public int getOpIndex() { |
| return opValue >>> 16; |
| } |
| |
| public int getOpCode() { |
| return opValue & 0xFFFF; |
| } |
| |
| public String describeCmd() { |
| String[] stack = new String[10]; |
| int stackPnt = 0; |
| |
| for (char c : calcCmd().toCharArray()) { |
| switch (c) { |
| case 'S': |
| case 'D': |
| case 'P': |
| stack[stackPnt++] = ""+c; |
| break; |
| case 'n': |
| stack[stackPnt-1] = "not("+stack[stackPnt-1]+")"; |
| break; |
| case 'a': |
| stack[stackPnt-2] = "("+stack[stackPnt-1]+" and "+stack[stackPnt-2]+")"; |
| stackPnt--; |
| break; |
| case 'o': |
| stack[stackPnt-2] = "("+stack[stackPnt-1]+" or "+stack[stackPnt-2]+")"; |
| stackPnt--; |
| break; |
| case 'x': |
| stack[stackPnt-2] = "("+stack[stackPnt-1]+" xor "+stack[stackPnt-2]+")"; |
| stackPnt--; |
| break; |
| case '1': |
| stack[stackPnt++] = "all white"; |
| break; |
| case '0': |
| stack[stackPnt++] = "all black"; |
| break; |
| default: |
| throw new RuntimeException("unknown cmd '"+c+"'."); |
| } |
| } |
| |
| return stack[--stackPnt]; |
| } |
| |
| |
| public String calcCmd() { |
| // taken from https://wiki.winehq.org/Ternary_Raster_Ops |
| |
| // bit 0-4: Specify the order of arguments to the raster operation |
| String argOrder = ARG_ORDER[this.opValue & 0x001F]; |
| assert(argOrder != null); |
| |
| // The boolean operators, 1st (6-7 bit), 2nd (8-9 bit), 3rd (a-b bit), 4th (c-d bit), 5th (e-f bit) |
| int nbrOfOps = 0; |
| int[] opArr = new int[5]; |
| for (int i=0, bit=6; i<opArr.length; i++, bit+=2) { |
| if ((opArr[i] = (this.opValue >>> bit) & 0x03) != 0) { |
| nbrOfOps = i+1; |
| } |
| } |
| |
| StringBuilder sbArg = new StringBuilder(), sbOp = new StringBuilder(); |
| sbArg.append(argOrder.charAt(0)); |
| for (int opIdx=0,argIdx=1; opIdx < nbrOfOps; opIdx++) { |
| char opCh = OPS.charAt(opArr[opIdx]); |
| char ch = argOrder.charAt(argIdx); |
| sbOp.append(opCh); |
| if (ch == '.') { |
| sbArg.insert(argIdx, sbOp.charAt(0)); |
| sbOp.deleteCharAt(0); |
| } |
| if (opCh == 'n') { |
| continue; |
| } |
| sbArg.append(ch == '.' ? argOrder.charAt(++argIdx) : ch); |
| argIdx++; |
| } |
| sbArg.append(sbOp); |
| |
| // bit 5: Used to apply the NOT operator to the results of the other operations |
| // if 0, there are a ODD number of operations, even number otherwise |
| if ((nbrOfOps % 2) == ((this.opValue >>> 0x05) & 1)) { |
| sbArg.append('n'); |
| } |
| |
| String ret = sbArg.toString(); |
| return ret.startsWith("DDx") ? "DDx".equals(ret) ? "0" : "1" : ret; |
| } |
| |
| public void process(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| for (char op : calcCmd().toCharArray()) { |
| switch (op) { |
| case 'S': opS(stack, dst, src, pat); break; |
| case 'P': opP(stack, dst, src, pat); break; |
| case 'D': opD(stack, dst, src, pat); break; |
| case 'n': opN(stack, dst, src, pat); break; |
| case 'a': opA(stack, dst, src, pat); break; |
| case 'o': opO(stack, dst, src, pat); break; |
| case 'x': opX(stack, dst, src, pat); break; |
| case '1': op1(stack, dst, src, pat); break; |
| case '0': op0(stack, dst, src, pat); break; |
| default: throw new IllegalStateException(); |
| } |
| } |
| } |
| |
| private static void opS(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| stack.push(src); |
| } |
| |
| private static void opP(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| stack.push(pat); |
| } |
| |
| private static void opD(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| stack.push(dst); |
| } |
| |
| private static void opN(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper = checkClone(stack.pop(), dst, src, pat, true); |
| for (int i=0; i<oper.length; i++) { |
| oper[i] = (oper[i]&0xFF000000) | (~oper[i] & 0x00FFFFFF); |
| } |
| stack.push(oper); |
| } |
| |
| private static void opA(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper1 = checkClone(stack.pop(), dst, src, pat, true); |
| int[] oper2 = checkClone(stack.pop(), dst, src, pat, false); |
| for (int i=0; i<oper1.length; i++) { |
| oper1[i] = (oper1[i]&0xFF000000) | ((oper1[i] & oper2[i]) & 0x00FFFFFF); |
| } |
| stack.push(oper1); |
| } |
| |
| private static void opO(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper1 = checkClone(stack.pop(), dst, src, pat, true); |
| int[] oper2 = checkClone(stack.pop(), dst, src, pat, false); |
| for (int i=0; i<oper1.length; i++) { |
| oper1[i] = (oper1[i]&0xFF000000) | ((oper1[i] | oper2[i]) & 0x00FFFFFF); |
| } |
| stack.push(oper1); |
| } |
| |
| private static void opX(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper1 = checkClone(stack.pop(), dst, src, pat, true); |
| int[] oper2 = checkClone(stack.pop(), dst, src, pat, false); |
| for (int i=0; i<oper1.length; i++) { |
| oper1[i] = (oper1[i]&0xFF000000) | ((oper1[i] ^ oper2[i]) & 0x00FFFFFF); |
| } |
| stack.push(oper1); |
| } |
| |
| private static void op1(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper = new int[dst.length]; |
| Arrays.fill(oper, 0xFFFFFFFF); |
| stack.push(oper); |
| } |
| |
| private static void op0(Deque<int[]> stack, int[] dst, int[] src, int[] pat) { |
| int[] oper = new int[dst.length]; |
| Arrays.fill(oper, 0xFF000000); |
| stack.push(oper); |
| } |
| |
| private static int[] checkClone(int[] oper, int[] dst, int[] src, int[] pat, boolean force) { |
| if (force && (oper == src || oper == pat || oper == dst)) { |
| return oper.clone(); |
| } else { |
| return oper; |
| } |
| } |
| } |