- imgrow=0;
- for (textrow=0; textrow<24; textrow++) {
- if (st->rowimage[textrow] == textrow) {
- screen_plan[textrow]=0;
- }
- else if (cheapdisplay && st->rowimage[textrow]>=0 &&
- textrow<21 && st->rowimage[textrow]<21 &&
- st->rowimage[textrow]>=2 && textrow>=2 &&
- (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) {
- screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow];
- for (i=0; i<8; i++) {
- crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i];
- }
- startdisplayrow=0;
- }
- else {
- st->rowimage[textrow]=imgrow;
- screen_plan[textrow]=imgrow | A2_SP_PUT;
-
- for (row=textrow*8; row<textrow*8+8; row++) {
- char *pp;
- int pixmultinc,pixbright;
- int scanstart_i, scanend_i;
- int squishright_i, squishdiv;
- int pixrate;
- double bloomthisrow,shiftthisrow;
- int ytop=(imgrow*h/24) + ((row-textrow*8) * h/192);
- int ybot=ytop+h/192;
-
- /* First we generate the pattern that the video circuitry shifts out
- of memory. It has a 14.something MHz dot clock, equal to 4 times
- the color burst frequency. So each group of 4 bits defines a
- color. Each character position, or byte in hires, defines 14
- dots, so odd and even bytes have different color spaces. So,
- pattern[0..600] gets the dots for one scan line. */
-
- memset(dec->pattern,0,sizeof(dec->pattern));
- pp=dec->pattern+20;
-
- if ((st->gr_mode&A2_GR_HIRES) && (row<160 ||
- (st->gr_mode&A2_GR_FULL))) {
-
- /* Emulate the mysterious pink line, due to a bit getting
- stuck in a shift register between the end of the last
- row and the beginning of this one. */
- if ((st->hireslines[row][0] & 0x80) &&
- (st->hireslines[row][39]&0x40)) {
- pp[-1]=1;
- }
-
- for (col=0; col<40; col++) {
- u_char b=st->hireslines[row][col];
- int shift=(b&0x80)?0:1;
-
- /* Each of the low 7 bits in hires mode corresponded to 2 dot
- clocks, shifted by one if the high bit was set. */
- for (i=0; i<7; i++) {
- pp[shift+1] = pp[shift] =(b>>i)&1;
- pp+=2;
- }
- }
- }
- else if ((st->gr_mode&A2_GR_LORES) && (row<160 ||
- (st->gr_mode&A2_GR_FULL))) {
- for (col=0; col<40; col++) {
- u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf;
- /* The low or high nybble was shifted out one bit at a time. */
- for (i=0; i<14; i++) {
- *pp = (nib>>((col*14+i)&3))&1;
- pp++;
- }
- }
- }
- else {
- for (col=0; col<40; col++) {
- int rev;
- c=st->textlines[textrow][col];
- /* hi bits control inverse/blink as follows:
- 0x00: inverse
- 0x40: blink
- 0x80: normal
- 0xc0: normal */
- rev=!(c&0x80) && (!(c&0x40) || st->blink);
-
- for (i=0; i<7; i++) {
- for (i=0; i<7; i++) {
- unsigned long pix=XGetPixel(text_im,
- ((c&0x3f)^0x20)*7+i, row%8);
- pp[1] = pp[2] = pix^rev;
- pp+=2;
- }
- }
- }
- }
-
- /*
- Interpolate the 600-dotclock line into however many horizontal
- screen pixels we're using, and convert to RGB.
-
- We add some 'bloom', variations in the horizontal scan width with
- the amount of brightness, extremely common on period TV sets. They
- had a single oscillator which generated both the horizontal scan
- and (during the horizontal retrace interval) the high voltage for
- the electron beam. More brightness meant more load on the
- oscillator, which caused an decrease in horizontal deflection. Look
- for (bloomthisrow).
-
- Also, the A2 did a bad job of generating horizontal sync pulses
- during the vertical blanking interval. This, and the fact that the
- horizontal frequency was a bit off meant that TVs usually went a
- bit out of sync during the vertical retrace, and the top of the
- screen would be bent a bit to the left or right. Look for
- (shiftthisrow).
-
- We also add a teeny bit of left overscan, just enough to be
- annoying, but you can still read the left column of text.
-
- We also simulate compression & brightening on the right side of the
- screen. Most TVs do this, but you don't notice because they
- overscan so it's off the right edge of the CRT. But the A2 video
- system used so much of the horizontal scan line that you had to
- crank the horizontal width down in order to not lose the right few
- characters, and you'd see the compression on the right
- edge. Associated with compression is brightening; since the
- electron beam was scanning slower, the same drive signal hit the
- phosphor harder. Look for (squishright_i) and (squishdiv).
- */
-
- for (i=j=0; i<600; i++) {
- j += dec->pattern[i];
- }
- crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) +
- (row>180 ? (row-180)*(row-180)*0.0005 : 0.0);
- bloomthisrow = -10.0 * crtload[row];
- shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05)
- * horiz_desync : 0.0);
-
- scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0);
- if (scanstart_i<0) scanstart_i=0;
- if (scanstart_i>30*65536) scanstart_i=30*65536;
- scanend_i=599*65536;
- squishright_i=scanstart_i + 530*65536;
- squishdiv=w/15;
- pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w);
-
- if (use_cmap) {
- for (y=ytop; y<ybot; y++) {
- int level=(!(y==ytop && ybot-ytop>=3) &&
- !(y==ybot-1 && ybot-ytop>=5));
- int hist=0;
- int histi=0;
-
- pixmultinc=pixrate;
- for (x=0, i=scanstart_i;
- x<w && i<scanend_i;
- x++, i+=pixmultinc) {
- int pati=(i>>16);
- int offset=pati&3;
- while (pati>=histi) {
- hist=(((hist<<1) & ((1<<A2_CMAP_HISTBITS)-1)) |
- dec->pattern[histi]);
- histi++;
- }
- XPutPixel(image, x, y,
- colors[A2_CMAP_INDEX(colormode,level,hist,offset)]);
- if (i >= squishright_i) {
- pixmultinc += pixmultinc/squishdiv;
- }
- }
- for ( ; x<w; x++) {
- XPutPixel(image, x, y, colors[0]);
- }
- }
- } else {
-
- ntsc_to_yiq(dec);
-
- pixbright=(int)(contrast_control*65536.0);
- pixmultinc=pixrate;
- for (x=0, i=scanstart_i, rrp=raw_rgb;
- x<w && i<scanend_i;
- x++, i+=pixmultinc, rrp+=3) {
- int pixfrac=i&0xffff;
- int invpixfrac=65536-pixfrac;
- int pati=i>>16;
- int r,g,b;
-
- int interpy=((dec->ntscy[pati]*invpixfrac +
- dec->ntscy[pati+1]*pixfrac)>>16);
- int interpi=((dec->ntsci[pati]*invpixfrac +
- dec->ntsci[pati+1]*pixfrac)>>16);
- int interpq=((dec->ntscq[pati]*invpixfrac +
- dec->ntscq[pati+1]*pixfrac)>>16);
-
- /*
- According to the NTSC spec, Y,I,Q are generated as:
-
- y=0.30 r + 0.59 g + 0.11 b
- i=0.60 r - 0.28 g - 0.32 b
- q=0.21 r - 0.52 g + 0.31 b
-
- So if you invert the implied 3x3 matrix you get what standard
- televisions implement with a bunch of resistors (or directly in
- the CRT -- don't ask):
-
- r = y + 0.948 i + 0.624 q
- g = y - 0.276 i - 0.639 q
- b = y - 1.105 i + 1.729 q
-
- These coefficients are below in 16.16 format.
- */
-
- r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright)
- >>16;
- g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright)
- >>16;
- b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright)
- >>16;
- if (r<0) r=0;
- if (g<0) g=0;
- if (b<0) b=0;
- rrp[0]=r;
- rrp[1]=g;
- rrp[2]=b;
-
- if (i>=squishright_i) {
- pixmultinc += pixmultinc/squishdiv;
- pixbright += pixbright/squishdiv;
- }
- }
- for ( ; x<w; x++, rrp+=3) {
- rrp[0]=rrp[1]=rrp[2]=0;
- }
-
- for (y=ytop; y<ybot; y++) {
- /* levelmult represents the vertical size of scan lines. Each
- line is brightest in the middle, and there's a dark band
- between them. */
- int levelmult;
- double levelmult_fp=(y + 0.5 - (ytop+ybot)*0.5) / (ybot-ytop);
- levelmult_fp = 1.0-(levelmult_fp*levelmult_fp*levelmult_fp
- *levelmult_fp)*16.0;
- if (levelmult_fp<0.0) levelmult_fp=0.0;
- levelmult = (int)(64.9*levelmult_fp);
-
- /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes
- to show why standard graphics sw has to be fast, or else
- people will have to work around it and risk incompatibility.
- The quickdraw folks understood this. The other answer would
- be for X11 to have fewer formats for bitm.. oh, never
- mind. If neither of these cases work (they probably cover 99%
- of setups) it falls back on the Xlib routines. */
- if (image->format==ZPixmap && image->bits_per_pixel==32 &&
- sizeof(unsigned long)==4 &&
- image->byte_order==localbyteorder) {
- unsigned long *pixelptr =
- (unsigned long *) (image->data + y * image->bytes_per_line);
- for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
- unsigned long ntscri, ntscgi, ntscbi;
- ntscri=((unsigned long)rrp[0])*levelmult;
- ntscgi=((unsigned long)rrp[1])*levelmult;
- ntscbi=((unsigned long)rrp[2])*levelmult;
- if (ntscri>65535) ntscri=65535;
- if (ntscgi>65535) ntscgi=65535;
- if (ntscbi>65535) ntscbi=65535;
- *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
- ((ntscgi>>green_invprec)<<green_shift) |
- ((ntscbi>>blue_invprec)<<blue_shift);
- }
- }
- else if (image->format==ZPixmap && image->bits_per_pixel==16 &&
- sizeof(unsigned short)==2 &&
- image->byte_order==localbyteorder) {
- unsigned short *pixelptr =
- (unsigned short *)(image->data + y*image->bytes_per_line);
- for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
- unsigned long ntscri, ntscgi, ntscbi;
- ntscri=((unsigned long)rrp[0])*levelmult;
- ntscgi=((unsigned long)rrp[1])*levelmult;
- ntscbi=((unsigned long)rrp[2])*levelmult;
- if (ntscri>65535) ntscri=65535;
- if (ntscgi>65535) ntscgi=65535;
- if (ntscbi>65535) ntscbi=65535;
- *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
- ((ntscgi>>green_invprec)<<green_shift) |
- ((ntscbi>>blue_invprec)<<blue_shift);
- }
-
- }
- else {
- for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
- unsigned long pixel, ntscri, ntscgi, ntscbi;
- /* Convert to 16-bit color values, with saturation. The ntscr
- values are 22.10 fixed point, and levelmult is 24.6, so we
- get 16 bits out*/
- ntscri=((unsigned long)rrp[0])*levelmult;
- ntscgi=((unsigned long)rrp[1])*levelmult;
- ntscbi=((unsigned long)rrp[2])*levelmult;
- if (ntscri>65535) ntscri=65535;
- if (ntscgi>65535) ntscgi=65535;
- if (ntscbi>65535) ntscbi=65535;
- pixel = ((ntscri>>red_invprec)<<red_shift) |
- ((ntscgi>>green_invprec)<<green_shift) |
- ((ntscbi>>blue_invprec)<<blue_shift);
- XPutPixel(image, x, y, pixel);
- }
- }
- }
- }
- }
- imgrow++;
- }