Программы, которые поддерживают GDI расширение файла
Программы, обслуживающие файл GDI
Как открыть файл GDI?
Отсутствие возможности открывать файлы с расширением GDI может иметь различное происхождение. К счастью, наиболее распространенные проблемы с файлами GDI могут быть решены без глубоких знаний в области ИТ, а главное, за считанные минуты. Мы подготовили список, который поможет вам решить ваши проблемы с файлами GDI.
Шаг 1. Получить Linux operating systems
Наиболее распространенной причиной таких проблем является отсутствие соответствующих приложений, поддерживающих файлы GDI, установленные в системе. Решение простое, просто скачайте и установите Linux operating systems. Выше вы найдете полный список программ, которые поддерживают GDI файлы, классифицированные в соответствии с системными платформами, для которых они доступны. Если вы хотите загрузить установщик Linux operating systems наиболее безопасным способом, мы рекомендуем вам посетить сайт Various Linux developers и загрузить его из официальных репозиториев.
Шаг 2. Проверьте версию Linux operating systems и обновите при необходимости
Если у вас уже установлен Linux operating systems в ваших системах и файлы GDI по-прежнему не открываются должным образом, проверьте, установлена ли у вас последняя версия программного обеспечения. Иногда разработчики программного обеспечения вводят новые форматы вместо уже поддерживаемых вместе с новыми версиями своих приложений. Причиной того, что Linux operating systems не может обрабатывать файлы с GDI, может быть то, что программное обеспечение устарело. Последняя версия Linux operating systems должна поддерживать все форматы файлов, которые совместимы со старыми версиями программного обеспечения.
Шаг 3. Настройте приложение по умолчанию для открытия GDI файлов на Linux operating systems
Если проблема не была решена на предыдущем шаге, вам следует связать GDI файлы с последней версией Linux operating systems, установленной на вашем устройстве. Процесс связывания форматов файлов с приложением по умолчанию может отличаться в деталях в зависимости от платформы, но основная процедура очень похожа.
Процедура изменения программы по умолчанию в Windows
Процедура изменения программы по умолчанию в Mac OS
Шаг 4. Проверьте GDI на наличие ошибок
Если вы выполнили инструкции из предыдущих шагов, но проблема все еще не решена, вам следует проверить файл GDI, о котором идет речь. Отсутствие доступа к файлу может быть связано с различными проблемами.
1. Проверьте GDI файл на наличие вирусов или вредоносных программ.
Если GDI действительно заражен, возможно, вредоносное ПО блокирует его открытие. Сканируйте файл GDI и ваш компьютер на наличие вредоносных программ или вирусов. Если сканер обнаружил, что файл GDI небезопасен, действуйте в соответствии с инструкциями антивирусной программы для нейтрализации угрозы.
2. Убедитесь, что файл с расширением GDI завершен и не содержит ошибок
Если вы получили проблемный файл GDI от третьего лица, попросите его предоставить вам еще одну копию. Возможно, что файл не был должным образом скопирован в хранилище данных и является неполным и поэтому не может быть открыт. Это может произойти, если процесс загрузки файла с расширением GDI был прерван и данные файла повреждены. Загрузите файл снова из того же источника.
3. Проверьте, есть ли у вашей учетной записи административные права
Иногда для доступа к файлам пользователю необходимы права администратора. Выйдите из своей текущей учетной записи и войдите в учетную запись с достаточными правами доступа. Затем откройте файл Generic Download Install.
4. Проверьте, может ли ваша система обрабатывать Linux operating systems
Операционные системы могут иметь достаточно свободных ресурсов для запуска приложения, поддерживающего файлы GDI. Закройте все работающие программы и попробуйте открыть файл GDI.
5. Убедитесь, что у вас установлены последние версии драйверов, системных обновлений и исправлений
Последние версии программ и драйверов могут помочь вам решить проблемы с файлами Generic Download Install и обеспечить безопасность вашего устройства и операционной системы. Возможно, файлы GDI работают правильно с обновленным программным обеспечением, которое устраняет некоторые системные ошибки.
Вы хотите помочь?
Если у Вас есть дополнительная информация о расширение файла GDI мы будем признательны, если Вы поделитесь ею с пользователями нашего сайта. Воспользуйтесь формуляром, находящимся здесь и отправьте нам свою информацию о файле GDI.
Полное название: Generic Download Install Разработчик: Various Linux developers Место в рейтинге: 2786 Популярность:
Как открыть файл GDI?
Вы не знаете что сделать в ситуации, когда Ваш файл GDI не хочет открыться? Причин такой ситуации может быть несколько, и что из этого следует, несколько решений проблем с файлами GDI. Однозначно самой правдоподобной причиной является отсутствие программы в Вашем устройстве, которая может правильно интерпретировать данные, содержащиеся в файле GDI. Эту проблему Вы решите, проходя 3 следующие шаги:
скачать одну программу из списков, которые Вы найдете внизу установите скачанную программу на своем компьютере выберите установленную программу в качестве программы по умолчанию для обслуживания файлов GDI.
Linux operating systems
Что еще я могу сделать, чтобы решить проблемы с файлами GDI?
Причин отсутствия возможности открытия файла GDI на компьютере может быть как минимум несколько. Проще всего найти соответствующую аппликацию, установленную уже на компьютере, которая позволит открытие файла GDI. Если по-прежнему что-то не так, стоит проверить:
не поврежден ли GDI файл не имеет ли файл ошибочной связи в записях реестра полная ли инсталляция аппликации, обслуживающей данный формат файла не инфицирован ли файл актуальный ли драйвер, используемый для открытия файла имеет ли компьютер достаточные параметры/технические ресурсы чтобы открыть файл с данным расширением не удалено ли случайно расширение из реестра Windows
После исключения вышеуказанных дефектов файл GDI по-прежнему не совместим с Вашим программным обеспечением? Поетому проблема более сложна и требует помощи специалиста.
Kasper Torbjörn
Создатель интернет-решений, поддерживающих и решающих проблемы не очень продвинутых пользователей в ежедневной работе с компьютером.
Автор: Виталий Брусенцев The RSDN Group Источник: RSDN Magazine #1
Опубликовано: 13.12.2001 Исправлено: 13.03.2005 Версия текста: 1.0
Заглянем «под капот»
Что новенького?
Далее мы еще будем рассматривать специфические (и такие эффектные!) возможности GDI+. Здесь же только опишем основные новшества.
Архитектурные новинки библиотеки:
Новые технологии и возможности (задержите дыхание):
Требования к среде выполнения
В его состав входят только инструкция по установке и уже упомянутая динамическая библиотека GdiPlus.dll, которую необходимо скопировать в системный каталог Windows 98/ME, Windows NT SP6 или Windows 2000. При этом возможности, предоставляемые непосредственно ядром Windows XP (в частности, технология ClearType для качественного отображения шрифтов на LCD-мониторах), будут недоступны.
ПРИМЕЧАНИЕ Я не случайно не упомянул про Windows 95. На сайте Microsoft отсутствует всяческое упоминание о поддержке GDI+ для этой операционной системы. Тем не менее, единственная доступная мне для тестирования машина с Windows 95 OSR2 выполнила тестовое приложение без каких-либо проблем. Но ввиду отсутствия какой-либо официальной поддержки для использования GDI+ крайне рекомендуется обновить систему хотя бы до Windows 98.
Поддерживаемые технологии разработки
Набор заголовочных файлов (headers) и библиотека импорта GdiPlus.lib, необходимые для сборки демонстрационных приложений, входят в состав последнего Platform SDK. Те, кто до сих пор не обновил идущий с Visual Studio 6.0 Platform SDK образца 1998 года, могут загрузить его с сайта Microsoft по адресу:
Минимальный компонент, в состав которого входит GDI+, называется Windows Core SDK и имеет размер около 230 мегабайт.
ПРИМЕЧАНИЕ Я понимаю, что для многих читателей, имеющих доступ в Интернет через домашний модем, предложение скачать дистрибутив такого размера прозвучит как насмешка. В качестве крайней временной меры можно раздобыть только набор заголовочных файлов GdiPlus*.h, BaseTsd.h и библиотеку импорта GdiPlus.Lib из нового Platform SDK. Но гарантировать работоспособность такого решения во всех ситуациях я не возьмусь. Да и в любом случае, обновить Platform SDK необходимо. Возможно, вам удастся найти его на CD-ROM.
На момент написания этих строк доступна версия Platform SDK за август 2001 г.
Демонстрационные примеры будут в подавляющем большинстве написаны с использованием Windows API, что позволит сосредоточиться на использовании GDI+. Но вы без труда сможете подключить эту библиотеку к своим MFC- или WTL-приложениям. Иногда я также буду приводить соответствующий пример на C# для WinForms.
Начинаем работу
Иерархия классов GDI+
Типичное рабочее место программиста на C++, как правило, включает в себя стену, на которой гордо красуется Диаграмма классов (неважно каких). Теперь рядом можно наклеить еще один плакат.
При первом взгляде на диаграмму видно, что она очень напоминает, например, ту часть библиотеки MFC, которая отвечает за рисование, только классов гораздо больше (40 против 15 у MFC). Это и неудивительно, учитывая фирму, которая разрабатывала эти библиотеки. Основные отличия отражают новые возможности GDI+. Мы подробно рассмотрим их в следующих частях.
Ключевым же классом в GDI+ является Graphics (программисты на J++ вздрогнули). Именно он содержит почти две сотни методов, отвечающих за рисование, отсечение и параметры устройства вывода. Напрашивается явная аналогия с контекстом устройства (Device Context) прежнего GDI, и эти понятия действительно тесно связаны. Из четырех конструкторов Graphics два создают его из HDC. Главное отличие заключается в изменении программной модели: теперь вы не работаете с хендлом, а вызываете методы класса. Хотя программистам на MFC эта концепция уже хорошо знакома.
Дальнейшее наследование (например, класс TextureBrush порожден от Brush ) скорее отражает цели разработчиков (скрытие деталей реализации и повторное использование оберточного кода), чем инфраструктуру библиотеки, так как в inline-методах «родственных» классов просто содержатся вызовы различных функций GdiPlus.dll. Можно сказать, что Microsoft в очередной раз спроецировала обычный «плоский» API языка C на объектно-ориентированную библиотеку C++.
Оставшаяся часть классов не имеет общего родителя и предназначена для упрощения работы со структурами данных GDI+.
Инициализация и завершение
Перед тем как начать использовать классы и функции GDI+, необходимо инициализировать эту библиотеку. Для этого где-нибудь в начале своей программы нужно поместить вызов функции GdiplusStartup:
Поля структуры GdiplusStartupInput управляют различными аспектами инициализации: в частности, можно задать функцию, которая будет вызываться при возникновении ошибок, или перехватывать все обращения к функциям GDI+. Эти детали мы рассматривать не будем. К счастью, конструктор по умолчанию структуры GdiplusStartupInput выполняет инициализацию, достаточную в большинстве случаев. При этом в качестве выходного параметра output можно задать NULL.
«Магическое значение», на которое указывает выходной параметр token, необходимо сохранить.
Для завершения работы с библиотекой вызовите функцию GdiplusShutdown:
Здесь в качестве параметра и необходимо передать то самое число, которое возвратила GdiplusStartup в параметре token.
Создаем первое приложение
Настало время применить все эти сведения на практике. Для этого создадим в MS Visual C++ базовое WINAPI-приложение, которое послужит полигоном для дальнейших экспериментов. Ниже для этого приведена пошаговая процедура.
Итак, создаем новый проект Win32 Application. Выбираем опцию A typical «Hello, World!» application и нажимаем «Finish». Получившееся приложение необходимо подготовить для использования GDI+. Для этого в файле stdafx.h после строки с комментарием:
добавляем следующие строчки:
и в конце файла stdafx.cpp добавляем строку
Кроме того, в файле stdafx.h необходимо удалить или закомментировать строку
Иначе компилятор выдаст кучу ошибок об отсутствии символов MIDL_INTERFACE, PROPID, IStream и т.д.
Если полученное в результате приложение успешно собралось, значит, мы все сделали правильно. Пойдем дальше.
Готово. Наконец-то мы можем что-нибудь нарисовать. Найдите в теле функции WndProc обработчик сообщения WM_PAINT и замените следующим кодом:
Теперь где-нибудь перед функцией WndProc создадим функцию OnPaint с кодом рисования:
В результате у нас получится примерно вот что:
Замечу, что здесь приведен полный текст программы, аналогичной по возможностям той, что мы создали в предыдущем разделе. Сравните объем исходных текстов этих двух примеров. NO COMMENTS.
Несколько замечаний о компиляции и сборке проектов
Хочется указать на несколько «подводных камней», которые могут сбить с толку при первой попытке откомпилировать и собрать проект, использующий GDI+. В основном здесь упомянуты те проблемы, с которыми сталкиваются (и постоянно спрашивают о них в различных форумах) начинающие.
Где взять GdiPlus.h?
Как я уже сказал, все заголовочные файлы, библиотека импорта и документация к библиотеке входят в состав последнего Platform SDK. Они не идут в составе Visual С++ 6.0 и его сервис паков.
Почему выдается ошибка о типе ULONG_PTR?
Почему компилятор не дает создать объект GDI+ при помощи new?
Такое поведение возможно при попытке откомпилировать MFC-приложение с использованием GDI+ в Debug-конфигурации.
В начале файла программы, видимо, имеется следующий фрагмент:
Не забудьте про пространство имен Gdiplus и библиотеку импорта
В приводимых примерах кода используются простые имена классов, такие как Brush и Rect. Это стало возможным благодаря тому, что в начале заголовочного файла программы есть директива
Если это решение не подходит (например, в проекте уже существуют классы с такими именами), то перед именами классов необходимо ставить префикс пространства имен, например
Также, если по каким-то соображениям директива
На этом пока все. В следующей части мы рассмотрим богатые возможности, которые GDI+ предоставляет для работы с растровыми изображениями.
Опубликовано: 14.05.2002 Исправлено: 13.03.2005 Версия текста: 1.0
Необходимые замечания к предыдущей части
Прежде всего, я благодарю всех читателей, которые откликнулись на первую часть статьи со своими замечаниями и комментариями. Похоже, что затронутая тема интересует многих программистов, решающих задачи обработки графики. В этом разделе я постарался ответить на многие вопросы, возникшие при обсуждении GDI+. Данная статья вовсе не заменяет документацию Platform SDK, а, напротив, пытается восполнить ее пробелы на основе собственных экспериментов и общения в Usenet (преимущественно в группах новостей microsoft.public.dotnet.framework.drawing и microsoft.public.win32programmer.gdi ).
Несколько слов о производительности
Большое количество обсуждений было связано с вопросами производительности GDI+. Появились даже «экстремальные» суждения о полной непригодности этой графической библиотеки как чрезвычайно медлительного средства. Немалую роль в таком отношении сыграло и молчание MS в ответ на многочисленные вопросы о поддержке в GDI+ средств аппаратной акселерации. Представители Microsoft Developer Support лишь туманно отвечали, что, дескать, сравнивать производительность GDI+ и GDI не совсем корректно (читай: GDI+ не выигрывает от сравнения) и что версия 1.0 не имеет собственного Device Driver Interface (читай: аппаратная акселерация отсутствует).
В последнее время заявления представителей Microsoft стали вселять б о льшую надежду. Было сказано, что «команда разработки GDI+ прекрасно знает о том, что производительность требует улучшения во многих областях» и что «производительность – это то, над чем сейчас работает команда GDI+». В компьютерном сообществе немедленно начали циркулировать слухи о том, что аппаратная акселерация будущих версий GDI+ связана с выходом DirectX 9. Ну что же, поживем – увидим…
Со своей стороны скажу следующее.
Во-первых, вычислительная мощность современных PC уже достигла того уровня, когда 90% ресурсов процессора офисное приложение может позволить себе тратить на рисование. В профессиональных пакетах компьютерной графики такие же изобразительные средства достигаются тяжким трудом программистов (и по цене примерно такой же скорости). Впервые средство создания офисной графики такого качества попало в руки массовых разработчиков. И неразумно отказываться, например, от удобной поддержки графических форматов в GDI+ только из-за того, что вас не устраивает в ней, скажем, скорость вывода примитивов.
Кроме того, GDI+ и не создавалась как «игровой движок» (для этого существуют такие мощнейшие API, как DirectX и OpenGL), а во многих других областях ее производительности вполне хватает – взгляните хотя бы на интерфейс Windows XP. Если же еще грамотно использовать возможности оптимизации этой библиотеки (постараюсь сказать и об этом), то можно поднять ее производительность до более высокого уровня.
Не стоит забывать и про возможности GDI – в тех областях, где производительность и/или функциональность GDI+ оставляет желать лучшего, можно использовать эти средства вместе. К этой теме я еще буду возвращаться, сейчас лишь приведу полезную ссылку на Microsoft Knowledge Base: INFO: Interoperability Between GDI and GDI+ (Q311221).
Поправка
Действительно, обработчик сообщения WM_PAINT окна приложения выглядит так:
Такой подход будет работать, пока окно перерисовывается целиком. Но, если была временно скрыта, а затем показана часть окна (например, при открытии и закрытии пункта меню), то произойдет неприятность. Windows оптимизирует процесс перерисовки, передавая в структуре ps.rcPaint лишь координаты области, нуждающейся в обновлении. Код обработчика передаст эти же координаты в функцию OnPaint, которая послушно воспроизведет в этой маленькой области копию всего большого окна. Очевидно, что это совсем не то, что нужно.
ПРИМЕЧАНИЕ
Чтобы быть до конца точным, добавлю, что при изменении размеров окна приложения также могут приходить сообщения о частичной перерисовке. Это произойдет, если для класса окна при его регистрации не будут выставлены стили CS_HREDRAW и CS_VREDRAW.
Для того чтобы учитывать частичную перерисовку, есть два общих подхода. Первый означает, что мы доверяем Windows операции отсечения (clipping) и передаем в OnPaint координаты всей клиентской области окна (изменения выделены в листинге):
Замечу, что для эффективного отсечения GDI+ необходимо модифицировать и код функции OnPaint (добавить вызов метода Graphics::SetClip с координатами области перерисовки). При этом все операции рисования остаются неизменными, а необходимость их выполнения определяется ядром GDI+.
Другой подход является более правильным, но он сложен в реализации, и мы его рассматривать здесь не будем. Он подразумевает самостоятельное определение только тех действий, которые необходимы для частичной перерисовки. При этом можно добиться значительного выигрыша в скорости (например, отказавшись от загрузки шрифта, если выводимый текст не попадает в область вывода).
Динамическая загрузка GdiPlus.dll
Возник довольно интересный вопрос: как можно избежать системного сообщения об ошибке и продолжить работу с использованием GDI, если система, в которой запущено приложение, не поддерживает GDI+? Ведь, в отличие от большинства обычных библиотек импорта, «начинка» GDI+ спрятана за структурой классов-оберток…
Здесь может помочь такая сравнительно малоизвестная возможность Visual C++, как отложенная загрузка (Delayed Loading) DLL. При использовании этой опции компоновщик генерирует специальные заглушки для всех импортируемых функций. Приложение стартует немного быстрее, а реальная загрузка библиотеки откладывается (отсюда и название) до первого вызова любой импортируемой функции. Подробно почитать об этой технике можно в декабрьском выпуске MSJ за 1998 год сразу в двух колонках: «Under The Hood» Мэтта Питрека и «QnA Win32» Джефри Рихтера, здесь же только упомяну, что для использования отложенной загрузки необходимо включать библиотеку DELAYIMP.LIB.
Скрыть детали использования DLL и инициализации GDI+ можно, используя примерно такой класс:
Таким образом, этот класс самостоятельно занимается инициализацией/очисткой GDI+, а также обрабатывает структурное исключение, возникающее при отсутствии библиотеки Gdiplus.dll. Для инициализации библиотеки достаточно объявить экземпляр такого класса. Этот способ используется в примерах к статье.
ПРИМЕЧАНИЕ
Должен признаться, что использование в проекте опций отложенной загрузки GdiPlus.Dll иногда приводит к каким-то проблемам в Debug-конфигурации. При попытке собрать приложение с отладочной информацией компоновщик с упрямством выдавал фатальную ошибку. Release- же конфигурация собиралась нормально. Поэтому я отказался от использования delayed loading в отладочных проектах, используя условную компиляцию.
Поддержка Windows 95
Что касается технической стороны вопроса, то эксперименты показали: установка дистрибутива gdiplus.dll в системах с Windows 95 возможна, и демонстрационные приложения с использованием GDI+ выполняются без заметных проблем.
С правовой же точки зрения, этого делать нельзя. Microsoft достаточно жестко указывает в лицензионном соглашении дистрибутива, что он может устанавливаться только на следующие операционные системы: «Windows 2000, Windows Millennium Edition, Windows NT 4.0 and Windows 98». Как уже говорилось, Windows XP располагает собственной версией GDI+.
С чем связано такое ограничение – можно только догадываться. Возможно, с тем, что компания из Редмонда вообще прекратила техническую поддержку Windows 95.
Что нового?
За время, прошедшее с выхода первой части, произошли некоторые изменения.
Похоже, введение затянулось. Ну что же, перейдем, наконец, к теме этой части.
Итак, в этой части статьи мы постараемся хорошенько изучить класс Bitmap и особенности работы с ним. Такое внимание к единственному классу вполне обосновано: во-первых, он предоставляет очень много возможностей, а, во-вторых, без использования Bitmap в GDI+ вообще невозможно работать с растровой графикой. Даже если вы создаете кисть с растром (TextureBrush) или контекст устройства (Graphics) в памяти, для их инициализации потребуется экземпляр Bitmap.
Поддержка основных графических форматов
Это качество является одним из наиболее привлекательных свойств библиотеки GDI+. Например, скромный и неприметный редактор Paint в Windows XP неожиданно приобрел возможность открывать и сохранять не только картинки BMP, но также и JPG, TIF, GIF и PNG, что сразу сделало его на порядок более мощным средством. Это полезное качество появилось в нем благодаря использованию GDI+ (Paint из комплекта Windows 2000 тоже поддерживал GIF и JPG, но делал это заметно хуже, используя набор модулей расширения FLT для Office 97).
К графическим фильтрам GDI+ уже прочно прикрепилось жаргонное название «кодек» (codec, Compressor/Decompressor). Чтобы не отстать от моды, будем их так называть и мы.
У класса Bitmap существует набор перегруженных конструкторов для создания растра из всевозможных источников. При создании объекта Bitmap, например, из файла, анализируется его формат (а вовсе не расширение!), и автоматически используется соответствующий кодек. Определить, кодек какого формата был использован для загрузки, можно при помощи метода Image::GetRawFormat :
Каждый кодек по мере возможностей учитывает специфические качества своего формата – например, загрузчик GIF правильно прочитывает прозрачные GIF89a, чего так не хватало функции OleLoadPicturePath.
Имеется также возможность сохранения созданных растров в графических файлах различных форматов. Более подробно работа с кодеками будет рассматриваться в Части III, здесь же узнаем о некоторых особенностях загрузки и отображения растров.
Загрузка из файлов и потоков IStream
Итак, для загрузки графического файла в экземпляр класса Bitmap существует следующий конструктор:
Параметр filename должен содержать имя существующего файла. Как уже отмечалось, все строковые параметры методов GDI+ требуют UNICODE-строк, поэтому при передаче строковой константы в программе на C++ необходимо предварять ее префиксом ‘L’.
Параметр useIcm определяет, будет ли при загрузке растра использоваться ICM (Image Color Management) и по умолчанию равен FALSE. Если же использовать ICM необходимо, графический файл должен содержать всю необходимую информацию, например, цветовые профили конкретных устройств.
Существует достаточно неприятная ошибка в файловом загрузчике форматов – часто (но не всегда), при указании несуществующего имени файла, вместо того, чтобы вернуть код ошибки в переменной Status, приложение завершается с выдачей примерно такого сообщения (на самом деле, в системах Windows 9x окно сообщения об ошибке выглядит еще уродливее):
Microsoft признает наличие этой ошибки, и в скором времени планируется выход заплатки (и очередной статьи в Knowledge Base). Сейчас единственный разумный способ избежать такого исхода – это убедиться в существовании файла перед его открытием.
ПРЕДУПРЕЖДЕНИЕ
Недавно найдены еще одни «грабли», связанные с тем, что некоторые цифровые фотокамеры не записывают в сохраняемый фотоснимок TIFF его физические размеры. Такой файл прекрасно прочитается кодеком TIFF, и, при везении, даже нарисуется – если явно задать пиксельные размеры получаемого рисунка. В противном случае, GDI+ услужливо попытается рассчитать их на основании физических размеров – и работа приложения завершится с такой же «диагностикой». Будьте бдительны!
Более гибкие возможности загрузки таятся в таком конструкторе Bitmap:
Создание растров из ресурсов программы
Следующий конструктор Bitmap позволяет загрузить растр из ресурсов:
Но не обольщайтесь, далеко не всякий ресурс удастся загрузить таким образом. Этот конструктор предназначен для загрузки именно BITMAP-ресурсов, и не поддерживает, скажем, загрузку GIF. Возможно, это ограничения реализации. К счастью, их достаточно легко обойти: просто предоставьте загрузчику интерфейс IStream с необходимыми данными. Это можно сделать, например, воспользовавшись функцией Windows API CreateStreamOnHGlobal :
У меня есть серьезное подозрение, что вызов Release в вышеприведенном примере не приведет к немедленному уничтожению потока IStream. Похоже, при загрузке из файлов и потоков объекты Bitmap удерживают источник данных в течение своей жизни и «отпускают» его только в деструкторе. Если об этом забыть, потом можно долго удивляться, почему к графическому файлу нет доступа после прочитывания его в Bitmap.
В таком случае загрузка изображения из ресурсов будет выглядеть примерно так:
Загрузка из растровых данных и объектов GDI
Помимо чтения растров из мест их внешнего хранения, объект Bitmap предоставляет возможность создания «на лету» – из существующих в памяти растров, объектов GDI и поверхностей DirectDraw. Вот соответствующие конструкторы:
Важно понимать, что все эти конструкторы «однонаправленные» – при их вызове в объекте Bitmap создается собственный буфер, в который копируются (с необходимым преобразованием) растровые данные. При любых модификациях объекта Bitmap (если, например, создать на нем объект Graphics и попробовать в него нарисовать) источник данных остается неизменным – даже после выполнения деструктора Bitmap. Более того, перенос изменений обратно в исходный объект GDI часто сопряжен с трудностями – например, может потребоваться самостоятельная работа с палитрами и битовыми массивами. Некоторые рекомендации по этому поводу можно встретить ниже, в разделе «Работа с растровыми данными напрямую».
Отмечу, что, на мой взгляд, в GDI+ было уделено большое внимание обратной совместимости. Вместе с тем, эта совместимость в значительной степени реализована в режиме «только для чтения». Так, например, библиотека корректно прочитает из памяти растр с заголовком BITMAPV4HEADER, содержащим информацию о альфа-канале, но при сохранении в BMP из GDI+ будет сгенерирован только заголовок BITMAPINFOHEADER, и вся информация о прозрачности пропадет.
Что же касается сохранения растров с прозрачностью, к вашим услугам форматы TIFF, PNG и GIF (об этом речь пойдет в Части 3).
Существуют также конструкторы, позволяющие задать формат создаваемого растра:
Можно самостоятельно выделить буфер для создаваемого растра. Вот соответствующий конструктор:
Здесь stride – величина смещения (в байтах) между строками растра, scan0 – адрес созданного буфера. Невзирая на формат, величина stride должна делиться нацело на 4 (и естественно, быть достаточно большой для хранения строки пикселов необходимой ширины). Она может также быть отрицательной (для создания растров с обратным порядком следования строк). После уничтожения объекта Bitmap выделенный буфер не удаляется – ответственность за его освобождение лежит на создателе.
Взаимодействие растров с объектом Graphics
Текущая реализация библиотеки оперирует 32-битным цветом при любых цветовых вычислениях, даже если один из оперируемых растров имеет индексные (основанные на палитре) цвета. Таким образом, хороший способ экономить ресурсы процессора и память системы – это сразу работать с 32-битными растрами (что и предлагается по умолчанию). Например, при создании экземпляра Graphics из растрового контекста устройства (HDC) создается также промежуточный 32-битный буфер, в который и осуществляется вывод, а уже затем производится копирование этого буфера на контекст. Если контекст устройства имеет другую глубину цвета, то понадобится дополнительное преобразование, что плохо скажется на производительности. Кроме того, такая организация работы с графическими контекстами диктует свои ограничения на обращение к контексту средствами GDI – во время существования Graphics этого делать нельзя. За подробностями обращайтесь к Q311221.
Вывод изображений и геометрические преобразования
Поддержка координатных преобразований в GDI+ – слишком большая тема, чтобы касаться ее в разговоре о растровой графике. Однако эта библиотека предоставляет также специальные средства геометрических преобразований при выводе изображений, о которых сейчас пойдет речь.
Обратите внимание на выделенные параметры. Такой способ (явное указание размеров выводимого изображения) является предпочтительным, и вот почему.
Во-первых, уже упоминалось, что некоторые файлы не содержат информацию о разрешении. При выводе такого изображения без указания размеров библиотека GDI+ попытается вычислить их на основании разрешения, и произойдет сбой.
Во-вторых, явно указав исходные пиксельные размеры растра, вы тем самым подсказываете GDI+, что масштабирования выполнять не нужно, и вывод произойдет быстрее.
2. Вывод изображения или его части в параллелограмм с аффинными преобразованиями координат всех точек исходного растра. В GDI отсутствует подобная функциональность. Этим методам для работы требуется массив из 3-х точек, образующих вершины параллелограмма (четвертая вершина вычисляется на их основе).
Но, конечно, в большинстве случаев для поворота выводимого растра проще будет применить координатные преобразования GDI+.
Кроме того, для поворота изображений на угол, кратный 90 градусов, или их отражения относительно осей симметрии, в классе Image существует метод
ПРИМЕЧАНИЕ
Замечу, что при сохранении растров формата JPEG, повернутых (или отраженных) таким образом, дополнительной потери качества не происходит.
Качество изображения
Увеличивая, растягивая и искажая исходную картинку, можно запросто получить в результате нечто безобразное – зубчатые края изображения, ступенчатые границы прямых линий… Причина кроется в самой природе растровой графики – изображение несет в себе информацию, ограниченную выбранным разрешением.
Тем не менее, существуют различные алгоритмы, позволяющие добиться значительного улучшения качества результирующего изображения, или, точнее говоря, качества его человеческого восприятия. Некоторые из них изначально встроены в библиотеку GDI+.
Метод Graphics::SetInterpolationMode позволяет указать, какой режим (или алгоритм) интерполяции будет использован при выводе изображения с изменением его пиксельных размеров. Константы возможных режимов описаны в перечислении InterpolationMode:
Разумеется, выигрывая в качестве, проигрываешь в скорости, поэтому при использовании режима с наивысшим качеством InterpolationModeHighQualityBicubic медленные компьютеры могут выводить изображения больших размеров в течение нескольких секунд! Но только этот метод способен адекватно отображать картинку при уменьшении ее до 25 процентов (и менее) от оригинала. Этот режим очень поможет различным автоматическим генераторам иконок ( thumbnail ) изображений в ASP.NET.
На качество (и скорость) вывода растров также влияют некоторые другие установки класса Graphics. Перечислим их с кратким описанием:
Метод
Назначение
SetSmoothingMode
Позволяет указать метод устранения ступенчатости ( antialiasing ) при выводе примитивов – линий и геометрических фигур
SetCompositingMode
Устанавливает или отключает учет прозрачности при наложении растровых изображений
SetCompositingQuality
Управляет качеством расчета цветовых компонентов при наложении растров
SetPixelOffsetMode
Задает метод учета смещения пикселов при интерполяции. Грубо говоря, определяет, являются ли координаты пикселов (или их центров) целыми числами при расчетах
SetRenderingOrigin
Устанавливает позицию начальной точки при смешении ( dithering ) цветов в 8- и 16-битных режимах
Устранение мерцания
При этом все операции вывода с участием такого объекта отразятся на содержимом используемого экземпляра Bitmap. Это предоставляет очень простую возможность двойной буферизации вывода – когда изображение сначала готовится «за кадром», а затем мгновенно (ну, почти мгновенно) переносится на экран, устраняя досадное мерцание при анимации:
Для создания «теневого» Graphics подойдет далеко не всякий растр – в частности, создание провалится для индексных (работа с палитрами при выводе не поддерживается) и Grayscale-растров (состоящих из оттенков серого цвета). Это связано именно с ограничениями ядра GDI+.
При этом, кстати, вывод довольно заметно ускоряется (несмотря на необходимость дополнительного переноса на экран) – у меня демонстрационное приложение вместо 70 FPS стало выдавать 75-80.
Еще несколько слов о производительности
На этот конструктор накладываются определенные ограничения. В частности, устройство вывода Graphics не должно быть связано с HDC принтера или метафайла. Далее, при смене характеристик устройства вывода (например, при изменении разрешения или глубины цвета экрана) CachedBitmap необходимо пересоздавать для работы с новым устройством, в противном случае вывод производиться не будет.
Для вывода оптимизированных растров на экран служит метод Graphics::DrawCachedBitmap :
Хорошая новость : DrawCachedBitmap поддерживает прозрачность и альфа-канал (проверено в лабораторных условиях).
Плохая новость : применение координатных преобразований к устройству вывода не поддерживается (кроме координатного переноса). В частности, поворачивать такие растры при выводе, к сожалению, нельзя. Если это необходимо, примените технику промежуточного вывода в память с поворотом, а затем уже кэшируйте полученный Bitmap. Разумеется, этот прием не подойдет, если угол поворота меняется динамически (тогда лучше совсем отказаться от кэширования, чтобы не тратить каждый раз время на создание промежуточного изображения).
Перед тем, как использовать CachedBitmap, подумайте о возможных неудобствах: вам придется отлавливать момент изменения видеорежима и соответственно перестраивать все оптимизированные растры. Кроме того, выгода от их применения невелика: у меня в разных тестах она не превышала 9% (при избавлении от многих других тормозящих операций). Куда выгоднее оказалось вынести создание промежуточного контекста Graphics из функции OnPaint в код инициализации, хотя и это не панацея: такой буфер придется пересоздавать при изменении размеров окна.
Демонстрационные приложения
В завершении данного раздела приведу два небольших демонстрационных приложения.
Первое написано на WinAPI. Оно иллюстрирует применение многих описанных приемов работы: отложенную загрузку GdiPlus.DLL, загрузку растров формата GIF из ресурсов программы и использование двойной буферизации для устранения мерцания. Кроме того, в исходном коде есть (хотя и упрощенный) пример работы с анимированным изображением формата GIF.
Второе написано на языке C# и представляет собой пример вывода анимированных файлов GIF в окне WinForms-программы. В нем также реализованы подсчет производительности (с использованием класса System.Timers.Timer) и двойная буферизация.
Работа с растровыми данными напрямую
К сожалению, у библиотеки GDI+ существуют и ограничения. Некоторые из них связаны с дефектами текущей реализации, и, возможно, вскоре исчезнут. Другие же напрямую следуют из новой архитектуры библиотеки, которая попыталась избавиться от «проклятого наследия» кое-каких архаизмов GDI. Так или иначе, зачастую приходится обращаться напрямую к растровым данным, содержащимся внутри этих красивых оберток. В данном разделе мы узнаем, какие возможности для этого предоставляет GDI+.
Класс Color
В 32-битной цветовой модели наконец-то нашлось применение четвертому байту: он больше не является просто мусором для выравнивания, а законно хранит значение Alpha – технически говоря, величину непрозрачности пиксела. Это было учтено при проектировании класса Color – у него появился соответствующий конструктор:
Если используется более традиционная форма конструктора Color с тремя цветовыми компонентами, то значение alpha устанавливается равным 255 (полная непрозрачность). Кроме того, в классе Color описан большой набор именованных цветовых констант (например, Color::AliceBlue ) – в них значение Alpha также равно 255.
а также реализация стандартного метода ToString, позволяющая получить «читабельную» строку с названием цвета (если это возможно) или с перечислением его ARGB-компонентов (в противном случае):
Прямой доступ к пикселам
Для получения и установки цвета определенной точки растра класс Graphics предоставляет методы GetPixel / SetPixel :
Что можно о них сказать? Не используйте их, если это возможно – их производительность ужасна (как, впрочем, и производительность аналогичных функций GDI).
Более быстрым способом будет получение доступа сразу к некоторой прямоугольной области растра. Для этого необходимо использовать метод Bitmap::LockBits :
ПРИМЕЧАНИЕ
На самом деле вызов LockBits приводит к копированию указанной области во временный буфер. Изменение растровых данных в этом буфере отразится на содержимом Bitmap только после обратного вызова UnlockBits с тем же указателем lockedBitmapData в качестве параметра.
При этом если указать формат временного буфера (PixelFormat), отличный от формата исходного растра, вызовы LockBits/UnlockBits потребуют дополнительных преобразований.
Для доступа к растру исходного изображения можно также воспользоваться следующим (недокументированным) обстоятельством. В GDI+ версии 1 метод Bitmap::GetHBitmap всегда возвращает DIB Section. Вам достаточно вызвать GetObject() для этого HBITMAP, чтобы получить растровые данные и необходимые структуры BITMAPINFO (пример работы с DIB Sections см. в Q186221). Однако на это поведение нельзя полагаться в будущих версиях библиотеки.
Поддержка прозрачности
Как мы уже знаем, внутренним форматом библиотеки является 32-битный с альфа-каналом. Из этого факта следует, что растры с прозрачными областями прекрасно поддерживаются: достаточно задать 0 в поле Alpha, и пиксел будет рассматриваться как полностью прозрачный. Именно так загрузчик GIF преобразует растры в 32-битный формат.
Если возникла необходимость назначить прозрачным определенный цвет растра, сделать это можно несколькими способами. Во-первых, используя прямую замену цветов при помощи функций GetPixel/SetPixel. При этом придется пройтись по всем точкам картинки, заменяя точки выбранного цвета на прозрачные. Как уже говорилось, этот способ не является быстрым. Во-вторых, можно применить прямой доступ к памяти посредством вызова LockBits.
Если вы действительно вставите данный кусок в демонстрационную программу, то заметите досадную ошибку в текущей версии GDI+. При передаче в метод Graphics::DrawImage ненулевого указателя на класс ImageAttributes начинает выводиться только первый кадр анимации, несмотря на то, что активным кадром может быть любой другой.
Растровые операции
В Usenet часто задается вопрос: а как можно заставить GDI+ использовать растровые операции (ROP), определенные в GDI? Например, для выделения набора объектов было бы неплохо их инвертировать (или закрасить инверсным прямоугольником). В САПР может понадобиться рисовать инверсную фигуру или контур (макет) будущего объекта. Кроме того, режим R2_XOR позволяет очень просто восстановить изображение под объектом, всего лишь повторно нарисовав объект на том же месте.
Специалисты Microsoft обычно отвечают в духе Дзен: «На самом деле, Вам не нужна такая возможность. XOR-графика попросту уродлива (согласен! – В.Б. ). Современные 32-битные графические видеорежимы позволяют выделять и накладывать изображения с помощью Alpha-канала. Применение различных кодов ROP для достижения прозрачности также устарело – прозрачность изначально реализована в GDI+». И действительно, в GDI+ вообще не поддерживаются ROP. Если попробовать «силой» выставить контексту устройства, например, режим R2_XOR, он будет проигнорирован при выводе.
Как использовать эти методы? Вот ссылки на статьи Knowledge Base c соответствующими примерами:
Не зная о существовании этих статей, я самостоятельно набрел на эту возможность и написал пример на C#, позволяющий инвертировать часть изображения. Попробуйте запустить пример, нажать где-нибудь на свободном участке формы левую кнопку мыши и потащить курсор. Выделенный участок инвертируется даже за пределами формы – взаимодействие высокоуровневой GDI+ и низкоуровневой GDI налицо!
Думаю, на этом пора остановиться. Следующая часть статьи будет целиком посвящена средствам работы с графическими форматами в GDI+.