Аннотация:
В статье рассмотрена работа с растрами средствами GDI+ — новой библиотеки от Microsoft. Описываются методы создания растров из внешних источников, их взаимодействие с устройствами вывода и работа с графическими файлами.
Продолжая тему, хочу поделиться своими наблюдениями.
Если создать Bitmap конструктором:
Bitmap(const BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData);
внутреннего буфера в bitmap-e, видимо, не создается и изменения, произведенные, "вручную" в данных gdiBitmapData, отражаются на bitmap-объекте и наооборот. Еще одна фича, которая мне понравилась — это создать объект Graphics на основе bitmap-а. Тогда можно рисовать прямо на буффере gdiBitmapData методами Graphics.
Привет. Отличная статья.
По поводу Bitmap::GetHBITMAP() — если я правильно понял описание в SDK, то этот метод создает HBITMAP — копию изображения, содержащегося в объекте Bitmap. В этом случае изменение данных в HBITMAP не отобразится на Bitmap. Похоже в любом случае для быстрой работы (модификации) данных придется с использовать копию, создаваемую LockBits, а потом заливать ее обратно в Bitmap с помощью UnlockBits. Есть ли еще способы быстрого доступа к растровым данным?
Статья обошла стороной очень интересные возможности GDI+ в области работы с картинками, связанные с использванием класса ImageAttributes при отрисовке растра.
Этот класс позволяет проделывать много презабавных штук с картинками, в частности — задавать при отрисовке линейное преобразование каждого пиксела в пространстве ARGB. Есть и другие полезные возможности.
Спасибо за отзыв.
Действительно, метод GetHBITMAP создаст _копию_ растровых данных, но содержащуюся именно в DIB Section, как и было сказано в статье. Правда, это поведение так и не было документировано.
Если же требуется быстрый доступ к битам растра на запись, то можно попробовать пойти от противного: держать растр изначально в "сыром" формате DIB Section, а для получения GDI+ Bitmap создавать его, например, при помощи FromHBITMAP. При этом способ вполне законный и налицо экономия одного копирования.
Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере
600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются.
Что посоветуешь для сжатия выходных картинок?
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Здравствуйте, dad, Вы писали:
R>>Спасибо за отзыв.
dad>Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере dad>600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются. dad>Что посоветуешь для сжатия выходных картинок?
Здравствуйте, AP999, Вы писали:
AP>Продолжая тему, хочу поделиться своими наблюдениями. AP>Если создать Bitmap конструктором: AP>Bitmap(const BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData); AP>внутреннего буфера в bitmap-e, видимо, не создается и изменения, произведенные, "вручную" в данных gdiBitmapData, отражаются на bitmap-объекте и наооборот. Еще одна фича, которая мне понравилась — это создать объект Graphics на основе bitmap-а. Тогда можно рисовать прямо на буффере gdiBitmapData методами Graphics.
Здравствуйте, dad, Вы писали:
dad>Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере dad>600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются. dad>Что посоветуешь для сжатия выходных картинок?
Странно — у меня скриншот экрана в формате PNG занимает около 50 килобайт при формате 1024х768.
Вот код (можно скачать отсюда
dad>>Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере dad>>600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются. dad>>Что посоветуешь для сжатия выходных картинок?
R>Странно — у меня скриншот экрана в формате PNG занимает около 50 килобайт при формате 1024х768. R>Вот код (можно скачать отсюда
dad>>Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере dad>>600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются. dad>>Что посоветуешь для сжатия выходных картинок?
AS>TIFF c LZW недостаточно сжимает?
мне нужен был png & gif
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
dad>>Отлично создаются форматы gip, png, только вот немеренных объемов ( до 2М) при развмере dad>>600х400 , как я ни бился сжимать не удается, атрибуты качества есть только у jpeg , но они както игнорируются. dad>>Что посоветуешь для сжатия выходных картинок?
R>Странно — у меня скриншот экрана в формате PNG занимает около 50 килобайт при формате 1024х768.
У меня был баг, я в процедуре выбора кодека, написал
if( frm.compare( pImageCodecInfo[j].MimeType ) == 1 )
врезультате чего у меня всегда все сохранялось в едином формате,bmp
поэтому ие и шоп не понимал, только мозила умная
Но cximage все таки получше ужимает, да более явно у него все сделано в том числе и с доп. настройкой качества и весит не много (40-60К) к экзезешничку если статично.
Короче если бы знал, что сделаю с cximage от gdiplus, наверно, отказался бы теперь уже просто некогда либу переписывать под winapi
Спасибо что откликнулся.
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re: GDI+: графика нового поколения
От:
Аноним
Дата:
10.02.05 15:29
Оценка:
Здравствуйте, Виталий Брусенцев, Вы писали:
ВБ>Статья:
ВБ>Авторы: ВБ> Виталий Брусенцев
ВБ>Аннотация: ВБ>В статье рассмотрена работа с растрами средствами GDI+ — новой библиотеки от Microsoft. Описываются методы создания растров из внешних источников, их взаимодействие с устройствами вывода и работа с графическими файлами.
Скажите, пожалуйста, а как создасть кисть GPBrush, реализующую текстурную заливку (элементом GPImage) с заданным значением полупрозрачности (альфа-канала для всех пикселей изображения), эта кисть затем применяется для всевозможных заливок!!!
Хорошо.
Но я бы хотел знать вовсе не то какие существуют классы использующие библиотеку gdiplus.dll.
Мне нужно знать как непосредственно использовать подпрограммы из этой библиотеки.
Я понимаю, что классы разных там языков используют эти подпрограммы.
Но прочитав вашу статью я так и не понял как именно используются сами подпрограммы этой библиотеки.
Во первых — это интереснее, во вторых в голове сразу становится яснее понимание работы компьютера.
К сожалению, я не могу по другому лучше понять использование библиотек API.
ДА И НЕ ХОЧУ В ПРИНЦИПЕ. ВОТ. Где я могу узнать об использовании подпрограмм библиотеки gdiplus32.dll чтобы мне меньше всего начисляло по трафику?
Ну очень хочется узнать как использовать сами подпрограммы.
Представьте себе, если программа составляется на АССЕМБЛЕРЕ, где нет этих классов.
Там нужно записывать в стек определенные параметры инструкциями процессора например
push eax
и затем вызывать нужную подпрограмму библиотеки инструкцией, например
call GdipBitmapSetPixel
Вот так например я могу на АСМе вызвать функцию SetDIBitsToDevice
библиотеки gdi32.dll
Я считаю, что такой путь использования функций графических библиотек понятнее.
Я серьезно говорю. Пусть на меня никто не обидится. Так мы же четко понимаем то что происходит в компьютере. А прочитав использование классов я ничего толком не понимаю.
Подобным вышеприведенным методом можно вызывать все 609 функций библиотеки gdiplus.dll.
Даже если возникает необходимость узнать адрес конкретной функции, это можно сделать имея адреса всего двух API-функций
LoadLibrary — находит библиотеку в памяти или загружает ее и возвр. описатель её в регистре eax ЦП
GetProcAddress — возвращает адрес нужной функции в в регистре eax ЦП.
Затем загнать в четырехбайтную ячейку памяти (например переменная addr) этот адрес и вызвать инструкцию
call addr
А ещё меня просто сводит с ума расширения арифметико-логического устройства процессора SSE и SSE2.
Там такие инструкции...! За одну инструкцию можно выполнить сразу четыре арифметические операции.
Кстати, вот можете скопировать в буфер обмена мое творение на встроенном ассемблере C++ Builder.
Тут используются инструкции SSE2. Я добился ими ускорения вычисления массива изображения в 8 раз по сравнению с обычними командами процессора.
Есть одно несовершенство — разрезание динамически меняющегося изображения.
Но я думаю что эта проблема будет решена.
Здравствуйте, sgi1981, Вы писали:
S>Но я бы хотел знать вовсе не то какие существуют классы использующие библиотеку gdiplus.dll. S>Мне нужно знать как непосредственно использовать подпрограммы из этой библиотеки. S>Я понимаю, что классы разных там языков используют эти подпрограммы. S>Но прочитав вашу статью я так и не понял как именно используются сами подпрограммы этой библиотеки.
В SDK есть такой файл — GdiPlusFlat.h. Там объявлены все функции API. Можете использовать их напрямую без "классов разных там языков".
Здравствуйте, algol, Вы писали:
A>В SDK есть такой файл — GdiPlusFlat.h. Там объявлены все функции API. Можете использовать их напрямую без "классов разных там языков".
ВОт. Теперь первая часть проблемы отпала. Остается только узнать для чего каждый параметр предназначен, а то предназначение не описано в gdiplusflat.h
Везде и всегда должна быть физическая реальность и должен быть здравый смысл
А кто нибудь слышал, что в GF 7900, 7600 есть "полная аппаратная поддержка всех функция GDI+".
Я вот такое в обзоре этих карточек на IXBT увидел.
И вопрос: Все GDI+ радости на этих картах будут очень быстро рисоваться или я
что-то не так понимаю?
И если это так, то кто-нибудь пробовал?
Проблема в том, что при любом FileName функция SaveBitmap выдаёт результат (tRes) равный FileNotFound.
Неважно, корректное или нет имя файла.
В чём проблема?
Здравствуйте, Аноним, Вы писали:
А>Возник вопрос по использованию GDI+ А>Проблема в том, что при любом FileName функция SaveBitmap выдаёт результат (tRes) равный FileNotFound. А>Неважно, корректное или нет имя файла.
Здесь ошибка в том, что GetRawFormat() получает GUID формата изображения (котороый сохраняется в переменной Main, как я понял), а в методе Save() нужно указывать CLSID энкодера для нужного формата изображения, а не сам формат. В MSDN для получения CLSID энкодера приводится функция GetEncoderClsid(), которую легко переделать, чтобы находить CLSID не по MimeType (как в примерах), а по FormatID.
И еще, BSTR в Save() не требуется, в функциях GDI+ обычно используются просто WCHAR*.
Re[3]: GDI+: графика нового поколения
От:
Аноним
Дата:
08.04.06 14:03
Оценка:
Здравствуйте, algol, Вы писали:
A>Здравствуйте, Аноним, Вы писали:
А>>Возник вопрос по использованию GDI+ А>>Проблема в том, что при любом FileName функция SaveBitmap выдаёт результат (tRes) равный FileNotFound. А>>Неважно, корректное или нет имя файла.
A>Здесь ошибка в том, что GetRawFormat() получает GUID формата изображения (котороый сохраняется в переменной Main, как я понял), а в методе Save() нужно указывать CLSID энкодера для нужного формата изображения, а не сам формат.
Большое спасибо!! Я торопился и перепутал
теперь всё работает.
В MSDN для получения CLSID энкодера приводится функция GetEncoderClsid(), которую легко переделать, чтобы находить CLSID не по MimeType (как в примерах), а по FormatID.
Взгляну. A>И еще, BSTR в Save() не требуется, в функциях GDI+ обычно используются просто WCHAR*.
Это просто привычка.
Огромное спасибо. Очень торопился и всё напутал.
FonBalrog
ВБ>Авторы: ВБ> Виталий Брусенцев
ВБ>Аннотация: ВБ>В статье рассмотрена работа с растрами средствами GDI+ — новой библиотеки от Microsoft. Описываются методы создания растров из внешних источников, их взаимодействие с устройствами вывода и работа с графическими файлами.
Хорошо.
Я прочитал эту статью и нашел все АПИ-функции, действия которых соответствует действиям классов, примеры работы с которыми описаны в статье.
Но просматривая АПИ-функции включаемого файла я нашел интересную особенность этой библиотеки.
В ней есть функции предназначенные для загрузки изображений из файлов.
Пока я нашел вот такие из этих функций.
GdipLoadImageFromStream(IStream* stream, GpImage **image);
Загружают изображение image из потока.
GdipLoadImageFromFile(GDIPCONST WCHAR* filename, GpImage **image);
Загружают изображение image из файла.
GdipLoadImageFromStreamICM(IStream* stream, GpImage **image);
Загружают изображение image из потока используя ICM.
GdipLoadImageFromFileICM(GDIPCONST WCHAR* filename, GpImage **image);
Загружают изображение image из файла, используя ICM.
Приведенные выше функции читают файл или поток на предмет нахождения изображения в нем,
распознают декомпрессор этого изображения,
выделяют блок памяти для указателя *image (в саму функцию передается уже указатель на указатель на будущий блок памяти, тем самым обеспечивается возврат значения указателя), в котором содержится некоторый объект хранящий все пикселыизображения и све его свойства.
Это понятно.
Но в этой же библиотеке есть ещё и такие функции для загрузки изображений из файлов
GdipCreateBitmapFromStream(IStream* stream, GpBitmap **bitmap);
загружает BITMAP из потока
GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename, GpBitmap **bitmap);
загружает BITMAP из файла
GdipCreateBitmapFromStreamICM(IStream* stream, GpBitmap **bitmap);
загружает BITMAP из потока методом ICM
GdipCreateBitmapFromFileICM(GDIPCONST WCHAR* filename, GpBitmap **bitmap);
загружает BITMAP из файла методом ICM
Эти функции уже загружают изображение не в объект image, а в объект BITMAP.
Возникает вопрос.
А чем отличаются друг от друга эти объекты ?
Ну думая, над этим вопросом, я пришел к мысли о нескольких различиях.
Во первых BITMAP — это объект содержащий меньше информации об изображении, чем объект image.
Но если допустить (может я не прав), что оба эти объекта хранят значения пикселов изображения, а не его сжатый вид,
то зачем создавать дополнительный "урезанный" объект BITMAP ? Ведь image содержит все то же самое и даже больше.
Ещё один вопрос в том, как перевести image в BITMAP. Функций прямого перевода я не нашел во включаемом файле.
Дело в том, что мне нужно получить прямой доступ к пикселам изображения в сжатом формате из файла.
Функции, которая бы давала массив значений цветов пикселов из объекта image я не нашел.
Я нашел только нужную функцию, которая дает этот массив из объекта BITMAP.
GdipBitmapLockBits(GpBitmap* bitmap,
GDIPCONST GpRect* rect,
UINT flags,
PixelFormat format,
BitmapData* lockedBitmapData);
Значит для загрузки изображения нужно применить функцию GdipCreateBitmapFromFile(GDIPCONST WCHAR* filename, GpBitmap **bitmap);
Но если файл содержит несколько кадров, а не один, то как мне получить в массиве пикселов только нужный кадр ?
Везде и всегда должна быть физическая реальность и должен быть здравый смысл
Здравствуйте, sgi1981, Вы писали:
S> .....? ... ? .....................?
Возможно, я Вас очень сильно удивлю, если подскажу, что кроме подключаемых файлов и вышеупомянутой статьи есть такой ресурс в инете, как msdn.microsoft.com. И там есть ответы на все Ваши вопросы: и про отличие Image от Bitmap, и про доступ к отдельным пикселям, и про многокадровые изображения, и даже на те вопросы, которые у Вас ещё не возникли. Так что, смелее!
Здравствуйте, romson, Вы писали:
R>Здравствуйте, sgi1981, Вы писали:
S>> .....? ... ? .....................?
R>Возможно, я Вас очень сильно удивлю, если подскажу, что кроме подключаемых файлов и вышеупомянутой статьи есть такой ресурс в инете, как msdn.microsoft.com.
Я хорошо знаю, что этот ресур есть.
Но :
во первых — веб страницы того сайта имеют большой размер и мне на денежный балланс за интернет идет большое уменьшение,
во вторых — язык мне тот не родной, думать я на нем не собираюсь и не хочу,
в третьих — для чего создавался тогда сайт http://rsdn.ru если есть сайт http://msdn.com ?
Может и всех форумчан так можно послать на http://msdn.com ?
Чего они забивают вопросами этот сайт, если на майкрософтовском сайте инфы дочЁрта ?
Вот пусть и читают там.
Кто что думает об этом... ?
Везде и всегда должна быть физическая реальность и должен быть здравый смысл
Здравствуйте, sgi1981, Вы писали:
S>во первых — веб страницы того сайта имеют большой размер и мне на денежный балланс за интернет идет большое уменьшение,
Есть еще оффлайновая версия MSDN, ставится на компьютере.
S>во вторых — язык мне тот не родной, думать я на нем не собираюсь и не хочу,
Думать не надо, а читать все-таки придется.
S>Кто что думает об этом... ?
Если по существу вопроса, то Image (GpImage) это базовый класс для Bitmap (GpBitmap) и Metafile (GpMetafile). Функции Image поддерживают оба типа изображений. Тип конкретного экземпляра можно узнать через GdipGetImageType(). Если тип ImageTypeBitmap, то GpImage* может быть приведен к GpBitmap* и использован в функциях Bitmap. GpBitmap* может быть использован везде как GpImage*.
Если вы посмотрите исходники класса Bitmap, то увидите, что указатель на GDI+ объект определен в базовом классе Image как GpImage* nativeImage. В конструкторах туда пишется GpBitmap*, а в методах Bitmap он приводится обратно в GpBitmap*:
static_cast<GpBitmap*>(nativeImage)
Для работы с отдельными пикселами есть GdipBitmapLockBits() и GdipBitmapGetPixel()/GdipBitmapSetPixel().
Для выбора нужного кадра можно использовать GdipImageGetFrameCount() и GdipImageSelectActiveFrame().
Здравствуйте, sgi1981, Вы писали:
S>Я хорошо знаю, что этот ресур есть. S>Но : S>во первых — веб страницы того сайта имеют большой размер и мне на денежный балланс за интернет идет большое уменьшение, S>во вторых — язык мне тот не родной, думать я на нем не собираюсь и не хочу, S>в третьих — для чего создавался тогда сайт http://rsdn.ru если есть сайт http://msdn.com ? S>Может и всех форумчан так можно послать на http://msdn.com ? S>Чего они забивают вопросами этот сайт, если на майкрософтовском сайте инфы дочЁрта ? S>Вот пусть и читают там. S>Кто что думает об этом... ?
Т.е. Вы хотите, чтобы кто-то за Вас полез на MSDN, нашёл нужную инфу, перевёл её на Ваш родной язык и выложил Вам на блюдечке? Оригинально! Я думаю, Вам всё же не помешает потратить немного Вашего драгоценного интернетного баланса на прочтение рекомендаций данного форума:
Прежде, чем задавать технический вопрос по электронной почте или в дискуссионную группу, в чате или на форуме, сделайте следующее:
1. Попытайтесь найти ответ с помошью поиска в Web.
2. Попытайтесь найти ответ в руководстве.
...
Здравствуйте, algol, Вы писали:
A>Если по существу вопроса, то Image (GpImage) это базовый класс для Bitmap (GpBitmap) и Metafile (GpMetafile). Функции Image поддерживают оба типа изображений. Тип конкретного экземпляра можно узнать через GdipGetImageType(). Если тип ImageTypeBitmap, то GpImage* может быть приведен к GpBitmap* и использован в функциях Bitmap. GpBitmap* может быть использован везде как GpImage*.
ВОТ СПАСИБО !
Теперь я проверил на АСМЕ.
Вместо объекта image можно задавать функции вывода изображения GdipDrawImageI объект bitmap.
push offset graphics;указатель на указатель на будущий объект graphics
push WND;описатель окна
call GdipCreateFromHWND;создать объект graphics по описателю WND окна
push offset bitmap1;теперь будем создавать объект bitmap1 (БИТМАП) из файла, имя которого
push ps1;здесь
call GdipCreateBitmapFromFile;читаем файл и создаем БИТМАП
push y3;а теперь будем выводить БИТМАП в окно - пишем в стек координаты начала вывода
push x3;
push bitmap1;задаем наш БИТМАП
push graphics;задаем объект graphics, в который выводится будет
call GdipDrawImageI;и WINDOWS рисует в нашем окне изображение
push 0;
push graphics;
call GdipFlush;
A>Для работы с отдельными пикселами есть GdipBitmapLockBits() и GdipBitmapGetPixel()/GdipBitmapSetPixel(). A>Для выбора нужного кадра можно использовать GdipImageGetFrameCount() и GdipImageSelectActiveFrame().
Да, ещё буду экспериментировать.
Везде и всегда должна быть физическая реальность и должен быть здравый смысл
Re[5]: GDI+: графика нового поколения
От:
Аноним
Дата:
12.04.06 12:06
Оценка:
Здравствуйте, algol, Вы писали:
A>Для работы с отдельными пикселами есть GdipBitmapLockBits() и GdipBitmapGetPixel()/GdipBitmapSetPixel(). A>Для выбора нужного кадра можно использовать GdipImageGetFrameCount() и GdipImageSelectActiveFrame().
написано
...
При этом, если указать формат временного буфера (PixelFormat), отличный от формата исходного растра, вызовы LockBits/UnlockBits потребуют дополнительных преобразований.
...
Так вот, незнаю, как на самом деле работает функция LockBits у класса.
Но когда я загружал 8-битный BMP-файл функцией
GdipCreateBitmapFromFile
и потом пытался преобразовать формат пикселов из 8-ми битного в 24-битный функцией
GdipBitmapLockBits, то
ничего не получилось и функция GdipBitmapLockBits выдала значение 2 — неправильно задан параметр.
удавалось таким образом выполнять преобразования
из 24-битного формата в 32-битный
из 16-битного в 32-битный.
Но из 8-битного в 32-битный никак та функция не преобразовывает.
Так что мне нужно специально сохранять изображение другим кодеком в файл в другом формате, а потом из файла опять его читать ?
ВБ>Авторы: ВБ> Виталий Брусенцев
ВБ>Аннотация: ВБ>В статье рассмотрена работа с растрами средствами GDI+ — новой библиотеки от Microsoft. Описываются методы создания растров из внешних источников, их взаимодействие с устройствами вывода и работа с графическими файлами.
Преогромнейшее спасибо! Статья помогла мне разобраться с возможностями этой графической библиотеки, особенно признателен за приложение — пример. Респект и уважуха!