Skip to content

Commit

Permalink
fix: resolve issue with premature image disposal causing black replay (
Browse files Browse the repository at this point in the history
  • Loading branch information
thisames authored Jan 7, 2025
1 parent ec06d51 commit 30dd729
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 25 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Next

## 4.9.1

- fix: blank screen when viewing session replay recordings ([#139](https://github.com/PostHog/posthog-flutter/pull/139))

## 4.9.0

- feat: add getter for current session identifier ([#134](https://github.com/PostHog/posthog-flutter/pull/134))
Expand Down
1 change: 0 additions & 1 deletion lib/src/replay/mask/image_mask_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class ImageMaskPainter {
(image.width * pixelRatio).round(),
(image.height * pixelRatio).round(),
);
image.dispose();
return maskedImage;
}
}
58 changes: 34 additions & 24 deletions lib/src/replay/screenshot/screenshot_capturer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import 'package:posthog_flutter/src/replay/vendor/equality.dart';
import 'package:posthog_flutter/src/util/logging.dart';

class ImageInfo {
final ui.Image image;
final int id;
final int x;
final int y;
Expand All @@ -18,7 +17,7 @@ class ImageInfo {
final bool shouldSendMetaEvent;
final Uint8List imageBytes;

ImageInfo(this.image, this.id, this.x, this.y, this.width, this.height,
ImageInfo(this.id, this.x, this.y, this.width, this.height,
this.shouldSendMetaEvent, this.imageBytes);
}

Expand Down Expand Up @@ -56,6 +55,16 @@ class ScreenshotCapturer {
_views[renderObject] = statusView;
}

Future<Uint8List?> _getImageBytes(ui.Image img) async {
final ByteData? byteData =
await img.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null || byteData.lengthInBytes == 0) {
printIfDebug('Error: Failed to convert image to byte data.');
return null;
}
return byteData.buffer.asUint8List();
}

Future<ImageInfo?> captureScreenshot() async {
final context = PostHogMaskController.instance.containerKey.currentContext;
if (context == null) {
Expand Down Expand Up @@ -90,24 +99,16 @@ class ScreenshotCapturer {
// using png because its compressed, the native SDKs will decompress it
// and transform to jpeg if needed (soon webp)
// https://github.com/brendan-duncan/image does not have webp encoding
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) {
printIfDebug('Error: Failed to convert image to byte data.');
image.dispose();
return null;
}

Uint8List pngBytes = byteData.buffer.asUint8List();
image.dispose();

if (pngBytes.isEmpty) {
Uint8List? pngBytes = await _getImageBytes(image);
if (pngBytes == null || pngBytes.isEmpty) {
printIfDebug('Error: Failed to convert image byte data to Uint8List.');
image.dispose();
return null;
}

if (const PHListEquality().equals(pngBytes, statusView.imageBytes)) {
printIfDebug('Snapshot is the same as the last one.');
image.dispose();
return null;
}

Expand All @@ -120,29 +121,38 @@ class ScreenshotCapturer {
if (screenElementsRects != null) {
final ui.Image maskedImage = await _imageMaskPainter.drawMaskedImage(
image, screenElementsRects, pixelRatio);

// Dispose the original image after masking
image.dispose();

Uint8List? maskedImagePngBytes = await _getImageBytes(maskedImage);
if (maskedImagePngBytes == null || maskedImagePngBytes.isEmpty) {
maskedImage.dispose();
return null;
}

final imageInfo = ImageInfo(
maskedImage,
viewId,
globalPosition.dx.toInt(),
globalPosition.dy.toInt(),
srcWidth.toInt(),
srcHeight.toInt(),
shouldSendMetaEvent,
pngBytes);
maskedImagePngBytes);
_updateStatusView(shouldSendMetaEvent, renderObject, statusView);
return imageInfo;
}
}

final imageInfo = ImageInfo(
image,
viewId,
globalPosition.dx.toInt(),
globalPosition.dy.toInt(),
srcWidth.toInt(),
srcHeight.toInt(),
shouldSendMetaEvent,
pngBytes);
viewId,
globalPosition.dx.toInt(),
globalPosition.dy.toInt(),
srcWidth.toInt(),
srcHeight.toInt(),
shouldSendMetaEvent,
pngBytes,
);
_updateStatusView(shouldSendMetaEvent, renderObject, statusView);
return imageInfo;
} catch (e) {
Expand Down

0 comments on commit 30dd729

Please sign in to comment.