psilogic: (Default)
[personal profile] psilogic
Я тут пытаюсь рисовать правильные тени. Что-то в таком роде:



Там текст кнопки отбрасывает тень. Область, куда отбрасывается тень, не является однотонной. Сам текст однотонный только в данном примере - можно отбрасывать тень от любого объекта.

Я напишу, как это сделано - возможно вам пригодится или вы посоветуете метод лучше? Может, в 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. Тень получается полупрозрачной, рисунок фона не теряется. Наконец, поверх всего этого ХРЕНЬ рисуется еще раз, без смещения и полупрозрачности.

Все вместе, если программировать с нуля, получается довольно длинно. А нет ли более дешевого способа?

Date: 2009-12-30 09:35 pm (UTC)
From: [identity profile] cybister.livejournal.com
Эм... ну вряд-ли подручными средствами что-то лучше можно сделать. Вроде как сейчас есть куча готовых примочек. Я как-то сидел мучался с графиками в php, потом нашел одну либу - там как хочешь куда хочешь и какие хочешь можно было рисовать. Посмотрел я на свои достижения скромные на тот момент и стал использовать библиотеку.

Date: 2009-12-30 09:37 pm (UTC)
From: [identity profile] cybister.livejournal.com
Если я ошибаюсь - поправте меня, это CPP+Winapi ибольше ничего, так?

Date: 2009-12-30 09:43 pm (UTC)
From: [identity profile] psilogic.livejournal.com
Ну типа того. Между ними еще самопальная либа (собственно алгоритм - часть ее кода).
Page generated Aug. 22nd, 2025 12:23 am
Powered by Dreamwidth Studios