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
= Color
.BLACK
;
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 int ch
) {
137 * Public constructor sets the attributes.
139 * @param attr attributes to use
141 public Cell(final CellAttributes attr
) {
146 * Public constructor sets the character and attributes.
148 * @param ch character to set to
149 * @param attr attributes to use
151 public Cell(final int ch
, final CellAttributes attr
) {
157 * Public constructor creates a duplicate.
159 * @param cell the instance to copy
161 public Cell(final Cell cell
) {
165 // ------------------------------------------------------------------------
166 // Cell -------------------------------------------------------------------
167 // ------------------------------------------------------------------------
170 * Set the image data for this cell.
172 * @param image the image for this cell
174 public void setImage(final BufferedImage image
) {
176 imageHashCode
= image
.hashCode();
177 width
= Width
.SINGLE
;
181 * Get the image data for this cell.
183 * @return the image for this cell
185 public BufferedImage
getImage() {
186 if (invertedImage
!= null) {
187 return invertedImage
;
193 * Get the bitmap image background color for this cell.
195 * @return the bitmap image background color
197 public Color
getBackground() {
202 * If true, this cell has image data.
204 * @return true if this cell is an image rather than a character with
207 public boolean isImage() {
215 * Restore the image in this cell to its normal version, if it has one.
217 public void restoreImage() {
218 invertedImage
= null;
222 * If true, this cell has image data, and that data is inverted.
224 * @return true if this cell is an image rather than a character with
225 * attributes, and the data is inverted
227 public boolean isInvertedImage() {
228 if ((image
!= null) && (invertedImage
!= null)) {
235 * Invert the image in this cell, if it has one.
237 public void invertImage() {
241 if (invertedImage
== null) {
242 invertedImage
= new BufferedImage(image
.getWidth(),
243 image
.getHeight(), BufferedImage
.TYPE_INT_ARGB
);
245 int [] rgbArray
= image
.getRGB(0, 0,
246 image
.getWidth(), image
.getHeight(), null, 0, image
.getWidth());
248 for (int i
= 0; i
< rgbArray
.length
; i
++) {
249 // Set the colors to fully inverted.
250 if (rgbArray
[i
] != 0x00FFFFFF) {
251 rgbArray
[i
] ^
= 0x00FFFFFF;
253 // Also set alpha to non-transparent.
254 rgbArray
[i
] |= 0xFF000000;
256 invertedImage
.setRGB(0, 0, image
.getWidth(), image
.getHeight(),
257 rgbArray
, 0, image
.getWidth());
262 * Getter for cell character.
264 * @return cell character
266 public int getChar() {
271 * Setter for cell character.
273 * @param ch new cell character
275 public void setChar(final int ch
) {
280 * Getter for cell width.
282 * @return Width.SINGLE, Width.LEFT, or Width.RIGHT
284 public Width
getWidth() {
289 * Setter for cell width.
291 * @param width new cell width, one of Width.SINGLE, Width.LEFT, or
294 public void setWidth(final Width width
) {
299 * Reset this cell to a blank.
302 public void reset() {
305 width
= Width
.SINGLE
;
308 invertedImage
= null;
309 background
= Color
.BLACK
;
310 backgroundHashCode
= 0;
314 * UNset this cell. It will not be equal to any other cell until it has
315 * been assigned attributes and a character.
317 public void unset() {
320 width
= Width
.SINGLE
;
323 invertedImage
= null;
324 background
= Color
.BLACK
;
325 backgroundHashCode
= 0;
329 * Check to see if this cell has default attributes: white foreground,
330 * black background, no bold/blink/reverse/underline/protect, and a
331 * character value of ' ' (space).
333 * @return true if this cell has default attributes.
335 public boolean isBlank() {
336 if ((ch
== UNSET_VALUE
) || (image
!= null)) {
339 if ((getForeColor().equals(Color
.WHITE
))
340 && (getBackColor().equals(Color
.BLACK
))
348 && (width
== Width
.SINGLE
)
358 * Comparison check. All fields must match to return true.
360 * @param rhs another Cell instance
361 * @return true if all fields are equal
364 public boolean equals(final Object rhs
) {
365 if (!(rhs
instanceof Cell
)) {
369 Cell that
= (Cell
) rhs
;
371 // Unsetted cells can never be equal.
372 if ((ch
== UNSET_VALUE
) || (that
.ch
== UNSET_VALUE
)) {
376 // If this or rhs has an image and the other doesn't, these are not
378 if ((image
!= null) && (that
.image
== null)) {
381 if ((image
== null) && (that
.image
!= null)) {
384 // If this and rhs have images, both must match.
385 if ((image
!= null) && (that
.image
!= null)) {
386 if ((invertedImage
== null) && (that
.invertedImage
!= null)) {
389 if ((invertedImage
!= null) && (that
.invertedImage
== null)) {
392 // Either both objects have their image inverted, or neither do.
393 // Now if the images are identical the cells are the same
395 if (image
.equals(that
.image
)
396 && (background
.equals(that
.background
))
404 // Normal case: character and attributes must match.
405 if ((ch
== that
.ch
) && (width
== that
.width
)) {
406 return super.equals(rhs
);
412 * Hashcode uses all fields in equals().
417 public int hashCode() {
421 hash
= (B
* hash
) + super.hashCode();
422 hash
= (B
* hash
) + ch
;
423 hash
= (B
* hash
) + width
.hashCode();
426 hash = (B * hash) + image.hashCode();
427 hash = (B * hash) + background.hashCode();
429 hash
= (B
* hash
) + imageHashCode
;
430 hash
= (B
* hash
) + backgroundHashCode
;
432 if (invertedImage
!= null) {
433 hash
= (B
* hash
) + invertedImage
.hashCode();
439 * Set my field values to that's field.
441 * @param rhs an instance of either Cell or CellAttributes
444 public void setTo(final Object rhs
) {
445 // Let this throw a ClassCastException
446 CellAttributes thatAttr
= (CellAttributes
) rhs
;
448 this.imageHashCode
= 0;
449 this.backgroundHashCode
= 0;
450 this.width
= Width
.SINGLE
;
451 super.setTo(thatAttr
);
453 if (rhs
instanceof Cell
) {
454 Cell that
= (Cell
) rhs
;
456 this.width
= that
.width
;
457 this.image
= that
.image
;
458 this.invertedImage
= that
.invertedImage
;
459 this.background
= that
.background
;
460 this.imageHashCode
= that
.imageHashCode
;
461 this.backgroundHashCode
= that
.backgroundHashCode
;
466 * Set my field attr values to that's field.
468 * @param that a CellAttributes instance
470 public void setAttr(final CellAttributes that
) {
476 * Make human-readable description of this Cell.
478 * @return displayable String
481 public String
toString() {
482 return String
.format("fore: %s back: %s bold: %s blink: %s ch %c",
483 getForeColor(), getBackColor(), isBold(), isBlink(), ch
);