/* * 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 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 m_rbase; agg::renderer_scanline_aa_solid > m_solid_renderer; typedef agg::span_gradient, agg::gradient_x, agg::pod_auto_array > span_gradient_t; typedef agg::span_allocator span_allocator_t; agg::renderer_scanline_aa, 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 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 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_*/