Программирование: тень от рисунка?
Dec. 30th, 2009 08:36 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Я тут пытаюсь рисовать правильные тени. Что-то в таком роде:

Там текст кнопки отбрасывает тень. Область, куда отбрасывается тень, не является однотонной. Сам текст однотонный только в данном примере - можно отбрасывать тень от любого объекта.
Я напишу, как это сделано - возможно вам пригодится или вы посоветуете метод лучше? Может, в GDI+ есть что-то? Я сходу не нашел.
Пояснения, что есть что:
Итак. Пусть дан некий контекст (this). В нем уже что-то нарисовано, некий фоновый рисунок, куда будет отбрасываться тень (в примере - "стеклянная" кнопка). Поверх этого будет нарисована некая ХРЕНЬ, которая определяется параметром drawer. ХРЕНЬ должна отбросить тень на фон. Также дан некий прямоугольник r, в пределах которого все происходит.
Выше приведена - внешняя часть алгоритма. Конструктор создает новый контекст в памяти (CreateCompatibleDC), функция start выделяет память под прямоугольник (CreateCompatibleDC) и копирует туда фоновый рисунок из this (BitBlt). Далее функция DrawState рисует ХРЕНЬ со смещением на distance и делает ее однотонной (DSS_MONO). В результате в контексте mc получается тот же самый фон и поверх него - смещенный монохромный рисунок (черный). Потом этот рисунок в функции transparent смешивается с текушим рисунком при помощи AlphaBlend. Тень получается полупрозрачной, рисунок фона не теряется. Наконец, поверх всего этого ХРЕНЬ рисуется еще раз, без смещения и полупрозрачности.
Все вместе, если программировать с нуля, получается довольно длинно. А нет ли более дешевого способа?

Там текст кнопки отбрасывает тень. Область, куда отбрасывается тень, не является однотонной. Сам текст однотонный только в данном примере - можно отбрасывать тень от любого объекта.
Я напишу, как это сделано - возможно вам пригодится или вы посоветуете метод лучше? Может, в GDI+ есть что-то? Я сходу не нашел.
static BOOL CALLBACK DrawStateProc( HDC hdc, // handle to device context LPARAM lData, // image information WPARAM wData, // more image information int cx, // image width int cy // image height ) { GContextDrawer *drawer= (GContextDrawer*)lData; drawer->draw(GSimpleContext(hdc), GRect(0, 0, cx, cy)); return TRUE; } void GContext::drawShaded(const GRect &r, GContextDrawer &drawer, int distance) { if (distance != 0) { GMemoryContext mc(this); mc.start(r, GMemoryContext::COPY_BITS|GMemoryContext::COPY_PARAMS); HBRUSH brush= GBrush::createSolid(GColor::Black); DrawState(mc.getHandle(), brush, DrawStateProc, (LPARAM)&drawer, NULL, distance, distance, r.wid(), r.hig(), DST_COMPLEX|DSS_MONO); DeleteObject(brush); mc.transparent(50); } drawer.draw(*this, r); }
Пояснения, что есть что:
- r - прямоугольник в пределах которого будет нарисовано нечто.
- GContext - простая обертка вокруг HDC (как CDC)
- GRect - прямоугольник (поля x1, y1, x2, y2)
- distance - расстояние "отброса" тени (по умолчанию 2)
- GContextDrawer - класс, описывающий некий абстрактный объект, который может нарисовать сам себя в указанном прямоугольнике указанного контекста: draw(GContext&, GRect&). В данном случае он просто пишет текст в этом прямоугольнике.
- GMemoryContext - контекст для рисования в памяти.
Итак. Пусть дан некий контекст (this). В нем уже что-то нарисовано, некий фоновый рисунок, куда будет отбрасываться тень (в примере - "стеклянная" кнопка). Поверх этого будет нарисована некая ХРЕНЬ, которая определяется параметром drawer. ХРЕНЬ должна отбросить тень на фон. Также дан некий прямоугольник r, в пределах которого все происходит.
Выше приведена - внешняя часть алгоритма. Конструктор создает новый контекст в памяти (CreateCompatibleDC), функция start выделяет память под прямоугольник (CreateCompatibleDC) и копирует туда фоновый рисунок из this (BitBlt). Далее функция DrawState рисует ХРЕНЬ со смещением на distance и делает ее однотонной (DSS_MONO). В результате в контексте mc получается тот же самый фон и поверх него - смещенный монохромный рисунок (черный). Потом этот рисунок в функции transparent смешивается с текушим рисунком при помощи AlphaBlend. Тень получается полупрозрачной, рисунок фона не теряется. Наконец, поверх всего этого ХРЕНЬ рисуется еще раз, без смещения и полупрозрачности.
Все вместе, если программировать с нуля, получается довольно длинно. А нет ли более дешевого способа?
no subject
Date: 2009-12-30 09:35 pm (UTC)no subject
Date: 2009-12-30 09:37 pm (UTC)no subject
Date: 2009-12-30 09:43 pm (UTC)