[Impeller] fix scaling of trampoline import of GLES textures into Vulkan. (#161331)

Not sure if this just needs the dpr transform or the entire canvas
transform.

Fixes https://github.com/flutter/flutter/issues/159688

## Before


![flutter_04](https://github.com/user-attachments/assets/be60b1d0-4e94-491c-b1da-a03d66897f12)


## After


![flutter_03](https://github.com/user-attachments/assets/64cca8b7-3ae7-4f40-af26-ae61d9ec6290)
diff --git a/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/MainActivity.kt b/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/MainActivity.kt
index 807b51c..6b74332 100644
--- a/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/MainActivity.kt
+++ b/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/MainActivity.kt
@@ -13,6 +13,7 @@
 import com.example.android_engine_test.extensions.NativeDriverSupportPlugin
 import com.example.android_engine_test.fixtures.BlueOrangeGradientPlatformViewFactory
 import com.example.android_engine_test.fixtures.ChangingColorButtonPlatformViewFactory
+import com.example.android_engine_test.fixtures.OtherFaceTexturePlugin
 import com.example.android_engine_test.fixtures.SmileyFaceTexturePlugin
 import io.flutter.embedding.android.FlutterActivity
 import io.flutter.embedding.engine.FlutterEngine
@@ -25,6 +26,7 @@
             .plugins
             .apply {
                 add(SmileyFaceTexturePlugin())
+                add(OtherFaceTexturePlugin())
                 add(NativeDriverSupportPlugin())
             }
 
diff --git a/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/fixtures/OtherFaceTexturePlugin.kt b/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/fixtures/OtherFaceTexturePlugin.kt
new file mode 100644
index 0000000..24cb3a2
--- /dev/null
+++ b/dev/integration_tests/android_engine_test/android/app/src/main/kotlin/com/example/native_driver_test/fixtures/OtherFaceTexturePlugin.kt
@@ -0,0 +1,91 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@file:Suppress("PackageName")
+
+package com.example.android_engine_test.fixtures
+
+import android.graphics.Color
+import android.graphics.Paint
+import android.os.Build
+import android.view.Surface
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import io.flutter.view.TextureRegistry.SurfaceTextureEntry
+
+class OtherFaceTexturePlugin :
+    FlutterPlugin,
+    MethodCallHandler {
+    private val tag = "OtherFaceTexturePlugin"
+    private lateinit var channel: MethodChannel
+    private lateinit var binding: FlutterPluginBinding
+    private lateinit var surfaceTextureEntry: SurfaceTextureEntry
+
+    private var surface: Surface? = null
+
+    override fun onAttachedToEngine(binding: FlutterPluginBinding) {
+        this.binding = binding
+        channel = MethodChannel(binding.binaryMessenger, "other_face_texture")
+        channel.setMethodCallHandler(this)
+        surfaceTextureEntry = binding.textureRegistry.createSurfaceTexture()
+    }
+
+    override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
+        channel.setMethodCallHandler(null)
+        surfaceTextureEntry.release()
+    }
+
+    override fun onMethodCall(
+        call: MethodCall,
+        result: MethodChannel.Result
+    ) {
+        if (call.method == "initTexture") {
+            val height = call.argument<Int>("height") ?: 1
+            val width = call.argument<Int>("width") ?: 1
+            surfaceTextureEntry.surfaceTexture().setDefaultBufferSize(width, height)
+            result.success(updateTexture())
+        } else {
+            result.notImplemented()
+        }
+    }
+
+    private fun updateTexture(): Long {
+        var surface = this.surface
+        if (surface == null) {
+            surface = Surface(surfaceTextureEntry.surfaceTexture())
+            this.surface = surface
+        }
+        drawOnSurface(surface!!)
+        return surfaceTextureEntry.id()
+    }
+
+    private fun drawOnSurface(surface: Surface) {
+        val canvas =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                surface.lockHardwareCanvas()
+            } else {
+                surface.lockCanvas(null)
+            }
+
+        // Yellow background
+        canvas.drawRGB(255, 230, 15)
+
+        val paint = Paint()
+        paint.style = Paint.Style.FILL
+
+        // Black eyes
+        paint.color = Color.BLACK
+        canvas.drawCircle(225f, 225f, 25f, paint) // Left eye
+        canvas.drawCircle(425f, 225f, 25f, paint) // Right eye
+
+        // Black mouth
+        paint.color = Color.BLACK
+        canvas.drawCircle(300f, 300f, 50f, paint) // Simple mouth
+
+        surface.unlockCanvasAndPost(canvas)
+    }
+}
diff --git a/dev/integration_tests/android_engine_test/lib/external_texture_other_face_main.dart b/dev/integration_tests/android_engine_test/lib/external_texture_other_face_main.dart
new file mode 100644
index 0000000..3df4b89
--- /dev/null
+++ b/dev/integration_tests/android_engine_test/lib/external_texture_other_face_main.dart
@@ -0,0 +1,54 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:android_driver_extensions/extension.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_driver/driver_extension.dart';
+
+import 'src/allow_list_devices.dart';
+
+const MethodChannel _channel = MethodChannel('other_face_texture');
+Future<int> _fetchTexture(int width, int height) async {
+  final int? result = await _channel.invokeMethod<int>('initTexture', <String, int>{
+    'width': width,
+    'height': height,
+  });
+  return result!;
+}
+
+void main() async {
+  ensureAndroidOrIosDevice();
+  enableFlutterDriverExtension(commands: <CommandExtension>[nativeDriverCommands]);
+
+  // Run on full screen.
+  SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
+
+  // Fetch the texture ID.
+  final Future<int> textureId = _fetchTexture(512, 512);
+  runApp(MainApp(textureId));
+}
+
+final class MainApp extends StatelessWidget {
+  const MainApp(this.textureId, {super.key});
+  final Future<int> textureId;
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      debugShowCheckedModeBanner: false,
+      home: FutureBuilder<int>(
+        future: textureId,
+        builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
+          if (snapshot.hasData) {
+            return Texture(textureId: snapshot.data!);
+          }
+          return const CircularProgressIndicator();
+        },
+      ),
+    );
+  }
+}
diff --git a/dev/integration_tests/android_engine_test/test_driver/external_texture_other_face_main_test.dart b/dev/integration_tests/android_engine_test/test_driver/external_texture_other_face_main_test.dart
new file mode 100644
index 0000000..4ea49c1
--- /dev/null
+++ b/dev/integration_tests/android_engine_test/test_driver/external_texture_other_face_main_test.dart
@@ -0,0 +1,54 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:android_driver_extensions/native_driver.dart';
+import 'package:android_driver_extensions/skia_gold.dart';
+import 'package:flutter_driver/flutter_driver.dart';
+import 'package:test/test.dart';
+
+import '_luci_skia_gold_prelude.dart';
+
+void main() async {
+  // To test the golden file generation locally, comment out the following line.
+  // autoUpdateGoldenFiles = true;
+
+  const String appName = 'com.example.android_engine_test';
+  late final FlutterDriver flutterDriver;
+  late final NativeDriver nativeDriver;
+
+  setUpAll(() async {
+    if (isLuci) {
+      await enableSkiaGoldComparator();
+    }
+    flutterDriver = await FlutterDriver.connect();
+    nativeDriver = await AndroidNativeDriver.connect(flutterDriver);
+    await nativeDriver.configureForScreenshotTesting();
+  });
+
+  tearDownAll(() async {
+    await nativeDriver.close();
+    await flutterDriver.close();
+  });
+
+  test('should screenshot and match an external smiley face texture', () async {
+    await flutterDriver.waitFor(find.byType('Texture'));
+
+    // On Android: Background the app, trim memory, and restore the app.
+    if (nativeDriver case final AndroidNativeDriver nativeDriver) {
+      print('Backgrounding the app, trimming memory, and resuming the app.');
+      await nativeDriver.backgroundApp();
+
+      print('Trimming memory.');
+      await nativeDriver.simulateLowMemory(appName: appName);
+
+      print('Resuming the app.');
+      await nativeDriver.resumeApp(appName: appName);
+    }
+
+    await expectLater(
+      nativeDriver.screenshot(),
+      matchesGoldenFile('external_texture_other_face.android.png'),
+    );
+  }, timeout: Timeout.none);
+}
diff --git a/engine/src/flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc b/engine/src/flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc
index eb144e8..0cf96aa 100644
--- a/engine/src/flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc
+++ b/engine/src/flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc
@@ -122,15 +122,17 @@
     VALIDATION_LOG << "Invalid external texture.";
     return;
   }
+  SkMatrix matrix = context.canvas->GetTransform();
+  SkRect mapped_bounds = matrix.mapRect(bounds);
 
   const auto& surface_context =
       SurfaceContextVK::Cast(*context.aiks_context->GetContext());
   const auto& context_vk = ContextVK::Cast(*surface_context.GetParent());
 
-  auto dst_texture =
-      GetCachedTextureSource(surface_context.GetParent(),                    //
-                             ISize::MakeWH(bounds.width(), bounds.height())  //
-      );
+  auto dst_texture = GetCachedTextureSource(
+      surface_context.GetParent(),                                  //
+      ISize::MakeWH(mapped_bounds.width(), mapped_bounds.height())  //
+  );
   if (!dst_texture || !dst_texture->IsValid()) {
     VALIDATION_LOG << "Could not fetch trampoline texture target.";
     return;
@@ -224,7 +226,10 @@
     PaintContext& context,
     const SkRect& bounds,
     const DlImageSampling sampling) const {
-  context.canvas->DrawImage(dl_image_, SkPoint{0, 0}, sampling, context.paint);
+  context.canvas->DrawImageRect(
+      dl_image_,
+      SkRect::MakeSize(SkSize::Make(dl_image_->width(), dl_image_->height())),
+      bounds, sampling, context.paint);
 }
 
 }  // namespace flutter