001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hssf.usermodel;
019:
020: import org.apache.poi.hssf.record.PaletteRecord;
021: import org.apache.poi.hssf.util.HSSFColor;
022:
023: /**
024: * Represents a workbook color palette.
025: * Internally, the XLS format refers to colors using an offset into the palette
026: * record. Thus, the first color in the palette has the index 0x8, the second
027: * has the index 0x9, etc. through 0x40
028: *
029: * @author Brian Sanders (bsanders at risklabs dot com)
030: */
031: public class HSSFPalette {
032: private PaletteRecord palette;
033:
034: protected HSSFPalette(PaletteRecord palette) {
035: this .palette = palette;
036: }
037:
038: /**
039: * Retrieves the color at a given index
040: *
041: * @param index the palette index, between 0x8 to 0x40 inclusive
042: * @return the color, or null if the index is not populated
043: */
044: public HSSFColor getColor(short index) {
045: //Handle the special AUTOMATIC case
046: if (index == HSSFColor.AUTOMATIC.index)
047: return HSSFColor.AUTOMATIC.getInstance();
048: else {
049: byte[] b = palette.getColor(index);
050: if (b != null) {
051: return new CustomColor(index, b);
052: }
053: }
054: return null;
055: }
056:
057: /**
058: * Finds the first occurance of a given color
059: *
060: * @param red the RGB red component, between 0 and 255 inclusive
061: * @param green the RGB green component, between 0 and 255 inclusive
062: * @param blue the RGB blue component, between 0 and 255 inclusive
063: * @return the color, or null if the color does not exist in this palette
064: */
065: public HSSFColor findColor(byte red, byte green, byte blue) {
066: byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
067: for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null; b = palette
068: .getColor(++i)) {
069: if (b[0] == red && b[1] == green && b[2] == blue) {
070: return new CustomColor(i, b);
071: }
072: }
073: return null;
074: }
075:
076: /**
077: * Finds the closest matching color in the custom palette. The
078: * method for finding the distance between the colors is fairly
079: * primative.
080: *
081: * @param red The red component of the color to match.
082: * @param green The green component of the color to match.
083: * @param blue The blue component of the color to match.
084: * @return The closest color or null if there are no custom
085: * colors currently defined.
086: */
087: public HSSFColor findSimilarColor(byte red, byte green, byte blue) {
088: HSSFColor result = null;
089: int minColorDistance = Integer.MAX_VALUE;
090: byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
091: for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null; b = palette
092: .getColor(++i)) {
093: int colorDistance = red - b[0] + green - b[1] + blue - b[2];
094: if (colorDistance < minColorDistance) {
095: result = getColor(i);
096: }
097: }
098: return result;
099: }
100:
101: /**
102: * Sets the color at the given offset
103: *
104: * @param index the palette index, between 0x8 to 0x40 inclusive
105: * @param red the RGB red component, between 0 and 255 inclusive
106: * @param green the RGB green component, between 0 and 255 inclusive
107: * @param blue the RGB blue component, between 0 and 255 inclusive
108: */
109: public void setColorAtIndex(short index, byte red, byte green,
110: byte blue) {
111: palette.setColor(index, red, green, blue);
112: }
113:
114: /**
115: * Adds a new color into an empty color slot.
116: * @param red The red component
117: * @param green The green component
118: * @param blue The blue component
119: *
120: * @return The new custom color.
121: *
122: * @throws RuntimeException if there are more more free color indexes.
123: */
124: public HSSFColor addColor(byte red, byte green, byte blue) {
125: byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
126: short i;
127: for (i = (short) PaletteRecord.FIRST_COLOR_INDEX; i < PaletteRecord.STANDARD_PALETTE_SIZE
128: + PaletteRecord.FIRST_COLOR_INDEX; b = palette
129: .getColor(++i)) {
130: if (b == null) {
131: setColorAtIndex(i, red, green, blue);
132: return getColor(i);
133: }
134: }
135: throw new RuntimeException("Could not find free color index");
136: }
137:
138: private static class CustomColor extends HSSFColor {
139: private short byteOffset;
140: private byte red;
141: private byte green;
142: private byte blue;
143:
144: private CustomColor(short byteOffset, byte[] colors) {
145: this (byteOffset, colors[0], colors[1], colors[2]);
146: }
147:
148: private CustomColor(short byteOffset, byte red, byte green,
149: byte blue) {
150: this .byteOffset = byteOffset;
151: this .red = red;
152: this .green = green;
153: this .blue = blue;
154: }
155:
156: public short getIndex() {
157: return byteOffset;
158: }
159:
160: public short[] getTriplet() {
161: return new short[] { (short) (red & 0xff),
162: (short) (green & 0xff), (short) (blue & 0xff) };
163: }
164:
165: public String getHexString() {
166: StringBuffer sb = new StringBuffer();
167: sb.append(getGnumericPart(red));
168: sb.append(':');
169: sb.append(getGnumericPart(green));
170: sb.append(':');
171: sb.append(getGnumericPart(blue));
172: return sb.toString();
173: }
174:
175: private String getGnumericPart(byte color) {
176: String s;
177: if (color == 0) {
178: s = "0";
179: } else {
180: int c = color & 0xff; //as unsigned
181: c = (c << 8) | c; //pad to 16-bit
182: s = Integer.toHexString(c).toUpperCase();
183: while (s.length() < 4) {
184: s = "0" + s;
185: }
186: }
187: return s;
188: }
189: }
190: }
|