|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.6 root 7: //
8: // ビデオコントローラ
9: //
1.1 root 10:
11: // e82000..e823ff パレット
12: // e82400..e824ff R0(e82400.w) のミラー
13: // e82500..e825ff R1(e82500.w) のミラー
14: // e82600..e826ff R2(e82600.w) のミラー
15: // e82700..e82fff $00 が読めるようだ
16: // 以上の 4KB をミラー。
17:
1.1.1.6 root 18: #include "videoctlr.h"
1.1.1.13! root 19: #include "event.h"
! 20: #include "gvram.h"
1.1.1.11 root 21: #include "mainapp.h"
1.1.1.10 root 22: #include "planevram.h"
1.1.1.7 root 23: #include "renderer.h"
1.1.1.9 root 24: #include "scheduler.h"
1.1.1.13! root 25: #include "textscreen.h"
1.1.1.7 root 26: #include "uimessage.h"
1.1.1.9 root 27: #include <cmath>
28:
29: // InsideOut p.135
30: static const busdata read_wait = busdata::Wait( 9 * 40_nsec);
31: static const busdata write_wait = busdata::Wait(11 * 40_nsec);
1.1 root 32:
1.1.1.6 root 33: // コンストラクタ
1.1 root 34: VideoCtlrDevice::VideoCtlrDevice()
1.1.1.8 root 35: : inherited(OBJ_VIDEOCTLR)
1.1 root 36: {
1.1.1.13! root 37: txbmp.Create(1024, 1024);
! 38: grbmp.Create(1024, 1024);
! 39: mixbmp.Create(1024, 1024);
! 40:
! 41: palette.resize(512);
! 42: hostcolor.resize(512);
1.1.1.9 root 43:
44: #if defined(HAVE_AVX2)
45: enable_avx2 = gMainApp.enable_avx2;
46: #endif
1.1.1.10 root 47: #if defined(HAVE_NEON)
48: enable_neon = gMainApp.enable_neon;
49: #endif
1.1 root 50: }
51:
1.1.1.6 root 52: // デストラクタ
1.1 root 53: VideoCtlrDevice::~VideoCtlrDevice()
54: {
1.1.1.8 root 55: }
56:
57: // 初期化
58: bool
59: VideoCtlrDevice::Init()
60: {
1.1.1.13! root 61: gvram = GetGVRAMDevice();
1.1.1.9 root 62: planevram = GetPlaneVRAMDevice();
1.1.1.8 root 63: renderer = GetRenderer();
1.1.1.13! root 64: uimessage = gMainApp.GetUIMessage();
1.1.1.8 root 65:
1.1.1.13! root 66: auto evman = GetEventManager();
! 67: contrast_event = evman->Regist(this,
! 68: ToEventCallback(&VideoCtlrDevice::ContrastCallback),
! 69: "VideoCtlr Analog Contrast");
1.1.1.10 root 70: // 約30fps。これ以上速くても分からない。
1.1.1.13! root 71: contrast_event->time = 30_msec;
1.1.1.10 root 72:
1.1.1.8 root 73: return true;
1.1 root 74: }
75:
1.1.1.6 root 76: // リセット
1.1.1.3 root 77: void
1.1.1.6 root 78: VideoCtlrDevice::ResetHard(bool poweron)
1.1.1.3 root 79: {
1.1.1.9 root 80: if (poweron) {
81: // 本当かどうかは知らないが、とりあえず。
82: // 初期状態は放電されていれば 0 のはずで、
83: // ビデオコントローラ側の出力の初期値はおそらく High のはず。
84: running_contrast = 0;
85: SetContrast(15);
86: }
1.1.1.13! root 87:
! 88: // リセット直後に再描画を起こすため、一致しない値にしておく。
! 89: prev_reg1 = -1;
! 90: prev_reg2 = -1;
1.1.1.3 root 91: }
92:
1.1.1.10 root 93: /*static*/ inline uint32
94: VideoCtlrDevice::Decoder(uint32 addr)
1.1.1.8 root 95: {
96: return addr & 0xfff;
97: }
98:
1.1.1.9 root 99: busdata
1.1.1.10 root 100: VideoCtlrDevice::Read(busaddr addr)
1.1 root 101: {
1.1.1.10 root 102: uint32 paddr = addr.Addr();
103: uint32 offset = Decoder(paddr);
1.1.1.9 root 104: busdata data;
1.1.1.3 root 105:
1.1.1.10 root 106: data = Get16(offset & ~1U);
107: if (__predict_false(loglevel >= 3)) {
108: if (offset < 0x400) { // パレット
109: putlogn("Palette $%06x -> $%04x", (paddr & ~1U), data.Data());
110: } else if (offset < 0x700) { // R0,R1,R2
111: uint rn = Offset2Rn(offset);
112: putlogn("R%u -> $%04x", rn, data.Data());
113: }
1.1 root 114: }
1.1.1.9 root 115: data |= read_wait;
1.1.1.10 root 116: data |= BusData::Size2;
1.1 root 117: return data;
118: }
119:
1.1.1.9 root 120: busdata
1.1.1.10 root 121: VideoCtlrDevice::Write(busaddr addr, uint32 data)
1.1 root 122: {
1.1.1.10 root 123: uint32 offset = Decoder(addr.Addr());
124: uint32 reqsize = addr.GetSize();
125: uint32 datasize = std::min(2 - (offset & 1U), reqsize);
126: data >>= (reqsize - datasize) * 8;
127:
128: if (__predict_false(loglevel >= 2)) {
129: if (offset < 0x400) { // パレット
130: putlogn("Palette $%06x.%c <- $%0*x", addr.Addr(),
131: (datasize == 1 ? 'B' : 'W'), datasize * 2, data);
132: } else if (offset < 0x700) { // R0,R1,R2
133: uint rn = Offset2Rn(offset);
134: if (datasize == 1) {
1.1.1.13! root 135: putlogn("R%u.%c <- $%02x",
1.1.1.10 root 136: rn, ((offset & 1U) ? 'H' : 'L'), data);
137: } else {
1.1.1.13! root 138: putlogn("R%u <- $%04x", rn, data);
1.1.1.10 root 139: }
140: }
1.1 root 141: }
142:
1.1.1.10 root 143: if (datasize == 1) {
144: Set8(offset, data);
1.1.1.8 root 145: } else {
1.1.1.10 root 146: Set16(offset, data);
1.1.1.8 root 147: }
1.1.1.10 root 148: busdata r = write_wait;
149: r |= busdata::Size(datasize);
150: return r;
1.1.1.8 root 151: }
1.1 root 152:
1.1.1.9 root 153: busdata
1.1.1.10 root 154: VideoCtlrDevice::Peek1(uint32 addr)
1.1.1.8 root 155: {
156: uint32 offset = Decoder(addr);
1.1.1.10 root 157: uint32 data = Get16(offset & ~1U);
158: if ((offset & 1U) == 0) {
159: return data >> 8;
1.1.1.8 root 160: } else {
1.1.1.10 root 161: return data & 0xff;
1.1 root 162: }
1.1.1.8 root 163: }
1.1 root 164:
1.1.1.9 root 165: bool
1.1.1.10 root 166: VideoCtlrDevice::Poke1(uint32 addr, uint32 data)
1.1.1.8 root 167: {
1.1.1.10 root 168: // パレットのみ編集可能とする。
1.1.1.8 root 169: uint32 offset = Decoder(addr);
170: if (offset < 0x400) {
171: if ((int32)data >= 0) {
1.1.1.10 root 172: Set8(offset, data);
1.1.1.8 root 173: }
1.1.1.9 root 174: return true;
1.1.1.8 root 175: }
176:
1.1.1.9 root 177: return false;
1.1 root 178: }
179:
1.1.1.13! root 180: // モニタ更新の下請け。CRTC から呼ばれる。
! 181: int
! 182: VideoCtlrDevice::MonitorUpdate(TextScreen& screen, int y, uint32 crtc_r20)
! 183: {
! 184: std::array<uint16, 3> tmp = reg;
! 185:
! 186: for (uint i = 0; i < tmp.size(); i++) {
! 187: screen.Print(0, y + i, "$%06x R%u: $%04x",
! 188: baseaddr + 0x400 + i * 0x100, i, tmp[i]);
! 189: }
! 190:
! 191: // R0 (p.234)
! 192: // CRTC R20 の bit10-8 と同じでなければいけないと書いてあるが、
! 193: // 違ったら CRTC のほうが使われてるっぽいので、こっちを赤とかにしてみる。
! 194: // (NetBSD/x68k の X)
! 195: uint tmp0 = tmp[0] & 7;
! 196: uint diff = tmp0 ^ (crtc_r20 >> 8);
! 197: screen.Print(18, y, ((diff & R0_SIZ) ? TA::ReverseRed : TA::Normal),
! 198: "SIZ=%s", siz_str[tmp0 >> 2]);
! 199: screen.Print(32, y, ((diff & R0_COL) ? TA::ReverseRed : TA::Normal),
! 200: "COL=%s", col_str[tmp0 & R0_COL]);
! 201: // ちょっと間借りして内部情報を表示。
! 202: {
! 203: char sobuf[32];
! 204: int len = strlcpy(sobuf, "(SCREEN_ORDER=", sizeof(sobuf));
! 205: for (uint32 s = screen_order; s != 0; s >>= 4) {
! 206: static const char so_str[] = ".STGBK";
! 207: sobuf[len++] = so_str[s & 0xf];
! 208: }
! 209: sobuf[len] = '\0';
! 210: len = strlcat(sobuf, ")", sizeof(sobuf));
! 211: screen.Puts(screen.GetCol() - len, y, sobuf);
! 212: }
! 213: y++;
! 214:
! 215: // R1 (p.188)
! 216: {
! 217: std::array<const char *, 4> plist {};
! 218: uint sp = (tmp[1] >> 12) & 3;
! 219: uint tp = (tmp[1] >> 10) & 3;
! 220: uint gp = (tmp[1] >> 8) & 3;
! 221: plist[sp] = "SP";
! 222: plist[tp] = "TX";
! 223: plist[gp] = "GR";
! 224: std::string pstr;
! 225: if (plist[3]) {
! 226: pstr = "Invalid";
! 227: } else if (plist[0] == NULL || plist[1] == NULL || plist[2] == NULL) {
! 228: pstr = "Conflict";
! 229: } else {
! 230: pstr = string_format("%s > %s > %s", plist[0], plist[1], plist[2]);
! 231: }
! 232:
! 233: uint gn = tmp[1] & 0xff;
! 234: std::string gstr;
! 235: switch (tmp0 & 3) {
! 236: case 0: // 16色モード、グラフィックページは4枚。
! 237: {
! 238: std::array<const char *, 4> glist {};
! 239: uint p3 = (tmp[1] >> 6) & 3;
! 240: uint p2 = (tmp[1] >> 4) & 3;
! 241: uint p1 = (tmp[1] >> 2) & 3;
! 242: uint p0 = tmp[1] & 3;
! 243: glist[p0] = "P0";
! 244: glist[p1] = "P1";
! 245: glist[p2] = "P2";
! 246: glist[p3] = "P3";
! 247: if (glist[0] && glist[1] && glist[2] && glist[3]) {
! 248: gstr = string_format("%s > %s > %s > %s",
! 249: glist[0], glist[1], glist[2], glist[3]);
! 250: } else {
! 251: gstr = "Conflict";
! 252: }
! 253: break;
! 254: }
! 255:
! 256: case 1: // 256色モード、グラフィックページは2枚。
! 257: // この場合 $e4 か $4e だけが有効。
! 258: if (gn == 0xe4) {
! 259: gstr = "P0 > P1";
! 260: } else if (gn == 0x4e) {
! 261: gstr = "P1 > P0";
! 262: } else {
! 263: gstr = "Invalid";
! 264: }
! 265: break;
! 266:
! 267: case 2: // 未定義
! 268: gstr = "?";
! 269: break;
! 270:
! 271: case 3: // 65536色モード、グラフィックページは1枚。
! 272: gstr = "P0";
! 273: break;
! 274:
! 275: default:
! 276: __unreachable();
! 277: }
! 278:
! 279: // 2 3 4 5 6
! 280: // 89012345678901234567890123456789012345678901
! 281: // Priority=SP > TX > GR, GP=P0 > P1 > P2 > P3
! 282: screen.Print(18, y, "Priority=%s,", pstr.c_str());
! 283: screen.Print(42, y, "GP=%s", gstr.c_str());
! 284: }
! 285: y++;
! 286:
! 287: // R2 (p.210)
! 288: uint x = 18;
! 289: uint tmp2 = tmp[2];
! 290: screen.Print(x + 0, y, TA::OnOff(tmp2 & R2_YS), "YS");
! 291: screen.Print(x + 3, y, TA::OnOff(tmp2 & R2_AH), "AH");
! 292: screen.Print(x + 6, y, TA::OnOff(tmp2 & R2_VH), "VH");
! 293: screen.Print(x + 9, y, TA::OnOff(tmp2 & R2_EX), "EX");
! 294: screen.Print(x + 12, y, TA::OnOff(tmp2 & R2_HP), "HP");
! 295: screen.Print(x + 15, y, TA::OnOff(tmp2 & R2_BP), "BP");
! 296: screen.Print(x + 18, y, TA::OnOff(tmp2 & R2_GG), "GG");
! 297: screen.Print(x + 21, y, TA::OnOff(tmp2 & R2_GT), "GT");
! 298: screen.Print(x + 24, y, TA::OnOff(tmp2 & 0x0080), "0");
! 299: screen.Print(x + 26, y, TA::OnOff(tmp2 & R2_SP_ON), "SP");
! 300: screen.Print(x + 29, y, TA::OnOff(tmp2 & R2_TX_ON), "TX");
! 301: screen.Print(x + 32, y, TA::OnOff(tmp2 & R2_GR_ON), "GR");
! 302: screen.Print(x + 35, y, TA::OnOff(tmp2 & R2_G3_ON), "G3");
! 303: screen.Print(x + 38, y, TA::OnOff(tmp2 & R2_G2_ON), "G2");
! 304: screen.Print(x + 41, y, TA::OnOff(tmp2 & R2_G1_ON), "G1");
! 305: screen.Print(x + 44, y, TA::OnOff(tmp2 & R2_G0_ON), "G0");
! 306: y++;
! 307:
! 308: return y;
! 309: }
! 310:
! 311: // CRTC からも使う。
! 312: /*static*/ const char * const
! 313: VideoCtlrDevice::siz_str[2] = {
! 314: "512x512",
! 315: "1024x1024",
! 316: };
! 317:
! 318: /*static*/ const char * const
! 319: VideoCtlrDevice::col_str[4] = {
! 320: "16",
! 321: "256",
! 322: "undef",
! 323: "65536",
! 324: };
! 325:
1.1.1.10 root 326: // R0, R1, R2 のアドレスオフセットからレジスタ番号 0, 1, 2 を返す。
1.1.1.8 root 327: // offset は R0, R1, R2 の領域を指していること。
1.1.1.10 root 328: /*static*/ inline uint32
329: VideoCtlrDevice::Offset2Rn(uint32 offset)
1.1 root 330: {
1.1.1.8 root 331: assert(0x400 <= offset && offset < 0x700);
1.1.1.10 root 332: return (offset >> 8) - 4;
1.1 root 333: }
334:
1.1.1.10 root 335: // offset の位置の内容をワードで返す。
336: // offset は偶数であること。
1.1.1.8 root 337: uint32
1.1.1.10 root 338: VideoCtlrDevice::Get16(uint32 offset) const
1.1 root 339: {
1.1.1.10 root 340: assert((offset & 1U) == 0);
341:
342: if (offset < 0x400) { // パレット
343: return palette[offset >> 1];
344: } else if (offset < 0x700) { // R0,R1,R2
345: uint rn = Offset2Rn(offset);
346: return reg[rn];
347: } else {
348: return 0x00;
349: }
1.1 root 350: }
351:
1.1.1.10 root 352: // offset の位置のバイトを更新する。
1.1 root 353: void
1.1.1.10 root 354: VideoCtlrDevice::Set8(uint32 offset, uint32 data)
1.1 root 355: {
1.1.1.10 root 356: uint32 offset2 = offset & ~1U;
1.1.1.8 root 357:
1.1.1.10 root 358: uint32 tmp = Get16(offset2);
359: if ((offset & 1U) == 0) {
360: tmp = (data << 8) | (tmp & 0xff);
361: } else {
362: tmp = (tmp & 0xff00) | data;
363: }
364: Set16(offset2, tmp);
365: }
366:
367: // offset の位置のワードを更新する。
368: // offset は偶数であること。
369: void
370: VideoCtlrDevice::Set16(uint32 offset, uint32 data)
371: {
372: assert((offset & 1U) == 0);
373:
374: if (offset < 0x400) { // パレット
375: palette[offset >> 1] = data;
376: MakePalette();
377: } else if (offset < 0x700) { // R0,R1,R2
378: uint rn = Offset2Rn(offset);
379: switch (rn) {
380: case 0: reg[rn] = data & 0x0003U; break;
381: case 1: reg[rn] = data & 0x3fffU; break;
382: case 2: reg[rn] = data & 0xff7fU; break;
383: default:
384: break;
385: }
386: } else {
387: // nop
1.1.1.8 root 388: }
1.1 root 389: }
1.1.1.2 root 390:
1.1.1.9 root 391: // コントラストを設定する。
392: // 指定値は 0-15 だが、内部では 0-xff で保持。
393: void
1.1.1.10 root 394: VideoCtlrDevice::SetContrast(uint contrast_)
1.1.1.9 root 395: {
396: target_contrast = contrast_ * 0x11;
397:
398: if (running_contrast != target_contrast) {
399: initial_contrast = running_contrast;
400: contrast_time0 = scheduler->GetVirtTime();
401: scheduler->RestartEvent(contrast_event);
402: }
403: }
404:
405: // コントラスト変化イベント。
1.1.1.2 root 406: //
1.1.1.9 root 407: // Human68k 電源オフ時などの画面フェードアウトは ROM や Human68k がソフト
408: // ウェアで時間をかけてコントラストを変えているのではなく、ハードウェアで
409: // 実装してある。(わざわざ?) RC 回路が入っているのでおそらく意図していると
410: // 思われ。電源オフ時は ROM ルーチンがシステムポートにコントラスト 0 を1回
411: // だけ書き込んでいる。
412: //
413: // 細かいことは無視して RC 回路の過渡現象の式だけ持ってきて使う。
414: //
415: // 下がる時は E * exp(-t / RC) で、
416: // E は開始時の値なので initial_contrast、原点は target_contrast とする。
417: //
418: // E |*
419: // |*
420: // | *
421: // | ***
422: // | ****
1.1.1.13! root 423: // 0 +---------> t
1.1.1.9 root 424: //
425: // 上がる時は E * (1 - exp(-t / RC)) で、
426: // この場合 E は定常値なので target_contrast、原点が initial_contrast となる。
427: //
428: // E | ****
429: // | ***
430: // | *
431: // |*
432: // |*
1.1.1.13! root 433: // 0 +---------> t
1.1.1.9 root 434: //
435: // 途中まで充電されてる状態から始まる過渡現象がこれと同じかどうかは知らない
436: // けど、とりあえず毎回 initial と target の差だけで動作する。
437: void
1.1.1.13! root 438: VideoCtlrDevice::ContrastCallback(Event *ev)
1.1.1.9 root 439: {
440: const float RC = 0.18; // 時定数 1.8[KΩ]*100[uF]
441:
442: if (running_contrast != target_contrast) {
443: // 下がる時は (initial - target) * exp(-t / RC)
444: // 上がる時は (target - initial) * (1 - exp(-t / RC))
445: uint64 now = scheduler->GetVirtTime();
446: float t = (float)(now - contrast_time0) / 1e9;
447: float e = std::exp(-t / RC);
448: float n;
449: if (initial_contrast > target_contrast) {
450: float v = (float)(initial_contrast - target_contrast);
451: n = target_contrast + v * e;
452: } else {
453: float v = (float)(target_contrast - initial_contrast);
454: n = initial_contrast + v * (1 - e);
455: }
456: uint32 new_contrast = (uint32)(n + 0.5);
457: putlog(2, "Contrast %x->%x: t=%g e=%g new=%x",
458: initial_contrast, target_contrast, t, e, new_contrast);
459:
460: if (new_contrast != running_contrast) {
461: running_contrast = new_contrast;
462: }
463:
464: scheduler->RestartEvent(ev);
465: }
466: }
467:
1.1.1.2 root 468: void
469: VideoCtlrDevice::MakePalette()
470: {
1.1.1.6 root 471: // パレット情報から Color 形式の色データを作成
1.1.1.2 root 472: // X680x0 のパレットは %GGGGG'RRRRR'BBBBB'I で並んでいる。
1.1.1.13! root 473: for (int i = 0; i < palette.size(); i++) {
! 474: uint I = (palette[i] & 1) << 2; // 輝度ビット
! 475: uint R = (((palette[i] >> 6) & 0x1f) << 3) + I;
! 476: uint G = (((palette[i] >> 11)) << 3) + I;
! 477: uint B = (((palette[i] >> 1) & 0x1f) << 3) + I;
1.1.1.8 root 478: hostcolor[i].r = R;
479: hostcolor[i].g = G;
480: hostcolor[i].b = B;
1.1.1.2 root 481: }
1.1.1.6 root 482:
1.1.1.11 root 483: // パレット変更が起きたことをテキストレンダラに通知。
484: // この後ここでグラフィックレンダラにも通知。
485: planevram->Invalidate2();
1.1.1.7 root 486:
487: // UI にも通知
1.1.1.13! root 488: uimessage->Post(UIMessage::PALETTE);
1.1.1.8 root 489: }
1.1.1.9 root 490:
1.1.1.11 root 491: // 画面の作成。CRTC から VDisp のタイミングで呼ばれる。
492: // Renderer に作画指示を出すかどうかを決める。
493: void
494: VideoCtlrDevice::VDisp()
495: {
496: bool update_needed = false;
497:
1.1.1.13! root 498: // 各画面に更新があるか。モニタ用にもなるためオフでも行う。
1.1.1.11 root 499: if (planevram->Snap()) {
500: update_needed = true;
501: }
1.1.1.13! root 502: if (gvram->Snap()) {
! 503: update_needed = true;
! 504: }
! 505:
! 506: // プライオリティかオンオフが変わっていても再描画。
! 507: if (reg[1] != prev_reg1 || (reg[2] & 0xff) != prev_reg2) {
! 508: std::array<uint32, 4> list {};
! 509: // オンオフと描画順。
! 510: // XXX グラフィック画面のプレーン(ページ)は未対応
! 511: #if 0
! 512: if ((reg[2] & R2_SP_ON)) {
! 513: uint sp = (reg[1] >> 12) & 3;
! 514: list[sp] = SORDER_SP;
! 515: }
! 516: #endif
! 517: if ((reg[2] & R2_TX_ON)) {
! 518: uint tp = (reg[1] >> 10) & 3;
! 519: list[tp] = SORDER_TX;
! 520: }
! 521: if ((reg[2] & R2_GR_ON)) {
! 522: uint gp = (reg[1] >> 8) & 3;
! 523: list[gp] = SORDER_GR;
! 524: }
! 525:
! 526: uint32 new_order = 0;
! 527: for (uint i = 0; i < 3; i++) {
! 528: if (list[i] != SORDER_NONE) {
! 529: new_order <<= 4;
! 530: new_order |= list[i];
! 531: }
! 532: }
! 533: // 全レイヤーオフなら仮想的な黒画面を出力。
! 534: if (new_order == 0) {
! 535: new_order = SORDER_BLACK;
! 536: }
! 537: // 変化があれば再描画。
! 538: if (new_order != screen_order) {
! 539: screen_order = new_order;
! 540: update_needed = true;
! 541: }
! 542:
! 543: prev_reg1 = reg[1];
! 544: prev_reg2 = reg[2] & 0xff;
! 545: }
1.1.1.11 root 546:
547: // コントラストが変わっていても再描画。
548: if (__predict_false(rendering_contrast != running_contrast)) {
549: pending_contrast = running_contrast;
550: update_needed = true;
551: }
552:
553: if (update_needed) {
554: renderer->NotifyRender();
555: }
556: }
557:
1.1.1.9 root 558: // 画面合成。
559: // レンダリングスレッドから呼ばれる。
560: bool
1.1.1.11 root 561: VideoCtlrDevice::Render(BitmapRGBX& dst)
1.1.1.9 root 562: {
1.1.1.13! root 563: uint32 local_screen_order = screen_order;
! 564:
! 565: // 各画面を描画。各画面(レイヤー)はモニタ表示用としても使うため、
! 566: // 表示オンオフに関わらず、常に作成は行う。
! 567: bool tx_updated = planevram->Render(txbmp);
! 568: bool gr_updated = gvram->Render(grbmp);
! 569:
! 570: // mixbmp に合成。mixbmp もモニタ表示に使う。
! 571: // XXX 重ね合わせはまだ
! 572: bool mix_updated = false;
! 573: for (uint32 s = local_screen_order; s != 0; s >>= 4) {
! 574: switch (s & 0xf) {
! 575: case SORDER_TX:
! 576: mixbmp.CopyFrom(&txbmp);
! 577: mix_updated = tx_updated;
! 578: break;
! 579: case SORDER_GR:
! 580: mixbmp.CopyFrom(&grbmp);
! 581: mix_updated = gr_updated;
! 582: break;
! 583: case SORDER_BLACK:
! 584: mixbmp.Fill(0);
! 585: mix_updated = true;
! 586: break;
! 587: case SORDER_SP:
! 588: case SORDER_BG:
! 589: default:
! 590: break;
! 591: }
! 592: }
1.1.1.9 root 593:
594: // 最後にコントラスト適用。
1.1.1.13! root 595: bool updated = false;
1.1.1.11 root 596: int contrast = pending_contrast.exchange(rendering_contrast);
597: if (contrast != rendering_contrast) {
598: rendering_contrast = contrast;
1.1.1.9 root 599: updated = true;
600: }
1.1.1.13! root 601: if (__predict_false(mix_updated || updated)) {
! 602: RenderContrast(dst, mixbmp);
1.1.1.9 root 603: updated = true;
604: }
1.1.1.13! root 605:
1.1.1.9 root 606: return updated;
607: }
608:
609: #if 0
610: #define PERFCONT
611: #include "stopwatch.h"
612: static uint64 perf_total;
1.1.1.10 root 613: static uint perf_count;
1.1.1.9 root 614: #endif
615:
616: // コントラストを適用。
617: void
618: VideoCtlrDevice::RenderContrast(BitmapRGBX& dst, const BitmapRGBX& src)
619: {
620: assert(src.GetWidth() % 4 == 0);
621:
622: uint32 contrast = rendering_contrast;
1.1.1.13! root 623: if (__predict_false(contrast == 255)) {
! 624: Rect rect(0, 0, dst.GetWidth(), dst.GetHeight());
! 625: dst.DrawBitmap(0, 0, src, rect);
! 626: } else if (__predict_false(contrast == 0)) {
! 627: uint8 *d = dst.GetBuf();
! 628: memset(d, 0, dst.GetStride() * dst.GetHeight());
1.1.1.9 root 629: } else {
630: #if defined(PERFCONT)
631: Stopwatch sw;
632: sw.Start();
633: #endif
634:
635: #if defined(HAVE_AVX2)
636: if (__predict_true(enable_avx2)) {
637: RenderContrast_avx2(dst, src, contrast);
638: } else
639: #endif
1.1.1.10 root 640: #if defined(HAVE_NEON)
641: if (__predict_true(enable_neon)) {
642: RenderContrast_neon(dst, src, contrast);
643: } else
644: #endif
1.1.1.9 root 645: {
646: RenderContrast_gen(dst, src, contrast);
647: }
648:
649: #if defined(PERFCONT)
650: sw.Stop();
651: perf_total += sw.Elapsed();
652: if (++perf_count % 100 == 0) {
1.1.1.10 root 653: printf("%" PRIu64 " usec\n", perf_total / perf_count / 1000);
1.1.1.9 root 654: }
655: #endif
656: }
657: }
658:
1.1.1.13! root 659: // コントラスト (1-254) を適用。dst サイズでクリップする。C++ 版。
1.1.1.9 root 660: /*static*/ void
661: VideoCtlrDevice::RenderContrast_gen(BitmapRGBX& dst, const BitmapRGBX& src,
662: uint32 contrast)
663: {
664: std::array<uint8, 256> lut;
665:
1.1.1.10 root 666: for (uint i = 0; i < lut.size(); i++) {
1.1.1.9 root 667: lut[i] = (i * contrast) >> 8;
668: }
669:
1.1.1.13! root 670: for (uint y = 0, yend = dst.GetHeight(); y < yend; y++) {
! 671: const uint32 *s32 = (const uint32 *)src.GetRowPtr(y);
! 672: uint32 *d32 = (uint32 *)dst.GetRowPtr(y);
! 673: for (uint x = 0, xend = dst.GetWidth(); x < xend; x++) {
! 674: // 1ピクセルずつ処理する。
! 675: Color cs(*s32++);
! 676:
! 677: uint32 r = lut[cs.r];
! 678: uint32 g = lut[cs.g];
! 679: uint32 b = lut[cs.b];
1.1.1.9 root 680:
1.1.1.13! root 681: Color cd(r, g, b);
! 682: *d32++ = cd.u32;
! 683: }
1.1.1.9 root 684: }
685: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.