/* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ package org.openoffice.xmerge.util; import java.awt.Color; /** * Utility class mapping RGB colour specifications to the colour indices used * in the Pocket PC. * *

The original converter was written for use with Pocket Word it was later * put into the utils so Pocket excel could use this code also. For this reason * the default values are those used by Pocket Word but a colour table can be * passed in through the constructor to map the 16 values to a colour table.

* *

These colour indices are based on the Windows VGA 16 colour palette, which * later was used as the basis for the named colours in the HTML 3.2 * specification.

* *

In Pocket Word's case, the match to the VGA 16 palette is not exact as it * swaps Grey and Silver, with Silver being the darker colour (i.e. having the * lower RGB value).

*/ public class ColourConverter { /** Colour table index for Black */ private static final short BLACK = 0; /** Colour table index for Silver */ private static final short SILVER = 1; /** Colour table index for Grey */ private static final short GREY = 2; /** Colour table index for White */ private static final short WHITE = 3; /** Colour table index for Red */ private static final short RED = 4; /** Colour table index for Lime */ private static final short LIME = 5; /** Colour table index for Blue */ private static final short BLUE = 6; /** Colour table index for Aqua */ private static final short AQUA = 7; /** Colour table index for Fuchsia */ private static final short FUCHSIA = 8; /** Colour table index for Yellow */ private static final short YELLOW = 9; /** Colour table index for Maroon */ private static final short MAROON = 10; /** Colour table index for Green */ private static final short GREEN = 11; /** Colour table index for Navy */ private static final short NAVY = 12; /** Colour table index for Teal */ private static final short TEAL = 13; /** Colour table index for Purple */ private static final short PURPLE = 14; /** Colour table index for Olive */ public static final short OLIVE = 15; private short tableLookup[] = null; /** * Default constructor used in the case where a lookup table is not required. */ public ColourConverter() { } /** * Constructor that passes in the colour lookup table. * *

This is required in cases where the 16 colour values are something * other than there default values (e.g. in the case of pocket Excel).

* * @param lookup a 16 bit array mapping the 16 colours to their values. */ public ColourConverter(short lookup[]) { tableLookup = lookup; } /** * Uses the colour table if it exists to translate default values to values * in the colorTable. */ private short colourLookup(short colour) { if(tableLookup!=null) { return tableLookup[colour]; } else { return colour; } } /** * Uses the colour table if it exists to translate default values to values * in the colorTable. */ private short indexLookup(short index) { short result = 0; if(tableLookup!=null) { for(short i = 0;i < tableLookup.length;i++) { if(tableLookup[i]==index) result = i; } } else { result = index; } return result; } /** * This method maps a Pocket Word colour index value to an RGB value as used * by OpenOffice. * * @param colour The index into Pocket Word's colour table. * * @return A {@code Color} object representing the RGB value of the Pocket * Word colour. */ public Color convertToRGB (short colour) { short index = indexLookup(colour); int r = 0; int g = 0; int b = 0; switch (index) { case SILVER: r = g = b = 128; break; case GREY: r = g = b = 192; break; case WHITE: r = g = b = 255; break; case RED: r = 255; break; case LIME: g = 255; break; case BLUE: b = 255; break; case AQUA: g = b = 255; break; case FUCHSIA: r = b = 255; break; case YELLOW: r = g = 255; break; case MAROON: r = 128; break; case GREEN: g = 128; break; case NAVY: b = 128; break; case TEAL: b = g = 128; break; case PURPLE: r = b = 128; break; case OLIVE: r = g = 128; break; case BLACK: default: r = g = b = 0; break; } return new Color(r, g, b); } /** * This method approximates an RGB value (as used by Writer) to one of the * 16 available colours. * *

Most of the supported colours have their components set to either 0, * 128 or 255. The exception is 'Grey' which is {@literal 0xC0C0C0}.

* * @param colour {@code Color} object representing the RGB value of the * colour. * * @return Index into the Pocket Word colour table which represents the * closest match to the specified colour. */ public short convertFromRGB (Color colour) { int matchedRGB = 0; short indexColour = 0; int reducedMap[] = new int[] { 0, 0, 128 }; int red = colour.getRed(); int green = colour.getGreen(); int blue = colour.getBlue(); // We need to convert the pale colours to their base color rather than // white so we modify the rgb values if the colour is sufficiently white. if(red>0xC0 && green>0xC0 && blue>0xC0) { if(red!=0xFF) red = getClosest(red, reducedMap); if(green!=0xFF) green = getClosest(green, reducedMap); if(blue!=0xFF) blue = getClosest(blue, reducedMap); } // Need to derive an RGB value that has been rounded to match the ones // Pocket Word knows about. matchedRGB += getClosest(red) << 16; matchedRGB += getClosest(green) << 8; matchedRGB += getClosest(blue); // The colour map used by Pocket Word doesn't have any combinations of // values beyond 0 and any other value. A value of 255 in any RGB code // indicates a dominant colour. Other colours are only modifiers to the // principal colour(s). Thus, for this conversion, modifiers can be // dropped. if ((matchedRGB & 0xFF0000) == 0xFF0000 || (matchedRGB & 0xFF00) == 0xFF00 || (matchedRGB & 0xFF) == 0xFF) { if ((matchedRGB & 0xFF0000) == 0x800000) { matchedRGB ^= 0x800000; } if ((matchedRGB & 0xFF00) == 0x8000) { matchedRGB ^= 0x8000; } if ((matchedRGB & 0xFF) == 0x80) { matchedRGB ^= 0x80; } } /* * And now for the actual matching ... * * Colours are based on the Windows VGA 16 palette. One difference * though is that Pocket Word seems to switch the RGB codes for Grey * and Silver. In Pocket Word Silver is the darker colour leaving Grey * is closest to White. * * Shades of grey will be converted to either Silver or White, where * Grey may be a more appropriate colour. This is handled specially * only for Silver and White matches. */ switch (matchedRGB) { case 0x000000: indexColour = BLACK; break; case 0x808080: if (!isGrey(colour)) { indexColour = SILVER; } else { indexColour = GREY; } break; case 0xFFFFFF: if (!isGrey(colour)) { indexColour = WHITE; } else { indexColour = GREY; } break; case 0xFF0000: indexColour = RED; break; case 0x00FF00: indexColour = LIME; break; case 0x0000FF: indexColour = BLUE; break; case 0x00FFFF: indexColour = AQUA; break; case 0xFF00FF: indexColour = FUCHSIA; break; case 0xFFFF00: indexColour = YELLOW; break; case 0x800000: indexColour = MAROON; break; case 0x008000: indexColour = GREEN; break; case 0x000080: indexColour = NAVY; break; case 0x008080: indexColour = TEAL; break; case 0x800080: indexColour = PURPLE; break; case 0x808000: indexColour = OLIVE; break; default: // Just in case! indexColour = BLACK; break; } return colourLookup(indexColour); } /** * Default implementation, checks for the closest of value to 0, 128 or 255. */ private int getClosest(int value) { int points[] = new int[] { 0, 128, 255 }; return getClosest(value, points); } /** * Utility method that returns the closest of the three points to the value * supplied. */ private int getClosest(int value, int[] points) { if (value == points[0] || value == points[1] || value == points[2]) { return value; } if (value < points[1]) { int x = value - points[0]; return (Math.round((float)x / (points[1] - points[0])) == 1 ? points[1] : points[0]); } else { int x = value - points[1]; return (Math.round((float)x / (points[2] - points[1])) >= 1 ? points[2] : points[1]); } } /** * Checks to see if the supplied colour can be considered to be grey. */ private boolean isGrey(Color c) { int matchedRGB = 0; int points[] = new int[] { 128, 192, 255 }; matchedRGB += getClosest(c.getRed(), points) << 16; matchedRGB += getClosest(c.getGreen(), points) << 8; matchedRGB += getClosest(c.getBlue(), points); return matchedRGB == 0xC0C0C0; } }