pax_global_header00006660000000000000000000000064145475423420014524gustar00rootroot0000000000000052 comment=460e4ca34f1e310aebccefc8756cc56983ea1d4c view-0.3.00/000077500000000000000000000000001454754234200125565ustar00rootroot00000000000000view-0.3.00/.gitignore000066400000000000000000000000621454754234200145440ustar00rootroot00000000000000view cfl2png viewer.inc tags *.swp Makefile.local view-0.3.00/.gitlab-ci.yml000066400000000000000000000005551454754234200152170ustar00rootroot00000000000000variables: OMP_NUM_THREADS: "1" before_script: - apt-get update -qq && apt-get install -y -qq - apt-get install -y -qq make git gcc - apt-get install -y -qq libgtk-3-dev - apt-get install -y -qq libbart-dev stages: - build Build: image: debian:bookworm stage: build script: - make all artifacts: paths: - view - cfl2png view-0.3.00/LICENSE000066400000000000000000000030351454754234200135640ustar00rootroot00000000000000Copyright (c) 2015-2023. Martin Uecker. Copyright (c) 2017-2023. AG Uecker. University Medical Center Göttingen. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. view-0.3.00/Makefile000066400000000000000000000036311454754234200142210ustar00rootroot00000000000000 -include Makefile.local CUDA?=0 CUDA_BASE ?= /usr/local/cuda CUDA_LIB ?= lib DEBUG?=0 OMP?=1 PKG_CONFIG?=pkg-config BUILDTYPE = Linux UNAME = $(shell uname -s) ifeq ($(UNAME),Darwin) BUILDTYPE = MacOSX endif ifeq ($(BART_TOOLBOX_PATH),) TOOLBOX_INC=/usr/include/bart/ TOOLBOX_LIB=/usr/lib/bart/ else TOOLBOX_INC=$(BART_TOOLBOX_PATH)/src/ TOOLBOX_LIB=$(BART_TOOLBOX_PATH)/lib/ endif ifeq ($(origin CC), default) CC = gcc endif CFLAGS ?= -Wall ifeq ($(DEBUG),1) CFLAGS += -Og -g else CFLAGS += -O2 endif ifeq ($(BUILDTYPE), MacOSX) CFLAGS += -std=c11 -Xpreprocessor else CFLAGS += -std=c11 endif ifeq ($(OMP),1) CFLAGS += -fopenmp else CFLAGS += -Wno-unknown-pragmas endif # clang ifeq ($(findstring clang, $(CC)), clang) CFLAGS += -fblocks LDFLAGS += -lBlocksRuntime endif ifeq ($(CUDA),1) CUDA_L := -L$(CUDA_BASE)/$(CUDA_LIB) -lcufft -lcudart -lcublas else CUDA_L := endif EXPDYN = -rdynamic ifeq ($(BUILDTYPE), MacOSX) LDFLAGS += -L/opt/local/lib -lm -lpng -lomp -lrt else LDFLAGS += -lm -lpng -lrt endif all: view cfl2png src/viewer.inc: src/viewer.ui @echo "STRINGIFY(`cat src/viewer.ui`)" > src/viewer.inc view: src/main.c src/view.[ch] src/draw.[ch] src/viewer.inc $(CC) $(CFLAGS) $(CPPFLAGS) $(EXPDYN) -o view -I$(TOOLBOX_INC) `$(PKG_CONFIG) --cflags gtk+-3.0` src/main.c src/view.c src/draw.c `$(PKG_CONFIG) --libs gtk+-3.0` $(TOOLBOX_LIB)/libmisc.a $(TOOLBOX_LIB)/libgeom.a $(TOOLBOX_LIB)/libnum.a $(TOOLBOX_LIB)/libmisc.a $(CUDA_L) $(LDFLAGS) cfl2png: src/cfl2png.c src/view.[ch] src/draw.[ch] src/viewer.inc $(CC) $(CFLAGS) $(CPPFLAGS) $(EXPDYN) -o cfl2png -I$(TOOLBOX_INC) src/cfl2png.c src/draw.c $(TOOLBOX_LIB)/libmisc.a $(TOOLBOX_LIB)/libgeom.a $(TOOLBOX_LIB)/libnum.a $(TOOLBOX_LIB)/libmisc.a $(CUDA_L) $(LDFLAGS) install: install -D view $(DESTDIR)/usr/lib/bart/commands/view install cfl2png $(DESTDIR)/usr/lib/bart/commands/ clean: rm -f view cfl2png src/viewer.inc view-0.3.00/README.md000066400000000000000000000016031454754234200140350ustar00rootroot00000000000000 Small image viewer for multi-dimensional files. Compiles on Linux and Mac OS X. It needs some libraries from the Berkeley Advanced Reconstruction Toolbox (BART). https://mrirecon.github.io/bart/ ![viewer](viewer.png) Installation: Requires BART 0.8.00 or later. Mac OS X: sudo port install pkgconfig sudo port install gtk3 sudo port install adwaita-icon-theme sudo port install libomp Linux: sudo apt-get install libgtk-3-dev Compile with make after setting the TOOLBOX_PATH environment variable to the directory where BART is installed. ### Usage `view ...` ### Troubleshooting If an error is raised along the lines of `Gtk-WARNING **: cannot open display`, ensure X11 is installed and running. On later versions of OS X, you may need ![XQuartz](https://www.xquartz.org/) for View to run. Set the environment variable `DISPLAY=":0"`, run XQuartz, and then retry `view`. view-0.3.00/genctags000077500000000000000000000001251454754234200142750ustar00rootroot00000000000000#!/bin/bash ctags --langmap=c++:+.cu --extra=+f `find . -regex '.*\.[ch]u*' -print` view-0.3.00/src/000077500000000000000000000000001454754234200133455ustar00rootroot00000000000000view-0.3.00/src/cfl2png.c000066400000000000000000000153771454754234200150610ustar00rootroot00000000000000/* Copyright 2017-2023. AG Uecker. University Medical Center Göttingen. * All rights reserved. Use of this source code is governed by * a BSD-style license which can be found in the LICENSE file. */ #include #include #include #include #include #include "num/multind.h" #include "num/init.h" #include "misc/misc.h" #include "misc/debug.h" #include "misc/mmio.h" #include "misc/opts.h" #include "misc/png.h" #if 0 #include "misc/io.h" #else extern void io_reserve_input(const char* name); extern void io_unregister(const char* name); #endif #include "draw.h" #ifndef CFL_SIZE #define CFL_SIZE sizeof(complex float) #endif #ifndef DIMS #define DIMS 16 #endif static void export_images(const char* output_prefix, int xdim, int ydim, float windowing[2], bool absolute_windowing, float zoom, enum mode_t mode, enum flip_t flip, enum interp_t interpolation, const long dims[DIMS], const complex float* idata); static const char help_str[] = "Export images to png."; int main(int argc, char* argv[argc]) { const char* in_file; const char* out_prefix; struct arg_s args[] = { ARG_INFILE(true, &in_file, "input"), ARG_STRING(true, &out_prefix, "output_prefix"), }; int xdim = 0; int ydim = 0; float windowing[2] = {0.f, 1.f}; bool absolute_windowing = false; enum mode_t mode = MAGN; float zoom =2.f; enum flip_t flip = OO; enum interp_t interpolation = NLINEAR; struct opt_s modeopt[] = { OPT_SELECT('M', enum mode_t, &mode, MAGN, "magnitude gray (default) "), OPT_SELECT('V', enum mode_t, &mode, MAGN_VIRIDS, "magnitude viridis"), OPT_SELECT('C', enum mode_t, &mode, CMPL, "complex"), OPT_SELECT('G', enum mode_t, &mode, CMPL_MYGBM, "complex MYGBM"), OPT_SELECT('P', enum mode_t, &mode, PHSE, "phase"), OPT_SELECT('Y', enum mode_t, &mode, PHSE_MYGBM, "phase MYGBM"), OPT_SELECT('R', enum mode_t, &mode, REAL, "real"), OPT_SELECT('T', enum mode_t, &mode, MAGN_TURBO, "magnitude turbo"), OPT_SELECT('F', enum mode_t, &mode, FLOW, "flow"), }; struct opt_s flipopt[] = { OPT_SELECT('O', enum flip_t, &flip, OO, "flip OO"), OPT_SELECT('X', enum flip_t, &flip, XO, "flip XO"), OPT_SELECT('Y', enum flip_t, &flip, OY, "flip OY"), OPT_SELECT('Z', enum flip_t, &flip, XY, "flip XY"), }; struct opt_s interpopt[] = { OPT_SELECT('L', enum interp_t, &interpolation, NLINEAR, "n-linear interpolation"), OPT_SELECT('M', enum interp_t, &interpolation, NLINEARMAG, "n-linear interpolation on the magnitude"), OPT_SELECT('N', enum interp_t, &interpolation, NEAREST, "nearest neighbor interpolation"), OPT_SELECT('C', enum interp_t, &interpolation, LIINCO, "line-integral convolution"), }; const struct opt_s opts[] = { OPT_INT('x', &xdim, "xdim", "output xdim (default: 0) "), OPT_INT('y', &ydim, "ydim", "output ydim (default: 0). If both are zero, first two non-singleton dims are used."), OPT_FLOAT('l', &windowing[0], "l", "lower windowing value"), OPT_FLOAT('u', &windowing[1], "u", "upper windowing value"), OPT_SET('A', &absolute_windowing, "use absolute windowing"), OPT_FLOAT('z', &zoom, "z", "zoom factor (default: 2) "), OPT_SUBOPT('C', "cmap", "colormap. -Ch for help.", ARRAY_SIZE(modeopt), modeopt), OPT_SUBOPT('F', "flip", "flip. -Fh for help.", ARRAY_SIZE(flipopt), flipopt), OPT_SUBOPT('I', "interp", "interp. -Ih for help.", ARRAY_SIZE(interpopt), interpopt), OPT_INT('d', &debug_level, "level", "Debug level"), }; cmdline(&argc, argv, ARRAY_SIZE(args), args, help_str, ARRAY_SIZE(opts), opts); if (!absolute_windowing) assert( (windowing[0] >= 0.f) && (windowing[1] <= 1.f) && (windowing[0] < windowing[1])); assert((0 <= xdim) && (xdim < DIMS)); assert((0 <= ydim) && (ydim < DIMS)); /* * If the filename ends in ".hdr", ".cfl" or just "." (from * tab-completion), just replace the "." with a \0-character. */ io_unregister(in_file); char* dot = strrchr(in_file, '.'); if ( (NULL != dot) && ( !strcmp(dot, ".cfl") || !strcmp(dot, ".hdr") || !strcmp(dot, "."))) *dot = '\0'; io_reserve_input(in_file); long dims[DIMS]; complex float* idata = load_cfl(in_file, DIMS, dims); char* ext = rindex(out_prefix, '.'); if (NULL != ext) { if (0 != strcmp(ext, ".png")) error("Unknown file extension."); else *ext = '\0'; } export_images(out_prefix, xdim, ydim, windowing, absolute_windowing, zoom, mode, flip, interpolation, dims, idata); unmap_cfl(DIMS, dims, idata); return 0; } /** * Convert flat index to pos * */ static void unravel_index(int D, long pos[D], unsigned long flags, const long dims[D], long index) { long ind = index; for (int d = 0; d < D; ++d) { if (!MD_IS_SET(flags, d)) continue; pos[d] = ind % dims[d]; ind /= dims[d]; } } void export_images(const char* output_prefix, int xdim, int ydim, float windowing[2], bool absolute_windowing, float zoom, enum mode_t mode, enum flip_t flip, enum interp_t interpolation, const long dims[DIMS], const complex float* idata) { if (xdim == ydim) { long sq_dims[2] = { 0 }; int l = 0; for (int i = 0; (i < DIMS) && (l < 2); i++) if (1 != dims[i]) sq_dims[l++] = i; assert(2 == l); xdim = sq_dims[0]; ydim = sq_dims[1]; } double max = 0.; if (absolute_windowing) { max = 1.; } else { for (long j = 0; j < md_calc_size(DIMS, dims); j++) if (max < cabsf(idata[j])) max = cabsf(idata[j]); if (0. == max) max = 1.; } int rgbw = dims[xdim] * zoom; int rgbh = dims[ydim] * zoom; int rgbstr = 4 * rgbw; // loop over all dims other than xdim and ydim long loopdims[DIMS]; unsigned long imflags = (MD_BIT(xdim)|MD_BIT(ydim)); md_select_dims(DIMS, ~imflags, loopdims, dims); debug_printf(DP_DEBUG3, "imflags: %lu\nloopdims: ", imflags); debug_print_dims(DP_DEBUG3, DIMS, loopdims); long strs[DIMS]; md_calc_strides(DIMS, strs, dims, sizeof(complex float)); #pragma omp parallel for for (unsigned long d = 0l; d < md_calc_size(DIMS, loopdims); ++d) { long pos[DIMS] = { [0 ... DIMS - 1] = 0 }; unravel_index(DIMS, pos, ~0L, loopdims, d); debug_printf(DP_DEBUG3, "\ti: %lu\n\t", d); debug_print_dims(DP_DEBUG3, DIMS, pos); // Prepare output filename char* name = construct_filename_view(DIMS, loopdims, pos, output_prefix, "png"); debug_printf(DP_DEBUG2, "\t%s\n", name); complex float* buf = xmalloc(rgbh * rgbw * sizeof(complex float)); update_buf(xdim, ydim, DIMS, dims, strs, pos, flip, interpolation, zoom, zoom, false, rgbw, rgbh, idata, buf); unsigned char* rgb = xmalloc(rgbh * rgbstr); draw(rgbw, rgbh, rgbstr, (unsigned char(*)[rgbw][rgbstr / 4][4])rgb, mode, 1. / max, windowing[0], windowing[1], 0, rgbw, buf); xfree(buf); if (0 != png_write_bgr32(name, rgbw, rgbh, 0, rgb)) error("Error: writing image file.\n"); xfree(rgb); xfree(name); } } view-0.3.00/src/colormaps.inc000066400000000000000000000602011454754234200160360ustar00rootroot00000000000000// cyclic_mygbm is a phase colormap from the the // "colorcet" project (https://github.com/bokeh/colorcet) // using a transition from magenta through yellow, green, and blue back to // magenta. More information can be found in: // Peter Kovesi. Good Colour Maps: How to Design Them. // arXiv:1509.03700 [cs.GR] 2015 // cyclic_mygbm by Peter Kovesi (http://www.peterkovesi.com) is licensed under the // Creative Commons Attribution 4.0 International Public License (CC-BY) // (https://creativecommons.org/licenses/by/4.0/) // Direct link to the colormap is // https://github.com/bokeh/colorcet/blob/master/assets/CETperceptual_csv_0_1/cyclic_mygbm_30-95_c78_n256_s25.csv static const double cyclic_mygbm[256][3] = { {0.18062, 0.13244, 0.91856}, {0.19173, 0.12446, 0.92396}, {0.20389, 0.11765, 0.92889}, {0.21681, 0.11214, 0.93335}, {0.23028, 0.10794, 0.93739}, {0.24417, 0.10525, 0.94101}, {0.25828, 0.10403, 0.94425}, {0.27246, 0.10417, 0.94716}, {0.28659, 0.10560, 0.94977}, {0.30059, 0.10807, 0.95212}, {0.31440, 0.11153, 0.95426}, {0.32796, 0.11565, 0.95622}, {0.34126, 0.12031, 0.95803}, {0.35426, 0.12544, 0.95973}, {0.36698, 0.13084, 0.96134}, {0.37942, 0.13637, 0.96288}, {0.39160, 0.14210, 0.96436}, {0.40352, 0.14786, 0.96581}, {0.41520, 0.15363, 0.96722}, {0.42669, 0.15941, 0.9686}, {0.43799, 0.16515, 0.96996}, {0.44913, 0.17088, 0.97129}, {0.46013, 0.17650, 0.97261}, {0.47106, 0.18204, 0.97389}, {0.48191, 0.18751, 0.97514}, {0.49273, 0.19280, 0.97635}, {0.50357, 0.19798, 0.97752}, {0.51446, 0.20302, 0.97863}, {0.52541, 0.20790, 0.97969}, {0.53647, 0.21259, 0.98067}, {0.54767, 0.21709, 0.98157}, {0.55903, 0.22137, 0.98238}, {0.57057, 0.22542, 0.9831}, {0.58230, 0.22925, 0.98372}, {0.59423, 0.23284, 0.98424}, {0.60636, 0.23623, 0.98465}, {0.61868, 0.23935, 0.98497}, {0.63116, 0.24227, 0.98518}, {0.64379, 0.24496, 0.9853}, {0.65656, 0.24750, 0.98533}, {0.66943, 0.24982, 0.98527}, {0.68237, 0.25203, 0.98515}, {0.69537, 0.25408, 0.98496}, {0.70839, 0.25601, 0.98471}, {0.72139, 0.25786, 0.98441}, {0.73437, 0.25968, 0.98405}, {0.74728, 0.26143, 0.98365}, {0.76012, 0.26319, 0.98321}, {0.77286, 0.26494, 0.98271}, {0.78548, 0.26676, 0.98216}, {0.79794, 0.26867, 0.98153}, {0.81023, 0.27069, 0.98082}, {0.82233, 0.27285, 0.98001}, {0.83418, 0.27525, 0.97905}, {0.84576, 0.27791, 0.97793}, {0.85702, 0.28089, 0.97661}, {0.86794, 0.28430, 0.97504}, {0.87845, 0.28819, 0.97317}, {0.88852, 0.29263, 0.97097}, {0.89810, 0.29768, 0.96837}, {0.90717, 0.30340, 0.96533}, {0.91567, 0.30984, 0.96182}, {0.92361, 0.31700, 0.9578}, {0.93095, 0.32489, 0.95323}, {0.93769, 0.33352, 0.94809}, {0.94383, 0.34283, 0.94239}, {0.94939, 0.35275, 0.93613}, {0.95439, 0.36323, 0.92931}, {0.95886, 0.37422, 0.92198}, {0.96283, 0.38558, 0.91416}, {0.96634, 0.39727, 0.90588}, {0.96944, 0.40921, 0.89721}, {0.97216, 0.42130, 0.88817}, {0.97454, 0.43350, 0.87883}, {0.97663, 0.44573, 0.86923}, {0.97845, 0.45797, 0.8594}, {0.98004, 0.47017, 0.84939}, {0.98142, 0.48228, 0.83922}, {0.98262, 0.49430, 0.82894}, {0.98364, 0.50620, 0.81854}, {0.98453, 0.51799, 0.80808}, {0.98527, 0.52964, 0.79753}, {0.98588, 0.54116, 0.78694}, {0.98637, 0.55255, 0.77629}, {0.98676, 0.56382, 0.76559}, {0.98704, 0.57494, 0.75486}, {0.98722, 0.58595, 0.74407}, {0.98733, 0.59683, 0.73326}, {0.98736, 0.60758, 0.72241}, {0.98732, 0.61821, 0.71153}, {0.98723, 0.62871, 0.7006}, {0.98710, 0.63909, 0.68965}, {0.98694, 0.64936, 0.67864}, {0.98677, 0.65949, 0.66762}, {0.98660, 0.66950, 0.65654}, {0.98644, 0.67938, 0.64543}, {0.98630, 0.68916, 0.63428}, {0.98621, 0.69879, 0.62309}, {0.98616, 0.70831, 0.61184}, {0.98617, 0.71772, 0.60055}, {0.98624, 0.72700, 0.5892}, {0.98636, 0.73618, 0.57779}, {0.98653, 0.74526, 0.56632}, {0.98675, 0.75426, 0.55476}, {0.98702, 0.76315, 0.54311}, {0.98731, 0.77198, 0.53134}, {0.98761, 0.78074, 0.51946}, {0.98792, 0.78944, 0.50744}, {0.98820, 0.79807, 0.49526}, {0.98845, 0.80666, 0.48291}, {0.98864, 0.81518, 0.47039}, {0.98875, 0.82365, 0.45765}, {0.98875, 0.83206, 0.4447}, {0.98863, 0.84039, 0.43151}, {0.98834, 0.84863, 0.41804}, {0.98786, 0.85677, 0.40433}, {0.98715, 0.86476, 0.39033}, {0.98616, 0.87259, 0.37607}, {0.98485, 0.88020, 0.36152}, {0.98316, 0.88754, 0.3467}, {0.98105, 0.89458, 0.33163}, {0.97845, 0.90123, 0.31637}, {0.97534, 0.90744, 0.30092}, {0.97166, 0.91315, 0.28537}, {0.96737, 0.91831, 0.26983}, {0.96244, 0.92285, 0.25432}, {0.95686, 0.92672, 0.23895}, {0.95064, 0.92990, 0.22388}, {0.94377, 0.93236, 0.2092}, {0.93628, 0.93410, 0.19506}, {0.92820, 0.93512, 0.18153}, {0.91959, 0.93544, 0.16884}, {0.91047, 0.93510, 0.15697}, {0.90091, 0.93414, 0.1461}, {0.89097, 0.93263, 0.13623}, {0.88070, 0.93061, 0.12747}, {0.87014, 0.92815, 0.11977}, {0.85937, 0.92531, 0.11315}, {0.84840, 0.92216, 0.10742}, {0.83729, 0.91874, 0.10257}, {0.82605, 0.91511, 0.098502}, {0.81472, 0.91132, 0.095091}, {0.80332, 0.90739, 0.092162}, {0.79187, 0.90336, 0.089659}, {0.78036, 0.89925, 0.087518}, {0.76881, 0.89508, 0.08551}, {0.75723, 0.89086, 0.083837}, {0.74562, 0.88662, 0.082243}, {0.73398, 0.88235, 0.080673}, {0.72230, 0.87807, 0.079194}, {0.71060, 0.87376, 0.077792}, {0.69886, 0.86946, 0.076415}, {0.68710, 0.86514, 0.075063}, {0.67529, 0.86082, 0.073757}, {0.66346, 0.85649, 0.072319}, {0.65156, 0.85215, 0.071005}, {0.63963, 0.84780, 0.069678}, {0.62764, 0.84345, 0.068313}, {0.61560, 0.83909, 0.066946}, {0.60351, 0.83473, 0.065602}, {0.59134, 0.83035, 0.064284}, {0.57911, 0.82597, 0.063016}, {0.56681, 0.82158, 0.061599}, {0.55443, 0.81718, 0.060374}, {0.54195, 0.81278, 0.059088}, {0.52940, 0.80837, 0.057695}, {0.51672, 0.80395, 0.056522}, {0.50395, 0.79952, 0.055189}, {0.49106, 0.79508, 0.053903}, {0.47801, 0.79064, 0.052644}, {0.46484, 0.78619, 0.051424}, {0.45151, 0.78173, 0.050257}, {0.43803, 0.77726, 0.04922}, {0.42437, 0.77277, 0.04812}, {0.41056, 0.76827, 0.047322}, {0.39656, 0.76375, 0.04674}, {0.38239, 0.75922, 0.046427}, {0.36808, 0.75466, 0.046596}, {0.35361, 0.75006, 0.047299}, {0.33906, 0.74543, 0.04874}, {0.32443, 0.74075, 0.050897}, {0.30984, 0.73602, 0.054069}, {0.29532, 0.73122, 0.058336}, {0.28105, 0.72634, 0.063783}, {0.26717, 0.72137, 0.070322}, {0.25387, 0.71631, 0.077992}, {0.24134, 0.71112, 0.08687}, {0.22981, 0.70580, 0.096608}, {0.21961, 0.70035, 0.10741}, {0.21092, 0.69475, 0.11899}, {0.20400, 0.68901, 0.13129}, {0.19894, 0.68310, 0.14422}, {0.19593, 0.67703, 0.15768}, {0.19486, 0.67081, 0.17161}, {0.19560, 0.66443, 0.18594}, {0.19795, 0.65791, 0.2005}, {0.20163, 0.65125, 0.21533}, {0.20639, 0.64446, 0.2303}, {0.21183, 0.63756, 0.24538}, {0.21771, 0.63057, 0.26052}, {0.22381, 0.62348, 0.27565}, {0.22992, 0.61632, 0.29071}, {0.23593, 0.60910, 0.30574}, {0.24162, 0.60182, 0.32064}, {0.24693, 0.59450, 0.33543}, {0.25184, 0.58716, 0.35008}, {0.25622, 0.57980, 0.36459}, {0.26011, 0.57242, 0.37897}, {0.26346, 0.56501, 0.3932}, {0.26624, 0.55762, 0.4073}, {0.26846, 0.55021, 0.42127}, {0.27013, 0.54280, 0.43513}, {0.27122, 0.53539, 0.44886}, {0.27173, 0.52798, 0.46247}, {0.27170, 0.52057, 0.476}, {0.27112, 0.51317, 0.48942}, {0.26997, 0.50576, 0.50275}, {0.26823, 0.49837, 0.516}, {0.26598, 0.49096, 0.52919}, {0.26316, 0.48352, 0.54228}, {0.25982, 0.47610, 0.55529}, {0.25594, 0.46864, 0.56822}, {0.25162, 0.46117, 0.58105}, {0.24680, 0.45365, 0.5938}, {0.24161, 0.44609, 0.60643}, {0.23605, 0.43849, 0.61894}, {0.23017, 0.43081, 0.63131}, {0.22410, 0.42303, 0.64353}, {0.21793, 0.41517, 0.6556}, {0.21170, 0.40718, 0.66749}, {0.20550, 0.39906, 0.67919}, {0.19945, 0.39079, 0.6907}, {0.19367, 0.38234, 0.70201}, {0.18818, 0.37372, 0.71312}, {0.18300, 0.36488, 0.72404}, {0.17829, 0.35585, 0.73477}, {0.17392, 0.34657, 0.7453}, {0.16999, 0.33707, 0.75566}, {0.16639, 0.32732, 0.76586}, {0.16312, 0.31735, 0.7759}, {0.16005, 0.30712, 0.78581}, {0.15724, 0.29667, 0.79557}, {0.15457, 0.28595, 0.80522}, {0.15202, 0.27505, 0.81474}, {0.14966, 0.26395, 0.82414}, {0.14744, 0.25264, 0.8334}, {0.14554, 0.24118, 0.84252}, {0.14402, 0.22960, 0.85145}, {0.14312, 0.21800, 0.86019}, {0.14305, 0.20639, 0.86869}, {0.14404, 0.19481, 0.87691}, {0.14630, 0.18336, 0.88484}, {0.15007, 0.17219, 0.89241}, {0.15537, 0.16140, 0.8996}, {0.16230, 0.15103, 0.90637}, {0.17075, 0.14136, 0.9127}, }; // viridis is the new default perceptually uniform colormap of the // "matplotlib" project (http://matplotlib.org/) // Direct link to the colormap is // https://github.com/BIDS/colormap/blob/master/colormaps.py // Viridis (and the other colormaps available at the link) are created by // Nathaniel J. Smith, Stefan van der Walt, and (in the case of viridis) Eric Firing. // The file (https://github.com/BIDS/colormap/blob/master/colormaps.py) // and the colormaps in it are released under the CC0 license / // public domain dedication. The authors would appreciate credit if you use or // redistribute these colormaps, but do not impose any legal restrictions. // See for a copy // of the CC0 license. static const double viridis[256][3] = { {0.267004, 0.004874, 0.329415}, {0.268510, 0.009605, 0.335427}, {0.269944, 0.014625, 0.341379}, {0.271305, 0.019942, 0.347269}, {0.272594, 0.025563, 0.353093}, {0.273809, 0.031497, 0.358853}, {0.274952, 0.037752, 0.364543}, {0.276022, 0.044167, 0.370164}, {0.277018, 0.050344, 0.375715}, {0.277941, 0.056324, 0.381191}, {0.278791, 0.062145, 0.386592}, {0.279566, 0.067836, 0.391917}, {0.280267, 0.073417, 0.397163}, {0.280894, 0.078907, 0.402329}, {0.281446, 0.084320, 0.407414}, {0.281924, 0.089666, 0.412415}, {0.282327, 0.094955, 0.417331}, {0.282656, 0.100196, 0.422160}, {0.282910, 0.105393, 0.426902}, {0.283091, 0.110553, 0.431554}, {0.283197, 0.115680, 0.436115}, {0.283229, 0.120777, 0.440584}, {0.283187, 0.125848, 0.444960}, {0.283072, 0.130895, 0.449241}, {0.282884, 0.135920, 0.453427}, {0.282623, 0.140926, 0.457517}, {0.282290, 0.145912, 0.461510}, {0.281887, 0.150881, 0.465405}, {0.281412, 0.155834, 0.469201}, {0.280868, 0.160771, 0.472899}, {0.280255, 0.165693, 0.476498}, {0.279574, 0.170599, 0.479997}, {0.278826, 0.175490, 0.483397}, {0.278012, 0.180367, 0.486697}, {0.277134, 0.185228, 0.489898}, {0.276194, 0.190074, 0.493001}, {0.275191, 0.194905, 0.496005}, {0.274128, 0.199721, 0.498911}, {0.273006, 0.204520, 0.501721}, {0.271828, 0.209303, 0.504434}, {0.270595, 0.214069, 0.507052}, {0.269308, 0.218818, 0.509577}, {0.267968, 0.223549, 0.512008}, {0.266580, 0.228262, 0.514349}, {0.265145, 0.232956, 0.516599}, {0.263663, 0.237631, 0.518762}, {0.262138, 0.242286, 0.520837}, {0.260571, 0.246922, 0.522828}, {0.258965, 0.251537, 0.524736}, {0.257322, 0.256130, 0.526563}, {0.255645, 0.260703, 0.528312}, {0.253935, 0.265254, 0.529983}, {0.252194, 0.269783, 0.531579}, {0.250425, 0.274290, 0.533103}, {0.248629, 0.278775, 0.534556}, {0.246811, 0.283237, 0.535941}, {0.244972, 0.287675, 0.537260}, {0.243113, 0.292092, 0.538516}, {0.241237, 0.296485, 0.539709}, {0.239346, 0.300855, 0.540844}, {0.237441, 0.305202, 0.541921}, {0.235526, 0.309527, 0.542944}, {0.233603, 0.313828, 0.543914}, {0.231674, 0.318106, 0.544834}, {0.229739, 0.322361, 0.545706}, {0.227802, 0.326594, 0.546532}, {0.225863, 0.330805, 0.547314}, {0.223925, 0.334994, 0.548053}, {0.221989, 0.339161, 0.548752}, {0.220057, 0.343307, 0.549413}, {0.218130, 0.347432, 0.550038}, {0.216210, 0.351535, 0.550627}, {0.214298, 0.355619, 0.551184}, {0.212395, 0.359683, 0.551710}, {0.210503, 0.363727, 0.552206}, {0.208623, 0.367752, 0.552675}, {0.206756, 0.371758, 0.553117}, {0.204903, 0.375746, 0.553533}, {0.203063, 0.379716, 0.553925}, {0.201239, 0.383670, 0.554294}, {0.199430, 0.387607, 0.554642}, {0.197636, 0.391528, 0.554969}, {0.195860, 0.395433, 0.555276}, {0.194100, 0.399323, 0.555565}, {0.192357, 0.403199, 0.555836}, {0.190631, 0.407061, 0.556089}, {0.188923, 0.410910, 0.556326}, {0.187231, 0.414746, 0.556547}, {0.185556, 0.418570, 0.556753}, {0.183898, 0.422383, 0.556944}, {0.182256, 0.426184, 0.557120}, {0.180629, 0.429975, 0.557282}, {0.179019, 0.433756, 0.557430}, {0.177423, 0.437527, 0.557565}, {0.175841, 0.441290, 0.557685}, {0.174274, 0.445044, 0.557792}, {0.172719, 0.448791, 0.557885}, {0.171176, 0.452530, 0.557965}, {0.169646, 0.456262, 0.558030}, {0.168126, 0.459988, 0.558082}, {0.166617, 0.463708, 0.558119}, {0.165117, 0.467423, 0.558141}, {0.163625, 0.471133, 0.558148}, {0.162142, 0.474838, 0.558140}, {0.160665, 0.478540, 0.558115}, {0.159194, 0.482237, 0.558073}, {0.157729, 0.485932, 0.558013}, {0.156270, 0.489624, 0.557936}, {0.154815, 0.493313, 0.557840}, {0.153364, 0.497000, 0.557724}, {0.151918, 0.500685, 0.557587}, {0.150476, 0.504369, 0.557430}, {0.149039, 0.508051, 0.557250}, {0.147607, 0.511733, 0.557049}, {0.146180, 0.515413, 0.556823}, {0.144759, 0.519093, 0.556572}, {0.143343, 0.522773, 0.556295}, {0.141935, 0.526453, 0.555991}, {0.140536, 0.530132, 0.555659}, {0.139147, 0.533812, 0.555298}, {0.137770, 0.537492, 0.554906}, {0.136408, 0.541173, 0.554483}, {0.135066, 0.544853, 0.554029}, {0.133743, 0.548535, 0.553541}, {0.132444, 0.552216, 0.553018}, {0.131172, 0.555899, 0.552459}, {0.129933, 0.559582, 0.551864}, {0.128729, 0.563265, 0.551229}, {0.127568, 0.566949, 0.550556}, {0.126453, 0.570633, 0.549841}, {0.125394, 0.574318, 0.549086}, {0.124395, 0.578002, 0.548287}, {0.123463, 0.581687, 0.547445}, {0.122606, 0.585371, 0.546557}, {0.121831, 0.589055, 0.545623}, {0.121148, 0.592739, 0.544641}, {0.120565, 0.596422, 0.543611}, {0.120092, 0.600104, 0.542530}, {0.119738, 0.603785, 0.541400}, {0.119512, 0.607464, 0.540218}, {0.119423, 0.611141, 0.538982}, {0.119483, 0.614817, 0.537692}, {0.119699, 0.618490, 0.536347}, {0.120081, 0.622161, 0.534946}, {0.120638, 0.625828, 0.533488}, {0.121380, 0.629492, 0.531973}, {0.122312, 0.633153, 0.530398}, {0.123444, 0.636809, 0.528763}, {0.124780, 0.640461, 0.527068}, {0.126326, 0.644107, 0.525311}, {0.128087, 0.647749, 0.523491}, {0.130067, 0.651384, 0.521608}, {0.132268, 0.655014, 0.519661}, {0.134692, 0.658636, 0.517649}, {0.137339, 0.662252, 0.515571}, {0.140210, 0.665859, 0.513427}, {0.143303, 0.669459, 0.511215}, {0.146616, 0.673050, 0.508936}, {0.150148, 0.676631, 0.506589}, {0.153894, 0.680203, 0.504172}, {0.157851, 0.683765, 0.501686}, {0.162016, 0.687316, 0.499129}, {0.166383, 0.690856, 0.496502}, {0.170948, 0.694384, 0.493803}, {0.175707, 0.697900, 0.491033}, {0.180653, 0.701402, 0.488189}, {0.185783, 0.704891, 0.485273}, {0.191090, 0.708366, 0.482284}, {0.196571, 0.711827, 0.479221}, {0.202219, 0.715272, 0.476084}, {0.208030, 0.718701, 0.472873}, {0.214000, 0.722114, 0.469588}, {0.220124, 0.725509, 0.466226}, {0.226397, 0.728888, 0.462789}, {0.232815, 0.732247, 0.459277}, {0.239374, 0.735588, 0.455688}, {0.246070, 0.738910, 0.452024}, {0.252899, 0.742211, 0.448284}, {0.259857, 0.745492, 0.444467}, {0.266941, 0.748751, 0.440573}, {0.274149, 0.751988, 0.436601}, {0.281477, 0.755203, 0.432552}, {0.288921, 0.758394, 0.428426}, {0.296479, 0.761561, 0.424223}, {0.304148, 0.764704, 0.419943}, {0.311925, 0.767822, 0.415586}, {0.319809, 0.770914, 0.411152}, {0.327796, 0.773980, 0.406640}, {0.335885, 0.777018, 0.402049}, {0.344074, 0.780029, 0.397381}, {0.352360, 0.783011, 0.392636}, {0.360741, 0.785964, 0.387814}, {0.369214, 0.788888, 0.382914}, {0.377779, 0.791781, 0.377939}, {0.386433, 0.794644, 0.372886}, {0.395174, 0.797475, 0.367757}, {0.404001, 0.800275, 0.362552}, {0.412913, 0.803041, 0.357269}, {0.421908, 0.805774, 0.351910}, {0.430983, 0.808473, 0.346476}, {0.440137, 0.811138, 0.340967}, {0.449368, 0.813768, 0.335384}, {0.458674, 0.816363, 0.329727}, {0.468053, 0.818921, 0.323998}, {0.477504, 0.821444, 0.318195}, {0.487026, 0.823929, 0.312321}, {0.496615, 0.826376, 0.306377}, {0.506271, 0.828786, 0.300362}, {0.515992, 0.831158, 0.294279}, {0.525776, 0.833491, 0.288127}, {0.535621, 0.835785, 0.281908}, {0.545524, 0.838039, 0.275626}, {0.555484, 0.840254, 0.269281}, {0.565498, 0.842430, 0.262877}, {0.575563, 0.844566, 0.256415}, {0.585678, 0.846661, 0.249897}, {0.595839, 0.848717, 0.243329}, {0.606045, 0.850733, 0.236712}, {0.616293, 0.852709, 0.230052}, {0.626579, 0.854645, 0.223353}, {0.636902, 0.856542, 0.216620}, {0.647257, 0.858400, 0.209861}, {0.657642, 0.860219, 0.203082}, {0.668054, 0.861999, 0.196293}, {0.678489, 0.863742, 0.189503}, {0.688944, 0.865448, 0.182725}, {0.699415, 0.867117, 0.175971}, {0.709898, 0.868751, 0.169257}, {0.720391, 0.870350, 0.162603}, {0.730889, 0.871916, 0.156029}, {0.741388, 0.873449, 0.149561}, {0.751884, 0.874951, 0.143228}, {0.762373, 0.876424, 0.137064}, {0.772852, 0.877868, 0.131109}, {0.783315, 0.879285, 0.125405}, {0.793760, 0.880678, 0.120005}, {0.804182, 0.882046, 0.114965}, {0.814576, 0.883393, 0.110347}, {0.824940, 0.884720, 0.106217}, {0.835270, 0.886029, 0.102646}, {0.845561, 0.887322, 0.099702}, {0.855810, 0.888601, 0.097452}, {0.866013, 0.889868, 0.095953}, {0.876168, 0.891125, 0.095250}, {0.886271, 0.892374, 0.095374}, {0.896320, 0.893616, 0.096335}, {0.906311, 0.894855, 0.098125}, {0.916242, 0.896091, 0.100717}, {0.926106, 0.897330, 0.104071}, {0.935904, 0.898570, 0.108131}, {0.945636, 0.899815, 0.112838}, {0.955300, 0.901065, 0.118128}, {0.964894, 0.902323, 0.123941}, {0.974417, 0.903590, 0.130215}, {0.983868, 0.904867, 0.136897}, {0.993248, 0.906157, 0.143936}, }; // The Turbo colormap is a replacement for jet, the old default colormap of // matlab. Turbo, like jet, is a rainbow colormap, but unlike jet it does not // have its banding problems, so it does not introduce false detail. // It is available from Google LLC under the Apache-2.0 license. // This particular look-up table was adapted from // https://gist.github.com/mikhailov-work/6a308c20e494d9e0ccc29036b28faa7a // Copyright 2019 Google LLC. // SPDX-License-Identifier: Apache-2.0 // Author: Anton Mikhailov // The look-up tables contains 256 entries. Each entry is a an sRGB triplet. static const double turbo[256][3] = {{0.18995,0.07176,0.23217},{0.19483,0.08339,0.26149},{0.19956,0.09498,0.29024},{0.20415,0.10652,0.31844},{0.20860,0.11802,0.34607},{0.21291,0.12947,0.37314},{0.21708,0.14087,0.39964},{0.22111,0.15223,0.42558},{0.22500,0.16354,0.45096},{0.22875,0.17481,0.47578},{0.23236,0.18603,0.50004},{0.23582,0.19720,0.52373},{0.23915,0.20833,0.54686},{0.24234,0.21941,0.56942},{0.24539,0.23044,0.59142},{0.24830,0.24143,0.61286},{0.25107,0.25237,0.63374},{0.25369,0.26327,0.65406},{0.25618,0.27412,0.67381},{0.25853,0.28492,0.69300},{0.26074,0.29568,0.71162},{0.26280,0.30639,0.72968},{0.26473,0.31706,0.74718},{0.26652,0.32768,0.76412},{0.26816,0.33825,0.78050},{0.26967,0.34878,0.79631},{0.27103,0.35926,0.81156},{0.27226,0.36970,0.82624},{0.27334,0.38008,0.84037},{0.27429,0.39043,0.85393},{0.27509,0.40072,0.86692},{0.27576,0.41097,0.87936},{0.27628,0.42118,0.89123},{0.27667,0.43134,0.90254},{0.27691,0.44145,0.91328},{0.27701,0.45152,0.92347},{0.27698,0.46153,0.93309},{0.27680,0.47151,0.94214},{0.27648,0.48144,0.95064},{0.27603,0.49132,0.95857},{0.27543,0.50115,0.96594},{0.27469,0.51094,0.97275},{0.27381,0.52069,0.97899},{0.27273,0.53040,0.98461},{0.27106,0.54015,0.98930},{0.26878,0.54995,0.99303},{0.26592,0.55979,0.99583},{0.26252,0.56967,0.99773},{0.25862,0.57958,0.99876},{0.25425,0.58950,0.99896},{0.24946,0.59943,0.99835},{0.24427,0.60937,0.99697},{0.23874,0.61931,0.99485},{0.23288,0.62923,0.99202},{0.22676,0.63913,0.98851},{0.22039,0.64901,0.98436},{0.21382,0.65886,0.97959},{0.20708,0.66866,0.97423},{0.20021,0.67842,0.96833},{0.19326,0.68812,0.96190},{0.18625,0.69775,0.95498},{0.17923,0.70732,0.94761},{0.17223,0.71680,0.93981},{0.16529,0.72620,0.93161},{0.15844,0.73551,0.92305},{0.15173,0.74472,0.91416},{0.14519,0.75381,0.90496},{0.13886,0.76279,0.89550},{0.13278,0.77165,0.88580},{0.12698,0.78037,0.87590},{0.12151,0.78896,0.86581},{0.11639,0.79740,0.85559},{0.11167,0.80569,0.84525},{0.10738,0.81381,0.83484},{0.10357,0.82177,0.82437},{0.10026,0.82955,0.81389},{0.09750,0.83714,0.80342},{0.09532,0.84455,0.79299},{0.09377,0.85175,0.78264},{0.09287,0.85875,0.77240},{0.09267,0.86554,0.76230},{0.09320,0.87211,0.75237},{0.09451,0.87844,0.74265},{0.09662,0.88454,0.73316},{0.09958,0.89040,0.72393},{0.10342,0.89600,0.71500},{0.10815,0.90142,0.70599},{0.11374,0.90673,0.69651},{0.12014,0.91193,0.68660},{0.12733,0.91701,0.67627},{0.13526,0.92197,0.66556},{0.14391,0.92680,0.65448},{0.15323,0.93151,0.64308},{0.16319,0.93609,0.63137},{0.17377,0.94053,0.61938},{0.18491,0.94484,0.60713},{0.19659,0.94901,0.59466},{0.20877,0.95304,0.58199},{0.22142,0.95692,0.56914},{0.23449,0.96065,0.55614},{0.24797,0.96423,0.54303},{0.26180,0.96765,0.52981},{0.27597,0.97092,0.51653},{0.29042,0.97403,0.50321},{0.30513,0.97697,0.48987},{0.32006,0.97974,0.47654},{0.33517,0.98234,0.46325},{0.35043,0.98477,0.45002},{0.36581,0.98702,0.43688},{0.38127,0.98909,0.42386},{0.39678,0.99098,0.41098},{0.41229,0.99268,0.39826},{0.42778,0.99419,0.38575},{0.44321,0.99551,0.37345},{0.45854,0.99663,0.36140},{0.47375,0.99755,0.34963},{0.48879,0.99828,0.33816},{0.50362,0.99879,0.32701},{0.51822,0.99910,0.31622},{0.53255,0.99919,0.30581},{0.54658,0.99907,0.29581},{0.56026,0.99873,0.28623},{0.57357,0.99817,0.27712},{0.58646,0.99739,0.26849},{0.59891,0.99638,0.26038},{0.61088,0.99514,0.25280},{0.62233,0.99366,0.24579},{0.63323,0.99195,0.23937},{0.64362,0.98999,0.23356},{0.65394,0.98775,0.22835},{0.66428,0.98524,0.22370},{0.67462,0.98246,0.21960},{0.68494,0.97941,0.21602},{0.69525,0.97610,0.21294},{0.70553,0.97255,0.21032},{0.71577,0.96875,0.20815},{0.72596,0.96470,0.20640},{0.73610,0.96043,0.20504},{0.74617,0.95593,0.20406},{0.75617,0.95121,0.20343},{0.76608,0.94627,0.20311},{0.77591,0.94113,0.20310},{0.78563,0.93579,0.20336},{0.79524,0.93025,0.20386},{0.80473,0.92452,0.20459},{0.81410,0.91861,0.20552},{0.82333,0.91253,0.20663},{0.83241,0.90627,0.20788},{0.84133,0.89986,0.20926},{0.85010,0.89328,0.21074},{0.85868,0.88655,0.21230},{0.86709,0.87968,0.21391},{0.87530,0.87267,0.21555},{0.88331,0.86553,0.21719},{0.89112,0.85826,0.21880},{0.89870,0.85087,0.22038},{0.90605,0.84337,0.22188},{0.91317,0.83576,0.22328},{0.92004,0.82806,0.22456},{0.92666,0.82025,0.22570},{0.93301,0.81236,0.22667},{0.93909,0.80439,0.22744},{0.94489,0.79634,0.22800},{0.95039,0.78823,0.22831},{0.95560,0.78005,0.22836},{0.96049,0.77181,0.22811},{0.96507,0.76352,0.22754},{0.96931,0.75519,0.22663},{0.97323,0.74682,0.22536},{0.97679,0.73842,0.22369},{0.98000,0.73000,0.22161},{0.98289,0.72140,0.21918},{0.98549,0.71250,0.21650},{0.98781,0.70330,0.21358},{0.98986,0.69382,0.21043},{0.99163,0.68408,0.20706},{0.99314,0.67408,0.20348},{0.99438,0.66386,0.19971},{0.99535,0.65341,0.19577},{0.99607,0.64277,0.19165},{0.99654,0.63193,0.18738},{0.99675,0.62093,0.18297},{0.99672,0.60977,0.17842},{0.99644,0.59846,0.17376},{0.99593,0.58703,0.16899},{0.99517,0.57549,0.16412},{0.99419,0.56386,0.15918},{0.99297,0.55214,0.15417},{0.99153,0.54036,0.14910},{0.98987,0.52854,0.14398},{0.98799,0.51667,0.13883},{0.98590,0.50479,0.13367},{0.98360,0.49291,0.12849},{0.98108,0.48104,0.12332},{0.97837,0.46920,0.11817},{0.97545,0.45740,0.11305},{0.97234,0.44565,0.10797},{0.96904,0.43399,0.10294},{0.96555,0.42241,0.09798},{0.96187,0.41093,0.09310},{0.95801,0.39958,0.08831},{0.95398,0.38836,0.08362},{0.94977,0.37729,0.07905},{0.94538,0.36638,0.07461},{0.94084,0.35566,0.07031},{0.93612,0.34513,0.06616},{0.93125,0.33482,0.06218},{0.92623,0.32473,0.05837},{0.92105,0.31489,0.05475},{0.91572,0.30530,0.05134},{0.91024,0.29599,0.04814},{0.90463,0.28696,0.04516},{0.89888,0.27824,0.04243},{0.89298,0.26981,0.03993},{0.88691,0.26152,0.03753},{0.88066,0.25334,0.03521},{0.87422,0.24526,0.03297},{0.86760,0.23730,0.03082},{0.86079,0.22945,0.02875},{0.85380,0.22170,0.02677},{0.84662,0.21407,0.02487},{0.83926,0.20654,0.02305},{0.83172,0.19912,0.02131},{0.82399,0.19182,0.01966},{0.81608,0.18462,0.01809},{0.80799,0.17753,0.01660},{0.79971,0.17055,0.01520},{0.79125,0.16368,0.01387},{0.78260,0.15693,0.01264},{0.77377,0.15028,0.01148},{0.76476,0.14374,0.01041},{0.75556,0.13731,0.00942},{0.74617,0.13098,0.00851},{0.73661,0.12477,0.00769},{0.72686,0.11867,0.00695},{0.71692,0.11268,0.00629},{0.70680,0.10680,0.00571},{0.69650,0.10102,0.00522},{0.68602,0.09536,0.00481},{0.67535,0.08980,0.00449},{0.66449,0.08436,0.00424},{0.65345,0.07902,0.00408},{0.64223,0.07380,0.00401},{0.63082,0.06868,0.00401},{0.61923,0.06367,0.00410},{0.60746,0.05878,0.00427},{0.59550,0.05399,0.00453},{0.58336,0.04931,0.00486},{0.57103,0.04474,0.00529},{0.55852,0.04028,0.00579},{0.54583,0.03593,0.00638},{0.53295,0.03169,0.00705},{0.51989,0.02756,0.00780},{0.50664,0.02354,0.00863},{0.49321,0.01963,0.00955},{0.47960,0.01583,0.01055}}; view-0.3.00/src/draw.c000066400000000000000000000327311454754234200144540ustar00rootroot00000000000000/* Copyright 2015-2016. Martin Uecker. * All rights reserved. Use of this source code is governed by * a BSD-style license which can be found in the LICENSE file. * * Author: * 2015-2016 Martin Uecker */ #include #include #include #include #include "misc/misc.h" #include "geom/draw.h" #include "draw.h" #include "colormaps.inc" // multind.h #define MD_BIT(x) (1u << (x)) #define MD_IS_SET(x, y) ((x) & MD_BIT(y)) #define MD_CLEAR(x, y) ((x) & ~MD_BIT(y)) #define MD_SET(x, y) ((x) | MD_BIT(y)) static double clamp(double a, double b, double x) { return (x < a) ? a : ((x > b) ? b : x); } static double window(double a, double b, double x) { if (a == b) return (0. == x) ? 0. : 1.; return clamp(0., 1., (x - a) / (b - a)); } static void trans_magnitude(double rgb[3], double a, double b, complex double value) { double magn = window(a, b, cabs(value)); rgb[0] *= magn; rgb[1] *= magn; rgb[2] *= magn; } static void interpolate_cmap(double rgb[3], double x, const double cmap[256][3]) { int a = x * 255; int b = MIN(255, a + 1); double f = x * 255. - a; for (int i = 0; i < 3; ++i) rgb[i] *= cmap[a][i] + f * (cmap[b][i] - cmap[a][i]); } static void trans_magnitude_viridis(double rgb[3], double a, double b, complex double value) { double magn = window(a, b, cabs(value)); interpolate_cmap(rgb, magn, viridis); } static void trans_magnitude_turbo(double rgb[3], double a, double b, complex double value) { double magn = window(a, b, cabs(value)); interpolate_cmap(rgb, magn, turbo); } static void trans_real(double rgb[3], double a, double b, complex double value) { rgb[0] *= window(a, b, +creal(value)); rgb[1] *= window(a, b, -creal(value)); rgb[2] *= 0.; } static void trans_phase(double rgb[3], double a, double b, complex double value) { UNUSED(a); UNUSED(b); rgb[0] *= (1. + sin(carg(value) + 0. * 2. * M_PI / 3.)) / 2.; rgb[1] *= (1. + sin(carg(value) + 1. * 2. * M_PI / 3.)) / 2.; rgb[2] *= (1. + sin(carg(value) + 2. * 2. * M_PI / 3.)) / 2.; } static void trans_phase_MYGBM(double rgb[3], double a, double b, complex double value) { UNUSED(a); UNUSED(b); double arg = carg(value); if (isfinite(arg)) { double val = (arg + M_PI) / 2. / M_PI; assert((0.0 <= val) && (val <= 1.0)); interpolate_cmap(rgb, val, cyclic_mygbm); } } static void trans_complex(double rgb[3], double a, double b, complex double value) { trans_magnitude(rgb, a, b, value); trans_phase(rgb, a, b, value); } static void trans_complex_MYGBM(double rgb[3], double a, double b, complex double value) { trans_magnitude(rgb, a, b, value); trans_phase_MYGBM(rgb, a, b, value); } static void trans_flow(double rgb[3], double a, double b, complex double value) { trans_magnitude(rgb, a, b, value); double pha = clamp(-1., 1., carg(value) / M_PI); rgb[0] *= (1. + pha) / 2.; rgb[1] *= (1. - fabs(pha)) / 2.; rgb[2] *= (1. - pha) / 2.; } // we could move in the multiplication with the factor // as an extra argument which could save time static complex float int_nlinear(int N, const float x[N], const long strs[N], const complex float* in) { return (0 == N) ? in[0] : ( (1. - x[N - 1]) * int_nlinear(N - 1, x, strs, in + 0) + x[N - 1] * int_nlinear(N - 1, x, strs, in + strs[N - 1])); } static complex float int_nlinearmag(int N, const float x[N], const long strs[N], const complex float* in) { return (0 == N) ? cabsf(in[0]) : ( (1. - x[N - 1]) * int_nlinearmag(N - 1, x, strs, in + 0) + x[N - 1] * int_nlinearmag(N - 1, x, strs, in + strs[N - 1])); } static complex float int_nearest(int N, const float x[N], const long strs[N], const complex float* in) { size_t offs = 0; for (int i = 0; i < N; ++i) offs += round(x[i]) * strs[i]; return *(in + offs); } static complex float lic_sample(int N, const float pos[N], const long dims[N], const long strs[N], const complex float* in); complex float sample(int N, const float pos[N], const long dims[N], const long strs[N], enum interp_t interpolation, const complex float* in) { if (LIINCO == interpolation) return lic_sample(N, pos, dims, strs, in); float rem[N]; int div[N]; int D = 0; long strs2[N]; // 0 1. [0 1] dims 2 for (int i = 0; i < N; i++) { div[i] = 0; rem[i] = 0.; if (dims[i] > 1) { float xrem = 0.; // values outside of valid range set to the edge values if (pos[i] < 0.) { div[i] = 0.; } else if (pos[i] > (dims[i] - 1)) { div[i] = dims[i] - 1; } else { div[i] = truncf(pos[i]); xrem = pos[i] - truncf(pos[i]); } if (xrem != 0.) { strs2[D] = strs[i] / sizeof(complex float); rem[D++] = xrem; } if ((div[i] < 0) || (div[i] >= dims[i]) || ((div[i] >= dims[i] - 1) && (xrem > 0.))) return 0.; } } long off0 = 0; for (int i = 0; i < N; i++) off0 += div[i] * strs[i]; switch (interpolation) { case NLINEAR: return int_nlinear(D, rem, strs2, (const complex float*)(((char*)in) + off0)); case NLINEARMAG: return int_nlinearmag(D, rem, strs2, (const complex float*)(((char*)in) + off0)); case NEAREST: return int_nearest(D, rem, strs2, (const complex float*)(((char*)in) + off0)); default: assert(0); } } static complex float lic_hash(int p0, int p1) { p0 += 12345; p0 *= 2654435761U; p0 += p1; p0 += 12345; p0 *= 2654435761U; p0 ^= p0 >> 13; p0 *= 2654435761U; p0 ^= p0 >> 17; p0 *= 2654435761U; return (1. * (p0 % 256) + 1.i * ((p0 / 256) % 256)) / 256.; } // line integral convolution complex float lic_sample(int N, const float pos[N], const long dims[N], const long strs[N], const complex float* in) { int L = 9; float os = 3.; assert(N >= 2); assert(1 < dims[0]); assert(1 < dims[1]); complex float out = 0.; float pos1[N]; for (int i = 0; i < N; i++) pos1[i] = pos[i]; complex float val = sample(N, pos1, dims, strs, NLINEAR, in); for (int i = 0; i < L; i++) { complex float a = sample(N, pos1, dims, strs, NLINEAR, in); a /= cabsf(a); pos1[0] += crealf(a) / os; pos1[1] += cimagf(a) / os; int p0 = (int)(os * pos1[0]); int p1 = (int)(os * pos1[1]); out += lic_hash(p0, p1); } for (int i = 0; i < N; i++) pos1[i] = pos[i]; for (int i = 0; i < L; i++) { complex float a = sample(N, pos1, dims, strs, NLINEAR, in); a /= cabsf(a); pos1[0] -= crealf(a) / os; pos1[1] -= cimagf(a) / os; int p0 = (int)(os * pos1[0]); int p1 = (int)(os * pos1[1]); out += lic_hash(p0, p1); } return out * cabsf(val); } /* The idea is the following: * samples sit in the middle of their pixels, so for even zoom factors, * the original values are between adjacent pixels, with pixels outside * of the valid range (negative pos2 and pos2 greater than dim-1) set to the * corresponding values. Therefore we need to start the pos2 array at negative * positions. **/ extern void resample(int X, int Y, long str, complex float* buf, int N, const double pos[N], const double dx[N], const double dy[N], const long dims[N], const long strs[N], enum interp_t interpolation, const complex float* in) { #pragma omp parallel for collapse(2) for (int x = 0; x < X; x++) { for (int y = 0; y < Y; y++) { float pos2[N]; for (int i = 0; i < N; i++) { /* start is only != 0 if dx or dy are != 0. * Further, for negative dx/dy, it needs the same sign. * ....0.......1.... d (|d| - 1.) / 2. * |---*---|---*---| 1.00 -> -0.000 * |-*-|-*-|-*-|-*-| 0.50 -> -0.250 * |*|*|*|*|*|*|*|*| 0.25 -> -0.375 **/ double start = - (dx[i] != 0.) * copysign((fabs(dx[i]) - 1.) / 2., dx[i]) - (dy[i] != 0.) * copysign((fabs(dy[i]) - 1.) / 2., dy[i]); pos2[i] = pos[i] + start + x * dx[i] + y * dy[i]; } buf[str * y + x] = sample(N, pos2, dims, strs, interpolation, in); } } } extern void draw(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], enum mode_t mode, float scale, float winlow, float winhigh, float phrot, long str, const complex float* buf) { #pragma omp parallel for collapse(2) for (int x = 0; x < X; x++) { for (int y = 0; y < Y; y++) { double rgb[3] = { 1., 1., 1. }; complex float val = scale * buf[str * y + x]; if (isfinite(crealf(val)) && isfinite(cimagf(val))) { val *= cexpf(1.i * phrot); switch (mode) { case MAGN: trans_magnitude(rgb, winlow, winhigh, val); break; case MAGN_VIRIDS: trans_magnitude_viridis(rgb, winlow, winhigh, val); break; case PHSE: trans_phase(rgb, winlow, winhigh, val); break; case PHSE_MYGBM: trans_phase_MYGBM(rgb, winlow, winhigh, val); break; case CMPL: trans_complex(rgb, winlow, winhigh, val); break; case CMPL_MYGBM: trans_complex_MYGBM(rgb, winlow, winhigh, val); break; case REAL: trans_real(rgb, winlow, winhigh, val); break; case MAGN_TURBO: trans_magnitude_turbo(rgb, winlow, winhigh, val); break; case FLOW: trans_flow(rgb, winlow, winhigh, val); break; default: assert(0); } } else { rgb[0] = 0.; rgb[1] = 0.; rgb[2] = 0.; } (*rgbbuf)[y][x][0] = 255. * rgb[2]; (*rgbbuf)[y][x][1] = 255. * rgb[1]; (*rgbbuf)[y][x][2] = 255. * rgb[0]; (*rgbbuf)[y][x][3] = 255.; } } } void update_buf(long xdim, long ydim, int N, const long dims[N], const long strs[N], const long pos[N], enum flip_t flip, enum interp_t interpolation, double xzoom, double yzoom, bool plot, long rgbw, long rgbh, const complex float* data, complex float* buf) { if (plot) rgbh = 1; double dpos[N]; for (int i = 0; i < N; i++) dpos[i] = pos[i]; dpos[xdim] = 0.; if (!plot) dpos[ydim] = 0.; double dx[N]; for (int i = 0; i < N; i++) dx[i] = 0.; double dy[N]; for (int i = 0; i < N; i++) dy[i] = 0.; dx[xdim] = 1.; if (!plot) dy[ydim] = 1.; if ((XY == flip) || (XO == flip)) { dpos[xdim] = dims[xdim] - 1; dx[xdim] *= -1.; } if ((XY == flip) || (OY == flip)) { if (!plot) { dpos[ydim] = dims[ydim] - 1; dy[ydim] *= -1.; } } assert(xdim < N); assert(ydim < N); dx[xdim] = dx[xdim] / xzoom; dy[ydim] = dy[ydim] / yzoom; resample(rgbw, rgbh, rgbw, buf, N, dpos, dx, dy, dims, strs, interpolation, data); } const char color_white[3] = { 255, 255, 255 }; const char color_blue[3] = { 255, 0, 0 }; const char color_red[3] = { 0, 0, 255 }; extern void draw_line(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], float x0, float y0, float x1, float y1, const char (*color)[3]) { unsigned char color2[4] = { (*color)[0], (*color)[1], (*color)[2], 1 }; bresenham_rgba(Y, X, rgbbuf, &color2, y0, x0, y1, x1); } extern void draw_grid(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], const float (*coord)[4][2], int divs, const char (*color)[3]) { for (int i = 0; i <= divs; i++) { for (int j = 0; j <= divs; j++) { float x0 = (*coord)[0][0] + i * ((*coord)[1][0] - (*coord)[0][0]) / divs; float y0 = (*coord)[0][1] + i * ((*coord)[1][1] - (*coord)[0][1]) / divs; float x1 = (*coord)[2][0] + i * ((*coord)[3][0] - (*coord)[2][0]) / divs; float y1 = (*coord)[2][1] + i * ((*coord)[3][1] - (*coord)[2][1]) / divs; draw_line(X, Y, rgbstr, rgbbuf, x0, y0, x1, y1, color); x0 = (*coord)[0][0] + i * ((*coord)[2][0] - (*coord)[0][0]) / divs; y0 = (*coord)[0][1] + i * ((*coord)[2][1] - (*coord)[0][1]) / divs; x1 = (*coord)[1][0] + i * ((*coord)[3][0] - (*coord)[1][0]) / divs; y1 = (*coord)[1][1] + i * ((*coord)[3][1] - (*coord)[1][1]) / divs; draw_line(X, Y, rgbstr, rgbbuf, x0, y0, x1, y1, color); } } } extern void draw_plot(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], enum mode_t mode, float scale, float winlow, float winhigh, float phrot, long str, const complex float* buf) { unsigned char bg[4] = { 255, 255, 255, 0 }; unsigned char half[4] = { 163, 163, 163, 255 }; assert(X == rgbstr / 4); assert(X == str); for (int i = 0; i < X; i++) for (int j = 0; j < Y; j++) for (int c = 0; c < 4; c++) (*rgbbuf)[j][i][c] = bg[c]; for (int i = 1; i < 10; i++) { bresenham_rgba(Y, X, rgbbuf, &half, 0, i * (X / 10), Y - 1, i * (X / 10)); bresenham_rgba(Y, X, rgbbuf, &half, i * (Y / 10), 0, i * (Y / 10), X - 1); } unsigned char colorr[4] = { 0, 0, 0, 255 }; unsigned char colori[4] = { 255, 255, 0, 255 }; for (int x = 0; x < X - 1; x++) { NESTED(double, trafo, (complex float val)) { complex float v2 = scale * val * cexpf(1.i * phrot); return window(winlow, winhigh, crealf(v2)) - window(winlow, winhigh, -crealf(v2)); }; float rx0 = (float)(x + 0); float ry0 = Y / 2 * (1. - trafo(buf[x + 0])); float rx1 = (float)(x + 1); float ry1 = Y / 2 * (1. - trafo(buf[x + 1])); xiaolin_wu_rgba(Y, X, rgbbuf, &colorr, ry0, rx0, ry1, rx1); float ix0 = (float)(x + 0); float iy0 = Y / 2 * (1. - trafo(1.i * buf[x + 0])); float ix1 = (float)(x + 1); float iy1 = Y / 2 * (1. - trafo(1.i * buf[x + 1])); xiaolin_wu_rgba(Y, X, rgbbuf, &colori, iy0, ix0, iy1, ix1); } } static const char* spec = "xyzcmnopqsfrtuvw"; char* construct_filename_view(unsigned int D, const long loopdims[D], const long pos[D], const char* prefix, const char* ext) { // Prepare output filename int len = 0; len += snprintf(NULL, 0, "%s", prefix); for (unsigned int i = 0; i < D; i++) if (1 != loopdims[i]) len += snprintf(NULL, 0, "_%c%04ld", spec[i], pos[i]); len += snprintf(NULL, 0, ".%s", ext); len++; char* name = xmalloc(len); int off = 0; off += snprintf(name + off, len - off, "%s", prefix); for (unsigned int i = 0; i < D; i++) if (1 != loopdims[i]) off += snprintf(name + off, len - off, "_%c%04ld", spec[i], pos[i]); off += snprintf(name + off, len - off, ".%s", ext); return name; } view-0.3.00/src/draw.h000066400000000000000000000034511454754234200144560ustar00rootroot00000000000000 #include #include enum mode_t { MAGN, MAGN_VIRIDS, CMPL, CMPL_MYGBM, PHSE, PHSE_MYGBM, REAL, MAGN_TURBO, FLOW }; enum flip_t { OO, XO, OY, XY }; enum interp_t { NLINEAR, NLINEARMAG, NEAREST, LIINCO }; extern complex float sample(int N, const float pos[N], const long dims[N], const long strs[N], enum interp_t interpolation, const complex float* in); extern void resample(int X, int Y, long str, complex float* buf, int N, const double pos[N], const double dx[N], const double dy[N], const long dims[N], const long strs[N], enum interp_t interpolation, const complex float* in); extern void draw(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], enum mode_t mode, float scale, float winlow, float winhigh, float phrot, long str, const complex float* buf); extern void draw_plot(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], enum mode_t mode, float scale, float winlow, float winhigh, float phrot, long str, const complex float* buf); extern void update_buf(long xdim, long ydim, int N, const long dims[N], const long strs[N], const long pos[N], enum flip_t flip, enum interp_t interpolation, double xzoom, double yzoom, bool plot, long rgbw, long rgbh, const complex float* data, complex float* buf); extern void draw_line(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], float x0, float y0, float x1, float y1, const char (*color)[3]); extern void draw_grid(int X, int Y, int rgbstr, unsigned char (*rgbbuf)[Y][rgbstr / 4][4], const float (*coord)[4][2], int divs, const char (*color)[3]); extern const char color_white[3]; extern const char color_red[3]; extern const char color_blue[3]; extern char* construct_filename_view(unsigned int D, const long loopdims[D], const long pos[D], const char* prefix, const char* ext); view-0.3.00/src/main.c000066400000000000000000000037371454754234200144470ustar00rootroot00000000000000/* Copyright 2015-2023. Martin Uecker. * All rights reserved. Use of this source code is governed by * a BSD-style license which can be found in the LICENSE file. */ #include #include #include #undef MAX #undef MIN #include "misc/misc.h" #include "misc/mmio.h" #include "misc/opts.h" #if 0 #include "misc/io.h" #else extern void io_reserve_input(const char* name); extern void io_unregister(const char* name); #endif #include "view.h" static const char help_str[] = "View images."; int main(int argc, char* argv[argc]) { gtk_disable_setlocale(); gtk_init(&argc, &argv); long count; const char** in_files; struct arg_s args[] = { ARG_TUPLE(true, &count, 1, { OPT_INFILE, sizeof(char*), &in_files, "image" }), }; bool absolute_windowing = false; const struct opt_s opts[] = { OPT_SET('a', &absolute_windowing, "Use absolute windowing"), }; cmdline(&argc, argv, ARRAY_SIZE(args), args, help_str, ARRAY_SIZE(opts), opts); struct view_s* v = NULL; for (int i = 0; i < count; i++) { long dims[DIMS]; /* * If the filename ends in ".hdr", ".cfl" or just "." (from * tab-completion), just replace the "." with a \0-character. * * Ignoring '.hdr' and '.cfl' is useful since now this viewer * can be set as the default program to open these files from * a file manager. */ io_unregister(in_files[i]); char* dot = strrchr(in_files[i], '.'); if ((NULL != dot) && ( !strcmp(dot, ".cfl") || !strcmp(dot, ".hdr") || !strcmp(dot, "."))) *dot = '\0'; io_reserve_input(in_files[i]); complex float* x = load_cfl(in_files[i], DIMS, dims); // FIXME: we never delete them struct view_s* v2 = window_new(in_files[i], NULL, dims, x, absolute_windowing); // If multiple files are passed on the commandline, add them to window // list. This enables sync of windowing and so on... if (NULL != v) { window_connect_sync(v, v2); } else { v = v2; } } gtk_main(); return 0; } view-0.3.00/src/view.c000066400000000000000000000633201454754234200144670ustar00rootroot00000000000000/* Copyright 2015-2016. Martin Uecker. * All rights reserved. Use of this source code is governed by * a BSD-style license which can be found in the LICENSE file. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #undef MAX #undef MIN #include "num/multind.h" #include "misc/misc.h" #include "misc/png.h" #include "misc/debug.h" #include "draw.h" #include "view.h" #ifndef DIMS #define DIMS 16 #endif #define STRINGIFY(x) # x const char* viewer_gui = #include "viewer.inc" ; struct view_s { struct view_s* next; struct view_s* prev; bool sync; bool cross_hair; bool status_bar; const char* name; // geometry long* pos; //[DIMS]; int xdim; int ydim; double xzoom; double yzoom; enum flip_t flip; bool transpose; // representation bool plot; bool absolute_windowing; enum mode_t mode; double winhigh; double winlow; double phrot; double max; enum interp_t interpolation; complex float* buf; cairo_surface_t* source; // rgb buffer int rgbh; int rgbw; int rgbstr; unsigned char* rgb; bool invalid; bool rgb_invalid; // data long dims[DIMS]; long strs[DIMS]; const complex float* data; // geometry unsigned long geom_flags; const float (*geom)[3][3]; const float (*geom_current)[3][3]; // widgets GtkComboBox* gtk_mode; GtkComboBox* gtk_flip; GtkComboBox* gtk_interp; GtkWidget* gtk_drawingarea; GtkWidget* gtk_viewport; GtkAdjustment* gtk_winlow; GtkAdjustment* gtk_winhigh; GtkSpinButton* gtk_button_winlow; GtkSpinButton* gtk_button_winhigh; GtkToolItem* toolbar_scale1; GtkToolItem* toolbar_scale2; GtkToolItem* toolbar_button1; GtkToolItem* toolbar_button2; GtkAdjustment* gtk_zoom; GtkAdjustment* gtk_aniso; GtkEntry* gtk_entry; GtkToggleToolButton* gtk_transpose; GtkToggleToolButton* gtk_fit; GtkToggleToolButton* gtk_sync; GtkToggleToolButton* gtk_absolutewindowing; GtkAdjustment* gtk_posall[DIMS]; GtkCheckButton* gtk_checkall[DIMS]; GtkWidget *dialog; // Save dialog GtkFileChooser *chooser; // Save dialog GtkWindow *window; // windowing int lastx; int lasty; }; #if 0 static void add_text(cairo_surface_t* surface, int x, int y, int size, const char* text) { cairo_t* cr = cairo_create(surface); cairo_set_source_rgb(cr, 1., 1., 1.); PangoLayout* layout = pango_cairo_create_layout(cr); pango_layout_set_text(layout, text, -1); PangoFontDescription* desc = pango_font_description_new(); pango_font_description_set_family(desc, "sans"); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); pango_font_description_set_absolute_size(desc, size * PANGO_SCALE); pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); int w = 0; int h = 0; pango_layout_get_pixel_size(layout, &w, &h); cairo_move_to(cr, (x >= 0) ? x : -(x + (double)w), (y >= 0) ? y : -(y + (double)h)); pango_cairo_show_layout(cr, layout); g_object_unref(layout); cairo_destroy(cr); } #endif extern void update_geom(struct view_s* v) { if (NULL == v->geom) return; long dims[DIMS]; md_select_dims(DIMS, v->geom_flags, dims, v->dims); long strs[DIMS]; md_calc_strides(DIMS, strs, dims, 1); v->geom_current = &v->geom[md_calc_offset(DIMS, strs, v->pos)]; printf("%f %f %f %f %f %f %f %f %f\n", (*v->geom_current)[0][0], (*v->geom_current)[0][1], (*v->geom_current)[0][2], (*v->geom_current)[1][0], (*v->geom_current)[1][1], (*v->geom_current)[1][2], (*v->geom_current)[2][0], (*v->geom_current)[2][1], (*v->geom_current)[2][2]); } extern gboolean update_view(struct view_s* v) { if (NULL != v->source) cairo_surface_destroy(v->source); v->rgbw = v->dims[v->xdim] * v->xzoom; v->rgbh = v->dims[v->ydim] * v->yzoom; v->rgbstr = 4 * v->rgbw; v->rgb = realloc(v->rgb, v->rgbh * v->rgbstr); v->buf = realloc(v->buf, v->rgbh * v->rgbw * sizeof(complex float)); v->source = cairo_image_surface_create_for_data(v->rgb, CAIRO_FORMAT_RGB24, v->rgbw, v->rgbh, v->rgbstr); // trigger redraw gtk_widget_set_size_request(v->gtk_drawingarea, v->rgbw, v->rgbh); gtk_widget_queue_draw(v->gtk_drawingarea); return FALSE; } extern gboolean fit_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; gboolean flag = gtk_toggle_tool_button_get_active(v->gtk_fit); if (!flag) return FALSE; double aniso = gtk_adjustment_get_value(v->gtk_aniso); GtkAllocation alloc; gtk_widget_get_allocation(v->gtk_viewport, &alloc); double xz = (double)(alloc.width - 5) / (double)v->dims[v->xdim]; double yz = (double)(alloc.height - 5) / (double)v->dims[v->ydim]; if (yz > xz / aniso) yz = xz / aniso; // aniso gtk_adjustment_set_value(v->gtk_zoom, yz); return FALSE; } extern gboolean configure_callback(GtkWidget *widget, GdkEvent* event, gpointer data) { UNUSED(event); return fit_callback(widget, data); } extern void view_setpos(struct view_s* v, unsigned int flags, const long pos[DIMS]) { for (unsigned int i = 0; i < DIMS; i++) { if (MD_IS_SET(flags, i)) { gtk_adjustment_set_value(v->gtk_posall[i], pos[i]); for (struct view_s* v2 = v->next; v2 != v; v2 = v2->next) if (v->sync && v2->sync) gtk_adjustment_set_value(v2->gtk_posall[i], pos[i]); } } } extern void view_refresh(struct view_s* v) { if (v->absolute_windowing) { long idims[DIMS]; md_select_dims(DIMS, MD_BIT(v->xdim) | MD_BIT(v->ydim), idims, v->dims); complex float* tmp = md_alloc(DIMS, idims, sizeof(complex float)); long pos[DIMS]; md_copy_dims(DIMS, pos, v->pos); pos[v->xdim] = 0; pos[v->ydim] = 0; md_slice(DIMS, ~(MD_BIT(v->xdim) | MD_BIT(v->ydim)), pos, v->dims, tmp, v->data, sizeof(complex float)); long size = md_calc_size(DIMS, idims); double max = 0.; for (long j = 0; j < size; j++) if (max < cabsf(tmp[j])) max = cabsf(tmp[j]); max = MIN(1.e10, max); md_free(tmp); if (0 == v->max) { gtk_adjustment_set_upper(v->gtk_winhigh, max); gtk_adjustment_set_value(v->gtk_winhigh, max); gtk_adjustment_set_upper(v->gtk_winlow, max); v->max = max; } if (v->max < max) { gtk_adjustment_set_upper(v->gtk_winhigh, max); gtk_adjustment_set_upper(v->gtk_winlow, max); v->max = max; } } else { long size = md_calc_size(DIMS, v->dims); double max = 0.; for (long j = 0; j < size; j++) if (max < cabsf(v->data[j])) max = cabsf(v->data[j]); max = MIN(1.e10, max); if (0. == max) max = 1.; v->max = max; } update_view(v); } extern void view_add_geometry(struct view_s* v, unsigned long flags, const float (*geom)[3][3]) { v->geom_flags = flags; v->geom = geom; v->geom_current = NULL; update_geom(v); } extern gboolean refresh_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); view_refresh(data); return FALSE; } extern gboolean geom_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; // Avoid calling this function from itself. // Toggling the GTK_TOOGLE_BUTTONs below would normally lead to another call of this function. static bool in_callback = false; if (in_callback) return FALSE; in_callback = true; for (int j = 0; j < DIMS; j++) { v->pos[j] = gtk_adjustment_get_value(v->gtk_posall[j]); bool check = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[j])); if (!check) continue; if (1 == v->dims[j]) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[j]), FALSE); } else if ((j != v->xdim) && (j != v->ydim)) { for (int i = 0; i < DIMS; i++) { if (v->xdim == (DIMS + j - i) % DIMS) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->xdim]), FALSE); v->xdim = j; break; } if (v->ydim == (DIMS + j - i) % DIMS) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->ydim]), FALSE); v->ydim = j; break; } } } } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->xdim]), TRUE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->ydim]), TRUE); double zoom = gtk_adjustment_get_value(v->gtk_zoom); double aniso = gtk_adjustment_get_value(v->gtk_aniso); v->xzoom = zoom * aniso; v->yzoom = zoom; v->flip = gtk_combo_box_get_active(v->gtk_flip); v->interpolation = gtk_combo_box_get_active(v->gtk_interp); v->transpose = gtk_toggle_tool_button_get_active(v->gtk_transpose); if (v->transpose) { if (v->xdim < v->ydim) { int swp = v->xdim; v->xdim = v->ydim; v->ydim = swp; } } else { if (v->xdim > v->ydim) { int swp = v->xdim; v->xdim = v->ydim; v->ydim = swp; } } v->lastx = -1; v->lasty = -1; v->invalid = true; update_geom(v); update_view(v); in_callback = false; return FALSE; } extern gboolean window_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; v->mode = gtk_combo_box_get_active(v->gtk_mode); v->winlow = gtk_adjustment_get_value(v->gtk_winlow); v->winhigh = gtk_adjustment_get_value(v->gtk_winhigh); for (struct view_s* v2 = v->next; v2 != v; v2 = v2->next) { if (v->sync && v2->sync) { gtk_adjustment_set_value(v2->gtk_winlow, v->winlow); gtk_adjustment_set_value(v2->gtk_winhigh, v->winhigh); gtk_combo_box_set_active(v2->gtk_mode, v->mode); } } v->rgb_invalid = true; update_view(v); return FALSE; } static void update_buf_view(struct view_s* v) { update_buf(v->xdim, v->ydim, DIMS, v->dims, v->strs, v->pos, v->flip, v->interpolation, v->xzoom, v->yzoom, v->plot, v->rgbw, v->rgbh, v->data, v->buf); } extern gboolean save_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; long loopdims[DIMS]; md_select_dims(DIMS, ~(MD_BIT(v->xdim) | MD_BIT(v->ydim)), loopdims, v->dims); char* name = construct_filename_view(DIMS, loopdims, v->pos, v->name, "png"); v->dialog = gtk_file_chooser_dialog_new("Save File", v->window, GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_ACCEPT, NULL); v->chooser = GTK_FILE_CHOOSER(v->dialog); gtk_file_chooser_set_current_name(v->chooser, basename(name)); char* dname = strdup(v->name); // Outputfolder = Inputfolder gtk_file_chooser_set_current_folder(v->chooser, dirname(dname)); gtk_file_chooser_set_do_overwrite_confirmation(v->chooser, TRUE); gint res = gtk_dialog_run(GTK_DIALOG(v->dialog)); if (GTK_RESPONSE_ACCEPT == res) { // export single image char *filename = gtk_file_chooser_get_filename(v->chooser); if (CAIRO_STATUS_SUCCESS != cairo_surface_write_to_png(v->source, filename)) gtk_entry_set_text(v->gtk_entry, "Error: writing image file.\n"); gtk_entry_set_text(v->gtk_entry, "Saved!"); g_free(filename); } gtk_widget_destroy (v->dialog); xfree(name); xfree(dname); return FALSE; } extern gboolean save_movie_callback(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; int frame_dim = 10; v->dialog = gtk_file_chooser_dialog_new("Export movie to folder", v->window, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "Cancel", GTK_RESPONSE_CANCEL, "Export", GTK_RESPONSE_ACCEPT, NULL); v->chooser = GTK_FILE_CHOOSER(v->dialog); char* dname = strdup(v->name); // Outputfolder = Inputfolder gtk_file_chooser_set_current_folder(v->chooser, dirname(dname)); gint res = gtk_dialog_run(GTK_DIALOG (v->dialog)); if (GTK_RESPONSE_ACCEPT == res) { char *chosen_dir = gtk_file_chooser_get_filename(v->chooser); for (unsigned int f = 0; f < v->dims[frame_dim]; f++) { v->pos[frame_dim] = f; update_buf_view(v); draw(v->rgbw, v->rgbh, v->rgbstr, (unsigned char(*)[v->rgbw][v->rgbstr / 4][4])v->rgb, v->mode, 1. / v->max, v->winlow, v->winhigh, v->phrot, v->rgbw, v->buf); char output_name[256]; int len = snprintf(output_name, 256, "%s/mov-%04d.png", chosen_dir, f); if (len + 1 >= sizeof(output_name)) { gtk_entry_set_text(v->gtk_entry, "Error: writing image file.\n"); break; } if (CAIRO_STATUS_SUCCESS != cairo_surface_write_to_png(v->source, output_name)) gtk_entry_set_text(v->gtk_entry, "Error: writing image file.\n"); } g_free(chosen_dir); gtk_entry_set_text(v->gtk_entry, "Movie exported."); } gtk_widget_destroy (v->dialog); xfree(dname); return FALSE; } struct xy_s { float x; float y; }; static struct xy_s pos2screen(const struct view_s* v, const float (*pos)[DIMS]) { float x = (*pos)[v->xdim]; float y = (*pos)[v->ydim]; if ((XY == v->flip) || (XO == v->flip)) x = v->dims[v->xdim] - 1 - x; if ((XY == v->flip) || (OY == v->flip)) y = v->dims[v->ydim] - 1 - y; // shift to the center of pixels x += 0.5; y += 0.5; x *= v->xzoom; y *= v->yzoom; if (v->plot) y = v->rgbh / 2; return (struct xy_s){ x, y }; } static void screen2pos(const struct view_s* v, float (*pos)[DIMS], struct xy_s xy) { for (unsigned int i = 0; i < DIMS; i++) (*pos)[i] = v->pos[i]; float x = xy.x / v->xzoom - 0.5; float y = xy.y / v->yzoom - 0.5; if ((XY == v->flip) || (XO == v->flip)) x = v->dims[v->xdim] - 1 - x; if ((XY == v->flip) || (OY == v->flip)) y = v->dims[v->ydim] - 1 - y; (*pos)[v->xdim] = roundf(x); if (!v->plot) (*pos)[v->ydim] = roundf(y); } static void clear_status_bar(struct view_s* v) { char buf = '\0'; gtk_entry_set_text(v->gtk_entry, &buf); } static void update_status_bar(struct view_s* v, const float (*pos)[DIMS]) { int x2 = (*pos)[v->xdim]; int y2 = (*pos)[v->ydim]; complex float val = sample(DIMS, *pos, v->dims, v->strs, v->interpolation, v->data); // FIXME: make sure this matches exactly the pixel char buf[100]; snprintf(buf, 100, "Pos: %03d %03d Magn: %.3e Val: %+.3e%+.3ei Arg: %+.2f", x2, y2, cabsf(val), crealf(val), cimagf(val), cargf(val)); gtk_entry_set_text(v->gtk_entry, buf); } extern gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { UNUSED(widget); struct view_s* v = data; if (v->invalid) { update_buf_view(v); v->invalid = false; v->rgb_invalid = true; } if (v->rgb_invalid) { (v->plot ? draw_plot : draw)(v->rgbw, v->rgbh, v->rgbstr, (unsigned char(*)[v->rgbw][v->rgbstr / 4][4])v->rgb, v->mode, v->absolute_windowing ? 1. : 1. / v->max, v->winlow, v->winhigh, v->phrot, v->rgbw, v->buf); v->rgb_invalid = false; } // add_text(v->source, 3, 3, 10, v->name); if (v->cross_hair) { float posi[DIMS]; for (unsigned int i = 0; i < DIMS; i++) posi[i] = v->pos[i]; struct xy_s xy = pos2screen(v, &posi); draw_line(v->rgbw, v->rgbh, v->rgbstr, (unsigned char (*)[v->rgbw][v->rgbstr / 4][4])v->rgb, 0, (int)xy.y, v->rgbw - 1, (int)xy.y, (v->xdim > v->ydim) ? &color_red : &color_blue); draw_line(v->rgbw, v->rgbh, v->rgbstr, (unsigned char (*)[v->rgbw][v->rgbstr / 4][4])v->rgb, (int)xy.x, 0, (int)xy.x, v->rgbh - 1, (v->xdim < v->ydim) ? &color_red : &color_blue); // float coords[4][2] = { { 0, 0 }, { 100, 0 }, { 0, 100 }, { 100, 100 } }; // draw_grid(v->rgbw, v->rgbh, v->rgbstr, (unsigned char (*)[v->rgbw][v->rgbstr / 4][4])v->rgb, &coords, 4, &color_white); } if (v->status_bar) { float posi[DIMS]; for (unsigned int i = 0; i < DIMS; i++) posi[i] = v->pos[i]; update_status_bar(v, &posi); for (struct view_s* v2 = v->next; v2 != v; v2 = v2->next) if (v->sync && v2->sync) update_status_bar(v2, &posi); } cairo_set_source_surface(cr, v->source, 0, 0); cairo_paint(cr); return FALSE; } struct view_s* create_view(const char* name, const long pos[DIMS], const long dims[DIMS], const complex float* data) { long sq_dims[2] = { 0 }; int l = 0; for (int i = 0; (i < DIMS) && (l < 2); i++) if (1 != dims[i]) sq_dims[l++] = i; assert(2 == l); struct view_s* v = xmalloc(sizeof(struct view_s)); v->next = v->prev = v; v->sync = true; v->cross_hair = false; v->status_bar = false; v->name = name; v->max = 0.; v->pos = xmalloc(DIMS * sizeof(long)); for (int i = 0; i < DIMS; i++) v->pos[i] = (NULL != pos) ? pos[i] : 0; v->xdim = sq_dims[0]; v->ydim = sq_dims[1]; v->plot = false; v->xzoom = 2.; v->yzoom = 2.; v->source = NULL; v->rgb = NULL; v->buf = NULL; md_copy_dims(DIMS, v->dims, dims); md_calc_strides(DIMS, v->strs, dims, sizeof(complex float)); v->data = data; v->geom_flags = 0ul; v->geom = NULL; v->geom_current = NULL; v->winlow = 0.; v->winhigh = 1.; v->phrot = 0.; v->lastx = -1; v->lasty = -1; v->invalid = true; return v; } static void delete_view(struct view_s* v) { v->next->prev = v->prev; v->prev->next = v->next; free(v->buf); free(v->rgb); #if 0 free(v->pos); //free(v); #endif } extern gboolean toggle_sync(GtkToggleButton* button, gpointer data) { UNUSED(button); struct view_s* v = data; v->sync = gtk_toggle_tool_button_get_active(v->gtk_sync); return FALSE; } extern gboolean toggle_plot(GtkToggleButton* button, gpointer data) { UNUSED(button); struct view_s* v = data; v->plot = !v->plot; gtk_widget_set_sensitive(GTK_WIDGET(v->gtk_mode), v->plot ? FALSE : TRUE); v->invalid = true; update_view(v); return FALSE; } static void set_windowing(struct view_s* v) { double max = v->absolute_windowing ? v->max : 1; max = MIN(1.e10, max); double inc = exp(log(10) * round(log(max) / log(10.))) * 0.001; int digits = MAX(3, 3 - (int)round(log(max) / log(10.))); gtk_adjustment_configure(v->gtk_winhigh, v->winhigh, 0, max, inc, gtk_adjustment_get_page_increment(v->gtk_winhigh), gtk_adjustment_get_page_size(v->gtk_winhigh)); gtk_adjustment_configure(v->gtk_winlow, v->winlow, 0, max, inc, gtk_adjustment_get_page_increment(v->gtk_winlow), gtk_adjustment_get_page_size(v->gtk_winlow)); gtk_spin_button_set_digits(v->gtk_button_winhigh, digits); gtk_spin_button_set_digits(v->gtk_button_winlow, digits); gtk_widget_set_visible(GTK_WIDGET(v->toolbar_scale1), v->absolute_windowing ? FALSE : TRUE); gtk_widget_set_visible(GTK_WIDGET(v->toolbar_scale2), v->absolute_windowing ? FALSE : TRUE); gtk_widget_set_visible(GTK_WIDGET(v->toolbar_button1), v->absolute_windowing ? TRUE : FALSE); gtk_widget_set_visible(GTK_WIDGET(v->toolbar_button2), v->absolute_windowing ? TRUE : FALSE); } extern gboolean toogle_absolute_windowing(GtkToggleToolButton* button, gpointer data) { UNUSED(button); struct view_s* v = data; if ((TRUE == gtk_toggle_tool_button_get_active(button)) == v->absolute_windowing) return FALSE; if (v->absolute_windowing) { long size = md_calc_size(DIMS, v->dims); double max = 0.; for (long j = 0; j < size; j++) if (max < cabsf(v->data[j])) max = cabsf(v->data[j]); max = MIN(1.e10, max); if (0. == max) max = 1.; v->max = max; v->winhigh = MIN(v->winhigh / v->max, 1); v->winlow = MIN(v->winlow / v->max, 1); v->absolute_windowing = false; } else { v->winhigh *= v->max; v->winlow *= v->max; v->absolute_windowing = true; } set_windowing(v); return FALSE; } extern void set_position(struct view_s* v, unsigned int dim, unsigned int p) { v->pos[dim] = p; gtk_adjustment_set_value(v->gtk_posall[dim], p); for (struct view_s* v2 = v->next; v2 != v; v2 = v2->next) if (v->sync && v2->sync) gtk_adjustment_set_value(v2->gtk_posall[dim], p); } extern gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { UNUSED(widget); struct view_s* v = data; struct xy_s xy = { event->x, event->y }; float pos[DIMS]; screen2pos(v, &pos, xy); if (event->button == GDK_BUTTON_PRIMARY) { v->cross_hair = false; v->status_bar = false; clear_status_bar(v); v->rgb_invalid = true; update_view(v); } if (event->button == GDK_BUTTON_SECONDARY) { v->cross_hair = true; v->status_bar = true; set_position(v, v->xdim, pos[v->xdim]); set_position(v, v->ydim, pos[v->ydim]); } return FALSE; } extern gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { UNUSED(widget); struct view_s* v = data; int y = event->y; int x = event->x; if (event->state & GDK_BUTTON1_MASK) { if (-1 != v->lastx) { double low = gtk_adjustment_get_value(v->gtk_winlow); double high = gtk_adjustment_get_value(v->gtk_winhigh); low -= (x - v->lastx) * gtk_adjustment_get_step_increment(v->gtk_winlow) * 5; high -= (y - v->lasty) * gtk_adjustment_get_step_increment(v->gtk_winhigh) * 5; if (high > low) { gtk_adjustment_set_value(v->gtk_winlow, low); gtk_adjustment_set_value(v->gtk_winhigh, high); for (struct view_s* v2 = v->next; v2 != v; v2 = v2->next) { if (v->sync && v2->sync) { gtk_adjustment_set_value(v2->gtk_winlow, low); gtk_adjustment_set_value(v2->gtk_winhigh, high); } } } } v->lastx = x; v->lasty = y; } else { v->lastx = -1; v->lasty = -1; } return FALSE; } extern gboolean show_hide(GtkWidget *widget, GtkCheckButton* button) { gboolean flag = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button)); (flag ? gtk_widget_show : gtk_widget_hide)(widget); return FALSE; } static int nr_windows = 0; extern gboolean window_close(GtkWidget *widget, GdkEvent* event, gpointer data) { UNUSED(widget); UNUSED(event); struct view_s* v = data; delete_view(v); if (0 == --nr_windows) gtk_main_quit(); return FALSE; } extern struct view_s* window_new(const char* name, const long pos[DIMS], const long dims[DIMS], const complex float* x, bool absolute_windowing) { struct view_s* v = create_view(name, pos, dims, x); GtkBuilder* builder = gtk_builder_new(); // gtk_builder_add_from_file(builder, "viewer.ui", NULL); gtk_builder_add_from_string(builder, viewer_gui, -1, NULL); v->gtk_drawingarea = GTK_WIDGET(gtk_builder_get_object(builder, "drawingarea1")); v->gtk_viewport = GTK_WIDGET(gtk_builder_get_object(builder, "scrolledwindow1")); v->gtk_winlow = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "winlow")); v->gtk_winhigh = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "winhigh")); v->gtk_entry = GTK_ENTRY(gtk_builder_get_object(builder, "entry")); #if 0 PangoFontDescription* desc = pango_font_description_new(); pango_font_description_set_family(desc, "mono"); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); pango_font_description_set_absolute_size(desc, 10 * PANGO_SCALE); gtk_widget_override_font(GTK_WIDGET(v->gtk_entry), desc); pango_font_description_free(desc); #endif v->gtk_zoom = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "zoom")); v->gtk_aniso = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "aniso")); v->gtk_mode = GTK_COMBO_BOX(gtk_builder_get_object(builder, "mode")); gtk_combo_box_set_active(v->gtk_mode, 0); v->gtk_flip = GTK_COMBO_BOX(gtk_builder_get_object(builder, "flip")); gtk_combo_box_set_active(v->gtk_flip, 0); v->gtk_interp = GTK_COMBO_BOX(gtk_builder_get_object(builder, "interp")); gtk_combo_box_set_active(v->gtk_interp, 0); v->gtk_transpose = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "transpose")); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(v->gtk_transpose), TRUE); v->gtk_fit = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "fit")); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(v->gtk_fit), TRUE); v->gtk_sync = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "sync")); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(v->gtk_sync), v->sync ? TRUE : FALSE); v->gtk_button_winlow = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale1_button")); v->gtk_button_winhigh = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "scale2_button")); v->toolbar_scale1 = GTK_TOOL_ITEM(gtk_builder_get_object(builder, "toolbar_scale1")); v->toolbar_scale2 = GTK_TOOL_ITEM(gtk_builder_get_object(builder, "toolbar_scale2")); v->toolbar_button1 = GTK_TOOL_ITEM(gtk_builder_get_object(builder, "toolbar_button1")); v->toolbar_button2 = GTK_TOOL_ITEM(gtk_builder_get_object(builder, "toolbar_button2")); v->absolute_windowing = absolute_windowing; v->gtk_absolutewindowing = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "abswindow")); gtk_toggle_tool_button_set_active(v->gtk_absolutewindowing , absolute_windowing ? TRUE : FALSE); for (int j = 0; j < DIMS; j++) { char pname[10]; snprintf(pname, 10, "pos%02d", j); v->gtk_posall[j] = GTK_ADJUSTMENT(gtk_builder_get_object(builder, pname)); gtk_adjustment_set_upper(v->gtk_posall[j], v->dims[j] - 1); gtk_adjustment_set_value(v->gtk_posall[j], v->pos[j]); snprintf(pname, 10, "check%02d", j); v->gtk_checkall[j] = GTK_CHECK_BUTTON(gtk_builder_get_object(builder, pname)); } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->xdim]), TRUE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v->gtk_checkall[v->ydim]), TRUE); #if 0 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "toolbar1"))); gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "toolbar2"))); gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "entry"))); #endif gtk_builder_connect_signals(builder, v); GtkWindow* window = GTK_WINDOW(gtk_builder_get_object(builder, "window1")); g_object_unref(G_OBJECT(builder)); v->window = window; gtk_window_set_title(window, name); gtk_widget_show(GTK_WIDGET(window)); nr_windows++; // fit_callback(NULL, v); refresh_callback(NULL, v); geom_callback(NULL, v); window_callback(NULL, v); set_windowing(v); return v; } void window_connect_sync(struct view_s* v, struct view_s* v2) { // add to linked list for sync v2->next = v->next; v->next->prev = v2; v2->prev = v; v->next = v2; window_callback(NULL, v); } struct view_s* view_clone(struct view_s* v, const long pos[DIMS]) { struct view_s* v2 = window_new(v->name, pos, v->dims, v->data, v->absolute_windowing); window_connect_sync(v, v2); return v2; } extern gboolean window_clone(GtkWidget *widget, gpointer data) { UNUSED(widget); struct view_s* v = data; view_clone(v, v->pos); return FALSE; } view-0.3.00/src/view.h000066400000000000000000000010741454754234200144720ustar00rootroot00000000000000 #ifndef DIMS #define DIMS 16 #endif struct view_s; extern struct view_s* window_new(const char* name, const long pos[DIMS], const long dims[DIMS], const complex float* x, _Bool absolute_windowing); extern void view_add_geometry(struct view_s* v, unsigned long flags, const float (*geom)[3][3]); extern void window_connect_sync(struct view_s* a, struct view_s* b); extern void view_refresh(struct view_s* v); extern void view_setpos(struct view_s* v, unsigned int flags, const long pos[DIMS]); extern struct view_s* view_clone(struct view_s* v, const long pos[DIMS]); view-0.3.00/src/viewer.ui000066400000000000000000001677651454754234200152330ustar00rootroot00000000000000 10 1 0.10000000000000001 1 MAGNITUDE (gray) MAGNITUDE (viridis) COMPLEX COMPLEX (MYGBM) PHASE PHASE (MYGBM) REAL MAGNITUDE (turbo) FLOW 00 X0 0Y XY NLIN NLINMAG NEAREST LIINCO 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 1 0.001 0.050000000000000003 1 0.001 0.050000000000000003 10 2 0.10000000000000001 1 False 600 600 True False vertical True False icons True False True True True True False gtk-new False True True False True False liststore1 0 0 False True True False fit True gtk-zoom-fit False True True False True False start liststore2 0 0 False True True False True False start liststore3 0 0 False True True False transpose True gtk-page-setup False True True False True True True True False gtk-save False True True False True True True True False media-record False True True False Refresh True gtk-refresh False True True False sync True gtk-connect False True True False plot True gtk-connect False True True False absolute windowing True gtk-italic False True False True 0 200 True False 100 True False start 100 True True True start winlow 1 3 3 right False True 100 False False start True True winlow 3 False False True 100 True False start 100 True True True start winhigh 1 3 3 right False True 100 False False start True True winhigh 3 False False True 100 True False start True True zoom 10 1 right False True 100 True False start True True aniso 10 1 right False True True False False True True False True True True False False True True False showpos True gtk-preferences False True False True 1 True False True True in 100 100 True False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK True True 0 True end in 150 200 True False end True False True True False 0 True 0 0 1 1 True True 0 pos00 1 0 1 1 True True 0 pos01 1 1 1 1 True True 0 pos02 1 2 1 1 True True 0 pos03 1 3 1 1 True True 0 pos04 1 4 1 1 True True 0 pos05 1 5 1 1 True True 0 pos06 1 6 1 1 True True 0 pos07 1 7 1 1 True True 0 pos08 1 8 1 1 True True 0 pos09 1 9 1 1 True True 0 pos10 1 10 1 1 True True 0 pos11 1 11 1 1 True True 0 pos12 1 12 1 1 True True 0 pos13 1 13 1 1 True True 0 pos14 1 14 1 1 True True 0 pos15 1 15 1 1 True True False 0 True 0 1 1 1 True True False 0 True 0 2 1 1 True True False 0 True 0 3 1 1 True True False 0 True 0 4 1 1 True True False 0 True 0 5 1 1 True True False 0 True 0 6 1 1 True True False 0 True 0 7 1 1 True True False 0 True 0 8 1 1 True True False 0 True 0 9 1 1 True True False 0 True 0 10 1 1 True True False 0 True 0 11 1 1 True True False 0 True 0 12 1 1 True True False 0 True 0 13 1 1 True True False 0 True 0 14 1 1 True True False 0 True 0 15 1 1 False True end 1 True True 2 True True False False True 3 view-0.3.00/viewer.png000066400000000000000000003452151454754234200145770ustar00rootroot00000000000000PNG  IHDRZrnsBIT|d IDATxw|{UW*m6`liސ/@Hޗzu0{Ƹ.[_t;$ْ,>ۙyfgg)@ M߁c3\n/e=֗L cIINf5 . |FVU2$Ij<@ AX =58;>OTU\Y@ g$I) *X4@ AB\ ).wfă:JT hUO@ 34MA%iy:!R }]$"Kx t @h "0t$ I.@^dYV">ǭpԙNYQ )-.@sU"md%"IeN|f2rZcdӹ4 e:6q1fj^*ZDƖAבMV3E74$dG:U/>I bA4n:lڲ%Y1yo}(N7y6u`Ƣ-.$xt]nlT:<(&_Y4CbYӧOpzF-m]#7-. GӴ S  .^Y"c,-EaVz(Lo{m[ȥ{g?'w-ŗŽ?P=w8FΙO6~/Hr_.5ª5_F7&@Q=.㬼W0 .L[Rzb_)} 5/so>.䲫o@r\B=1 Q-hu #)@C? )L=BʕKK^C=t)O1) |#y}{s7c';yBWIQ1㸱vI@)MbXB~}x%̽v!5nLfk)-²sǭ ӻ' ]}#$}l(945TMMRLXMkfN̪p-;o#+3O>w?;UW]Q#٭޽CS=Nxwi Kp1~~ϯHOcǟUȖ4+\t(lv۶WnMvܲ: @rJUl-z]T e/wzگTA!Ǎaæ-,+,fkG@5`\t|/nD6Y˫`ɯW\MS8GW]e|*}\pȲ;1$Z; y,`˶(dfϻ7M֣[gYIdr,8΀ eFR\\ϾdC/'yOd][1~E1M;;y%~g9+7gc\Hzj*GE~Q%v"IJiT.`# c+xꭵhhԪ.Itx} ח ';erz#.W#hztSp4dhL| xz0HIIfάvtw 4S.HYY95̝3 㒩QVb- ʹNJJ2Ӧ\JS$'sf2{|:asww,̚eW/@ƅih8yRFUW\qcXH6d0yV$L²OWbXr>_]8ŷ,vŪu^Gtro@Ç f;jlPSHNL7US9v8k֮k̊->D1%lIHѰѧ{2RS=s7m艓ք&eS _zUD7KhU}ÆföڙQ,Qӧx8!Ǝͺo6p藼3 RRU^@ ɒמ~o'ꅭnhA}?HJ.ҫgwbO=O<'|L.?U62n̅CyLeu c/ŷ.HΒW͖b_3x6m`K&Mje.0'ѧ(HN9&YY$'%2}, #.0v.7*>SjБ'УkE6d0|?YI+؆eۭLu}.Cd]?/VXc0&5ZfKIizpWsocʥ(oO<%p_%R۫;NKK"Ybٲmݺߋd"'; еv%db蚗q/`8N.~9nرm'_hxvY h [z^^'3jaرO6bbT1|t1LifSpY ͌SXq啡~l9Ɓ'~C_5Y &^r !QPX_B6Y8~"?(M-ջW~.?Ci1T|1Ě/,EQ|S-YY{n**$6miz8zTrUTӳikwfæ<̺QQBl5y:CQ$dk|1^#OkK[N:5y]}-,}mndY͋q=kե* ]k_JH4tOm;БccQ%;%Muc+5,fƼQ\R8^\Rs1[#3ܝU%~Z&;=~ܜl;}棷_%;= ՠEd1}#Z^OT⒲=_3j cسw#Oaz@NI;gӦ\ow薻p8ޱIA%}AnN6"]:.PVVAn]rKMTVVMj*:i ]oRvtvD$ Սk̝7tM eSa7Yxu$3x_y ?$͝i<˘>GvdeWst&MYذi++y%IbPŰ1K=誫vDp/dF1FC؆r4DC>TM ǙgkWs\?, quŏqHnВeԡ'JCl:n 2__:weVz̚_SQYI~y?y\ FEV E=پ}';2g |y?-' SN\l1m꥘LWYGiiݺv=OYyS\颠̌tp ArQ%]I>x{)))e̩A5 X|";,k`Yم%+׬qc/"%%Mvw ۓUlB8Y|O|l9Oߊ{3;NM͢Ol޲u_o``#71 Ӄg!rOXהѽ[W}U6v{F"EVZHlyВDa9Յ;\~€5{LrmAC˫|b v[t`8].av׷;DA!f{JU.|'?P{?knzz.U,ZczwOwPRRgӖĻ_Çڒgyik8 7_ c/k?+.p$Xxaeسw|3 #):ɔV:?.|*f͘Z{&eM6|Yzѣ?"lyR!/7Ə =|(:~]r?QbkUq|zy2x@~zbt]g'{Xڕ-YQG$j-ZcEV"sNg:z^bl6:i*cz`SI$YQdB@uhrϗQ,00Ŧ I2ǁq m6 ͋"9)SHf;VF1@ Q(ߖ 43L5$ŐLQq15:rܬt.et ={* ,mz?,_lbx]L:=qP047E%nL,8zN %Yͳv/<=I67x$)aeaL+ ]CuTBB|Njj f$vT@p")i(,ݻd7lhIɤ( r`Gї$>ik}7hʉjȜѾkѬ tkӔF4W_sU2yDN)7V݁l;Q۞(CTrZu ]漳ҥa8›r悔im@;4jh -a赫ڶ/_rJtx2)++c|+LĈinZ$K$7yA{wZlٽwotBSQi Z$K$7yAGZA3"&$hV@ aغs-@ Aw53@ @ A ! -@ "׮a矠}omPWT' ۣ{^t]Cˡev 7fG0LhGjwOiCG#ձIo~5MkM9ZeHeoΧؑ*t;RY;RY"A:@ h!2 5hU:rJGnwlc헎"w^YΝ3rRSSY"AGAh ]((vjDL&lV;ɉȲ2uvבvu̚q={lp t]ZZi>`dee!-#XGvƍ%5AR1rM=jэO pFAQ}z瑑Df4iJaQ!!3=E--ӳgOּ5>6p!3p00ta̘ >|>fNCFF-D|fq`ȔTְ)U`7Kd%\2'3F:bv(Z|Dkƍw}Ʃ*{E>ƣC qxcf#d ȔW/oi IFe>HJRNtvבv>a): +<(X00t0 tujjjZL4 ǟ|ĢonٚI95f^lF}ؗ؄fOЊ>Z%%%37*0v ɃQL2&en U[Z"eگ8y''+33p( W͛rΡGذiLO7aqv|-5)ˈƟef8/WйS' ċe AdbbC=C? ؟ݻ+IV%=--uKCe8!^{ %?҉2x9ɿp*//ym۷oOÆc[HO`]&o]TU>b ֵKxaQ+Wa| \.7Q#ϙMNHH`ؐ}`pX "g߰wۻ~}#~GjG.O?ZԨ,9֢2$t맍aA7Kis\4cƞ7US(q$yi &q\A*DWװ}ǷTWpˢo7d2qCyX78Nn0?g;I闗GII)/.y~~{O [xXpu?3.?}zdeZlݶ̠PdOʕsҫGy~=bct-^yY Uٴu+\<~?v,fs ^qq 兤x/.y>gO3N=?vNo-TՋ,x^08,#fNZV/x9}Fnu Æ7!9LonZgtGkk[eҰ^qswп[N༚=hLq>Ѫu,w> `$Z\M,3ѬZ7\wrƍ͆M[Bҫaƍq-,}M\??GŗPRZnCTdY;nǵdYfЀ|cfϜARbb,+!, @|a/1.X,.IR]/F@QL?]?9Ͽ;o%oԈ Cs8j:_9z~`Ԉ B.&%%4JHINFe***٬ʡGܩ srsrΧ555A|\\Hޥe1p@kvw п1w bo뗀ϸ 6_CZJH-Zpl=,~>S8܏k[L-hٵawi`϶w͂훿'#+N݊2lz /rb<у >9:;.Oâ{.W 2GҳS .酮R}:x^xF,:\?jWՒ$ gK&Md-TTFT4r_s fg3nE,}*ZDHĐih+2Ooq"̜6JJ΢9f)))raZb O=ѣf3g X+׬e/>.NMs7q͋8tv귁HxސO8at6-^b"zmG\|Ӥ_maWPZ|G*ٵmUٿԾص.jjJM;0 $rһ[Gѷ[gd$d$F+ow#0k2.edA+h3NG5& I=HuuuCI5їdRh,%iVݚTLBj?.-M+;[dԎ|V[S\ﯾw Ƞ:`ӖrUWjN# +x}"3md~seрhƪk`ذ)Æ\6{&Yte嚵A+ $$L7LNJbƴg,vVVkٶ!>r%]t!-55`AIYY8NJKKIN;~xG(Ne6>]$c:K_D~~ NbB< Ɗիٰi3ϙ0JJȌQSSfITWB54McgϘSoG0q4-0u N{9Z|W_œ'#ĐѧocOxֵkiE5GnZ?~#owTNV OymD%dgHJIc:$_FRn[dddS?ոyZ?͞,TVVRSSC||@FUFYmasc钗HY_%JЎڕU\q?p^={r$5%3f`Y4#GՓ͟ea(pV|l f={v7[crpV5TN".% 1۷o7+*&).JtMVCNA$FWKMyaOiCct9'xuPGk]y4rW_rIL*pcܲp?.***CyaTU<=S^~zppq-7$ RVV2GqS]]t/@cѬXS& > VMd2sP]}_GyE99x<, EE+a֌i!76ЫWOu2gJEřmђ8]NjbX8u*/Be濰Z| X,\w x<?b 3jh^"mw#G`gPXXL|9EEN}TUp:΢4m)[ogӧ4ֶõ‚vizm~}bMf ;uS]{&!!ϣ?fo>=dÇӯ_?{ɓ'3-yoaJ#xÜN''NWqNӁ(q8us)//o2^IIxY**ʩ(b6pԄ+^n^x#.w߸蔓MAa!o!,\h_S^^,m6 ;rkV{=d#ܵ:"PSSClly}zW0ՂвZ-i_>yjjCH۝u]vC( V(2ζOm 4=At3SƤG̀P$;j5 $%d1|毡ޕ 4=0|`6#1imIq갥iqCkk}dz{A}m8BF:zow0vXĀ:{Fva۶o##[sMD;i+}3z0 ؾ0}mwa(`p`nz~ fanֿw)cY,5gKJJ"11lоy}uyyNVvv!z5x F9QQ ^Ғ }1&8γv}?_Y&#b2d`~m.F"!!1s+++ٴy#_|9\}5(J6 l֘4V}|?uP |?fKē;sE]F 晇 kG74Wgyk}7tݠ_^?=z͈J}>Z.0 PUY7b#|cmg|W_⚫cٚ}c#GdS\ :+x%lIX,$$3dzhd$ E1-ݎlj֕u䲝/tܕ%_4tܬ4W+// Y(%hyYuq;\6,˵XAki.:re;_xD<'z`N]ETPnj`6[u"FDsGIGnwl `۱Z:~iqHG.@V#M6u"FAБ]G.m X%e6K;vheoMk0;b66BhN)m`=td:Z;_'lJ~q2٬-Sr).. ;͎T6A~LvolJaa!@jwOiCG#ձIo~YY[þ=aЭ[wU6AnIoכRSS:V}J۠:2Oz#2xէε@ +En]+@  -@ BZ@ -0@ Zah @ @ h!%@B@.((( 33\!h4U? ĉ f )) IRpah @ hS]]INNN{Eԡ@ 6Miiq}d"==Ғ@Цzmc2@ A ! -@ BZ@ -MxαcG8r%%85ĒF.]ܹ+,츶_Bu[ &QZ'NgӦ$&&ӷo&]Jbb?~={g۶-9N.k[}.z &bC0 vƑ#>}6:uFUUt]yѿ@ʱcG쳏)**dȐaa7Bm E\oqD>a4Kz$Cѱ& -]ٸq=ӧ024 I/׻)TU^/Æih?-!IŸ\*? ^ر#$$$*ҏdeecGM𖦨>\@bB+Wa8.8\n7F~tErHLLDalܴ^V^-[q:BgGoΝX JJJ ;g_|yr/׻)6ļ{y&G,(,M~$I 9\Mh9r}PU3QU>}8|v=sdك 66u_C~y(u뮹oX.z dQ>Ǝ;%9ooQ#C]!C~ƐBgGo߾ :!{| I'ZΗF^19rXDlM hkȲLFF ͒$F4JK3fYgͺukغBK( ]HKK\]9)N ֵ+֬׽Y`}E g& >lxٳg/l6<oEQLLgwSoW]Vf9s$I| ϝ;7n3l=CpbX:C͎dgU:x8quL. ]׈ 3͘fjjg_UUv^=?7/næI6ԇB?@jLs8lڼsf7GGS;f4׬Ql%??Ge _uiN}4ř7-喛녽‹KIO{~MB5q(P6E1y*&|rv;6)o,]#n2DQa1Ed:<~kD\\,4>ΎH_MM . $MShΧi{ )񤤔p0,˭7e۩(7-('*HQ:u>s 'i|]Yj 55fϚQoSSrۼ<5 &ך𓚒BjQu֕~F(/+}zsMdE!9ɷSxGGSDzQ#ХSRXե9)gDh߸ 曌:^˕W^,4ihRPpԩ|rrrmX,Q\RBfϙ]HK?tI|0X++*3s::VSp6s0WhVHNJ0tn7o}O-{I"9)I,y<<));ÄDeUYZcu4G{5~Cw뭷 mGN=z Q$rrv_^ص{70ؾ[222HH 1;~7G0 ֬SNq餋1 ˅Ii*l(FapȑemĒH.=;:e2&c`~BgI$/X_68݇^iigwp#A4\.^orF$I̛7[\wu>i4Mhuԅ;wp!z,Gk:NNφwÇ Wɤc3 PPPĉ0Vλ4dY֛6~6tE6+Z-RS>eJDBgG$ׯ.4c2zL7|ޑ|.זp㏮fE՟ 35@ C\8cw4ihl6ʎۈ%'SpM9IΉ'عsC ;k?DQM@UUCS 4(faV>i*l(F$1q8nbcb#nBgG$ׯnګp:(ɝ; #L h )~ߌ__xh"ZөSضmK`j0t &_>}^.'Os6tjMLLL]:wGn?t$E=JDGǙN_$y4?"E$=!fIĢE9вZ0ذa="--xl6[`鴪\.())wѥKW`ZEW)U }/ZS_z7M{j?AcG4WgJěǓ7vچ[xL&ߊ"URYYEAA>NAM\\\ ZG"@p~ /Ь/>UQG.]HJJ$I"&&vAzzI=!׶h]:>=ztGix*a( &d |ݞQolZIKK#!!ΨMf"׶h]$I K3@$VWB5iM"@ @ A ! -@ mٌZQUsQAZ@ 8))iIcKUU Im_ rrr8yz=Zf dg @ Zs- T$\rrr[ICL @ @ h!%h IDAT@Bn|@ pgΜs.D$IgVrIe aXVEAUU4MC4v; \qp =:|nk|>|8]v%994dY0 ?u]0 dYP!2ʲ,H8_@|IP%? Ei8TQKc6˅vxzrݸn\.W\,h$I ;?zMu뢿KhMaΓe9x)_]Qy󩛷O+~,_κ1Mq ÇaPTTDQQ!n\#FFF&iiil۶gbd5êQ˲@o$;`#Sgϙlߘ umy5#8NC;j)Ÿ.ƌf;DEaav鹔rkDŽuPIlYVRRR";;;|<Ⱦ}8riii$%%j u)=>Z- è#}O$T : ZmZh4MqѨ1\㹤 Rq䳄$* ?J$-1J A!Br<}a)GGp q5 R),..ٳ8sL_ˮT*XZZ֐/~f6׵|,)|t:h6昑dY.d tc}/ "H=e6Yg*#Hii<ɲ)RMsr?I\.dFrHQGr nx2c=gbqqolV^G>G2D$1*Pޔn~+z]EU_-yT%%zA"@>G.̌!Zn- TdHO+*_:Ûb1m39 doIImܐۯASo0nq5ܹsZRi<tapDQSbvCL|Ǐ{KKK_X ǎC65CJfYsDlMhTo6g*,\C7D"dYdY #+\ǖ k;gmkTU=zO5eN| T_DFdi (puj\r=}T*7yF GDH$f1;;pu;wsssr&!F=hKVz!]sP͠n}f7C2DKrpttڄ sb/Uy*DdthN^gp@,p^/["oz|Iȉz9۷o"*JO߉D"ug359]wyJ>,{A7aHP(YD$A2D.he,//޽{8w1??og1s6`뙬-7yH}a$I^)f P !YJ|dfJ;Cۺ\Dt;yj* oj5j55ew۶LDIR'ɜiS$a6jD_s>cӏŋx饗o~|k_WC[S10"|>'OxZ-c !h|ֳu)/f˓J3,ze Uo#aDF@R1+"q* }0DeH ,b1( 6ʱђkbek4 ZޥO~o_~Ӿիx뭷gΜW^yU## |N©SI%g޺Y׍.Vl.VŦ؍ z29rl{OdZ-|7i^{Q*G8<$5吊 A24$!c~~Νٳg^>V\F"7lOrl-}.ɢEtqd98TIR LKz $׭yy_Mp'A^}e.^cqqϕfQ(pΝC#ZS*3tNpi<䓸|2.\ײ77750ͣ vҿYOζ=3>*ތꦗp$YYBy>5ԙӠp{P]GI'oagX[Y r;tqԊr?/g?g??B|o3_}?y?i,zCTj_AqƯnsVXhh=ˆD"X\\SO=˗ݲe#ɘZ@r fCT80A^~WF<6$%uR!yץj$CeTi2!o|_"\Mu)Ag)5MBp6E\.3l/+~ݛ~q ܽd>W^< ^ xGr 4;;kZ8qeLuP(+R4)Z6Y^]V|rmFh4F>;v5h w鐞e.\fl\vII"}VeS~OQV߳+۶ Zm?>b~_7WZ_/ 8{,>\r駟&Ga*UB+UTU4z1z?SN vYNI9IHr%*pCR95dވ94f6ZH"k,hT.Pql-\)Ɍ<[׍{l)$ɥ<Vбn#[|^ÂܶZ![7+/"~m x73'|w8T85e$UD>lj'&ZVz\.t:mr6ۈY/ BN_Q8ۧ9}t1UMФ?^}4$!)Iu#kq:LM Ize{k;;;Lr℉AI~Iª}YGI]G_}ۨS$^{5ܸqOF*{gnÇ#ZS 8IvٳgbRX,fHU3R=hB,9C3ɔ$`rFδq]/ ,1 æCcɉzKA=I%+__:t'En=\N׻ bhVZ DZwood.PTpőJ֔Āh>ӧqֳܹrVe ezA!C^ȳ#(uB.k|sIO3; < #l2^r!'TT&J> om#C2+QNA͓EpmDv솉T*q}fݯuOT*N: pX,)gX RɼA0V$1!Cb6HK)YA \1aȐ7!0,Ѕ8mג ȆҐNu־ӊ^M!:m=NײJVgTf+ EKacq,>/Q| e7Z-ܾ} gz>epDk M,95Mqɓ'Zǽ{jyjȓmʔm*|rzƁa  [/C N"s1aob5ɲ]ڠ .a5%z}r@h^={3/\x _ƍk=.L8} .^)S͑ɓ=϶$p],//# # f6J,: b^rr$Z:d+sy.pؐMz$wIX !C|ZDdHN>ײEbi iJe72ߗkF.O=sH$d2L&3s5њ2gff9X:FP@{.޽#afftZid1Bϛ0,N22,ADK2%a9Ѳ2cD~-cu™4B!dYdّa|֔Arp.JhZwww\pdpx_&C:ĠW3yn9ʦ2dC+Afwp8,H%ItPg> H$i!tQ~۞y}u_틼AJWrp8J85DEf BJ޽%eyR)㽐%(LJm3 =ț j !u[`7a<1JoaP<y&vvvæBt#)z-ɖ^\\\hpQ)e2 ܻwF133T*em!>n~P)Z2"387mڕJl}Eaxt:ƔM ?EO>q$Ҵon- i0*853AiFx'O#Hܥ%ܻw+++( rH&"@z=l$ٌ^.)G& { 8lI%4S$K}Wu3NޝltmیieapDkJA  t7Ppmm @<G*&ul52K/M2ަ~-, Hti'Z$B-W>*%ke{Rx&Åvk';I>Fq $I,,,9G ҄*T:rObB`rH,ɖ=ZI_|lVZ\eI+PzY\ NN4 +֬E%*p͓)^W 'Uj':AzGr`IR駟~u|gw* No#ZSh4j X O<zꩁ_ \|333P9hS{Aj%*% .@Eީb1d08}t:x<8;կuSzQA%YAt$D,D"GHA-/%=TRѶ)e{T1KmTUQtx#ZS9c&αcp,..;wݻj8}4ɤI maCx[؏/WDمJ%0bϖ( o0h6fl/ISju_ggRɌtWY^pp5њ2b1#s0ř3gpفIǑ bA9A>-YgdoBfA!ѪVT*VKt&otl:W 1 zEL^$HpyJ#\fRLR)AÉ#ZGG>eX Ǐ@VWWuy_YTd*bG*ZVd,>"H`ggjLd ˻ o·{D" D"ܺΜO{5;T*xgs CB$IȎΎg=aNcf ¡yKH%YR`&` fJO@3ar@B_S2 l—7PbBZE2Ap=ܽ{+++G8AЧ 0 gB5,^ǑსjB=wRuhSY"K"խ!@m Uٌp1THr( T,Sx˗'0^**Y*XhM0l|)R)L߿5looi(iC2kPlhlCMLfh,W(h߽on ɈtX"t˖](?k[4Aɹ̈́g޺j>xo\_o^x|oy晁8 hM(l:[j!Lرc%VannDb`K5Hhe+dHd5h2b!=\甐ɀTD"68H_$Tu'}X02?D$a!ghqdEL= BR4GZ=:;F֟%kƍ8}4R{=v;>њPfgTj40???ݻ5B!S2&4XPx6<-FBXȌbff~Gy ̰d, 'WZv$Jg \iߕ^VF# EV+?C _qR~L&bii J/^ߡ785Ѓ/ih5 ܽ{<@:*=.ЄKl$L6n۾ٶ %3h95dܷAx{٨p9  pFo%$YK,iE$K6$)وM T*q)3&f__ΎfeRwԩdfhM0G7eAOܹE6a 0!(KoCd5fth Rtd 'IA+4AلuczIϟ(x~jiZn߾ųܟ285%`H9;wneݽ{6+8XBAfx}zu|d3-9⤬L{8#~2Åli%Z-uA$۩'f~M~_,0j\~˿z]Jq\]#Z =20??DT*̙3huSf@,RJ/5dxGѹxzfh ^6re#I.cY8%M~ɲQzr~8wH$d2L&3s5њ`pׅ@6岩̾f, N=؍pIfIXҁ:!XTxڡ+tˌRg ͦ)ź*Grdt#j _cȨ B!dYdّa| H HWH41~zT^ ޤ/znQo$݈#Z YnlWn4(JVV8v!Z iULR:aj5T՜q8/!`9R<`^ ۲;dm~4l-ItҠZ[z'u}ʼnkt2QoCnܸܹG&lQ"T*t:=RJjӧO#LY4e ?6f],E45Rܠb=6YWkb"Ò3n0ItK/`0!m{M&ha}} ~ 8L$p#Z٫ ZƆ:>  ز5I2aAǩ oTKIB)†ӃNBt:n- V:fs4]/zv{r}95@VN<L8y@\__GX4DK6*5A6Y2ۉ=ӄh4W}%-dDŐJddL![*3ndbc jGز?X[[Cʕߞ8 NXxd<ܼy kkk5G&opsss8qP,QVFu@ fi\, !kL;"RbbѨQ)F;jB!b1fe2dYa #~0$Tsz*yP-/_$q'd9 .]Ƈ¼l̛p^7T?XYYUd2$I-{IhVAp\iXcE3Q)Z6Ac~Wޡ7Rʤثc*=u!I;zݑ F2yњPhMFRAX{y2߿REٙ*: $gr}Hx`byY?ٰWq apDkBI ;j5loocuu+++Ø)Z:4daӭ)rҋo4h fiaգ$:$ZCoxT@p:Aگ#Vmapp5њ`Ȗ6^A7,RZTY6Oa.,* rBfXn0ɐXsVXWCBV3C@Vs$HEN88FV+LjS-9y뵙hM hPj_B}ܵZ4Vh1f~׃h1Dϋt|DKC,9kfп2N Ifo/Q:ISMvhɉ'Yɀ#Zp8lvdcǎ}gnllFҕJFÁN7aBeI-0Zt:Ce CwL_'Bhla?NZz~.#Zi g~7_;;}-\.c{{R vTʴWy TGHl75R0HCnMM7uV zݔzFV/ͯFI6_;wnV뭭X*?K.yF G&9s[}/T*;n&ȕ_K% ߲v0ɘP{?]mtвlxFx GlݼyZWr\O)JxC:Q ox \pafҚhџe .!IF?AuRnP#bxб$8B׋}Q,Yǒ֯ٺ}C$AR;H_Wg9]wyJ>,{A7aHp`',LB'NhYr4fofǒ;32Jbk+B3資Z xctH$z5/| /⥗^›o:[[myNњ0L@l6Y,, ۉr  x (+ibX :8U 4Ґ$#a+RgkԪ U믿W⭷k!yW+chM4vVPlˣdW6k&mԎ'i\h!X8q+{lyIcDvOjyGTG}4]rNњ`t:qr9 dپAoV\F-iY-2^Vr&C լP(d7IͦY&ץ3n%hv wZYYA^➟;w u d2\.7ua3LJ;С4*u,uuYQ{+ Ft dKz)T"Ѩ11K~ pO!T*5QzB]8~۠_tњ@dtzlC`W݋hPa7[zdxFl]xEry~c{Ⴟ9  -?KpI51P(677}ollhM0Z8@aC`W*LRTY>6B%_U-.aD"F#[xKyPI/_Dl!ģ°CBgϞ'|+W>Sx˗'0AJam3Owiggt333{YFbbV lDšcشS-ZүBǨ}Y YjCzstaY|\w%Ƭg ^M{°/&6x <3x'W)Zwvvn133فԾ*hגjָc!Xlg"0ߟשPs^T*{C_CpDk@ֱc^*&T*X,fB㸍 2֣^AD;JP(7hT*T˖d V+(C(&X/^L&bii J/^t  C45%hL|~ o:)D"T*T*DQC%ad!pM4,랡Cv:xgTz`7$+ޫ#% 4ӊQ1TB"@*2W`umxgJ%ÅA {5O"x(2$?H[xjbnnsssG46M}:^rb!ME0JO ;ԁ]%kffƜ_$ZzB*$#H<$ɣy5$I#Zc =Cw:qd2 $ZF×$y[YN۾nIf%NgT2 Ѩ4J}8!}F IDATy ܱH b&{ۀ1IˢC0> tqߑ$,ݞu؃>sHٜ( lϊ*VWWumyuh٣fU:'rǹspyԩSh4RUlnn* gUE5͢jSbV|]m "a>tn쯤?)X[[Cʕߞ8 NXxd<ܼy kkk5G&tŌ׋pggǔtX__pT!Hs7W7c P{e XDcz#cݦZr- ϚNqݿq>htkE%߳T蒥2&ŏe}\ʼn#YΗ.]Ƈ¼aJ4)m/(J&677Q*L64jC<` 63II%IzGN:"~r|?͚q\.խ^#RZb{{ZZ䨛4O"zeSub -m 㲐&[䖌dM0ɔ[@{؎'LA *((hi4$KTXEZq0tX47L&\.h4jTF1EIc* ]Z" .H8, 3-ghM*r|85!`&yE.C.f,˨T*hZVj$ɇyƾg5п*628fffH$P,m8KOh86{I*r 'lu*[6E ˊlE9т#ZIQ z%Z;;;VѲ AIҳNxnRnk,\J%ZR7fqzg֡asAC+z,6>AMHig(![F-85HRt:f~ml'E5K*Z.?LT~qa2dL=cYs\@H_MgT*o,1ѢS x :q@gE/ǒFaJHHcL]BQ TXWYS)(J(֖lYZҾMlK2&$ͭsOG[)fUL?RNiWTקv! }%`z"HZ#dM4f2zn 4;8hM$b{t$Z~V<fBhɁ-0ސg!Z }ДnC/ъF>91ڗJ%_+i>[ln4| {mj]B24!F&2u-|[H$KH$|O3ΜYy]׮];#Jg}=ܠ0$85>p8d2D"ѓ7 %ZRGu*eZ$$b3:02ZY4W*/<%Ѣb X4yɓ'q ,,,  akk [[[n,2t4ђ79ċv]A~VC7J$ȨP'"vNDP#|җ_~/^K/_wln\8PC21{rsBRA^7,iץ~lmZ<9ەPa"(C*[,GAz!C0$NP(|H&677=>RJ$Y:S?'$g:FfQ,}%lo*n:^z pxW\e1#Zc kZƟ%n`b-SIH")ӳ&b/0Ln^rAc5C$]f|놥yJP(L\4!ju&_򗇺~D[JeISh&Z4@J KQfi%HWپQ !HR&lVIjMA^H\T c*+.ֱcLBB4yf %:e+++X\seY ܹsgvh)d=dLGyGVC(2,f62Dt|$ i>2F**ҧDJ#6^R`{{b\L\D(TX,~idHEKb:\(Knb1_B(B"0Nt:l6kobh4FN*Z685fDfN{2W*"DK,It, z)ך Dt%8y͘.,fgg} "8yƒ R :w!N#ɘ& /UMit,>ALi4&*X$z|޷ x0RͦSѢe۝znn20(F9>w V,-?Jxcٷ$L`q8ɠ*Ő0??o1mtxϥULmP!!͚> y
  • 6Nr+-h6X__G\ꚶ*jjR1u3fR4!xo^@>ogyO>;# oTzBADtDS<Mzo~dǔV&DLHx =_{5ܸqOF*{。ס?857E겷!V& \gJ%0! 汲$Il-61T8r!/w2hG⪽qR \,6 7cp $ᮃ&}Zzݼ&mBf03 Qa8'!6<*pDJrB`MlP֩K6%" GLP z>21!Hh4Gx*^ "T=mJhYgB{K,I3tw:czgia`PEaDyPFepb'AAvMn#͚1tp)7ܻ0J3xqGf)1jggbTo4F(hZWG+Hh9K%TuhN*kMIH˰Cd٬ H\>`5q(!2wС̓υI)CJuSO TVCR"JI0}WHB4 8&iȱRo\O?y;Dϊo$1%| hQ q FnDk{{[[[l5 cƭVt:n[u|]-Yʕ̪$Zz$Z0k6Fb] VΧO?t&.h6\雕+prP! frt:T]766P,Q.M!A2ejۦFIB,H0pDkLFh8g,VMߗ%3Vf R |EM6&r9r9;Q$@PL6mr wOFVd ߒ?t5A {yg=TB6%RI;~}6"Lm`8xggl6k-88 hjE#%MD+JK; lh+-Q&Zl#l-IIlqU)T(fܶáa/6$QlRqΚPUI u!o*:ԢףIܾn^$3#OMA% hbsDWȒT,8k% _Cpjrrl: uXǖ=Yj&ktC(k?%SY£ۺvh4h,*ȉ>Ah 6*V1IE+bvv( R86j67772UdAzXH& m.I-@d #$Z$[-BjಬkȔ$ph4d29MFX,z8?_1كYCD7+Hm"t!'=y, -G[~htPi4&vصzңÆe2CgffP(ߒh2;$[>+I/0oًb;X[[Cʕߞ8 NXxd<ܼy kkk5GD{$ɢҒfJc ;0V-ÐUo|MV̮>/D,C*2I;GMxd2(Jfe/ |T$X꣍0[lD}<2t;j0NR\l4,C4'3ke0sՂycW=Z-L38-d:9Fѯ1aTnq}\ʼn#Y^.]?y1V2M!gm}X<B*Z V,HXtQćaz> 5c45k333h6Mb}6ehW}%Ѳ~7gYCf^3e$LoZA,7YG|- eDNgnVTy(QVh4Lqh4jRR/!E`s<7 Ul6nQT* ?(؎D;jR4L{A^w$kL|:Gz !:Dxܤ1=CB&ٗiثEF$dJ {7R"g9GEV47QʃhG+ ݖMos:ZIv)[gn IRF)ln Ba7#Qۑr[IhKLI7u&@@duT*}IQ&%-o: NjZF)˦֐\^FR޸yj_49ҨH$033_(F6 7:d2t:m2 cTi|HL+b\,c呤m$Y KUOrLmA,YFD'YCkKP#٢*?ۆ'I9ĉ&Ⱦ@սAkhx= ٖ0#`A1 ItدFa!Ie>ي>lR)SsP(d޼dR*tf2$ZreXK[M"eH[&I:bkBsI\R4~ Z,n;& x&aQ IDAT<^i5R#`jWRm}װ*doIe~8CV5DeBJX~5䍧ͭQZ Twl!)qx'cT zJM4 T}j|ZY[KZvܤGJeK::Dͻn2&2/,2Y87s㍉7JFT?PG2ϰYRZ''P NrwP* ׉,u>:L3ț M5ժ1ebf\v3+-Ft ys%CP"Z4˛ VR Ot:EeI.hʇT%φ o|QIctfBJ%fqYx܄l'#I䇱\>~+3o6 C FE3HÎJL*؛k*BP0sNd "ܓc!}QCaͨeX@"QڌƜJCEŶ ڰ.3lYiv~ty*wzÀ$HT:AR [[[DDM:=IA$C7*W2,s}6.A]Lzu?ђuUUj-`IITdF$a{MCyIoXඓxmoo%=u$E(`ٻ2/IB+I@e5FvׯΝۨz wRi<tapDqXftST*TUb1iwiiG2)$do&'C2 HAB + 6!CBaT*Kq1Fzwl6MђJT $ O6 xY4Uໆ&r\J$b>׏ߗw! 7o^GVի\.wJyapDk Tlݶ,Mj"! lEL3?'=Lhq Sj9U*0TtZY[L3Y敲yH$r=,;n;3dYrRJ~,6>]ށ7/ @cXSQy^#B2*id&&'uI@wHj /x:DgcȈ۷o"HٓH_Wg9]wyJ>,{A7aHpDkHAlHSV,@VkgfiLRAtu "ѲY-լa+Zң&A13ǜj,")EoNb ,n||͇dhp I/n$=GIl56Kqd:lTt\7['1÷^dM3r>@YwTok)(["9t&ʏ?_җ/ŋx饗 7խ[ Q'ՐLΰ /*K on6A`oGJz9r/QSdL-YH%$V] uV(ϳ5&4Kߍ 1Yf$_r{uy>ѫB@z:w>ٔ7+R$dCaT@簠UnkPiXM  qY oDz*z-3gꫯW^qh"d])̀?+ 7( %!+%6w p333dh^VQ*P. aWM2q[I%#2yРI$~T/2\BC4_:+UB!ђK`< hu~24yÂL*Z_I֬dCat}^NZfdIEVr@wnqVaii 'N'︂pvv^bvjUb3Y" 9}5ON&&'v$zlDv=Ɗ̓:wg6R)eϴyO*yl?I0{AI2D |t/|vtR+ɚ9ibzG /⹘͛cv Zi;JA$onnhd9h8vR|wSOb[ZZɓ'U:u +++}6@0VB3=CEŀb7*L$/ݦ.FU%OtD+|}v}GdD.jIw=1[L@ \ɫqipUxy5~Dg{&JTQflQV_jX`<0T~l u"_ nC @-օnMDK-=Ԭqϔ+ VVV7xkkkX[[É'eQ#ݬ7/LI\F.ϜYN$$$gwRy2B*YJ6V@{n7`0'^z)gSunla]ܥ)aӭ<_Xu1E(^ҨwꊥV>,s='!!aqqE|x}6}$=Ru(VaG@4 1f3#Z@Z#J<ɅHD{*QNqbii)sd:u*#Yw)*)⬜ -%[@W֑h t+gkuRbDKgVˣ@4dEGWR4qN9 Z*PqJeT{N89+UC:^|O~%W=| h Wtfqu}\b| 'gTh li8=OxWeU,.Kլ,(Zf_o@ziܫP3H]3$VfNM$K7D;lnnfijh4üqWo4$j6OɝZuQ"W(n٣o_`C::IW勀^G_W\sPV?cn Z(+<x 666Rc|襌ey;tpdU  6fgg.yoƃ, %7-wFJR4ܝR^Uפi,..b3C+!צi1mr-_h(^qX4.!i! U{q*T¤}6+R®Q_${Zʥ"^~Z **I:NF:3*3ƕLϗ{'YA/\>k6~.\x_ ZwOUѢjAc<_{=|x>} lnnf*HXD.0>z$dZ xt%8|M~_]TV (g,G@] ݆>WFx;pw>+ܪoJ(""8lZIw/\d y;w.=?냂hE 3HDk{{;#Z7oă-4̭ƀ(]:O'BWNQtH2ՅZ-#dmlldD{ sSQu,1kh$B,S='RDɖ#O.SbU[V)|PVém~RRe7/`ou]LOOi{-͍ -`pbDz\33IUkaa?7y'Zm̵+0iD@A* )48?#AY0w_ߕ.U!RdChJwc8c'ZYY*N83nɕ,l638%icu @4݄7&`qK`9QQähvRCv*w]>gݛ3 "FMr1qGم#&ܼ*XT9Pa)Zx:v1@hЅC"JM`4W^'#i`yNT'FU9 Lvp1{;-;*VVVҭnd جxc߰_I/uQ2E@݃Sqq.K(݇f$tqQe[(uO%Ǒh/i6BlS>nJxJ-Mzq"^DkLUd !dUjY$ZuA8UT"s;mb0h-E3^E ʴ$Z9mZ"rqhy?k.Eḧ\-W#(ɌT;/p3zs{1Q]=9EMZNZu\Ȉ7P*Vv:,l@:.pUWJ?֟c}'(0.DkL@.hD_WThb4(UF̥R)SI>55 Êk1gZmg4hJlAN>#*ERgw*FEςa޽ޯQ_REnT`diZw}upI3%\RF+z  u@>ctt wpvո1u^=WɕӰK,uMOOg13Vagg'K[ 8s NRW:ާRvU  : 5Ah&)߶a) @Ron?er< #7~j#EUT%`煅=mb@;L(LB'0ݱ 0}|]J+HUYr4n&]@аiT}yvꤩE<7!kzwE#NJ9NFqHC1]T᫧@1,(ZOիwQT:v+W>n޼ ZEdu% 7cd81C4Uh^ǍL75^)A 7.ji#EFuQe -")SRQG\Zcͯʪ@I;@_4kwBP}{J}}AЮ~{WF^/{Ω&s\*?~>c8(+X[[ɓ:*P%AElNcgTowJݏ42n!bdYV$g$Jh* 3u RnUҭ8ģGpܹ澵 9IggVN&? 2BnReOiGƈ$܇yI m•NUG#e_L_SI[ }:;^KalR IDATho3(0ID@g4*B?WhrI)ઌPלdkff&KR5<|?FȈ .լuT*9s'N*ʀҢn0g:u骉+az4Ξ#uڿ9zNJi&$WrGurR*WӍ_sTSSߕb?j^9 NSQ#U]48\ נ0/S ΆPHb`ye䃫 Ur#lll`ss3KZE Ȗ*UN"Ø2rnДheu,//ɓ8u?~gϞhdZ-lnnfyHjZZi46#.uqѵKw(sԯޏV~l S–Gb-޶<^fhI%<f:?R5vʍ𰋼I\W~VW,,,tNO\FAchf *Z$Z\VU}6B-=njj*(ɓ'<\677sݸ4@<.uqԻ`MwC}?xj 7`QdIƺ?S}'( s#q]q0dRLҜYUмXQ{F+|~.@h+7QṩRgX:(I~{6}dZz}ϧũ:L$ "b f}N#5$o)? Z-͕E>ȮF_~K.?)~_﻾kڵ+P|jjj1Ѭ}?zHDPhF#[mDF&>npCF|"^KQ:6}ߩ,=JFDx"™W>:{6~| I}"]'ExC' UR/J*_ {FG91mM:e(k?ď~h4<&xI@ g^ ~!;}/R:p6 +r!{yFuλyߏB"%+Th܋mSTkE$cR~jsҪ9" XiqeX^^l,%ԝ'oK43xf)TlÌg#c@ܿ oXaYPQ0????+F]*"ڶT{S}0R_/UvTrX9T:y]_*#=.L+Ť}1ENa U5^5O_r Ν;j5u :HbO?".Ν;3gΠVӧ(JG0x)U+Oy#F @lq{{;#Zt...fj} $+: :,'D)]/E|/ïw?H$xT9~vQIV^/#zv!"!ym6}s !y}5*T]f7x6C5SUy>TI-lT*;wlҥK.:$l.12Q']*n‡~{0??F1@WlGN%4 4Ml/Z'ܞ`TU幄"d!:&%c]Ŋ2ʏݨ$+kQgt=w7 #5JJcIcIJla 7KמVw.x,ffF3;;;{.}_ٳ>@7VCJ:$,voŋ133'NɓX]]n޼G`?=΅a΂ ˼M{3z,j}}f3b?|FdqDj@ORD]yU<~L #M6C^}>#ra+JۑG\Jɖ>GD0O:I& .V5N~.\xl1 uH $\.`ss333Z]]͖z_ǍYqfk{{;Rll-2$nCMd;f6tHˋATF3P)7cTfEu#Q{G"u4oAC}htܳҡIuxHppd+L -|p'#]Xܹt^D08#yN>3gɓX\\dys>вt #t:gnVkORM7???@2u)ڞ}PrNb?#R9FU\yG8vr]AK=,hHd+7yn ?՟U݊y{H)TZ6o3|4==JZ64 u yQ# gK!?w:)R3,ՆBOR#rK#"y؈( Ϫ=NJ%u\FAR)O]i:H0 owauu5A`2J&`z2z=Z-b+"= H1VL-' .޼%)FIi*VI>WbiR '(渐(0 DARn;s|w;̙38}4>}Fm z`65|;R)ՁsU.CE%K vDhXGuWI ;+sLU$#'6DлB7.,GDRh2Y'ދRli;u_fNqƟ /H SW,QR c Z[;fWgJPoӸu677\E3KXP$A%\ڣhED "v*t]\JU%JFqKlr_R)}L92UM^w}ΘdҷRJMQۣ9vG;nPb1|Q߽^/SI0;; $_|n5 u(PPCFQ(݈j?W_}wbww7˩O?4S*^C >`Pq̛:R)[Pթ 'u\Av1\1$h/Dwi{c Rj[1T X%J|ԥkN&t:ق{ ,3j+lQa^IfggGRoJlѽOu[y vJ尫R%nP.W<)!#Ni4ׯkGkeeKKKhh48qDF<`:2tQII`P<`16K3gʜ-\=WrH9ɮEu\JN^tq-dOh]R"Rl:EgMjΟ>v;#YZ۟Mx( > $:>5U8PS޷$%o<98} ^/Jz)-\)p :tͰ3 LRnܸWX[[ VVV` 2:swqQ$J(+1Ә`Fnwh1>Or%JRja-[D8+Zl@ޝE"刼(Xwɟovv6U確-*ZXIV>$=N~Sma;Rc~155!h)tr|c耺I;@؂C?WL&$G x>菙*Rx\`mm 'OD >ߩ!<_~%1;;eF^Ç6ˠwŅ+M/\KW꼟<'Fd)HiE.s/uQm$Yr^Rc[%R>sx|m۱z>N^CիѣGgњ F8 $!*hZꫯpE?Ξ=i:u 7n(]5w5re sss.E^q@իR" lMB{\\{cG <|]| 5aDW^L~4P[[[x߿jY={'OÇl6,թ)b ":4 oZ$͓]m,ݕ@刪kFJ=D-ed4 uSuW0en*X9=eFJ\DJ#IEDR|.C 0*A+OQ'UTGϢ3-WӶQQG)H1FR+ք y9[ܻwKKKŹso?&rTE.&W~si7SSS S&n3HITb]@-*uK]a{ƂĖ01LI B5VU3$u-Fvլ@F!yD+"Ys/u=z\VD U0QCI@4ב4Mܺu KKK8uΜ9m2}]j5j=)͙,"&7Ip8mll ff0 }Y0U8UH5F*(5C.`}hԚH2ij4=FfX=ߩ2+SHSDRD/֨ gX S YE(<;eJjA & u0ꀥDn߾%,,,VauukkksVWWq cv_OBfFZER 2Ϟ=ˮǴPVz5 /0nh5;;eWE ,y "Liُ2sX_.QTR.g*$+y ZcqR~G bD;8҉+}&hjvwwh42Aɓ'ux b~~OƗ_~wٳ{:'Z1J)Z̵mt]z=<}tO;4Ik UTeݑUEK򔚥7U鵵'IĩbVyws*UHq:wJmsו3!RTYI^*޳^7[$Ry>n(p(@ʥt=<~V 8}4Ο?{h Dr"4&V3 ˟uOh;oc=%AT; C p[l=o_?yy}{,Y!Ҵ=J-=ֿKUQ%)Ϯ>ydQVMWtq߆<.5'z>ܼyHTx뭷qc]I ZLF4P1EZ F?Vh}R IDAT]#pc+ $ĩ(YZSF+_1 ɊTGA.V[.RN*x^GdUۘh~HʻVȖos7wBS`>0J'055h'z`/:?W~VW,,,tNO\FA y@|%@333snܸӧOcvvϟ*kܹsg &gN2"AWJtPc<TCWKOI~&_H%Ɠ9 p}I&^\Y?$eܜRˏ S{O:",?zuCoY?OEnqkl6G:gzz/_Z| ~ߡl>~l 'kj?3.+ q]\v < .^7|o۷ٳ,gߓ#t`/.6ЅXղEjED+. ut /_ )Xy1I~"2} u%gD"XzwOoZ^\:??r'CcJEU䄽 ZaR?ͩmFMwMJjZ+hF_~K.?)~_﻾kڵ+P ~lf0o߾ XZZ™3gpO7055ymJ 2vvv2wK(Byޠ˺*И,'YN_kJ/B0@4=L7y}x5z\UǨRRkݼZ/))"7A0 cxpWO81벜r3/Ko~8<~g?YYP eP*n[n᫯Çj .O>7h4GT\AF}VLUM)5^ѬF.RcXi{nCQJz\$"0BDTHXE.ZT!ud"h&h$ۿ?Lgzuww~!~e?Dq4KP&H}fhZfff-|׸xbF.^uܼyַUb055elg6y#oUx,}9cMwD~F%Y){-nf⏶2 d[=L(K!c~矧T.-CO]['|唪 2x= ;rJAW'iJZI/>:.\xUױ7oh33W0Jz{x>N>|;޽{xU$HI$H\ 1/O~Fe'6l,?7DNϥ;}Z*ĩZGjk"mSdST}"up哐a2cB,HwV~ѳ/%F7[^^=I%Zy*`J[mdMkDg!Erc+iyqg=40^ )˪8Gj=iL/~+WܹsVX[[_P#a?L1%5z666O޹sǓ'OPq)?j'Of4~? nQڠ =q[_ɘH,u;7bB1RJx)usÑRrF,O?HqSb ~#*kf-Dk38HbJnJq4M\tPZ њ,԰vQ[oaii ΝV!OowysssBPSU WuOIyDKˌa?jEˉԧx]`7~d}J z鱑{uWE|[i|mB],ైH@DnC g#Z8ITUܻwoq6-޽ju~;{)AǍDkP5 0hZ(˘ӧOqTU8u/ŕ+WW_azzٌje 6_uWfQiwQaSX\^j 9NIJZFD>U漏9l;SH\ @H p("T p*YФTBUAgF yq…쳿jp}P1BAp~~NF.VVVo} O >C\v NB\F\޳(NϣQB_eh{#+YyDωȝS)/\W5FDJQ7IGq)Q߻$oX>s毱2ѽd|Dy#Rt>5|AIW//(|uywѤdxo/+W>yjuΝǥK- ZcĸHiR2t033kݾ}߱E;whZӟ۷ogp}`ՆdxZwjpTW7z̯×.wڢfP\ E2 ؛AeP#y8=3+2Zq1Iƕ*+{TIυՓ|o3| 4* jژkW`(֘p1t0;;%2/'Ow;wgΜAGZW_}[n֭[#+cn?Gjpf)WMʖƨQQ\.] >"9^ow)#O"wl_bD"kJm֯pCI;o^7￈Ըh'X\*PQ'vG`1??ܸqx뭷Pױl2*?~mжqdyQ:(QiE7-d2K\k>󈖟W"W5\^VD'9Tk,Ev 5c !zV#23i: U 4 5c8*&rz>s;{.fffPozr<سou+W3pЌy1#(h1w+U6dD'2jH1NpmPJr +ZR"]] 5:zR׮+"0>Q*9z+ZrxNrO3walNMGnJ눃Rh O<$@(-GeLMMe)a48(hَ :6h Axׯ/*Ξ=]T*,//ױM<{l@YU R Wb_gjD\љpd#pWky_S(Y;lw@rw/KDSA_7 ^??tU{AH!> 믗Iu͟qs9l"pu>}W~˗ER=\kkkyhϚϛ7obmm o>|J%,,,`aaXXX@^<I_ ]C88Yt݁'p"r=^ȖZͶuT*FD XqOt~ylK0 :ܒQ,7y}튩*QE ;>h;hhWWW#|3U@\N\-ָqjZBR,666p5\t oƙ3gPTMf~~J% W9y#LEIV:N͚Hƈ Ac?Evs;9X^索IyqtQ%Z]ѽQץD{lRd^hz (Yg(E7WzG+0 D~?jt:ꫯp ܾ}JNʈrj%ZnS ":v9H.<(E"%щz='I L'WNvD \acYZ׋HEJ^S㶴>JRhE*gYﹹ,!cM%  o ORd_J6{8:m\ =z}VcleZZE­[gFqZsssVY\[^(stStgCe)#8j'7 1SJC/ Rcʢ"Duu ϾJTI+t?QP+[uc=[.3833=NV+{u:=  RUU[Wوa<8{x~HVRwG>+_9EBV<k|jX^^ƅ  ۾.-%%ͅ4my3tyQ۩ ?W&Nw2G)R޷ȞQ_J^#Eɯ*T}SKϟ X ~g\V͒&&=* \e/Ɋ>&x}Q1UgscѣGx!߿Yf+ќ76%[n%sU5(.#Ͳ)5kT8)rx_9]OWbKl"Ϣ]`yT\>|vrFh`ssɚxoF!Mju=sJ=ZVhI9lp"dݹs'KZ hA W4]qty$3/pJ)7GJݠQWX9\Vm꣨ 1rUs077w:lnnbccO>fpBcD޹J]>y5{@q ZcqQsWw5 ܻwofffP>AQ%:F('`J\Ė:fEx}Saa-'9JU)2=6;{˿=Aև=Ѥ >Bç~7juo6._~w̵+0iDk 8.٩Ixjݻw/#Y$I PE.]TrUR;.DKJ˨#*5$YQ'J0S׏7*#_|\a;Sd,&k}}Ϟ=FA ;n\uswk`LW~VW,,,tNO\FAAgϞ޽{T*XXX\z$DQ FskxK&)Z.5J)eE)<`lke|'TkyNRɢDf|g6ommaccOȮF_~K.?)~_﻾kڵ+P&a~?0d\j.JRh)HҘTQVj04E4*E8gTqQXUxRn7&z/YHRĒ[ MNy?Yʀ'Tknn.[a S5csˤv2uB'd"o1IUeچ_xpy?~"`)&#8@~幢pJw6Ӝ9NڼRȌ)h<[,.HJORRu!"Z<*q IDAT*EʖU*=*ֳgϲqU'Q&Js=w)GKǘ<$7OE1]|я~}FѤ/Bњ 4x(,9<c~Xx U6|q"G&]x!saZ=VFzߘxCA&:m~Lp&QWA@s`o@=( U9,n"ՇUp֮$)W^DTT\) RRVU#IIgϞٳgh6cۿIhXɓ'w ux|TT*annjZ E‰%W|yK+IFJ+j\Ez8h؞݁x3ԊCu#VE4Vn7WsjZ R/{qY v$vvvjl6 4,AA A}.hs.Ƚ}cIOǩTldT 5Ae\b?77Ebnn 8}4={ullld):NHH.t:]+RBAVՙ>_sUKS@D*~]sr/ 2*HpbO#Y.ևXF#[axJ'ڳS?mYt`FIi$?RN#$nG/ElM?O_Wo~cvQ (-Ƨ(Zfffn3bvn%Wt!=9"E1a$mJšKaD)ehtEuH+CZhlZz)&F4 Ri`AM j̙KQ #yAսϹ+DZJd}86~VRV:"Y$_r Ν;j5u њ C~vV V J% f4JbѢO3{ u_uA BbW8<cRȿ"5jsaz@Tu*Gf>2B*YT/x=g#ɢjp ɢ~]!{?I(A{AJd+$#j4a~~>[};&%L?bԦIBW=uJs&.]t.{Q7*YVBZ\6vw;,wyQQW+E5H%˽ǤV*1EtpvGdKX$Uw=qu$BJ*=Rjj|2U7~677l6d)"*˹=R5U?Wa@%%(;y.q}yc*>33Z3ezN8qVw.x잔8)ݻV}gitXA dHöh4P0??ZN NY'ݏ+"rBVOkIQb\Ȗ7L1[<e#YKTȉ&} :a-sMaw;Rպ;☊[? U*PQ'~Gњ񳳳B^TnZ6Dq`u:=q[#Xo}wY ~}8J?kisQ\2W;R]iv 0v I ZHNp o|L$lSWꂍmd4& W)~poOR¤*6I0Zh  5f@w\T ˠ捍 j̕vB00TQ8qDC3ZKP#+5Q"#;T0?'Um&BPEs3TѕT;;;hvhZo40]|ۅ`Gw~G'6@:eEJ>'dl_ɕ1"p:janf4/PRͳ$ mGJIH@I{EA'[=ύơWTҾmZfo]I}=  U`R;J,V\FVV{UՌ u:aPpӢ rkk juOREFT/H#g4Esc5DH,%Yn>Qّɻ:vnJȾSXޅzjŦNTy(Jyȗ?U{+D PkTBMHJ@lz]UbupshMifߥXV3AZq*^ rq=xA ^IVD|O'An$x+,Ѭ~uJ@w 14U;E̙a rr=_ʦ*=&M#{O"NK{_}[ͯh Xz~j3vq\ ._~JS`h[rS 5vƐ Y1133) LPL2zՅ"0]? Sc糽FJXdpy'Zj@Ou1T4'h;Iڨja}"`6bjjj`MNʗWK% @bnn;;;Y<⸉*{|m+)h:\+)Ss?9jX]]ǏGTrkkk8yr Z(QUJRFlDn^c~~sssY`"j6ARɝ9@L]~)RV=60S3zoj,A]|N%ER-5=u*l,1MP$D(RU2IYtxָ5+"-F棅" }BK?Uf!vBq`Ezǫ^h$*9:H݄LK[zu:2%3dK}3ϒU ~>v#Oℜh 'ZN:#qdy cb$wUuuh{<&4X1J)@8rXx"JBd!%HyBC"x!0"HDY r&篧g꿪{huWs{nTݷ=ug0"1p?@Y0W`Nj? =`Am{X'3?ڃrbmƐT4ykuyM{t6E677ի~n߾e.\|dڵWmss3\ˀQD ,3 ;kZpnDUByvVVV<ڞpgHQy h.,|S`phŔ2eeigfmߴyMS,H+o1}Nܾ}Ӯ^S21+vʳ+2uHdF efS";VZ zrBy3[ %v 1lnO> m,b)cD (h2?)c6/ew(Ȃx}T֐lLcuO db)+ ߺ h-XfyOڄj^֤P(X $X"(\.`VVVij~ߚͦ?s2/RW:+RūYliLKwRά#; ĐB NMccXIcfjt~@vױG5s3 eDZ06<汻 ĂB 1FU3mIg3ytZ0 G=DNT,C 䈃T;΄"0By 1Xq-؉)ҘҙXyxlUl"I )f4/DBYy8֕ ="P`SF؅ei9%ԑ8-;Je?X$vD',jv-П{Gb RGY&ARy}-Z?k^Ҝɒh-Es໅Qvwwy6GhY a|u XhaKO;3(xR񀀮YS\uVef}<Ɗb=h-+|iQP6 tئXh1 SX-NݙyPO<&$&^#lz=l,<:h|(K3LSŒ`G+ 6K~``V H6 `r݋z=+_uk,sξv5v`Fcgͦ};߶k^[p2Y<4@Kf&G+|ov`^J`fuAqVRzggg:ښj؜ 3mu\= e0Y+fƁY+51I%>im;PJcPbiT3p`@X ] ZT>hOkiu@hiS ݻGձ:yhƘE[oi=V(nLP|}Şxy/}O|-v& t6Y&M|qJpζ~1VPHmk4a%v%MY`tu'mR>>yfIx M7ưh]1HRsuq AA939샆6=,5P+2Ot\p/X_x[ bG'o2!:,; cT.KݮaRifSُ~#Ї>dԧg^z^~eg?{bgsٛoӿ G^hf𨂟6Cr;OVt:':V| +JLp<v tԔ0+J^`1}*خi,8RMby ϑQEaLp(ehikcuWMlft? omOn0>ִP.(70x<"47|yp;ep2o^~e{_=|>OgO$Llrщ,D]*e; vV+]jYݶnv'rOvxHU=1[E{ʊ6.hWX,V\_ @~GY;NOer)R0p^|@[c+,0A~?׼~Yo\`/VUrMw-Sޞ}ߵ~'?I{?}^h. %b)VT&+JణlB˜6~-v?W^ ~Bd>yh3Yժ%C% ,Lp&yu M0cJF+sf:yQeCڎM(Q=6 ~x) H< PYT!z@mqZ}Gy nl2?!0-113j6xTGy+V6uSIft1&67S8]%QTsw ϛC%bA=-irTRA T1cȂBs;>@y2V eӱVs(f>0Օo-w&<ʉ-R>i\no'i dŀYֆ1` طve'֟|@-," U4tϬAYӔB\GON1 {uv` ;߳LY03[&'CК,2yR6x593j‡BMhv b3GJ?mUp@hwUl&di@VDxJic̰x LY.3BL^l>TNۄ>99|ޞcjjJ%YEPi"ۛC={a}K_ n^xޙ.V&xZ4 Z+ޣJWb:ckZA DVB171+ \#8`i{x<,~&.|9bLG!~FMYh;@l%h1p)J]{[+Mot|1 /vww-p8nv=3ScRT wE2t>9{ҥKVTŋo~sf2d@!%0-+zn끝0bE!C%bvvv)T*%#|i̊2,{ooP,*wr?n;={7'3!GYx3nZ 1}ҔlZOӱvm8 B]zaQ@Q_\%f Se, 4s|wY_3*MCǜq+ Юz?/ P/btJ`#8>?&BY($'Yu=a+M%Ddm|y8ߐuQ!y[Vr00ds,lL4!n|ͦ/R)U:1`\oOig!B[21HcefӶ޽{j%Fv}t:/+[Sa\~_ &v?(P.ϏMs/i~s eqX63rZ-wݽ{׶lZMJ\q >U7VxϹ`zFԔf T汧鲁V&dS,h-}4Y՚`p3&VZ)&T1ajd3Ƃ4^X<8Y(`>Í2{>L^+ng;/<ԏA:н^n߾m7n@ݻL Eߧ€+^5zqa/:NX@;c?#cJ3$ZX9Ż_*i:"(B6}@A.0;X]!/&P~6#*.&-79b:kj:bAzJ1Ӛ ooZeI_Q2.C1(+u0Xղfin Mk6dNo }vDpTػB#ﭖ?b~[\4FcT4;-R*̙.V*1'9E*I%moo[Z QZ@4sN.wp4FǦG rݻ&k63`5`phVV, 6K@H^GMl5 *lbmPV%<0@<Т *\MdˀLX"(p=#w]1k۶e;;;\m@YdC01r%BN(#y(* pY@Ch؈pNj0;A ]y2&u;ڵWʕg\wq2CzkvE~,ZNxĠUf NPF=B>+\Z0FI߷r&aL2Dj^np1 Lc?jRcsZ1 fBdQ{4y+L' Na,bS)s2B{׳fi[[[vΝdhz;f6&pQ03qZ҉х Sg5{f&ړ/:urv+Rl/^sg@ai!샂]S 0Łuj6nnI#jY0s" ?34 +qf1917`)K2Dgg*4dy)\sgցd&S71&kÏ=9ܼynܸaw ;y!17MbxlJgQ'6=ߟ. 6h4ЪT*Hܾ&"xpMۆ7b~U64ht:@a&ANMB!,|&Bby)f*˅wvv޽{XfyVVVnP 0?1C$ J~LfUi@@b`_S 4^Y8/'f􀡚~>M^`o6„c1:hdnךfp qmoo'vNOe\oaZ7 ܊4??lT;X5D3b[?F+?i~Ϯ^Su-p#[^g׮jZN&|>`cH5/hF#{ݽ{ךfD73P \rlgϞ[nޞ59X+`4KT:1k檘;isT{ +'N Qoi:NX_im &)c@҂@͛vMIyY,Px׫@6)hu v=bwL1F⾴çѮ/tqhâo{vo:ɾκrY{khK 9^Atݙ|yU|8p<m;;;~` rlj5~d/ٹzbʩڑ'j_Mẁe{=3jZ\#3xm8(tԛMS;>0i} sl{{;qbx1ӔƦze,:xR,iSV L3s@˞ձ_=Ӿ2~,myoxg K\I֝hi=!8 yp7o$x1ðngΜ T*v`f :? %6+فcu.7KzZR,L3IzW` 1vT9ɒC3Lc uU E'A6tj `fvܹ/&qZZjL+Y@B0l߫RX,/)+2^چi#U0鍝9h'4m'3_1/iu6Qw;cBYq㏠a*W')Ν;!~Vղ^7]*ds&~x5of"DerDWpe3p8 G9sΝ;p:777֭[nB*Jb/LiS:(QլV%F]yfn4Ӕ u^fi;ǠVeb剉*04d ŊWH eͬ 8`I cwܱh1JQ۳-YðtmL qY|V#cc<0Ӧ,&,fu~)L[N$Ђx+eYdy"03ذ84k5$&E,ʠ¾QRXVz>q:${٤?PlgVkZjZ U@_ムhc7+ЊZVpJgo412ȇ<(ұg{l0zl =F͛UnÀ, Fi*,5(y xfgY\ػb h<\r"+ !pfIk~ߚͦmoox  mpuU9`yPѶQ:FՄU2U?ieEL-\݈1v\G[ːk^nm?h4fzlwmv.eˉZf+sy>)^hv 9s`~;S(^[  p>#CqOZDjjZ-]4e6cxҟez=3ZΓyӔZLr\.7 VE9"nN uV*6`cYE$es{z]cP[x X9^i`!o=[PǞMB`Gk_=zW_m?n'[L$'hyeIRQ19Ppo0īJbzݻg߷\n!"i>{ߟB!ԧ>e;Gà[_~ٞyhffO<}3OYd$'hejy 8B IU*t:u̒l0V~dn-9jPM 7(JuZW?Mp rX=eLoga@K=V('nEf~/1Yv/bu}|^Ɔ]~}yg2d@+3;8̂:o%zN&cgX@Ah4,֖ k6V.CAԬjPVݹYARYMGSA,-M@,Upc7+3Lb'=&i"p8Lʖ>^c 4MMge>,P?.+~z Aw J{b {uUIs;Y|GXyc($m_YSK[{˔e}sۥKRŋd>ɀV&f6q>Lve{g˿lЮ_nׯ_3gݻw'̍la&=3K4jr6 lkk˪jpj8y G2Ax 8M(gN+&|'`hq QA .\LjƚB4 +JQK2B5R|.=Uhy:382#gؿ*M:[X`aj| LGhCf`4rپomv۞y晥H3.dBa4[Ѱ .˗g ;ݻgZnݺpDvR)2TXf@RX V#m\}2Z NbSy2@aT+bcf9>tj<'L˨)=nj) rff,ƤZ16QA { " gV6KۏIZ4-Jn޼a=x&v TsL&q9j he'R[sVV5+GB!85x+ϗC6?x`c9<*ydaRMAV @ L*EW(M{5tlM3%@5bex*-il2i1 8C.Xt0Y5'7~YdoozM|ɅLO2I|oo/qRXV @y†۝{Ԭ= kZ~Sj1ӃSUGt1bƊLK&OF_rěB[LU1Aeb2Z /kgLF|M+8MYڱ8E,. ("@i_ّEeOigV*Ut {g3yx$Za b1-#n,Wcp4oK ˂ZUѰKY:C3fn7 0*6T*YOY^%wym4S4Elw=,(+iZgS,i4Χf_0ZmdْLlrAɗp4g6U(i@uBVfpݙURl^>b̒W/jc7ycbY ư(xruyX^/N=M1@~J^]PP%y1 ݏ08`+`(JPŏgeC<fqgg<<(9-;Әx@DS*jN-)|t&V2IBb &Xtwyvz)ZLrSPJt:{Pacph4J(34T굋Tſy x@YB!x(@~-2,5<업k`sPn/#~NaY_ l<=epXŀMk; +/lO6ݩIKchF }_,7Z8q~c& qċDPZPۇ2 4oezvګͷ+B<+=hdaF#Dvp8zpjv$z_8Nx>h9i̗ V(J~X~<kKV|ؠ1(HOwԔ@c4rX^E̼h}8|1b zr͟g.ɾ_+۷µz%6xٳg… vy[__~3~?0ZfZ1 ě\# |5:{&X9=sRcJɫ;+Ì׵,h;/ 1L4?Ep{n^vj3xGSc_|>`|Nbф90PV=/=J-<`ꕗ-vZX~S,r%[L,9j5k۶f?]xΟ?okkkV55aF&f&AYbF' |lA&/-JmdEq? jyl?k}=奣mq:^ڳ-خ?烊3<7UT*!Ȼ)yf͛!üfIW_7R1Z&vki𴀬L>yhV666vY[__jvz=k6cfG,yJp,^0Gv~wk-ϱl< +6&0rU͇:;`y(x1&I1F ʊJvh۶bN BqX6Ȃ]~WlĘc{cbL0$eC IxTU諫nΝ YJ%(vl6ZXr5aʘw  D&9]We4b +6{1vco@ ld<_5Y?hΗ8sqoX aC\nb!vz`ǁtX1u sZ~~ir9J`J63Kl+}s^ljMYtz)ݣsI&--ЊM'ENRJ2rp FoP@մx&(b62#V7xoy[=k=lN{1'΢p\y3S>fz?+^> [1-6 Q(@ 5U,H '<ކV.çRP(ZnO_>iRX\r;~L C~wtNɣ+-b9fēT3 [˗̙3g1payţR'aX=Ѕ{x( sJ9{iceeH0r^;s:jF-i1bO1AUOIpgZ>[Ugeeźnl*t1?E #Zٓ6fC b (*YoBgP(ؙ3gٳaZ--(p->MrPhwVf$FsJOD9 -0]=Ę,1e>j!9ܯYM-i ƱBvbŨ1 C\?]QH \2G;Wmmm5_"Qe [ Ĝ&@_!Y/oYr7.?3ml/BFګv[vfzRڻ]KɲZ,⤀Uv j5k4fjՊĮbhF#JSҺ# ,H™a_, ęmŔ+| RPLi^9Tu6T,ć ^%ƈLu:\B5oLVVm}}5PF;&Ulł*70;M3znZ-N< eC@?5ːk^nm?h4fzlwmv.eC ڣ T< ^[R L{Y}@=(6M7>ϧF,xa.Ą1M^{+(QS m13[iZZG70c:f,fsl:j&|>Duo4agΜZf>\~ߚͦ}<>{:OKG q6C܆@1ÀQė!o=[PIP|}ŞxL|{߳ׯ[Ѱߴ Ν;g=گڡd)Aϒ紕y& ^UլRcO  F;6|4Kk䉕+O]']#E?<c@q:1}( h{q]c1m|gTA thAЯ6M̱/B<&ON\.gZjUΞ=kgϞ lllعs󶱱az=,p9s}1+?=6b)>̼{y"9ߍ]+xlRifSWs玽=ﱕ?#?>{ꩧlkkˮ^j0W]E~>:JFKWA7I[KR8LQbZ5q~ Nb!OG7Q3`I p IDAT?Vnx,ofUHඊ)8ߧSnZޞِQӉ T=h;='p? :cs炏d4xpx'8x<`Fm~uH2y~y,i20y/})+WOO7ߴ . _ s-Rֲ8^E']F#+aSV C" jƢ'Z}wR `0bȇ\L8)9rZ&EocZi]<P{]ۘ㵡N,T\>0 2fI'#jho/JG*}  2t|_ A,x=w֟Y U}Xʰli6vܹ?,3K.#т9J9l`nјZ2\x>V,Y1J>Q`v(@+*WI?= d鳚>AցrĔV98@- Rmo~>V棖vw8e$-Z1YNr~8EL+Gr!gHZW>*~{{}vo֞+y [V=F =too/4jZaX,<bb- ŀRXy2*KSәK/m+4ϫr32-lмM+J "P+̇5c+`xr`f1 ,T`O/42fXdO|'d2-'zn|;-ۿ~>gww^xp_R5 K/^?lzQt(IOvy-S!׀`kkk#OtBԉ.`T{ G +.bQp9rYɟ?,PlDLY^!i?p<"1hdk$93"wb1Fk<dpY``JJpQ۝:iCf 먻gq#, {~,p>1={l1V,RLZ**kR`P]P/"PW*3ZBJ%wWbRFeQX nkv;8b$@+N۱& (xzhzۀe`Цc+*C5?r&O1^纂d6 gk*0@O̼`,Rzu'Of';F#vvfr=Sd8 *Jjs|8tLЮ W3O&|LonK`NQs$/˒eYޞݽ{{1FK/oaofK/4Y$Su2:e@eމe^fkQ+3X2ii %+iN)R0l1؊ V\/~v@4/[Z\1>ZvWX9-ak `~}}ݪժUUݵJbJz dee%oT*!o(cxwww$޸wیc {o~k˔Ehd7muu՚ͦ_]t~~}{߳o}[6lssr\a>KV|m?kE sӀbʛ̖v+8RoG\n`ë`Yh*-CLƍs;- lrJ_Z6+OZceaq,8ͰAF`a}8YCm?1F]Ύz ɡ8Y6sBl2>ɟY=ɰߢey=sv޶__o~󛶲bVgJRL/КS)ä+e QcfeT>0% CwWǤp͒;)viӭWwe/i@+--x5 M[@x,)يB͟ǀ?WiyAb&CCr9+JV.lH`FjZj 0`"3?jYlZu@$q7[7 bqy͕hY%~3ummm;;sL^1K=>MLӆWcuu… v̙pf C-3N&bJR}}8-56xc둦28Bд$ƎnZWcw 5-WX/JFS\n/̖ف2%'Yx|s}tp??^ld,˶|jZmJ?\.v"=0p~}$̙ޢ b4  hǐ]}b2R͛7w=OvwwƍVTŋv3X!105*.KxZ]]RQ']^@c"b cCX1}d1Oo uO3wx`+hդl&oMO}1gHGՄ?P. Qg)m[|Z2ΫVTl4;y퍝`0؄y3t1ec6Z\ONtޞ֛vbf2d@SDWY˥㜴jure-VŕJź@A;M@ t25؊>~>-mə4aF g38_eDG(cǿ9L O.eNpٙB2k xevN@O<Ƞnoo/D.6񪿝?1J]hq)Ŗw2~_u +]=<<#gQ\Xe2-0!lllXј`T7Z'Ibi**={lZTjF#y"F] \x4RPEt`*C9=63zƘ?N&Uze{x^ a%TX>MO<|ʖͳ@C͸1R`8`0lj` Zpݵrl acKktC컅tq:F h-@tE>M> :ybR.J?oV _ X X1?-~>6szh/hlyuՅ}[(l&L .%h#|b3zP-V^k_rxm前ꟄQ ʠt~L372  >`FX-r_0qB:xc. 7x~eI677ի~n߾e.!Y^g׮jZ D24F0iW*pvap9v: 4`E4#V@HZi#ҀyLO\^] 0{1Xx|(3ܧm̌Y;eh14 p)Q4̅R) / bdyv0 7nK.'mJ ~gJ.p_i۷oիq@V&Wjb+#.sLA)ҶS0P ϰ5c-T?K|`9=FRr\cƱ(@YG9gVXpF/sUC{̧GS5X>40+'A~N˕o](ԗ ΠJ${:q(@2-)ӟc. =0#=H,?~~{e2{fFA6p?2xVGͬM `5-?&2yFHCY do"oLZr1 f~v%vr ebs#61R =.Z(-dlɀ0 xCft5 Nz ӑT$-)ӴIWRcxEMfx`"m%i4!=? l,G x}կA~]4 p1pa!'fdyǚqZ#S{e9UZ^+iq9>302m1aX!UpA^ ihL2Yd@kA1sBbY\c[xca!3Ьe@+c h@VU&"p@ 3&e4WL1@l~{,䰲<:u!@;›Y"$V^ݮSQV~Z-޶^gRVWW۶mV+-5&!,c617PV1(fLE2 UU2FZ8LcfaeY-}Q%ƿ5 11`z!}U|i^ڶ b&֦}t,4(P\D x=́w ~YhXŷ,YicFDA9tJVrlr98c Mfٴ|>vΝ F )u$m҅32ԱLgXp4kjׯengg*]Oٕ+.t,[2 ɼ/!@Z-5&L6r\%ޚq|$S1+4fK=3c ({3:\WV. 2S+wJ&X?y`W˯? ǀ}` m3;Lʻo߾m[[[l6C:X>LbbUfÊ0Cy,;5ŦYYкv5v`Fcgͦ};߶k^[p2Yd@k9,;uIc4Jr\PX)6;`Ϻc'\ pl @n<'MEC |NQȊ0yaDSz>oкI[}Y[oi=V(&B>׾{3o}˾o[׳__^zɍIe,QSW_}7TQ-~S:famRZQV\3 +G5qz^ZJ6)4eU@!?(@3K뙅1 s"Z IDAT: Swl!,`6d7%ݮZp! 2lssݻg΁Kw+97[\Nry t' /t0B;t0V*`>VK vX,5c+J3̾/__ٟٟƆ}߰>L3|ceffGӎ:hIX,Z^u؈ޏMuէ-\JR%3A18J{=N˘cb0ph^Ҁ+1/3c3! <^~bP`CDA-LMu,ʀihajX,V>%VղĂc޷Hvծzvsgg18cl1P+Xe",'@dAr#Y{+l$$DB( /%JL< !gݯGwW1ݫާz=TS>o}++ Hu`y ʑ|^CuYeٰ!7JmgHˣ*^Swr5zO{~={077]_8:7bG0::q&Q&Ӌja;$뒲Oa ň^&iƟǂ.ce{*# a\1eOo({:]+ar\P1ibia&>$Z$xx8,~s X^?,K-RBoZ}djdDWV z=|jZJr3t*|_d2fݾZڮt0tn~\krrj5_T88p;BӬxKE׍5*Fu-6uSfbߓ܏V1% \|H@!rgt&1c#nH1&3jt:A] 1c_ڕu#$XBM  tj@DVux=L ,OkcD- *B;Ύ{8(fXc!56b4hS-{LŘ ҡ aϛܩbς׎Al{cJBnS5/| G"Jr\ 1I^K}Tضga֪vIE l:* r?r5$Q-Ҙ+(Ubuo>S;pI{C ;/b~Иf^{ Yuۿ[߯``cc>(/'C R uK*֑%=Bde@65JZLN,yo1g[bfaɖd((Njh.d{>WM A[T9#Ֆi>-;{KjSNa48}⻺跭7ߌ??ƻ.iٟ&뮾^۱;Dsp\?^nU<3c8¤+ѢK#D;.C;BY'^Gst:גN)DA)7ʺbFܪp9-1tyUt^KnݪVi(ѢcL^tJ4֍Eb 6UÔ(h}ږD6۞m]6lX(R?yppp/4a'hG˧DKUiu'jX>q%UV]}v.mJXPa###$UgTCW{ ( T*?Cr-}wD\VEKΙT3GPVWŎ{hh(1uXEvvįPmz_Zϫ[ 515"\Fz:DޡLKl{l`#Z>UT766:JXKUlP#O%ö"$UcO'JNb꠭Ws͑|#,_^Y8 *a,Ma'~:پ27;%-K, w-0/F*-1C3ISukY$^#VJTС.osIY"o?VgWfJ%a||fARU+&3žTxY&]5AIJ9@-_%Y'Oހ_~ z},jꫯzVN1^Ƙb;ML:60'f)v4l;c48>f,ѸΖ3߭bʉ$%fd屄e ]^GU[K -ɢ"Ju5a||G111fMĔBB#ں]ژ ]0=F}t#[LYUN@ e lLVZB\ HLS*MN t&.ĶVeH`˥FJcXWh;$bѥøyΝ;څ\'pԕchbtS,Γ.2&:"vGk;lvDIZ+T 刑,-KLqֵ;VW#1$62]ߒ9uCcHx$U1FV-!v>Fֱcp@fQTPThqƤ?I"nV7n۟nI"Ѻ#`5%ZkZpRz]1X$Zc_¾bQYާ+QEB|vh1vorr|>j5&j\122n7xsϳ9. }.cDB b vd3qX&ͻCTҥ/utSVKTN ԋ(b(bPF7Yڍ(pc:'~Pk98@ippb1?~ǎt R:Í`cc#t06Оuk/MRI< xOjZ!݃-k\hպ3պS1d|ZPԅLz( VXYYARA\(677144ZEd20@mc?DB;nDqEK@ըcddSSS8z(&'': 6\ L-VqQ*VH*7xYE޴\$ZIJƱh![ټB6_%v1dy!K)Z2賳j!J%ĉűc055$ HbI9ڧ*>URXr* ݈',&dS2J,%$~5VbLu2)Z|ZbfiGmۏUtL||f12ئR`'''q1bffE6 Ya̺mJ*maǶm[/ fŞ-xRDKݞ7>6p#ld;ϊR۠*W:QqDU`*l!hVR`ee%tHaVViWcDBa:cckT->G똟zgGf5a5B8 4i+q1;~кTd]=J\cU4%5]F N=^>;N#]8vFsbff.'ZTKnJ$YOT.C r2LR(vhMJ-5KU%ȱI~9SurrG022|>|>Fènw$TnGoG  m:cnss3-Q{m-ڍ%]ڦIemORbx2{KRA">s;Iz~ss\А,bspױԜh`QX;x;"Qڑ8 X*V KKKX^^"2j+,;?k%!`GFh``koˏF+V^OgcƋWkZ7֣-YĠNUHFfJkU(=pUCUASY sgff¹Ǒ绒,}~ZoV5HIS5ubuV!YZ*Y,ZlZGR[/q}I)c,yMjj;6: G}.czz-[F`~~>lsuaE;=4R٢lwʧ3AԎgMD禪 V5E낱d+[EEc]e5rBDZ DKueŌ5>th𴶃t:Q?~Ǐ,9a *X M]*0==G bDE8;h;gHUb+)>Җ3Ll{%1Us];b TVkl`eCkt2;ҔJHuS2.ǡ#Y+/4͉!@h,)ZL4oɊu7s1VCgjqlt:Fq6on`$vItKe똓 =؞;K]2,k.CPPm9<<ܡRYN%Z6`M,*]vݱ3?GF`¡.ʛf 199bD @06ù5lk$=hZI'V^뽂cla]VdYtafkC۳N$`l]>:gH*Ѳ cºA ͦCl6[DjdoM@PPeAU#qyIxND˖F+H\ VIT:[eAIIZV6Q1-d %/z.XSSSAuyXuadd$p_T*all GHH}m-Xc,%l>1nߗD+I T,'Vg}j?@72gY7uuy~3;Do)`~@hW4,v;LKBp 'Z{!*v[,c!t=؝Tv^SGږLuI؁ !fq#yUFZ xiYiDMn9=w:2[éb 2 9bCU”L/n_]]ER 9X?>>#GT*Db6( k17%Jb˒^cy2 IDATJېbs3՟3;Rlw[t./percܫ8:NjNB.[a uYm #l4W]us k8b2Ŏ,N'Y2;XilViJFm7B)\L Nmݰwlw'ObVVXZZBTHp266I7-Z!@و K|ԍe1&(dbL&ӡ:(Y\`kl>XN.MSS^y13辱$s!ލK0ލ/cϪ#=N4T*aRsze6VR[q(Q,+[w-N褶SX߻F1WJ>G;(J#YTvpHjLٲ:Sa6By;WKKK]WҪJ@|XX,&puMuĈ;G%g,.UlPUNv(:ktuT*T*j)2րp))fhh8q;IvLט3wMjnCڈ@bj!Iɕ] RϭdNzuDncsvY{O`}jL%to+AlbX{3F3`K.J%LMMd\jleeʼnn;X \QiP#4āDo[k`v{n`` ,Js)A{PΙD?"^*DO(~mm-jVWWQBbz {?IPT^U*1( U5rfҩƙ3mUSh2{Smcl{,iȺ>yr,iS,WEԺ܍|jy*YzL&zf~9 ajj ǎ 햹FFFPw&Ξ= ^E^\.nwqϥs7hp\ tdl#=**4݈pli|P4I"aJAKRz k$ջUbeo6`u ]-kͽł>1 %R;|gZV5cLlwr‚U,RMo:!A *aINsQ#VBRVw VG%Z:HK"tV, mۖR6܏h@˗qQ,--U)J?*y1221j}.cD`ELLLtt6@5ֈS5#3J%IȲZmdfuŎֺ$P`-Z n?j\4 IƗ0$˖-f0ѴeF$f|U1J BeH_Yiۢ ۍƫ$%UfcueJ [ֵnK5+rhzEI {=.e*nosiZ(h4XXX2VWW1118gE^}|ct:՞Ixޏ~qɞu|_*xGU s[:l1ʒ,UbnRmJ|uiɖ}nMhUDKc |\BJ"l=}t^26nc}ORtҌ~ʪkޯ$zÙ]+j}{{N?/~.?|W|h]gv{Vǃ$B :NLeI;Fui9ΖFK]2I$eRF5F5P1)S|TRKrkVaIu:SBe˧ϔhi/Kųvd),u s\{Q[nE5!19ܗV]jP.'krTkv`?1UΒ>;'Ju,wa>I`OhZxgCmGQV /W W3J"DCdBE 6md^KX16IHD*4f T%@n, ޷{$b;^Uau_}NJ8Z}x>.yNŌܪ*Z}jg Z2}[,GlXgU E;Jf`Ljhڶ; g=jZ=ٳ.@ Yfɉ'jXXX@>i/˗l6qVB JR+uح I?"4`}1:r(A-3%ZvV yccF+++X^^;`988Fνfhz': 7܀_|w_FӇ'i?-*C*ݐJmb'5pj44%Fl,h4APj:5.d҈U~7t.5I1Pコf0!iQG>b0F4y$laC\dŢ&lڑfbc077H *h$B [R ud(*o֍v9hq`a[7H>Y=wSkipVg8>cxg裏btto{~mGpuȐdv;r:CRm1LʟdLh4::Ok%,.{$&6M")jv.D;#V&MY%mORt1" ŔFZjL.M i^{ X]]2L vfɹ87G^٠wksd@m%X(iScəUS5K]J0\=Aթؠ1i<&t@d~c?SO=savv\3337:v'Z)=&Sc'F|>$FIVWWcjXeBIGdĔـpӭxmM"mhï5}* ݞD(kFϪz%,V*]a=r@^q:"x XZZ2f._*P,;]TvlYj I`l*ݞ^_]iIFn֒ؾv@a $t{LղK!4D^[Qg2] Ols]ox u_+Er+JR;~l6xױSN8D+6w(a6GU(ٲmolj*VUwII Ւ0 ^l')aI_އ<^?6+IJ{I7{5h:㵭SϯdƺcRA\Rp \.\.w(^󘛛o%ZT}=TKqX6i:xκggԫ`RsJجc{}tfѲ qlTN-q{8sXYZEfg߰_D+ҥ8vxAx"rw]H^Ǎ%=Gy>XR r!sd0P92Q!5 ѥ,KhǠ׌[b糮N[WVIucqX^I5ոfY`tt4[.]0$+g*jO,$+VΫkJ!SM0m͙6n#U͙ǘ "Fc16ݶT?V͍ bVtu}dUXmk7g~'o/z5[^}0%/ň>s'? &4+?U-Vu'F|Jl|U/D˺i}c#Z^{~Pg$ SJ;+ IhOFԎ|5 Ȟ~PظK-U!**jH5 ?$e;1:u;O,EWjpYLّ"^^[[CFV q\z#Ul{T7HmJ4~'nP~*apq֯^CU[*P+qy,.v#Zzm뼮%nVQ[.R rZ-4[`}p%h]'hQp%K6F) / $*)Au PxKrqR>apET'7v%zx&.[D9r_vj5${Z]]\geeoԄ\L˗177P$wIcqSZØj$dt'lllEel6DK'ukޟ%1-uԸi-"ъkߒ8 K*'ipN#Ȝƀ?~炬!aIAvM`Sl+[mвj<,1$ˎIR).h-62]'-`'ڻۅՕC7p)h]'݌RI=6پ9-n XmBMf;Uu!rAt(Qkcv{k|B踖Ul0v$K:k)kF=pL=@bb.CxZ(+S5p|V .nbF r* p$t#[N4nWsñ -wLųm=FbXVՊcHdڷ^ ɠh:2,'Z jB"h4[n".QRÒ=vhRPEXcACD#YZ>9B؉*ѽ Q7uƂc&N詛*U:1Jc]5l$Zkkka;-B2Ͳkk4aC9\.P&*N +: u3ވy|t@ruhɾ%6E]֛yHXR;厑2>a0Xe2-µQ?wql`ޢѨܹ ֎?Q- rV!VF:Qŵv9D54T;UVR U܇DKSaYy}ǦvK"Ie]uewܮϰSU/(N f]ϚJRd J,F'ˁh-..l6;( t6KRն*q7e>S;$ R ]b$VIb#D<`IlۻmV޾ImuW(2PlhMMMaaa?\PEL333r+D @770cWN$QG7b$ګ -NtޘN~"\}[-0+ ݩfz96 vﳽ%(`^罸~7Uln^u\=l Dҥ]˗v|> w}W߮8X__E h6]eLOϼeh4pχm>p8KuHfӘ9r8l::fsuNp8'Zp8}-p8>p8GDp8Op!677q\*ZOryt͸}.cDp8=gQG~###=SVo Ο?9NC+~t=N| IDAT_ĉ=_̙3򗿌U<x؎>cCu gvdF&pN:_w]^G'~++\r8ccvP>9<#җ8q|I|Sh9ñhWV >,z衰(*^x~ܒ*ኖ&2J!J]8v{npe4M<U,166 .ڎ&j/|g00BV܀lsra@Pr˾n28r8$Ϡ\bllɖnv1R`&뷾-iFGGl9oau866n/">/FӧO~*UW[*Y{U6o~O?4U:r8 =y,--asso{tW+Z[nwr_5EW :z Ν,rfffo|uN=Bߎ>d2Y|㏣P(8r8bhZpR]l_cuuNN=4N-> ~P(t8"rt";.^\.?~|8qcN=BB w//L |>dx /^#NtL«'ocD#lll`pp0`~_~ad2Y<sNvǹsgz^0cvNr-cjd?GE&3~:r8snǍ7ތV1tlBϥs7h9{*ZtGGx'd?fNT bbZqS=-~O~ͦa8[N=;N  :8w\.p^#0>kaa=qʯ Q* Nchhl6q{ycj__xᇱ|O<~i! ug#'|Aԩ[O#7~7} vb###r-pDdh4u1WFL&#<#뮻O~O\.3 ^z_p94k]Lq1=}ϟAQEqFΝLCcp?GE؏ݎa8yx{ރ\γ;G}ݏl6ss1==iΟ?͉&100BT*A2 ~g~.R)y ]Ò:Ξ=mOOn_^Ýwf&ubELLL\ _?ʗfqO̙3O}Swm;p8/Opߋ/~?rqUx+Z_7_Rx+ ?j ]/?l`quccp8Lӧr;Ŀ^)p86uWb~%)S9ۏYp83:_]wp@p:r8z\G/p8'8r8h9p Np8'Zp8}-p8>p8G3Ѻp8\r8h9p Np8'Zp8}-p8>p8GDp8Opp8'8r8h9p =3{p8p8GDp8Opp8'8r8h9p Np8'Zp8}-p8>a& ( ׺<p8/^ OuՍkZ@p8 LJ#w C׸hp8<>v%:kA<|x5-p8w8`}}Zp8U ~iip83