2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.awt
.Color
;
32 import java
.awt
.image
.BufferedImage
;
35 * This class represents a single text cell or bit of image on the screen.
37 public final class Cell
extends CellAttributes
{
39 // ------------------------------------------------------------------------
40 // Constants --------------------------------------------------------------
41 // ------------------------------------------------------------------------
44 * How this cell needs to be displayed if it is part of a larger glyph.
48 * This cell is an entire glyph on its own.
53 * This cell is the left half of a wide glyph.
58 * This cell is the right half of a wide glyph.
64 * The special "this cell is unset" (null) value. This is the Unicode
65 * "not a character" value.
67 private static final char UNSET_VALUE
= (char) 65535;
69 // ------------------------------------------------------------------------
70 // Variables --------------------------------------------------------------
71 // ------------------------------------------------------------------------
74 * The character at this cell.
79 * The display width of this cell.
81 private Width width
= Width
.SINGLE
;
84 * The image at this cell.
86 private BufferedImage image
= null;
89 * The image at this cell, inverted.
91 private BufferedImage invertedImage
= null;
94 * The background color used for the area the image portion might not
97 private Color background
= null;
100 * hashCode() needs to call image.hashCode(), which can get quite
103 private int imageHashCode
= 0;
106 * hashCode() needs to call background.hashCode(), which can get quite
109 private int backgroundHashCode
= 0;
111 // ------------------------------------------------------------------------
112 // Constructors -----------------------------------------------------------
113 // ------------------------------------------------------------------------
116 * Public constructor sets default values of the cell to blank.
126 * Public constructor sets the character. Attributes are the same as
129 * @param ch character to set to
132 public Cell(final char ch
) {
137 // ------------------------------------------------------------------------
138 // Cell -------------------------------------------------------------------
139 // ------------------------------------------------------------------------
142 * Set the image data for this cell.
144 * @param image the image for this cell
146 public void setImage(final BufferedImage image
) {
148 imageHashCode
= image
.hashCode();
149 width
= Width
.SINGLE
;
153 * Get the image data for this cell.
155 * @return the image for this cell
157 public BufferedImage
getImage() {
158 if (invertedImage
!= null) {
159 return invertedImage
;
165 * Get the bitmap image background color for this cell.
167 * @return the bitmap image background color
169 public Color
getBackground() {
174 * If true, this cell has image data.
176 * @return true if this cell is an image rather than a character with
179 public boolean isImage() {
187 * Restore the image in this cell to its normal version, if it has one.
189 public void restoreImage() {
190 invertedImage
= null;
194 * If true, this cell has image data, and that data is inverted.
196 * @return true if this cell is an image rather than a character with
197 * attributes, and the data is inverted
199 public boolean isInvertedImage() {
200 if ((image
!= null) && (invertedImage
!= null)) {
207 * Invert the image in this cell, if it has one.
209 public void invertImage() {
213 if (invertedImage
== null) {
214 invertedImage
= new BufferedImage(image
.getWidth(),
215 image
.getHeight(), BufferedImage
.TYPE_INT_ARGB
);
217 int [] rgbArray
= image
.getRGB(0, 0,
218 image
.getWidth(), image
.getHeight(), null, 0, image
.getWidth());
220 for (int i
= 0; i
< rgbArray
.length
; i
++) {
221 // Set the colors to fully inverted.
222 if (rgbArray
[i
] != 0x00FFFFFF) {
223 rgbArray
[i
] ^
= 0x00FFFFFF;
225 // Also set alpha to non-transparent.
226 rgbArray
[i
] |= 0xFF000000;
228 invertedImage
.setRGB(0, 0, image
.getWidth(), image
.getHeight(),
229 rgbArray
, 0, image
.getWidth());
234 * Getter for cell character.
236 * @return cell character
238 public char getChar() {
243 * Setter for cell character.
245 * @param ch new cell character
247 public void setChar(final char ch
) {
252 * Getter for cell width.
254 * @return Width.SINGLE, Width.LEFT, or Width.RIGHT
256 public Width
getWidth() {
261 * Setter for cell width.
263 * @param ch new cell width, one of Width.SINGLE, Width.LEFT, or
266 public void setWidth(final Width width
) {
271 * Reset this cell to a blank.
274 public void reset() {
277 width
= Width
.SINGLE
;
280 invertedImage
= null;
281 background
= Color
.BLACK
;
282 backgroundHashCode
= 0;
286 * UNset this cell. It will not be equal to any other cell until it has
287 * been assigned attributes and a character.
289 public void unset() {
292 width
= Width
.SINGLE
;
295 invertedImage
= null;
296 background
= Color
.BLACK
;
297 backgroundHashCode
= 0;
301 * Check to see if this cell has default attributes: white foreground,
302 * black background, no bold/blink/reverse/underline/protect, and a
303 * character value of ' ' (space).
305 * @return true if this cell has default attributes.
307 public boolean isBlank() {
308 if ((ch
== UNSET_VALUE
) || (image
!= null)) {
311 if ((getForeColor().equals(Color
.WHITE
))
312 && (getBackColor().equals(Color
.BLACK
))
320 && (width
== Width
.SINGLE
)
330 * Comparison check. All fields must match to return true.
332 * @param rhs another Cell instance
333 * @return true if all fields are equal
336 public boolean equals(final Object rhs
) {
337 if (!(rhs
instanceof Cell
)) {
341 Cell that
= (Cell
) rhs
;
343 // Unsetted cells can never be equal.
344 if ((ch
== UNSET_VALUE
) || (that
.ch
== UNSET_VALUE
)) {
348 // If this or rhs has an image and the other doesn't, these are not
350 if ((image
!= null) && (that
.image
== null)) {
353 if ((image
== null) && (that
.image
!= null)) {
356 // If this and rhs have images, both must match.
357 if ((image
!= null) && (that
.image
!= null)) {
358 if ((invertedImage
== null) && (that
.invertedImage
!= null)) {
361 if ((invertedImage
!= null) && (that
.invertedImage
== null)) {
364 // Either both objects have their image inverted, or neither do.
365 // Now if the images are identical the cells are the same
367 if (image
.equals(that
.image
)
368 && (background
.equals(that
.background
))
376 // Normal case: character and attributes must match.
377 if ((ch
== that
.ch
) && (width
== that
.width
)) {
378 return super.equals(rhs
);
384 * Hashcode uses all fields in equals().
389 public int hashCode() {
393 hash
= (B
* hash
) + super.hashCode();
394 hash
= (B
* hash
) + (int)ch
;
395 hash
= (B
* hash
) + width
.hashCode();
398 hash = (B * hash) + image.hashCode();
399 hash = (B * hash) + background.hashCode();
401 hash
= (B
* hash
) + imageHashCode
;
402 hash
= (B
* hash
) + backgroundHashCode
;
404 if (invertedImage
!= null) {
405 hash
= (B
* hash
) + invertedImage
.hashCode();
411 * Set my field values to that's field.
413 * @param rhs an instance of either Cell or CellAttributes
416 public void setTo(final Object rhs
) {
417 // Let this throw a ClassCastException
418 CellAttributes thatAttr
= (CellAttributes
) rhs
;
420 this.imageHashCode
= 0;
421 this.backgroundHashCode
= 0;
422 this.width
= Width
.SINGLE
;
423 super.setTo(thatAttr
);
425 if (rhs
instanceof Cell
) {
426 Cell that
= (Cell
) rhs
;
428 this.width
= that
.width
;
429 this.image
= that
.image
;
430 this.invertedImage
= that
.invertedImage
;
431 this.background
= that
.background
;
432 this.imageHashCode
= that
.imageHashCode
;
433 this.backgroundHashCode
= that
.backgroundHashCode
;
438 * Set my field attr values to that's field.
440 * @param that a CellAttributes instance
442 public void setAttr(final CellAttributes that
) {
448 * Make human-readable description of this Cell.
450 * @return displayable String
453 public String
toString() {
454 return String
.format("fore: %s back: %s bold: %s blink: %s ch %c",
455 getForeColor(), getBackColor(), isBold(), isBlink(), ch
);