summaryrefslogtreecommitdiff
path: root/plugins/Clist_ng/INCLUDE/gfx.h
blob: 10b4da7e19809879ea8f71d3a006e3cb7a09cc91 (plain)
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_*/