Graphics.DrawImage слишком медленный для больших изображений?

Мой новый любимый псевдоним, основанный на bonyiii 's answer (upvoted), и мой собственный ответ о " Передайте аргумент команде alit Git ":

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

Я потерял файл, удаленный по ошибке несколькими коммитами назад? Quick:

git restore my_deleted_file

Кризис предотвращен.


Роберт Дайли предлагает в комментариях следующий псевдоним:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

И jegan добавляет в комментарии :

. Для установки псевдонима из командной строки I использовала эту команду:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 

29
задан Miroslav Mares 13 June 2012 в 18:03
поделиться

4 ответа

Да, это слишком медленно.

Я столкнулся с этой проблемой несколько лет назад во время разработки Paint.NET (с самого начала, и это было довольно сложно!). Производительность рендеринга была ужасной, так как она всегда была пропорциональна размеру растрового изображения, а не размеру области, которую было сказано перерисовать. То есть частота кадров уменьшалась по мере увеличения размера растрового изображения, а частота кадров никогда не увеличивалась, поскольку размер недопустимой области / области перерисовки уменьшался при реализации OnPaint () и вызове Graphics.DrawImage (). Небольшое растровое изображение, скажем, 800x600, всегда работало нормально, но большие изображения (например, 2400x1800) были очень медленными. (В любом случае, вы можете предположить, что в предыдущем абзаце ничего лишнего не происходило, например, масштабирование с помощью какого-нибудь дорогого бикубического фильтра, что могло бы негативно повлиять на производительность.)

Можно заставить WinForms использовать GDI вместо GDI + и избегайте даже создания объекта Graphics за кулисами, после чего вы можете наложить поверх него еще один инструментарий рендеринга (например, Direct2D). Однако это не просто. Я делаю это в Paint.NET, и вы можете увидеть, что требуется, используя что-то вроде Reflector в классе с именем GdiPaintControl в DLL-библиотеке SystemLayer, но для того, что вы делаете, я бы посчитал это последним средством.

Однако используемый вами размер растрового изображения (800x1200) все равно должен работать достаточно хорошо в GDI +, не прибегая к расширенному взаимодействию, если только вы не нацеливаетесь на что-то столь же низкое, как Pentium II с 300 МГц. Вот несколько советов, которые могут помочь:

  • Если вы используете непрозрачное растровое изображение (без альфа / прозрачности) в вызове Graphics.DrawImage(), и особенно если это 32-разрядное растровое изображение с альфа-канал (но вы знаете , что он непрозрачный, или вам все равно), затем установите Graphics.CompositingMode в CompositingMode.SourceCopy перед вызовом DrawImage() (обязательно установите его обратно в исходное значение после, в противном случае обычные рисунки примитивов будут выглядеть очень некрасиво). Это пропускает много дополнительных математических вычислений на пиксель.
  • Убедитесь, что Graphics.InterpolationMode не настроен на что-то вроде InterpolationMode.HighQualityBicubic. Использование NearestNeighbor будет самым быстрым, хотя, если есть какое-либо растяжение, оно может выглядеть не очень хорошо (если только оно не растягивается ровно в 2, 3, 4 раза и т. Д.) Bilinear обычно является хорошим компромиссом. Вы никогда не должны использовать ничего, кроме NearestNeighbor, если размер растрового изображения соответствует области, в которую вы рисуете, в пикселях.
  • Всегда используйте объект Graphics, данный вам в OnPaint().
  • Всегда делайте свой рисунок в OnPaint. Если вам нужно перерисовать область, позвоните по номеру Invalidate(). Если вам нужно, чтобы розыгрыш произошел прямо сейчас, позвоните Update() после Invalidate(). Это разумный подход, поскольку сообщения WM_PAINT (что приводит к вызову OnPaint()) являются сообщениями с «низким приоритетом». Любая другая обработка, выполняемая оконным менеджером, будет выполняться в первую очередь, и, в противном случае, вы можете получить много пропущенных и пропущенных кадров.
  • Использование System.Windows.Forms.Timer в качестве частоты кадров / таймера не очень хорошо работает. Они реализованы с использованием Win32 SetTimer и приводят к сообщениям WM_TIMER, которые затем приводят к возникновению события Timer.Tick, а WM_TIMER - другое сообщение с низким приоритетом, которое отправляется только тогда, когда очередь сообщений пуста. Вам лучше использовать System.Threading.Timer, а затем использовать Control.Invoke() (чтобы убедиться, что вы на правильном пути!) И вызывать Control.Update().
  • Как правило, не используйте Control.CreateGraphics(). (следствие «всегда рисовать в OnPaint()» и «всегда использовать Graphics, данное вам OnPaint()»)
  • Я рекомендую не использовать обработчик событий Paint. Вместо этого реализуйте OnPaint() в классе, который вы пишете, который должен быть получен из Control. Производные от другого класса, например PictureBox или UserControl, либо не добавят никакой ценности для вас, либо добавят дополнительные издержки. (КСТАТИ PictureBox часто неправильно понимают. Вы, вероятно, почти никогда не захотите его использовать.)

Надежда, которая помогает.

98
ответ дан Rick Brewster 13 June 2012 в 18:03
поделиться

GDI +, вероятно, не лучший выбор для игр. DirectX / XNA или OpenGL должны быть предпочтительными, так как они используют любое графическое ускорение и очень быстро.

3
ответ дан Ani 13 June 2012 в 18:03
поделиться

GDI + никоим образом не является демоном скорости. Любые серьезные манипуляции с изображениями обычно должны идти в нативную сторону (вызовы pInvoke и / или манипуляции с помощью указателя, полученного путем вызова LockBits.)

Вы изучали XNA / DirectX / OpenGL? Это фреймворки, разработанные для разработки игр, и они будут на порядок более эффективными и гибкими, чем при использовании фреймворков UI, таких как WinForms или WPF. Все библиотеки предлагают привязки C #.

Вы можете вводить в нативный код, используя такие функции, как BitBlt , но есть и дополнительные затраты, связанные с пересечением границы управляемого кода.

3
ответ дан Ed S. 13 June 2012 в 18:03
поделиться

Хотя это древний вопрос, а WinForms - это древний фреймворк, я хотел бы поделиться тем, что я только что обнаружил случайно: рисование растрового изображения в BufferedGraphics и его последующее отображение в графическом контексте, предоставляемом OnPaint, представляет собой способ быстрее рисования растрового изображения непосредственно в графическом контексте OnPaint - по крайней мере, на моем компьютере с Windows 10.

Это удивительно, потому что интуитивно я предполагал, что копирование данных будет несколько медленнее (и поэтому я подумал, что это обычно оправдано только тогда, когда кто-то хочет делать двойную буферизацию вручную). Но очевидно, что с объектом BufferedGraphics происходит нечто более сложное.

Поэтому создайте BufferedGraphics в конструкторе элемента управления, который будет содержать растровое изображение (в моем случае я хотел нарисовать полноэкранное растровое изображение 1920x1080):

        using (Graphics graphics = CreateGraphics())
        {
            graphicsBuffer = BufferedGraphicsManager.Current.Allocate(graphics, new Rectangle(0,0,Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height));
        }

и использовать его в OnPaint (при аннулировании OnPaintBackground )

    protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}

    protected override void OnPaint(PaintEventArgs e)
    {   
        Graphics g = graphicsBuffer.Graphics;

        g.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);

        graphicsBuffer.Render(e.Graphics);
    }

вместо наивного определения

    protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}

    protected override void OnPaint(PaintEventArgs e)
    {   
        e.Graphics.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);
    }

См. На следующих скриншотах сравнение частоты результирующего события MouseMove (я реализую очень простой элемент управления рисованием растровых изображений). Вверху находится версия, где растровое изображение рисуется напрямую, в нижней части используется BufferedGraphics. В обоих случаях я двигал мышь примерно с одинаковой скоростью.

enter image description here

2
ответ дан oliver 13 June 2012 в 18:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: