From e3904236a78c926ce19563785b1f700eced70996 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Fri, 18 Sep 2026 22:35:49 +0100 Subject: [PATCH] fix(totp): scan QR ecran - indices Byte dans ZXing (boucle infinie a 100 % CPU) ZXing.Delphi declarait rgbIndex/luminanceIndex en Byte dans les conversions octets bruts -> luminance : au-dela de 255 ils repassaient a 0, la boucle ne finissait jamais. L'APK n'y passait pas (CreateFromBitmap). Mesure apres correctif : 60 ms sur un ecran 1920x1080 avec TRY_HARDER, 86 ms sans QR. Co-Authored-By: Claude Opus 5 --- .../Filtering/ZXing.RGBLuminanceSource.pas | 14 +++++----- delphi-backend/Source/PM.ScreenQr.pas | 26 +++++++++++++------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/android-client/ThirdParty/ZXing/Filtering/ZXing.RGBLuminanceSource.pas b/android-client/ThirdParty/ZXing/Filtering/ZXing.RGBLuminanceSource.pas index 4c824c1..cb61562 100644 --- a/android-client/ThirdParty/ZXing/Filtering/ZXing.RGBLuminanceSource.pas +++ b/android-client/ThirdParty/ZXing/Filtering/ZXing.RGBLuminanceSource.pas @@ -364,7 +364,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceRGB24( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets r, g, b : Integer; begin rgbIndex := 0; @@ -389,7 +389,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceBGR24( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets b, g, r : Byte; begin rgbIndex := 0; @@ -414,7 +414,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceRGB32( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets r, g, b : Byte; begin rgbIndex := 0; @@ -439,7 +439,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceBGR32( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets b, g, r : Byte; begin rgbIndex := 0; @@ -464,7 +464,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceBGRA32( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets b, g, r, alpha : Byte; luminance : Byte; @@ -495,7 +495,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceRGBA32( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets r, g, b, alpha : Byte; luminance : Byte; @@ -526,7 +526,7 @@ procedure TRGBLuminanceSource.CalculateLuminanceARGB32( const rgbRawBytes: TArray); var rgbIndex, - luminanceIndex, + luminanceIndex : Integer; // pmserver: etaient Byte -> boucle infinie au-dela de 255 octets r, g, b, alpha : Byte; luminance : Byte; diff --git a/delphi-backend/Source/PM.ScreenQr.pas b/delphi-backend/Source/PM.ScreenQr.pas index 42476e8..4715f53 100644 --- a/delphi-backend/Source/PM.ScreenQr.pas +++ b/delphi-backend/Source/PM.ScreenQr.pas @@ -13,6 +13,10 @@ interface /// Texte du premier QR trouve a l'ecran, '' si aucun. function ScanScreenForQr: string; +// Exposees pour le banc d'essai (mesure du temps de decodage). +function CaptureVirtualScreen(out AWidth, AHeight: Integer): TArray; +function DecodeQrPixels(const APixels: TArray; AW, AH: Integer; + ATryHarder: Boolean): string; implementation @@ -65,10 +69,9 @@ begin end; end; -function ScanScreenForQr: string; +function DecodeQrPixels(const APixels: TArray; AW, AH: Integer; + ATryHarder: Boolean): string; var - LPixels: TArray; - LW, LH: Integer; LSource: TLuminanceSource; LBinarizer: THybridBinarizer; LImage: TBinaryBitmap; @@ -78,19 +81,16 @@ var LRes: TReadResult; begin Result := ''; - LPixels := CaptureVirtualScreen(LW, LH); - if LPixels = nil then Exit; - LFormats := TList.Create; LFormats.Add(TBarcodeFormat.QR_CODE); LHints := TDictionary.Create; LHints.Add(TDecodeHintType.POSSIBLE_FORMATS, LFormats); - LHints.Add(TDecodeHintType.TRY_HARDER, nil); + if ATryHarder then LHints.Add(TDecodeHintType.TRY_HARDER, nil); LReader := TMultiFormatReader.Create; LReader.Hints := LHints; // BGR32 et non BGRA32 : le 4e octet d'une capture GDI vaut 0, et la // conversion BGRA le lirait comme une transparence totale - image blanche. - LSource := TRGBLuminanceSource.Create(LPixels, LW, LH, TBitmapFormat.BGR32); + LSource := TRGBLuminanceSource.Create(APixels, AW, AH, TBitmapFormat.BGR32); LBinarizer := THybridBinarizer.Create(LSource); LImage := TBinaryBitmap.Create(LBinarizer); LRes := nil; @@ -112,4 +112,14 @@ begin end; end; +function ScanScreenForQr: string; +var + LPixels: TArray; + LW, LH: Integer; +begin + Result := ''; + LPixels := CaptureVirtualScreen(LW, LH); + if LPixels <> nil then Result := DecodeQrPixels(LPixels, LW, LH, True); +end; + end.