diff --git a/src/render/ngage/SDL_render_ngage.cpp b/src/render/ngage/SDL_render_ngage.cpp index 18f5084139..47ba9fbf9a 100644 --- a/src/render/ngage/SDL_render_ngage.cpp +++ b/src/render/ngage/SDL_render_ngage.cpp @@ -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; } diff --git a/src/render/ngage/SDL_render_ngage_c.hpp b/src/render/ngage/SDL_render_ngage_c.hpp index 007b7145a7..600d4d0622 100644 --- a/src/render/ngage/SDL_render_ngage_c.hpp +++ b/src/render/ngage/SDL_render_ngage_c.hpp @@ -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 diff --git a/src/render/ngage/SDL_render_ops.cpp b/src/render/ngage/SDL_render_ops.cpp index 8a2244436b..89006e662c 100644 --- a/src/render/ngage/SDL_render_ops.cpp +++ b/src/render/ngage/SDL_render_ops.cpp @@ -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; } } }