Skip to content

Commit b224127

Browse files
authored
_PUTIMAGE text surface support (QB64-Phoenix-Edition#709)
* Initial text surface rendering support * Enhance clearcolor handling for text surfaces * Add tests for _putimage text surface functionality * Add support for TTF fonts for _putimage text surfaces
1 parent 81ca033 commit b224127

3 files changed

Lines changed: 1033 additions & 12 deletions

File tree

internal/c/libqb.cpp

Lines changed: 242 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
40704073
void 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;
70787297
int32 qbg_color_assign[256]; // for modes with quasi palettes!
70797298
uint32 pal_mode10[2][9];
70807299

7081-
uint8 charset8x8[256][8][8];
7082-
uint8 charset8x16[256][16][8];
7083-
70847300
int32 lineclip_draw; // 1=draw, 0=don't draw
70857301
int32 lineclip_x1, lineclip_y1, lineclip_x2, lineclip_y2;
70867302
int32 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

Comments
 (0)