1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.xmerge.util; 25 26 27 import java.awt.Color; 28 29 /** 30 * Utility class mapping RGB colour specifications to the colour indices used 31 * in the Pocket PC. The original converter was written for use with Pocket 32 * Word it was later put into the utils so Pocket excel could use this code 33 * also. For this reason the defualt values are those used by Pocket Word but 34 * a colour table can be passed in through the constructor to map the 16 35 * values to a colour table. 36 * 37 * These colour indices are based on the Windows VGA 16 colour palette, which 38 * later was used as the basis for the named colours in the HTML 3.2 39 * specification. 40 * 41 * In Pocket Word's case, the match to the VGA 16 palette is not exact as it 42 * swaps Grey and Silver, with Silver being the darker colour (i.e. having the 43 * lower RGB value). 44 */ 45 46 public class ColourConverter { 47 48 /** Colour table index for Black */ 49 private static final short BLACK = 0; 50 51 /** Colour table index for Silver */ 52 private static final short SILVER = 1; 53 54 /** Colour table index for Grey */ 55 private static final short GREY = 2; 56 57 /** Colour table index for White */ 58 private static final short WHITE = 3; 59 60 /** Colour table index for Red */ 61 private static final short RED = 4; 62 63 /** Colour table index for Lime */ 64 private static final short LIME = 5; 65 66 /** Colour table index for Blue */ 67 private static final short BLUE = 6; 68 69 /** Colour table index for Aqua */ 70 private static final short AQUA = 7; 71 72 /** Colour table index for Fuchsia */ 73 private static final short FUCHSIA = 8; 74 75 /** Colour table index for Yellow */ 76 private static final short YELLOW = 9; 77 78 /** Colour table index for Maroon */ 79 private static final short MAROON = 10; 80 81 /** Colour table index for Green */ 82 private static final short GREEN = 11; 83 84 /** Colour table index for Navy */ 85 private static final short NAVY = 12; 86 87 /** Colour table index for Teal */ 88 private static final short TEAL = 13; 89 90 /** Colour table index for Purple */ 91 private static final short PURPLE = 14; 92 93 /** Colour table index for Olive */ 94 public static final short OLIVE = 15; 95 96 private short tableLookup[] = null; 97 98 /** 99 * Default constructor used in the case where a lookup table is not 100 * required 101 */ ColourConverter()102 public ColourConverter() { 103 104 } 105 106 /** 107 * Constructor that passes in the colour lookup table. This is required in 108 * cases where the 16 colour values are something other than there default 109 * values (e.g. in the case of pocket Excel) 110 * 111 * @param lookup a 16 bit array mapping the 16 colours to their values 112 */ ColourConverter(short lookup[])113 public ColourConverter(short lookup[]) { 114 115 tableLookup = lookup; 116 } 117 118 /** 119 * Uses the colour table it it exists to translate default values to 120 * values in the colorTable 121 */ colourLookup(short colour)122 private short colourLookup(short colour) { 123 124 if(tableLookup!=null) { 125 return tableLookup[colour]; 126 } else { 127 return colour; 128 } 129 } 130 131 /** 132 * Uses the colour table it it exists to translate default values to 133 * values in the colorTable 134 */ indexLookup(short index)135 private short indexLookup(short index) { 136 137 short result = 0; 138 139 if(tableLookup!=null) { 140 for(short i = 0;i < tableLookup.length;i++) { 141 if(tableLookup[i]==index) 142 result = i; 143 } 144 } else { 145 result = index; 146 } 147 148 return result; 149 } 150 /** 151 * This method maps a Pocket Word colour index value to an RGB value as 152 * used by OpenOffice. 153 * 154 * @param colour The index into Pocket Word's colour table. 155 * 156 * @return A Color object representing the RGB value of the Pocket Word 157 * colour. 158 */ convertToRGB(short colour)159 public Color convertToRGB (short colour) { 160 161 short index = indexLookup(colour); 162 163 int r = 0; 164 int g = 0; 165 int b = 0; 166 167 switch (index) { 168 case SILVER: 169 r = g = b = 128; 170 break; 171 172 case GREY: 173 r = g = b = 192; 174 break; 175 176 case WHITE: 177 r = g = b = 255; 178 break; 179 180 case RED: 181 r = 255; 182 break; 183 184 case LIME: 185 g = 255; 186 break; 187 188 case BLUE: 189 b = 255; 190 break; 191 192 case AQUA: 193 g = b = 255; 194 break; 195 196 case FUCHSIA: 197 r = b = 255; 198 break; 199 200 case YELLOW: 201 r = g = 255; 202 break; 203 204 case MAROON: 205 r = 128; 206 break; 207 208 case GREEN: 209 g = 128; 210 break; 211 212 case NAVY: 213 b = 128; 214 break; 215 216 case TEAL: 217 b = g = 128; 218 break; 219 220 case PURPLE: 221 r = b = 128; 222 break; 223 224 case OLIVE: 225 r = g = 128; 226 break; 227 228 case BLACK: 229 default: 230 r = g = b = 0; 231 break; 232 } 233 234 return new Color(r, g, b); 235 } 236 237 238 /** 239 * This method approximates an RGB value (as used by Writer) to one of the 240 * 16 available colours 241 * 242 * Most of the supported colours have their components set to either 0, 128 243 * or 255. The exception is 'Grey' which is 0xC0C0C0. 244 * 245 * @param colour Color object representing the RGB value of the colour. 246 * 247 * @return Index into the Pocket Word colour table which represents the 248 * closest match to the specified colour. 249 */ convertFromRGB(Color colour)250 public short convertFromRGB (Color colour) { 251 int matchedRGB = 0; 252 short indexColour = 0; 253 int reducedMap[] = new int[] { 0, 0, 128 }; 254 255 int red = colour.getRed(); 256 int green = colour.getGreen(); 257 int blue = colour.getBlue(); 258 259 // We need to convert the pale colors to their base color rather than 260 // white so we modify the rgb values if the colour is sufficiently 261 // white 262 if(red>0xC0 && green>0xC0 && blue>0xC0) { 263 264 if(red!=0xFF) 265 red = getClosest(red, reducedMap); 266 if(green!=0xFF) 267 green = getClosest(green, reducedMap); 268 if(blue!=0xFF) 269 blue = getClosest(blue, reducedMap); 270 } 271 272 /* 273 * Need to derive an RGB value that has been rounded to match the ones 274 * Pocket Word knows about. 275 */ 276 matchedRGB += getClosest(red) << 16; 277 matchedRGB += getClosest(green) << 8; 278 matchedRGB += getClosest(blue); 279 280 /* 281 * The colour map used by Pocket Word doesn't have any combinations of 282 * values beyond 0 and any other value. A value of 255 in any RGB 283 * code indicates a dominant colour. Other colours are only modifiers 284 * to the principal colour(s). Thus, for this conversion, modifiers 285 * can be dropped. 286 */ 287 if ((matchedRGB & 0xFF0000) == 0xFF0000 || (matchedRGB & 0xFF00) == 0xFF00 288 || (matchedRGB & 0xFF) == 0xFF) { 289 if ((matchedRGB & 0xFF0000) == 0x800000) { 290 matchedRGB ^= 0x800000; 291 } 292 if ((matchedRGB & 0xFF00) == 0x8000) { 293 matchedRGB ^= 0x8000; 294 } 295 if ((matchedRGB & 0xFF) == 0x80) { 296 matchedRGB ^= 0x80; 297 } 298 } 299 300 301 /* 302 * And now for the actual matching ... 303 * 304 * Colours are based on the Windows VGA 16 palette. One difference 305 * though is that Pocket Word seems to switch the RGB codes for Grey 306 * and Silver. In Pocket Word Silver is the darker colour leaving Grey 307 * is closest to White. 308 * 309 * Shades of grey will be converted to either Silver or White, where 310 * Grey may be a more appropriate colour. This is handled specially 311 * only for Silver and White matches. 312 */ 313 switch (matchedRGB) { 314 case 0x000000: 315 indexColour = BLACK; 316 break; 317 318 case 0x808080: 319 if (!isGrey(colour)) { 320 indexColour = SILVER; 321 } 322 else { 323 indexColour = GREY; 324 } 325 break; 326 327 case 0xFFFFFF: 328 if (!isGrey(colour)) { 329 indexColour = WHITE; 330 } 331 else { 332 indexColour = GREY; 333 } 334 break; 335 336 case 0xFF0000: 337 indexColour = RED; 338 break; 339 340 case 0x00FF00: 341 indexColour = LIME; 342 break; 343 344 case 0x0000FF: 345 indexColour = BLUE; 346 break; 347 348 case 0x00FFFF: 349 indexColour = AQUA; 350 break; 351 352 case 0xFF00FF: 353 indexColour = FUCHSIA; 354 break; 355 356 case 0xFFFF00: 357 indexColour = YELLOW; 358 break; 359 360 case 0x800000: 361 indexColour = MAROON; 362 break; 363 364 case 0x008000: 365 indexColour = GREEN; 366 break; 367 368 case 0x000080: 369 indexColour = NAVY; 370 break; 371 372 case 0x008080: 373 indexColour = TEAL; 374 break; 375 376 case 0x800080: 377 indexColour = PURPLE; 378 break; 379 380 case 0x808000: 381 indexColour = OLIVE; 382 break; 383 384 default: // Just in case! 385 indexColour = BLACK; 386 break; 387 } 388 389 return colourLookup(indexColour); 390 } 391 392 393 /* 394 * Default implementation, checks for the closest of value to 0, 128 or 255. 395 */ getClosest(int value)396 private int getClosest(int value) { 397 int points[] = new int[] { 0, 128, 255 }; 398 399 return getClosest(value, points); 400 } 401 402 403 /* 404 * Utility method that returns the closest of the three points to the value 405 * supplied. 406 */ getClosest(int value, int[] points)407 private int getClosest(int value, int[] points) { 408 409 if (value == points[0] || value == points[1] || value == points[2]) { 410 return value; 411 } 412 413 if (value < points[1]) { 414 int x = value - points[0]; 415 return (Math.round((float)x / (points[1] - points[0])) == 1 ? points[1] : points[0]); 416 } 417 else { 418 int x = value - points[1]; 419 return (Math.round((float)x / (points[2] - points[1])) >= 1 ? points[2] : points[1]); 420 } 421 } 422 423 424 /* 425 * Checks to see if the supplied colour can be considered to be grey. 426 */ isGrey(Color c)427 private boolean isGrey(Color c) { 428 int matchedRGB = 0; 429 int points[] = new int[] { 128, 192, 255 }; 430 431 matchedRGB += getClosest(c.getRed(), points) << 16; 432 matchedRGB += getClosest(c.getGreen(), points) << 8; 433 matchedRGB += getClosest(c.getBlue(), points); 434 435 if (matchedRGB == 0xC0C0C0) { 436 return true; 437 } 438 439 return false; 440 } 441 } 442 443