@@ -269,6 +269,7 @@ export class Html5Qrcode {
269269
270270 private shouldScan : boolean ;
271271 private flippedScan : boolean ;
272+ private invertedScan : boolean ;
272273
273274 // Nullable elements
274275 // TODO(mebjas): Reduce the state-fulness of this mammoth class, by splitting
@@ -338,6 +339,7 @@ export class Html5Qrcode {
338339 this . foreverScanTimeout ;
339340 this . shouldScan = true ;
340341 this . flippedScan = false ;
342+ this . invertedScan = false ;
341343 this . stateManagerProxy = StateManagerFactory . create ( ) ;
342344 }
343345
@@ -1213,7 +1215,10 @@ export class Html5Qrcode {
12131215 internalConfig , qrCodeSuccessCallback , qrCodeErrorCallback ) ;
12141216 } , this . getTimeoutFps ( internalConfig . fps ) ) ;
12151217 } ;
1216-
1218+ if ( this . context ) {
1219+ // apply invert function that replace this.context.filter = 'invert(X)'
1220+ this . context = this . invert ( this . context , ( this . invertedScan ) ? '1' : '0' ) ;
1221+ }
12171222 // Try scanning normal frame and in case of failure, scan
12181223 // the inverted context if not explictly disabled.
12191224 // TODO(mebjas): Move this logic to decoding library.
@@ -1234,14 +1239,11 @@ export class Html5Qrcode {
12341239 }
12351240 this . flippedScan = false ;
12361241 }
1237- // previous scan failed, flipped scan failed, try to invert colors
1238- if ( this . context ! . filter === "none" && this . flippedScan === false ) {
1239- this . context ! . filter = "invert(1)" ;
1240- } else {
1241- // restore initial filter value
1242- if ( this . context ! . filter === "invert(1)" && this . flippedScan === false ) {
1243- this . context ! . filter = "none" ;
1244- }
1242+ // previous scan failed, check if next time I shouldapply invert function with amount parameter set to 1 or 0
1243+ if ( this . invertedScan === false && this . flippedScan === false ) {
1244+ this . invertedScan = true ;
1245+ } else if ( this . invertedScan === true && this . flippedScan === false ) {
1246+ this . invertedScan = false ;
12451247 }
12461248 triggerNextScan ( ) ;
12471249 } else {
@@ -1349,6 +1351,69 @@ export class Html5Qrcode {
13491351 const type = ( typeof cameraIdOrConfig ) ;
13501352 throw `Invalid type of 'cameraIdOrConfig' = ${ type } ` ;
13511353 }
1354+
1355+ /**
1356+ * Method that replace CanvasRenderingContext2D.filter that is not supported from Safari
1357+ * https://github.com/davidenke/context-filter-polyfill/blob/main/src/filters/invert.filter.ts
1358+ * @param context
1359+ * @param amount from 0 to 1
1360+ */
1361+ private invert (
1362+ context : CanvasRenderingContext2D ,
1363+ amount : any = '0'
1364+ ) {
1365+ amount = this . normalizeNumberPercentage ( amount ) ;
1366+ // do not manipulate without proper amount
1367+ if ( amount <= 0 ) {
1368+ return context ;
1369+ }
1370+ // a maximum of 100%
1371+ if ( amount > 1 ) {
1372+ amount = 1 ;
1373+ }
1374+ const { height, width } = context . canvas ;
1375+ const imageData = context . getImageData ( 0 , 0 , width , height ) ;
1376+ const { data } = imageData ;
1377+ const { length } = data ;
1378+
1379+ // in rgba world, every
1380+ // n * 4 + 0 is red,
1381+ // n * 4 + 1 green and
1382+ // n * 4 + 2 is blue
1383+ // the fourth can be skipped as it's the alpha channel
1384+ for ( let i = 0 ; i < length ; i += 4 ) {
1385+ data [ i + 0 ] = Math . abs ( data [ i + 0 ] - 255 * amount ) ;
1386+ data [ i + 1 ] = Math . abs ( data [ i + 1 ] - 255 * amount ) ;
1387+ data [ i + 2 ] = Math . abs ( data [ i + 2 ] - 255 * amount ) ;
1388+ }
1389+
1390+ // set back image data to context
1391+ context . putImageData ( imageData , 0 , 0 ) ;
1392+
1393+ // return the context itself
1394+ return context ;
1395+ }
1396+
1397+ /**
1398+ * filter options are often represented as number-percentage,
1399+ * means that they'll be percentages like `50%` or floating
1400+ * in-between 0 and 1 like `.5`, so we normalize them.
1401+ * https://developer.mozilla.org/en-US/docs/Web/CSS/filter#number-percentage
1402+ * https://github.com/davidenke/context-filter-polyfill/blob/main/src/utils/filter.utils.ts
1403+ * @param percentage
1404+ * @returns
1405+ */
1406+ private normalizeNumberPercentage (
1407+ percentage : string
1408+ ) {
1409+ let normalized = parseFloat ( percentage ) ;
1410+ // check for percentages and divide by a hundred
1411+ if ( / % \s * ?$ / i. test ( percentage ) ) {
1412+ normalized /= 100 ;
1413+ }
1414+ return normalized ;
1415+ }
1416+
13521417 //#endregion
13531418
13541419 //#region Documented private methods for file based scanner.
0 commit comments