[N-Gage] Preserve source textures and optimize rotation with DDA

- Add temporary render bitmap to avoid destroying source texture data
- Implement incremental DDA algorithm for rotation
- Replaces per-pixel FixMul operations with simple additions and preserves
  textures for reuse.
This commit is contained in:
Michael Fitzmayer
2026-04-15 20:05:26 +02:00
parent 4870f81d9c
commit e5c8523b36
3 changed files with 95 additions and 25 deletions

View File

@@ -160,7 +160,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), iWorkBuffer1(0), iWorkBuffer2(0), iWorkBufferSize(0) {}
CRenderer::CRenderer() : iRenderer(0), iDirectScreen(0), iScreenGc(0), iWsSession(), iWsWindowGroup(), iWsWindowGroupID(0), iWsWindow(), iWsScreen(0), iWsEventStatus(), iWsEvent(), iShowFPS(EFalse), iFPS(0), iFont(0), iWorkBuffer1(0), iWorkBuffer2(0), iWorkBufferSize(0), iTempRenderBitmap(0), iTempRenderBitmapWidth(0), iTempRenderBitmapHeight(0) {}
CRenderer::~CRenderer()
{
@@ -173,6 +173,12 @@ CRenderer::~CRenderer()
iWorkBuffer1 = 0;
iWorkBuffer2 = 0;
iWorkBufferSize = 0;
// Free temp render bitmap.
delete iTempRenderBitmap;
iTempRenderBitmap = 0;
iTempRenderBitmapWidth = 0;
iTempRenderBitmapHeight = 0;
}
void CRenderer::ConstructL()
@@ -321,6 +327,40 @@ bool CRenderer::EnsureWorkBufferCapacity(TInt aRequiredSize)
return true;
}
bool CRenderer::EnsureTempBitmapCapacity(TInt aWidth, TInt aHeight)
{
if (iTempRenderBitmap &&
iTempRenderBitmapWidth >= aWidth &&
iTempRenderBitmapHeight >= aHeight) {
return true;
}
// Delete old bitmap.
delete iTempRenderBitmap;
iTempRenderBitmap = 0;
// Create new bitmap.
iTempRenderBitmap = new CFbsBitmap();
if (!iTempRenderBitmap) {
iTempRenderBitmapWidth = 0;
iTempRenderBitmapHeight = 0;
return false;
}
TInt error = iTempRenderBitmap->Create(TSize(aWidth, aHeight), EColor4K);
if (error != KErrNone) {
delete iTempRenderBitmap;
iTempRenderBitmap = 0;
iTempRenderBitmapWidth = 0;
iTempRenderBitmapHeight = 0;
return false;
}
iTempRenderBitmapWidth = aWidth;
iTempRenderBitmapHeight = aHeight;
return true;
}
#ifdef __cplusplus
extern "C" {
#endif
@@ -421,14 +461,18 @@ bool CRenderer::Copy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rec
useBuffer1 = !useBuffer1;
}
// Render directly from work buffer without copying back to bitmap.
// Note: We need a temporary bitmap for rendering the transformed data.
// For now, copy to original bitmap (this could be further optimized with a render target).
Mem::Copy(phdata->cachedDataAddress, source, pitch * h);
// Use temp bitmap to avoid destroying source texture.
if (!EnsureTempBitmapCapacity(w, h)) {
return false;
}
// Copy transformed data to temp bitmap.
Mem::Copy(iTempRenderBitmap->DataAddress(), source, pitch * h);
// Render from temp bitmap, preserving original texture.
TRect aSource(TPoint(srcrect->x, srcrect->y), TSize(srcrect->w, srcrect->h));
TPoint aDest(dstrect->x, dstrect->y);
iRenderer->Gc()->BitBlt(aDest, phdata->bitmap, aSource);
iRenderer->Gc()->BitBlt(aDest, iTempRenderBitmap, aSource);
return true;
}
@@ -500,14 +544,18 @@ bool CRenderer::CopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const NGAGE
useBuffer1 = !useBuffer1;
}
// Render directly from work buffer without copying back to bitmap.
// Note: We need a temporary bitmap for rendering the transformed data.
// For now, copy to original bitmap (this could be further optimized with a render target).
Mem::Copy(phdata->cachedDataAddress, source, pitch * h);
// Use temp bitmap to avoid destroying source texture.
if (!EnsureTempBitmapCapacity(w, h)) {
return false;
}
// Copy transformed data to temp bitmap.
Mem::Copy(iTempRenderBitmap->DataAddress(), source, pitch * h);
// Render from temp bitmap, preserving original texture.
TRect aSource(TPoint(copydata->srcrect.x, copydata->srcrect.y), TSize(copydata->srcrect.w, copydata->srcrect.h));
TPoint aDest(copydata->dstrect.x, copydata->dstrect.y);
iRenderer->Gc()->BitBlt(aDest, phdata->bitmap, aSource);
iRenderer->Gc()->BitBlt(aDest, iTempRenderBitmap, aSource);
return true;
}

View File

@@ -92,8 +92,14 @@ class CRenderer : public MDirectScreenAccess
void *iWorkBuffer2;
TInt iWorkBufferSize;
// Helper method to ensure work buffers have sufficient capacity.
// Temporary render bitmap to avoid destroying source textures.
CFbsBitmap *iTempRenderBitmap;
TInt iTempRenderBitmapWidth;
TInt iTempRenderBitmapHeight;
// Helper methods.
bool EnsureWorkBufferCapacity(TInt aRequiredSize);
bool EnsureTempBitmapCapacity(TInt aWidth, TInt aHeight);
};
#endif // ngage_video_render_ngage_c_hpp

View File

@@ -87,26 +87,42 @@ void ApplyRotation(void *dest, void *source, int pitch, int width, int height, T
FixSinCos(angle, sin_angle, cos_angle);
}
// Pre-calculate pitch in pixels to avoid repeated division.
const TInt pitchPixels = pitch >> 1;
// Incremental DDA: Calculate per-pixel increments.
// As we move right (x+1), the rotated position changes by (cos, -sin).
TFixed dx_cos = cos_angle;
TFixed dx_sin = -sin_angle;
for (int y = 0; y < height; ++y) {
// Calculate destination row offset once per row.
TInt dstRowOffset = y * pitchPixels;
// Calculate starting position for this row.
TFixed translated_y = Int2Fix(y) - center_y;
TFixed row_start_x = FixMul(translated_y, sin_angle) + center_x;
TFixed row_start_y = FixMul(translated_y, cos_angle) + center_y;
// For first pixel in row, account for x=0 translation.
TFixed src_x = row_start_x - FixMul(center_x, cos_angle);
TFixed src_y = row_start_y + FixMul(center_x, sin_angle);
for (int x = 0; x < width; ++x) {
// Translate point to origin.
TFixed translated_x = Int2Fix(x) - center_x;
TFixed translated_y = Int2Fix(y) - center_y;
// Rotate point (clockwise).
TFixed rotated_x = FixMul(translated_x, cos_angle) + FixMul(translated_y, sin_angle);
TFixed rotated_y = FixMul(translated_y, cos_angle) - FixMul(translated_x, sin_angle);
// Translate point back.
int final_x = Fix2Int(rotated_x + center_x);
int final_y = Fix2Int(rotated_y + center_y);
// Convert to integer coordinates.
int final_x = Fix2Int(src_x);
int final_y = Fix2Int(src_y);
// Check bounds.
if (final_x >= 0 && final_x < width && final_y >= 0 && final_y < height) {
dst_pixels[y * pitch / 2 + x] = src_pixels[final_y * pitch / 2 + final_x];
dst_pixels[dstRowOffset + x] = src_pixels[final_y * pitchPixels + final_x];
} else {
dst_pixels[y * pitch / 2 + x] = 0;
dst_pixels[dstRowOffset + x] = 0;
}
// Incremental step: move to next pixel (just additions, no multiplications!).
src_x += dx_cos;
src_y += dx_sin;
}
}
}