001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.util.bits; 010 011import imagingbook.common.geometry.basic.Pnt2d.PntInt; 012 013/** 014 * This class implements a true 2D bitmap container, i.e., each 0/1 element occupies only a single bit (unlike 015 * {@code boolean} arrays, which require at least 8 bits per element). 016 * 017 * @author WB 018 * @version 2022/09/13 019 * @see BitVector 020 */ 021public class BitMap { 022 023 private final int width; 024 private final int height; 025 private final BitVector bitvec; 026 027 /** 028 * Constructor, creates an empty bitmap (with all elements set to 0). Both dimensions must be at least 1. 029 * 030 * @param width the width of the new bitmap 031 * @param height the height of the new bitmap 032 */ 033 public BitMap(int width, int height) { 034 this(width, height, null); 035 } 036 037 /** 038 * Constructor, creates a bitmap from a one-dimensional byte array. Elements of the specified byte array are assumed 039 * in row-major order, zero values map to 0, anything else to 1. Passing {@code null} for the byte array creates an 040 * empty bitmap (with all elements set to 0). Both dimensions must be at least 1. 041 * 042 * @param width the width of the new bitmap 043 * @param height the height of the new bitmap 044 * @param bytes a byte array ({@code null} is allowed) 045 */ 046 public BitMap(int width, int height, byte[] bytes) { 047 if (width <= 0) { 048 throw new IllegalArgumentException("width of bitmap must be at least 1: " + width); 049 } 050 if (height <= 0) { 051 throw new IllegalArgumentException("height of bitmap must be at least 1: " + height); 052 } 053 if (bytes != null && width * height != bytes.length) { 054 throw new IllegalArgumentException("width/height do not match byte[] length: " + bytes.length); 055 } 056 this.width = width; 057 this.height = height; 058 this.bitvec = (bytes != null) ? 059 BitVector.from(bytes) : 060 BitVector.create(width * height); 061 } 062 063 // --------------------------------------------------------- 064 065 /** 066 * Returns the width of this {@link BitMap}. 067 * @return the width 068 */ 069 public int getWidth() { 070 return this.width; 071 } 072 073 /** 074 * Returns the height of this {@link BitMap}. 075 * @return the height 076 */ 077 public int getHeight() { 078 return this.height; 079 } 080 081 /** 082 * Returns {@code true} is the specified element is set (1), {@code false} otherwise (0). 083 * 084 * @param x the x-coordinate 085 * @param y the y-coordinate 086 * @return as described 087 */ 088 public boolean get(int x, int y) { 089 return bitvec.get(y * width + x); 090 } 091 092 /** 093 * Returns {@code true} is the specified element is set (1), {@code false} otherwise (0). 094 * 095 * @param p the x/y-coordinate (point) 096 * @return as described 097 */ 098 public boolean get(PntInt p) { 099 return bitvec.get(p.y * width + p.x); 100 } 101 102 /** 103 * Sets the specified bit-element to the given boolean value (1 for {@code true}, 0 for {@code false}). 104 * 105 * @param x the x-coordinate 106 * @param y the y-coordinate 107 * @param val a boolean value 108 */ 109 public void set(int x, int y, boolean val) { 110 if (val) { 111 this.set(x, y); 112 } 113 else { 114 this.unset(x, y); 115 } 116 } 117 118 /** 119 * Sets the specified bit-element to the given boolean value (1 for {@code true}, 0 for {@code false}). 120 * 121 * @param p the x/y-coordinate (point) 122 * @param val a boolean value 123 */ 124 public void set(PntInt p, boolean val) { 125 set(p.x, p.y, val); 126 } 127 128 /** 129 * Sets the specified element (to bit-value 1). 130 * @param x the x-coordinate 131 * @param y the y-coordinate 132 */ 133 public void set(int x, int y) { 134 bitvec.set(y * width + x); 135 } 136 137 /** 138 * Sets the specified element (to bit-value 1). 139 * @param p the x/y-coordinate (point) 140 */ 141 public void set(PntInt p) { 142 bitvec.set(p.y * width + p.x); 143 } 144 145 /** 146 * Unsets the specified element (to bit-value 0). 147 * @param x the x-coordinate 148 * @param y the y-coordinate 149 */ 150 public void unset(int x, int y) { 151 bitvec.unset(y * width + x); 152 } 153 154 /** 155 * Unsets the specified element (to bit-value 0). 156 * @param p the x/y-coordinate (point) 157 */ 158 public void unset(PntInt p) { 159 bitvec.unset(p.y * width + p.x); 160 } 161 162 /** 163 * Sets all elements to 1. 164 */ 165 public void setAll() { 166 bitvec.setAll(); 167 } 168 169 /** 170 * Sets all elements to 0. 171 */ 172 public void unsetAll() { 173 bitvec.unsetAll(); 174 } 175 176 /** 177 * Returns the underlying 1D {@link BitVector}. 178 * @return the bit vector 179 */ 180 public BitVector getBitVector() { 181 return this.bitvec; 182 } 183 184 /** 185 * Returns the contents of this bitmap as a one-dimensional {@code byte} array, with elements in row-major order. 186 * Bit-value 0 maps to byte value 0, value 1 maps to 1. 187 * 188 * @return a one-dimensional {@code byte} array 189 */ 190 public byte[] toByteArray() { 191 return this.bitvec.toByteArray(); 192 } 193 194 // static methods -------------------------------------------------- 195 196 /** 197 * Creates a new 2D bitmap of the specified size. 198 * @param width the width of the new bitmap 199 * @param height the height of the new bitmap 200 * @return the new bitmap 201 */ 202 public static BitMap create(int width, int height) { 203 return new BitMap(width, height); 204 } 205 206}