Бессонный хакатон: где экран?
Продолжаю писать о рефакторинге кода, написанного в свой бессонный хакатон. На этот раз хочется избавиться от подобранных вручную координат экрана эмулируемого компьютера — это неаккуратно и не универсально, — в другом браузере эти координаты скорее всего будут другими.
У меня какое-то время не было хороших идей как бы это сделать, всё что мне приходило в голову — это перебрать координаты и найти несколько примыкающих к друг другу палок определённого размера и цвета, которые окаймляют искомый экран.
Такое решение наводило на меня тоску, но потом вспомнилось, что в графической библиотеке ПХП есть фильтр, который выделяет края и я решил глянуть не будет ли от него пользы.

Код для наложения фильтров выглядит следующим образом:
$im = imagecreatefrompng($screenshot_file);
imagefilter($im, IMG_FILTER_GRAYSCALE);
imagefilter($im, IMG_FILTER_EDGEDETECT);
Мне казалось, что на получившемся изображении можно искать четыре точки экрана на известном расстоянии, но оказалось, что у такого метода есть ложные срабатывания. Поэтому я стал перебирать на экране все горизонтальные белые линии, пока не встречаю две параллельные нужной мне длины.

Таким образом очень надёжно находится граница экрана эмулятора, от которой на фиксированном расстоянии находится искомый экран. В итоге получается вот такой код:
const FRAME_W = 1039;
const FRAME_H = 808;
const FRAME_COLOR = 0xFFFFFF;
const FRAME_BORDER = 30;
$im = imagecreatefrompng($_SERVER["argv"][1]);
[$w, $h] = [imagesx($im), imagesy($im)];
imagefilter($im, IMG_FILTER_GRAYSCALE);
imagefilter($im, IMG_FILTER_EDGEDETECT);
for ($y = 0; $y <= $h - FRAME_H; $y++) {
for ($x = 0; $x <= $w - FRAME_W; $x++) {
$is_frame = imagecolorat($im, $x, $y) === FRAME_COLOR;
for ($i = 1; $i<FRAME_W; $i+=2) {
$is_frame =
$is_frame &&
imagecolorat($im, $x + $i, $y) === FRAME_COLOR &&
imagecolorat($im, $x + $i, $y + FRAME_H - 1) === FRAME_COLOR;
if (!$is_frame) {
break;
}
}
if ($is_frame) {
printf(
"%d,%d\n",
intdiv($x, 2) + FRAME_BORDER,
intdiv($y, 2) + FRAME_BORDER
);
exit(0);
}
}
}
exit(1);
У меня тут всё в удвоенных координатах, которые я потом делю на два, чтобы получить реальные, потому что у меня экран «Ретина». Надо бы найти где-нибудь обычный экран и посмотреть что придётся переделать. Думаю, все мои константные координаты и размеры надо будет поделить на два.