Подобный метод отличается от всем известных водяных знаков, которые легко распознать и удалить.
А теперь сделаем курсовую работу по OpenCV и ImageMagick:
Преобразовать все цвета к 4 битам на канал. 4096 цветов хватит всем.
Распознать прямоугольные границы букв в документе и измененить кернинг и межстрочный интервал в случайных границах. Кернинг запоминаем для кадой пары букв в строке, чтобы у читателя глаза не повытекали.
Определить прямоугольные границы иллюстраций, подвинуть каждую из них в случайном направлении, растянуть на 1-5% по каждой оси и повернуть на случайный угол в пределах +- 2 градуса. Если косинус угла поворота картинки умноженный на её ширину меньше, чем 1 мм, увеличивать угол поворота в 2 раза до тех пор, пока не станет больше.
Бонусное задание: перед обработкой документа выровнять межстрочные интервалы и левую/правую границу текста в пределах +/- треть буквы.
Коль скоро тут уже про алименты вспомнили, замечу, что дети, как таковые неплохо помогают от выгорания.
Было что-то подобное до них, потом - как рукой сняло. Вероятно, это как-то связано с привычкой жалеть себя, которая уходит, когда понимаешь, что кто-то от меня зависит и в краткосрочной перспективе, и в долгосрочной. Причем, их жаль сильнее, т.к. происходящее со мной - это последствия моих решений, а происходящее с детьми от них практически не зависит - до определенного возраста это в основном последствия решений родителей.
Смотришь на него (и на нее, но она - меньше), и думаешь:
буду лежать на диване смотреть в потолок - он возьмет из этого куски в свою модель поведения и принятия решений. Может, не "дословно" и не "буквально", но на пользу это не пойдет. Как пример личного опыта могу сказать, что замечаю за собой какие-то базовые привычки, которые были характерны для родителей - реакция на крик, на проблемы, на расстройство близких. Не все из них, к сожалению, конструктивны, полезны и делают меня счастливее. На более понятном примере (все совпадения случайны и все такое: сын запойного пьяницы не обязательно будет пить. Но запойным пьяницей человек стал не случайно, а вследствие каких-то ошибок. Не разобравшись в них сам, человек не сможет ни научить сына решать подобные вопросы, ни стать примером удачного поведения в некоторых обстоятельствах - простейшее из которых, это "друзья" и их фразой "да не гони, сделай то, что тебе нельзя, нам так хочется".
допустим, сегодня я "выгорю" и без депрессии брошу работу, но вместо медитации со взглядом сквозь стены пойду кататься на велосипеде. 8 часов, 5 дней в неделю. Я променяю на это летние детские тренировки на роликах и его бассейн. Почему? Потому, что к лету у нас закончатся деньги на них.
а может, я выгорю, устану понимать ошибки и особенности заказчика, поведу себя непрофессионально, и пошлю его туда, куда хороших людей не посылают. Потом уволюсь, сменю работу, приобрету нового заказчика (сменю шило на мыло - они все не идеальные), потеряю месячный заработок и дни отпуска, а потому детей вместо отпуска пошлю не смотреть замки, или водопады, или необычную природу, или кататься на лыжах и т.д, а в соседнюю комнату, чтоб работать не мешали.
Думаю, есть еще важный для понимания Черноруссии эффект: мнение человека о себе зависит от его достижений, среди которых и трудности, которые он смог преодолеть. По мотивам "да я вырос в девяностые", "да я играл деревянными игрушками, прибитыми к полу", "да у меня, вместо современных детских площадок с резиновым покрытием на земле, была стройка со ржавой арматурой", и т.д.
Думаю, это же можно экстраполировать и на общество. Это огромное поле для манипуляций и пропаганды.
Вряд ли люди часто задумываются о том, что фраза "я выбираюсь/выбрался из грубокой задницы" подразумевает, что предварительно человек в этой глубокой заднице оказался, и что последний факт далеко не всегда может быть предметом гордости.
Вот если бы Янык не отказался подписывать эту евроассоциацию — всё было бы нормально
Если бы я был крайнерусом, то я бы возразил, что совпадение событий по времени не гарантирует причинно-следственных связей, равно как и роль евроассоциации сильно преувеличена. Понять происходяшее с другой стороны поребрика может быть сложно из-за некоторых отличий в менталитете между гражданами, считающими себя частью огромной империи, ядерной державы и всего такого (будь то Великобритания или что побольше), и гражданами относительно небольшой страны, периодически терявшей государственность и отстаивавшей свое право на неё.
На тот момент, фактически, президент стал посмешищем, но вёл себя как царь Иван Грозный, попутно отжимая все, что можно и что нельзя, в пользу детей и их друзей.
Первое спровоцировало часть гражданского общества: как писали ранее, у нас них исторически есть любовь и гордость в отношении страны, как нации, но нет подобного в отношении символа вроде потомственного монарха или усатого грузина. Видимо, поэтому стало очень обидно, когда Легитимный, истерящий по поводу того, что его портрет нарисовали на упаковке контрацептивов, откровенно доил всю страну и пытался запретить высказывать негативное отношение к себе.
Владельцам же крупного бизнеса, скорее всего, стало обидно, что приходят некие юноши, и с видом Гайзенберга (того, что варил отроскомнадзоренное вещество в лаборатории) подминают под себя бизнес.
В сумме, получили возмущенный народ и поддержку его не только оппозицией, но и бизнес-элитами, читай - масс-медиа.
Дальше случилась эскалация с человеческими жертвами, которая породила еще больше вопросов "какого этого Дважды Несудимый себе позволяет", что стало последней каплей, т.к. вызвало опасения, что если его не выгнать сейчас, то Крайнеруссия останется со своим Легитимным и его свитой надолго.
Ага, а потом рыцарь ордена битья рук линейкой пишет что-нибудь вроде:
void set_duration(int first_delay, int loop_duration);
И сиди потом разбирайся, в каких единицах параметр. И через год исправляй ошибки за кем-то, кто неглядя решил, что там секунды.
Я бы лучше написал миллисекунды, и если уж кому-нибудь станет интересно, что у этих миллисекунд под капотом, то пускай он как-нибудь наведёт курсор на имя типа, чтоб IDE ему показала определение.
Для реализации makeString(Numeric) можно добавить if constexpr и вообще как шестнадцатеричные цифры это выводить, т.к. массив симвовов у нас строкой не считается.
Или для makeString(Iterable) сделать вариант шаблона, который будет выводить такой массив в виде base64.
В любом случае, это уже частные решения вывода массивов 1-байтных целых в строку. основной идеей было показать trait-ы на практическом примере и CppInsight.
Но вообще, я изначально хотел запретить makeString(char) из-за его неочевидного поведения, но сначала не стал потому, что хотел вводить новые техники постепенно, потом - потому, что из этого вырос целый раздел про CppInsight раскапывание причин непонятного поведения компилятора. А потом забыл.
Стандартом гарантированы 9 перегрузок std::to_string, принимающих примитивный тип по копии. Любой другой std::to_string приводит к undefined behavior: в моем случае это будет лишнее копирование.
Можно было бы подстраховаться, добавив еще одно требование к шаблону, по типу is_numeric, но для краткости, думаю, и так сойдет. Тем более, я старался вводить новые подходы в статью по одному и с объяснением задачи, которую он решает.
В общем случае - согласен, в данном случае для обертки вокруг std::to_string ссылка не нужна, т. к. у нас гарантированно примитивный тип, который дёшево копируется.
Почему комманд буферы надо генерировать в нескольких потоках?
Потому, что пока вы выводите картинку на экран, CPU простаивает - почему бы не загрузить его просчетом следующего кадра?
Везде будут сплошные критические секции
Сплошные критические секции появляются тогда, когда пытаются организовать наивную многопоточную обработку данных, владение которыми разделено между несколькими потоками.
По-хорошему, данными в любой момент владеет один поток и критические секции нужны только для того, чтобы безопасно передать данные от одного потока другому.
Поток игры:
Создаем пустой "мир" из примитивов для рендеринга в потоке логики. А лучше два: мир для звука и графики.
Поток логики, он единственный, кому нужны игровые сущности по типу ADoomGuy. Делаем "тик" часов. Обрабатываем каждую игровую сущность по одной, в результате получая примитивы по типу CMesh+CMaterial+CTransform+CSound, которые засовываются в нужный "мир" по одному без блокировки.
По окончанию обработки кадра игровой логикой, берем весь мир (отдельно звук и картинку) и засовываем в список готовых к рендерингу кадров. Здесь в момент push_back (и только для него) нужна синхронизация, чтоб рендерер не сделал pop_front пока список в разобранном состоянии.
Если слишком сильно опередили потоки рендеринга - ждём.
Поток рендеринга:
берем самый старый готовый "мир" из своего списка (world = frames.pop_front();) в этот момент, и только в него нужна блокировка.
отпустив блокировку, рендерим мир: или настраиваем графический API, или смешиваем звуки и применяем к ним эффекты.
По аналогии решает проблема ввода-вывода. Так же, по аналогии можно выделить физический примитивы в отдельную сущность и "рендерить" физику (т.е. скорости и положение сущностей игры на момент следующего кадра) в отдельном потоке - разница в том, что запросы на просчет физики будут отправляться после тика, а до тика придется ждать завершения просчета физики.
Итого, получаем минимум блокировок и максимум параллелизма. Из минусов - небольшой лаг отрисовки в пару кадров, но де-факто это стандарт, и при плавных 60-120 fps это не критично. Особенно, когда игра выдает 120 fps, монитор рисует 60 fрs, а тактильный отклик на клик кнопки доходит до мозга на всех 30 fps.
И решения тут два: либо делай IRenderer так, чтобы он отвечал и за создание окна (дополнительная ответственность у классов), либо привязывай IRenderer к какому-то окну уже созданному (но ведь можно же рендерить без окон!!! Не универсально!!)
...
UDP: Для эффективности приложения не стоит возвращать из Tick OutputData по значению.
Напомнило анекдот
Каждый раз, когда профессор приходил в буфет, его очень раздражало, что студенты просят "одно кофе". Но однажды он услышал: - Мне, пожалуйста, один кофе. "Ну наконец-то", - с облегчением подумал профессор. - И один булочка...
Для эффективности приложения следует возвращать OutputData по значению. При этом OutpupData должен иметь перемещающий конструктор. Если вы будете хранить его в классе, то мало того, что ответственность размажется, так оно еще и масштабироваться на потоки не будет.
Я не настоящий сварщик, но я бы для эффективности приложения все сущности складывал в OutputData, перемещая туда временные объекты.
Примерно так:
один потом обрабатывает пользовательский ввод
один поток занимается миром игры согласно последнему полученному вводу от игрока, по сути, двигает коробки и выполняет триггеры. Если нужно, таким же образом складывает команды и очередь.
один поток выводит примитивы отвечающие предыдущему отрендеренному состоянию игры через графическое API.
один поток создает (рендерит) примитивы из сущностей игры для вывода на экран и в звуковую карту в следующем кадре, пока предыдущий кадр рендерится на экран.
в конце кадра предыдущий список примитивов и состояния мира заменяется текущим.
Насколько я понимаю, Unreal Engine примерно так и работает. При этом "примитив" это скорее не треугольник, а mesh, источник света или еще какая-нибудь относительно высокоуровневая сущность.
При это слой рендеринта получает команду "отрендерить мир", и дальше делит это на части: отрендерить mesh-ы, отрендерить свет, отрендерить тени. Слой рендеринга мешей в свою очередь знает, как отрендерить их максимально эффективно (отсортировать по материалу, установить материал, отправить пачку треугольников). И еще ниже идет графическое API, которое знает как перевести ваш материал в шейдеры и как настроить видеокарту на дальнейший рендеринг треугольников с ним.
А теперь сделаем курсовую работу по OpenCV и ImageMagick:
Преобразовать все цвета к 4 битам на канал. 4096 цветов хватит всем.
Распознать прямоугольные границы букв в документе и измененить кернинг и межстрочный интервал в случайных границах. Кернинг запоминаем для кадой пары букв в строке, чтобы у читателя глаза не повытекали.
Определить прямоугольные границы иллюстраций, подвинуть каждую из них в случайном направлении, растянуть на 1-5% по каждой оси и повернуть на случайный угол в пределах +- 2 градуса. Если косинус угла поворота картинки умноженный на её ширину меньше, чем 1 мм, увеличивать угол поворота в 2 раза до тех пор, пока не станет больше.
Бонусное задание: перед обработкой документа выровнять межстрочные интервалы и левую/правую границу текста в пределах +/- треть буквы.
Смогу, причём, по фотографии. Присылайте.
Один нюанс, моментального чуда не обещаю. Обычно нужно 4-6 недель и четкое следование моим рекомендациям, в частности, посещение травматолога.
Никакого мошенничества, оплату принимаю по факту выздоровления.
Коль скоро тут уже про алименты вспомнили, замечу, что дети, как таковые неплохо помогают от выгорания.
Было что-то подобное до них, потом - как рукой сняло. Вероятно, это как-то связано с привычкой жалеть себя, которая уходит, когда понимаешь, что кто-то от меня зависит и в краткосрочной перспективе, и в долгосрочной. Причем, их жаль сильнее, т.к. происходящее со мной - это последствия моих решений, а происходящее с детьми от них практически не зависит - до определенного возраста это в основном последствия решений родителей.
Смотришь на него (и на нее, но она - меньше), и думаешь:
буду лежать на диване смотреть в потолок - он возьмет из этого куски в свою модель поведения и принятия решений. Может, не "дословно" и не "буквально", но на пользу это не пойдет. Как пример личного опыта могу сказать, что замечаю за собой какие-то базовые привычки, которые были характерны для родителей - реакция на крик, на проблемы, на расстройство близких. Не все из них, к сожалению, конструктивны, полезны и делают меня счастливее. На более понятном примере (все совпадения случайны и все такое: сын запойного пьяницы не обязательно будет пить. Но запойным пьяницей человек стал не случайно, а вследствие каких-то ошибок. Не разобравшись в них сам, человек не сможет ни научить сына решать подобные вопросы, ни стать примером удачного поведения в некоторых обстоятельствах - простейшее из которых, это "друзья" и их фразой "да не гони, сделай то, что тебе нельзя,
нам так хочется".допустим, сегодня я "выгорю" и без депрессии брошу работу, но вместо медитации со взглядом сквозь стены пойду кататься на велосипеде. 8 часов, 5 дней в неделю. Я променяю на это летние детские тренировки на роликах и его бассейн. Почему? Потому, что к лету у нас закончатся деньги на них.
а может, я выгорю, устану понимать ошибки и особенности заказчика, поведу себя непрофессионально, и пошлю его туда, куда хороших людей не посылают. Потом уволюсь, сменю работу, приобрету нового заказчика (сменю шило на мыло - они все не идеальные), потеряю месячный заработок и дни отпуска, а потому детей вместо отпуска пошлю не смотреть замки, или водопады, или необычную природу, или кататься на лыжах и т.д, а в соседнюю комнату, чтоб работать не мешали.
Думаю, есть еще важный для понимания Черноруссии эффект: мнение человека о себе зависит от его достижений, среди которых и трудности, которые он смог преодолеть. По мотивам "да я вырос в девяностые", "да я играл деревянными игрушками, прибитыми к полу", "да у меня, вместо современных детских площадок с резиновым покрытием на земле, была стройка со ржавой арматурой", и т.д.
Думаю, это же можно экстраполировать и на общество. Это огромное поле для манипуляций и пропаганды.
Вряд ли люди часто задумываются о том, что фраза "я выбираюсь/выбрался из грубокой задницы" подразумевает, что предварительно человек в этой глубокой заднице оказался, и что последний факт далеко не всегда может быть предметом гордости.
Если бы я был крайнерусом, то я бы возразил, что совпадение событий по времени не гарантирует причинно-следственных связей, равно как и роль евроассоциации сильно преувеличена. Понять происходяшее с другой стороны поребрика может быть сложно из-за некоторых отличий в менталитете между гражданами, считающими себя частью огромной империи, ядерной державы и всего такого (будь то Великобритания или что побольше), и гражданами относительно небольшой страны, периодически терявшей государственность и отстаивавшей свое право на неё.
На тот момент, фактически, президент стал посмешищем, но вёл себя как царь Иван Грозный, попутно отжимая все, что можно и что нельзя, в пользу детей и их друзей.
Первое спровоцировало часть гражданского общества: как писали ранее, у
насних исторически есть любовь и гордость в отношении страны, как нации, но нет подобного в отношении символа вроде потомственного монарха или усатого грузина. Видимо, поэтому стало очень обидно, когда Легитимный, истерящий по поводу того, что его портрет нарисовали на упаковке контрацептивов, откровенно доил всю страну и пытался запретить высказывать негативное отношение к себе.Владельцам же крупного бизнеса, скорее всего, стало обидно, что приходят некие юноши, и с видом Гайзенберга (того, что варил отроскомнадзоренное вещество в лаборатории) подминают под себя бизнес.
В сумме, получили возмущенный народ и поддержку его не только оппозицией, но и бизнес-элитами, читай - масс-медиа.
Дальше случилась эскалация с человеческими жертвами, которая породила еще больше вопросов "какого этого Дважды Несудимый себе позволяет", что стало последней каплей, т.к. вызвало опасения, что если его не выгнать сейчас, то Крайнеруссия останется со своим Легитимным и его свитой надолго.
Комментарий удален по просьбе правообладателя (ответил невпопад)
Ага, а потом рыцарь ордена битья рук линейкой пишет что-нибудь вроде:
И сиди потом разбирайся, в каких единицах параметр. И через год исправляй ошибки за кем-то, кто неглядя решил, что там секунды.
Я бы лучше написал миллисекунды, и если уж кому-нибудь станет интересно, что у этих миллисекунд под капотом, то пускай он как-нибудь наведёт курсор на имя типа, чтоб IDE ему показала определение.
Рендеры, если говорить точнее: https://youtu.be/GS3nb4bwHKQ
Конечно можно - вы справились! Моя задача была не в этом.
Спасибо, писал про концепты на остатках ночиного энтузиазма и не разобрался с приоритетом применения.
Сегодня дописал код и статью для поддержки вашего примера.
Для реализации
makeString(Numeric)можно добавить if constexpr и вообще как шестнадцатеричные цифры это выводить, т.к. массив симвовов у нас строкой не считается.Или для
makeString(Iterable)сделать вариант шаблона, который будет выводить такой массив в виде base64.В любом случае, это уже частные решения вывода массивов 1-байтных целых в строку. основной идеей было показать trait-ы на практическом примере и CppInsight.
Ага, perfect forwarding - неплохой вариант.
Если ту, что буква греческого алфавита, то да!
Но вообще, я изначально хотел запретить
makeString(char)из-за его неочевидного поведения, но сначала не стал потому, что хотел вводить новые техники постепенно, потом - потому, что из этого вырос целый раздел про CppInsight раскапывание причин непонятного поведения компилятора. А потом забыл.Стандартом гарантированы 9 перегрузок std::to_string, принимающих примитивный тип по копии. Любой другой std::to_string приводит к undefined behavior: в моем случае это будет лишнее копирование.
Можно было бы подстраховаться, добавив еще одно требование к шаблону, по типу is_numeric, но для краткости, думаю, и так сойдет. Тем более, я старался вводить новые подходы в статью по одному и с объяснением задачи, которую он решает.
В общем случае - согласен, в данном случае для обертки вокруг
std::to_stringссылка не нужна, т. к. у нас гарантированно примитивный тип, который дёшево копируется.В кратце про UE: https://docs.unrealengine.com/4.27/en-US/ProgrammingAndScripting/Rendering/ParallelRendering/
Побольше про id Tech: https://fabiensanglard.net/doom3_bfg/
Потому, что пока вы выводите картинку на экран, CPU простаивает - почему бы не загрузить его просчетом следующего кадра?
Сплошные критические секции появляются тогда, когда пытаются организовать наивную многопоточную обработку данных, владение которыми разделено между несколькими потоками.
По-хорошему, данными в любой момент владеет один поток и критические секции нужны только для того, чтобы безопасно передать данные от одного потока другому.
Поток игры:
Создаем пустой "мир" из примитивов для рендеринга в потоке логики. А лучше два: мир для звука и графики.
Поток логики, он единственный, кому нужны игровые сущности по типу ADoomGuy. Делаем "тик" часов. Обрабатываем каждую игровую сущность по одной, в результате получая примитивы по типу CMesh+CMaterial+CTransform+CSound, которые засовываются в нужный "мир" по одному без блокировки.
По окончанию обработки кадра игровой логикой, берем весь мир (отдельно звук и картинку) и засовываем в список готовых к рендерингу кадров. Здесь в момент push_back (и только для него) нужна синхронизация, чтоб рендерер не сделал pop_front пока список в разобранном состоянии.
Если слишком сильно опередили потоки рендеринга - ждём.
Поток рендеринга:
берем самый старый готовый "мир" из своего списка (world = frames.pop_front();) в этот момент, и только в него нужна блокировка.
отпустив блокировку, рендерим мир: или настраиваем графический API, или смешиваем звуки и применяем к ним эффекты.
По аналогии решает проблема ввода-вывода. Так же, по аналогии можно выделить физический примитивы в отдельную сущность и "рендерить" физику (т.е. скорости и положение сущностей игры на момент следующего кадра) в отдельном потоке - разница в том, что запросы на просчет физики будут отправляться после тика, а до тика придется ждать завершения просчета физики.
Итого, получаем минимум блокировок и максимум параллелизма. Из минусов - небольшой лаг отрисовки в пару кадров, но де-факто это стандарт, и при плавных 60-120 fps это не критично. Особенно, когда игра выдает 120 fps, монитор рисует 60 fрs, а тактильный отклик на клик кнопки доходит до мозга на всех 30 fps.
Напомнило анекдот
Каждый раз, когда профессор приходил в буфет, его очень раздражало, что студенты просят "одно кофе". Но однажды он услышал:
- Мне, пожалуйста, один кофе.
"Ну наконец-то", - с облегчением подумал профессор.
- И один булочка...
Для эффективности приложения следует возвращать OutputData по значению. При этом OutpupData должен иметь перемещающий конструктор. Если вы будете хранить его в классе, то мало того, что ответственность размажется, так оно еще и масштабироваться на потоки не будет.
Я не настоящий сварщик, но я бы для эффективности приложения все сущности складывал в OutputData, перемещая туда временные объекты.
Примерно так:
один потом обрабатывает пользовательский ввод
один поток занимается миром игры согласно последнему полученному вводу от игрока, по сути, двигает коробки и выполняет триггеры. Если нужно, таким же образом складывает команды и очередь.
один поток выводит примитивы отвечающие предыдущему отрендеренному состоянию игры через графическое API.
один поток создает (рендерит) примитивы из сущностей игры для вывода на экран и в звуковую карту в следующем кадре, пока предыдущий кадр рендерится на экран.
в конце кадра предыдущий список примитивов и состояния мира заменяется текущим.
Насколько я понимаю, Unreal Engine примерно так и работает. При этом "примитив" это скорее не треугольник, а mesh, источник света или еще какая-нибудь относительно высокоуровневая сущность.
При это слой рендеринта получает команду "отрендерить мир", и дальше делит это на части: отрендерить mesh-ы, отрендерить свет, отрендерить тени. Слой рендеринга мешей в свою очередь знает, как отрендерить их максимально эффективно (отсортировать по материалу, установить материал, отправить пачку треугольников). И еще ниже идет графическое API, которое знает как перевести ваш материал в шейдеры и как настроить видеокарту на дальнейший рендеринг треугольников с ним.
Старая, заезженная тема. Особенно интересно, когда аргументы макроса имеют побочные эффекты.