소스 검색

[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.
Michael Fitzmayer 1 개월 전
부모
커밋
e5c8523b36
3개의 변경된 파일94개의 추가작업 그리고 24개의 파일을 삭제
  1. 59 11
      src/render/ngage/SDL_render_ngage.cpp
  2. 7 1
      src/render/ngage/SDL_render_ngage_c.hpp
  3. 28 12
      src/render/ngage/SDL_render_ops.cpp

+ 59 - 11
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;
 }

+ 7 - 1
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

+ 28 - 12
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) {
-        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;
+        // Calculate destination row offset once per row.
+        TInt dstRowOffset = y * pitchPixels;
 
-            // 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);
+        // 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;
 
-            // Translate point back.
-            int final_x = Fix2Int(rotated_x + center_x);
-            int final_y = Fix2Int(rotated_y + 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) {
+            // 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;
         }
     }
 }