Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > TransparentBlt выдает ошибку


Автор: GremlinProg 3.1.2009, 19:28
обнаружил недокументированную заплатку в функции TransparentBlt
как я сейчас понимаю, она была сделана для предотвращения утечки, о которой говорится в MSDN:
Цитата(TransparentBlt)

Windows 95/98: TransparentBlt contains a memory leak that can exhaust system resources. To draw a transparent bitmap using BitBlt, see Knowledge Base article Q79212.

ошибку обнаружил при реализации потокового наложения слоев:
Код

void blt(HDC hdc,int DstX,int DstY,int DstCx,int DstCy,int SrcX,int SrcY){
    for(int i = devlist.size();i--;){
        HDC src    = devlist.at(i);
        ::TransparentBlt( hdc, DstX, DstY, DstCx, DstCy, src, SrcX, SrcY, DstCx, DstCy, clr_transparent );
    }
}

blt - метод, который накладывает разнородные слои из списка devlist в исходный hdc через ключ прозрачности clr_transparent
при этом, размеры слоев градуируются с заданным шагом, например: если минимальный размер ячейки слоя составляет 20 пикселей, то ширина и высота битмапов, выбираемых в контексты списка devlist, всегда кратны 20 пикселам
hdc не градуируется таким образом, т.е., к примеру, если в hdc выбран битмап размером 25 x 47, то в списке devlist все битмапы 20 x 40, т.е. в devlist размеры округляются в меньшую сторону

в реальной работе, если вызвать blt(hdc,0,0,25,47,0,0), то TransparentBlt на всех слоях возвращается с ошибкой "неверный параметр", соответственно, на hdc ничего не выводится

уже не знал в чем проблема, грешил на совместимость девайсов, пока не попробовал вместо TransparentBlt вызывать BitBlt, т.е. просто копирование слоев, а не наложение

предположение о несовместимости девайсов сразу отпало, поскольку BitBlt работает коректно
проверил результат работы blt с DstCx и DstCy - меньшими, либо равными размерам битмапов в devlist, т.е.
(DstCx <= 20) &&  (DstCy <= 40)
после этого и TransparentBlt заработал как надо

вобщем, код c TransparentBlt пришлось модифицировать следующим образом:
Код

void blt(HDC hdc,int DstX,int DstY,int DstCx,int DstCy,int SrcX,int SrcY){
    RECT rc;
    for(int cx,cy,i = devlist.size();i--;){
        HDC src    = devlist.at(i);
        ::GetClipBox(src,&rc);
        if(SrcX + DstCx > rc.right){
            cx    = rc.right - SrcX;
        }else{
            cx    = DstCx;
        }
        if(SrcY + DstCy > rc.bottom){
            cy    = rc.bottom - SrcY;
        }else{
            cy    = DstCy;
        }
        ::TransparentBlt( hdc, DstX, DstY, cx, cy, src, SrcX, SrcY, cx, cy, clr_transparent );
    }
}

GetClipBox дает позицию и размеры битмапа в девайсе, а DstCx и DstCy корректируются перед выводом

такое и раньше бывало, но я не пробовал использовать TransparentBlt на дискретной сетке, чтобы четко определить, в чем подвох
пишите, если будут опровержения или подтверждения по теме
чтобы не быть голословным, нужно несколько мнений, подкрепленных практикой

ссылка на документацию: http://msdn.microsoft.com/en-us/library/ms532303(VS.85).aspx

Автор: Earnest 6.1.2009, 18:16
Сталкивалась с тем, что StretchBlt тоже врет при выходе за границы. Видимо, код масштабирования там кривоват... Тоже пришлось проверять и корректировать границы. Подробностей уже не помню, но не думаю, чтобы это было связано с ошибкой утечки памяти, на которую ты сослался... Не в первый раз сталкиваюсь с тем, что функции ГУИ работают по принципу "шаг вправо, шаг влево - расстрел". Ну очень недружелюбны ко всякого рода неточностям...

Автор: GremlinProg 6.1.2009, 19:23
думаешь в масштабировании дело
ну, вариант, в принципе
я просто не нашел ни одного упоминания на какие-либо ограничения по координатам в документации, поэтому и предположил

меня вообще удивляет наличие самой функции масштабирования в TransparentBlt, и отсутствие аналога BitBlt по ключу прозрачности
масштабирование в любом случае задействует больше ресурсов, чем копирование
у меня, по крайней мере, здесь масштабирование не используется, наложение битмапов производится один в один

если TransparentBlt и StretchBlt создают таки в памяти дубль битмапа, чтобы интерполировать видимые участки, то утечка так же возможна

на сколько я понимаю, TransparentBlt, по крайней мере создает маску прозрачности, что вполне логично может привести к вариантам кэширования или хэширования масок для ускорения повторного рисования (у борланда такое наблюдается, хотя там просто хэширование данных GDI-объектов), отсюда и могут вылезать проблемы с потерей каких-либо отдельных ресурсов

Автор: Earnest 6.1.2009, 20:12
Утечка действительно скорее всего связана с созданием промежуточной маски. Меня вообще удивляет упоминание TransparentBlt в контексте 95\98. Ну не было там такой функции (раньше). Она, на моей памяти, появилась в NT. Возможно, это ее симуляция как раз через промежуточную маску, которая появилась позже. MS ведь то отказывался от поддержки 98, то опять продолжал... Все эта совместимость, будь она неладна. 

А вот в NT и дальше вроде это уже через драйвер... но врать не буду, не знаю.
Насчет того, что там еще и масштабирование до кучи, так это, видимо, связано с желанием иметь универсальную функцию. 

Немного не в тему, но просто похоже. Несколько лет назад, когда NT была еще свежа, я столкнулась с удивительным багом: StrechBlt с флагом COPYSRC работала нормально, а стоило заменить COPY на любую другую операцию - OR, AND и т.д. начинались ТАКИЕ тормоза, что в первый раз я решила, что все просто зависло. Причем, это проявлялось только на некотороых видеокартах - примерно процентах на 30 (это я сужу по реакции пользователей и тестах на наших машинах). Пришлось даже ввести специальную опцию в программу -"отключить прозрачность растров" - иначе работать было просто невозможно: как сквозь смолу плыть... Самым для меня удивительным было то, что StrechBlt заполняла внеэкранный буфер, причем он точно был в моей памяти (дибсекция), а ни в какой ни в видеокарте. И при чем здесь, казалось бы, железо... Но факт оставался фактом: сочетание NT + видеокарта.

Добавлено через 1 минуту и 14 секунд
Хм, несколько лет назад... да уж как бы не все пятнадцать... smile

Добавлено через 6 минут и 36 секунд
Цитата(GremlinProg @  6.1.2009,  20:23 Найти цитируемый пост)
на сколько я понимаю, TransparentBlt, по крайней мере создает маску прозрачности, что вполне логично может привести к вариантам кэширования или хэширования масок для ускорения повторного рисования 

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

Автор: GremlinProg 6.1.2009, 20:37
Цитата(Earnest @  6.1.2009,  22:12 Найти цитируемый пост)
Самым для меня удивительным было то, что StrechBlt заполняла внеэкранный буфер, причем он точно был в моей памяти (дибсекция(dibsection)), а ни в какой ни в видеокарте. И при чем здесь, казалось бы, железо... Но факт оставался фактом: сочетание NT + видеокарта.

это похоже на танцы с виртуальной памятью: виртуальный блок - локальный, а фактический - в видеопамяти
я пробовал закрепить dib в физической памяти(VirtualLock) и ничего из этого не получилось, пишет вроде как "регион уже закреплен", т.е. это точно не свопируемая память, хотя вполне может и правда закреплен самой CreateDibSection, например, если производится эмуляция )

Автор: Earnest 6.1.2009, 20:50
Цитата(GremlinProg @  6.1.2009,  21:37 Найти цитируемый пост)
это похоже на танцы с виртуальной памятью: виртуальный блок - локальный, а фактический - в видеопамяти

Да, я тоже пришла к такому выводу - по крайней мере, на время выполнения операции.

Знаешь, я со временем пришла к мысли, что если хочешь сделать что-то полезное, то лучше не погружаться во внутренний мир болота, а прыгать по кочкам, и черт с ним, с их химическим составом. Иначе за деревьями леса не увидишь... Иногда, конечно, приходится нырять в тину, если по другому не получается, но потом я сразу забываю подробности погружения - до следующего smile 
А может, это просто мне лень стало со временем, во всех этих багах копаться...

Автор: GremlinProg 6.1.2009, 21:05
на счет погружения - верно, но пытливый моск рукам покоя не дает
я просто считал, что баг в самой TransparentBlt, а тут - всего лишь учет границ
функция-то удобная, как раз, чтобы не погружаться в тину: SRCINVERT, SRCPAINT и т.п., опять же - маску создавать не надо
странно, что в документации об этом ни слова

Добавлено через 13 минут и 29 секунд
кстати, в тему о прозрачности:
Код

if(GetDeviceCaps(hdcDest, CAPS1) & C1_TRANSPARENT)
{
   // Special transparency background mode
      oldMode = SetBkMode(hdcDest, NEWTRANSPARENT);
      rgbBk = SetBkColor(hdcDest, rgbTransparent);
   // Actual blt is a simple source copy; transparency is automatic.
      BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCCOPY);
      SetBkColor(hdcDest, rgbBk);
      SetBkMode(hdcDest, oldMode);
}

это не мое, один из источников http://www.codeguru.com/cpp/g-m/bitmap/article.php/c1753, т.е. это как раз простой вариант прозрачности через BitBlt, хоть и аппаратно-зависимый, надо будет посмотреть, что быстрее и, возможно, составить триггер, если TransparentBlt таки формирует маску

Автор: GremlinProg 6.1.2009, 21:59
Код

#define NEWTRANSPARENT    3
#define CAPS1            94
#define C1_TRANSPARENT    1

к сожалению, мой жифорс такого не поддерживает

Автор: Earnest 6.1.2009, 22:46
Я тоже про это не слышала... интересно... наверное, это использование новых возможностей железа...
Но когда нужно поддерживать невесть какие машины, а прозрачность - далеко не главный вопрос, на первое место выходит простота кода. Его и так столько, что с ума сойти...

Кстати, интересно, есть ли баг с утечкой маски в TransparentBlt - там ведь только о Win95\98 говорится... Надо бы проверить: кажется, были непонятные утечки ресурсов, и TransparentBlt кое-где используется. Вот  выйдет народ на работу, озадачу кого-нибудь, у кого моск не такой ленивый как у меня...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)