- Создано 02.08.14
Глюки при работе с функцией CopyRect
В ходе работы над программой RitmInMe я предусмотрел несколько форматов вывода результата, но на практике для небольших стихов самым удобным оказалось элементарное PrintScreen. Естественно, скриншот приходилось обрезать, да и перед этой операцией окошко программы лучше было бы свести к необходимому минимуму... В общем, в контекстное меню окна редактирования была добавлена опция Mini-PrintScreen, которая реализовала всю эту нуднятину, да ещё и затирала ставшие лишними изображения кнопок в правом верхнем углу скриншота программы. Но тут влезло несовершенство программного обеспечения: функция CopyRect, с помощью которой я вырезал из полной копии экрана нужное мне, в некоторых случаях работала с артефактами – повторялись строки и/или столбцы пикселей (см. пример):
Глюк небольшой, но противно...
Для начала я заменил обращение к CopyRect на обычный цикл попиксельной переписи участка изображения. Артефакты исчезли, но работало это раздражающе медленно. Коллективный разум программистов на Delphi устами Google присоветовал мне аналог CopyRect – APIшную функцию BitBlt.
Вот полученный код (с заремленными неудачными вариантами):
DC := GetDC(0);
BackgroundCanvas := TCanvas.Create;
BackgroundCanvas.Handle := DC;
w0 := Form1.Left+3;
h0 := Form1.Top+3;
{
BGBitmap.Canvas.CopyRect(Rect(0,
0, BGbitmap.Width-1, BGbitmap.Height-1)
,BackgroundCanvas
,Rect(w0, h0, Form1.Left+BGbitmap.Width-1,
Form1.Top+BGbitmap.Height-1));
}
{
for j := 0 to BGbitmap.Height-1 do
for i := 0 to BGbitmap.Width-1 do
BGBitmap.Canvas.Pixels[i,j]
:= BackgroundCanvas.Pixels[i+w0,j+h0];
}
try
BitBlt(BGBitmap.Canvas.Handle,0,0, BGbitmap.Width-1, BGbitmap.Height-1
,BackgroundCanvas.Handle,w0, h0,SrcCopy);
except
//
end;
Резюме: В CopyRect работало растягивание, и мне бы следовало аккуратнее посчитать координаты исходного и результирующего прямоугольников (в исходном – добавка для координаты нижнего правого угла не "-1", а "+2"). Но... Я предпочёл применить функцию BitBlt, у которой размер прямоугольника задаётся только результирующим и растягивания заведомо нет.
- Создано 15.10.14
Сортировка в TStringList
Строковый тип, используемый в Delphi, дополняется объектом TStringList, который, помимо того, что является массивом строк, имеет ряд полезных свойств и методов. Одним из них является возможность сортировки - как присваиванием свойству sorted значения true, так и обращением к методу sort. Результаты одинаковы: строки переупорядочиваются физически, и присваиванием свойству sorted значения false в исходное состояние порядок строк не возвращает.
Интересен смысл, вложенный разработчиками в фразу "сортировка по алфавиту". При работе с не-юникодной кодировкой такая сортировка сильно отличается от упорядочения по кодам символов. Для иллюстрации этого я написал проект, состоящий из списка (TListBox) и двух переключателей (TCheckBox). Клики по CheckBox1 долженствуют демонстрировать символы и их коды (32-255) в естественном и отсортированном по стандартному алгоритму порядке, а CheckBox2 - работу метода CustomSort над последовательностью строк, первоначально отсортированных в обратном порядке.
Можете убедиться, что "сортировка по алфавиту" для русской и английской локали отнюдь не тривиальна:
(В системном шрифте MS Sans Serif разных локалей отображаются не все символы)
Видно, что сортировка букв, используемая в русской локали, вполне хороша и для украинского языка. В английской локали непонятна смена правила сортировки: для "a-c" сначала идёт большая буква, а потом малая, но для "d-z" порядок обратный (для русского языка всё единообразно).
Сортировка неалфавитных символов не лишена логики, но и не очевидна. Во всяком случае, программисту эти таблички не помешает иметь в виду.
Код функции сортировки, используемой методом CustomSort в прилагаемом проекте, простейший и реализует сортировку по кодам символов. Навесив на эти коды свою таблицу перекодировки, можно организовать сколь угодно вычурную сортировку.
- Создано 07.03.13
Строки
Строковый тип, используемый в Delphi, обоснованно является как предметом гордости пишущих на этом языке, так и предметом нападок со стороны пишущих на С. Пишущий на Delphi экономит массу времени за счёт использования типа string и объектов, основанных на TStringList, программа получается более читаемой. А пишущий на C готов в обморок упасть представляя, какие операции с памятью при этом неявно выполняются, дополнительно расходуя ресурсы компьютера.
Как по мне, и те и те правы. Помнится, мне приходилось программировать ещё в машинных кодах и мой тогдашний учитель полагал даже ассемблер излишним и расточительным – в машинных кодах были доступны приёмы, которые в тогдашнем ассемблере не посчитали нужным реализовать. Я и сам испытывал подобные ощущения при переходе с ассемблера на языки более высокого уровня, и тем более – на программирование баз данных. Идеальный код – хорошо, но время... Поэтому, программируя конкретный фрагмент программного продукта, всегда следует соизмерять его важность с трудозатратами.
Если вы пишете фрагмент кода для одноразового выполнения по клику пользователя, то тут допустимо любое расточительство ресурсов компьютера. Процессор в этом режиме и так почти всё время простаивает. Здесь оправдано применение строк Delphi и такого кода, по моим прикидкам, 96%. А вот остальные 4% требуют большего внимания и это внимание окупается. Тут при работе с переменными типа string используют С-шный подход: ставят в соответствие такой переменной массив типа pchar и уже над ним производят необходимые манипуляции. В результате получаем существенную экономию как по работе с памятью, так и по быстродействию полученного кода. В основном такого внимания заслуживают частоиспользуемые подпрограммы.
На такое решение меня подтолкнуло изучение компонента MDBFTable от MichaL MutL – на его основе моя дочь писала учебную программку по зарплате, а я уж помогал (благо программированием в этой области занимаюсь лет 20). Доступ к DBF-файлам в MDBFTable реализован как работа с массивами строк Delphi, наиболее существенные операции над которыми производятся как над массивами типа pchar. Было интересно посмотреть реализацию вещей, с которыми я работал на уровне языка управления базами данных, на более низком уровне – в Delphi. Ситуация оказалась стандартной: на низком уровне доступно больше и сделать это можно экономичнее (по ресурсам компьютера, но не по трудозатратам программиста). Для учебных целей такой подход вполне катит, но вот писать такую программу для реального использования и сопровождать её было бы му́кой.
При работе со строками иногда возникает соблазн запихать туда через функцию chr не-символы с кодами 0-31. Здесь надо быть крайне осторожным. chr(0) не рекомендую категорически – уж слишком часто стандартные функции Delphi являются обёрткой для С-шных функций, для которых #0 является ограничителем строки. Коды из диапазона 1-15 так же могут служить источниками глюков, хотя #9, #10, #13 не вызывают проблем.