render_bench/0000755000175000017500000000000011407021102013042 5ustar rasterrasterrender_bench/main.c0000644000175000017500000004223507715425766014177 0ustar rasterraster#include "main.h" #define REPS 4096 typedef struct _xrender_surf { int w, h; int depth; Visual *vis; Drawable draw; Picture pic; int allocated : 1; } Xrender_Surf; double get_time(void); void time_test(char *description, void (*func) (void)); Xrender_Surf *xrender_surf_new(Display *disp, Drawable draw, Visual *vis, int w, int h, int alpha); Xrender_Surf *xrender_surf_adopt(Display *disp, Drawable draw, Visual *vis, int w, int h); void xrender_surf_free(Display *disp, Xrender_Surf *rs); void xrender_surf_populate(Display *disp, Xrender_Surf *rs, int w, int h, int *img_data); void xrender_surf_blend(Display *disp, Xrender_Surf *src, Xrender_Surf *dst, int x, int y, int w, int h, int smooth); void populate_from_file(Display *disp, Xrender_Surf *rs, char *file); void main_loop(void); void setup_window(void); int win_w = 320; int win_h = 320; static Display *disp = NULL; static Window win; double get_time(void) { struct timeval timev; gettimeofday(&timev, NULL); return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); } void time_test(char *description, void (*func) (void)) { double t1, t2, t; int i; printf("---------------------------------------------------------------\n"); printf("Test: %s\n", description); t1 = get_time(); for (i = 0; i < REPS; i++) func(); XSync(disp, False); t2 = get_time(); t = t2 - t1; printf("Time: %3.3f sec.\n", t); } Xrender_Surf * xrender_surf_new(Display *disp, Drawable draw, Visual *vis, int w, int h, int alpha) { Xrender_Surf *rs; XRenderPictFormat *fmt; XRenderPictureAttributes att; rs = calloc(1, sizeof(Xrender_Surf)); if (alpha) fmt = XRenderFindStandardFormat(disp, PictStandardARGB32); else fmt = XRenderFindStandardFormat(disp, PictStandardRGB24); rs->w = w; rs->h = h; rs->depth = fmt->depth; rs->vis = vis; rs->draw = XCreatePixmap(disp, draw, w, h, fmt->depth); att.dither = 1; att.component_alpha = 1; att.repeat = 0; rs->pic = XRenderCreatePicture(disp, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att); rs->allocated = 1; return rs; } Xrender_Surf * xrender_surf_adopt(Display *disp, Drawable draw, Visual *vis, int w, int h) { Xrender_Surf *rs; XRenderPictFormat *fmt; XRenderPictureAttributes att; rs = calloc(1, sizeof(Xrender_Surf)); fmt = XRenderFindVisualFormat(disp, vis); rs->w = w; rs->h = h; rs->depth = fmt->depth; rs->vis = vis; rs->draw = draw; att.dither = 1; att.component_alpha = 1; att.repeat = 0; rs->pic = XRenderCreatePicture(disp, rs->draw, fmt, CPRepeat | CPDither | CPComponentAlpha, &att); rs->allocated = 0; return rs; } void xrender_surf_free(Display *disp, Xrender_Surf *rs) { if (rs->allocated) XFreePixmap(disp, rs->draw); XRenderFreePicture(disp, rs->pic); free(rs); } void xrender_surf_populate(Display *disp, Xrender_Surf *rs, int w, int h, int *img_data) { GC gc; XGCValues gcv; XImage *xim; int x, y; /* yes this isn't optimal - i know.. i just want some data for now */ gc = XCreateGC(disp, rs->draw, 0, &gcv); xim = XCreateImage(disp, rs->vis, rs->depth, ZPixmap, 0, NULL, w, h, 32, 0); xim->data = malloc(xim->bytes_per_line * xim->height); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int pixel; int a, r, g, b; pixel = img_data[(y * w) + x]; a = (pixel >> 24) & 0xff; r = (pixel >> 16) & 0xff; g = (pixel >> 8 ) & 0xff; b = (pixel ) & 0xff; r = (r * (a + 1)) / 256; g = (g * (a + 1)) / 256; b = (b * (a + 1)) / 256; XPutPixel(xim, x, y, (a << 24) | (r << 16) | (g << 8) | b); } } XPutImage(disp, rs->draw, gc, xim, 0, 0, 0, 0, w, h); free(xim->data); xim->data = NULL; XDestroyImage(xim); XFreeGC(disp, gc); } void xrender_surf_blend(Display *disp, Xrender_Surf *src, Xrender_Surf *dst, int x, int y, int w, int h, int smooth) { XFilters *flt; XTransform xf; xf.matrix[0][0] = (65536 * src->w) / w; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0; xf.matrix[1][0] = 0; xf.matrix[1][1] = (65536 * src->h) / h; xf.matrix[1][2] = 0; xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536; if (smooth) XRenderSetPictureFilter(disp, src->pic, "bilinear", NULL, 0); else XRenderSetPictureFilter(disp, src->pic, "nearest", NULL, 0); XRenderSetPictureTransform(disp, src->pic, &xf); XRenderComposite(disp, PictOpOver, src->pic, None, dst->pic, 0, 0, 0, 0, x, y, w, h); } void populate_from_file(Display *disp, Xrender_Surf *rs, char *file) { Imlib_Image im; DATA32 *pixels; int w, h; im = imlib_load_image(file); imlib_context_set_image(im); pixels = imlib_image_get_data_for_reading_only(); w = imlib_image_get_width(); h = imlib_image_get_height(); xrender_surf_populate(disp, rs, w, h, pixels); imlib_image_put_back_data(pixels); imlib_free_image_and_decache(); } Xrender_Surf *surf_win = NULL; Xrender_Surf *surf_off = NULL; Xrender_Surf *surf_img = NULL; Imlib_Image im_win = NULL; Imlib_Image im_img = NULL; void test_over_x(void) { int x, y; x = rand() % (surf_win->w - surf_img->w); y = rand() % (surf_win->h - surf_img->h); xrender_surf_blend(disp, surf_img, surf_win, x, y, surf_img->w, surf_img->h, 1); } void test_over_off_x(void) { int x, y; x = rand() % (surf_off->w - surf_img->w); y = rand() % (surf_off->h - surf_img->h); xrender_surf_blend(disp, surf_img, surf_off, x, y, surf_img->w, surf_img->h, 1); } void test_over_imlib2(void) { int x, y; int w, h, ww, hh; imlib_context_set_anti_alias(1); imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); x = rand() % (ww - w); y = rand() % (hh - h); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, x, y, w, h); } void test_over_scale_half_x(void) { int x, y; x = rand() % (surf_win->w - (surf_img->w / 2)); y = rand() % (surf_win->h - (surf_img->h / 2)); xrender_surf_blend(disp, surf_img, surf_win, x, y, surf_img->w / 2, surf_img->h / 2, 0); } void test_over_off_scale_half_x(void) { int x, y; x = rand() % (surf_off->w - (surf_img->w / 2)); y = rand() % (surf_off->h - (surf_img->h / 2)); xrender_surf_blend(disp, surf_img, surf_off, x, y, surf_img->w / 2, surf_img->h / 2, 0); } void test_over_scale_half_imlib2(void) { int x, y; int w, h, ww, hh; imlib_context_set_anti_alias(0); imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); x = rand() % (ww - (w / 2)); y = rand() % (hh - (h / 2)); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, x, y, w / 2, h / 2); } void test_over_scale_double_smooth_x(void) { int x, y; x = rand() % (surf_win->w - (surf_img->w * 2)); y = rand() % (surf_win->h - (surf_img->h * 2)); xrender_surf_blend(disp, surf_img, surf_win, x, y, surf_img->w * 2, surf_img->h * 2, 1); } void test_over_off_scale_double_smooth_x(void) { int x, y; x = rand() % (surf_off->w - (surf_img->w * 2)); y = rand() % (surf_off->h - (surf_img->h * 2)); xrender_surf_blend(disp, surf_img, surf_off, x, y, surf_img->w * 2, surf_img->h * 2, 1); } void test_over_scale_double_smooth_imlib2(void) { int x, y; int w, h, ww, hh; imlib_context_set_anti_alias(1); imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); x = rand() % (ww - (w * 2)); y = rand() % (hh - (h * 2)); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, x, y, w * 2, h * 2); } void test_over_scale_double_nearest_x(void) { int x, y; x = rand() % (surf_win->w - (surf_img->w * 2)); y = rand() % (surf_win->h - (surf_img->h * 2)); xrender_surf_blend(disp, surf_img, surf_win, x, y, surf_img->w * 2, surf_img->h * 2, 0); } void test_over_off_scale_double_nearest_x(void) { int x, y; x = rand() % (surf_off->w - (surf_img->w * 2)); y = rand() % (surf_off->h - (surf_img->h * 2)); xrender_surf_blend(disp, surf_img, surf_off, x, y, surf_img->w * 2, surf_img->h * 2, 0); } void test_over_scale_double_nearest_imlib2(void) { int x, y; int w, h, ww, hh; imlib_context_set_anti_alias(0); imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); x = rand() % (ww - (w * 2)); y = rand() % (hh - (h * 2)); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, x, y, w * 2, h * 2); } int count = 0; void test_over_scale_general_nearest_x(void) { int w, h; w = 1 + ((surf_img->w * count) / (REPS / 16)); h = 1 + ((surf_img->h * count) / (REPS / 16)); xrender_surf_blend(disp, surf_img, surf_win, 0, 0, w, h, 0); count++; } void test_over_off_scale_general_nearest_x(void) { int w, h; w = 1 + ((surf_img->w * count) / (REPS / 16)); h = 1 + ((surf_img->h * count) / (REPS / 16)); xrender_surf_blend(disp, surf_img, surf_off, 0, 0, w, h, 0); count++; } void test_over_scale_general_nearest_imlib2(void) { int w, h, ww, hh; imlib_context_set_anti_alias(0); imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); ww = 1 + ((w * count) / (REPS / 16)); hh = 1 + ((h * count) / (REPS / 16)); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, 0, 0, ww, hh); count++; } void test_over_scale_general_smooth_x(void) { int w, h; w = 1 + ((surf_img->w * count) / (REPS / 16)); h = 1 + ((surf_img->h * count) / (REPS / 16)); xrender_surf_blend(disp, surf_img, surf_win, 0, 0, w, h, 1); count++; } void test_over_off_scale_general_smooth_x(void) { int w, h; w = 1 + ((surf_img->w * count) / (REPS / 16)); h = 1 + ((surf_img->h * count) / (REPS / 16)); xrender_surf_blend(disp, surf_img, surf_off, 0, 0, w, h, 1); count++; } void test_over_scale_general_smooth_imlib2(void) { int w, h, ww, hh; imlib_context_set_image(im_win); ww = imlib_image_get_width(); hh = imlib_image_get_height(); imlib_context_set_image(im_img); w = imlib_image_get_width(); h = imlib_image_get_height(); ww = 1 + ((w * count) / (REPS / 16)); hh = 1 + ((h * count) / (REPS / 16)); if ((ww < w) && (hh < h)) imlib_context_set_anti_alias(0); else imlib_context_set_anti_alias(1); imlib_context_set_image(im_win); imlib_blend_image_onto_image(im_img, 0, 0, 0, w, h, 0, 0, ww, hh); count++; } void main_loop(void) { /* printf query filters */ printf("Available XRENDER filters:\n"); { int i; XFilters *flt; flt = XRenderQueryFilters(disp, win); for (i = 0; i < flt->nfilter; i++) printf("%s\n", flt->filter[i]); } printf("Setup...\n"); /* setup */ surf_win = xrender_surf_adopt(disp, win, DefaultVisual(disp, DefaultScreen(disp)), win_w, win_h); surf_off = xrender_surf_new(disp, win, DefaultVisual(disp, DefaultScreen(disp)), 320, 320, 0); surf_img = xrender_surf_new(disp, win, DefaultVisual(disp, DefaultScreen(disp)), 100, 100, 1); populate_from_file(disp, surf_win, "tst_opaque.png"); populate_from_file(disp, surf_off, "tst_opaque.png"); populate_from_file(disp, surf_img, "tst_transparent.png"); im_win = imlib_load_image("tst_opaque.png"); im_img = imlib_load_image("tst_transparent.png"); printf("*** ROUND 1 ***\n"); srand(7); time_test("Test Xrender doing non-scaled Over blends", test_over_x); srand(7); time_test("Test Xrender (offscreen) doing non-scaled Over blends", test_over_off_x); srand(7); time_test("Test Imlib2 doing non-scaled Over blends", test_over_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); sleep(2); printf("*** ROUND 2 ***\n"); srand(7); time_test("Test Xrender doing 1/2 scaled Over blends", test_over_scale_half_x); srand(7); time_test("Test Xrender (offscreen) doing 1/2 scaled Over blends", test_over_off_scale_half_x); srand(7); time_test("Test Imlib2 doing 1/2 scaled Over blends", test_over_scale_half_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); sleep(2); printf("*** ROUND 3 ***\n"); srand(7); time_test("Test Xrender doing 2* smooth scaled Over blends", test_over_scale_double_smooth_x); srand(7); time_test("Test Xrender (offscreen) doing 2* smooth scaled Over blends", test_over_off_scale_double_smooth_x); srand(7); time_test("Test Imlib2 doing 2* smooth scaled Over blends", test_over_scale_double_smooth_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); sleep(2); printf("*** ROUND 4 ***\n"); srand(7); time_test("Test Xrender doing 2* nearest scaled Over blends", test_over_scale_double_nearest_x); srand(7); time_test("Test Xrender (offscreen) doing 2* nearest scaled Over blends", test_over_off_scale_double_nearest_x); srand(7); time_test("Test Imlib2 doing 2* nearest scaled Over blends", test_over_scale_double_nearest_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); printf("*** ROUND 6 ***\n"); count = 0; time_test("Test Xrender doing general nearest scaled Over blends", test_over_scale_general_nearest_x); count = 0; time_test("Test Xrender (offscreen) doing general nearest scaled Over blends", test_over_off_scale_general_nearest_x); count = 0; time_test("Test Imlib2 doing general nearest scaled Over blends", test_over_scale_general_nearest_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); sleep(2); printf("*** ROUND 7 ***\n"); count = 0; time_test("Test Xrender doing general smooth scaled Over blends", test_over_scale_general_smooth_x); count = 0; time_test("Test Xrender (offscreen) doing general smooth scaled Over blends", test_over_off_scale_general_smooth_x); count = 0; time_test("Test Imlib2 doing general smooth scaled Over blends", test_over_scale_general_smooth_imlib2); imlib_context_set_image(im_win); imlib_context_set_display(disp); imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp))); imlib_context_set_colormap(DefaultColormap(disp, DefaultScreen(disp))); imlib_context_set_drawable(win); imlib_render_image_on_drawable(0, 0); sleep(2); XSync(disp, False); } void setup_window(void) { XSetWindowAttributes att; XClassHint *xch; att.background_pixmap = None; att.colormap = DefaultColormap(disp, DefaultScreen(disp)); att.border_pixel = 0; att.event_mask = ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask; win = XCreateWindow(disp, RootWindow(disp, DefaultScreen(disp)), 0, 0, win_w, win_h, 0, DefaultDepth(disp, DefaultScreen(disp)), InputOutput, DefaultVisual(disp, DefaultScreen(disp)), CWColormap | CWBorderPixel | CWEventMask | CWBackPixmap, &att); XStoreName(disp, win, "Render Test Program"); xch = XAllocClassHint(); xch->res_name = "Main"; xch->res_class = "Render_Demo"; XSetClassHint(disp, win, xch); XFree(xch); XMapWindow(disp, win); XSync(disp, False); usleep(200000); XSync(disp, False); } int main(int argc, char **argv) { disp = XOpenDisplay(NULL); if (!disp) { printf("ERROR: Cannot connect to display!\n"); exit(-1); } setup_window(); main_loop(); return 0; } render_bench/main.h0000644000175000017500000000052307715421432014160 0ustar rasterraster#ifndef MAIN_H #define MAIN_H #include #include #include #include #include #include #include #include #include #include #include #include #include #endif render_bench/Makefile0000644000175000017500000000063007715421474014530 0ustar rasterrasterSRCS = main.c HEAD = FLGS = -g -I/usr/X11R6/include `imlib2-config --cflags` LIBS = -L/usr/X11R6/lib -lm -lX11 -lXext -lXrender `imlib2-config --libs` #################### OBJS = $(SRCS:.c=.o) render_bench: $(OBJS) $(RM) $@ $(CC) -o $@ $(OBJS) $(LIBS) .c.o: $(CC) $(FLGS) -c $< -o $@ clean:: rm -rf render_bench *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut "#"* render_bench/tst_opaque.png0000644000175000017500000001346307715335250015766 0ustar rasterrasterPNG  IHDR@@B2IDATx];$ a {uCAc!Қ odLWueV|H?/LveuUdZFZN_kחgo0~Q|ӏ5X>bk,/:|}!xw;)4| 6<<I BF:$$8FxMMߧР,Dl76!B8F&~cT>#A H'pIH MNBd嗂DȀ''ͥ3<߸U'. X~#i& AN<A-)] Xjڲ&e3ўWҠcOHsM o3ok $ըFQ jvʗ4 XӌM}Qx{jJkH0HiDſLՂʺl#6kA% F.?1|$ jZH#9f`6&IF !5f^ =54RtNj?չNH甬v sR9gjgXp,KL)nca0 4. HS;9֪cBB_ Q 9^_0#|HfmZO5/I?[kUH`z,Ģ@o%͖a ,Fe }+` :kK'BؼP.VD B; 9QnD.Vih(:J F V@0 KG(kZ -`f;7IJc,z  BgmB׮MUsR szGeI <bQ_ ?^:[$- !wzUB٩QP 2 B E$ZAh \0/&#E徘4yERgKS;x PWHỎf_/wB|{3nBcW6̫ QJIJswR?j8T-SuIB,J!RG] fx$Z=&U@h@h1霴{LjV5S1[ jZs4`Lh@-w$/Q T O˞k#c{#Pl90 6s7ԉFLf,w$_F2oBBwve}V֍ NDtY;4k"`_?.lJZk ~́o0$b:i"FB$ʇW &`Ì#&F8 sFI󾷸F8 I$bh6-BTl2).z`BVso'5@-8 K-uXupnYvWyaO!4`!X M €_,4ZYMHsMI#qX+؄4 x4RtNxJJ4$x });67R #dA\H AZyaX+؄4'^ݒiD:' k:6!M 5BF X5Ze/bs2V I+޷5FZ kҚ|;i|* xr Q ךs <b؄֤N-4 bFtIi'^ݒiD:'LlBLzMiV'ͥ5E diKS;:`!_co@K@X~DP 2V']Zh5 x7%D{oܓF*"dMFZ7 l6ZEMH/jZ#|&ľk X ѣcR]hϫh1\Zt#H n_m<֨cA._Fb' jbUT R@A jRk3^bLd`V]c1Z4{j=ISSoo_J9?Oӳ?ue?hlB\kR5y<0ߟ<= Jzĵ&i_jRt $7 n668Y\" HMX nPM4IckrwuHv&5_F8M̹9tJv&&c'nl1zdI,5y1•W娦AH!I/%7RL#$ƑUhjFrEUB;*-u4Fc%`Dfɤ&rNb@=N4+wBDR4 Nqi>:I,VMu.*Y_暔-W`w@%M KH%($5)7ZtY+IbنFJd9hz &abl 97CnI4/Fr¤i2)6a!tJb-: Isil^ -@JljW3h9+\O$vY촳Ϧv~'a&W$+W'̲Vh"V_C#ǙAz--k%jLҀ$B:F3R5_{U#g+Wx1^VçVo~*%Nw_uO{ޜn Sq+Bu[$wZOi0ú3k%=|r,Byz|1ӧ4ӗ~U)^SmOzruN/4F ucr C DJDnZ΀90>W?~m+?Ai@*r9_uzW4o-W賫ģ'?xs_uFv͖PrwK'Z>ҙ{)Ƀ)%Azc=#ܼ|dH¢E#zzM+~/i/D1hHId|[!K_%l߆ 3٪ϓRP.}WL )K#O2}馄+ 9UTcSwo]CH#M޷[59G?˟&^z,~ Jkl޵)=nn*Z)^oS>S 3e z>J,AplM̕Z(W L7$gD`D!Ja{9gӓX^\$ٔ~=}`RҖ@x6:hYG{'^{ɟӯrF]iP.-@cȧ3@H$-l&LB"iA#2  T(V\(Ob|!e2Mg'6qFzAcg JjMS X$']5 m0xVܰn Zv%l\Ybծ߶qࣥJ]T%+m (:ʄ4HA*30. ] L 2 _*mYchcfAbH 61(@PkN7^َ62T[2\ztM|՛N[ĵЅ{ҙUCw%(0nLJ0\13 4ʾ gT} *D,>} 'X@*a@5[>&JWg kA@)B,Uٔ+ R W?;w2X<-4ʳ?~XOXg d@QQ ʗ oS[D+Q Fȥ4  ݛ[X83=[<>єT0BJ ?HEfvWN @@@&Qꋮ7Md,[>P[gIRz37nXxR]>R $xh $ x!6ѕ`{uRQ{Ȥ4X&ƃ$g\J㽽LϾorlҪ+;m0*jKHۨEJ7>O{H'kɄ^Hj~!iai0!jo@u~'ƊW*T\ 6K 4 kp: ] /9Sp\`l#e1X R+B!?֩tLqJ*H.ަ_eޱKZ+mՕ5mp`K1FWRҪa+Pı; *:|EP*?)x2J1 zűB4t30Secth= 0RCcD\ 01r_}ϼtͨrDBAB.g:4]s.N +VoYjEP" R:V8IGK 8˃֊ Tת"D"&@kе dXc@:Xh3B@ ` ]IUI}=7sp%-ܩӘ-7n^ݟ_;u豛1(N2̟ ( eݯWkvzk:XRQ`Zѷw~BJY΁dBE%Bb0BuZv3nht48:FSXeM! +AZN p(Ncj[-n?^eۀs%,wra{NNG$ċ+ aX5[:36C~ SZbl0(uB_F@Ŗ"^3R !&9@ :8>Fzuc2۶iYZ-'$ X<M0ܹi[]N +Bzx4 JɄ,o0WQBWZ4e*:Q &g09CVzαP1UrQwߗ5BZGwV֝ f¯ j2@r9La$VX\&E`[y#os. e}j+:$K7oZ1Th#'p4\ p "n:WL'eC=: 9 L`f4  `: r(Z҂&NùT̟OqdƵ[MW@1Zb6oYOdK`-0B #rގ5(JQ 'm̕]dR2ICg0}`&6ȡׁTвF#i 9$N,pЪV!:CĸfFP]C_}úz*LX!@`eHcpOv<A^u(%(8 \ JT' պ1"3=oƄt& ǯ̡YdIzGOZ =a'Nɷ{M1ե |.)VS82%,A*EpBB vܖ q|-5P ,@J{~. H%t;b>5xiu).d L  岥Jb`7ixAd_>TpDCאKLB .q> 3<%aj{MO,c%It@"dr :=OU`lJw *+ H4D#`I$w6U&-ˬi~y2(ZF+maEʀ$q$~eD  X#!# Z! )@t(LG΃RۓF>۸%  BJ՚n%[JH!c. "j,2?Zm /+m` OM84S<ڛyuÛGI#D$4B_Vf`) V]!P2U0Ӏ緣|\Kg|d>8"٤aExm0ڀDka<ҖN#du0 BMeM8R*[;9Qϔ-N+۝1356& \`P=^ 9e&TRזnRTU.(9,K+%oWe-JH੔hDalW|hlYj`U $te`p )  ~;%/ 0'((Q`bTim{O?Zm/UCs ZH>຾։xb^3&Wx;N``JAHza3u}FDUW1GsA߻@{/J'>=,[h$af.QÕ,^Rygg Hѭv--RP vǦազW"dn `jm_jњR7kKtd۶m;/mZ?*s1wUT[M['e.j^-Z] @5u @88s0 ""PAܗ*=^3\3 Υo288GVehBM^M@ 鹾9,?v0=0x# \]W<Š _70RE+o6<Rtb*(?h&GGG0  ۫i(?:>Wo|F{FxWb\@Ts7ޒ6ӈfLj$.h&T ,}gksy.ʏ }g4M%geњ;GdY%m| @NTXRWLx\ 8J!rOE8}b+2#LgҪE68.ݼ?h؎ʨ^(=&mJZ>zxG PB0ylTw=wuB܆]3MG $f+sM9s$꾂)x!;Qh{˚;lw=89Ib.Xƽ?W߲{ӲM4[nCEiw*pO?ߛx-+2S%G%9WNe"RRW|>"%E`^ɢƂnCRўwvٯTGf-u6|?GZ}fpe=ΛSrܸ7_u=ucn,+$s\#~\TBp6 V6(s5}YhXp{%*M Θw޾;oJub և6Ѳft0w3u]{n]Dd^?OzbӨU<%R ]pxL:8s؈E^-g͛t`_LrBzWɉҮA/J {_~Ə|ٟO|\,Y0#],e9L\ .M~ _*L%-u9 F LoIDATڄzdٙl ʊy5%d)GBͫ:`) H]2[sc6^w!8 ^+F0Šs%(Lo.Y}-[Z_g7g|rX8itoEyv{瑩3QU+i?.GB.mMV,{lDBkg}-RJqN#yOwGuɴ37F(#g#10n ?#Օ@D,<7߽oˍ[{=3&>N}=]1Z˳xi{ rjTqeO޺yx4m$QѼ5c d0^Fe." |"=glsMás84fjh lZo~嫖(-.Nњe9ԅo{fRO<;4a@޺:[ox|we1{`d˝knЌd6e}/ i6 q Hĥ'8nI@MwC85d HSgJ|3ǂA=RcVo6~s_Wf-=o=*j̯H۞N ~0З z@-rlA>3ċn@vH 4BKl3U89&=0aa`ݳ.S6 C`FQƑ\u   P0P83]Ǒ\lL$z pmw%@8sX,]/5{/skFg!_H.˄-Q5BݹWBB;mONՓG&|mZ9@L4Fn(&翼p VfJ-TYyQ=T:fj8=Uř&&(d FLXͫ32Y0pVp|'&k_w^5ő I;}tչr@X`))QBBc$~e<2"#\Mf0q広V-}`sg{k9VѕbC kS3BaB܂ y>0 oA09Jso! c`b639p@e!ԤuE{|" cߢ 3Sՙ/xoLg*LԂS@Ph\Z+5ϗ XTHZwְsf>3fyAA@>λ;Nϯ:')IЄ9:ydn&p<:ytz'"yN|DID?Qψ*k)w&Їq{B}jPJ.J|  )L:q8]& y=``p=@bOʭ7Εʮz-ɴktĦ]};LG6ϋUF۪"]Ί&:}Տr.ݍN7>UovŀJ#c s(2pa CPm \_VC4-4Q-qBRIVwGm m%BhEॆE'UW=[xl5> )WNpkAg7g=Pkk;&x*PG+TPl)UzoxY;D͝h=NYjkOl\ Fks㏞-p^?ب)<07_mOm];)8c R+?㎍}\ ';p*i;\`y-Tv (`߿{/<.5t_vm+5/FԯnRs ] զw)=. /hrduPQAZ8-i);Nru*wl*DRIדբhJ|`*ם|n=ScPզ԰V鶊V^=}2d.l9y`tr|xYtYqylG9i ^I\8M'b.5RxA]<]j0ji+8JߗqEuЯrxO tx)b GA$Ďd])@#6:0:?:RD hK81U M(WhNU6\x[:!%C:8mb:(3S VWXBd$ =2ˍEYc+q "O'7}TH*.Wn ݢPXŝRM2"s^q;ȦwJ]D!S\f{T|;D"]bSF3@{~lu )j'SEeLu$Y.ᵩUB#zS$m hֵu:.:ODʘ$ٗHJN72yDRip:wQ)P1W[\aJI5 /Bm5gʨKyAf/ܵbv;?4Ie78!XZ7J.hw c[҃;LjO,됊#ލ b ;N@8::t\m@_4j_ZĊ= WLRx(?{%<wwQd(p?"VW{c5$~:;sQq@x0T^M^+ qS:dy.3t zitz.!bU"tIENDB`