diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index cf7d0343b343d471cca00ee0b8ba040cbe3002c1..eb4e7d33ca9f9a3319b89564fbbdd70df8b0e711 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -90,7 +90,8 @@ enum patch { // Methods patch_getPixel, patch_setPixel, - patch_copy + patch_copy, + patch_clear }; static const char *const patch_opt[] = { "valid", @@ -103,6 +104,7 @@ static const char *const patch_opt[] = { "getPixel", "setPixel", "copy", + "clear", NULL}; static int patch_fields_ref = LUA_NOREF; @@ -378,8 +380,17 @@ static int lib_patch_copy(lua_State *L) int src_img_width = -1; int src_img_height = -1; + patch_t *src_patch = NULL; + if (!lua_istable(L, 2)) - return luaL_typerror(L, 2, "table"); + { + src_patch = *((patch_t **)luaL_checkudata(L, 2, META_PATCH)); + if (!src_patch) + return LUA_ErrInvalid(L, "patch_t"); + + src_img_width = src_patch->width; + src_img_height = src_patch->height; + } else { src_img_width = luaL_optinteger(L, 3, patch->width); @@ -387,17 +398,17 @@ static int lib_patch_copy(lua_State *L) lua_remove(L, 3); lua_remove(L, 3); - } - if (src_img_width <= 0) - return luaL_error(L, "invalid source image width"); - if (src_img_height <= 0) - return luaL_error(L, "invalid source image height"); + if (src_img_width <= 0) + return luaL_error(L, "invalid source image width"); + if (src_img_height <= 0) + return luaL_error(L, "invalid source image height"); + } int sx = luaL_optinteger(L, 3, 0); int sy = luaL_optinteger(L, 4, 0); - int sw = luaL_optinteger(L, 5, patch->width); - int sh = luaL_optinteger(L, 6, patch->height); + int sw = luaL_optinteger(L, 5, src_img_width); + int sh = luaL_optinteger(L, 6, src_img_height); int dx = luaL_optinteger(L, 7, 0); int dy = luaL_optinteger(L, 8, 0); boolean copy_transparent = lua_optboolean(L, 9); @@ -407,12 +418,12 @@ static int lib_patch_copy(lua_State *L) if (sh <= 0) return luaL_error(L, "invalid copy rect height"); - size_t size = (unsigned)(src_img_width * src_img_height); - - img_prepare_buffer(size); - if (lua_istable(L, 2)) { + size_t size = (unsigned)(src_img_width * src_img_height); + + img_prepare_buffer(size); + if (lua_objlen(L, 2) + 1 != size) return luaL_error(L, "invalid table length"); @@ -422,9 +433,27 @@ static int lib_patch_copy(lua_State *L) img_get_pixels_from_table(L, size); lua_pop(L, 2); + + Patch_UpdatePixels(patch, patch_update_buffer, src_img_width, src_img_height, PICFMT_FLAT16, sx, sy, sw, sh, dx, dy, copy_transparent); + } + else + { + V_DrawIntoPatch(patch, src_patch, dx << FRACBITS, dy << FRACBITS, FRACUNIT, FRACUNIT, 0, NULL, sx << FRACBITS, sy << FRACBITS, sw << FRACBITS, sh << FRACBITS, copy_transparent); } - Patch_UpdatePixels(patch, patch_update_buffer, src_img_width, src_img_height, PICFMT_FLAT16, sx, sy, sw, sh, dx, dy, copy_transparent); + return 0; +} + +static int lib_patch_clear(lua_State *L) +{ + patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH)); + if (!patch) + return LUA_ErrInvalid(L, "patch_t"); + + if (patch->type != PATCH_TYPE_DYNAMIC) + return luaL_error(L, "cannot modify a static patch"); + + Patch_Clear(patch); return 0; } @@ -471,6 +500,9 @@ static int patch_get(lua_State *L) case patch_copy: lua_pushcfunction(L, lib_patch_copy); break; + case patch_clear: + lua_pushcfunction(L, lib_patch_clear); + break; } return 1; } diff --git a/src/r_patch.c b/src/r_patch.c index 4c4cdc01978c881d3426bc357398d72d3debcb01..af66ee5daa67f80d161bb3bdc3e41287d60f5fe9 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -19,7 +19,7 @@ #include "hardware/hw_glob.h" #endif -static void Patch_MarkDirtyRect(dynamicpatch_t *dpatch, INT16 left, INT16 top, INT16 right, INT16 bottom); +static boolean Patch_CheckDirtyRect(dynamicpatch_t *dpatch); static void Patch_ClearDirtyRect(dynamicpatch_t *dpatch); patch_t *Patch_Create(INT16 width, INT16 height) @@ -51,8 +51,15 @@ patch_t *Patch_CreateDynamic(INT16 width, INT16 height) return patch; } -static void Patch_MarkDirtyRect(dynamicpatch_t *dpatch, INT16 left, INT16 top, INT16 right, INT16 bottom) +void Patch_MarkDirtyRect(patch_t *patch, INT16 left, INT16 top, INT16 right, INT16 bottom) { + dynamicpatch_t *dpatch = (dynamicpatch_t*)patch; + + left = max(0, left); + right = min(right, patch->width); + top = max(0, top); + bottom = min(bottom, patch->height); + if (left < dpatch->rect_dirty[0]) dpatch->rect_dirty[0] = left; if (top < dpatch->rect_dirty[1]) @@ -63,6 +70,37 @@ static void Patch_MarkDirtyRect(dynamicpatch_t *dpatch, INT16 left, INT16 top, I dpatch->rect_dirty[3] = bottom; } +static boolean Patch_CheckDirtyRect(dynamicpatch_t *dpatch) +{ + patch_t *patch = (patch_t*)dpatch; + + // left + if (dpatch->rect_dirty[0] < 0 + || dpatch->rect_dirty[0] > patch->width + || dpatch->rect_dirty[0] >= dpatch->rect_dirty[2]) // right + return false; + + // top + if (dpatch->rect_dirty[1] < 0 + || dpatch->rect_dirty[1] > patch->height + || dpatch->rect_dirty[1] >= dpatch->rect_dirty[3]) // bottom + return false; + + // right + if (dpatch->rect_dirty[2] > patch->width + || dpatch->rect_dirty[2] < 0 + || dpatch->rect_dirty[2] <= dpatch->rect_dirty[0]) // left + return false; + + // bottom + if (dpatch->rect_dirty[3] > patch->height + || dpatch->rect_dirty[3] < 0 + || dpatch->rect_dirty[3] <= dpatch->rect_dirty[1]) // top + return false; + + return true; +} + static void Patch_ClearDirtyRect(dynamicpatch_t *dpatch) { dpatch->rect_dirty[0] = INT16_MAX; @@ -83,6 +121,53 @@ static void Patch_InitDynamicColumns(patch_t *patch) } } +void Patch_Clear(patch_t *patch) +{ + if (patch->type != PATCH_TYPE_DYNAMIC) + return; + + size_t total_pixels = patch->width * patch->height; + + memset(patch->pixels, 0, total_pixels * sizeof(UINT8)); + + for (INT32 x = 0; x < patch->width; x++) + patch->columns[x].num_posts = 0; + + dynamicpatch_t *dpatch = (dynamicpatch_t*)patch; + + memset(dpatch->pixels_opaque, 0, BIT_ARRAY_SIZE(total_pixels)); + + dpatch->is_dirty = dpatch->update_columns = true; +} + +void Patch_ClearRect(patch_t *patch, INT16 x, INT16 y, INT16 width, INT16 height) +{ + if (patch->type != PATCH_TYPE_DYNAMIC) + return; + + dynamicpatch_t *dpatch = (dynamicpatch_t*)patch; + + for (INT16 dy = y; dy < y + height; dy++) + { + if (dy < 0) + continue; + else if (dy >= patch->height) + break; + + for (INT16 dx = x; dx < x + width; dx++) + { + if (dx < 0) + continue; + else if (dx >= patch->width) + break; + + size_t position = (dx * patch->height) + dy; + unset_bit_array(dpatch->pixels_opaque, position); + dpatch->is_dirty = dpatch->update_columns = true; + } + } +} + patch_t *Patch_CreateFromDoomPatch(softwarepatch_t *source) { patch_t *patch = (patch_t*)Patch_Create(0, 0); @@ -337,16 +422,18 @@ void Patch_SetPixel(patch_t *patch, void *pixel, pictureformat_t informat, INT32 { // No pixel in this position, so columns need to be rebuilt if (!in_bit_array(dpatch->pixels_opaque, position)) + { + set_bit_array(dpatch->pixels_opaque, position); dpatch->update_columns = true; + } - set_bit_array(dpatch->pixels_opaque, position); writePixelFunc(&patch->pixels[position], pixel); dpatch->is_dirty = true; } if (dpatch->is_dirty) - Patch_MarkDirtyRect(dpatch, x, y, x + 1, y + 1); + Patch_MarkDirtyRect(patch, x, y, x + 1, y + 1); } void Patch_UpdatePixels(patch_t *patch, @@ -409,12 +496,7 @@ void Patch_UpdatePixels(patch_t *patch, dynamicpatch_t *dpatch = (dynamicpatch_t *)patch; - INT32 dest_x1 = max(0, dx); - INT32 dest_x2 = min(dx + sw, patch->width); - INT32 dest_y1 = max(0, dy); - INT32 dest_y2 = min(dy + sh, patch->height); - - Patch_MarkDirtyRect(dpatch, dest_x1, dest_y1, dest_x2, dest_y2); + Patch_MarkDirtyRect(patch, dx, dy, dx + sw, dy + sh); for (INT32 x = dx; x < dx + sw; x++, sx++) { @@ -453,9 +535,15 @@ void Patch_UpdatePixels(patch_t *patch, } else { - set_bit_array(dpatch->pixels_opaque, position); + if (!in_bit_array(dpatch->pixels_opaque, position)) + { + set_bit_array(dpatch->pixels_opaque, position); + dpatch->update_columns = true; + } + writePixelFunc(&patch->pixels[position], input); - dpatch->update_columns = dpatch->is_dirty = true; + + dpatch->is_dirty = true; } } } @@ -482,18 +570,21 @@ void Patch_DoDynamicUpdate(patch_t *patch) if (patch->columns == NULL) Patch_InitDynamicColumns(patch); - if (dpatch->update_columns) + if (Patch_CheckDirtyRect(dpatch)) { - for (INT32 x = dpatch->rect_dirty[0]; x < dpatch->rect_dirty[2]; x++) - Patch_RebuildColumn(&patch->columns[x], x, patch->height, dpatch->pixels_opaque); - } - - Patch_FreeMiscData(patch); + if (dpatch->update_columns) + { + for (INT32 x = dpatch->rect_dirty[0]; x < dpatch->rect_dirty[2]; x++) + Patch_RebuildColumn(&patch->columns[x], x, patch->height, dpatch->pixels_opaque); + } #ifdef HWRENDER - if (patch->hardware) - HWR_UpdatePatchRegion(patch, dpatch->rect_dirty[0], dpatch->rect_dirty[1], dpatch->rect_dirty[2], dpatch->rect_dirty[3]); + if (patch->hardware) + HWR_UpdatePatchRegion(patch, dpatch->rect_dirty[0], dpatch->rect_dirty[1], dpatch->rect_dirty[2], dpatch->rect_dirty[3]); #endif + } + + Patch_FreeMiscData(patch); dpatch->is_dirty = false; dpatch->update_columns = false; diff --git a/src/r_patch.h b/src/r_patch.h index 9ee5892fad59c319601c20cfb3770aacd540c398..0a5c1c0bbc72a65cdb0c3584806f469d6b8ba3ac 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -37,6 +37,10 @@ void Patch_UpdatePixels(patch_t *patch, boolean Patch_NeedsUpdate(patch_t *patch); void Patch_DoDynamicUpdate(patch_t *patch); +void Patch_MarkDirtyRect(patch_t *dpatch, INT16 left, INT16 top, INT16 right, INT16 bottom); + +void Patch_Clear(patch_t *patch); +void Patch_ClearRect(patch_t *patch, INT16 x, INT16 y, INT16 width, INT16 height); bitarray_t *Patch_GetOpaqueRegions(patch_t *patch); diff --git a/src/v_video.c b/src/v_video.c index 150bd0d338754b574fbd44b60193933b1b6a5d0a..176fd28883afc52aee65657241348779456f68ec 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1064,6 +1064,152 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN } } +void V_DrawIntoPatch(patch_t *dest_patch, patch_t *src_patch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 flags, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h, boolean copy_transparent) +{ + if (dest_patch->type != PATCH_TYPE_DYNAMIC) + return; + + dynamicpatch_t *dpatch = (dynamicpatch_t *)dest_patch; + + if (Patch_NeedsUpdate(src_patch)) + Patch_DoDynamicUpdate(src_patch); + + if (src_patch->columns == NULL) + return; + + if (sx < 0) + { + w += sx; + sx = 0; + } + if (sy < 0) + { + h += sy; + sy = 0; + } + + if (sx >= src_patch->width << FRACBITS || w <= 0) + return; + if (sy >= src_patch->height << FRACBITS || h <= 0) + return; + + UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t) = standardpdraw; + + v_translevel = NULL; + v_colormap = NULL; + + UINT32 alphalevel = ((flags & V_ALPHAMASK) >> V_ALPHASHIFT); + UINT32 blendmode = ((flags & V_BLENDMASK) >> V_BLENDSHIFT); + + if (alphalevel || blendmode) + { + if (alphalevel >= 10) + return; // invis + + if (alphalevel || blendmode) + { + v_translevel = R_GetBlendTable(blendmode+1, alphalevel); + patchdrawfunc = translucentpdraw; + } + } + + if (colormap) + { + v_colormap = colormap; + patchdrawfunc = v_translevel ? transmappedpdraw : mappedpdraw; + } + + fixed_t fdup = pscale; + fixed_t vdup = vscale; + fixed_t colfrac = FixedDiv(FRACUNIT, fdup); + fixed_t rowfrac = FixedDiv(FRACUNIT, vdup); + + fixed_t offsetx = 0, offsety = 0; + + // left offset + if (flags & V_FLIP) + offsetx = FixedMul((src_patch->width - src_patch->leftoffset)<<FRACBITS, pscale) + 1; + else + offsetx = FixedMul(src_patch->leftoffset<<FRACBITS, pscale); + + // top offset + offsety = FixedMul(src_patch->topoffset<<FRACBITS, vscale); + + // Subtract the offsets from x/y positions + x -= offsetx; + y -= offsety; + + x >>= FRACBITS; + y >>= FRACBITS; + + fixed_t pwidth = src_patch->width; + if (pscale != FRACUNIT) + pwidth = (pwidth * pscale) >> FRACBITS; + + int dest_x = x; + + Patch_MarkDirtyRect(dest_patch, x, y, x + (w >> FRACBITS), y + (h >> FRACBITS)); + + if (copy_transparent) + Patch_ClearRect(dest_patch, x, y, w >> FRACBITS, h >> FRACBITS); + + if (dest_patch->pixels == NULL) + dest_patch->pixels = Z_Calloc(dest_patch->width * dest_patch->height, PU_PATCH_DATA, NULL); + + for (fixed_t col = sx; (col>>FRACBITS) < src_patch->width && (col - sx) < w; col += colfrac, dest_x++) + { + if (dest_x < 0) + continue; + else if (dest_x >= dest_patch->width) + break; + + unsigned texturecolumn = col >> FRACBITS; + if (flags & V_FLIP) + texturecolumn = src_patch->width - 1 - texturecolumn; + + column_t *column = &src_patch->columns[texturecolumn]; + + for (unsigned i = 0; i < column->num_posts; i++) + { + post_t *post = &column->posts[i]; + INT32 topdelta = post->topdelta << FRACBITS; + UINT8 *source = column->pixels + post->data_offset; + + int dest_y = y; + + fixed_t ofs; + if (topdelta - sy > 0) + { + dest_y = y + FixedInt(FixedMul(topdelta - sy, vdup)); + ofs = 0; + } + else + ofs = sy - topdelta; + + for (; dest_y < dest_patch->height && (size_t)(ofs>>FRACBITS) < post->length && ((ofs - sy) + topdelta) < h; ofs += rowfrac, dest_y++) + { + if (dest_y < 0) + continue; + + size_t position = (dest_x * dest_patch->height) + dest_y; + if (!in_bit_array(dpatch->pixels_opaque, position)) + { + set_bit_array(dpatch->pixels_opaque, position); + dpatch->update_columns = true; + } + + UINT8 *dest = &dest_patch->pixels[position]; + *dest = patchdrawfunc(dest, source, ofs); + + dpatch->is_dirty = true; + } + + if (dest_y >= dest_patch->height) + break; + } + } +} + // // V_DrawContinueIcon // Draw a mini player! If we can, that is. Otherwise we draw a star. diff --git a/src/v_video.h b/src/v_video.h index ff03836b597bec40f8712f534afef32bd46a2db8..34031d8da63460c869c4a366b97759f50f32138f 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -172,6 +172,7 @@ void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue); #define V_DrawFixedPatch(x,y,sc,s,p,c) V_DrawStretchyFixedPatch(x,y,sc,sc,s,p,c) void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap); void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); +void V_DrawIntoPatch(patch_t *dest_patch, patch_t *src_patch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 flags, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h, boolean copy_transparent); void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor);