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.
76 private char ch
= ' ';
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 char ch
) {
137 * Public constructor sets the character and attributes.
139 * @param ch character to set to
140 * @param attr attributes to use
142 public Cell(final char ch
, final CellAttributes attr
) {
148 * Public constructor creates a duplicate.
150 * @param cell the instance to copy
152 public Cell(final Cell cell
) {
156 // ------------------------------------------------------------------------
157 // Cell -------------------------------------------------------------------
158 // ------------------------------------------------------------------------
161 * Set the image data for this cell.
163 * @param image the image for this cell
165 public void setImage(final BufferedImage image
) {
167 imageHashCode
= image
.hashCode();
168 width
= Width
.SINGLE
;
172 * Get the image data for this cell.
174 * @return the image for this cell
176 public BufferedImage
getImage() {
177 if (invertedImage
!= null) {
178 return invertedImage
;
184 * Get the bitmap image background color for this cell.
186 * @return the bitmap image background color
188 public Color
getBackground() {
193 * If true, this cell has image data.
195 * @return true if this cell is an image rather than a character with
198 public boolean isImage() {
206 * Restore the image in this cell to its normal version, if it has one.
208 public void restoreImage() {
209 invertedImage
= null;
213 * If true, this cell has image data, and that data is inverted.
215 * @return true if this cell is an image rather than a character with
216 * attributes, and the data is inverted
218 public boolean isInvertedImage() {
219 if ((image
!= null) && (invertedImage
!= null)) {
226 * Invert the image in this cell, if it has one.
228 public void invertImage() {
232 if (invertedImage
== null) {
233 invertedImage
= new BufferedImage(image
.getWidth(),
234 image
.getHeight(), BufferedImage
.TYPE_INT_ARGB
);
236 int [] rgbArray
= image
.getRGB(0, 0,
237 image
.getWidth(), image
.getHeight(), null, 0, image
.getWidth());
239 for (int i
= 0; i
< rgbArray
.length
; i
++) {
240 // Set the colors to fully inverted.
241 if (rgbArray
[i
] != 0x00FFFFFF) {
242 rgbArray
[i
] ^
= 0x00FFFFFF;
244 // Also set alpha to non-transparent.
245 rgbArray
[i
] |= 0xFF000000;
247 invertedImage
.setRGB(0, 0, image
.getWidth(), image
.getHeight(),
248 rgbArray
, 0, image
.getWidth());
253 * Getter for cell character.
255 * @return cell character
257 public char getChar() {
262 * Setter for cell character.
264 * @param ch new cell character
266 public void setChar(final char ch
) {
271 * Getter for cell width.
273 * @return Width.SINGLE, Width.LEFT, or Width.RIGHT
275 public Width
getWidth() {
280 * Setter for cell width.
282 * @param width new cell width, one of Width.SINGLE, Width.LEFT, or
285 public void setWidth(final Width width
) {
290 * Reset this cell to a blank.
293 public void reset() {
296 width
= Width
.SINGLE
;
299 invertedImage
= null;
300 background
= Color
.BLACK
;
301 backgroundHashCode
= 0;
305 * UNset this cell. It will not be equal to any other cell until it has
306 * been assigned attributes and a character.
308 public void unset() {
311 width
= Width
.SINGLE
;
314 invertedImage
= null;
315 background
= Color
.BLACK
;
316 backgroundHashCode
= 0;
320 * Check to see if this cell has default attributes: white foreground,
321 * black background, no bold/blink/reverse/underline/protect, and a
322 * character value of ' ' (space).
324 * @return true if this cell has default attributes.
326 public boolean isBlank() {
327 if ((ch
== UNSET_VALUE
) || (image
!= null)) {
330 if ((getForeColor().equals(Color
.WHITE
))
331 && (getBackColor().equals(Color
.BLACK
))
339 && (width
== Width
.SINGLE
)
349 * Comparison check. All fields must match to return true.
351 * @param rhs another Cell instance
352 * @return true if all fields are equal
355 public boolean equals(final Object rhs
) {
356 if (!(rhs
instanceof Cell
)) {
360 Cell that
= (Cell
) rhs
;
362 // Unsetted cells can never be equal.
363 if ((ch
== UNSET_VALUE
) || (that
.ch
== UNSET_VALUE
)) {
367 // If this or rhs has an image and the other doesn't, these are not
369 if ((image
!= null) && (that
.image
== null)) {
372 if ((image
== null) && (that
.image
!= null)) {
375 // If this and rhs have images, both must match.
376 if ((image
!= null) && (that
.image
!= null)) {
377 if ((invertedImage
== null) && (that
.invertedImage
!= null)) {
380 if ((invertedImage
!= null) && (that
.invertedImage
== null)) {
383 // Either both objects have their image inverted, or neither do.
384 // Now if the images are identical the cells are the same
386 if (image
.equals(that
.image
)
387 && (background
.equals(that
.background
))
395 // Normal case: character and attributes must match.
396 if ((ch
== that
.ch
) && (width
== that
.width
)) {
397 return super.equals(rhs
);
403 * Hashcode uses all fields in equals().
408 public int hashCode() {
412 hash
= (B
* hash
) + super.hashCode();
413 hash
= (B
* hash
) + (int)ch
;
414 hash
= (B
* hash
) + width
.hashCode();
417 hash = (B * hash) + image.hashCode();
418 hash = (B * hash) + background.hashCode();
420 hash
= (B
* hash
) + imageHashCode
;
421 hash
= (B
* hash
) + backgroundHashCode
;
423 if (invertedImage
!= null) {
424 hash
= (B
* hash
) + invertedImage
.hashCode();
430 * Set my field values to that's field.
432 * @param rhs an instance of either Cell or CellAttributes
435 public void setTo(final Object rhs
) {
436 // Let this throw a ClassCastException
437 CellAttributes thatAttr
= (CellAttributes
) rhs
;
439 this.imageHashCode
= 0;
440 this.backgroundHashCode
= 0;
441 this.width
= Width
.SINGLE
;
442 super.setTo(thatAttr
);
444 if (rhs
instanceof Cell
) {
445 Cell that
= (Cell
) rhs
;
447 this.width
= that
.width
;
448 this.image
= that
.image
;
449 this.invertedImage
= that
.invertedImage
;
450 this.background
= that
.background
;
451 this.imageHashCode
= that
.imageHashCode
;
452 this.backgroundHashCode
= that
.backgroundHashCode
;
457 * Set my field attr values to that's field.
459 * @param that a CellAttributes instance
461 public void setAttr(final CellAttributes that
) {
467 * Make human-readable description of this Cell.
469 * @return displayable String
472 public String
toString() {
473 return String
.format("fore: %s back: %s bold: %s blink: %s ch %c",
474 getForeColor(), getBackColor(), isBold(), isBlink(), ch
);