@@ -4067,6 +4067,9 @@ void flush_old_hardware_commands() {
40674067 } // next_hardware_command_to_remove&&last_hardware_command_rendered
40684068} // flush_old_hardware_commands
40694069
4070+ uint8 charset8x8[256][8][8];
4071+ uint8 charset8x16[256][16][8];
4072+
40704073void sub__putimage(double f_dx1, double f_dy1, double f_dx2, double f_dy2, int32 src, int32 dst, double f_sx1, double f_sy1, double f_sx2, double f_sy2,
40714074 int32 passed) {
40724075
@@ -4168,10 +4171,6 @@ void sub__putimage(double f_dx1, double f_dy1, double f_dx2, double f_dy2, int32
41684171 } else {
41694172 s = read_page;
41704173 } // src
4171- if (s->text) {
4172- error(5);
4173- return;
4174- }
41754174 sbpp = s->bytes_per_pixel;
41764175
41774176 if (passed & 32) { // dst
@@ -4194,11 +4193,18 @@ void sub__putimage(double f_dx1, double f_dy1, double f_dx2, double f_dy2, int32
41944193 } else {
41954194 d = write_page;
41964195 } // dst
4197- if (d->text) {
4196+ if (!s->text && d->text) {
41984197 error(5);
41994198 return;
42004199 }
42014200 dbpp = d->bytes_per_pixel;
4201+ if (s->text && !d->text) {
4202+ // Text glyphs use up to 16 palette indices; destination must be able to represent all of them.
4203+ if ((d->bits_per_pixel != 32) && (d->bits_per_pixel < 4)) {
4204+ error(5);
4205+ return;
4206+ }
4207+ }
42024208 if ((sbpp == 4) && (dbpp == 1)) {
42034209 error(5);
42044210 return;
@@ -4358,6 +4364,219 @@ void sub__putimage(double f_dx1, double f_dy1, double f_dx2, double f_dy2, int32
43584364
43594365 // all co-ordinates resolved (but omitted co-ordinates are set to 0!)
43604366
4367+ if (s->text) {
4368+ int32 srcCellStepX = 1;
4369+ int32 srcCellStepY = 1;
4370+ int32 dstStepX = 1;
4371+ int32 dstStepY = 1;
4372+
4373+ if (passed & 64) {
4374+ if (passed & 512) {
4375+ srcCellStepX = (sx2 >= sx1) ? 1 : -1;
4376+ srcCellStepY = (sy2 >= sy1) ? 1 : -1;
4377+ } else {
4378+ sx2 = sw - 1;
4379+ sy2 = sh - 1;
4380+ }
4381+ } else {
4382+ sx1 = 0;
4383+ sy1 = 0;
4384+ sx2 = sw - 1;
4385+ sy2 = sh - 1;
4386+ }
4387+
4388+ const int32 srcCellsW = abs(sx2 - sx1) + 1;
4389+ const int32 srcCellsH = abs(sy2 - sy1) + 1;
4390+ const int32 fontWidth = fontwidth[s->font];
4391+ const int32 fontHeight = fontheight[s->font];
4392+ const int32 expectedDstW = d->text ? srcCellsW : (srcCellsW * fontWidth);
4393+ const int32 expectedDstH = d->text ? srcCellsH : (srcCellsH * fontHeight);
4394+
4395+ if (passed & 1) {
4396+ if (passed & 4) {
4397+ dstStepX = (dx2 >= dx1) ? 1 : -1;
4398+ dstStepY = (dy2 >= dy1) ? 1 : -1;
4399+
4400+ // Text source blit is copy-only (no scaling).
4401+ if (((abs(dx2 - dx1) + 1) != expectedDstW) || ((abs(dy2 - dy1) + 1) != expectedDstH)) {
4402+ error(5);
4403+ return;
4404+ }
4405+ } else {
4406+ dx2 = dx1 + expectedDstW - 1;
4407+ dy2 = dy1 + expectedDstH - 1;
4408+ }
4409+ } else {
4410+ dx1 = 0;
4411+ dy1 = 0;
4412+ dx2 = expectedDstW - 1;
4413+ dy2 = expectedDstH - 1;
4414+ }
4415+
4416+ int32 clipLeadX = 0;
4417+ int32 clipTrailX = 0;
4418+ int32 clipLeadY = 0;
4419+ int32 clipTrailY = 0;
4420+
4421+ const int32 srcClipX1 = s->clipping_or_scaling ? s->view_x1 : 0;
4422+ const int32 srcClipY1 = s->clipping_or_scaling ? s->view_y1 : 0;
4423+ const int32 srcClipX2 = s->clipping_or_scaling ? s->view_x2 : (sw - 1);
4424+ const int32 srcClipY2 = s->clipping_or_scaling ? s->view_y2 : (sh - 1);
4425+
4426+ if (srcCellStepX > 0) {
4427+ if (sx1 < srcClipX1)
4428+ clipLeadX = srcClipX1 - sx1;
4429+ if (sx2 > srcClipX2)
4430+ clipTrailX = sx2 - srcClipX2;
4431+ } else {
4432+ if (sx1 > srcClipX2)
4433+ clipLeadX = sx1 - srcClipX2;
4434+ if (sx2 < srcClipX1)
4435+ clipTrailX = srcClipX1 - sx2;
4436+ }
4437+
4438+ if (srcCellStepY > 0) {
4439+ if (sy1 < srcClipY1)
4440+ clipLeadY = srcClipY1 - sy1;
4441+ if (sy2 > srcClipY2)
4442+ clipTrailY = sy2 - srcClipY2;
4443+ } else {
4444+ if (sy1 > srcClipY2)
4445+ clipLeadY = sy1 - srcClipY2;
4446+ if (sy2 < srcClipY1)
4447+ clipTrailY = srcClipY1 - sy2;
4448+ }
4449+
4450+ if ((clipLeadX + clipTrailX >= srcCellsW) || (clipLeadY + clipTrailY >= srcCellsH))
4451+ return;
4452+
4453+ sx1 += srcCellStepX * clipLeadX;
4454+ sy1 += srcCellStepY * clipLeadY;
4455+ sx2 -= srcCellStepX * clipTrailX;
4456+ sy2 -= srcCellStepY * clipTrailY;
4457+
4458+ const int32 dstUnitX = d->text ? 1 : fontWidth;
4459+ const int32 dstUnitY = d->text ? 1 : fontHeight;
4460+ dx1 += dstStepX * clipLeadX * dstUnitX;
4461+ dy1 += dstStepY * clipLeadY * dstUnitY;
4462+ dx2 -= dstStepX * clipTrailX * dstUnitX;
4463+ dy2 -= dstStepY * clipTrailY * dstUnitY;
4464+
4465+ const int32 clippedCellsW = abs(sx2 - sx1) + 1;
4466+ const int32 clippedCellsH = abs(sy2 - sy1) + 1;
4467+
4468+ auto srcData = reinterpret_cast<uint16 *>(s->offset);
4469+ const uint32 textTransparentColor = uint32(s->transparent_color);
4470+
4471+ if (d->text) {
4472+ auto dstData = reinterpret_cast<uint16 *>(d->offset);
4473+ const int32 viewPrintTop = d->top_row - 1;
4474+ const int32 viewPrintBottom = d->bottom_row - 1;
4475+
4476+ for (int32 cellY = 0; cellY < clippedCellsH; ++cellY) {
4477+ const int32 srcY = sy1 + cellY * srcCellStepY;
4478+ const int32 dstY = dy1 + cellY * dstStepY;
4479+ if ((dstY < 0) || (dstY >= d->height))
4480+ continue;
4481+ if ((dstY < viewPrintTop) || (dstY > viewPrintBottom))
4482+ continue;
4483+
4484+ for (int32 cellX = 0; cellX < clippedCellsW; ++cellX) {
4485+ const int32 srcX = sx1 + cellX * srcCellStepX;
4486+ const int32 dstX = dx1 + cellX * dstStepX;
4487+ if ((dstX < 0) || (dstX >= d->width))
4488+ continue;
4489+
4490+ const uint16 cell = srcData[srcY * s->width + srcX];
4491+ if (uint32(cell) != textTransparentColor)
4492+ dstData[dstY * d->width + dstX] = cell;
4493+ }
4494+ }
4495+ } else {
4496+ const int32 dstClipX1 = d->clipping_or_scaling ? d->view_x1 : 0;
4497+ const int32 dstClipY1 = d->clipping_or_scaling ? d->view_y1 : 0;
4498+ const int32 dstClipX2 = d->clipping_or_scaling ? d->view_x2 : (d->width - 1);
4499+ const int32 dstClipY2 = d->clipping_or_scaling ? d->view_y2 : (d->height - 1);
4500+
4501+ for (int32 cellY = 0; cellY < clippedCellsH; ++cellY) {
4502+ const int32 srcY = sy1 + cellY * srcCellStepY;
4503+
4504+ for (int32 cellX = 0; cellX < clippedCellsW; ++cellX) {
4505+ const int32 srcX = sx1 + cellX * srcCellStepX;
4506+ const uint16 cell = srcData[srcY * s->width + srcX];
4507+ if (uint32(cell) == textTransparentColor)
4508+ continue;
4509+
4510+ const uint8 c = uint8(cell & 0xFF);
4511+ const uint8 attr = uint8(cell >> 8);
4512+ const uint8 fc = uint8(attr & 0x0F);
4513+ const uint8 bc = uint8(((attr >> 4) & 7) + ((attr >> 7) << 3));
4514+
4515+ const uint8 *glyphBits;
4516+ uint8 *ttfGlyphData = nullptr;
4517+
4518+ if (s->font >= 32) {
4519+ int32 rt_w, rt_h;
4520+ if (!FontRenderTextASCII(font[s->font], &c, 1, FONT_RENDER_MONOCHROME, &ttfGlyphData, &rt_w, &rt_h))
4521+ continue;
4522+ glyphBits = ttfGlyphData;
4523+ } else {
4524+ switch (fontHeight) {
4525+ case 8:
4526+ glyphBits = &charset8x8[c][0][0];
4527+ break;
4528+ case 14:
4529+ glyphBits = &charset8x16[c][1][0];
4530+ break;
4531+ default:
4532+ glyphBits = &charset8x16[c][0][0];
4533+ break;
4534+ }
4535+ }
4536+
4537+ if (d->bits_per_pixel == 32) {
4538+ for (int32 py = 0; py < fontHeight; ++py) {
4539+ const int32 dstY = dy1 + dstStepY * (cellY * fontHeight + py);
4540+ if ((dstY < dstClipY1) || (dstY > dstClipY2))
4541+ continue;
4542+
4543+ for (int32 px = 0; px < fontWidth; ++px) {
4544+ const int32 dstX = dx1 + dstStepX * (cellX * fontWidth + px);
4545+ if ((dstX < dstClipX1) || (dstX > dstClipX2))
4546+ continue;
4547+
4548+ const uint8 bit = glyphBits[py * fontWidth + px];
4549+ d->offset32[dstY * d->width + dstX] = bit ? s->pal[fc] : s->pal[bc];
4550+ }
4551+ }
4552+ } else {
4553+ for (int32 py = 0; py < fontHeight; ++py) {
4554+ const int32 dstY = dy1 + dstStepY * (cellY * fontHeight + py);
4555+ if ((dstY < dstClipY1) || (dstY > dstClipY2))
4556+ continue;
4557+
4558+ for (int32 px = 0; px < fontWidth; ++px) {
4559+ const int32 dstX = dx1 + dstStepX * (cellX * fontWidth + px);
4560+ if ((dstX < dstClipX1) || (dstX > dstClipX2))
4561+ continue;
4562+
4563+ const uint8 bit = glyphBits[py * fontWidth + px];
4564+ d->offset[dstY * d->width + dstX] = bit ? fc : bc;
4565+ }
4566+ }
4567+ }
4568+
4569+ if (ttfGlyphData) {
4570+ free(ttfGlyphData);
4571+ ttfGlyphData = nullptr;
4572+ }
4573+ }
4574+ }
4575+ }
4576+
4577+ return;
4578+ }
4579+
43614580 if (use_hardware) {
43624581 // calculate omitted co-ordinates
43634582 if ((passed & 4) && (passed & 512))
@@ -7078,9 +7297,6 @@ uint8 *qbg_visual_page_offset;
70787297int32 qbg_color_assign[256]; // for modes with quasi palettes!
70797298uint32 pal_mode10[2][9];
70807299
7081- uint8 charset8x8[256][8][8];
7082- uint8 charset8x16[256][16][8];
7083-
70847300int32 lineclip_draw; // 1=draw, 0=don't draw
70857301int32 lineclip_x1, lineclip_y1, lineclip_x2, lineclip_y2;
70867302int32 lineclip_skippixels; // the number of pixels from x1,y1 which won't be drawn
@@ -19060,9 +19276,23 @@ void sub__clearcolor(uint32 c, int32 i, int32 passed) {
1906019276 im = &img[i];
1906119277 // text?
1906219278 if (im->text) {
19063- if ((passed & 1) && (!(passed & 2)))
19064- return; // you can disable clearcolor using _CLEARCOLOR _NONE in text modes
19065- error(5);
19279+ if (passed & 1) {
19280+ if (passed & 2) {
19281+ error(5);
19282+ return;
19283+ } // invalid options
19284+ im->transparent_color = -1; // _CLEARCOLOR _NONE: disable transparency
19285+ return;
19286+ }
19287+ if (!(passed & 2)) {
19288+ error(5);
19289+ return;
19290+ } // invalid options
19291+ if (c > 65535) {
19292+ error(5);
19293+ return;
19294+ } // invalid cell value for a text surface
19295+ im->transparent_color = (int32)c;
1906619296 return;
1906719297 }
1906819298 // palette?
@@ -19346,7 +19576,7 @@ int32 func__clearcolor(int32 i, int32 passed) {
1934619576 i = write_page_index;
1934719577 }
1934819578 if (img[i].text)
19349- return -1;
19579+ return img[i].transparent_color; // -1 = none, 0-65535 = cell value
1935019580 if (img[i].compatible_mode == 32)
1935119581 return 0;
1935219582 return img[i].transparent_color;
0 commit comments