RitmInMe Описание программы Оператор цифровой трансформации - Dilibrium / Дилибриум
Facebook    Twitter    Google+    LiveJournal    Мой Мир
ВКонтакте     Одноклассники

Font Size

Глюки при работе с функцией CopyRect

В ходе работы над программой RitmInMe я предусмотрел несколько форматов вывода результата, но на практике для небольших стихов самым удобным оказалось элементарное PrintScreen. Естественно, скриншот приходилось обрезать, да и перед этой операцией окошко программы лучше было бы свести к необходимому минимуму... В общем, в контекстное меню окна редактирования была добавлена опция Mini-PrintScreen, которая реализовала всю эту нуднятину, да ещё и затирала ставшие лишними изображения кнопок в правом верхнем углу скриншота программы. Но тут влезло несовершенство программного обеспечения: функция CopyRect, с помощью которой я вырезал из полной копии экрана нужное мне, в некоторых случаях работала с артефактами – повторялись строки и/или столбцы пикселей (см. пример):

Глюк небольшой, но противно...

Для начала я заменил обращение к CopyRect на обычный цикл попиксельной переписи участка изображения. Артефакты исчезли, но работало это раздражающе медленно. Коллективный разум программистов на Delphi устами Google присоветовал мне аналог CopyRectAPIшную функцию 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, у которой размер прямоугольника задаётся только результирующим и растягивания заведомо нет.

Сортировка в TStringList

Строковый тип, используемый в Delphi, дополняется объектом TStringList, который, помимо того, что является массивом строк, имеет ряд полезных свойств и методов. Одним из них является возможность сортировки - как присваиванием свойству sorted значения true, так и обращением к методу sort. Результаты одинаковы: строки переупорядочиваются физически, и присваиванием свойству sorted значения false в исходное состояние порядок строк не возвращает.

Интересен смысл, вложенный разработчиками в фразу "сортировка по алфавиту". При работе с не-юникодной кодировкой такая сортировка сильно отличается от упорядочения по кодам символов. Для иллюстрации этого я написал проект, состоящий из списка (TListBox) и двух переключателей (TCheckBox). Клики по CheckBox1 долженствуют демонстрировать символы и их коды (32-255) в естественном и отсортированном по стандартному алгоритму порядке, а CheckBox2 - работу метода CustomSort над последовательностью строк, первоначально отсортированных в обратном порядке.

Можете убедиться, что "сортировка по алфавиту" для русской и английской локали отнюдь не тривиальна:
 


(В системном шрифте MS Sans Serif разных локалей отображаются не все символы)

Видно, что сортировка букв, используемая в русской локали, вполне хороша и для украинского языка. В английской локали непонятна смена правила сортировки: для "a-c" сначала идёт большая буква, а потом малая, но для "d-z" порядок обратный (для русского языка всё единообразно).

Сортировка неалфавитных символов не лишена логики, но и не очевидна. Во всяком случае, программисту эти таблички не помешает иметь в виду.

Код функции сортировки, используемой методом CustomSort в прилагаемом проекте, простейший и реализует сортировку по кодам символов. Навесив на эти коды свою таблицу перекодировки, можно организовать сколь угодно вычурную сортировку.

Строки

Строковый тип, используемый в 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 не вызывают проблем.