1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
|
/*
* astyle --force-indent=tab=4 --brackets=linux --indent-switches
* --pad=oper --one-line=keep-blocks --unpad=paren
*
* Miranda IM: the free IM client for Microsoft* Windows*
*
* Copyright 2000-2010 Miranda ICQ/IM project,
* all portions of this codebase are copyrighted to the people
* listed in contributors.txt.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* you should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* part of clist_ng plugin for Miranda.
*
* (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
*
* $Id: gfx.h 137 2010-10-16 21:03:23Z silvercircle $
*
* low level painting functions - manage DCs, render skin image glyphs,
* text and backbuffer drawing.
*
* provide wrappers for AGG render pipelines
*
*/
#ifndef __GFX_H_
#define __GFX_H_
#define M_PI 3.14159265358979323846
BYTE __forceinline percent_to_byte(UINT32 percent)
{
return(BYTE) ((FLOAT) (((FLOAT) percent) / 100) * 255);
}
class AGGBaseContext
{
public:
/**
* CTOR - construct AGGBaseContext and attach it to a bitmap
* @param hdc device context (not really needed)
* @param hbm bitmap handle
* @return
*/
AGGBaseContext(HDC hdc, HBITMAP hbm)
{
m_hdc= hdc;
attach(hbm);
}
/**
* construct AGGBaseContext and attach it to a in-memory bitmap used
* by a BufferedPaint operation
* @param hdc device context (not really required)
* @param rgbq RGBQUAD* memory buffer with bitmap bits in BGRA32 format
* @param width bitmap width (can be larger than the actual painting rectangle)
* @param height bitmap height
* @return
*/
AGGBaseContext(HDC hdc, RGBQUAD* rgbq, LONG width, LONG height)
{
m_hdc = hdc;
attach(rgbq, width, height);
}
/**
* construct an empty AGGBase context and assign the optional HDC
* must be attached to a memory bitmap later by using one of the
* attach() methods.
* @param hdc device context
* @return
*/
AGGBaseContext(HDC hdc) { m_hdc = hdc; }
/**
* attach the AGG rendering buffer to a memory bitmap, selected into a HDC for
* off-screen drawing.
* @param hbm bitmap to use for rendering.
*/
void attach(HBITMAP hbm)
{
BITMAP bm;
::GetObject(hbm, sizeof(bm), &bm);
m_rbuf.attach((unsigned char*)bm.bmBits, bm.bmWidth, bm.bmHeight, bm.bmWidthBytes);
}
/**
* attach the AGG rendering buffer to a RGBQUAD array (as returned by
* GetBufferedPaintBits() API
*
* @param rgbq pointer to memory buffer holding the bitmap bits
* @param width width (in pixels, always properly aligned)
* @param height height (in pixels)
*/
void attach(RGBQUAD* rgbq, LONG width, LONG height)
{
m_rbuf.attach((unsigned char *)rgbq, width, height, width * 4);
}
HDC m_hdc;
agg::rendering_buffer m_rbuf;
~AGGBaseContext() {}
};
class AGGContext
{
public:
AGGContext(HBITMAP hbm)
{
BITMAP bm;
::GetObject(hbm, sizeof(bm), &bm);
m_rbuf.attach((unsigned char*)bm.bmBits, bm.bmWidth, bm.bmHeight, bm.bmWidthBytes);
initPipeline();
};
AGGContext(RGBQUAD* rgbq, LONG width, LONG height)
{
m_rbuf.attach((unsigned char *)rgbq, width, height, width * 4);
initPipeline();
}
AGGContext() {}
void initPipeline()
{
m_pixfmt.attach(m_rbuf);
m_rbase.attach(m_pixfmt);
m_solid_renderer.attach(m_rbase);
m_span_interpolator = agg::span_interpolator_linear<>(m_gradient_trans);
m_span_gradient = span_gradient_t(m_span_interpolator, m_gradient_func, m_color_array, 0, 200);
m_gradient_renderer.attach(m_rbase, m_span_allocator, m_span_gradient);
}
void attach(HBITMAP hbm)
{
BITMAP bm;
::GetObject(hbm, sizeof(bm), &bm);
m_rbuf.attach((unsigned char*)bm.bmBits, bm.bmWidth, bm.bmHeight, bm.bmWidthBytes);
initPipeline();
}
void attach(RGBQUAD* rgbq, LONG width, LONG height, LONG stride)
{
m_rbuf.attach((unsigned char *)rgbq, width, height, stride);
initPipeline();
}
/*
* fill the color array we need for the gradient with
*/
template<class Array> static void fill_color_array(Array& arr, agg::rgba8& begin, agg::rgba8& end)
{
unsigned size = arr.size();
for(unsigned i = 0; i < size; ++i)
arr[i] = begin.gradient(end, i / double(size));
}
public:
agg::rendering_buffer m_rbuf;
agg::pixfmt_bgra32 m_pixfmt;
agg::renderer_base<agg::pixfmt_bgra32> m_rbase;
agg::renderer_scanline_aa_solid<agg::renderer_base<agg::pixfmt_bgra32> > m_solid_renderer;
typedef agg::span_gradient<agg::rgba8, agg::span_interpolator_linear<>, agg::gradient_x, agg::pod_auto_array<agg::rgba8, 256> > span_gradient_t;
typedef agg::span_allocator<agg::rgba8> span_allocator_t;
agg::renderer_scanline_aa<agg::renderer_base<agg::pixfmt_bgra32>, span_allocator_t, span_gradient_t> m_gradient_renderer;
span_allocator_t m_span_allocator;
span_gradient_t m_span_gradient;
agg::span_interpolator_linear<> m_span_interpolator;
agg::gradient_x m_gradient_func;
agg::pod_auto_array<agg::rgba8, 256> m_color_array;
agg::trans_affine m_gradient_trans;
};
class AGGPaintHelper
{
public:
AGGPaintHelper(HDC hdc)
{
hdcMem = hdc;
aggctx = new AGGBaseContext(hdcMem);
}
~AGGPaintHelper()
{
delete aggctx;
};
public:
agg::rounded_rect* current_shape;
AGGBaseContext* aggctx;
HDC hdcMem;
};
class CLCPaintHelper : public AGGPaintHelper
{
public:
CLCPaintHelper(const HWND hwndCLC, ClcData* clcdat, DWORD windowstyle, RECT* rc, int yFirst, int fontShift, int idx) : AGGPaintHelper(0)
{
hwnd = hwndCLC;
indent = 0;
dat = clcdat;
style = windowstyle;
clRect = rc;
y = yFirst;
index = idx;
bFirstNGdrawn = false;
groupCountsFontTopShift = fontShift;
isContactFloater = false;
}
void setHDC(const HDC hdc)
{
hdcMem = hdc;
}
virtual ~CLCPaintHelper() {};
void setFloater () { isContactFloater = true; }
void Paint (ClcGroup* group, ClcContact* contact, int rowHeight);
int drawAvatar (RECT *rc, ClcContact *contact, int y, WORD cstatus, int rowHeight);
HFONT changeToFont (const unsigned int id);
void setHotTrackColour ();
int indent;
int y;
int index;
int fSelected;
HANDLE hTheme, hbp;
bool fAvatar, fSecondLine;
TDspOverride* dsp;
HWND hwnd;
ClcData* dat;
DWORD style;
RECT* clRect;
bool bFirstNGdrawn;
int groupCountsFontTopShift;
bool isContactFloater;
TStatusItem *sevencontact_pos, *soddcontact_pos, *sfirstitem,
*ssingleitem, *slastitem, *sfirstitem_NG, *ssingleitem_NG, *slastitem_NG;
private:
int m_fontHeight;
};
class Gfx
{
public:
#ifdef _USE_D2D
static ID2D1Factory* pD2DFactory;
#endif
static COLORREF txtColor;
//static ULONG_PTR gdiPlusToken;
//static Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
public:
static inline HANDLE initiateBufferedPaint (const HDC hdcSrc, RECT& rc, HDC& hdcOut);
static inline void finalizeBufferedPaint (HANDLE hbp, RECT *rc, BYTE alpha = 0);
static HBITMAP createRGBABitmap (const LONG cx, const LONG cy);
static inline void drawBGFromSurface (const HWND hwnd, const RECT& rc, HDC hdc);
static inline void renderSkinItem (HDC hdc, RECT* rc, TImageItem* item);
static inline void renderSkinItem (AGGPaintHelper *ph, TStatusItem *item, RECT* rc);
static void setBitmapAlpha (HBITMAP hBitmap, BYTE bAlpha);
static void deSaturate (HBITMAP hBitmap, bool fReduceContrast = false);
static void preMultiply (HBITMAP hBitmap, int mode);
static void __fastcall renderImageItem (HDC hdc, TImageItem *item, RECT *rc);
static void colorizeGlyph (TImageItem *item, const COLORREF clr, float H, float S, float V);
static inline void hsvTransformPixel (BYTE *p, float value, float vsu, float vsw, BYTE alpha);
static inline void rgbTransformPixel (BYTE *p, float r, float g, float b, BYTE alpha);
static void D2D_Init ();
static void D2D_Release ();
static inline void setTextColor (const COLORREF clr);
static inline COLORREF getTextColor ();
static int renderText (HDC hdc, HANDLE hTheme, const TCHAR *szText, RECT *rc, DWORD dtFlags, const int iGlowSize = 0, int length = -1, bool fForceAero = false);
static void shutDown() { if (m_p) free(m_p); }
/**
* load a png image using miranda image load service.
*
* @param szFilename full path and filename of the image
*
* @return HBITMAP of the image loaded
*/
template<class T> static HBITMAP loadPNG(const T* szFilename)
{
HBITMAP hBitmap = 0;
DWORD dwFlags = 0;
if(sizeof(T) > 1)
dwFlags = IMGL_WCHAR;
hBitmap = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)szFilename, dwFlags);
if(CALLSERVICE_NOTFOUND == (INT_PTR)hBitmap) {
hBitmap = 0;
throw(CRTException("Critical error while loading a skin", L"The image service plugin is not available"));
}
return(hBitmap);
}
/*
* a generic buffer used by various things in the class Will only be realloc'd and grow when
* needed. Free'd on shutdown. It's primarily used by bitmap operations as temporary buffer
*/
static BYTE* m_p;
static size_t m_sAllocated;
};
/*
* some inlined functions
*/
/**
* Initiate a buffered paint operation
*
* @param hdcSrc The source device context (usually obtained by BeginPaint())
* @param rc RECT&: the target rectangle that receives the painting
* @param hdcOut HDC& (out) receives the buffered device context handle
*
* @return (HANDLE) buffered paint handle
*/
inline HANDLE Gfx::initiateBufferedPaint(const HDC hdcSrc, RECT& rc, HDC& hdcOut)
{
HANDLE hbp = Api::pfnBeginBufferedPaint(hdcSrc, &rc, BPBF_TOPDOWNDIB, NULL, &hdcOut);
return(hbp);
}
/**
* finalize buffered paint cycle and apply (if applicable) the global alpha value
*
* @param hbp HANDLE: handle of the buffered paint context
* @param rc RECT*: target rectangly where alpha value should be applied
*/
inline void Gfx::finalizeBufferedPaint(HANDLE hbp, RECT *rc, BYTE alpha)
{
if(alpha > 0)
Api::pfnBufferedPaintSetAlpha(hbp, rc, alpha);
Api::pfnEndBufferedPaint(hbp, TRUE);
}
/**
* blit the background from the back buffer surface (cfg::dat.hdcBg) to the
* client area of the child frame.
* @param hwnd child window handle
* @param rcCl child window client area
* @param hdc painting DC
*/
inline void Gfx::drawBGFromSurface(const HWND hwnd, const RECT& rcCl, HDC hdc)
{
RECT rcMapped = rcCl;
MapWindowPoints(hwnd, pcli->hwndContactList, (POINT *)&rcMapped, 2);
BitBlt(hdc, rcCl.left, rcCl.top, rcCl.right - rcCl.left, rcCl.bottom - rcCl.top, cfg::dat.hdcBg,
rcMapped.left, rcMapped.top, SRCCOPY);
}
/**
* render a simply skin item (image item only, do not render the underlays)
* @param hdc device context
* @param rc target rectangle
* @param imageItem image item to render
*/
inline void Gfx::renderSkinItem(HDC hdc, RECT* rc, TImageItem *imageItem)
{
if(imageItem)
renderImageItem(hdc, imageItem, rc);
}
/**
* render a skin item with a possible underlay (gradient and corner
* shape)
* @param ph CLCPaintHelper* (clc painting context with AGG base context)
* @param item TStatusItem* (item to render)
* @param rc target rectangle
*/
inline void Gfx::renderSkinItem(AGGPaintHelper *ph, TStatusItem *item, RECT* rc)
{
TImageItem *imageItem = item->imageItem;
if (CLC::fInPaint && CLC::iHottrackItem) {
item = &Skin::statusItems[ID_EXTBKHOTTRACK];
if (item->IGNORED == 0)
imageItem = item->imageItem;
CLC::fHottrackDone = true;
}
if(!(item->dwFlags & S_ITEM_SKIP_UNDERLAY)) {
/*
* attach the item rendering pipeline to the AGG context rendering buffer
*/
item->pixfmt->attach(ph->aggctx->m_rbuf);
item->rbase->attach(*(item->pixfmt));
if(item->dwFlags & AGG_USE_GRADIENT_X_RENDERER) {
item->span_gradient_x->d1(rc->left);
item->span_gradient_x->d2(rc->right);
}
else {
item->span_gradient_y->d1(rc->top);
item->span_gradient_y->d2(rc->bottom);
}
/*
* for each rendering cycle, the first shape defines the final shape for this item
* this allows items like "first item of a group" or "last item of a group" define
* their own shapes - overlays like a selection or hottrack item will use the
* original shape and only render their own color(s).
*/
if(0 == ph->current_shape)
ph->current_shape = item->rect;
ph->current_shape->rect(rc->left, rc->top, rc->right, rc->bottom);
agg::rasterizer_scanline_aa<> r;
r.add_path(*(ph->current_shape));
agg::scanline_p8 sl;
if(item->dwFlags & AGG_USE_GRADIENT_X_RENDERER)
agg::render_scanlines(r, sl, *(item->gradient_renderer_x));
else if(item->dwFlags & AGG_USE_GRADIENT_Y_RENDERER)
agg::render_scanlines(r, sl, *(item->gradient_renderer_y));
else
agg::render_scanlines(r, sl, *(item->solid_renderer));
}
if(imageItem && !(item->dwFlags & S_ITEM_SKIP_IMAGE))
renderImageItem(ph->hdcMem, imageItem, rc);
}
/**
* set our text color for Gfx::RenderText() - replaces GDI SetTextColor() for all
* cases where we are using DrawThemeTextEx() for full 32bit text rendering.
* @param clr new text color to use
*/
inline void Gfx::setTextColor(const COLORREF clr)
{
txtColor = clr;
}
inline COLORREF Gfx::getTextColor()
{
return(txtColor);
}
inline void Gfx::hsvTransformPixel(BYTE *p, float value, float v_s_u, float v_s_w, BYTE alpha)
{
// ain't matrices beautiful? :)
float r = (.299 * value +.701 * v_s_u +.168 * v_s_w) * p[2] + (.587 * value -.587 * v_s_u +.330 * v_s_w) * p[1] + (.114 * value -.114 * v_s_u -.497 * v_s_w) * p[0];
float g = (.299 * value -.299 * v_s_u -.328 * v_s_w) * p[2] + (.587 * value +.413 * v_s_u +.035 * v_s_w) * p[1] + (.114 * value -.114 * v_s_u +.292 * v_s_w) * p[0];
float b = (.299 * value -.3 * v_s_u +1.25 * v_s_w) * p[2]+ (.587* value -.588 * v_s_u -1.05 * v_s_w) * p[1] + (.114 * value +.886 * v_s_u -.203 * v_s_w) * p[0];
/*
* premultiply
*/
p[0] = (int)b * alpha/255;
p[1] = (int)g * alpha/255;
p[2] = (int)r * alpha/255;
}
inline void Gfx::rgbTransformPixel(BYTE *p, float r, float g, float b, BYTE alpha)
{
p[0] = (int)(p[0] + b) > 255 ? 255 : p[0] + b;
p[1] = (int)(p[1] + g) > 255 ? 255 : p[1] + g;
p[2] = (int)(p[2] + r) > 255 ? 255 : p[2] + r;
p[0] = p[0] * alpha/255;
p[1] = p[1] * alpha/255;
p[2] = p[2] * alpha/255;
}
#ifndef _XP_SUPPORT
#define INIT_PAINT(a, b, c) (hbp = Gfx::initiateBufferedPaint((a), (b), (c)))
#define FINALIZE_PAINT(a, b, c) Gfx::finalizeBufferedPaint((a), (b), (c))
#else
#endif
#endif /*__GFX_H_*/
|