@@ -4,31 +4,21 @@ import looksSame from "looks-same";
44import { CoreError } from "./core-error" ;
55import { ExistingBrowser } from "./existing-browser" ;
66import type { Image } from "../image" ;
7-
8- const DIRECTION = { FORWARD : "forward" , REVERSE : "reverse" } as const ;
7+ import { Coord , Length , Rect , XBand , getHeight , getIntersection , getWidth } from "./isomorphic" ;
8+ import * as logger from "../utils/logger" ;
9+ import os from "node:os" ;
910
1011interface BrowserFeatures {
1112 needsCompatLib : boolean ;
1213 pixelRatio : number ;
13- innerWidth : number ;
14+ innerWidth : Length < "css" , "x" > ;
1415}
1516
1617export interface CalibrationResult extends BrowserFeatures {
17- top : number ;
18- left : number ;
18+ viewportArea : Rect < "image" , "device" > ;
1919 usePixelRatio : boolean ;
2020}
2121
22- interface ViewportStart {
23- x : number ;
24- y : number ;
25- }
26-
27- interface ImageAnalysisResult {
28- viewportStart : ViewportStart ;
29- colorLength ?: number ;
30- }
31-
3222export class Calibrator {
3323 private _cache : Record < string , CalibrationResult > ;
3424 private _script : string ;
@@ -49,95 +39,130 @@ export class Calibrator {
4939
5040 const { innerWidth, pixelRatio } = features ;
5141 const hasPixelRatio = Boolean ( pixelRatio && pixelRatio > 1.0 ) ;
52- const imageFeatures = await this . _analyzeImage ( image , { calculateColorLength : hasPixelRatio } ) ;
42+ const imageFeatures = await this . _findMarkerAreaInImage ( image ) ;
5343
5444 if ( ! imageFeatures ) {
45+ const screenshotPath = path . join ( os . tmpdir ( ) , "testplane-calibration-page.png" ) ;
46+ await image . save ( screenshotPath ) ;
47+ logger . error (
48+ "Could not calibrate, because marker area was not found. See calibration page screenshot for details: " +
49+ screenshotPath ,
50+ ) ;
51+ await image . save ( screenshotPath ) ;
5552 throw new CoreError (
5653 "Could not calibrate. This could be due to calibration page has failed to open properly" ,
5754 ) ;
5855 }
5956
6057 const calibratedFeatures : CalibrationResult = {
6158 ...features ,
62- top : imageFeatures . viewportStart . y ,
63- left : imageFeatures . viewportStart . x ,
64- usePixelRatio : hasPixelRatio && imageFeatures . colorLength ! > innerWidth ,
59+ viewportArea : imageFeatures ,
60+ usePixelRatio : hasPixelRatio && imageFeatures . width > innerWidth ,
6561 } ;
6662
6763 this . _cache [ browser . id ] = calibratedFeatures ;
6864 return calibratedFeatures ;
6965 }
7066
71- private async _analyzeImage (
72- image : Image ,
73- params : { calculateColorLength ?: boolean } ,
74- ) : Promise < ImageAnalysisResult | null > {
67+ private async _findMarkerAreaInImage ( image : Image ) : Promise < Rect < "image" , "device" > | null > {
7568 const imageHeight = ( await image . getSize ( ) ) . height ;
7669
77- for ( let y = 0 ; y < imageHeight ; y ++ ) {
78- const result = await analyzeRow ( y , image , params ) ;
70+ let topPart : Rect < "image" , "device" > | null = null ;
71+
72+ for ( let y = 0 as Coord < "image" , "device" , "y" > ; y < imageHeight ; y ++ ) {
73+ const result = await findMarkerXBandInRow ( y , image ) ;
7974 if ( result ) {
80- return result ;
75+ topPart = {
76+ top : y ,
77+ left : result . left ,
78+ width : result . width ,
79+ height : getHeight ( y , imageHeight as Coord < "image" , "device" , "y" > ) ,
80+ } ;
81+ break ;
82+ }
83+ }
84+
85+ if ( topPart === null ) {
86+ return null ;
87+ }
88+
89+ for ( let y = ( imageHeight - 1 ) as Coord < "image" , "device" , "y" > ; y >= 0 ; y -- ) {
90+ const result = await findMarkerXBandInRow ( y , image ) ;
91+ if ( result ) {
92+ const bottomPart = {
93+ top : 0 ,
94+ left : result . left ,
95+ width : result . width ,
96+ height : getHeight ( 0 as Coord < "image" , "device" , "y" > , y ) ,
97+ } ;
98+
99+ return getIntersection ( topPart , bottomPart ) ;
81100 }
82101 }
83102
84103 return null ;
85104 }
86105}
87106
88- async function analyzeRow (
89- row : number ,
107+ async function findMarkerXBandInRow (
108+ row : Coord < "image" , "device" , "y" > ,
90109 image : Image ,
91- params : { calculateColorLength ?: boolean } = { } ,
92- ) : Promise < ImageAnalysisResult | null > {
93- const markerStart = await findMarkerInRow ( row , image , DIRECTION . FORWARD ) ;
110+ ) : Promise < XBand < "image" , "device" > | null > {
111+ const markerStart = await findMarkerStartInRow ( row , image ) ;
94112
95- if ( markerStart === - 1 ) {
113+ if ( markerStart === null ) {
96114 return null ;
97115 }
98116
99- const result : ImageAnalysisResult = { viewportStart : { x : markerStart , y : row } } ;
117+ const markerEnd = await findMarkerEndInRow ( row , image ) ;
100118
101- if ( ! params . calculateColorLength ) {
102- return result ;
119+ if ( markerEnd === null ) {
120+ return null ;
103121 }
104122
105- const markerEnd = await findMarkerInRow ( row , image , DIRECTION . REVERSE ) ;
106- const colorLength = markerEnd - markerStart + 1 ;
107-
108- return { ... result , colorLength } ;
123+ return {
124+ left : markerStart ,
125+ width : getWidth ( markerStart , markerEnd ) ,
126+ } ;
109127}
110128
111- async function findMarkerInRow ( row : number , image : Image , searchDirection : "forward" | "reverse" ) : Promise < number > {
112- const imageWidth = ( await image . getSize ( ) ) . width ;
129+ async function isMarkerColorAtPoint (
130+ image : Image ,
131+ x : Coord < "image" , "device" , "x" > ,
132+ y : Coord < "image" , "device" , "y" > ,
133+ ) : Promise < boolean > {
113134 const searchColor = { R : 148 , G : 250 , B : 0 } ;
135+ const color = await image . getRGB ( x , y ) ;
114136
115- if ( searchDirection === DIRECTION . REVERSE ) {
116- return searchReverse_ ( ) ;
117- } else {
118- return searchForward_ ( ) ;
119- }
137+ return looksSame . colors ( color , searchColor ) ;
138+ }
120139
121- async function searchForward_ ( ) : Promise < number > {
122- for ( let x = 0 ; x < imageWidth ; x ++ ) {
123- if ( await compare_ ( x ) ) {
124- return x ;
125- }
140+ async function findMarkerStartInRow (
141+ row : Coord < "image" , "device" , "y" > ,
142+ image : Image ,
143+ ) : Promise < Coord < "image" , "device" , "x" > | null > {
144+ const imageWidth = ( await image . getSize ( ) ) . width ;
145+
146+ for ( let x = 0 as Coord < "image" , "device" , "x" > ; x < imageWidth ; x ++ ) {
147+ if ( await isMarkerColorAtPoint ( image , x , row ) ) {
148+ return x ;
126149 }
127- return - 1 ;
128150 }
129151
130- async function searchReverse_ ( ) : Promise < number > {
131- for ( let x = imageWidth - 1 ; x >= 0 ; x -- ) {
132- if ( await compare_ ( x ) ) {
133- return x ;
134- }
152+ return null ;
153+ }
154+
155+ async function findMarkerEndInRow (
156+ row : Coord < "image" , "device" , "y" > ,
157+ image : Image ,
158+ ) : Promise < Coord < "image" , "device" , "x" > | null > {
159+ const imageWidth = ( await image . getSize ( ) ) . width ;
160+
161+ for ( let x = ( imageWidth - 1 ) as Coord < "image" , "device" , "x" > ; x >= 0 ; x -- ) {
162+ if ( await isMarkerColorAtPoint ( image , x , row ) ) {
163+ return x ;
135164 }
136- return - 1 ;
137165 }
138166
139- async function compare_ ( x : number ) : Promise < boolean > {
140- const color = await image . getRGB ( x , row ) ;
141- return looksSame . colors ( color , searchColor ) ;
142- }
167+ return null ;
143168}
0 commit comments