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
) {
138 * Public constructor creates a duplicate.
140 * @param cell the instance to copy
142 public Cell(final Cell cell
) {
146 // ------------------------------------------------------------------------
147 // Cell -------------------------------------------------------------------
148 // ------------------------------------------------------------------------
151 * Set the image data for this cell.
153 * @param image the image for this cell
155 public void setImage(final BufferedImage image
) {
157 imageHashCode
= image
.hashCode();
158 width
= Width
.SINGLE
;
162 * Get the image data for this cell.
164 * @return the image for this cell
166 public BufferedImage
getImage() {
167 if (invertedImage
!= null) {
168 return invertedImage
;
174 * Get the bitmap image background color for this cell.
176 * @return the bitmap image background color
178 public Color
getBackground() {
183 * If true, this cell has image data.
185 * @return true if this cell is an image rather than a character with
188 public boolean isImage() {
196 * Restore the image in this cell to its normal version, if it has one.
198 public void restoreImage() {
199 invertedImage
= null;
203 * If true, this cell has image data, and that data is inverted.
205 * @return true if this cell is an image rather than a character with
206 * attributes, and the data is inverted
208 public boolean isInvertedImage() {
209 if ((image
!= null) && (invertedImage
!= null)) {
216 * Invert the image in this cell, if it has one.
218 public void invertImage() {
222 if (invertedImage
== null) {
223 invertedImage
= new BufferedImage(image
.getWidth(),
224 image
.getHeight(), BufferedImage
.TYPE_INT_ARGB
);
226 int [] rgbArray
= image
.getRGB(0, 0,
227 image
.getWidth(), image
.getHeight(), null, 0, image
.getWidth());
229 for (int i
= 0; i
< rgbArray
.length
; i
++) {
230 // Set the colors to fully inverted.
231 if (rgbArray
[i
] != 0x00FFFFFF) {
232 rgbArray
[i
] ^
= 0x00FFFFFF;
234 // Also set alpha to non-transparent.
235 rgbArray
[i
] |= 0xFF000000;
237 invertedImage
.setRGB(0, 0, image
.getWidth(), image
.getHeight(),
238 rgbArray
, 0, image
.getWidth());
243 * Getter for cell character.
245 * @return cell character
247 public char getChar() {
252 * Setter for cell character.
254 * @param ch new cell character
256 public void setChar(final char ch
) {
261 * Getter for cell width.
263 * @return Width.SINGLE, Width.LEFT, or Width.RIGHT
265 public Width
getWidth() {
270 * Setter for cell width.
272 * @param width new cell width, one of Width.SINGLE, Width.LEFT, or
275 public void setWidth(final Width width
) {
280 * Reset this cell to a blank.
283 public void reset() {
286 width
= Width
.SINGLE
;
289 invertedImage
= null;
290 background
= Color
.BLACK
;
291 backgroundHashCode
= 0;
295 * UNset this cell. It will not be equal to any other cell until it has
296 * been assigned attributes and a character.
298 public void unset() {
301 width
= Width
.SINGLE
;
304 invertedImage
= null;
305 background
= Color
.BLACK
;
306 backgroundHashCode
= 0;
310 * Check to see if this cell has default attributes: white foreground,
311 * black background, no bold/blink/reverse/underline/protect, and a
312 * character value of ' ' (space).
314 * @return true if this cell has default attributes.
316 public boolean isBlank() {
317 if ((ch
== UNSET_VALUE
) || (image
!= null)) {
320 if ((getForeColor().equals(Color
.WHITE
))
321 && (getBackColor().equals(Color
.BLACK
))
329 && (width
== Width
.SINGLE
)
339 * Comparison check. All fields must match to return true.
341 * @param rhs another Cell instance
342 * @return true if all fields are equal
345 public boolean equals(final Object rhs
) {
346 if (!(rhs
instanceof Cell
)) {
350 Cell that
= (Cell
) rhs
;
352 // Unsetted cells can never be equal.
353 if ((ch
== UNSET_VALUE
) || (that
.ch
== UNSET_VALUE
)) {
357 // If this or rhs has an image and the other doesn't, these are not
359 if ((image
!= null) && (that
.image
== null)) {
362 if ((image
== null) && (that
.image
!= null)) {
365 // If this and rhs have images, both must match.
366 if ((image
!= null) && (that
.image
!= null)) {
367 if ((invertedImage
== null) && (that
.invertedImage
!= null)) {
370 if ((invertedImage
!= null) && (that
.invertedImage
== null)) {
373 // Either both objects have their image inverted, or neither do.
374 // Now if the images are identical the cells are the same
376 if (image
.equals(that
.image
)
377 && (background
.equals(that
.background
))
385 // Normal case: character and attributes must match.
386 if ((ch
== that
.ch
) && (width
== that
.width
)) {
387 return super.equals(rhs
);
393 * Hashcode uses all fields in equals().
398 public int hashCode() {
402 hash
= (B
* hash
) + super.hashCode();
403 hash
= (B
* hash
) + (int)ch
;
404 hash
= (B
* hash
) + width
.hashCode();
407 hash = (B * hash) + image.hashCode();
408 hash = (B * hash) + background.hashCode();
410 hash
= (B
* hash
) + imageHashCode
;
411 hash
= (B
* hash
) + backgroundHashCode
;
413 if (invertedImage
!= null) {
414 hash
= (B
* hash
) + invertedImage
.hashCode();
420 * Set my field values to that's field.
422 * @param rhs an instance of either Cell or CellAttributes
425 public void setTo(final Object rhs
) {
426 // Let this throw a ClassCastException
427 CellAttributes thatAttr
= (CellAttributes
) rhs
;
429 this.imageHashCode
= 0;
430 this.backgroundHashCode
= 0;
431 this.width
= Width
.SINGLE
;
432 super.setTo(thatAttr
);
434 if (rhs
instanceof Cell
) {
435 Cell that
= (Cell
) rhs
;
437 this.width
= that
.width
;
438 this.image
= that
.image
;
439 this.invertedImage
= that
.invertedImage
;
440 this.background
= that
.background
;
441 this.imageHashCode
= that
.imageHashCode
;
442 this.backgroundHashCode
= that
.backgroundHashCode
;
447 * Set my field attr values to that's field.
449 * @param that a CellAttributes instance
451 public void setAttr(final CellAttributes that
) {
457 * Make human-readable description of this Cell.
459 * @return displayable String
462 public String
toString() {
463 return String
.format("fore: %s back: %s bold: %s blink: %s ch %c",
464 getForeColor(), getBackColor(), isBold(), isBlink(), ch
);