mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-02 14:22:08 +02:00
[N-Gage] Fix image transformations, add color keying, and improve overall rendering performance. Fixes #15427
[N-Gage] Correct SDL_FLIP_HORIZONTAL for sprite sheet textures - ApplyFlip: swap toward midpoint when dest == source to avoid overwriting pixels before they are read - CopyEx: restore original bitmap pixels after BitBlt so the texture is not permanently mutated across frames - CopyEx: extract srcrect sub-region before transforming so flip/ rotate/scale operate on the correct pixels, not the full texture [N-Gage] Use scratch bitmap in CopyEx to avoid mutating source texture. Replace the write-then-restore pattern on the source texture with a persistent iScratchBitmap. The transform pipeline operates entirely within iPixelBufferA/B; the final result is copied into iScratchBitmap and BitBlt reads from there. The source texture is never written to. [N-Gage] Fix Copy() for render-target textures by using direct BitBlt Bypass pixel readback for SDL_TEXTUREACCESS_TARGET textures in CRenderer::Copy(). Reading raw pixels via DataAddress() on a server-side bitmap is unreliable; use BitBlt directly instead. [N-Gage] Implement color-key masking using Symbian's BitBltMasked. This implementation works fine, but relies on a platform specific property. This isn't ideal. Todo: Add SDL_PIXELFORMAT_ARGB4444 to the N-Gage's rendering back-end. [N-Gage] Avoid using BitBltMasked to improve performance. Much better! Write only non-color-key source pixels directly into the destination bitmap preserving existing destination pixels wherever the color key matches. This replaces BitBltMasked and avoids the cost of building an EGray2 mask. [N-Gage] Remove redundant function call since we're not using BitBltMasked() anymore. [N-Gage] Remove SDL_PROP_TEXTURE_NGAGE_COLOR_KEY_NUMBER - Promote SDL_PIXELFORMAT_XRGB4444 to SDL_PIXELFORMAT_ARGB4444 instead. - Remove now unused functions.
This commit is contained in:
@@ -1799,6 +1799,13 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (surface->format == SDL_PIXELFORMAT_XRGB4444) {
|
||||
for (i = 0; i < renderer->num_texture_formats; ++i) {
|
||||
if (renderer->texture_formats[i] == SDL_PIXELFORMAT_ARGB4444) {
|
||||
format = SDL_PIXELFORMAT_ARGB4444;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Exact match would be fine
|
||||
@@ -1893,7 +1900,8 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
|
||||
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h);
|
||||
texture = SDL_CreateTextureWithProperties(renderer, props);
|
||||
|
||||
texture = SDL_CreateTextureWithProperties(renderer, props);
|
||||
SDL_DestroyProperties(props);
|
||||
if (!texture) {
|
||||
return NULL;
|
||||
|
||||
@@ -116,6 +116,7 @@ static bool NGAGE_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
|
||||
renderer->npot_texture_wrap_unsupported = true;
|
||||
|
||||
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB4444);
|
||||
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB4444);
|
||||
SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 1024);
|
||||
SDL_SetHintWithPriority(SDL_HINT_RENDER_LINE_METHOD, "2", SDL_HINT_OVERRIDE);
|
||||
|
||||
@@ -162,6 +163,12 @@ static bool NGAGE_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
|
||||
|
||||
texture->internal = data;
|
||||
|
||||
// ARGB4444 textures are color-keyed: alpha=0 pixels are transparent.
|
||||
if (texture->format == SDL_PIXELFORMAT_ARGB4444) {
|
||||
data->has_color_key = true;
|
||||
data->mask_dirty = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -454,7 +461,7 @@ static bool NGAGE_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co
|
||||
}
|
||||
|
||||
const int bytes_per_pixel = 2;
|
||||
const int bitmap_pitch = texture->w * bytes_per_pixel;
|
||||
const int bitmap_pitch = NGAGE_GetBitmapScanLineLength(phdata);
|
||||
|
||||
const Uint8 *src = (const Uint8 *)pixels;
|
||||
dst += rect->y * bitmap_pitch + rect->x * bytes_per_pixel;
|
||||
@@ -466,6 +473,8 @@ static bool NGAGE_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co
|
||||
dst += bitmap_pitch;
|
||||
}
|
||||
|
||||
phdata->mask_dirty = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -483,7 +492,7 @@ static bool NGAGE_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons
|
||||
}
|
||||
|
||||
const int bytes_per_pixel = 2;
|
||||
const int bitmap_pitch = texture->w * bytes_per_pixel;
|
||||
const int bitmap_pitch = NGAGE_GetBitmapScanLineLength(phdata);
|
||||
|
||||
*pixels = (void *)(data + rect->y * bitmap_pitch + rect->x * bytes_per_pixel);
|
||||
*pitch = bitmap_pitch;
|
||||
@@ -492,6 +501,10 @@ static bool NGAGE_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons
|
||||
|
||||
static void NGAGE_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
|
||||
{
|
||||
NGAGE_TextureData *phdata = (NGAGE_TextureData *)texture->internal;
|
||||
if (phdata) {
|
||||
phdata->mask_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool NGAGE_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
|
||||
|
||||
@@ -75,6 +75,10 @@ void NGAGE_DestroyTextureData(NGAGE_TextureData *data)
|
||||
delete data->device;
|
||||
data->device = NULL;
|
||||
}
|
||||
if (data->mask_bitmap) {
|
||||
delete data->mask_bitmap;
|
||||
data->mask_bitmap = NULL;
|
||||
}
|
||||
delete data->bitmap;
|
||||
data->bitmap = NULL;
|
||||
}
|
||||
@@ -88,6 +92,14 @@ void *NGAGE_GetBitmapDataAddress(NGAGE_TextureData *data)
|
||||
return data->bitmap->DataAddress();
|
||||
}
|
||||
|
||||
int NGAGE_GetBitmapScanLineLength(NGAGE_TextureData *data)
|
||||
{
|
||||
if (!data || !data->bitmap) {
|
||||
return 0;
|
||||
}
|
||||
return (int)CFbsBitmap::ScanLineLength(data->bitmap->SizeInPixels().iWidth, EColor4K);
|
||||
}
|
||||
|
||||
void NGAGE_DrawLines(NGAGE_Vertex *verts, const int count)
|
||||
{
|
||||
gRenderer->DrawLines(verts, count);
|
||||
@@ -150,7 +162,7 @@ CRenderer *CRenderer::NewL()
|
||||
return self;
|
||||
}
|
||||
|
||||
CRenderer::CRenderer() : iRenderer(0), iDirectScreen(0), iScreenGc(0), iWsSession(), iWsWindowGroup(), iWsWindowGroupID(0), iWsWindow(), iWsScreen(0), iWsEventStatus(), iWsEvent(), iShowFPS(EFalse), iFPS(0), iFont(0), iCurrentRenderTarget(0), iPixelBufferA(0), iPixelBufferB(0), iPixelBufferSize(0), iPointsBuffer(0), iPointsBufferSize(0) {}
|
||||
CRenderer::CRenderer() : iRenderer(0), iDirectScreen(0), iScreenGc(0), iWsSession(), iWsWindowGroup(), iWsWindowGroupID(0), iWsWindow(), iWsScreen(0), iWsEventStatus(), iWsEvent(), iShowFPS(EFalse), iFPS(0), iFont(0), iCurrentRenderTarget(0), iPixelBufferA(0), iPixelBufferB(0), iPixelBufferSize(0), iScratchBitmap(0), iMaskBitmap(0), iPointsBuffer(0), iPointsBufferSize(0) {}
|
||||
|
||||
CRenderer::~CRenderer()
|
||||
{
|
||||
@@ -159,6 +171,10 @@ CRenderer::~CRenderer()
|
||||
|
||||
SDL_free(iPixelBufferA);
|
||||
SDL_free(iPixelBufferB);
|
||||
delete iScratchBitmap;
|
||||
iScratchBitmap = 0;
|
||||
delete iMaskBitmap;
|
||||
iMaskBitmap = 0;
|
||||
delete[] iPointsBuffer;
|
||||
}
|
||||
|
||||
@@ -326,18 +342,55 @@ bool CRenderer::Copy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rec
|
||||
}
|
||||
|
||||
SDL_FColor *c = &texture->color;
|
||||
int w = texture->w;
|
||||
int h = texture->h;
|
||||
const int bytes_per_pixel = 2;
|
||||
int pitch = w * bytes_per_pixel;
|
||||
void *source = phdata->bitmap->DataAddress();
|
||||
void *dest;
|
||||
|
||||
if (!source) {
|
||||
int sw = srcrect->w;
|
||||
int sh = srcrect->h;
|
||||
|
||||
// Fast path: render target texture with no color mod.
|
||||
// BitBlt directly from its bitmap — DataAddress() is unreliable
|
||||
// for bitmaps that have been drawn into via a CFbsBitGc.
|
||||
bool no_color_mod = (c->a == 1.f && c->r == 1.f && c->g == 1.f && c->b == 1.f);
|
||||
float sx, sy;
|
||||
SDL_GetRenderScale(renderer, &sx, &sy);
|
||||
bool no_scale = (sx == 1.f && sy == 1.f);
|
||||
|
||||
SDL_BlendMode blend;
|
||||
SDL_GetTextureBlendMode(texture, &blend);
|
||||
bool no_color_key = (blend != SDL_BLENDMODE_BLEND);
|
||||
|
||||
if (phdata->gc && no_color_mod && no_scale && no_color_key) {
|
||||
CFbsBitGc *gc = GetCurrentGc();
|
||||
if (gc) {
|
||||
TRect aSource(TPoint(srcrect->x, srcrect->y), TSize(sw, sh));
|
||||
TPoint aDest(dstrect->x, dstrect->y);
|
||||
gc->BitBlt(aDest, phdata->bitmap, aSource);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fast path: color-key with no color mod and no scale.
|
||||
// Blit directly from the source bitmap into the destination, skipping transparent pixels.
|
||||
if (no_color_mod && no_scale && !no_color_key && phdata->has_color_key) {
|
||||
void *tex_data_ck = phdata->bitmap->DataAddress();
|
||||
CFbsBitmap *dst_bmp = GetCurrentBitmap();
|
||||
if (dst_bmp && tex_data_ck) {
|
||||
int tex_stride_ck = CFbsBitmap::ScanLineLength(phdata->bitmap->SizeInPixels().iWidth, EColor4K) / 2;
|
||||
TUint16 *src_base = static_cast<TUint16 *>(tex_data_ck) + srcrect->y * tex_stride_ck + srcrect->x;
|
||||
BlitWithAlphaKey(dst_bmp, dstrect->x, dstrect->y, src_base, sw, sh, tex_stride_ck);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int src_pitch = sw * bytes_per_pixel;
|
||||
int tex_pitch = CFbsBitmap::ScanLineLength(texture->w, EColor4K);
|
||||
|
||||
void *tex_data = phdata->bitmap->DataAddress();
|
||||
if (!tex_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TInt required_size = pitch * h;
|
||||
TInt required_size = src_pitch * sh;
|
||||
if (required_size > iPixelBufferSize) {
|
||||
void *new_buffer_a = SDL_realloc(iPixelBufferA, required_size);
|
||||
if (!new_buffer_a) {
|
||||
@@ -354,39 +407,82 @@ bool CRenderer::Copy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rec
|
||||
iPixelBufferSize = required_size;
|
||||
}
|
||||
|
||||
dest = iPixelBufferA;
|
||||
|
||||
if (c->a != 1.f || c->r != 1.f || c->g != 1.f || c->b != 1.f) {
|
||||
ApplyColorMod(dest, source, pitch, w, h, texture->color);
|
||||
|
||||
source = dest;
|
||||
// Ensure scratch bitmap is allocated and large enough.
|
||||
if (!iScratchBitmap) {
|
||||
iScratchBitmap = new CFbsBitmap();
|
||||
if (!iScratchBitmap) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TSize scratch_size = iScratchBitmap->SizeInPixels();
|
||||
if (scratch_size.iWidth < sw || scratch_size.iHeight < sh) {
|
||||
iScratchBitmap->Reset();
|
||||
TInt err = iScratchBitmap->Create(TSize(sw, sh), EColor4K);
|
||||
if (err != KErrNone) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float sx;
|
||||
float sy;
|
||||
SDL_GetRenderScale(renderer, &sx, &sy);
|
||||
// Extract the srcrect region from the texture into buffer A.
|
||||
{
|
||||
TUint16 *tex_pixels = (TUint16 *)tex_data;
|
||||
TUint16 *buf_pixels = (TUint16 *)iPixelBufferA;
|
||||
int tex_pitch_u16 = tex_pitch / 2;
|
||||
for (int y = 0; y < sh; ++y) {
|
||||
TUint16 *src_row = tex_pixels + (srcrect->y + y) * tex_pitch_u16 + srcrect->x;
|
||||
TUint16 *dst_row = buf_pixels + y * sw;
|
||||
Mem::Copy(dst_row, src_row, src_pitch);
|
||||
}
|
||||
}
|
||||
|
||||
if (sx != 1.f || sy != 1.f) {
|
||||
void *source = iPixelBufferA;
|
||||
void *dest = iPixelBufferB;
|
||||
|
||||
if (!no_color_mod) {
|
||||
ApplyColorMod(dest, source, src_pitch, sw, sh, texture->color);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
if (!no_scale) {
|
||||
TFixed scale_x = Real2Fix(sx);
|
||||
TFixed scale_y = Real2Fix(sy);
|
||||
TFixed center_x = Int2Fix(w / 2);
|
||||
TFixed center_y = Int2Fix(h / 2);
|
||||
|
||||
dest == iPixelBufferA ? dest = iPixelBufferB : dest = iPixelBufferA;
|
||||
|
||||
ApplyScale(dest, source, pitch, w, h, center_x, center_y, scale_x, scale_y);
|
||||
|
||||
TFixed center_x = Int2Fix(sw / 2);
|
||||
TFixed center_y = Int2Fix(sh / 2);
|
||||
ApplyScale(dest, source, src_pitch, sw, sh, center_x, center_y, scale_x, scale_y);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
Mem::Copy(phdata->bitmap->DataAddress(), source, pitch * h);
|
||||
// Copy result into scratch bitmap and blit from there.
|
||||
// The source texture is never modified.
|
||||
{
|
||||
TUint16 *scratch_pixels = (TUint16 *)iScratchBitmap->DataAddress();
|
||||
TUint16 *res_pixels = (TUint16 *)source;
|
||||
int scratch_pitch_u16 = CFbsBitmap::ScanLineLength(iScratchBitmap->SizeInPixels().iWidth, EColor4K) / 2;
|
||||
|
||||
// Always copy all pixels into the scratch bitmap.
|
||||
for (int y = 0; y < sh; ++y) {
|
||||
TUint16 *dst_row = scratch_pixels + y * scratch_pitch_u16;
|
||||
TUint16 *src_row = res_pixels + y * sw;
|
||||
Mem::Copy(dst_row, src_row, src_pitch);
|
||||
}
|
||||
|
||||
if (phdata->bitmap) {
|
||||
CFbsBitGc *gc = GetCurrentGc();
|
||||
if (gc) {
|
||||
TRect aSource(TPoint(srcrect->x, srcrect->y), TSize(srcrect->w, srcrect->h));
|
||||
TRect aSource(TPoint(0, 0), TSize(sw, sh));
|
||||
TPoint aDest(dstrect->x, dstrect->y);
|
||||
gc->BitBlt(aDest, phdata->bitmap, aSource);
|
||||
|
||||
if (!no_color_key && phdata->has_color_key) {
|
||||
CFbsBitmap *dst_bmp = GetCurrentBitmap();
|
||||
if (dst_bmp) {
|
||||
BlitWithAlphaKey(dst_bmp, dstrect->x, dstrect->y, res_pixels, sw, sh, sw);
|
||||
}
|
||||
} else {
|
||||
gc->BitBlt(aDest, iScratchBitmap, aSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,18 +497,19 @@ bool CRenderer::CopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const NGAGE
|
||||
}
|
||||
|
||||
SDL_FColor *c = &texture->color;
|
||||
int w = texture->w;
|
||||
int h = texture->h;
|
||||
const int bytes_per_pixel = 2;
|
||||
int pitch = w * bytes_per_pixel;
|
||||
void *source = phdata->bitmap->DataAddress();
|
||||
void *dest;
|
||||
|
||||
if (!source) {
|
||||
int sw = copydata->srcrect.w;
|
||||
int sh = copydata->srcrect.h;
|
||||
int src_pitch = sw * bytes_per_pixel;
|
||||
int tex_pitch = CFbsBitmap::ScanLineLength(texture->w, EColor4K);
|
||||
|
||||
void *tex_data = phdata->bitmap->DataAddress();
|
||||
if (!tex_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TInt required_size = pitch * h;
|
||||
TInt required_size = src_pitch * sh;
|
||||
if (required_size > iPixelBufferSize) {
|
||||
void *new_buffer_a = SDL_realloc(iPixelBufferA, required_size);
|
||||
if (!new_buffer_a) {
|
||||
@@ -429,39 +526,96 @@ bool CRenderer::CopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const NGAGE
|
||||
iPixelBufferSize = required_size;
|
||||
}
|
||||
|
||||
dest = iPixelBufferA;
|
||||
// Ensure scratch bitmap is allocated and large enough for the srcrect.
|
||||
if (!iScratchBitmap) {
|
||||
iScratchBitmap = new CFbsBitmap();
|
||||
if (!iScratchBitmap) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TSize scratch_size = iScratchBitmap->SizeInPixels();
|
||||
if (scratch_size.iWidth < sw || scratch_size.iHeight < sh) {
|
||||
iScratchBitmap->Reset();
|
||||
TInt err = iScratchBitmap->Create(TSize(sw, sh), EColor4K);
|
||||
if (err != KErrNone) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the srcrect region from the texture into buffer A.
|
||||
{
|
||||
TUint16 *tex_pixels = (TUint16 *)tex_data;
|
||||
TUint16 *buf_pixels = (TUint16 *)iPixelBufferA;
|
||||
int tex_pitch_u16 = tex_pitch / 2;
|
||||
for (int y = 0; y < sh; ++y) {
|
||||
TUint16 *src_row = tex_pixels + (copydata->srcrect.y + y) * tex_pitch_u16 + copydata->srcrect.x;
|
||||
TUint16 *dst_row = buf_pixels + y * sw;
|
||||
Mem::Copy(dst_row, src_row, src_pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void *source = iPixelBufferA;
|
||||
void *dest = iPixelBufferB;
|
||||
|
||||
if (copydata->flip) {
|
||||
ApplyFlip(dest, source, pitch, w, h, copydata->flip);
|
||||
ApplyFlip(dest, source, src_pitch, sw, sh, copydata->flip);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
if (copydata->scale_x != 1.f || copydata->scale_y != 1.f) {
|
||||
dest == iPixelBufferA ? dest = iPixelBufferB : dest = iPixelBufferA;
|
||||
ApplyScale(dest, source, pitch, w, h, copydata->center.x, copydata->center.y, copydata->scale_x, copydata->scale_y);
|
||||
ApplyScale(dest, source, src_pitch, sw, sh, copydata->center.x, copydata->center.y, copydata->scale_x, copydata->scale_y);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
if (copydata->angle) {
|
||||
dest == iPixelBufferA ? dest = iPixelBufferB : dest = iPixelBufferA;
|
||||
ApplyRotation(dest, source, pitch, w, h, copydata->center.x, copydata->center.y, copydata->angle);
|
||||
ApplyRotation(dest, source, src_pitch, sw, sh, copydata->center.x, copydata->center.y, copydata->angle);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
if (c->a != 1.f || c->r != 1.f || c->g != 1.f || c->b != 1.f) {
|
||||
dest == iPixelBufferA ? dest = iPixelBufferB : dest = iPixelBufferA;
|
||||
ApplyColorMod(dest, source, pitch, w, h, texture->color);
|
||||
ApplyColorMod(dest, source, src_pitch, sw, sh, texture->color);
|
||||
void *tmp = source;
|
||||
source = dest;
|
||||
dest = tmp;
|
||||
}
|
||||
|
||||
Mem::Copy(phdata->bitmap->DataAddress(), source, pitch * h);
|
||||
// Copy the final result into the scratch bitmap and blit from there.
|
||||
// The source texture is never modified.
|
||||
{
|
||||
SDL_BlendMode blend;
|
||||
SDL_GetTextureBlendMode(texture, &blend);
|
||||
bool has_color_key = (blend == SDL_BLENDMODE_BLEND);
|
||||
|
||||
TUint16 *scratch_pixels = (TUint16 *)iScratchBitmap->DataAddress();
|
||||
TUint16 *res_pixels = (TUint16 *)source;
|
||||
int scratch_pitch_u16 = CFbsBitmap::ScanLineLength(iScratchBitmap->SizeInPixels().iWidth, EColor4K) / 2;
|
||||
|
||||
// Always copy all pixels into the scratch bitmap.
|
||||
for (int y = 0; y < sh; ++y) {
|
||||
TUint16 *dst_row = scratch_pixels + y * scratch_pitch_u16;
|
||||
TUint16 *src_row = res_pixels + y * sw;
|
||||
Mem::Copy(dst_row, src_row, src_pitch);
|
||||
}
|
||||
|
||||
if (phdata->bitmap) {
|
||||
CFbsBitGc *gc = GetCurrentGc();
|
||||
if (gc) {
|
||||
TRect aSource(TPoint(copydata->srcrect.x, copydata->srcrect.y), TSize(copydata->srcrect.w, copydata->srcrect.h));
|
||||
TRect aSource(TPoint(0, 0), TSize(sw, sh));
|
||||
TPoint aDest(copydata->dstrect.x, copydata->dstrect.y);
|
||||
gc->BitBlt(aDest, phdata->bitmap, aSource);
|
||||
|
||||
if (has_color_key && phdata->has_color_key) {
|
||||
CFbsBitmap *dst_bmp = GetCurrentBitmap();
|
||||
if (dst_bmp) {
|
||||
BlitWithAlphaKey(dst_bmp, copydata->dstrect.x, copydata->dstrect.y, res_pixels, sw, sh, sw);
|
||||
}
|
||||
} else {
|
||||
gc->BitBlt(aDest, iScratchBitmap, aSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,6 +838,14 @@ CFbsBitGc *CRenderer::GetCurrentGc()
|
||||
return iRenderer ? iRenderer->Gc() : NULL;
|
||||
}
|
||||
|
||||
CFbsBitmap *CRenderer::GetCurrentBitmap()
|
||||
{
|
||||
if (iCurrentRenderTarget && iCurrentRenderTarget->bitmap) {
|
||||
return iCurrentRenderTarget->bitmap;
|
||||
}
|
||||
return iRenderer ? iRenderer->Bitmap() : NULL;
|
||||
}
|
||||
|
||||
static SDL_Scancode ConvertScancode(int key)
|
||||
{
|
||||
SDL_Keycode keycode;
|
||||
|
||||
@@ -30,6 +30,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../SDL_sysrender.h"
|
||||
#include "SDL3/SDL_render.h"
|
||||
|
||||
typedef struct NGAGE_RendererData
|
||||
{
|
||||
@@ -63,6 +64,9 @@ typedef struct NGAGE_TextureData
|
||||
CFbsBitmap *bitmap;
|
||||
CFbsBitGc *gc;
|
||||
CFbsDevice *device;
|
||||
CFbsBitmap *mask_bitmap;
|
||||
bool has_color_key;
|
||||
bool mask_dirty;
|
||||
|
||||
} NGAGE_TextureData;
|
||||
|
||||
@@ -93,7 +97,8 @@ bool NGAGE_Copy(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcrect,
|
||||
bool NGAGE_CopyEx(SDL_Renderer *renderer, SDL_Texture *texture, NGAGE_CopyExData *copydata);
|
||||
bool NGAGE_CreateTextureData(NGAGE_TextureData *data, const int width, const int height, const int access);
|
||||
void NGAGE_DestroyTextureData(NGAGE_TextureData *data);
|
||||
void* NGAGE_GetBitmapDataAddress(NGAGE_TextureData *data);
|
||||
void *NGAGE_GetBitmapDataAddress(NGAGE_TextureData *data);
|
||||
int NGAGE_GetBitmapScanLineLength(NGAGE_TextureData *data);
|
||||
void NGAGE_DrawLines(NGAGE_Vertex *verts, const int count);
|
||||
void NGAGE_DrawPoints(NGAGE_Vertex *verts, const int count);
|
||||
void NGAGE_FillRects(NGAGE_Vertex *verts, const int count);
|
||||
|
||||
@@ -49,7 +49,8 @@ class CRenderer : public MDirectScreenAccess
|
||||
|
||||
// Render target management.
|
||||
void SetRenderTarget(NGAGE_TextureData *aTarget);
|
||||
CFbsBitGc* GetCurrentGc();
|
||||
CFbsBitGc *GetCurrentGc();
|
||||
CFbsBitmap *GetCurrentBitmap();
|
||||
|
||||
// Event handling.
|
||||
void DisableKeyBlocking();
|
||||
@@ -98,6 +99,8 @@ class CRenderer : public MDirectScreenAccess
|
||||
void *iPixelBufferA;
|
||||
void *iPixelBufferB;
|
||||
TInt iPixelBufferSize;
|
||||
CFbsBitmap *iScratchBitmap;
|
||||
CFbsBitmap *iMaskBitmap;
|
||||
TPoint *iPointsBuffer;
|
||||
TInt iPointsBufferSize;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,36 @@ void ApplyColorMod(void *dest, void *source, int pitch, int width, int height, S
|
||||
}
|
||||
}
|
||||
|
||||
void BlitWithAlphaKey(CFbsBitmap *dst, int dst_x, int dst_y, const void *src, int src_width, int src_height, int src_stride_u16)
|
||||
{
|
||||
// Write only non-transparent (alpha != 0) source pixels into the destination bitmap.
|
||||
// Pixels with alpha nibble == 0 are treated as color-key (transparent) and skipped.
|
||||
TUint16 *dst_data = static_cast<TUint16 *>(static_cast<void *>(dst->DataAddress()));
|
||||
const TUint16 *src_pixels = static_cast<const TUint16 *>(src);
|
||||
TSize dst_size = dst->SizeInPixels();
|
||||
int dst_stride_u16 = CFbsBitmap::ScanLineLength(dst_size.iWidth, EColor4K) / 2;
|
||||
|
||||
for (int y = 0; y < src_height; ++y) {
|
||||
int dy = dst_y + y;
|
||||
if (dy < 0 || dy >= dst_size.iHeight) {
|
||||
continue;
|
||||
}
|
||||
const TUint16 *src_row = src_pixels + y * src_stride_u16;
|
||||
TUint16 *dst_row = dst_data + dy * dst_stride_u16;
|
||||
for (int x = 0; x < src_width; ++x) {
|
||||
int dx = dst_x + x;
|
||||
if (dx < 0 || dx >= dst_size.iWidth) {
|
||||
continue;
|
||||
}
|
||||
TUint16 p = src_row[x];
|
||||
if (p & 0xF000) {
|
||||
// Strip the alpha nibble and write the RGB444 pixel.
|
||||
dst_row[dx] = p & 0x0FFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyFlip(void *dest, void *source, int pitch, int width, int height, SDL_FlipMode flip)
|
||||
{
|
||||
TUint16 *src_pixels = static_cast<TUint16 *>(source);
|
||||
@@ -87,12 +117,19 @@ void ApplyFlip(void *dest, void *source, int pitch, int width, int height, SDL_F
|
||||
|
||||
// Fast path: horizontal flip only.
|
||||
if (flip == SDL_FLIP_HORIZONTAL) {
|
||||
int width_minus_1 = width - 1;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
int dst_row_offset = y * pitch_offset;
|
||||
int src_row_offset = y * pitch_offset;
|
||||
int width_minus_1 = width - 1;
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_pixels[dst_row_offset + x] = src_pixels[src_row_offset + (width_minus_1 - x)];
|
||||
int row_offset = y * pitch_offset;
|
||||
if (dest == source) {
|
||||
for (int x = 0; x < width / 2; ++x) {
|
||||
TUint16 tmp = src_pixels[row_offset + x];
|
||||
src_pixels[row_offset + x] = src_pixels[row_offset + (width_minus_1 - x)];
|
||||
src_pixels[row_offset + (width_minus_1 - x)] = tmp;
|
||||
}
|
||||
} else {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_pixels[row_offset + x] = src_pixels[row_offset + (width_minus_1 - x)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -114,11 +151,25 @@ void ApplyFlip(void *dest, void *source, int pitch, int width, int height, SDL_F
|
||||
// Both horizontal and vertical flip
|
||||
int width_minus_1 = width - 1;
|
||||
int height_minus_1 = height - 1;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
int dst_row_offset = y * pitch_offset;
|
||||
int src_row_offset = (height_minus_1 - y) * pitch_offset;
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_pixels[dst_row_offset + x] = src_pixels[src_row_offset + (width_minus_1 - x)];
|
||||
if (dest == source) {
|
||||
// Swap pixels across the center point.
|
||||
for (int y = 0; y < (height + 1) / 2; ++y) {
|
||||
int top_offset = y * pitch_offset;
|
||||
int bot_offset = (height_minus_1 - y) * pitch_offset;
|
||||
int x_limit = (y == height_minus_1 - y) ? width / 2 : width;
|
||||
for (int x = 0; x < x_limit; ++x) {
|
||||
TUint16 tmp = src_pixels[top_offset + x];
|
||||
src_pixels[top_offset + x] = src_pixels[bot_offset + (width_minus_1 - x)];
|
||||
src_pixels[bot_offset + (width_minus_1 - x)] = tmp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
int dst_row_offset = y * pitch_offset;
|
||||
int src_row_offset = (height_minus_1 - y) * pitch_offset;
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_pixels[dst_row_offset + x] = src_pixels[src_row_offset + (width_minus_1 - x)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
#define ngage_video_render_ops_hpp
|
||||
|
||||
#include <3dtypes.h>
|
||||
#include <fbs.h>
|
||||
|
||||
void ApplyColorMod(void *dest, void *source, int pitch, int width, int height, SDL_FColor color);
|
||||
void BlitWithAlphaKey(CFbsBitmap *dst, int dst_x, int dst_y, const void *src, int src_width, int src_height, int src_stride_u16);
|
||||
void ApplyFlip(void *dest, void *source, int pitch, int width, int height, SDL_FlipMode flip);
|
||||
void ApplyRotation(void *dest, void *source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed angle);
|
||||
void ApplyScale(void *dest, void *source, int pitch, int width, int height, TFixed center_x, TFixed center_y, TFixed scale_x, TFixed scale_y);
|
||||
|
||||
Reference in New Issue
Block a user