-
Notifications
You must be signed in to change notification settings - Fork 0
/
LcdImageLine.java
283 lines (257 loc) · 8.99 KB
/
LcdImageLine.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
* @author Clément Petit (282626)
* @author Yanis Berkani (271348)
*/
package ch.epfl.gameboj.component.lcd;
import java.util.Objects;
import ch.epfl.gameboj.bits.BitVector;
import ch.epfl.gameboj.bits.Bits;
import ch.epfl.gameboj.Preconditions;
import static ch.epfl.gameboj.Preconditions.checkArgument;
/**
* represents a Game Boy image line.
*/
public final class LcdImageLine {
private static final int STANDARD_PALETTE = 0b11100100;
private final BitVector msb;
private final BitVector lsb;
private final BitVector opacity;
/**
* initializes the bit vectors msb, lsb and opacity.
*
* @param msb
* the bit vector containing the most significant bits of the pixels
* @param lsb
* the bit vector containing the least significant bits of the pixels
* @param opacity
* the bit vector containing the opacity
* @throws IllegalArgumentException
* if the bit vectors are not all the same size
*/
public LcdImageLine(BitVector msb, BitVector lsb, BitVector opacity) {
checkArgument(msb.size() == lsb.size() && lsb.size() == opacity.size());
this.msb = msb;
this.lsb = lsb;
this.opacity = opacity;
}
/**
* returns the length of the line in pixels.
*
* @return the length of the line in pixels
*/
public int size() {
return msb.size();
}
/**
* returns the bit vector containing the most significant bits of the pixels.
*
* @return the bit vector msb
*/
public BitVector msb() {
return msb;
}
/**
* returns the bit vector containing the least significant bits of the pixels.
*
* @return the bit vector lsb
*/
public BitVector lsb() {
return lsb;
}
/**
* returns the bit vector containing the opacity.
*
* @return the bit vector opacity
*/
public BitVector opacity() {
return opacity;
}
/**
* shifts the line by the given number of pixels, preserving its length.
*
* @param distance
* the distance to shift expressed in pixels
* @return the shifted line
*/
public LcdImageLine shift(int distance) {
return new LcdImageLine(msb.shift(distance), lsb.shift(distance),
opacity.shift(distance));
}
/**
* extracts a line of given size from the infinite extension by wrapping,
* from a given pixel.
*
* @param start
* the start pixel
* @param size
* the size
* @return a line of given size from the infinite extension by wrapping, from
* a given pixel
*/
public LcdImageLine extractWrapped(int size, int start) {
return new LcdImageLine(msb.extractWrapped(size, start),
lsb.extractWrapped(size, start),
opacity.extractWrapped(size, start));
}
/**
* transforms the colors of the line according to the palette, given in the
* form of an encoded byte.
*
* @param palette
* the palette (must be an 8 bits value)
* @throws IllegalArgumentException
* if the palette is invalid
* @return the line with transformed colors
*/
public LcdImageLine mapColors(int palette) {
Preconditions.checkBits8(palette);
if (palette == STANDARD_PALETTE) {
return this;
}
BitVector newLsb = new BitVector(size(), false);
BitVector newMsb = new BitVector(size(), false);
for (int i = 0; i < 4; i++) {
BitVector l = Bits.test(i, 0) ? lsb : lsb.not();
BitVector m = Bits.test(i, 1) ? msb : msb.not();
BitVector mask = m.and(l);
newMsb = newMsb.or(mask
.and(new BitVector(size(), Bits.test(palette, 2 * i + 1))));
newLsb = newLsb.or(
mask.and(new BitVector(size(), Bits.test(palette, 2 * i))));
}
return new LcdImageLine(newMsb, newLsb, opacity);
}
/**
* composes this line with another of the same length, placed above it,
* using the given opacity vector to perform the composition, the one of the
* upper line being ignored.
*
* @param that
* the other line (must be of the same size as this line)
* @param opacity
* the opacity vector
* @throws IllegalArgumentException
* if the other line is invalid
* @return the composed line
*/
public LcdImageLine below(LcdImageLine that, BitVector opacity) {
checkArgument(
that.size() == this.size() && opacity.size() == this.size());
BitVector newMsb = (that.msb.and(opacity))
.or(this.msb.and(opacity.not()));
BitVector newLsb = (that.lsb.and(opacity))
.or(this.lsb.and(opacity.not()));
BitVector newOpacity = this.opacity.or(opacity);
return new LcdImageLine(newMsb, newLsb, newOpacity);
}
/**
* composes this line with another of the same length, placed above it,
* using the opacity of the upper line to make the composition.
*
* @param that
* the other line (must be of the same size as this line)
* @throws IllegalArgumentException
* if the other line is invalid
* @return the composed line
*/
public LcdImageLine below(LcdImageLine that) {
return below(that, that.opacity);
}
/**
* joins this line with another of the same length, from a pixel of given
* index.
*
* @param that
* the other line (must be of the same size as this line)
* @param index
* the index (must be positive and strictly inferior to this
* line's size)
* @throws IndexOutOfBoundsException
* if the index is invalid
* @throws IllegalArgumentException
* if the other line is invalid
* @return the joined line
*/
public LcdImageLine join(LcdImageLine that, int index) {
checkArgument(index >= 0 && index <= this.size());
checkArgument(that.size() == this.size());
BitVector maskLeft = new BitVector(size(), true).shift(index);
BitVector maskRight = maskLeft.not();
BitVector newMsb = (this.msb.and(maskRight)).or(that.msb.and(maskLeft));
BitVector newLsb = (this.lsb.and(maskRight)).or(that.lsb.and(maskLeft));
BitVector newOpacity = (this.opacity.and(maskRight))
.or(that.opacity.and(maskLeft));
return new LcdImageLine(newMsb, newLsb, newOpacity);
}
/**
* checks if the given object is an LcdImageLine and if its three bit
* vectors are equals to those of this line.
*
* @param that
* the object
* @return true if that is an LcdImageLine and if its three bit vectors are
* equals to those of this line and false otherwise
*/
public boolean equals(Object that) {
return that instanceof LcdImageLine
&& msb.equals(((LcdImageLine) that).msb)
&& lsb.equals(((LcdImageLine) that).lsb)
&& opacity.equals(((LcdImageLine) that).opacity);
}
/**
* returns the hashcode of the line.
*
* @return the hashcode of the line
*/
public int hashcode() {
return Objects.hash(msb, lsb, opacity);
}
/**
* represents an LcdImageLine builder.
*/
public final static class Builder {
private final BitVector.Builder msb;
private final BitVector.Builder lsb;
/**
* initializes the builders msb and lsb with a given size.
*
* @param size
* the size (must be a strictly positive multiple of 32)
*/
public Builder(int size) {
checkArgument(size > 0);
msb = new BitVector.Builder(size);
lsb = new BitVector.Builder(size);
}
/**
* sets the value of the high and low bytes of the line at a given
* index.
*
* @param index
* the index
* @param msbByte
* the new high byte
* @param lsbByte
* the new low byte
* @return the builder
*/
public Builder setBytes(int index, int msbByte, int lsbByte) {
msb.setByte(index, msbByte);
lsb.setByte(index, lsbByte);
return this;
}
/**
* builds the line with the bytes defined so far, in which all pixels of
* color 0 are transparent, and the others opaque.
*
* @return the line with the bytes defined so far in which all pixels of
* color 0 are transparent and the others opaque
*/
public LcdImageLine build() {
BitVector m = msb.build();
BitVector l = lsb.build();
BitVector opacity = m.or(l);
return new LcdImageLine(m, l, opacity);
}
}
}