Fix HUD partial transparency
[jpcrr.git] / org / jpc / pluginsaux / HUDRenderer.java
blob0e0f2414998cad65432292105a62b942b47482d2
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
29 package org.jpc.pluginsaux;
31 import java.util.*;
32 import static org.jpc.pluginsaux.VGAFont.vgaFontData;
34 public class HUDRenderer
36 int[] backgroundBuffer;
37 int elementsAllocated;
38 int backgroundWidth;
39 int backgroundHeight;
40 int flags;
41 volatile int lightAmp;
42 volatile int gapLeft;
43 volatile int gapTop;
44 volatile int gapBottom;
45 volatile int gapRight;
46 List<RenderObject> renderObjects;
48 private abstract class RenderObject
50 abstract void render(int[] buffer, int w, int h);
53 public HUDRenderer(int _flags)
55 renderObjects = new LinkedList<RenderObject>();
56 lightAmp = 1;
57 flags = _flags;
60 public synchronized void setBackground(int[] bg, int w, int h)
62 if(elementsAllocated < w * h) {
63 backgroundBuffer = new int[w * h];
64 elementsAllocated = w * h;
66 if(w * h > 0)
67 System.arraycopy(bg, 0, backgroundBuffer, 0, w * h);
68 backgroundWidth = w;
69 backgroundHeight = h;
72 public synchronized int getRenderWidth()
74 return gapLeft + backgroundWidth + gapRight;
77 public synchronized int getRenderHeight()
79 return gapTop + backgroundHeight + gapBottom;
82 public synchronized void REMOTE_left_gap(int _flags, int gap)
84 if(((_flags & flags)) != flags)
85 return;
86 if(gap > 0) {
87 gapLeft = gap;
88 } else
89 gapLeft = 0;
92 public synchronized void REMOTE_top_gap(int _flags, int gap)
94 if(((_flags & flags)) != flags)
95 return;
96 if(gap > 0) {
97 gapTop = gap;
98 } else
99 gapTop = 0;
102 public synchronized void REMOTE_right_gap(int _flags, int gap)
104 if(((_flags & flags)) != flags)
105 return;
106 if(gap > 0) {
107 gapRight = gap;
108 } else
109 gapRight = 0;
112 public synchronized void REMOTE_bottom_gap(int _flags, int gap)
114 if(((_flags & flags)) != flags)
115 return;
116 if(gap > 0) {
117 gapBottom = gap;
118 } else
119 gapBottom = 0;
122 public synchronized void setLightAmplification(int factor)
124 lightAmp = factor;
127 public synchronized int[] getFinishedAndReset()
129 int[] ret = null;
130 int w = getRenderWidth();
131 int h = getRenderHeight();
132 if(w * h > 0)
133 ret = new int[w * h];
135 if(lightAmp == 1) {
136 for(int y = 0; y < backgroundHeight; y++)
137 System.arraycopy(backgroundBuffer, y * backgroundWidth, ret, (y + gapTop) * w + gapLeft,
138 backgroundWidth);
139 } else {
140 for(int y = 0; y < backgroundHeight; y++)
141 for(int x = 0; x < backgroundWidth; x++)
142 ret[(y + gapTop) * w + gapLeft + x] = backgroundBuffer[y * backgroundWidth + x] * lightAmp;
145 for(RenderObject obj : renderObjects)
146 if(ret != null)
147 obj.render(ret, w, h);
148 renderObjects.clear();
150 gapLeft = gapRight = gapTop = gapBottom = 0;
151 return ret;
154 final void renderPixel(int[] buffer, int bw, int bh, int x, int y, boolean state, int fillR, int fillG,
155 int fillB, int fillA, int lineR, int lineG, int lineB, int lineA)
157 if(x < 0 || y < 0 || x >= bw || y >= bh)
158 return;
159 int useR = fillR;
160 int useG = fillG;
161 int useB = fillB;
162 int useA = fillA;
164 if(state) {
165 useR = lineR;
166 useG = lineG;
167 useB = lineB;
168 useA = lineA;
170 useR &= 0xFF;
171 useG &= 0xFF;
172 useB &= 0xFF;
173 useA &= 0xFF;
175 if(useA == 0) {
176 //Nothing to modify.
177 } else if(useA == 255) {
178 buffer[y * bw + x] = (useR << 16) | (useG << 8) | useB;
179 } else {
180 int oldpx = buffer[y * bw + x];
181 float oldR = (oldpx >>> 16) & 0xFF;
182 float oldG = (oldpx >>> 8) & 0xFF;
183 float oldB = oldpx & 0xFF;
184 float fA = (float)useA / 255;
185 useR = (int)(useR * fA + oldR * (1 - fA));
186 useG = (int)(useG * fA + oldG * (1 - fA));
187 useB = (int)(useB * fA + oldB * (1 - fA));
188 buffer[y * bw + x] = (useR << 16) | (useG << 8) | useB;
193 private class WhiteSolidBox extends RenderObject
195 int x;
196 int y;
197 int w;
198 int h;
199 WhiteSolidBox(int _x, int _y, int _w, int _h)
201 x = _x;
202 y = _y;
203 w = _w;
204 h = _h;
207 void render(int[] buffer, int bw, int bh)
209 for(int j = y; j < y + h; j++) {
210 if(j < 0 || j >= bh)
211 continue;
212 for(int i = x; i < x + w; i++) {
213 if(i < 0 || i >= bw)
214 continue;
215 buffer[j * bw + i] = 0xFFFFFF;
221 public synchronized void REMOTE_white_solid_box(int _flags, int x, int y, int w, int h)
223 if(((_flags & flags)) != flags)
224 return;
225 renderObjects.add(new WhiteSolidBox(x, y, w, h));
228 private class Box extends RenderObject
230 int x;
231 int y;
232 int w;
233 int h;
234 int thick;
235 int lineR;
236 int lineG;
237 int lineB;
238 int lineA;
239 int fillR;
240 int fillG;
241 int fillB;
242 int fillA;
243 Box(int _x, int _y, int _w, int _h, int _thick, int lr, int lg, int lb, int la, int fr, int fg, int fb,
244 int fa)
246 x = _x;
247 y = _y;
248 w = _w;
249 h = _h;
250 thick = _thick;
251 lineR = lr;
252 lineG = lg;
253 lineB = lb;
254 lineA = la;
255 fillR = fr;
256 fillG = fg;
257 fillB = fb;
258 fillA = fa;
261 void render(int[] buffer, int bw, int bh)
263 for(int j = y; j < y + h && j < bh; j++) {
264 if(j < 0 || j >= bh)
265 continue;
266 for(int i = x; i < x + w && i < bw; i++) {
267 int dist = i - x;
268 if(j - y < dist)
269 dist = j - y;
270 if(x + w - i - 1 < dist)
271 dist = x + w - i - 1;
272 if(y + h - j - 1 < dist)
273 dist = y + h - j - 1;
274 renderPixel(buffer, bw, bh, i, j, dist < thick, fillR, fillG, fillB, fillA, lineR, lineG,
275 lineB, lineA);
281 public synchronized void REMOTE_box(int _flags, int _x, int _y, int _w, int _h, int _thick, int lr, int lg,
282 int lb, int la, int fr, int fg, int fb, int fa)
284 if((_flags & flags) != flags)
285 return;
286 renderObjects.add(new Box(_x, _y, _w, _h, _thick, lr, lg, lb, la, fr, fg, fb, fa));
289 private class Circle extends RenderObject
291 int x;
292 int y;
293 int r;
294 long r2inner;
295 long r2outer;
296 int lineR;
297 int lineG;
298 int lineB;
299 int lineA;
300 int fillR;
301 int fillG;
302 int fillB;
303 int fillA;
304 Circle(int _x, int _y, int _r, int _thick, int lr, int lg, int lb, int la, int fr, int fg, int fb,
305 int fa)
307 x = _x;
308 y = _y;
309 r = _r;
310 r2outer = (long)_r * _r;
311 if(_r < _thick)
312 r2inner = 0;
313 else
314 r2inner = (long)(_r - _thick) * (_r - _thick);
315 lineR = lr;
316 lineG = lg;
317 lineB = lb;
318 lineA = la;
319 fillR = fr;
320 fillG = fg;
321 fillB = fb;
322 fillA = fa;
325 void render(int[] buffer, int bw, int bh)
327 for(int j = y - r; j < y + r && j < bh; j++) {
328 if(j < 0 || j >= bh)
329 continue;
330 for(int i = x - r; i < x + r && i < bw; i++) {
331 if(i < 0 || i >= bw)
332 continue;
333 long ox = i - x;
334 long oy = j - y;
335 long d = ox * ox + oy * oy;
336 if(d > r2outer)
337 continue;
338 renderPixel(buffer, bw, bh, i, j, d >= r2inner, fillR, fillG, fillB, fillA, lineR, lineG,
339 lineB, lineA);
345 public synchronized void REMOTE_circle(int _flags, int _x, int _y, int _r, int _thick, int lr, int lg, int lb,
346 int la, int fr, int fg, int fb, int fa)
348 if((_flags & flags) != flags)
349 return;
350 renderObjects.add(new Circle(_x, _y, _r, _thick, lr, lg, lb, la, fr, fg, fb, fa));
353 private class Bitmap extends RenderObject
355 private static final int PIXELS_PER_ELEMENT = 31;
356 int x;
357 int y;
358 int w;
359 int h;
360 int stride;
361 int[] bitmapData;
362 int lineR;
363 int lineG;
364 int lineB;
365 int lineA;
366 int fillR;
367 int fillG;
368 int fillB;
369 int fillA;
371 Bitmap(int _x, int _y, String bmap, int lr, int lg, int lb, int la, int fr, int fg, int fb,
372 int fa, boolean dummy)
374 int i = 0;
375 x = _x;
376 y = _y;
377 lineR = lr;
378 lineG = lg;
379 lineB = lb;
380 lineA = la;
381 fillR = fr;
382 fillG = fg;
383 fillB = fb;
384 fillA = fa;
385 w = 0;
386 h = 0;
387 try {
388 w = bmap.charAt(i++);
389 if(w > 127)
390 w = (w & 0x7F) | (bmap.charAt(i++) << 7);
391 stride = (w + PIXELS_PER_ELEMENT - 1) / PIXELS_PER_ELEMENT;
392 int rawbytes = 4 * (w / PIXELS_PER_ELEMENT);
393 rawbytes += ((w % PIXELS_PER_ELEMENT) + 7) / 8;
394 h = (bmap.length() - i) / rawbytes;
395 bitmapData = new int[h * stride + 2];
396 for(int j = 0; j < h; j++)
397 for(int k = 0; k < rawbytes; k++)
398 bitmapData[j * stride + k / 4] |= ((int)bmap.charAt(i++) << (8 * (k % 4)));
399 } catch(Exception e) {
400 System.err.println("Bitmap: Failed to parse bitmap: " + e.getMessage());
401 e.printStackTrace();
405 Bitmap(int _x, int _y, String bmap, int lr, int lg, int lb, int la, int fr, int fg, int fb,
406 int fa)
408 x = _x;
409 y = _y;
410 lineR = lr;
411 lineG = lg;
412 lineB = lb;
413 lineA = la;
414 fillR = fr;
415 fillG = fg;
416 fillB = fb;
417 fillA = fa;
418 int cx = 0;
419 int cy = 0;
420 boolean newLine = true;
421 for(int i = 0; i < bmap.length(); i++) {
422 char ch = bmap.charAt(i);
423 switch(ch) {
424 case '\r':
425 case '\n':
426 if(!newLine)
427 cy++;
428 cx = 0;
429 newLine = true;
430 break;
431 default:
432 newLine = false;
433 if(cy >= h)
434 h = cy + 1;
435 if(cx >= w)
436 w = cx + 1;
437 cx++;
438 break;
441 stride = (w + PIXELS_PER_ELEMENT - 1) / PIXELS_PER_ELEMENT;
442 bitmapData = new int[h * stride + 2];
443 cx = 0;
444 cy = 0;
445 newLine = true;
446 for(int i = 0; i < bmap.length(); i++) {
447 char ch = bmap.charAt(i);
448 switch(ch) {
449 case '\r':
450 case '\n':
451 if(!newLine)
452 cy++;
453 cx = 0;
454 newLine = true;
455 break;
456 case ' ':
457 case '.':
458 newLine = false;
459 cx++;
460 break;
461 default:
462 bitmapData[cy * stride + cx / PIXELS_PER_ELEMENT] |=
463 (1 << (cx % PIXELS_PER_ELEMENT));
464 newLine = false;
465 cx++;
466 break;
471 void render(int[] buffer, int bw, int bh)
473 if(bitmapData == null)
474 return;
475 int counter = 0;
476 int pixel = bitmapData[counter];
477 int pixelModulus = 0;
478 for(int j = y; j < y + h && j < bh; j++) {
479 for(int i = x; i < x + w; i++) {
480 renderPixel(buffer, bw, bh, i, j, ((pixel >> pixelModulus) & 1) != 0, fillR, fillG, fillB,
481 fillA, lineR, lineG, lineB, lineA);
482 pixelModulus++;
483 if(pixelModulus == PIXELS_PER_ELEMENT) {
484 pixel = bitmapData[++counter];
485 pixelModulus = 0;
488 if(pixelModulus > 0) {
489 pixel = bitmapData[++counter];
490 pixelModulus = 0;
496 private class VGAChargen extends RenderObject
498 int x;
499 int y;
500 int stride;
501 boolean multiline;
502 String vgaChargenString;
503 int lineR;
504 int lineG;
505 int lineB;
506 int lineA;
507 int fillR;
508 int fillG;
509 int fillB;
510 int fillA;
512 VGAChargen(int _x, int _y, String text, int lr, int lg, int lb, int la, int fr, int fg, int fb,
513 int fa, boolean _multiline)
515 x = _x;
516 y = _y;
517 lineR = lr;
518 lineG = lg;
519 lineB = lb;
520 lineA = la;
521 fillR = fr;
522 fillG = fg;
523 fillB = fb;
524 fillA = fa;
525 vgaChargenString = text;
526 multiline = _multiline;
529 final void renderPartial(int[] buffer, int bw, int bh, int x, int y, long data)
531 for(int i = 0; i < 64; i++)
532 renderPixel(buffer, bw, bh, x + 7 - (i % 8), y + (i / 8), ((data >>> i) & 1) != 0, fillR, fillG, fillB,
533 fillA, lineR, lineG, lineB, lineA);
536 void render(int[] buffer, int bw, int bh)
538 int xbase = x, ybase = y;
539 int len = vgaChargenString.length();
540 for(int i = 0; i < len; i++) {
541 int ch = (int)vgaChargenString.charAt(i) & 0xFF;
542 if(multiline && (ch == 13 || ch == 10)) {
543 ybase += 16;
544 xbase = x;
546 if(!(xbase < -7 || ybase < -15 || xbase >= bw || ybase >= bh)) {
547 renderPartial(buffer, bw, bh, xbase, y, vgaFontData[2 * ch + 0]);
548 renderPartial(buffer, bw, bh, xbase, y + 8, vgaFontData[2 * ch + 1]);
550 xbase += 8;
555 public synchronized void REMOTE_bitmap(int _flags, int _x, int _y, String bmap, int lr, int lg, int lb, int la,
556 int fr, int fg, int fb, int fa)
558 if((_flags & flags) != flags)
559 return;
560 renderObjects.add(new Bitmap(_x, _y, bmap, lr, lg, lb, la, fr, fg, fb, fa));
563 public synchronized void REMOTE_bitmap_binary(int _flags, int _x, int _y, String bmap, int lr, int lg, int lb,
564 int la, int fr, int fg, int fb, int fa)
566 if((_flags & flags) != flags)
567 return;
568 renderObjects.add(new Bitmap(_x, _y, bmap, lr, lg, lb, la, fr, fg, fb, fa, true));
571 public synchronized void REMOTE_vga_chargen(int _flags, int _x, int _y, String text, int lr, int lg, int lb, int la,
572 int fr, int fg, int fb, int fa, boolean multiline)
574 if((_flags & flags) != flags)
575 return;
576 renderObjects.add(new VGAChargen(_x, _y, text, lr, lg, lb, la, fr, fg, fb, fa, multiline));