From 20bbb21f565191d345c6b7b59193f5344c5ac623 Mon Sep 17 00:00:00 2001 From: p989 Date: Wed, 8 Jul 2009 23:01:43 +0000 Subject: [PATCH] antigrain geometry renderer and static lib --- desmume/src/aggdraw.cpp | 177 + desmume/src/windows/DeSmuME_2005.vcproj | 12 +- desmume/src/windows/DeSmuME_2008.vcproj | 12 +- desmume/src/windows/agg/agg-2.5.lib | Bin 0 -> 1744994 bytes desmume/src/windows/agg/agg-2.5.sln | 20 + desmume/src/windows/agg/agg-2.5.vcproj | 883 ++ .../src/windows/agg/examples/pixel_formats.h | 184 + .../agg/font_freetype/agg_font_freetype.cpp | 1155 ++ .../agg/font_freetype/agg_font_freetype.h | 201 + .../agg/font_win32_tt/agg_font_win32_tt.cpp | 946 ++ .../agg/font_win32_tt/agg_font_win32_tt.h | 223 + desmume/src/windows/agg/gpc/VERSIONS.TXT | 123 + desmume/src/windows/agg/gpc/copying.txt | 22 + desmume/src/windows/agg/gpc/gpc.c | 2472 ++++ desmume/src/windows/agg/gpc/gpc.h | 133 + .../windows/agg/include/agg_alpha_mask_u8.h | 505 + desmume/src/windows/agg/include/agg_arc.h | 79 + desmume/src/windows/agg/include/agg_array.h | 1129 ++ .../src/windows/agg/include/agg_arrowhead.h | 88 + desmume/src/windows/agg/include/agg_basics.h | 539 + .../src/windows/agg/include/agg_bezier_arc.h | 163 + .../windows/agg/include/agg_bitset_iterator.h | 63 + desmume/src/windows/agg/include/agg_blur.h | 1303 ++ .../windows/agg/include/agg_bounding_rect.h | 122 + desmume/src/windows/agg/include/agg_bspline.h | 81 + .../agg/include/agg_clip_liang_barsky.h | 339 + .../src/windows/agg/include/agg_color_gray.h | 423 + .../src/windows/agg/include/agg_color_rgba.h | 756 ++ desmume/src/windows/agg/include/agg_config.h | 44 + .../agg/include/agg_conv_adaptor_vcgen.h | 166 + .../agg/include/agg_conv_adaptor_vpgen.h | 168 + .../windows/agg/include/agg_conv_bspline.h | 58 + .../agg/include/agg_conv_clip_polygon.h | 72 + .../agg/include/agg_conv_clip_polyline.h | 72 + .../agg/include/agg_conv_close_polygon.h | 134 + .../src/windows/agg/include/agg_conv_concat.h | 82 + .../windows/agg/include/agg_conv_contour.h | 71 + .../src/windows/agg/include/agg_conv_curve.h | 206 + .../src/windows/agg/include/agg_conv_dash.h | 74 + .../src/windows/agg/include/agg_conv_gpc.h | 441 + .../src/windows/agg/include/agg_conv_marker.h | 154 + .../agg/include/agg_conv_marker_adaptor.h | 60 + .../agg/include/agg_conv_segmentator.h | 57 + .../agg/include/agg_conv_shorten_path.h | 59 + .../agg/include/agg_conv_smooth_poly1.h | 86 + .../src/windows/agg/include/agg_conv_stroke.h | 79 + .../windows/agg/include/agg_conv_transform.h | 74 + .../agg/include/agg_conv_unclose_polygon.h | 61 + desmume/src/windows/agg/include/agg_curves.h | 701 ++ .../src/windows/agg/include/agg_dda_line.h | 295 + desmume/src/windows/agg/include/agg_ellipse.h | 128 + .../agg/include/agg_ellipse_bresenham.h | 118 + .../agg/include/agg_embedded_raster_fonts.h | 68 + .../agg/include/agg_font_cache_manager.h | 418 + .../windows/agg/include/agg_gamma_functions.h | 132 + .../src/windows/agg/include/agg_gamma_lut.h | 130 + .../agg/include/agg_glyph_raster_bin.h | 164 + .../windows/agg/include/agg_gradient_lut.h | 253 + .../src/windows/agg/include/agg_gsv_text.h | 158 + .../windows/agg/include/agg_image_accessors.h | 490 + .../windows/agg/include/agg_image_filters.h | 453 + .../windows/agg/include/agg_line_aa_basics.h | 199 + desmume/src/windows/agg/include/agg_math.h | 446 + .../src/windows/agg/include/agg_math_stroke.h | 531 + .../src/windows/agg/include/agg_path_length.h | 75 + .../windows/agg/include/agg_path_storage.h | 1554 +++ .../agg/include/agg_path_storage_integer.h | 304 + .../agg/include/agg_pattern_filters_rgba.h | 132 + .../agg/include/agg_pixfmt_amask_adaptor.h | 249 + .../src/windows/agg/include/agg_pixfmt_gray.h | 679 + .../src/windows/agg/include/agg_pixfmt_rgb.h | 869 ++ .../agg/include/agg_pixfmt_rgb_packed.h | 1318 ++ .../src/windows/agg/include/agg_pixfmt_rgba.h | 2911 +++++ .../agg/include/agg_pixfmt_transposer.h | 166 + .../agg/include/agg_rasterizer_cells_aa.h | 764 ++ .../agg/include/agg_rasterizer_compound_aa.h | 698 ++ .../agg/include/agg_rasterizer_outline.h | 157 + .../agg/include/agg_rasterizer_outline_aa.h | 609 + .../agg/include/agg_rasterizer_scanline_aa.h | 520 + .../agg/include/agg_rasterizer_sl_clip.h | 361 + .../windows/agg/include/agg_renderer_base.h | 723 ++ .../agg/include/agg_renderer_markers.h | 711 ++ .../windows/agg/include/agg_renderer_mclip.h | 349 + .../agg/include/agg_renderer_outline_aa.h | 1847 +++ .../agg/include/agg_renderer_outline_image.h | 1023 ++ .../agg/include/agg_renderer_primitives.h | 229 + .../agg/include/agg_renderer_raster_text.h | 273 + .../agg/include/agg_renderer_scanline.h | 861 ++ .../agg/include/agg_rendering_buffer.h | 305 + .../include/agg_rendering_buffer_dynarow.h | 142 + .../windows/agg/include/agg_rounded_rect.h | 77 + .../windows/agg/include/agg_scanline_bin.h | 269 + .../include/agg_scanline_boolean_algebra.h | 1576 +++ .../src/windows/agg/include/agg_scanline_p.h | 334 + .../agg/include/agg_scanline_storage_aa.h | 824 ++ .../agg/include/agg_scanline_storage_bin.h | 595 + .../src/windows/agg/include/agg_scanline_u.h | 508 + .../windows/agg/include/agg_shorten_path.h | 75 + .../src/windows/agg/include/agg_simul_eq.h | 153 + .../windows/agg/include/agg_span_allocator.h | 63 + .../windows/agg/include/agg_span_converter.h | 65 + .../windows/agg/include/agg_span_gouraud.h | 181 + .../agg/include/agg_span_gouraud_gray.h | 250 + .../agg/include/agg_span_gouraud_rgba.h | 286 + .../windows/agg/include/agg_span_gradient.h | 373 + .../agg/include/agg_span_gradient_alpha.h | 135 + .../agg/include/agg_span_image_filter.h | 252 + .../agg/include/agg_span_image_filter_gray.h | 757 ++ .../agg/include/agg_span_image_filter_rgb.h | 901 ++ .../agg/include/agg_span_image_filter_rgba.h | 929 ++ .../include/agg_span_interpolator_adaptor.h | 86 + .../include/agg_span_interpolator_linear.h | 241 + .../agg/include/agg_span_interpolator_persp.h | 472 + .../agg/include/agg_span_interpolator_trans.h | 101 + .../agg/include/agg_span_pattern_gray.h | 102 + .../agg/include/agg_span_pattern_rgb.h | 105 + .../agg/include/agg_span_pattern_rgba.h | 103 + .../src/windows/agg/include/agg_span_solid.h | 58 + .../agg/include/agg_span_subdiv_adaptor.h | 151 + .../windows/agg/include/agg_trans_affine.h | 524 + .../windows/agg/include/agg_trans_bilinear.h | 172 + .../agg/include/agg_trans_double_path.h | 140 + .../agg/include/agg_trans_perspective.h | 737 ++ .../agg/include/agg_trans_single_path.h | 106 + .../windows/agg/include/agg_trans_viewport.h | 312 + .../agg/include/agg_trans_warp_magnifier.h | 65 + .../windows/agg/include/agg_vcgen_bspline.h | 83 + .../windows/agg/include/agg_vcgen_contour.h | 103 + .../src/windows/agg/include/agg_vcgen_dash.h | 99 + .../agg/include/agg_vcgen_markers_term.h | 75 + .../agg/include/agg_vcgen_smooth_poly1.h | 96 + .../windows/agg/include/agg_vcgen_stroke.h | 111 + .../agg/include/agg_vcgen_vertex_sequence.h | 144 + .../windows/agg/include/agg_vertex_sequence.h | 178 + .../agg/include/agg_vpgen_clip_polygon.h | 92 + .../agg/include/agg_vpgen_clip_polyline.h | 87 + .../agg/include/agg_vpgen_segmentator.h | 70 + .../agg/include/ctrl/agg_bezier_ctrl.h | 201 + .../windows/agg/include/ctrl/agg_cbox_ctrl.h | 117 + .../src/windows/agg/include/ctrl/agg_ctrl.h | 123 + .../windows/agg/include/ctrl/agg_gamma_ctrl.h | 175 + .../agg/include/ctrl/agg_gamma_spline.h | 100 + .../agg/include/ctrl/agg_polygon_ctrl.h | 171 + .../windows/agg/include/ctrl/agg_rbox_ctrl.h | 146 + .../windows/agg/include/ctrl/agg_scale_ctrl.h | 151 + .../agg/include/ctrl/agg_slider_ctrl.h | 155 + .../agg/include/ctrl/agg_spline_ctrl.h | 164 + .../include/platform/agg_platform_support.h | 680 + .../agg/include/platform/mac/agg_mac_pmap.h | 92 + .../include/platform/win32/agg_win32_bmp.h | 123 + .../windows/agg/include/util/agg_color_conv.h | 89 + .../agg/include/util/agg_color_conv_rgb16.h | 294 + .../agg/include/util/agg_color_conv_rgb8.h | 478 + desmume/src/windows/agg/src/ChangeLog | 0 desmume/src/windows/agg/src/Makefile | 59 + desmume/src/windows/agg/src/agg_arc.cpp | 111 + desmume/src/windows/agg/src/agg_arrowhead.cpp | 115 + .../src/windows/agg/src/agg_bezier_arc.cpp | 261 + desmume/src/windows/agg/src/agg_bspline.cpp | 289 + desmume/src/windows/agg/src/agg_curves.cpp | 620 + .../agg/src/agg_embedded_raster_fonts.cpp | 10435 ++++++++++++++++ desmume/src/windows/agg/src/agg_gsv_text.cpp | 681 + .../src/windows/agg/src/agg_image_filters.cpp | 107 + .../windows/agg/src/agg_line_aa_basics.cpp | 91 + .../windows/agg/src/agg_line_profile_aa.cpp | 125 + .../src/windows/agg/src/agg_rounded_rect.cpp | 169 + .../src/windows/agg/src/agg_sqrt_tables.cpp | 120 + .../src/windows/agg/src/agg_trans_affine.cpp | 200 + .../windows/agg/src/agg_trans_double_path.cpp | 282 + .../windows/agg/src/agg_trans_single_path.cpp | 211 + .../agg/src/agg_trans_warp_magnifier.cpp | 79 + .../src/windows/agg/src/agg_vcgen_bspline.cpp | 203 + .../src/windows/agg/src/agg_vcgen_contour.cpp | 170 + .../src/windows/agg/src/agg_vcgen_dash.cpp | 240 + .../agg/src/agg_vcgen_markers_term.cpp | 108 + .../agg/src/agg_vcgen_smooth_poly1.cpp | 230 + .../src/windows/agg/src/agg_vcgen_stroke.cpp | 219 + .../agg/src/agg_vpgen_clip_polygon.cpp | 142 + .../agg/src/agg_vpgen_clip_polyline.cpp | 86 + .../windows/agg/src/agg_vpgen_segmentator.cpp | 76 + desmume/src/windows/agg/src/authors | 0 desmume/src/windows/agg/src/autogen.sh | 20 + desmume/src/windows/agg/src/configure.in | 15 + desmume/src/windows/agg/src/copying | 21 + .../windows/agg/src/ctrl/agg_bezier_ctrl.cpp | 375 + .../windows/agg/src/ctrl/agg_cbox_ctrl.cpp | 219 + .../windows/agg/src/ctrl/agg_gamma_ctrl.cpp | 438 + .../windows/agg/src/ctrl/agg_gamma_spline.cpp | 135 + .../windows/agg/src/ctrl/agg_polygon_ctrl.cpp | 337 + .../windows/agg/src/ctrl/agg_rbox_ctrl.cpp | 330 + .../windows/agg/src/ctrl/agg_scale_ctrl.cpp | 459 + .../windows/agg/src/ctrl/agg_slider_ctrl.cpp | 354 + .../windows/agg/src/ctrl/agg_spline_ctrl.cpp | 412 + desmume/src/windows/agg/src/install | 0 desmume/src/windows/agg/src/news | 0 .../platform/win32/agg_platform_support.cpp | 1478 +++ .../agg/src/platform/win32/agg_win32_bmp.cpp | 625 + desmume/src/windows/agg/src/readme | 5 + 198 files changed, 74003 insertions(+), 8 deletions(-) create mode 100644 desmume/src/aggdraw.cpp create mode 100644 desmume/src/windows/agg/agg-2.5.lib create mode 100644 desmume/src/windows/agg/agg-2.5.sln create mode 100644 desmume/src/windows/agg/agg-2.5.vcproj create mode 100644 desmume/src/windows/agg/examples/pixel_formats.h create mode 100644 desmume/src/windows/agg/font_freetype/agg_font_freetype.cpp create mode 100644 desmume/src/windows/agg/font_freetype/agg_font_freetype.h create mode 100644 desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.cpp create mode 100644 desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.h create mode 100644 desmume/src/windows/agg/gpc/VERSIONS.TXT create mode 100644 desmume/src/windows/agg/gpc/copying.txt create mode 100644 desmume/src/windows/agg/gpc/gpc.c create mode 100644 desmume/src/windows/agg/gpc/gpc.h create mode 100644 desmume/src/windows/agg/include/agg_alpha_mask_u8.h create mode 100644 desmume/src/windows/agg/include/agg_arc.h create mode 100644 desmume/src/windows/agg/include/agg_array.h create mode 100644 desmume/src/windows/agg/include/agg_arrowhead.h create mode 100644 desmume/src/windows/agg/include/agg_basics.h create mode 100644 desmume/src/windows/agg/include/agg_bezier_arc.h create mode 100644 desmume/src/windows/agg/include/agg_bitset_iterator.h create mode 100644 desmume/src/windows/agg/include/agg_blur.h create mode 100644 desmume/src/windows/agg/include/agg_bounding_rect.h create mode 100644 desmume/src/windows/agg/include/agg_bspline.h create mode 100644 desmume/src/windows/agg/include/agg_clip_liang_barsky.h create mode 100644 desmume/src/windows/agg/include/agg_color_gray.h create mode 100644 desmume/src/windows/agg/include/agg_color_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_config.h create mode 100644 desmume/src/windows/agg/include/agg_conv_adaptor_vcgen.h create mode 100644 desmume/src/windows/agg/include/agg_conv_adaptor_vpgen.h create mode 100644 desmume/src/windows/agg/include/agg_conv_bspline.h create mode 100644 desmume/src/windows/agg/include/agg_conv_clip_polygon.h create mode 100644 desmume/src/windows/agg/include/agg_conv_clip_polyline.h create mode 100644 desmume/src/windows/agg/include/agg_conv_close_polygon.h create mode 100644 desmume/src/windows/agg/include/agg_conv_concat.h create mode 100644 desmume/src/windows/agg/include/agg_conv_contour.h create mode 100644 desmume/src/windows/agg/include/agg_conv_curve.h create mode 100644 desmume/src/windows/agg/include/agg_conv_dash.h create mode 100644 desmume/src/windows/agg/include/agg_conv_gpc.h create mode 100644 desmume/src/windows/agg/include/agg_conv_marker.h create mode 100644 desmume/src/windows/agg/include/agg_conv_marker_adaptor.h create mode 100644 desmume/src/windows/agg/include/agg_conv_segmentator.h create mode 100644 desmume/src/windows/agg/include/agg_conv_shorten_path.h create mode 100644 desmume/src/windows/agg/include/agg_conv_smooth_poly1.h create mode 100644 desmume/src/windows/agg/include/agg_conv_stroke.h create mode 100644 desmume/src/windows/agg/include/agg_conv_transform.h create mode 100644 desmume/src/windows/agg/include/agg_conv_unclose_polygon.h create mode 100644 desmume/src/windows/agg/include/agg_curves.h create mode 100644 desmume/src/windows/agg/include/agg_dda_line.h create mode 100644 desmume/src/windows/agg/include/agg_ellipse.h create mode 100644 desmume/src/windows/agg/include/agg_ellipse_bresenham.h create mode 100644 desmume/src/windows/agg/include/agg_embedded_raster_fonts.h create mode 100644 desmume/src/windows/agg/include/agg_font_cache_manager.h create mode 100644 desmume/src/windows/agg/include/agg_gamma_functions.h create mode 100644 desmume/src/windows/agg/include/agg_gamma_lut.h create mode 100644 desmume/src/windows/agg/include/agg_glyph_raster_bin.h create mode 100644 desmume/src/windows/agg/include/agg_gradient_lut.h create mode 100644 desmume/src/windows/agg/include/agg_gsv_text.h create mode 100644 desmume/src/windows/agg/include/agg_image_accessors.h create mode 100644 desmume/src/windows/agg/include/agg_image_filters.h create mode 100644 desmume/src/windows/agg/include/agg_line_aa_basics.h create mode 100644 desmume/src/windows/agg/include/agg_math.h create mode 100644 desmume/src/windows/agg/include/agg_math_stroke.h create mode 100644 desmume/src/windows/agg/include/agg_path_length.h create mode 100644 desmume/src/windows/agg/include/agg_path_storage.h create mode 100644 desmume/src/windows/agg/include/agg_path_storage_integer.h create mode 100644 desmume/src/windows/agg/include/agg_pattern_filters_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_amask_adaptor.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_gray.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_rgb.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_rgb_packed.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_pixfmt_transposer.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_cells_aa.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_compound_aa.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_outline.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_outline_aa.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_scanline_aa.h create mode 100644 desmume/src/windows/agg/include/agg_rasterizer_sl_clip.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_base.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_markers.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_mclip.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_outline_aa.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_outline_image.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_primitives.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_raster_text.h create mode 100644 desmume/src/windows/agg/include/agg_renderer_scanline.h create mode 100644 desmume/src/windows/agg/include/agg_rendering_buffer.h create mode 100644 desmume/src/windows/agg/include/agg_rendering_buffer_dynarow.h create mode 100644 desmume/src/windows/agg/include/agg_rounded_rect.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_bin.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_boolean_algebra.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_p.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_storage_aa.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_storage_bin.h create mode 100644 desmume/src/windows/agg/include/agg_scanline_u.h create mode 100644 desmume/src/windows/agg/include/agg_shorten_path.h create mode 100644 desmume/src/windows/agg/include/agg_simul_eq.h create mode 100644 desmume/src/windows/agg/include/agg_span_allocator.h create mode 100644 desmume/src/windows/agg/include/agg_span_converter.h create mode 100644 desmume/src/windows/agg/include/agg_span_gouraud.h create mode 100644 desmume/src/windows/agg/include/agg_span_gouraud_gray.h create mode 100644 desmume/src/windows/agg/include/agg_span_gouraud_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_span_gradient.h create mode 100644 desmume/src/windows/agg/include/agg_span_gradient_alpha.h create mode 100644 desmume/src/windows/agg/include/agg_span_image_filter.h create mode 100644 desmume/src/windows/agg/include/agg_span_image_filter_gray.h create mode 100644 desmume/src/windows/agg/include/agg_span_image_filter_rgb.h create mode 100644 desmume/src/windows/agg/include/agg_span_image_filter_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_span_interpolator_adaptor.h create mode 100644 desmume/src/windows/agg/include/agg_span_interpolator_linear.h create mode 100644 desmume/src/windows/agg/include/agg_span_interpolator_persp.h create mode 100644 desmume/src/windows/agg/include/agg_span_interpolator_trans.h create mode 100644 desmume/src/windows/agg/include/agg_span_pattern_gray.h create mode 100644 desmume/src/windows/agg/include/agg_span_pattern_rgb.h create mode 100644 desmume/src/windows/agg/include/agg_span_pattern_rgba.h create mode 100644 desmume/src/windows/agg/include/agg_span_solid.h create mode 100644 desmume/src/windows/agg/include/agg_span_subdiv_adaptor.h create mode 100644 desmume/src/windows/agg/include/agg_trans_affine.h create mode 100644 desmume/src/windows/agg/include/agg_trans_bilinear.h create mode 100644 desmume/src/windows/agg/include/agg_trans_double_path.h create mode 100644 desmume/src/windows/agg/include/agg_trans_perspective.h create mode 100644 desmume/src/windows/agg/include/agg_trans_single_path.h create mode 100644 desmume/src/windows/agg/include/agg_trans_viewport.h create mode 100644 desmume/src/windows/agg/include/agg_trans_warp_magnifier.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_bspline.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_contour.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_dash.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_markers_term.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_smooth_poly1.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_stroke.h create mode 100644 desmume/src/windows/agg/include/agg_vcgen_vertex_sequence.h create mode 100644 desmume/src/windows/agg/include/agg_vertex_sequence.h create mode 100644 desmume/src/windows/agg/include/agg_vpgen_clip_polygon.h create mode 100644 desmume/src/windows/agg/include/agg_vpgen_clip_polyline.h create mode 100644 desmume/src/windows/agg/include/agg_vpgen_segmentator.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_bezier_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_cbox_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_gamma_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_gamma_spline.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_polygon_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_rbox_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_scale_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_slider_ctrl.h create mode 100644 desmume/src/windows/agg/include/ctrl/agg_spline_ctrl.h create mode 100644 desmume/src/windows/agg/include/platform/agg_platform_support.h create mode 100644 desmume/src/windows/agg/include/platform/mac/agg_mac_pmap.h create mode 100644 desmume/src/windows/agg/include/platform/win32/agg_win32_bmp.h create mode 100644 desmume/src/windows/agg/include/util/agg_color_conv.h create mode 100644 desmume/src/windows/agg/include/util/agg_color_conv_rgb16.h create mode 100644 desmume/src/windows/agg/include/util/agg_color_conv_rgb8.h create mode 100644 desmume/src/windows/agg/src/ChangeLog create mode 100644 desmume/src/windows/agg/src/Makefile create mode 100644 desmume/src/windows/agg/src/agg_arc.cpp create mode 100644 desmume/src/windows/agg/src/agg_arrowhead.cpp create mode 100644 desmume/src/windows/agg/src/agg_bezier_arc.cpp create mode 100644 desmume/src/windows/agg/src/agg_bspline.cpp create mode 100644 desmume/src/windows/agg/src/agg_curves.cpp create mode 100644 desmume/src/windows/agg/src/agg_embedded_raster_fonts.cpp create mode 100644 desmume/src/windows/agg/src/agg_gsv_text.cpp create mode 100644 desmume/src/windows/agg/src/agg_image_filters.cpp create mode 100644 desmume/src/windows/agg/src/agg_line_aa_basics.cpp create mode 100644 desmume/src/windows/agg/src/agg_line_profile_aa.cpp create mode 100644 desmume/src/windows/agg/src/agg_rounded_rect.cpp create mode 100644 desmume/src/windows/agg/src/agg_sqrt_tables.cpp create mode 100644 desmume/src/windows/agg/src/agg_trans_affine.cpp create mode 100644 desmume/src/windows/agg/src/agg_trans_double_path.cpp create mode 100644 desmume/src/windows/agg/src/agg_trans_single_path.cpp create mode 100644 desmume/src/windows/agg/src/agg_trans_warp_magnifier.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_bspline.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_contour.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_dash.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_markers_term.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_smooth_poly1.cpp create mode 100644 desmume/src/windows/agg/src/agg_vcgen_stroke.cpp create mode 100644 desmume/src/windows/agg/src/agg_vpgen_clip_polygon.cpp create mode 100644 desmume/src/windows/agg/src/agg_vpgen_clip_polyline.cpp create mode 100644 desmume/src/windows/agg/src/agg_vpgen_segmentator.cpp create mode 100644 desmume/src/windows/agg/src/authors create mode 100644 desmume/src/windows/agg/src/autogen.sh create mode 100644 desmume/src/windows/agg/src/configure.in create mode 100644 desmume/src/windows/agg/src/copying create mode 100644 desmume/src/windows/agg/src/ctrl/agg_bezier_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_cbox_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_gamma_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_gamma_spline.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_polygon_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_rbox_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_scale_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_slider_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/ctrl/agg_spline_ctrl.cpp create mode 100644 desmume/src/windows/agg/src/install create mode 100644 desmume/src/windows/agg/src/news create mode 100644 desmume/src/windows/agg/src/platform/win32/agg_platform_support.cpp create mode 100644 desmume/src/windows/agg/src/platform/win32/agg_win32_bmp.cpp create mode 100644 desmume/src/windows/agg/src/readme diff --git a/desmume/src/aggdraw.cpp b/desmume/src/aggdraw.cpp new file mode 100644 index 000000000..f295ca44b --- /dev/null +++ b/desmume/src/aggdraw.cpp @@ -0,0 +1,177 @@ +#include "GPU.h" +#include "NDSSystem.h" + +#include "agg_renderer_base.h" +#include "agg_renderer_primitives.h" +#include "agg_renderer_scanline.h" +#include "agg_bounding_rect.h" +#include "agg_trans_affine.h" +#include "agg_path_storage.h" +#include "agg_color_rgba.h" + +#include "agg_rasterizer_scanline_aa.h" +#include "agg_scanline_p.h" + +//raster text +#include "agg_glyph_raster_bin.h" +#include "agg_embedded_raster_fonts.h" +#include "agg_renderer_raster_text.h" + +#define AGG_RGB555 //pixfmt will be rgb555 +#include "pixel_formats.h" //this file is in agg/examples + +// The AGG base renderer +typedef agg::renderer_base RendererBase; + +// The AGG primitives renderer +typedef agg::renderer_primitives RendererPrimitives; + +// The AGG solid renderer +typedef agg::renderer_scanline_aa_solid RendererSolid; + +// AGG always wants a pointer to the +// beginning of the buffer, no matter what the stride. (AGG does handle +// negative strides correctly.) +const int stride = 512; + +//width and height of emu's rendering buffer +const int width = 256; +const int height = 384; + +agg::rendering_buffer rBuf; // AGG's rendering buffer, pointing into the emu's rendering buffer + +void agg_draw_solid_ellipse(int x, int y, int rx, int ry, int r, int g, int b, int a){ + + rBuf.attach(GPU_tempScreen, width, height, stride); + + pixfmt pixf(rBuf); + RendererBase rbase(pixf); + RendererPrimitives rprim(rbase); + + rprim.fill_color(pixfmt::color_type(r, g, b, a)); + + rprim.solid_ellipse(x, y, rx, ry); + +} + +void agg_draw_solid_rectangle(int x1, int y1, int x2, int y2, int r, int g, int b, int a){ + + rBuf.attach(GPU_tempScreen, width, height, stride); + + pixfmt pixf(rBuf); + RendererBase rbase(pixf); + RendererPrimitives rprim(rbase); + + rprim.fill_color(pixfmt::color_type(r, g, b, a)); + + rprim.solid_rectangle(x1, y1, x2, y2); + +} + +void agg_draw_solid_triangle(int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int a, int gamma){ + + rBuf.attach(GPU_tempScreen, width, height, stride); + + pixfmt pixf(rBuf); + RendererBase rbase(pixf); + + RendererSolid ren_aa(rbase); + agg::rasterizer_scanline_aa<> m_ras; + agg::scanline_p8 m_sl_p8; + + agg::path_storage path; + + path.move_to(x1, y1); + path.line_to(x2, y2); + path.line_to(x3, y3); + path.close_polygon(); + + ren_aa.color(agg::rgba(r, g, b, a)); + + m_ras.gamma(agg::gamma_power(gamma * 2.0)); + m_ras.add_path(path); + agg::render_scanlines(m_ras, m_sl_p8, ren_aa); + +} + +void agg_draw_raster_font(int x, int y, int r, int g, int b, int a, char* strbuf, char* fontchoice){ + + typedef agg::renderer_base ren_base; + typedef agg::glyph_raster_bin glyph_gen; + + int flipy = 0; + + struct font_type + { + const agg::int8u* font; + const char* name; + } + fonts[] = + { + { agg::gse4x6, "gse4x6" }, + { agg::gse4x8, "gse4x8" }, + { agg::gse5x7, "gse5x7" }, + { agg::gse5x9, "gse5x9" }, + { agg::gse6x9, "gse6x9" }, + { agg::gse6x12, "gse6x12" }, + { agg::gse7x11, "gse7x11" }, + { agg::gse7x11_bold, "gse7x11_bold" }, + { agg::gse7x15, "gse7x15" }, + { agg::gse7x15_bold, "gse7x15_bold" }, + { agg::gse8x16, "gse8x16" }, + { agg::gse8x16_bold, "gse8x16_bold" }, + { agg::mcs11_prop, "mcs11_prop" }, + { agg::mcs11_prop_condensed, "mcs11_prop_condensed" }, + { agg::mcs12_prop, "mcs12_prop" }, + { agg::mcs13_prop, "mcs13_prop" }, + { agg::mcs5x10_mono, "mcs5x10_mono" }, + { agg::mcs5x11_mono, "mcs5x11_mono" }, + { agg::mcs6x10_mono, "mcs6x10_mono" }, + { agg::mcs6x11_mono, "mcs6x11_mono" }, + { agg::mcs7x12_mono_high, "mcs7x12_mono_high" }, + { agg::mcs7x12_mono_low, "mcs7x12_mono_low" }, + { agg::verdana12, "verdana12" }, + { agg::verdana12_bold, "verdana12_bold" }, + { agg::verdana13, "verdana13" }, + { agg::verdana13_bold, "verdana13_bold" }, + { agg::verdana14, "verdana14" }, + { agg::verdana14_bold, "verdana14_bold" }, + { agg::verdana16, "verdana16" }, + { agg::verdana16_bold, "verdana16_bold" }, + { agg::verdana17, "verdana17" }, + { agg::verdana17_bold, "verdana17_bold" }, + { agg::verdana18, "verdana18" }, + { agg::verdana18_bold, "verdana18_bold" }, + 0, 0 + }; + + int fontnumber = 0; + + for(int i = 0; fonts[i].font; i++) { + if(!strcmp(fonts[i].name, fontchoice)) + fontnumber = i; + } + + glyph_gen glyph(0); + + rBuf.attach(GPU_tempScreen, width, height, stride); + + pixfmt pixf(rBuf); + ren_base rb(pixf); + + agg::renderer_raster_htext_solid rt(rb, glyph); + + rt.color(agg::rgba(r,g,b,a)); + + glyph.font(fonts[fontnumber].font); + rt.render_text(x, y, strbuf, !flipy); + +} +//temporary, just for testing the lib +void AGGDraw() { + + agg_draw_solid_triangle(0, 60, 200, 170, 100, 310, 255, 64, 64, 128, 99999); + agg_draw_solid_rectangle(100, 100, 200, 200, 0, 255, 0, 128); + agg_draw_solid_ellipse(70, 80, 50, 50, 255, 0, 0, 128); + agg_draw_raster_font(60, 60, 255, 0, 255, 128, "testing testing testing", "verdana18_bold"); +} diff --git a/desmume/src/windows/DeSmuME_2005.vcproj b/desmume/src/windows/DeSmuME_2005.vcproj index 74d43851e..74596ca91 100644 --- a/desmume/src/windows/DeSmuME_2005.vcproj +++ b/desmume/src/windows/DeSmuME_2005.vcproj @@ -51,7 +51,7 @@ FavorSizeOrSpeed="0" EnableFiberSafeOptimizations="false" WholeProgramOptimization="false" - AdditionalIncludeDirectories=".;..;"lua\lua-5.1.4\src";"glib-2.20.1\build";"glib-2.20.1\build\glib";.\zlib123;.\zziplib;.\winpcap;userconfig;defaultconfig" + AdditionalIncludeDirectories=".;..;"lua\lua-5.1.4\src";"glib-2.20.1\build";"glib-2.20.1\build\glib";.\zlib123;.\zziplib;.\winpcap;userconfig;defaultconfig;.\agg\include;.\agg\examples" PreprocessorDefinitions="DEBUG;_CRT_SECURE_NO_DEPRECATE;WIN32;SPU_INTERPOLATE;HAVE_LIBZ;HAVE_LIBZZIP;NOMINMAX;DEBUG;EXPERIMENTAL_WIFI" ExceptionHandling="1" BasicRuntimeChecks="0" @@ -75,7 +75,7 @@ /> + + diff --git a/desmume/src/windows/DeSmuME_2008.vcproj b/desmume/src/windows/DeSmuME_2008.vcproj index bb2eac60e..c8f41d454 100644 --- a/desmume/src/windows/DeSmuME_2008.vcproj +++ b/desmume/src/windows/DeSmuME_2008.vcproj @@ -1,4 +1,4 @@ - + + + diff --git a/desmume/src/windows/agg/agg-2.5.lib b/desmume/src/windows/agg/agg-2.5.lib new file mode 100644 index 0000000000000000000000000000000000000000..df8489eddd12bfac5d5cc60a5287882f1a9bf9b3 GIT binary patch literal 1744994 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%0A;`f!d12b=B&wv^Wh$javq~ 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

EtG$xJK~?>dGqEk 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

WjJ? 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�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 z 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 XDeMu^+X&RhsRO1F>=Xzyb~gM!`Um8k literal 0 HcmV?d00001 diff --git a/desmume/src/windows/agg/agg-2.5.sln b/desmume/src/windows/agg/agg-2.5.sln new file mode 100644 index 000000000..ecd28b6c2 --- /dev/null +++ b/desmume/src/windows/agg/agg-2.5.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "agg-2.5", "agg-2.5.vcproj", "{887D5BD9-14EB-4B81-AABA-71E1E1063044}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {887D5BD9-14EB-4B81-AABA-71E1E1063044}.Debug|Win32.ActiveCfg = Debug|Win32 + {887D5BD9-14EB-4B81-AABA-71E1E1063044}.Debug|Win32.Build.0 = Debug|Win32 + {887D5BD9-14EB-4B81-AABA-71E1E1063044}.Release|Win32.ActiveCfg = Release|Win32 + {887D5BD9-14EB-4B81-AABA-71E1E1063044}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/desmume/src/windows/agg/agg-2.5.vcproj b/desmume/src/windows/agg/agg-2.5.vcproj new file mode 100644 index 000000000..4d4e94bfe --- /dev/null +++ b/desmume/src/windows/agg/agg-2.5.vcproj @@ -0,0 +1,883 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desmume/src/windows/agg/examples/pixel_formats.h b/desmume/src/windows/agg/examples/pixel_formats.h new file mode 100644 index 000000000..0a601f687 --- /dev/null +++ b/desmume/src/windows/agg/examples/pixel_formats.h @@ -0,0 +1,184 @@ +#ifndef PIXEL_FORMATS_INCLUDED +#define PIXEL_FORMATS_INCLUDED + +#if defined(AGG_GRAY8) + +#include "agg_pixfmt_gray.h" +#define pix_format agg::pix_format_gray8 +typedef agg::pixfmt_gray8 pixfmt; +typedef agg::pixfmt_gray8_pre pixfmt_pre; +typedef agg::gray8 color_type; + +#elif defined(AGG_GRAY16) + +#include "agg_pixfmt_gray.h" +#define pix_format agg::pix_format_gray16 +typedef agg::pixfmt_gray16 pixfmt; +typedef agg::pixfmt_gray16_pre pixfmt_pre; +typedef agg::gray16 color_type; + +#elif defined(AGG_BGR24) + +#include "agg_pixfmt_rgb.h" +#define pix_format agg::pix_format_bgr24 +typedef agg::pixfmt_bgr24 pixfmt; +typedef agg::pixfmt_bgr24_pre pixfmt_pre; +#define pixfmt_gamma agg::pixfmt_bgr24_gamma +typedef agg::rgba8 color_type; +typedef agg::order_bgr component_order; + +#elif defined(AGG_RGB24) + +#include "agg_pixfmt_rgb.h" +#define pix_format agg::pix_format_rgb24 +typedef agg::pixfmt_rgb24 pixfmt; +typedef agg::pixfmt_rgb24_pre pixfmt_pre; +#define pixfmt_gamma agg::pixfmt_rgb24_gamma +typedef agg::rgba8 color_type; +typedef agg::order_rgb component_order; + +#elif defined(AGG_BGR48) + +#include "agg_pixfmt_rgb.h" +#define pix_format agg::pix_format_bgr48 +typedef agg::pixfmt_bgr48 pixfmt; +typedef agg::pixfmt_bgr48_pre pixfmt_pre; +#define pixfmt_gamma agg::pixfmt_bgr48_gamma +typedef agg::rgba16 color_type; +typedef agg::order_bgr component_order; + +#elif defined(AGG_RGB48) + +#include "agg_pixfmt_rgb.h" +#define pix_format agg::pix_format_rgb48 +typedef agg::pixfmt_rgb48 pixfmt; +typedef agg::pixfmt_rgb48_pre pixfmt_pre; +#define pixfmt_gamma agg::pixfmt_rgb48_gamma +typedef agg::rgba16 color_type; +typedef agg::order_rgb component_order; + +#elif defined(AGG_BGRA32) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_bgra32 +typedef agg::pixfmt_bgra32 pixfmt; +typedef agg::pixfmt_bgra32_pre pixfmt_pre; +typedef agg::rgba8 color_type; +typedef agg::order_bgra component_order; + +#elif defined(AGG_RGBA32) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_rgba32 +typedef agg::pixfmt_rgba32 pixfmt; +typedef agg::pixfmt_rgba32_pre pixfmt_pre; +typedef agg::rgba8 color_type; +typedef agg::order_rgba component_order; + +#elif defined(AGG_ARGB32) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_argb32 +typedef agg::pixfmt_argb32 pixfmt; +typedef agg::pixfmt_argb32_pre pixfmt_pre; +typedef agg::rgba8 color_type; +typedef agg::order_argb component_order; + +#elif defined(AGG_ABGR32) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_abgr32 +typedef agg::pixfmt_abgr32 pixfmt; +typedef agg::pixfmt_abgr32_pre pixfmt_pre; +typedef agg::rgba8 color_type; +typedef agg::order_argb component_order; + +#elif defined(AGG_BGRA64) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_bgra64 +typedef agg::pixfmt_bgra64 pixfmt; +typedef agg::pixfmt_bgra64_pre pixfmt_pre; +typedef agg::rgba16 color_type; +typedef agg::order_bgra component_order; + +#elif defined(AGG_RGBA64) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_rgba64 +typedef agg::pixfmt_rgba64 pixfmt; +typedef agg::pixfmt_rgba64_pre pixfmt_pre; +typedef agg::rgba16 color_type; +typedef agg::order_rgba component_order; + +#elif defined(AGG_ARGB64) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_argb64 +typedef agg::pixfmt_argb64 pixfmt; +typedef agg::pixfmt_argb64_pre pixfmt_pre; +typedef agg::rgba16 color_type; +typedef agg::order_argb component_order; + +#elif defined(AGG_ABGR64) + +#include "agg_pixfmt_rgba.h" +#define pix_format agg::pix_format_abgr64 +typedef agg::pixfmt_abgr64 pixfmt; +typedef agg::pixfmt_abgr64_pre pixfmt_pre; +typedef agg::rgba16 color_type; +typedef agg::order_argb component_order; + +#elif defined(AGG_RGB565) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_rgb565 +typedef agg::pixfmt_rgb565 pixfmt; +typedef agg::pixfmt_rgb565_pre pixfmt_pre; +typedef agg::rgba8 color_type; + +#elif defined(AGG_RGB555) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_rgb555 +typedef agg::pixfmt_rgb555 pixfmt; +typedef agg::pixfmt_rgb555_pre pixfmt_pre; +typedef agg::rgba8 color_type; + +#elif defined(AGG_RGB_AAA) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_rgbAAA +typedef agg::pixfmt_rgbAAA pixfmt; +typedef agg::pixfmt_rgbAAA_pre pixfmt_pre; +typedef agg::rgba16 color_type; + +#elif defined(AGG_BGR_AAA) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_bgrAAA +typedef agg::pixfmt_bgrAAA pixfmt; +typedef agg::pixfmt_bgrAAA_pre pixfmt_pre; +typedef agg::rgba16 color_type; + +#elif defined(AGG_RGB_BBA) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_rgbBBA +typedef agg::pixfmt_rgbBBA pixfmt; +typedef agg::pixfmt_rgbBBA_pre pixfmt_pre; +typedef agg::rgba16 color_type; + +#elif defined(AGG_BGR_ABB) + +#include "agg_pixfmt_rgb_packed.h" +#define pix_format agg::pix_format_bgrABB +typedef agg::pixfmt_bgrABB pixfmt; +typedef agg::pixfmt_bgrABB_pre pixfmt_pre; +typedef agg::rgba16 color_type; + +#else +#error Please define pixel format: AGG_GRAY8, AGG_BGR24, AGG_RGB24, etc. See this file above +#endif + +#endif diff --git a/desmume/src/windows/agg/font_freetype/agg_font_freetype.cpp b/desmume/src/windows/agg/font_freetype/agg_font_freetype.cpp new file mode 100644 index 000000000..b0f462265 --- /dev/null +++ b/desmume/src/windows/agg/font_freetype/agg_font_freetype.cpp @@ -0,0 +1,1155 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_font_freetype.h" +#include "agg_bitset_iterator.h" +#include "agg_renderer_scanline.h" + + +namespace agg +{ + + //------------------------------------------------------------------------------ + // + // This code implements the AUTODIN II polynomial + // The variable corresponding to the macro argument "crc" should + // be an unsigned long. + // Oroginal code by Spencer Garrett + // + + // generated using the AUTODIN II polynomial + // x^32 + x^26 + x^23 + x^22 + x^16 + + // x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + // + //------------------------------------------------------------------------------ + + static const unsigned crc32tab[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + + //------------------------------------------------------------------------------ + + static unsigned calc_crc32(const unsigned char* buf, unsigned size) + { + unsigned crc = (unsigned)~0; + const unsigned char* p; + unsigned len = 0; + unsigned nr = size; + + for (len += nr, p = buf; nr--; ++p) + { + crc = (crc >> 8) ^ crc32tab[(crc ^ *p) & 0xff]; + } + return ~crc; + } + + //------------------------------------------------------------------------ + static inline int dbl_to_plain_fx(double d) + { + return int(d * 65536.0); + } + + //------------------------------------------------------------------------ + static inline double int26p6_to_dbl(int p) + { + return double(p) / 64.0; + } + + //------------------------------------------------------------------------ + static inline int dbl_to_int26p6(double p) + { + return int(p * 64.0 + 0.5); + } + + + //------------------------------------------------------------------------ + template + bool decompose_ft_outline(const FT_Outline& outline, + bool flip_y, + const trans_affine& mtx, + PathStorage& path) + { + typedef typename PathStorage::value_type value_type; + + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + double x1, y1, x2, y2, x3, y3; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + int n; // index of contour in outline + int first; // index of first point in contour + char tag; // current point's state + + first = 0; + + for(n = 0; n < outline.n_contours; n++) + { + int last; // index of last point in contour + + last = outline.contours[n]; + limit = outline.points + last; + + v_start = outline.points[first]; + v_last = outline.points[last]; + + v_control = v_start; + + point = outline.points + first; + tags = outline.tags + first; + tag = FT_CURVE_TAG(tags[0]); + + // A contour cannot start with a cubic control point! + if(tag == FT_CURVE_TAG_CUBIC) return false; + + // check first point to determine origin + if( tag == FT_CURVE_TAG_CONIC) + { + // first point is conic control. Yes, this happens. + if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) + { + // start at last point if it is on the curve + v_start = v_last; + limit--; + } + else + { + // if both first and last points are conic, + // start at their middle and record its position + // for closure + v_start.x = (v_start.x + v_last.x) / 2; + v_start.y = (v_start.y + v_last.y) / 2; + + v_last = v_start; + } + point--; + tags--; + } + + x1 = int26p6_to_dbl(v_start.x); + y1 = int26p6_to_dbl(v_start.y); + if(flip_y) y1 = -y1; + mtx.transform(&x1, &y1); + path.move_to(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1))); + + while(point < limit) + { + point++; + tags++; + + tag = FT_CURVE_TAG(tags[0]); + switch(tag) + { + case FT_CURVE_TAG_ON: // emit a single line_to + { + x1 = int26p6_to_dbl(point->x); + y1 = int26p6_to_dbl(point->y); + if(flip_y) y1 = -y1; + mtx.transform(&x1, &y1); + path.line_to(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1))); + //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); + continue; + } + + case FT_CURVE_TAG_CONIC: // consume conic arcs + { + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if(point < limit) + { + FT_Vector vec; + FT_Vector v_middle; + + point++; + tags++; + tag = FT_CURVE_TAG(tags[0]); + + vec.x = point->x; + vec.y = point->y; + + if(tag == FT_CURVE_TAG_ON) + { + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(vec.x); + y2 = int26p6_to_dbl(vec.y); + if(flip_y) { y1 = -y1; y2 = -y2; } + mtx.transform(&x1, &y1); + mtx.transform(&x2, &y2); + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + continue; + } + + if(tag != FT_CURVE_TAG_CONIC) return false; + + v_middle.x = (v_control.x + vec.x) / 2; + v_middle.y = (v_control.y + vec.y) / 2; + + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(v_middle.x); + y2 = int26p6_to_dbl(v_middle.y); + if(flip_y) { y1 = -y1; y2 = -y2; } + mtx.transform(&x1, &y1); + mtx.transform(&x2, &y2); + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + + //path.curve3(conv(v_control.x), + // flip_y ? -conv(v_control.y) : conv(v_control.y), + // conv(v_middle.x), + // flip_y ? -conv(v_middle.y) : conv(v_middle.y)); + + v_control = vec; + goto Do_Conic; + } + + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(v_start.x); + y2 = int26p6_to_dbl(v_start.y); + if(flip_y) { y1 = -y1; y2 = -y2; } + mtx.transform(&x1, &y1); + mtx.transform(&x2, &y2); + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + + //path.curve3(conv(v_control.x), + // flip_y ? -conv(v_control.y) : conv(v_control.y), + // conv(v_start.x), + // flip_y ? -conv(v_start.y) : conv(v_start.y)); + goto Close; + } + + default: // FT_CURVE_TAG_CUBIC + { + FT_Vector vec1, vec2; + + if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) + { + return false; + } + + vec1.x = point[0].x; + vec1.y = point[0].y; + vec2.x = point[1].x; + vec2.y = point[1].y; + + point += 2; + tags += 2; + + if(point <= limit) + { + FT_Vector vec; + + vec.x = point->x; + vec.y = point->y; + + x1 = int26p6_to_dbl(vec1.x); + y1 = int26p6_to_dbl(vec1.y); + x2 = int26p6_to_dbl(vec2.x); + y2 = int26p6_to_dbl(vec2.y); + x3 = int26p6_to_dbl(vec.x); + y3 = int26p6_to_dbl(vec.y); + if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } + mtx.transform(&x1, &y1); + mtx.transform(&x2, &y2); + mtx.transform(&x3, &y3); + path.curve4(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2)), + value_type(dbl_to_int26p6(x3)), + value_type(dbl_to_int26p6(y3))); + + //path.curve4(conv(vec1.x), + // flip_y ? -conv(vec1.y) : conv(vec1.y), + // conv(vec2.x), + // flip_y ? -conv(vec2.y) : conv(vec2.y), + // conv(vec.x), + // flip_y ? -conv(vec.y) : conv(vec.y)); + continue; + } + + x1 = int26p6_to_dbl(vec1.x); + y1 = int26p6_to_dbl(vec1.y); + x2 = int26p6_to_dbl(vec2.x); + y2 = int26p6_to_dbl(vec2.y); + x3 = int26p6_to_dbl(v_start.x); + y3 = int26p6_to_dbl(v_start.y); + if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } + mtx.transform(&x1, &y1); + mtx.transform(&x2, &y2); + mtx.transform(&x3, &y3); + path.curve4(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2)), + value_type(dbl_to_int26p6(x3)), + value_type(dbl_to_int26p6(y3))); + + //path.curve4(conv(vec1.x), + // flip_y ? -conv(vec1.y) : conv(vec1.y), + // conv(vec2.x), + // flip_y ? -conv(vec2.y) : conv(vec2.y), + // conv(v_start.x), + // flip_y ? -conv(v_start.y) : conv(v_start.y)); + goto Close; + } + } + } + + path.close_polygon(); + + Close: + first = last + 1; + } + + return true; + } + + + + //------------------------------------------------------------------------ + template + void decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, + int x, int y, + bool flip_y, + Scanline& sl, + ScanlineStorage& storage) + { + int i; + const int8u* buf = (const int8u*)bitmap.buffer; + int pitch = bitmap.pitch; + sl.reset(x, x + bitmap.width); + storage.prepare(); + if(flip_y) + { + buf += bitmap.pitch * (bitmap.rows - 1); + y += bitmap.rows; + pitch = -pitch; + } + for(i = 0; i < bitmap.rows; i++) + { + sl.reset_spans(); + bitset_iterator bits(buf, 0); + int j; + for(j = 0; j < bitmap.width; j++) + { + if(bits.bit()) sl.add_cell(x + j, cover_full); + ++bits; + } + buf += pitch; + if(sl.num_spans()) + { + sl.finalize(y - i - 1); + storage.render(sl); + } + } + } + + + + //------------------------------------------------------------------------ + template + void decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, + int x, int y, + bool flip_y, + Rasterizer& ras, + Scanline& sl, + ScanlineStorage& storage) + { + int i, j; + const int8u* buf = (const int8u*)bitmap.buffer; + int pitch = bitmap.pitch; + sl.reset(x, x + bitmap.width); + storage.prepare(); + if(flip_y) + { + buf += bitmap.pitch * (bitmap.rows - 1); + y += bitmap.rows; + pitch = -pitch; + } + for(i = 0; i < bitmap.rows; i++) + { + sl.reset_spans(); + const int8u* p = buf; + for(j = 0; j < bitmap.width; j++) + { + if(*p) sl.add_cell(x + j, ras.apply_gamma(*p)); + ++p; + } + buf += pitch; + if(sl.num_spans()) + { + sl.finalize(y - i - 1); + storage.render(sl); + } + } + } + + + + + + + + + + + + + + //------------------------------------------------------------------------ + font_engine_freetype_base::~font_engine_freetype_base() + { + unsigned i; + for(i = 0; i < m_num_faces; ++i) + { + delete [] m_face_names[i]; + FT_Done_Face(m_faces[i]); + } + delete [] m_face_names; + delete [] m_faces; + delete [] m_signature; + if(m_library_initialized) FT_Done_FreeType(m_library); + } + + + //------------------------------------------------------------------------ + font_engine_freetype_base::font_engine_freetype_base(bool flag32, + unsigned max_faces) : + m_flag32(flag32), + m_change_stamp(0), + m_last_error(0), + m_name(0), + m_name_len(256-16-1), + m_face_index(0), + m_char_map(FT_ENCODING_NONE), + m_signature(new char [256+256-16]), + m_height(0), + m_width(0), + m_hinting(true), + m_flip_y(false), + m_library_initialized(false), + m_library(0), + m_faces(new FT_Face [max_faces]), + m_face_names(new char* [max_faces]), + m_num_faces(0), + m_max_faces(max_faces), + m_cur_face(0), + m_resolution(0), + m_glyph_rendering(glyph_ren_native_gray8), + m_glyph_index(0), + m_data_size(0), + m_data_type(glyph_data_invalid), + m_bounds(1,1,0,0), + m_advance_x(0.0), + m_advance_y(0.0), + + m_path16(), + m_path32(), + m_curves16(m_path16), + m_curves32(m_path32), + m_scanline_aa(), + m_scanline_bin(), + m_scanlines_aa(), + m_scanlines_bin(), + m_rasterizer() + { + m_curves16.approximation_scale(4.0); + m_curves32.approximation_scale(4.0); + m_last_error = FT_Init_FreeType(&m_library); + if(m_last_error == 0) m_library_initialized = true; + } + + + + //------------------------------------------------------------------------ + void font_engine_freetype_base::resolution(unsigned dpi) + { + m_resolution = dpi; + update_char_size(); + } + + + //------------------------------------------------------------------------ + int font_engine_freetype_base::find_face(const char* face_name) const + { + unsigned i; + for(i = 0; i < m_num_faces; ++i) + { + if(strcmp(face_name, m_face_names[i]) == 0) return i; + } + return -1; + } + + + //------------------------------------------------------------------------ + double font_engine_freetype_base::ascender() const + { + if(m_cur_face) + { + return m_cur_face->ascender * height() / m_cur_face->height; + } + return 0.0; + } + + //------------------------------------------------------------------------ + double font_engine_freetype_base::descender() const + { + if(m_cur_face) + { + return m_cur_face->descender * height() / m_cur_face->height; + } + return 0.0; + } + + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::load_font(const char* font_name, + unsigned face_index, + glyph_rendering ren_type, + const char* font_mem, + const long font_mem_size) + { + bool ret = false; + + if(m_library_initialized) + { + m_last_error = 0; + + int idx = find_face(font_name); + if(idx >= 0) + { + m_cur_face = m_faces[idx]; + m_name = m_face_names[idx]; + } + else + { + if(m_num_faces >= m_max_faces) + { + delete [] m_face_names[0]; + FT_Done_Face(m_faces[0]); + memcpy(m_faces, + m_faces + 1, + (m_max_faces - 1) * sizeof(FT_Face)); + memcpy(m_face_names, + m_face_names + 1, + (m_max_faces - 1) * sizeof(char*)); + m_num_faces = m_max_faces - 1; + } + + if (font_mem && font_mem_size) + { + m_last_error = FT_New_Memory_Face(m_library, + (const FT_Byte*)font_mem, + font_mem_size, + face_index, + &m_faces[m_num_faces]); + } + else + { + m_last_error = FT_New_Face(m_library, + font_name, + face_index, + &m_faces[m_num_faces]); + } + + if(m_last_error == 0) + { + m_face_names[m_num_faces] = new char [strlen(font_name) + 1]; + strcpy(m_face_names[m_num_faces], font_name); + m_cur_face = m_faces[m_num_faces]; + m_name = m_face_names[m_num_faces]; + ++m_num_faces; + } + else + { + m_face_names[m_num_faces] = 0; + m_cur_face = 0; + m_name = 0; + } + } + + + if(m_last_error == 0) + { + ret = true; + + switch(ren_type) + { + case glyph_ren_native_mono: + m_glyph_rendering = glyph_ren_native_mono; + break; + + case glyph_ren_native_gray8: + m_glyph_rendering = glyph_ren_native_gray8; + break; + + case glyph_ren_outline: + if(FT_IS_SCALABLE(m_cur_face)) + { + m_glyph_rendering = glyph_ren_outline; + } + else + { + m_glyph_rendering = glyph_ren_native_gray8; + } + break; + + case glyph_ren_agg_mono: + if(FT_IS_SCALABLE(m_cur_face)) + { + m_glyph_rendering = glyph_ren_agg_mono; + } + else + { + m_glyph_rendering = glyph_ren_native_mono; + } + break; + + case glyph_ren_agg_gray8: + if(FT_IS_SCALABLE(m_cur_face)) + { + m_glyph_rendering = glyph_ren_agg_gray8; + } + else + { + m_glyph_rendering = glyph_ren_native_gray8; + } + break; + } + update_signature(); + } + } + return ret; + } + + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::attach(const char* file_name) + { + if(m_cur_face) + { + m_last_error = FT_Attach_File(m_cur_face, file_name); + return m_last_error == 0; + } + return false; + } + + //------------------------------------------------------------------------ + unsigned font_engine_freetype_base::num_faces() const + { + if(m_cur_face) + { + return m_cur_face->num_faces; + } + return 0; + } + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::char_map(FT_Encoding char_map) + { + if(m_cur_face) + { + m_last_error = FT_Select_Charmap(m_cur_face, m_char_map); + if(m_last_error == 0) + { + update_signature(); + return true; + } + } + return false; + } + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::height(double h) + { + m_height = int(h * 64.0); + if(m_cur_face) + { + update_char_size(); + return true; + } + return false; + } + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::width(double w) + { + m_width = int(w * 64.0); + if(m_cur_face) + { + update_char_size(); + return true; + } + return false; + } + + //------------------------------------------------------------------------ + void font_engine_freetype_base::hinting(bool h) + { + m_hinting = h; + if(m_cur_face) + { + update_signature(); + } + } + + //------------------------------------------------------------------------ + void font_engine_freetype_base::flip_y(bool f) + { + m_flip_y = f; + if(m_cur_face) + { + update_signature(); + } + } + + //------------------------------------------------------------------------ + void font_engine_freetype_base::transform(const trans_affine& affine) + { + m_affine = affine; + if(m_cur_face) + { + update_signature(); + } + } + + //------------------------------------------------------------------------ + void font_engine_freetype_base::update_signature() + { + if(m_cur_face && m_name) + { + unsigned name_len = strlen(m_name); + if(name_len > m_name_len) + { + delete [] m_signature; + m_signature = new char [name_len + 32 + 256]; + m_name_len = name_len + 32 - 1; + } + + unsigned gamma_hash = 0; + if(m_glyph_rendering == glyph_ren_native_gray8 || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + unsigned char gamma_table[rasterizer_scanline_aa<>::aa_scale]; + unsigned i; + for(i = 0; i < rasterizer_scanline_aa<>::aa_scale; ++i) + { + gamma_table[i] = m_rasterizer.apply_gamma(i); + } + gamma_hash = calc_crc32(gamma_table, sizeof(gamma_table)); + } + + sprintf(m_signature, + "%s,%u,%d,%d,%d:%dx%d,%d,%d,%08X", + m_name, + m_char_map, + m_face_index, + int(m_glyph_rendering), + m_resolution, + m_height, + m_width, + int(m_hinting), + int(m_flip_y), + gamma_hash); + if(m_glyph_rendering == glyph_ren_outline || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + double mtx[6]; + char buf[100]; + m_affine.store_to(mtx); + sprintf(buf, ",%08X%08X%08X%08X%08X%08X", + dbl_to_plain_fx(mtx[0]), + dbl_to_plain_fx(mtx[1]), + dbl_to_plain_fx(mtx[2]), + dbl_to_plain_fx(mtx[3]), + dbl_to_plain_fx(mtx[4]), + dbl_to_plain_fx(mtx[5])); + strcat(m_signature, buf); + } + ++m_change_stamp; + } + } + + + //------------------------------------------------------------------------ + void font_engine_freetype_base::update_char_size() + { + if(m_cur_face) + { + if(m_resolution) + { + FT_Set_Char_Size(m_cur_face, + m_width, // char_width in 1/64th of points + m_height, // char_height in 1/64th of points + m_resolution, // horizontal device resolution + m_resolution); // vertical device resolution + } + else + { + FT_Set_Pixel_Sizes(m_cur_face, + m_width >> 6, // pixel_width + m_height >> 6); // pixel_height + } + update_signature(); + } + } + + + + + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::prepare_glyph(unsigned glyph_code) + { + m_glyph_index = FT_Get_Char_Index(m_cur_face, glyph_code); + m_last_error = FT_Load_Glyph(m_cur_face, + m_glyph_index, + m_hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING); +// m_hinting ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_NO_HINTING); + if(m_last_error == 0) + { + switch(m_glyph_rendering) + { + case glyph_ren_native_mono: + m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_MONO); + if(m_last_error == 0) + { + decompose_ft_bitmap_mono(m_cur_face->glyph->bitmap, + m_cur_face->glyph->bitmap_left, + m_flip_y ? -m_cur_face->glyph->bitmap_top : + m_cur_face->glyph->bitmap_top, + m_flip_y, + m_scanline_bin, + m_scanlines_bin); + m_bounds.x1 = m_scanlines_bin.min_x(); + m_bounds.y1 = m_scanlines_bin.min_y(); + m_bounds.x2 = m_scanlines_bin.max_x() + 1; + m_bounds.y2 = m_scanlines_bin.max_y() + 1; + m_data_size = m_scanlines_bin.byte_size(); + m_data_type = glyph_data_mono; + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + return true; + } + break; + + + case glyph_ren_native_gray8: + m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_NORMAL); + if(m_last_error == 0) + { + decompose_ft_bitmap_gray8(m_cur_face->glyph->bitmap, + m_cur_face->glyph->bitmap_left, + m_flip_y ? -m_cur_face->glyph->bitmap_top : + m_cur_face->glyph->bitmap_top, + m_flip_y, + m_rasterizer, + m_scanline_aa, + m_scanlines_aa); + m_bounds.x1 = m_scanlines_aa.min_x(); + m_bounds.y1 = m_scanlines_aa.min_y(); + m_bounds.x2 = m_scanlines_aa.max_x() + 1; + m_bounds.y2 = m_scanlines_aa.max_y() + 1; + m_data_size = m_scanlines_aa.byte_size(); + m_data_type = glyph_data_gray8; + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + return true; + } + break; + + + case glyph_ren_outline: + if(m_last_error == 0) + { + if(m_flag32) + { + m_path32.remove_all(); + if(decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path32)) + { + rect_d bnd = m_path32.bounding_rect(); + m_data_size = m_path32.byte_size(); + m_data_type = glyph_data_outline; + m_bounds.x1 = int(floor(bnd.x1)); + m_bounds.y1 = int(floor(bnd.y1)); + m_bounds.x2 = int(ceil(bnd.x2)); + m_bounds.y2 = int(ceil(bnd.y2)); + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + m_affine.transform(&m_advance_x, &m_advance_y); + return true; + } + } + else + { + m_path16.remove_all(); + if(decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path16)) + { + rect_d bnd = m_path16.bounding_rect(); + m_data_size = m_path16.byte_size(); + m_data_type = glyph_data_outline; + m_bounds.x1 = int(floor(bnd.x1)); + m_bounds.y1 = int(floor(bnd.y1)); + m_bounds.x2 = int(ceil(bnd.x2)); + m_bounds.y2 = int(ceil(bnd.y2)); + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + m_affine.transform(&m_advance_x, &m_advance_y); + return true; + } + } + } + return false; + + case glyph_ren_agg_mono: + if(m_last_error == 0) + { + m_rasterizer.reset(); + if(m_flag32) + { + m_path32.remove_all(); + decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path32); + m_rasterizer.add_path(m_curves32); + } + else + { + m_path16.remove_all(); + decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path16); + m_rasterizer.add_path(m_curves16); + } + m_scanlines_bin.prepare(); // Remove all + render_scanlines(m_rasterizer, m_scanline_bin, m_scanlines_bin); + m_bounds.x1 = m_scanlines_bin.min_x(); + m_bounds.y1 = m_scanlines_bin.min_y(); + m_bounds.x2 = m_scanlines_bin.max_x() + 1; + m_bounds.y2 = m_scanlines_bin.max_y() + 1; + m_data_size = m_scanlines_bin.byte_size(); + m_data_type = glyph_data_mono; + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + m_affine.transform(&m_advance_x, &m_advance_y); + return true; + } + return false; + + + case glyph_ren_agg_gray8: + if(m_last_error == 0) + { + m_rasterizer.reset(); + if(m_flag32) + { + m_path32.remove_all(); + decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path32); + m_rasterizer.add_path(m_curves32); + } + else + { + m_path16.remove_all(); + decompose_ft_outline(m_cur_face->glyph->outline, + m_flip_y, + m_affine, + m_path16); + m_rasterizer.add_path(m_curves16); + } + m_scanlines_aa.prepare(); // Remove all + render_scanlines(m_rasterizer, m_scanline_aa, m_scanlines_aa); + m_bounds.x1 = m_scanlines_aa.min_x(); + m_bounds.y1 = m_scanlines_aa.min_y(); + m_bounds.x2 = m_scanlines_aa.max_x() + 1; + m_bounds.y2 = m_scanlines_aa.max_y() + 1; + m_data_size = m_scanlines_aa.byte_size(); + m_data_type = glyph_data_gray8; + m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); + m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); + m_affine.transform(&m_advance_x, &m_advance_y); + return true; + } + return false; + } + } + return false; + } + + + + + //------------------------------------------------------------------------ + void font_engine_freetype_base::write_glyph_to(int8u* data) const + { + if(data && m_data_size) + { + switch(m_data_type) + { + default: return; + case glyph_data_mono: m_scanlines_bin.serialize(data); break; + case glyph_data_gray8: m_scanlines_aa.serialize(data); break; + case glyph_data_outline: + if(m_flag32) + { + m_path32.serialize(data); + } + else + { + m_path16.serialize(data); + } + break; + case glyph_data_invalid: break; + } + } + } + + + + //------------------------------------------------------------------------ + bool font_engine_freetype_base::add_kerning(unsigned first, unsigned second, + double* x, double* y) + { + if(m_cur_face && first && second && FT_HAS_KERNING(m_cur_face)) + { + FT_Vector delta; + FT_Get_Kerning(m_cur_face, first, second, + FT_KERNING_DEFAULT, &delta); + double dx = int26p6_to_dbl(delta.x); + double dy = int26p6_to_dbl(delta.y); + if(m_glyph_rendering == glyph_ren_outline || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + m_affine.transform_2x2(&dx, &dy); + } + *x += dx; + *y += dy; + + return true; + } + return false; + } + + + +} + + diff --git a/desmume/src/windows/agg/font_freetype/agg_font_freetype.h b/desmume/src/windows/agg/font_freetype/agg_font_freetype.h new file mode 100644 index 000000000..db923eb5a --- /dev/null +++ b/desmume/src/windows/agg/font_freetype/agg_font_freetype.h @@ -0,0 +1,201 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_FONT_FREETYPE_INCLUDED +#define AGG_FONT_FREETYPE_INCLUDED + +#include +#include FT_FREETYPE_H + + +#include "agg_scanline_storage_aa.h" +#include "agg_scanline_storage_bin.h" +#include "agg_scanline_u.h" +#include "agg_scanline_bin.h" +#include "agg_path_storage_integer.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_conv_curve.h" +#include "agg_font_cache_manager.h" +#include "agg_trans_affine.h" + +namespace agg +{ + + + //-----------------------------------------------font_engine_freetype_base + class font_engine_freetype_base + { + public: + //-------------------------------------------------------------------- + typedef serialized_scanlines_adaptor_aa gray8_adaptor_type; + typedef serialized_scanlines_adaptor_bin mono_adaptor_type; + typedef scanline_storage_aa8 scanlines_aa_type; + typedef scanline_storage_bin scanlines_bin_type; + + //-------------------------------------------------------------------- + ~font_engine_freetype_base(); + font_engine_freetype_base(bool flag32, unsigned max_faces = 32); + + // Set font parameters + //-------------------------------------------------------------------- + void resolution(unsigned dpi); + bool load_font(const char* font_name, unsigned face_index, glyph_rendering ren_type, + const char* font_mem = 0, const long font_mem_size = 0); + bool attach(const char* file_name); + bool char_map(FT_Encoding map); + bool height(double h); + bool width(double w); + void hinting(bool h); + void flip_y(bool f); + void transform(const trans_affine& affine); + + // Set Gamma + //-------------------------------------------------------------------- + template void gamma(const GammaF& f) + { + m_rasterizer.gamma(f); + } + + // Accessors + //-------------------------------------------------------------------- + int last_error() const { return m_last_error; } + unsigned resolution() const { return m_resolution; } + const char* name() const { return m_name; } + unsigned num_faces() const; + FT_Encoding char_map() const { return m_char_map; } + double height() const { return double(m_height) / 64.0; } + double width() const { return double(m_width) / 64.0; } + double ascender() const; + double descender() const; + bool hinting() const { return m_hinting; } + bool flip_y() const { return m_flip_y; } + + + // Interface mandatory to implement for font_cache_manager + //-------------------------------------------------------------------- + const char* font_signature() const { return m_signature; } + int change_stamp() const { return m_change_stamp; } + + bool prepare_glyph(unsigned glyph_code); + unsigned glyph_index() const { return m_glyph_index; } + unsigned data_size() const { return m_data_size; } + glyph_data_type data_type() const { return m_data_type; } + const rect_i& bounds() const { return m_bounds; } + double advance_x() const { return m_advance_x; } + double advance_y() const { return m_advance_y; } + void write_glyph_to(int8u* data) const; + bool add_kerning(unsigned first, unsigned second, + double* x, double* y); + + private: + font_engine_freetype_base(const font_engine_freetype_base&); + const font_engine_freetype_base& operator = (const font_engine_freetype_base&); + + void update_char_size(); + void update_signature(); + int find_face(const char* face_name) const; + + bool m_flag32; + int m_change_stamp; + int m_last_error; + char* m_name; + unsigned m_name_len; + unsigned m_face_index; + FT_Encoding m_char_map; + char* m_signature; + unsigned m_height; + unsigned m_width; + bool m_hinting; + bool m_flip_y; + bool m_library_initialized; + FT_Library m_library; // handle to library + FT_Face* m_faces; // A pool of font faces + char** m_face_names; + unsigned m_num_faces; + unsigned m_max_faces; + FT_Face m_cur_face; // handle to the current face object + int m_resolution; + glyph_rendering m_glyph_rendering; + unsigned m_glyph_index; + unsigned m_data_size; + glyph_data_type m_data_type; + rect_i m_bounds; + double m_advance_x; + double m_advance_y; + trans_affine m_affine; + + path_storage_integer m_path16; + path_storage_integer m_path32; + conv_curve > m_curves16; + conv_curve > m_curves32; + scanline_u8 m_scanline_aa; + scanline_bin m_scanline_bin; + scanlines_aa_type m_scanlines_aa; + scanlines_bin_type m_scanlines_bin; + rasterizer_scanline_aa<> m_rasterizer; + }; + + + + + //------------------------------------------------font_engine_freetype_int16 + // This class uses values of type int16 (10.6 format) for the vector cache. + // The vector cache is compact, but when rendering glyphs of height + // more that 200 there integer overflow can occur. + // + class font_engine_freetype_int16 : public font_engine_freetype_base + { + public: + typedef serialized_integer_path_adaptor path_adaptor_type; + typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; + typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; + typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; + typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; + + font_engine_freetype_int16(unsigned max_faces = 32) : + font_engine_freetype_base(false, max_faces) {} + }; + + //------------------------------------------------font_engine_freetype_int32 + // This class uses values of type int32 (26.6 format) for the vector cache. + // The vector cache is twice larger than in font_engine_freetype_int16, + // but it allows you to render glyphs of very large sizes. + // + class font_engine_freetype_int32 : public font_engine_freetype_base + { + public: + typedef serialized_integer_path_adaptor path_adaptor_type; + typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; + typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; + typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; + typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; + + font_engine_freetype_int32(unsigned max_faces = 32) : + font_engine_freetype_base(true, max_faces) {} + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.cpp b/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.cpp new file mode 100644 index 000000000..a47794919 --- /dev/null +++ b/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.cpp @@ -0,0 +1,946 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_font_win32_tt.h" +#include "agg_bitset_iterator.h" +#include "agg_renderer_scanline.h" + +#ifdef AGG_WIN9X_COMPLIANT +#define GetGlyphOutlineX GetGlyphOutline +#else +#define GetGlyphOutlineX GetGlyphOutlineW +#endif + +namespace agg +{ + + //------------------------------------------------------------------------------ + // + // This code implements the AUTODIN II polynomial + // The variable corresponding to the macro argument "crc" should + // be an unsigned long. + // Oroginal code by Spencer Garrett + // + + // generated using the AUTODIN II polynomial + // x^32 + x^26 + x^23 + x^22 + x^16 + + // x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + // + //------------------------------------------------------------------------------ + + static const unsigned crc32tab[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + //------------------------------------------------------------------------------ + static unsigned calc_crc32(const unsigned char* buf, unsigned size) + { + unsigned crc = (unsigned)~0; + const unsigned char* p; + unsigned len = 0; + unsigned nr = size; + + for (len += nr, p = buf; nr--; ++p) + { + crc = (crc >> 8) ^ crc32tab[(crc ^ *p) & 0xff]; + } + return ~crc; + } + + //------------------------------------------------------------------------ + static inline FIXED dbl_to_fx(double d) + { + int l; + l = int(d * 65536.0); + return *(FIXED*)&l; + } + + //------------------------------------------------------------------------ + static inline int dbl_to_plain_fx(double d) + { + return int(d * 65536.0); + } + + //------------------------------------------------------------------------ + static inline FIXED negate_fx(const FIXED& fx) + { + int l = -(*(int*)(&fx)); + return *(FIXED*)&l; + } + + //------------------------------------------------------------------------ + static inline double fx_to_dbl(const FIXED& p) + { + return double(p.value) + double(p.fract) * (1.0 / 65536.0); + } + + //------------------------------------------------------------------------ + static inline int fx_to_plain_int(const FIXED& fx) + { + return *(int*)(&fx); + } + + //------------------------------------------------------------------------ + static inline int fx_to_int26p6(const FIXED& p) + { + return (int(p.value) << 6) + (int(p.fract) >> 10); + } + + //------------------------------------------------------------------------ + static inline int dbl_to_int26p6(double p) + { + return int(p * 64.0 + 0.5); + } + + //------------------------------------------------------------------------ + template + void decompose_win32_glyph_bitmap_mono(const char* gbuf, + int w, int h, + int x, int y, + bool flip_y, + Scanline& sl, + ScanlineStorage& storage) + { + int i; + int pitch = ((w + 31) >> 5) << 2; + const int8u* buf = (const int8u*)gbuf; + sl.reset(x, x + w); + storage.prepare(); + if(flip_y) + { + buf += pitch * (h - 1); + y += h; + pitch = -pitch; + } + for(i = 0; i < h; i++) + { + sl.reset_spans(); + bitset_iterator bits(buf, 0); + int j; + for(j = 0; j < w; j++) + { + if(bits.bit()) sl.add_cell(x + j, cover_full); + ++bits; + } + buf += pitch; + if(sl.num_spans()) + { + sl.finalize(y - i - 1); + storage.render(sl); + } + } + } + + + + //------------------------------------------------------------------------ + template + void decompose_win32_glyph_bitmap_gray8(const char* gbuf, + int w, int h, + int x, int y, + bool flip_y, + Rasterizer& ras, + Scanline& sl, + ScanlineStorage& storage) + { + int i, j; + int pitch = ((w + 3) >> 2) << 2; + const int8u* buf = (const int8u*)gbuf; + sl.reset(x, x + w); + storage.prepare(); + if(flip_y) + { + buf += pitch * (h - 1); + y += h; + pitch = -pitch; + } + for(i = 0; i < h; i++) + { + sl.reset_spans(); + const int8u* p = buf; + for(j = 0; j < w; j++) + { + if(*p) + { + unsigned v = *p; + if(v == 64) v = 255; + else v <<= 2; + sl.add_cell(x + j, ras.apply_gamma(v)); + } + ++p; + } + buf += pitch; + if(sl.num_spans()) + { + sl.finalize(y - i - 1); + storage.render(sl); + } + } + } + + + + //------------------------------------------------------------------------ + template + bool decompose_win32_glyph_outline(const char* gbuf, + unsigned total_size, + bool flip_y, + const trans_affine& mtx, + PathStorage& path) + { + const char* cur_glyph = gbuf; + const char* end_glyph = gbuf + total_size; + double x, y; + typedef typename PathStorage::value_type value_type; + + while(cur_glyph < end_glyph) + { + const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; + + const char* end_poly = cur_glyph + th->cb; + const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); + + x = fx_to_dbl(th->pfxStart.x); + y = fx_to_dbl(th->pfxStart.y); + if(flip_y) y = -y; + mtx.transform(&x, &y); + path.move_to(value_type(dbl_to_int26p6(x)), + value_type(dbl_to_int26p6(y))); + + while(cur_poly < end_poly) + { + const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; + + if (pc->wType == TT_PRIM_LINE) + { + int i; + for (i = 0; i < pc->cpfx; i++) + { + x = fx_to_dbl(pc->apfx[i].x); + y = fx_to_dbl(pc->apfx[i].y); + if(flip_y) y = -y; + mtx.transform(&x, &y); + path.line_to(value_type(dbl_to_int26p6(x)), + value_type(dbl_to_int26p6(y))); + } + } + + if (pc->wType == TT_PRIM_QSPLINE) + { + int u; + for (u = 0; u < pc->cpfx - 1; u++) // Walk through points in spline + { + POINTFX pnt_b = pc->apfx[u]; // B is always the current point + POINTFX pnt_c = pc->apfx[u+1]; + + if (u < pc->cpfx - 2) // If not on last spline, compute C + { + // midpoint (x,y) + *(int*)&pnt_c.x = (*(int*)&pnt_b.x + *(int*)&pnt_c.x) / 2; + *(int*)&pnt_c.y = (*(int*)&pnt_b.y + *(int*)&pnt_c.y) / 2; + } + + double x2, y2; + x = fx_to_dbl(pnt_b.x); + y = fx_to_dbl(pnt_b.y); + x2 = fx_to_dbl(pnt_c.x); + y2 = fx_to_dbl(pnt_c.y); + if(flip_y) { y = -y; y2 = -y2; } + mtx.transform(&x, &y); + mtx.transform(&x2, &y2); + path.curve3(value_type(dbl_to_int26p6(x)), + value_type(dbl_to_int26p6(y)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + } + } + cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; + } + cur_glyph += th->cb; + } + return true; + } + + + + + //------------------------------------------------------------------------ + font_engine_win32_tt_base::~font_engine_win32_tt_base() + { + delete [] m_kerning_pairs; + delete [] m_gbuf; + delete [] m_signature; + delete [] m_typeface; + if(m_dc && m_old_font) ::SelectObject(m_dc, m_old_font); + unsigned i; + for(i = 0; i < m_num_fonts; ++i) + { + delete [] m_font_names[i]; + ::DeleteObject(m_fonts[i]); + } + delete [] m_font_names; + delete [] m_fonts; + } + + + + //------------------------------------------------------------------------ + font_engine_win32_tt_base::font_engine_win32_tt_base(bool flag32, + HDC dc, + unsigned max_fonts) : + m_flag32(flag32), + m_dc(dc), + m_old_font(m_dc ? (HFONT)::GetCurrentObject(m_dc, OBJ_FONT) : 0), + m_fonts(new HFONT [max_fonts]), + m_num_fonts(0), + m_max_fonts(max_fonts), + m_font_names(new char* [max_fonts]), + m_cur_font(0), + + m_change_stamp(0), + m_typeface(new char [256-16]), + m_typeface_len(256-16-1), + m_signature(new char [256+256-16]), + m_height(0), + m_width(0), + m_weight(FW_REGULAR), + m_italic(false), + m_char_set(DEFAULT_CHARSET), + m_pitch_and_family(FF_DONTCARE), + m_hinting(true), + m_flip_y(false), + m_font_created(false), + m_resolution(0), + m_glyph_rendering(glyph_ren_native_gray8), + m_glyph_index(0), + m_data_size(0), + m_data_type(glyph_data_invalid), + m_bounds(1,1,0,0), + m_advance_x(0.0), + m_advance_y(0.0), + m_gbuf(new char [buf_size]), + m_kerning_pairs(0), + m_num_kerning_pairs(0), + m_max_kerning_pairs(0), + + m_path16(), + m_path32(), + m_curves16(m_path16), + m_curves32(m_path32), + m_scanline_aa(), + m_scanline_bin(), + m_scanlines_aa(), + m_scanlines_bin(), + m_rasterizer() + { + m_curves16.approximation_scale(4.0); + m_curves32.approximation_scale(4.0); + memset(&m_matrix, 0, sizeof(m_matrix)); + m_matrix.eM11.value = 1; + m_matrix.eM22.value = 1; + } + + + + //------------------------------------------------------------------------ + int font_engine_win32_tt_base::find_font(const char* name) const + { + unsigned i; + for(i = 0; i < m_num_fonts; ++i) + { + if(strcmp(name, m_font_names[i]) == 0) return i; + } + return -1; + } + + //------------------------------------------------------------------------ + bool font_engine_win32_tt_base::create_font(const char* typeface_, + glyph_rendering ren_type) + { + if(m_dc) + { + unsigned len = strlen(typeface_); + if(len > m_typeface_len) + { + delete [] m_signature; + delete [] m_typeface; + m_typeface = new char [len + 32]; + m_signature = new char [len + 32 + 256]; + m_typeface_len = len + 32 - 1; + } + + strcpy(m_typeface, typeface_); + + int h = m_height; + int w = m_width; + + if(m_resolution) + { + h = ::MulDiv(m_height, m_resolution, 72); + w = ::MulDiv(m_width, m_resolution, 72); + } + + m_glyph_rendering = ren_type; + update_signature(); + int idx = find_font(m_signature); + if(idx >= 0) + { + m_cur_font = m_fonts[idx]; + ::SelectObject(m_dc, m_cur_font); + m_num_kerning_pairs = 0; + return true; + } + else + { + m_cur_font = ::CreateFont(-h, // height of font + w, // average character width + 0, // angle of escapement + 0, // base-line orientation angle + m_weight, // font weight + m_italic, // italic attribute option + 0, // underline attribute option + 0, // strikeout attribute option + m_char_set, // character set identifier + OUT_DEFAULT_PRECIS, // output precision + CLIP_DEFAULT_PRECIS, // clipping precision + ANTIALIASED_QUALITY, // output quality + m_pitch_and_family, // pitch and family + m_typeface); // typeface name + if(m_cur_font) + { + if(m_num_fonts >= m_max_fonts) + { + delete [] m_font_names[0]; + if(m_old_font) ::SelectObject(m_dc, m_old_font); + ::DeleteObject(m_fonts[0]); + memcpy(m_fonts, + m_fonts + 1, + (m_max_fonts - 1) * sizeof(HFONT)); + memcpy(m_font_names, + m_font_names + 1, + (m_max_fonts - 1) * sizeof(char*)); + m_num_fonts = m_max_fonts - 1; + } + + update_signature(); + m_font_names[m_num_fonts] = new char[strlen(m_signature) + 1]; + strcpy(m_font_names[m_num_fonts], m_signature); + m_fonts[m_num_fonts] = m_cur_font; + ++m_num_fonts; + ::SelectObject(m_dc, m_cur_font); + m_num_kerning_pairs = 0; + return true; + } + } + } + return false; + } + + + + + + //------------------------------------------------------------------------ + bool font_engine_win32_tt_base::create_font(const char* typeface_, + glyph_rendering ren_type, + double height_, + double width_, + int weight_, + bool italic_, + DWORD char_set_, + DWORD pitch_and_family_) + { + height(height_); + width(width_); + weight(weight_); + italic(italic_); + char_set(char_set_); + pitch_and_family(pitch_and_family_); + return create_font(typeface_, ren_type); + } + + + + + //------------------------------------------------------------------------ + void font_engine_win32_tt_base::update_signature() + { + m_signature[0] = 0; + if(m_dc && m_cur_font) + { + unsigned gamma_hash = 0; + if(m_glyph_rendering == glyph_ren_native_gray8 || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + unsigned char gamma_table[rasterizer_scanline_aa<>::aa_scale]; + unsigned i; + for(i = 0; i < rasterizer_scanline_aa<>::aa_scale; ++i) + { + gamma_table[i] = m_rasterizer.apply_gamma(i); + } + gamma_hash = calc_crc32(gamma_table, sizeof(gamma_table)); + } + + sprintf(m_signature, + "%s,%u,%d,%d:%dx%d,%d,%d,%d,%d,%d,%08X", + m_typeface, + m_char_set, + int(m_glyph_rendering), + m_resolution, + m_height, + m_width, + m_weight, + int(m_italic), + int(m_hinting), + int(m_flip_y), + int(m_pitch_and_family), + gamma_hash); + + if(m_glyph_rendering == glyph_ren_outline || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + double mtx[6]; + char buf[100]; + m_affine.store_to(mtx); + sprintf(buf, ",%08X%08X%08X%08X%08X%08X", + dbl_to_plain_fx(mtx[0]), + dbl_to_plain_fx(mtx[1]), + dbl_to_plain_fx(mtx[2]), + dbl_to_plain_fx(mtx[3]), + dbl_to_plain_fx(mtx[4]), + dbl_to_plain_fx(mtx[5])); + strcat(m_signature, buf); + } + ++m_change_stamp; + } + } + + + + //------------------------------------------------------------------------ + bool font_engine_win32_tt_base::prepare_glyph(unsigned glyph_code) + { + if(m_dc && m_cur_font) + { + int format = GGO_BITMAP; + + switch(m_glyph_rendering) + { + case glyph_ren_native_gray8: + format = GGO_GRAY8_BITMAP; + break; + + case glyph_ren_outline: + case glyph_ren_agg_mono: + case glyph_ren_agg_gray8: + format = GGO_NATIVE; + break; + } + +#ifndef GGO_UNHINTED // For compatibility with old SDKs. +#define GGO_UNHINTED 0x0100 +#endif + if(!m_hinting) format |= GGO_UNHINTED; + + GLYPHMETRICS gm; + int total_size = GetGlyphOutlineX(m_dc, + glyph_code, + format, + &gm, + buf_size, + (void*)m_gbuf, + &m_matrix); + + if(total_size < 0) + { + // GetGlyphOutline() fails when being called for + // GGO_GRAY8_BITMAP and white space (stupid Microsoft). + // It doesn't even initialize the glyph metrics + // structure. So, we have to query the metrics + // separately (basically we need gmCellIncX). + int total_size = GetGlyphOutlineX(m_dc, + glyph_code, + GGO_METRICS, + &gm, + buf_size, + (void*)m_gbuf, + &m_matrix); + + if(total_size < 0) return false; + gm.gmBlackBoxX = gm.gmBlackBoxY = 0; + total_size = 0; + } + + m_glyph_index = glyph_code; + m_advance_x = gm.gmCellIncX; + m_advance_y = -gm.gmCellIncY; + + switch(m_glyph_rendering) + { + case glyph_ren_native_mono: + decompose_win32_glyph_bitmap_mono(m_gbuf, + gm.gmBlackBoxX, + gm.gmBlackBoxY, + gm.gmptGlyphOrigin.x, + m_flip_y ? -gm.gmptGlyphOrigin.y : + gm.gmptGlyphOrigin.y, + m_flip_y, + m_scanline_bin, + m_scanlines_bin); + m_bounds.x1 = m_scanlines_bin.min_x(); + m_bounds.y1 = m_scanlines_bin.min_y(); + m_bounds.x2 = m_scanlines_bin.max_x() + 1; + m_bounds.y2 = m_scanlines_bin.max_y() + 1; + m_data_size = m_scanlines_bin.byte_size(); + m_data_type = glyph_data_mono; + return true; + + case glyph_ren_native_gray8: + decompose_win32_glyph_bitmap_gray8(m_gbuf, + gm.gmBlackBoxX, + gm.gmBlackBoxY, + gm.gmptGlyphOrigin.x, + m_flip_y ? -gm.gmptGlyphOrigin.y : + gm.gmptGlyphOrigin.y, + m_flip_y, + m_rasterizer, + m_scanline_aa, + m_scanlines_aa); + m_bounds.x1 = m_scanlines_aa.min_x(); + m_bounds.y1 = m_scanlines_aa.min_y(); + m_bounds.x2 = m_scanlines_aa.max_x() + 1; + m_bounds.y2 = m_scanlines_aa.max_y() + 1; + m_data_size = m_scanlines_aa.byte_size(); + m_data_type = glyph_data_gray8; + return true; + + case glyph_ren_outline: + m_affine.transform(&m_advance_x, &m_advance_y); + if(m_flag32) + { + m_path32.remove_all(); + if(decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path32)) + { + rect_d bnd = m_path32.bounding_rect(); + m_data_size = m_path32.byte_size(); + m_data_type = glyph_data_outline; + m_bounds.x1 = int(floor(bnd.x1)); + m_bounds.y1 = int(floor(bnd.y1)); + m_bounds.x2 = int(ceil(bnd.x2)); + m_bounds.y2 = int(ceil(bnd.y2)); + return true; + } + } + else + { + m_path16.remove_all(); + if(decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path16)) + { + rect_d bnd = m_path16.bounding_rect(); + m_data_size = m_path16.byte_size(); + m_data_type = glyph_data_outline; + m_bounds.x1 = int(floor(bnd.x1)); + m_bounds.y1 = int(floor(bnd.y1)); + m_bounds.x2 = int(ceil(bnd.x2)); + m_bounds.y2 = int(ceil(bnd.y2)); + return true; + } + } + break; + + case glyph_ren_agg_mono: + m_rasterizer.reset(); + m_affine.transform(&m_advance_x, &m_advance_y); + if(m_flag32) + { + m_path32.remove_all(); + decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path32); + m_rasterizer.add_path(m_curves32); + } + else + { + m_path16.remove_all(); + decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path16); + m_rasterizer.add_path(m_curves16); + } + m_scanlines_bin.prepare(); // Remove all + render_scanlines(m_rasterizer, m_scanline_bin, m_scanlines_bin); + m_bounds.x1 = m_scanlines_bin.min_x(); + m_bounds.y1 = m_scanlines_bin.min_y(); + m_bounds.x2 = m_scanlines_bin.max_x() + 1; + m_bounds.y2 = m_scanlines_bin.max_y() + 1; + m_data_size = m_scanlines_bin.byte_size(); + m_data_type = glyph_data_mono; + return true; + + case glyph_ren_agg_gray8: + m_rasterizer.reset(); + m_affine.transform(&m_advance_x, &m_advance_y); + if(m_flag32) + { + m_path32.remove_all(); + decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path32); + m_rasterizer.add_path(m_curves32); + } + else + { + m_path16.remove_all(); + decompose_win32_glyph_outline(m_gbuf, + total_size, + m_flip_y, + m_affine, + m_path16); + m_rasterizer.add_path(m_curves16); + } + m_scanlines_aa.prepare(); // Remove all + render_scanlines(m_rasterizer, m_scanline_aa, m_scanlines_aa); + m_bounds.x1 = m_scanlines_aa.min_x(); + m_bounds.y1 = m_scanlines_aa.min_y(); + m_bounds.x2 = m_scanlines_aa.max_x() + 1; + m_bounds.y2 = m_scanlines_aa.max_y() + 1; + m_data_size = m_scanlines_aa.byte_size(); + m_data_type = glyph_data_gray8; + return true; + } + } + return false; + } + + + + //------------------------------------------------------------------------ + void font_engine_win32_tt_base::write_glyph_to(int8u* data) const + { + if(data && m_data_size) + { + switch(m_data_type) + { + case glyph_data_mono: m_scanlines_bin.serialize(data); break; + case glyph_data_gray8: m_scanlines_aa.serialize(data); break; + case glyph_data_outline: + if(m_flag32) + { + m_path32.serialize(data); + } + else + { + m_path16.serialize(data); + } + break; + } + } + } + + + + //------------------------------------------------------------------------ + static bool pair_less(const KERNINGPAIR& v1, const KERNINGPAIR& v2) + { + if(v1.wFirst != v2.wFirst) return v1.wFirst < v2.wFirst; + return v1.wSecond < v2.wSecond; + } + + + //------------------------------------------------------------------------ + void font_engine_win32_tt_base::sort_kerning_pairs() + { + pod_array_adaptor pairs(m_kerning_pairs, m_num_kerning_pairs); + quick_sort(pairs, pair_less); + } + + + + //------------------------------------------------------------------------ + void font_engine_win32_tt_base::load_kerning_pairs() + { + if(m_dc && m_cur_font) + { + if(m_kerning_pairs == 0) + { + m_kerning_pairs = new KERNINGPAIR [16384-16]; + m_max_kerning_pairs = 16384-16; + } + m_num_kerning_pairs = ::GetKerningPairs(m_dc, + m_max_kerning_pairs, + m_kerning_pairs); + + if(m_num_kerning_pairs) + { + // Check to see if the kerning pairs are sorted and + // sort them if they are not. + //---------------- + unsigned i; + for(i = 1; i < m_num_kerning_pairs; ++i) + { + if(!pair_less(m_kerning_pairs[i - 1], m_kerning_pairs[i])) + { + sort_kerning_pairs(); + break; + } + } + } + } + } + + + //------------------------------------------------------------------------ + bool font_engine_win32_tt_base::add_kerning(unsigned first, unsigned second, + double* x, double* y) + { + if(m_dc && m_cur_font) + { + if(m_num_kerning_pairs == 0) + { + load_kerning_pairs(); + } + + int end = m_num_kerning_pairs - 1; + int beg = 0; + KERNINGPAIR t; + t.wFirst = (WORD)first; + t.wSecond = (WORD)second; + while(beg <= end) + { + int mid = (end + beg) / 2; + if(m_kerning_pairs[mid].wFirst == t.wFirst && + m_kerning_pairs[mid].wSecond == t.wSecond) + { + double dx = m_kerning_pairs[mid].iKernAmount; + double dy = 0.0; + if(m_glyph_rendering == glyph_ren_outline || + m_glyph_rendering == glyph_ren_agg_mono || + m_glyph_rendering == glyph_ren_agg_gray8) + { + m_affine.transform_2x2(&dx, &dy); + } + *x += dx; + *y += dy; + return true; + } + else + if(pair_less(t, m_kerning_pairs[mid])) + { + end = mid - 1; + } + else + { + beg = mid + 1; + } + } + return false; + } + return false; + } + + + +} + diff --git a/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.h b/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.h new file mode 100644 index 000000000..149a89a64 --- /dev/null +++ b/desmume/src/windows/agg/font_win32_tt/agg_font_win32_tt.h @@ -0,0 +1,223 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_FONT_WIN32_TT_INCLUDED +#define AGG_FONT_WIN32_TT_INCLUDED + +#include +#include "agg_scanline_storage_aa.h" +#include "agg_scanline_storage_bin.h" +#include "agg_scanline_u.h" +#include "agg_scanline_bin.h" +#include "agg_path_storage_integer.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_conv_curve.h" +#include "agg_trans_affine.h" +#include "agg_font_cache_manager.h" + +namespace agg +{ + + //-----------------------------------------------font_engine_win32_tt_base + class font_engine_win32_tt_base + { + enum { buf_size = 32768-32 }; + + public: + //-------------------------------------------------------------------- + typedef serialized_scanlines_adaptor_aa gray8_adaptor_type; + typedef serialized_scanlines_adaptor_bin mono_adaptor_type; + typedef scanline_storage_aa8 scanlines_aa_type; + typedef scanline_storage_bin scanlines_bin_type; + + //-------------------------------------------------------------------- + ~font_engine_win32_tt_base(); + font_engine_win32_tt_base(bool flag32, HDC dc, unsigned max_fonts = 32); + + // Set font parameters + //-------------------------------------------------------------------- + void resolution(unsigned dpi) { m_resolution = unsigned(dpi); } + void height(double h) { m_height = unsigned(h); } + void width(double w) { m_width = unsigned(w); } + void weight(int w) { m_weight = w; } + void italic(bool it) { m_italic = it; } + void char_set(DWORD c) { m_char_set = c; } + void pitch_and_family(DWORD p){ m_pitch_and_family = p; } + void flip_y(bool flip) { m_flip_y = flip; } + void hinting(bool h) { m_hinting = h; } + bool create_font(const char* typeface_, glyph_rendering ren_type); + + bool create_font(const char* typeface_, + glyph_rendering ren_type, + double height_, + double width_=0.0, + int weight_=FW_REGULAR, + bool italic_=false, + DWORD char_set_=ANSI_CHARSET, + DWORD pitch_and_family_=FF_DONTCARE); + + // Set Gamma + //-------------------------------------------------------------------- + template void gamma(const GammaF& f) + { + m_rasterizer.gamma(f); + } + + //-------------------------------------------------------------------- + void transform(const agg::trans_affine& mtx) + { + m_affine = mtx; + } + + // Accessors + //-------------------------------------------------------------------- + unsigned resolution() const { return m_resolution; } + const char* typeface() const { return m_typeface; } + double height() const { return m_height; } + double width() const { return m_width; } + int weight() const { return m_weight; } + bool italic() const { return m_italic; } + DWORD char_set() const { return m_char_set; } + DWORD pitch_and_family() const { return m_pitch_and_family; } + bool hinting() const { return m_hinting; } + bool flip_y() const { return m_flip_y; } + + + // Interface mandatory to implement for font_cache_manager + //-------------------------------------------------------------------- + const char* font_signature() const { return m_signature; } + int change_stamp() const { return m_change_stamp; } + + bool prepare_glyph(unsigned glyph_code); + unsigned glyph_index() const { return m_glyph_index; } + unsigned data_size() const { return m_data_size; } + glyph_data_type data_type() const { return m_data_type; } + const rect_i& bounds() const { return m_bounds; } + double advance_x() const { return m_advance_x; } + double advance_y() const { return m_advance_y; } + void write_glyph_to(int8u* data) const; + bool add_kerning(unsigned first, unsigned second, + double* x, double* y); + + private: + font_engine_win32_tt_base(const font_engine_win32_tt_base&); + const font_engine_win32_tt_base& operator = (const font_engine_win32_tt_base&); + + void update_signature(); + void load_kerning_pairs(); + void sort_kerning_pairs(); + int find_font(const char* name) const; + + bool m_flag32; + HDC m_dc; + HFONT m_old_font; + HFONT* m_fonts; + unsigned m_num_fonts; + unsigned m_max_fonts; + char** m_font_names; + HFONT m_cur_font; + + int m_change_stamp; + char* m_typeface; + unsigned m_typeface_len; + char* m_signature; + unsigned m_height; + unsigned m_width; + int m_weight; + bool m_italic; + DWORD m_char_set; + DWORD m_pitch_and_family; + bool m_hinting; + bool m_flip_y; + + bool m_font_created; + unsigned m_resolution; + glyph_rendering m_glyph_rendering; + unsigned m_glyph_index; + unsigned m_data_size; + glyph_data_type m_data_type; + rect_i m_bounds; + double m_advance_x; + double m_advance_y; + MAT2 m_matrix; + char* m_gbuf; + KERNINGPAIR* m_kerning_pairs; + unsigned m_num_kerning_pairs; + unsigned m_max_kerning_pairs; + trans_affine m_affine; + + path_storage_integer m_path16; + path_storage_integer m_path32; + conv_curve > m_curves16; + conv_curve > m_curves32; + scanline_u8 m_scanline_aa; + scanline_bin m_scanline_bin; + scanlines_aa_type m_scanlines_aa; + scanlines_bin_type m_scanlines_bin; + rasterizer_scanline_aa<> m_rasterizer; + }; + + + + + //------------------------------------------------font_engine_win32_tt_int16 + // This class uses values of type int16 (10.6 format) for the vector cache. + // The vector cache is compact, but when rendering glyphs of height + // more that 200 there integer overflow can occur. + // + class font_engine_win32_tt_int16 : public font_engine_win32_tt_base + { + public: + typedef serialized_integer_path_adaptor path_adaptor_type; + typedef font_engine_win32_tt_base::gray8_adaptor_type gray8_adaptor_type; + typedef font_engine_win32_tt_base::mono_adaptor_type mono_adaptor_type; + typedef font_engine_win32_tt_base::scanlines_aa_type scanlines_aa_type; + typedef font_engine_win32_tt_base::scanlines_bin_type scanlines_bin_type; + + font_engine_win32_tt_int16(HDC dc, unsigned max_fonts = 32) : + font_engine_win32_tt_base(false, dc, max_fonts) {} + }; + + //------------------------------------------------font_engine_win32_tt_int32 + // This class uses values of type int32 (26.6 format) for the vector cache. + // The vector cache is twice larger than in font_engine_win32_tt_int16, + // but it allows you to render glyphs of very large sizes. + // + class font_engine_win32_tt_int32 : public font_engine_win32_tt_base + { + public: + typedef serialized_integer_path_adaptor path_adaptor_type; + typedef font_engine_win32_tt_base::gray8_adaptor_type gray8_adaptor_type; + typedef font_engine_win32_tt_base::mono_adaptor_type mono_adaptor_type; + typedef font_engine_win32_tt_base::scanlines_aa_type scanlines_aa_type; + typedef font_engine_win32_tt_base::scanlines_bin_type scanlines_bin_type; + + font_engine_win32_tt_int32(HDC dc, unsigned max_fonts = 32) : + font_engine_win32_tt_base(true, dc, max_fonts) {} + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/gpc/VERSIONS.TXT b/desmume/src/windows/agg/gpc/VERSIONS.TXT new file mode 100644 index 000000000..0b877b628 --- /dev/null +++ b/desmume/src/windows/agg/gpc/VERSIONS.TXT @@ -0,0 +1,123 @@ + +Generic Polygon Clipper (gpc) Revision History +============================================== + + +v2.32 17th Dec 2004 +--------------------- + Fixed occasional memory leak occurring when processing some + degenerate polygon arrangements. + Added explicit type casting to memory allocator in support of + increased code portability. + +v2.31 4th Jun 1999 +--------------------- + Separated edge merging measure based on a user-defined GPC_EPSILON + value from general numeric equality testing and ordering, which now + uses direct arithmetic comparison rather an EPSILON based proximity + test. + Fixed problem with numerical equality test during construction of + local minima and scanbeam tables, leading to occasional crash. + Fixed hole array memory leak in gpc_add_contour. + Fixed uninitialised hole field bug in gpc_polygon_clip result. + +v2.30 11th Apr 1999 +--------------------- + Major re-write. + Minor API change: additional 'hole' array field added to gpc_polygon + datatype to indicate which constituent contours are internal holes, + and which form external boundaries. + Minor API change: additional 'hole' argument to gpc_add_contour + to indicate whether the new contour is a hole or external contour. + Minor API change: additional parameter to gpc_read_polygon and + gpc_write_polygon to indicate whether or not to read or write + contour hole flags. + Fixed NULL pointer bug in add/merge left/right operations. + Fixed numerical problem in intersection table generation. + Fixed zero byte malloc problem. + Fixed problem producing occasional 2 vertex contours. + Added bounding box test optimisations. + Simplified edge bundle creation, detection of scanbeam internal + edge intersections and tristrip scanbeam boundary code. + Renamed 'class' variable to be C++ friendly. + +v2.22 17th Oct 1998 +--------------------- + Re-implemented edge interpolation and intersection calculations + to improve numerical robustness. + Simplified setting of GPC_EPSILON. + +v2.21 19th Aug 1998 +--------------------- + Fixed problem causing occasional incorrect output when processing + self-intersecting polygons (bow-ties etc). + Removed bug which may lead to non-generation of uppermost triangle + in tristrip output. + +v2.20 26th May 1998 +--------------------- + Major re-write. + Added exclusive-or polygon set operation. + Replaced table-based processing of edge intersections with + rule-based system. + Replaced two-pass approach to scanbeam interior processing with + single pass method. + +v2.10a 14th May 1998 +--------------------- + Minor bug-fixes to counter some v2.10 reliability problems. + +v2.10 11th May 1998 +--------------------- + Major re-write. + Incorporated edge bundle processing of AET to overcome coincident + edge problems present in previous releases. + Replaced Vatti's method for processing scanbeam interior regions + with an adapted version of the scanbeam boundary processing + algorithm. + +v2.02 16th Apr 1998 (unreleased) +---------------------------------- + Fixed internal minimum vertex duplication in gpc_polygon_clip + result. + Improved line intersection code discourage superfluous + intersections near line ends. + Removed limited precision number formatting in gpc_write_polygon. + Modification to allow subject or clip polygon to be reused as the + result in gpc_polygon_clip without memory leakage. + +v2.01 23rd Feb 1998 +--------------------- + Removed bug causing duplicated vertices in output polygon. + Fixed scanbeam table index overrun problem. + +v2.00 25th Nov 1997 +--------------------- + Major re-write. + Replaced temporary horizontal edge work-around (using tilting) + with true horizontal edge handling. + Trapezoidal output replaced by tristrips. + gpc_op constants now feature a `GPC_' prefix. + Data structures now passed by reference to gpc functions. + Replaced AET search by proxy addressing in polygon table. + Eliminated most (all?) coincident vertex / edge crashes. + +v1.02 18th Oct 1997 (unreleased) +---------------------------------- + Significantly reduced number of mallocs in build_lmt. + Scanbeam table now built using heapsort rather than insertion + sort. + +v1.01 12th Oct 1997 +--------------------- + Fixed memory leak during output polygon build in + gpc_clip_polygon. + Removed superfluous logfile debug code. + Commented out malloc counts. + Added missing horizontal edge tilt-correction code in + gpc_clip_polygon. + +v1.00 8th Oct 1997 +-------------------- + First release. + diff --git a/desmume/src/windows/agg/gpc/copying.txt b/desmume/src/windows/agg/gpc/copying.txt new file mode 100644 index 000000000..5f1e6bc46 --- /dev/null +++ b/desmume/src/windows/agg/gpc/copying.txt @@ -0,0 +1,22 @@ + + http://www.cs.man.ac.uk/aig/staff/alan/software/ + +Author: Alan Murta (email: gpc@cs.man.ac.uk) +Version: 2.31 +Date: 4th June 1999 + +Copyright: (C) 1997-1999, Advanced Interfaces Group, + University of Manchester. + + This software is free for non-commercial use. It may be copied, + modified, and redistributed provided that this copyright notice + is preserved on all copies. The intellectual property rights of + the algorithms used reside with the University of Manchester + Advanced Interfaces Group. + + You may not use this software, in whole or in part, in support + of any commercial product without the express consent of the + author. + + There is no warranty or other guarantee of fitness of this + software for any purpose. It is provided solely "as is". diff --git a/desmume/src/windows/agg/gpc/gpc.c b/desmume/src/windows/agg/gpc/gpc.c new file mode 100644 index 000000000..b25800bcf --- /dev/null +++ b/desmume/src/windows/agg/gpc/gpc.c @@ -0,0 +1,2472 @@ +/* +=========================================================================== + +Project: Generic Polygon Clipper + + A new algorithm for calculating the difference, intersection, + exclusive-or or union of arbitrary polygon sets. + +File: gpc.c +Author: Alan Murta (email: gpc@cs.man.ac.uk) +Version: 2.32 +Date: 17th December 2004 + +Copyright: (C) 1997-2004, Advanced Interfaces Group, + University of Manchester. + + This software is free for non-commercial use. It may be copied, + modified, and redistributed provided that this copyright notice + is preserved on all copies. The intellectual property rights of + the algorithms used reside with the University of Manchester + Advanced Interfaces Group. + + You may not use this software, in whole or in part, in support + of any commercial product without the express consent of the + author. + + There is no warranty or other guarantee of fitness of this + software for any purpose. It is provided solely "as is". + +=========================================================================== +*/ + + +/* +=========================================================================== + Includes +=========================================================================== +*/ + +#include "gpc.h" +#include +#include +#include + + +/* +=========================================================================== + Constants +=========================================================================== +*/ + +#ifndef TRUE +#define FALSE 0 +#define TRUE 1 +#endif + +#define LEFT 0 +#define RIGHT 1 + +#define ABOVE 0 +#define BELOW 1 + +#define CLIP 0 +#define SUBJ 1 + +#define INVERT_TRISTRIPS FALSE + + +/* +=========================================================================== + Macros +=========================================================================== +*/ + +#define EQ(a, b) (fabs((a) - (b)) <= GPC_EPSILON) + +#define PREV_INDEX(i, n) ((i - 1 + n) % n) +#define NEXT_INDEX(i, n) ((i + 1 ) % n) + +#define OPTIMAL(v, i, n) ((v[PREV_INDEX(i, n)].y != v[i].y) || \ + (v[NEXT_INDEX(i, n)].y != v[i].y)) + +#define FWD_MIN(v, i, n) ((v[PREV_INDEX(i, n)].vertex.y >= v[i].vertex.y) \ + && (v[NEXT_INDEX(i, n)].vertex.y > v[i].vertex.y)) + +#define NOT_FMAX(v, i, n) (v[NEXT_INDEX(i, n)].vertex.y > v[i].vertex.y) + +#define REV_MIN(v, i, n) ((v[PREV_INDEX(i, n)].vertex.y > v[i].vertex.y) \ + && (v[NEXT_INDEX(i, n)].vertex.y >= v[i].vertex.y)) + +#define NOT_RMAX(v, i, n) (v[PREV_INDEX(i, n)].vertex.y > v[i].vertex.y) + +#define VERTEX(e,p,s,x,y) {add_vertex(&((e)->outp[(p)]->v[(s)]), x, y); \ + (e)->outp[(p)]->active++;} + +#define P_EDGE(d,e,p,i,j) {(d)= (e); \ + do {(d)= (d)->prev;} while (!(d)->outp[(p)]); \ + (i)= (d)->bot.x + (d)->dx * ((j)-(d)->bot.y);} + +#define N_EDGE(d,e,p,i,j) {(d)= (e); \ + do {(d)= (d)->next;} while (!(d)->outp[(p)]); \ + (i)= (d)->bot.x + (d)->dx * ((j)-(d)->bot.y);} + +#define MALLOC(p, b, s, t) {if ((b) > 0) { \ + p= (t*)malloc(b); if (!(p)) { \ + fprintf(stderr, "gpc malloc failure: %s\n", s); \ + exit(0);}} else p= NULL;} + +#define FREE(p) {if (p) {free(p); (p)= NULL;}} + + +/* +=========================================================================== + Private Data Types +=========================================================================== +*/ + +typedef enum /* Edge intersection classes */ +{ + NUL, /* Empty non-intersection */ + EMX, /* External maximum */ + ELI, /* External left intermediate */ + TED, /* Top edge */ + ERI, /* External right intermediate */ + RED, /* Right edge */ + IMM, /* Internal maximum and minimum */ + IMN, /* Internal minimum */ + EMN, /* External minimum */ + EMM, /* External maximum and minimum */ + LED, /* Left edge */ + ILI, /* Internal left intermediate */ + BED, /* Bottom edge */ + IRI, /* Internal right intermediate */ + IMX, /* Internal maximum */ + FUL /* Full non-intersection */ +} vertex_type; + +typedef enum /* Horizontal edge states */ +{ + NH, /* No horizontal edge */ + BH, /* Bottom horizontal edge */ + TH /* Top horizontal edge */ +} h_state; + +typedef enum /* Edge bundle state */ +{ + UNBUNDLED, /* Isolated edge not within a bundle */ + BUNDLE_HEAD, /* Bundle head node */ + BUNDLE_TAIL /* Passive bundle tail node */ +} bundle_state; + +typedef struct v_shape /* Internal vertex list datatype */ +{ + double x; /* X coordinate component */ + double y; /* Y coordinate component */ + struct v_shape *next; /* Pointer to next vertex in list */ +} vertex_node; + +typedef struct p_shape /* Internal contour / tristrip type */ +{ + int active; /* Active flag / vertex count */ + int hole; /* Hole / external contour flag */ + vertex_node *v[2]; /* Left and right vertex list ptrs */ + struct p_shape *next; /* Pointer to next polygon contour */ + struct p_shape *proxy; /* Pointer to actual structure used */ +} polygon_node; + +typedef struct edge_shape +{ + gpc_vertex vertex; /* Piggy-backed contour vertex data */ + gpc_vertex bot; /* Edge lower (x, y) coordinate */ + gpc_vertex top; /* Edge upper (x, y) coordinate */ + double xb; /* Scanbeam bottom x coordinate */ + double xt; /* Scanbeam top x coordinate */ + double dx; /* Change in x for a unit y increase */ + int type; /* Clip / subject edge flag */ + int bundle[2][2]; /* Bundle edge flags */ + int bside[2]; /* Bundle left / right indicators */ + bundle_state bstate[2]; /* Edge bundle state */ + polygon_node *outp[2]; /* Output polygon / tristrip pointer */ + struct edge_shape *prev; /* Previous edge in the AET */ + struct edge_shape *next; /* Next edge in the AET */ + struct edge_shape *pred; /* Edge connected at the lower end */ + struct edge_shape *succ; /* Edge connected at the upper end */ + struct edge_shape *next_bound; /* Pointer to next bound in LMT */ +} edge_node; + +typedef struct lmt_shape /* Local minima table */ +{ + double y; /* Y coordinate at local minimum */ + edge_node *first_bound; /* Pointer to bound list */ + struct lmt_shape *next; /* Pointer to next local minimum */ +} lmt_node; + +typedef struct sbt_t_shape /* Scanbeam tree */ +{ + double y; /* Scanbeam node y value */ + struct sbt_t_shape *less; /* Pointer to nodes with lower y */ + struct sbt_t_shape *more; /* Pointer to nodes with higher y */ +} sb_tree; + +typedef struct it_shape /* Intersection table */ +{ + edge_node *ie[2]; /* Intersecting edge (bundle) pair */ + gpc_vertex point; /* Point of intersection */ + struct it_shape *next; /* The next intersection table node */ +} it_node; + +typedef struct st_shape /* Sorted edge table */ +{ + edge_node *edge; /* Pointer to AET edge */ + double xb; /* Scanbeam bottom x coordinate */ + double xt; /* Scanbeam top x coordinate */ + double dx; /* Change in x for a unit y increase */ + struct st_shape *prev; /* Previous edge in sorted list */ +} st_node; + +typedef struct bbox_shape /* Contour axis-aligned bounding box */ +{ + double xmin; /* Minimum x coordinate */ + double ymin; /* Minimum y coordinate */ + double xmax; /* Maximum x coordinate */ + double ymax; /* Maximum y coordinate */ +} bbox; + + +/* +=========================================================================== + Global Data +=========================================================================== +*/ + +/* Horizontal edge state transitions within scanbeam boundary */ +const h_state next_h_state[3][6]= +{ + /* ABOVE BELOW CROSS */ + /* L R L R L R */ + /* NH */ {BH, TH, TH, BH, NH, NH}, + /* BH */ {NH, NH, NH, NH, TH, TH}, + /* TH */ {NH, NH, NH, NH, BH, BH} +}; + + +/* +=========================================================================== + Private Functions +=========================================================================== +*/ + +static void reset_it(it_node **it) +{ + it_node *itn; + + while (*it) + { + itn= (*it)->next; + FREE(*it); + *it= itn; + } +} + + +static void reset_lmt(lmt_node **lmt) +{ + lmt_node *lmtn; + + while (*lmt) + { + lmtn= (*lmt)->next; + FREE(*lmt); + *lmt= lmtn; + } +} + + +static void insert_bound(edge_node **b, edge_node *e) +{ + edge_node *existing_bound; + + if (!*b) + { + /* Link node e to the tail of the list */ + *b= e; + } + else + { + /* Do primary sort on the x field */ + if (e[0].bot.x < (*b)[0].bot.x) + { + /* Insert a new node mid-list */ + existing_bound= *b; + *b= e; + (*b)->next_bound= existing_bound; + } + else + { + if (e[0].bot.x == (*b)[0].bot.x) + { + /* Do secondary sort on the dx field */ + if (e[0].dx < (*b)[0].dx) + { + /* Insert a new node mid-list */ + existing_bound= *b; + *b= e; + (*b)->next_bound= existing_bound; + } + else + { + /* Head further down the list */ + insert_bound(&((*b)->next_bound), e); + } + } + else + { + /* Head further down the list */ + insert_bound(&((*b)->next_bound), e); + } + } + } +} + + +static edge_node **bound_list(lmt_node **lmt, double y) +{ + lmt_node *existing_node; + + if (!*lmt) + { + /* Add node onto the tail end of the LMT */ + MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node); + (*lmt)->y= y; + (*lmt)->first_bound= NULL; + (*lmt)->next= NULL; + return &((*lmt)->first_bound); + } + else + if (y < (*lmt)->y) + { + /* Insert a new LMT node before the current node */ + existing_node= *lmt; + MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node); + (*lmt)->y= y; + (*lmt)->first_bound= NULL; + (*lmt)->next= existing_node; + return &((*lmt)->first_bound); + } + else + if (y > (*lmt)->y) + /* Head further up the LMT */ + return bound_list(&((*lmt)->next), y); + else + /* Use this existing LMT node */ + return &((*lmt)->first_bound); +} + + +static void add_to_sbtree(int *entries, sb_tree **sbtree, double y) +{ + if (!*sbtree) + { + /* Add a new tree node here */ + MALLOC(*sbtree, sizeof(sb_tree), "scanbeam tree insertion", sb_tree); + (*sbtree)->y= y; + (*sbtree)->less= NULL; + (*sbtree)->more= NULL; + (*entries)++; + } + else + { + if ((*sbtree)->y > y) + { + /* Head into the 'less' sub-tree */ + add_to_sbtree(entries, &((*sbtree)->less), y); + } + else + { + if ((*sbtree)->y < y) + { + /* Head into the 'more' sub-tree */ + add_to_sbtree(entries, &((*sbtree)->more), y); + } + } + } +} + + +static void build_sbt(int *entries, double *sbt, sb_tree *sbtree) +{ + if (sbtree->less) + build_sbt(entries, sbt, sbtree->less); + sbt[*entries]= sbtree->y; + (*entries)++; + if (sbtree->more) + build_sbt(entries, sbt, sbtree->more); +} + + +static void free_sbtree(sb_tree **sbtree) +{ + if (*sbtree) + { + free_sbtree(&((*sbtree)->less)); + free_sbtree(&((*sbtree)->more)); + FREE(*sbtree); + } +} + + +static int count_optimal_vertices(gpc_vertex_list c) +{ + int result= 0, i; + + /* Ignore non-contributing contours */ + if (c.num_vertices > 0) + { + for (i= 0; i < c.num_vertices; i++) + /* Ignore superfluous vertices embedded in horizontal edges */ + if (OPTIMAL(c.vertex, i, c.num_vertices)) + result++; + } + return result; +} + + +static edge_node *build_lmt(lmt_node **lmt, sb_tree **sbtree, + int *sbt_entries, gpc_polygon *p, int type, + gpc_op op) +{ + int c, i, min, max, num_edges, v, num_vertices; + int total_vertices= 0, e_index=0; + edge_node *e, *edge_table; + + for (c= 0; c < p->num_contours; c++) + total_vertices+= count_optimal_vertices(p->contour[c]); + + /* Create the entire input polygon edge table in one go */ + MALLOC(edge_table, total_vertices * sizeof(edge_node), + "edge table creation", edge_node); + + for (c= 0; c < p->num_contours; c++) + { + if (p->contour[c].num_vertices < 0) + { + /* Ignore the non-contributing contour and repair the vertex count */ + p->contour[c].num_vertices= -p->contour[c].num_vertices; + } + else + { + /* Perform contour optimisation */ + num_vertices= 0; + for (i= 0; i < p->contour[c].num_vertices; i++) + if (OPTIMAL(p->contour[c].vertex, i, p->contour[c].num_vertices)) + { + edge_table[num_vertices].vertex.x= p->contour[c].vertex[i].x; + edge_table[num_vertices].vertex.y= p->contour[c].vertex[i].y; + + /* Record vertex in the scanbeam table */ + add_to_sbtree(sbt_entries, sbtree, + edge_table[num_vertices].vertex.y); + + num_vertices++; + } + + /* Do the contour forward pass */ + for (min= 0; min < num_vertices; min++) + { + /* If a forward local minimum... */ + if (FWD_MIN(edge_table, min, num_vertices)) + { + /* Search for the next local maximum... */ + num_edges= 1; + max= NEXT_INDEX(min, num_vertices); + while (NOT_FMAX(edge_table, max, num_vertices)) + { + num_edges++; + max= NEXT_INDEX(max, num_vertices); + } + + /* Build the next edge list */ + e= &edge_table[e_index]; + e_index+= num_edges; + v= min; + e[0].bstate[BELOW]= UNBUNDLED; + e[0].bundle[BELOW][CLIP]= FALSE; + e[0].bundle[BELOW][SUBJ]= FALSE; + for (i= 0; i < num_edges; i++) + { + e[i].xb= edge_table[v].vertex.x; + e[i].bot.x= edge_table[v].vertex.x; + e[i].bot.y= edge_table[v].vertex.y; + + v= NEXT_INDEX(v, num_vertices); + + e[i].top.x= edge_table[v].vertex.x; + e[i].top.y= edge_table[v].vertex.y; + e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) / + (e[i].top.y - e[i].bot.y); + e[i].type= type; + e[i].outp[ABOVE]= NULL; + e[i].outp[BELOW]= NULL; + e[i].next= NULL; + e[i].prev= NULL; + e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ? + &(e[i + 1]) : NULL; + e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL; + e[i].next_bound= NULL; + e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT; + e[i].bside[SUBJ]= LEFT; + } + insert_bound(bound_list(lmt, edge_table[min].vertex.y), e); + } + } + + /* Do the contour reverse pass */ + for (min= 0; min < num_vertices; min++) + { + /* If a reverse local minimum... */ + if (REV_MIN(edge_table, min, num_vertices)) + { + /* Search for the previous local maximum... */ + num_edges= 1; + max= PREV_INDEX(min, num_vertices); + while (NOT_RMAX(edge_table, max, num_vertices)) + { + num_edges++; + max= PREV_INDEX(max, num_vertices); + } + + /* Build the previous edge list */ + e= &edge_table[e_index]; + e_index+= num_edges; + v= min; + e[0].bstate[BELOW]= UNBUNDLED; + e[0].bundle[BELOW][CLIP]= FALSE; + e[0].bundle[BELOW][SUBJ]= FALSE; + for (i= 0; i < num_edges; i++) + { + e[i].xb= edge_table[v].vertex.x; + e[i].bot.x= edge_table[v].vertex.x; + e[i].bot.y= edge_table[v].vertex.y; + + v= PREV_INDEX(v, num_vertices); + + e[i].top.x= edge_table[v].vertex.x; + e[i].top.y= edge_table[v].vertex.y; + e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) / + (e[i].top.y - e[i].bot.y); + e[i].type= type; + e[i].outp[ABOVE]= NULL; + e[i].outp[BELOW]= NULL; + e[i].next= NULL; + e[i].prev= NULL; + e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ? + &(e[i + 1]) : NULL; + e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL; + e[i].next_bound= NULL; + e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT; + e[i].bside[SUBJ]= LEFT; + } + insert_bound(bound_list(lmt, edge_table[min].vertex.y), e); + } + } + } + } + return edge_table; +} + + +static void add_edge_to_aet(edge_node **aet, edge_node *edge, edge_node *prev) +{ + if (!*aet) + { + /* Append edge onto the tail end of the AET */ + *aet= edge; + edge->prev= prev; + edge->next= NULL; + } + else + { + /* Do primary sort on the xb field */ + if (edge->xb < (*aet)->xb) + { + /* Insert edge here (before the AET edge) */ + edge->prev= prev; + edge->next= *aet; + (*aet)->prev= edge; + *aet= edge; + } + else + { + if (edge->xb == (*aet)->xb) + { + /* Do secondary sort on the dx field */ + if (edge->dx < (*aet)->dx) + { + /* Insert edge here (before the AET edge) */ + edge->prev= prev; + edge->next= *aet; + (*aet)->prev= edge; + *aet= edge; + } + else + { + /* Head further into the AET */ + add_edge_to_aet(&((*aet)->next), edge, *aet); + } + } + else + { + /* Head further into the AET */ + add_edge_to_aet(&((*aet)->next), edge, *aet); + } + } + } +} + + +static void add_intersection(it_node **it, edge_node *edge0, edge_node *edge1, + double x, double y) +{ + it_node *existing_node; + + if (!*it) + { + /* Append a new node to the tail of the list */ + MALLOC(*it, sizeof(it_node), "IT insertion", it_node); + (*it)->ie[0]= edge0; + (*it)->ie[1]= edge1; + (*it)->point.x= x; + (*it)->point.y= y; + (*it)->next= NULL; + } + else + { + if ((*it)->point.y > y) + { + /* Insert a new node mid-list */ + existing_node= *it; + MALLOC(*it, sizeof(it_node), "IT insertion", it_node); + (*it)->ie[0]= edge0; + (*it)->ie[1]= edge1; + (*it)->point.x= x; + (*it)->point.y= y; + (*it)->next= existing_node; + } + else + /* Head further down the list */ + add_intersection(&((*it)->next), edge0, edge1, x, y); + } +} + + +static void add_st_edge(st_node **st, it_node **it, edge_node *edge, + double dy) +{ + st_node *existing_node; + double den, r, x, y; + + if (!*st) + { + /* Append edge onto the tail end of the ST */ + MALLOC(*st, sizeof(st_node), "ST insertion", st_node); + (*st)->edge= edge; + (*st)->xb= edge->xb; + (*st)->xt= edge->xt; + (*st)->dx= edge->dx; + (*st)->prev= NULL; + } + else + { + den= ((*st)->xt - (*st)->xb) - (edge->xt - edge->xb); + + /* If new edge and ST edge don't cross */ + if ((edge->xt >= (*st)->xt) || (edge->dx == (*st)->dx) || + (fabs(den) <= DBL_EPSILON)) + { + /* No intersection - insert edge here (before the ST edge) */ + existing_node= *st; + MALLOC(*st, sizeof(st_node), "ST insertion", st_node); + (*st)->edge= edge; + (*st)->xb= edge->xb; + (*st)->xt= edge->xt; + (*st)->dx= edge->dx; + (*st)->prev= existing_node; + } + else + { + /* Compute intersection between new edge and ST edge */ + r= (edge->xb - (*st)->xb) / den; + x= (*st)->xb + r * ((*st)->xt - (*st)->xb); + y= r * dy; + + /* Insert the edge pointers and the intersection point in the IT */ + add_intersection(it, (*st)->edge, edge, x, y); + + /* Head further into the ST */ + add_st_edge(&((*st)->prev), it, edge, dy); + } + } +} + + +static void build_intersection_table(it_node **it, edge_node *aet, double dy) +{ + st_node *st, *stp; + edge_node *edge; + + /* Build intersection table for the current scanbeam */ + reset_it(it); + st= NULL; + + /* Process each AET edge */ + for (edge= aet; edge; edge= edge->next) + { + if ((edge->bstate[ABOVE] == BUNDLE_HEAD) || + edge->bundle[ABOVE][CLIP] || edge->bundle[ABOVE][SUBJ]) + add_st_edge(&st, it, edge, dy); + } + + /* Free the sorted edge table */ + while (st) + { + stp= st->prev; + FREE(st); + st= stp; + } +} + +static int count_contours(polygon_node *polygon) +{ + int nc, nv; + vertex_node *v, *nextv; + + for (nc= 0; polygon; polygon= polygon->next) + if (polygon->active) + { + /* Count the vertices in the current contour */ + nv= 0; + for (v= polygon->proxy->v[LEFT]; v; v= v->next) + nv++; + + /* Record valid vertex counts in the active field */ + if (nv > 2) + { + polygon->active= nv; + nc++; + } + else + { + /* Invalid contour: just free the heap */ + for (v= polygon->proxy->v[LEFT]; v; v= nextv) + { + nextv= v->next; + FREE(v); + } + polygon->active= 0; + } + } + return nc; +} + + +static void add_left(polygon_node *p, double x, double y) +{ + vertex_node *nv; + + /* Create a new vertex node and set its fields */ + MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node); + nv->x= x; + nv->y= y; + + /* Add vertex nv to the left end of the polygon's vertex list */ + nv->next= p->proxy->v[LEFT]; + + /* Update proxy->[LEFT] to point to nv */ + p->proxy->v[LEFT]= nv; +} + + +static void merge_left(polygon_node *p, polygon_node *q, polygon_node *list) +{ + polygon_node *target; + + /* Label contour as a hole */ + q->proxy->hole= TRUE; + + if (p->proxy != q->proxy) + { + /* Assign p's vertex list to the left end of q's list */ + p->proxy->v[RIGHT]->next= q->proxy->v[LEFT]; + q->proxy->v[LEFT]= p->proxy->v[LEFT]; + + /* Redirect any p->proxy references to q->proxy */ + + for (target= p->proxy; list; list= list->next) + { + if (list->proxy == target) + { + list->active= FALSE; + list->proxy= q->proxy; + } + } + } +} + + +static void add_right(polygon_node *p, double x, double y) +{ + vertex_node *nv; + + /* Create a new vertex node and set its fields */ + MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node); + nv->x= x; + nv->y= y; + nv->next= NULL; + + /* Add vertex nv to the right end of the polygon's vertex list */ + p->proxy->v[RIGHT]->next= nv; + + /* Update proxy->v[RIGHT] to point to nv */ + p->proxy->v[RIGHT]= nv; +} + + +static void merge_right(polygon_node *p, polygon_node *q, polygon_node *list) +{ + polygon_node *target; + + /* Label contour as external */ + q->proxy->hole= FALSE; + + if (p->proxy != q->proxy) + { + /* Assign p's vertex list to the right end of q's list */ + q->proxy->v[RIGHT]->next= p->proxy->v[LEFT]; + q->proxy->v[RIGHT]= p->proxy->v[RIGHT]; + + /* Redirect any p->proxy references to q->proxy */ + for (target= p->proxy; list; list= list->next) + { + if (list->proxy == target) + { + list->active= FALSE; + list->proxy= q->proxy; + } + } + } +} + + +static void add_local_min(polygon_node **p, edge_node *edge, + double x, double y) +{ + polygon_node *existing_min; + vertex_node *nv; + + existing_min= *p; + + MALLOC(*p, sizeof(polygon_node), "polygon node creation", polygon_node); + + /* Create a new vertex node and set its fields */ + MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node); + nv->x= x; + nv->y= y; + nv->next= NULL; + + /* Initialise proxy to point to p itself */ + (*p)->proxy= (*p); + (*p)->active= TRUE; + (*p)->next= existing_min; + + /* Make v[LEFT] and v[RIGHT] point to new vertex nv */ + (*p)->v[LEFT]= nv; + (*p)->v[RIGHT]= nv; + + /* Assign polygon p to the edge */ + edge->outp[ABOVE]= *p; +} + + +static int count_tristrips(polygon_node *tn) +{ + int total; + + for (total= 0; tn; tn= tn->next) + if (tn->active > 2) + total++; + return total; +} + + +static void add_vertex(vertex_node **t, double x, double y) +{ + if (!(*t)) + { + MALLOC(*t, sizeof(vertex_node), "tristrip vertex creation", vertex_node); + (*t)->x= x; + (*t)->y= y; + (*t)->next= NULL; + } + else + /* Head further down the list */ + add_vertex(&((*t)->next), x, y); +} + + +static void new_tristrip(polygon_node **tn, edge_node *edge, + double x, double y) +{ + if (!(*tn)) + { + MALLOC(*tn, sizeof(polygon_node), "tristrip node creation", polygon_node); + (*tn)->next= NULL; + (*tn)->v[LEFT]= NULL; + (*tn)->v[RIGHT]= NULL; + (*tn)->active= 1; + add_vertex(&((*tn)->v[LEFT]), x, y); + edge->outp[ABOVE]= *tn; + } + else + /* Head further down the list */ + new_tristrip(&((*tn)->next), edge, x, y); +} + + +static bbox *create_contour_bboxes(gpc_polygon *p) +{ + bbox *box; + int c, v; + + MALLOC(box, p->num_contours * sizeof(bbox), "Bounding box creation", bbox); + + /* Construct contour bounding boxes */ + for (c= 0; c < p->num_contours; c++) + { + /* Initialise bounding box extent */ + box[c].xmin= DBL_MAX; + box[c].ymin= DBL_MAX; + box[c].xmax= -DBL_MAX; + box[c].ymax= -DBL_MAX; + + for (v= 0; v < p->contour[c].num_vertices; v++) + { + /* Adjust bounding box */ + if (p->contour[c].vertex[v].x < box[c].xmin) + box[c].xmin= p->contour[c].vertex[v].x; + if (p->contour[c].vertex[v].y < box[c].ymin) + box[c].ymin= p->contour[c].vertex[v].y; + if (p->contour[c].vertex[v].x > box[c].xmax) + box[c].xmax= p->contour[c].vertex[v].x; + if (p->contour[c].vertex[v].y > box[c].ymax) + box[c].ymax= p->contour[c].vertex[v].y; + } + } + return box; +} + + +static void minimax_test(gpc_polygon *subj, gpc_polygon *clip, gpc_op op) +{ + bbox *s_bbox, *c_bbox; + int s, c, *o_table, overlap; + + s_bbox= create_contour_bboxes(subj); + c_bbox= create_contour_bboxes(clip); + + MALLOC(o_table, subj->num_contours * clip->num_contours * sizeof(int), + "overlap table creation", int); + + /* Check all subject contour bounding boxes against clip boxes */ + for (s= 0; s < subj->num_contours; s++) + for (c= 0; c < clip->num_contours; c++) + o_table[c * subj->num_contours + s]= + (!((s_bbox[s].xmax < c_bbox[c].xmin) || + (s_bbox[s].xmin > c_bbox[c].xmax))) && + (!((s_bbox[s].ymax < c_bbox[c].ymin) || + (s_bbox[s].ymin > c_bbox[c].ymax))); + + /* For each clip contour, search for any subject contour overlaps */ + for (c= 0; c < clip->num_contours; c++) + { + overlap= 0; + for (s= 0; (!overlap) && (s < subj->num_contours); s++) + overlap= o_table[c * subj->num_contours + s]; + + if (!overlap) + /* Flag non contributing status by negating vertex count */ + clip->contour[c].num_vertices = -clip->contour[c].num_vertices; + } + + if (op == GPC_INT) + { + /* For each subject contour, search for any clip contour overlaps */ + for (s= 0; s < subj->num_contours; s++) + { + overlap= 0; + for (c= 0; (!overlap) && (c < clip->num_contours); c++) + overlap= o_table[c * subj->num_contours + s]; + + if (!overlap) + /* Flag non contributing status by negating vertex count */ + subj->contour[s].num_vertices = -subj->contour[s].num_vertices; + } + } + + FREE(s_bbox); + FREE(c_bbox); + FREE(o_table); +} + + +/* +=========================================================================== + Public Functions +=========================================================================== +*/ + +void gpc_free_polygon(gpc_polygon *p) +{ + int c; + + for (c= 0; c < p->num_contours; c++) + FREE(p->contour[c].vertex); + FREE(p->hole); + FREE(p->contour); + p->num_contours= 0; +} + + +void gpc_read_polygon(FILE *fp, int read_hole_flags, gpc_polygon *p) +{ + int c, v; + + fscanf(fp, "%d", &(p->num_contours)); + MALLOC(p->hole, p->num_contours * sizeof(int), + "hole flag array creation", int); + MALLOC(p->contour, p->num_contours + * sizeof(gpc_vertex_list), "contour creation", gpc_vertex_list); + for (c= 0; c < p->num_contours; c++) + { + fscanf(fp, "%d", &(p->contour[c].num_vertices)); + + if (read_hole_flags) + fscanf(fp, "%d", &(p->hole[c])); + else + p->hole[c]= FALSE; /* Assume all contours to be external */ + + MALLOC(p->contour[c].vertex, p->contour[c].num_vertices + * sizeof(gpc_vertex), "vertex creation", gpc_vertex); + for (v= 0; v < p->contour[c].num_vertices; v++) + fscanf(fp, "%lf %lf", &(p->contour[c].vertex[v].x), + &(p->contour[c].vertex[v].y)); + } +} + + +void gpc_write_polygon(FILE *fp, int write_hole_flags, gpc_polygon *p) +{ + int c, v; + + fprintf(fp, "%d\n", p->num_contours); + for (c= 0; c < p->num_contours; c++) + { + fprintf(fp, "%d\n", p->contour[c].num_vertices); + + if (write_hole_flags) + fprintf(fp, "%d\n", p->hole[c]); + + for (v= 0; v < p->contour[c].num_vertices; v++) + fprintf(fp, "% .*lf % .*lf\n", + DBL_DIG, p->contour[c].vertex[v].x, + DBL_DIG, p->contour[c].vertex[v].y); + } +} + + +void gpc_add_contour(gpc_polygon *p, gpc_vertex_list *new_contour, int hole) +{ + int *extended_hole, c, v; + gpc_vertex_list *extended_contour; + + /* Create an extended hole array */ + MALLOC(extended_hole, (p->num_contours + 1) + * sizeof(int), "contour hole addition", int); + + /* Create an extended contour array */ + MALLOC(extended_contour, (p->num_contours + 1) + * sizeof(gpc_vertex_list), "contour addition", gpc_vertex_list); + + /* Copy the old contour and hole data into the extended arrays */ + for (c= 0; c < p->num_contours; c++) + { + extended_hole[c]= p->hole[c]; + extended_contour[c]= p->contour[c]; + } + + /* Copy the new contour and hole onto the end of the extended arrays */ + c= p->num_contours; + extended_hole[c]= hole; + extended_contour[c].num_vertices= new_contour->num_vertices; + MALLOC(extended_contour[c].vertex, new_contour->num_vertices + * sizeof(gpc_vertex), "contour addition", gpc_vertex); + for (v= 0; v < new_contour->num_vertices; v++) + extended_contour[c].vertex[v]= new_contour->vertex[v]; + + /* Dispose of the old contour */ + FREE(p->contour); + FREE(p->hole); + + /* Update the polygon information */ + p->num_contours++; + p->hole= extended_hole; + p->contour= extended_contour; +} + + +void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, + gpc_polygon *result) +{ + sb_tree *sbtree= NULL; + it_node *it= NULL, *intersect; + edge_node *edge, *prev_edge, *next_edge, *succ_edge, *e0, *e1; + edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL; + lmt_node *lmt= NULL, *local_min; + polygon_node *out_poly= NULL, *p, *q, *poly, *npoly, *cf= NULL; + vertex_node *vtx, *nv; + h_state horiz[2]; + int in[2], exists[2], parity[2]= {LEFT, LEFT}; + int c, v, contributing, search, scanbeam= 0, sbt_entries= 0; + int vclass, bl, br, tl, tr; + double *sbt= NULL, xb, px, yb, yt, dy, ix, iy; + + /* Test for trivial NULL result cases */ + if (((subj->num_contours == 0) && (clip->num_contours == 0)) + || ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF))) + || ((clip->num_contours == 0) && (op == GPC_INT))) + { + result->num_contours= 0; + result->hole= NULL; + result->contour= NULL; + return; + } + + /* Identify potentialy contributing contours */ + if (((op == GPC_INT) || (op == GPC_DIFF)) + && (subj->num_contours > 0) && (clip->num_contours > 0)) + minimax_test(subj, clip, op); + + /* Build LMT */ + if (subj->num_contours > 0) + s_heap= build_lmt(&lmt, &sbtree, &sbt_entries, subj, SUBJ, op); + if (clip->num_contours > 0) + c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op); + + /* Return a NULL result if no contours contribute */ + if (lmt == NULL) + { + result->num_contours= 0; + result->hole= NULL; + result->contour= NULL; + reset_lmt(&lmt); + FREE(s_heap); + FREE(c_heap); + return; + } + + /* Build scanbeam table from scanbeam tree */ + MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double); + build_sbt(&scanbeam, sbt, sbtree); + scanbeam= 0; + free_sbtree(&sbtree); + + /* Allow pointer re-use without causing memory leak */ + if (subj == result) + gpc_free_polygon(subj); + if (clip == result) + gpc_free_polygon(clip); + + /* Invert clip polygon for difference operation */ + if (op == GPC_DIFF) + parity[CLIP]= RIGHT; + + local_min= lmt; + + /* Process each scanbeam */ + while (scanbeam < sbt_entries) + { + /* Set yb and yt to the bottom and top of the scanbeam */ + yb= sbt[scanbeam++]; + if (scanbeam < sbt_entries) + { + yt= sbt[scanbeam]; + dy= yt - yb; + } + + /* === SCANBEAM BOUNDARY PROCESSING ================================ */ + + /* If LMT node corresponding to yb exists */ + if (local_min) + { + if (local_min->y == yb) + { + /* Add edges starting at this local minimum to the AET */ + for (edge= local_min->first_bound; edge; edge= edge->next_bound) + add_edge_to_aet(&aet, edge, NULL); + + local_min= local_min->next; + } + } + + /* Set dummy previous x value */ + px= -DBL_MAX; + + /* Create bundles within AET */ + e0= aet; + e1= aet; + + /* Set up bundle fields of first edge */ + aet->bundle[ABOVE][ aet->type]= (aet->top.y != yb); + aet->bundle[ABOVE][!aet->type]= FALSE; + aet->bstate[ABOVE]= UNBUNDLED; + + for (next_edge= aet->next; next_edge; next_edge= next_edge->next) + { + /* Set up bundle fields of next edge */ + next_edge->bundle[ABOVE][ next_edge->type]= (next_edge->top.y != yb); + next_edge->bundle[ABOVE][!next_edge->type]= FALSE; + next_edge->bstate[ABOVE]= UNBUNDLED; + + /* Bundle edges above the scanbeam boundary if they coincide */ + if (next_edge->bundle[ABOVE][next_edge->type]) + { + if (EQ(e0->xb, next_edge->xb) && EQ(e0->dx, next_edge->dx) + && (e0->top.y != yb)) + { + next_edge->bundle[ABOVE][ next_edge->type]^= + e0->bundle[ABOVE][ next_edge->type]; + next_edge->bundle[ABOVE][!next_edge->type]= + e0->bundle[ABOVE][!next_edge->type]; + next_edge->bstate[ABOVE]= BUNDLE_HEAD; + e0->bundle[ABOVE][CLIP]= FALSE; + e0->bundle[ABOVE][SUBJ]= FALSE; + e0->bstate[ABOVE]= BUNDLE_TAIL; + } + e0= next_edge; + } + } + + horiz[CLIP]= NH; + horiz[SUBJ]= NH; + + /* Process each edge at this scanbeam boundary */ + for (edge= aet; edge; edge= edge->next) + { + exists[CLIP]= edge->bundle[ABOVE][CLIP] + + (edge->bundle[BELOW][CLIP] << 1); + exists[SUBJ]= edge->bundle[ABOVE][SUBJ] + + (edge->bundle[BELOW][SUBJ] << 1); + + if (exists[CLIP] || exists[SUBJ]) + { + /* Set bundle side */ + edge->bside[CLIP]= parity[CLIP]; + edge->bside[SUBJ]= parity[SUBJ]; + + /* Determine contributing status and quadrant occupancies */ + switch (op) + { + case GPC_DIFF: + case GPC_INT: + contributing= (exists[CLIP] && (parity[SUBJ] || horiz[SUBJ])) + || (exists[SUBJ] && (parity[CLIP] || horiz[CLIP])) + || (exists[CLIP] && exists[SUBJ] + && (parity[CLIP] == parity[SUBJ])); + br= (parity[CLIP]) + && (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + && (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + && (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + && (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + case GPC_XOR: + contributing= exists[CLIP] || exists[SUBJ]; + br= (parity[CLIP]) + ^ (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + ^ (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + ^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + ^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + case GPC_UNION: + contributing= (exists[CLIP] && (!parity[SUBJ] || horiz[SUBJ])) + || (exists[SUBJ] && (!parity[CLIP] || horiz[CLIP])) + || (exists[CLIP] && exists[SUBJ] + && (parity[CLIP] == parity[SUBJ])); + br= (parity[CLIP]) + || (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + || (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + || (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + || (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + } + + /* Update parity */ + parity[CLIP]^= edge->bundle[ABOVE][CLIP]; + parity[SUBJ]^= edge->bundle[ABOVE][SUBJ]; + + /* Update horizontal state */ + if (exists[CLIP]) + horiz[CLIP]= + next_h_state[horiz[CLIP]] + [((exists[CLIP] - 1) << 1) + parity[CLIP]]; + if (exists[SUBJ]) + horiz[SUBJ]= + next_h_state[horiz[SUBJ]] + [((exists[SUBJ] - 1) << 1) + parity[SUBJ]]; + + vclass= tr + (tl << 1) + (br << 2) + (bl << 3); + + if (contributing) + { + xb= edge->xb; + + switch (vclass) + { + case EMN: + case IMN: + add_local_min(&out_poly, edge, xb, yb); + px= xb; + cf= edge->outp[ABOVE]; + break; + case ERI: + if (xb != px) + { + add_right(cf, xb, yb); + px= xb; + } + edge->outp[ABOVE]= cf; + cf= NULL; + break; + case ELI: + add_left(edge->outp[BELOW], xb, yb); + px= xb; + cf= edge->outp[BELOW]; + break; + case EMX: + if (xb != px) + { + add_left(cf, xb, yb); + px= xb; + } + merge_right(cf, edge->outp[BELOW], out_poly); + cf= NULL; + break; + case ILI: + if (xb != px) + { + add_left(cf, xb, yb); + px= xb; + } + edge->outp[ABOVE]= cf; + cf= NULL; + break; + case IRI: + add_right(edge->outp[BELOW], xb, yb); + px= xb; + cf= edge->outp[BELOW]; + edge->outp[BELOW]= NULL; + break; + case IMX: + if (xb != px) + { + add_right(cf, xb, yb); + px= xb; + } + merge_left(cf, edge->outp[BELOW], out_poly); + cf= NULL; + edge->outp[BELOW]= NULL; + break; + case IMM: + if (xb != px) + { + add_right(cf, xb, yb); + px= xb; + } + merge_left(cf, edge->outp[BELOW], out_poly); + edge->outp[BELOW]= NULL; + add_local_min(&out_poly, edge, xb, yb); + cf= edge->outp[ABOVE]; + break; + case EMM: + if (xb != px) + { + add_left(cf, xb, yb); + px= xb; + } + merge_right(cf, edge->outp[BELOW], out_poly); + edge->outp[BELOW]= NULL; + add_local_min(&out_poly, edge, xb, yb); + cf= edge->outp[ABOVE]; + break; + case LED: + if (edge->bot.y == yb) + add_left(edge->outp[BELOW], xb, yb); + edge->outp[ABOVE]= edge->outp[BELOW]; + px= xb; + break; + case RED: + if (edge->bot.y == yb) + add_right(edge->outp[BELOW], xb, yb); + edge->outp[ABOVE]= edge->outp[BELOW]; + px= xb; + break; + default: + break; + } /* End of switch */ + } /* End of contributing conditional */ + } /* End of edge exists conditional */ + } /* End of AET loop */ + + /* Delete terminating edges from the AET, otherwise compute xt */ + for (edge= aet; edge; edge= edge->next) + { + if (edge->top.y == yb) + { + prev_edge= edge->prev; + next_edge= edge->next; + if (prev_edge) + prev_edge->next= next_edge; + else + aet= next_edge; + if (next_edge) + next_edge->prev= prev_edge; + + /* Copy bundle head state to the adjacent tail edge if required */ + if ((edge->bstate[BELOW] == BUNDLE_HEAD) && prev_edge) + { + if (prev_edge->bstate[BELOW] == BUNDLE_TAIL) + { + prev_edge->outp[BELOW]= edge->outp[BELOW]; + prev_edge->bstate[BELOW]= UNBUNDLED; + if (prev_edge->prev) + if (prev_edge->prev->bstate[BELOW] == BUNDLE_TAIL) + prev_edge->bstate[BELOW]= BUNDLE_HEAD; + } + } + } + else + { + if (edge->top.y == yt) + edge->xt= edge->top.x; + else + edge->xt= edge->bot.x + edge->dx * (yt - edge->bot.y); + } + } + + if (scanbeam < sbt_entries) + { + /* === SCANBEAM INTERIOR PROCESSING ============================== */ + + build_intersection_table(&it, aet, dy); + + /* Process each node in the intersection table */ + for (intersect= it; intersect; intersect= intersect->next) + { + e0= intersect->ie[0]; + e1= intersect->ie[1]; + + /* Only generate output for contributing intersections */ + if ((e0->bundle[ABOVE][CLIP] || e0->bundle[ABOVE][SUBJ]) + && (e1->bundle[ABOVE][CLIP] || e1->bundle[ABOVE][SUBJ])) + { + p= e0->outp[ABOVE]; + q= e1->outp[ABOVE]; + ix= intersect->point.x; + iy= intersect->point.y + yb; + + in[CLIP]= ( e0->bundle[ABOVE][CLIP] && !e0->bside[CLIP]) + || ( e1->bundle[ABOVE][CLIP] && e1->bside[CLIP]) + || (!e0->bundle[ABOVE][CLIP] && !e1->bundle[ABOVE][CLIP] + && e0->bside[CLIP] && e1->bside[CLIP]); + in[SUBJ]= ( e0->bundle[ABOVE][SUBJ] && !e0->bside[SUBJ]) + || ( e1->bundle[ABOVE][SUBJ] && e1->bside[SUBJ]) + || (!e0->bundle[ABOVE][SUBJ] && !e1->bundle[ABOVE][SUBJ] + && e0->bside[SUBJ] && e1->bside[SUBJ]); + + /* Determine quadrant occupancies */ + switch (op) + { + case GPC_DIFF: + case GPC_INT: + tr= (in[CLIP]) + && (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + case GPC_XOR: + tr= (in[CLIP]) + ^ (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + case GPC_UNION: + tr= (in[CLIP]) + || (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + } + + vclass= tr + (tl << 1) + (br << 2) + (bl << 3); + + switch (vclass) + { + case EMN: + add_local_min(&out_poly, e0, ix, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + break; + case ERI: + if (p) + { + add_right(p, ix, iy); + e1->outp[ABOVE]= p; + e0->outp[ABOVE]= NULL; + } + break; + case ELI: + if (q) + { + add_left(q, ix, iy); + e0->outp[ABOVE]= q; + e1->outp[ABOVE]= NULL; + } + break; + case EMX: + if (p && q) + { + add_left(p, ix, iy); + merge_right(p, q, out_poly); + e0->outp[ABOVE]= NULL; + e1->outp[ABOVE]= NULL; + } + break; + case IMN: + add_local_min(&out_poly, e0, ix, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + break; + case ILI: + if (p) + { + add_left(p, ix, iy); + e1->outp[ABOVE]= p; + e0->outp[ABOVE]= NULL; + } + break; + case IRI: + if (q) + { + add_right(q, ix, iy); + e0->outp[ABOVE]= q; + e1->outp[ABOVE]= NULL; + } + break; + case IMX: + if (p && q) + { + add_right(p, ix, iy); + merge_left(p, q, out_poly); + e0->outp[ABOVE]= NULL; + e1->outp[ABOVE]= NULL; + } + break; + case IMM: + if (p && q) + { + add_right(p, ix, iy); + merge_left(p, q, out_poly); + add_local_min(&out_poly, e0, ix, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + } + break; + case EMM: + if (p && q) + { + add_left(p, ix, iy); + merge_right(p, q, out_poly); + add_local_min(&out_poly, e0, ix, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + } + break; + default: + break; + } /* End of switch */ + } /* End of contributing intersection conditional */ + + /* Swap bundle sides in response to edge crossing */ + if (e0->bundle[ABOVE][CLIP]) + e1->bside[CLIP]= !e1->bside[CLIP]; + if (e1->bundle[ABOVE][CLIP]) + e0->bside[CLIP]= !e0->bside[CLIP]; + if (e0->bundle[ABOVE][SUBJ]) + e1->bside[SUBJ]= !e1->bside[SUBJ]; + if (e1->bundle[ABOVE][SUBJ]) + e0->bside[SUBJ]= !e0->bside[SUBJ]; + + /* Swap e0 and e1 bundles in the AET */ + prev_edge= e0->prev; + next_edge= e1->next; + if (next_edge) + next_edge->prev= e0; + + if (e0->bstate[ABOVE] == BUNDLE_HEAD) + { + search= TRUE; + while (search) + { + prev_edge= prev_edge->prev; + if (prev_edge) + { + if (prev_edge->bstate[ABOVE] != BUNDLE_TAIL) + search= FALSE; + } + else + search= FALSE; + } + } + if (!prev_edge) + { + aet->prev= e1; + e1->next= aet; + aet= e0->next; + } + else + { + prev_edge->next->prev= e1; + e1->next= prev_edge->next; + prev_edge->next= e0->next; + } + e0->next->prev= prev_edge; + e1->next->prev= e1; + e0->next= next_edge; + } /* End of IT loop*/ + + /* Prepare for next scanbeam */ + for (edge= aet; edge; edge= next_edge) + { + next_edge= edge->next; + succ_edge= edge->succ; + + if ((edge->top.y == yt) && succ_edge) + { + /* Replace AET edge by its successor */ + succ_edge->outp[BELOW]= edge->outp[ABOVE]; + succ_edge->bstate[BELOW]= edge->bstate[ABOVE]; + succ_edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP]; + succ_edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; + prev_edge= edge->prev; + if (prev_edge) + prev_edge->next= succ_edge; + else + aet= succ_edge; + if (next_edge) + next_edge->prev= succ_edge; + succ_edge->prev= prev_edge; + succ_edge->next= next_edge; + } + else + { + /* Update this edge */ + edge->outp[BELOW]= edge->outp[ABOVE]; + edge->bstate[BELOW]= edge->bstate[ABOVE]; + edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP]; + edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; + edge->xb= edge->xt; + } + edge->outp[ABOVE]= NULL; + } + } + } /* === END OF SCANBEAM PROCESSING ================================== */ + + /* Generate result polygon from out_poly */ + result->contour= NULL; + result->hole= NULL; + result->num_contours= count_contours(out_poly); + if (result->num_contours > 0) + { + MALLOC(result->hole, result->num_contours + * sizeof(int), "hole flag table creation", int); + MALLOC(result->contour, result->num_contours + * sizeof(gpc_vertex_list), "contour creation", gpc_vertex_list); + + c= 0; + for (poly= out_poly; poly; poly= npoly) + { + npoly= poly->next; + if (poly->active) + { + result->hole[c]= poly->proxy->hole; + result->contour[c].num_vertices= poly->active; + MALLOC(result->contour[c].vertex, + result->contour[c].num_vertices * sizeof(gpc_vertex), + "vertex creation", gpc_vertex); + + v= result->contour[c].num_vertices - 1; + for (vtx= poly->proxy->v[LEFT]; vtx; vtx= nv) + { + nv= vtx->next; + result->contour[c].vertex[v].x= vtx->x; + result->contour[c].vertex[v].y= vtx->y; + FREE(vtx); + v--; + } + c++; + } + FREE(poly); + } + } + else + { + for (poly= out_poly; poly; poly= npoly) + { + npoly= poly->next; + FREE(poly); + } + } + + /* Tidy up */ + reset_it(&it); + reset_lmt(&lmt); + FREE(c_heap); + FREE(s_heap); + FREE(sbt); +} + + +void gpc_free_tristrip(gpc_tristrip *t) +{ + int s; + + for (s= 0; s < t->num_strips; s++) + FREE(t->strip[s].vertex); + FREE(t->strip); + t->num_strips= 0; +} + + +void gpc_polygon_to_tristrip(gpc_polygon *s, gpc_tristrip *t) +{ + gpc_polygon c; + + c.num_contours= 0; + c.hole= NULL; + c.contour= NULL; + gpc_tristrip_clip(GPC_DIFF, s, &c, t); +} + + +void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, + gpc_tristrip *result) +{ + sb_tree *sbtree= NULL; + it_node *it= NULL, *intersect; + edge_node *edge, *prev_edge, *next_edge, *succ_edge, *e0, *e1; + edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL, *cf; + lmt_node *lmt= NULL, *local_min; + polygon_node *tlist= NULL, *tn, *tnn, *p, *q; + vertex_node *lt, *ltn, *rt, *rtn; + h_state horiz[2]; + vertex_type cft; + int in[2], exists[2], parity[2]= {LEFT, LEFT}; + int s, v, contributing, search, scanbeam= 0, sbt_entries= 0; + int vclass, bl, br, tl, tr; + double *sbt= NULL, xb, px, nx, yb, yt, dy, ix, iy; + + /* Test for trivial NULL result cases */ + if (((subj->num_contours == 0) && (clip->num_contours == 0)) + || ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF))) + || ((clip->num_contours == 0) && (op == GPC_INT))) + { + result->num_strips= 0; + result->strip= NULL; + return; + } + + /* Identify potentialy contributing contours */ + if (((op == GPC_INT) || (op == GPC_DIFF)) + && (subj->num_contours > 0) && (clip->num_contours > 0)) + minimax_test(subj, clip, op); + + /* Build LMT */ + if (subj->num_contours > 0) + s_heap= build_lmt(&lmt, &sbtree, &sbt_entries, subj, SUBJ, op); + if (clip->num_contours > 0) + c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op); + + /* Return a NULL result if no contours contribute */ + if (lmt == NULL) + { + result->num_strips= 0; + result->strip= NULL; + reset_lmt(&lmt); + FREE(s_heap); + FREE(c_heap); + return; + } + + /* Build scanbeam table from scanbeam tree */ + MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double); + build_sbt(&scanbeam, sbt, sbtree); + scanbeam= 0; + free_sbtree(&sbtree); + + /* Invert clip polygon for difference operation */ + if (op == GPC_DIFF) + parity[CLIP]= RIGHT; + + local_min= lmt; + + /* Process each scanbeam */ + while (scanbeam < sbt_entries) + { + /* Set yb and yt to the bottom and top of the scanbeam */ + yb= sbt[scanbeam++]; + if (scanbeam < sbt_entries) + { + yt= sbt[scanbeam]; + dy= yt - yb; + } + + /* === SCANBEAM BOUNDARY PROCESSING ================================ */ + + /* If LMT node corresponding to yb exists */ + if (local_min) + { + if (local_min->y == yb) + { + /* Add edges starting at this local minimum to the AET */ + for (edge= local_min->first_bound; edge; edge= edge->next_bound) + add_edge_to_aet(&aet, edge, NULL); + + local_min= local_min->next; + } + } + + /* Set dummy previous x value */ + px= -DBL_MAX; + + /* Create bundles within AET */ + e0= aet; + e1= aet; + + /* Set up bundle fields of first edge */ + aet->bundle[ABOVE][ aet->type]= (aet->top.y != yb); + aet->bundle[ABOVE][!aet->type]= FALSE; + aet->bstate[ABOVE]= UNBUNDLED; + + for (next_edge= aet->next; next_edge; next_edge= next_edge->next) + { + /* Set up bundle fields of next edge */ + next_edge->bundle[ABOVE][ next_edge->type]= (next_edge->top.y != yb); + next_edge->bundle[ABOVE][!next_edge->type]= FALSE; + next_edge->bstate[ABOVE]= UNBUNDLED; + + /* Bundle edges above the scanbeam boundary if they coincide */ + if (next_edge->bundle[ABOVE][next_edge->type]) + { + if (EQ(e0->xb, next_edge->xb) && EQ(e0->dx, next_edge->dx) + && (e0->top.y != yb)) + { + next_edge->bundle[ABOVE][ next_edge->type]^= + e0->bundle[ABOVE][ next_edge->type]; + next_edge->bundle[ABOVE][!next_edge->type]= + e0->bundle[ABOVE][!next_edge->type]; + next_edge->bstate[ABOVE]= BUNDLE_HEAD; + e0->bundle[ABOVE][CLIP]= FALSE; + e0->bundle[ABOVE][SUBJ]= FALSE; + e0->bstate[ABOVE]= BUNDLE_TAIL; + } + e0= next_edge; + } + } + + horiz[CLIP]= NH; + horiz[SUBJ]= NH; + + /* Process each edge at this scanbeam boundary */ + for (edge= aet; edge; edge= edge->next) + { + exists[CLIP]= edge->bundle[ABOVE][CLIP] + + (edge->bundle[BELOW][CLIP] << 1); + exists[SUBJ]= edge->bundle[ABOVE][SUBJ] + + (edge->bundle[BELOW][SUBJ] << 1); + + if (exists[CLIP] || exists[SUBJ]) + { + /* Set bundle side */ + edge->bside[CLIP]= parity[CLIP]; + edge->bside[SUBJ]= parity[SUBJ]; + + /* Determine contributing status and quadrant occupancies */ + switch (op) + { + case GPC_DIFF: + case GPC_INT: + contributing= (exists[CLIP] && (parity[SUBJ] || horiz[SUBJ])) + || (exists[SUBJ] && (parity[CLIP] || horiz[CLIP])) + || (exists[CLIP] && exists[SUBJ] + && (parity[CLIP] == parity[SUBJ])); + br= (parity[CLIP]) + && (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + && (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + && (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + && (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + case GPC_XOR: + contributing= exists[CLIP] || exists[SUBJ]; + br= (parity[CLIP]) + ^ (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + ^ (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + ^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + ^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + case GPC_UNION: + contributing= (exists[CLIP] && (!parity[SUBJ] || horiz[SUBJ])) + || (exists[SUBJ] && (!parity[CLIP] || horiz[CLIP])) + || (exists[CLIP] && exists[SUBJ] + && (parity[CLIP] == parity[SUBJ])); + br= (parity[CLIP]) + || (parity[SUBJ]); + bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP]) + || (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]); + tr= (parity[CLIP] ^ (horiz[CLIP]!=NH)) + || (parity[SUBJ] ^ (horiz[SUBJ]!=NH)); + tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP]) + || (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]); + break; + } + + /* Update parity */ + parity[CLIP]^= edge->bundle[ABOVE][CLIP]; + parity[SUBJ]^= edge->bundle[ABOVE][SUBJ]; + + /* Update horizontal state */ + if (exists[CLIP]) + horiz[CLIP]= + next_h_state[horiz[CLIP]] + [((exists[CLIP] - 1) << 1) + parity[CLIP]]; + if (exists[SUBJ]) + horiz[SUBJ]= + next_h_state[horiz[SUBJ]] + [((exists[SUBJ] - 1) << 1) + parity[SUBJ]]; + + vclass= tr + (tl << 1) + (br << 2) + (bl << 3); + + if (contributing) + { + xb= edge->xb; + + switch (vclass) + { + case EMN: + new_tristrip(&tlist, edge, xb, yb); + cf= edge; + break; + case ERI: + edge->outp[ABOVE]= cf->outp[ABOVE]; + if (xb != cf->xb) + VERTEX(edge, ABOVE, RIGHT, xb, yb); + cf= NULL; + break; + case ELI: + VERTEX(edge, BELOW, LEFT, xb, yb); + edge->outp[ABOVE]= NULL; + cf= edge; + break; + case EMX: + if (xb != cf->xb) + VERTEX(edge, BELOW, RIGHT, xb, yb); + edge->outp[ABOVE]= NULL; + cf= NULL; + break; + case IMN: + if (cft == LED) + { + if (cf->bot.y != yb) + VERTEX(cf, BELOW, LEFT, cf->xb, yb); + new_tristrip(&tlist, cf, cf->xb, yb); + } + edge->outp[ABOVE]= cf->outp[ABOVE]; + VERTEX(edge, ABOVE, RIGHT, xb, yb); + break; + case ILI: + new_tristrip(&tlist, edge, xb, yb); + cf= edge; + cft= ILI; + break; + case IRI: + if (cft == LED) + { + if (cf->bot.y != yb) + VERTEX(cf, BELOW, LEFT, cf->xb, yb); + new_tristrip(&tlist, cf, cf->xb, yb); + } + VERTEX(edge, BELOW, RIGHT, xb, yb); + edge->outp[ABOVE]= NULL; + break; + case IMX: + VERTEX(edge, BELOW, LEFT, xb, yb); + edge->outp[ABOVE]= NULL; + cft= IMX; + break; + case IMM: + VERTEX(edge, BELOW, LEFT, xb, yb); + edge->outp[ABOVE]= cf->outp[ABOVE]; + if (xb != cf->xb) + VERTEX(cf, ABOVE, RIGHT, xb, yb); + cf= edge; + break; + case EMM: + VERTEX(edge, BELOW, RIGHT, xb, yb); + edge->outp[ABOVE]= NULL; + new_tristrip(&tlist, edge, xb, yb); + cf= edge; + break; + case LED: + if (edge->bot.y == yb) + VERTEX(edge, BELOW, LEFT, xb, yb); + edge->outp[ABOVE]= edge->outp[BELOW]; + cf= edge; + cft= LED; + break; + case RED: + edge->outp[ABOVE]= cf->outp[ABOVE]; + if (cft == LED) + { + if (cf->bot.y == yb) + { + VERTEX(edge, BELOW, RIGHT, xb, yb); + } + else + { + if (edge->bot.y == yb) + { + VERTEX(cf, BELOW, LEFT, cf->xb, yb); + VERTEX(edge, BELOW, RIGHT, xb, yb); + } + } + } + else + { + VERTEX(edge, BELOW, RIGHT, xb, yb); + VERTEX(edge, ABOVE, RIGHT, xb, yb); + } + cf= NULL; + break; + default: + break; + } /* End of switch */ + } /* End of contributing conditional */ + } /* End of edge exists conditional */ + } /* End of AET loop */ + + /* Delete terminating edges from the AET, otherwise compute xt */ + for (edge= aet; edge; edge= edge->next) + { + if (edge->top.y == yb) + { + prev_edge= edge->prev; + next_edge= edge->next; + if (prev_edge) + prev_edge->next= next_edge; + else + aet= next_edge; + if (next_edge) + next_edge->prev= prev_edge; + + /* Copy bundle head state to the adjacent tail edge if required */ + if ((edge->bstate[BELOW] == BUNDLE_HEAD) && prev_edge) + { + if (prev_edge->bstate[BELOW] == BUNDLE_TAIL) + { + prev_edge->outp[BELOW]= edge->outp[BELOW]; + prev_edge->bstate[BELOW]= UNBUNDLED; + if (prev_edge->prev) + if (prev_edge->prev->bstate[BELOW] == BUNDLE_TAIL) + prev_edge->bstate[BELOW]= BUNDLE_HEAD; + } + } + } + else + { + if (edge->top.y == yt) + edge->xt= edge->top.x; + else + edge->xt= edge->bot.x + edge->dx * (yt - edge->bot.y); + } + } + + if (scanbeam < sbt_entries) + { + /* === SCANBEAM INTERIOR PROCESSING ============================== */ + + build_intersection_table(&it, aet, dy); + + /* Process each node in the intersection table */ + for (intersect= it; intersect; intersect= intersect->next) + { + e0= intersect->ie[0]; + e1= intersect->ie[1]; + + /* Only generate output for contributing intersections */ + if ((e0->bundle[ABOVE][CLIP] || e0->bundle[ABOVE][SUBJ]) + && (e1->bundle[ABOVE][CLIP] || e1->bundle[ABOVE][SUBJ])) + { + p= e0->outp[ABOVE]; + q= e1->outp[ABOVE]; + ix= intersect->point.x; + iy= intersect->point.y + yb; + + in[CLIP]= ( e0->bundle[ABOVE][CLIP] && !e0->bside[CLIP]) + || ( e1->bundle[ABOVE][CLIP] && e1->bside[CLIP]) + || (!e0->bundle[ABOVE][CLIP] && !e1->bundle[ABOVE][CLIP] + && e0->bside[CLIP] && e1->bside[CLIP]); + in[SUBJ]= ( e0->bundle[ABOVE][SUBJ] && !e0->bside[SUBJ]) + || ( e1->bundle[ABOVE][SUBJ] && e1->bside[SUBJ]) + || (!e0->bundle[ABOVE][SUBJ] && !e1->bundle[ABOVE][SUBJ] + && e0->bside[SUBJ] && e1->bside[SUBJ]); + + /* Determine quadrant occupancies */ + switch (op) + { + case GPC_DIFF: + case GPC_INT: + tr= (in[CLIP]) + && (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + && (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + case GPC_XOR: + tr= (in[CLIP]) + ^ (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + ^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + case GPC_UNION: + tr= (in[CLIP]) + || (in[SUBJ]); + tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]); + br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP]) + || (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]); + break; + } + + vclass= tr + (tl << 1) + (br << 2) + (bl << 3); + + switch (vclass) + { + case EMN: + new_tristrip(&tlist, e1, ix, iy); + e0->outp[ABOVE]= e1->outp[ABOVE]; + break; + case ERI: + if (p) + { + P_EDGE(prev_edge, e0, ABOVE, px, iy); + VERTEX(prev_edge, ABOVE, LEFT, px, iy); + VERTEX(e0, ABOVE, RIGHT, ix, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + e0->outp[ABOVE]= NULL; + } + break; + case ELI: + if (q) + { + N_EDGE(next_edge, e1, ABOVE, nx, iy); + VERTEX(e1, ABOVE, LEFT, ix, iy); + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + e0->outp[ABOVE]= e1->outp[ABOVE]; + e1->outp[ABOVE]= NULL; + } + break; + case EMX: + if (p && q) + { + VERTEX(e0, ABOVE, LEFT, ix, iy); + e0->outp[ABOVE]= NULL; + e1->outp[ABOVE]= NULL; + } + break; + case IMN: + P_EDGE(prev_edge, e0, ABOVE, px, iy); + VERTEX(prev_edge, ABOVE, LEFT, px, iy); + N_EDGE(next_edge, e1, ABOVE, nx, iy); + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + new_tristrip(&tlist, prev_edge, px, iy); + e1->outp[ABOVE]= prev_edge->outp[ABOVE]; + VERTEX(e1, ABOVE, RIGHT, ix, iy); + new_tristrip(&tlist, e0, ix, iy); + next_edge->outp[ABOVE]= e0->outp[ABOVE]; + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + break; + case ILI: + if (p) + { + VERTEX(e0, ABOVE, LEFT, ix, iy); + N_EDGE(next_edge, e1, ABOVE, nx, iy); + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + e1->outp[ABOVE]= e0->outp[ABOVE]; + e0->outp[ABOVE]= NULL; + } + break; + case IRI: + if (q) + { + VERTEX(e1, ABOVE, RIGHT, ix, iy); + P_EDGE(prev_edge, e0, ABOVE, px, iy); + VERTEX(prev_edge, ABOVE, LEFT, px, iy); + e0->outp[ABOVE]= e1->outp[ABOVE]; + e1->outp[ABOVE]= NULL; + } + break; + case IMX: + if (p && q) + { + VERTEX(e0, ABOVE, RIGHT, ix, iy); + VERTEX(e1, ABOVE, LEFT, ix, iy); + e0->outp[ABOVE]= NULL; + e1->outp[ABOVE]= NULL; + P_EDGE(prev_edge, e0, ABOVE, px, iy); + VERTEX(prev_edge, ABOVE, LEFT, px, iy); + new_tristrip(&tlist, prev_edge, px, iy); + N_EDGE(next_edge, e1, ABOVE, nx, iy); + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + next_edge->outp[ABOVE]= prev_edge->outp[ABOVE]; + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + } + break; + case IMM: + if (p && q) + { + VERTEX(e0, ABOVE, RIGHT, ix, iy); + VERTEX(e1, ABOVE, LEFT, ix, iy); + P_EDGE(prev_edge, e0, ABOVE, px, iy); + VERTEX(prev_edge, ABOVE, LEFT, px, iy); + new_tristrip(&tlist, prev_edge, px, iy); + N_EDGE(next_edge, e1, ABOVE, nx, iy); + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + e1->outp[ABOVE]= prev_edge->outp[ABOVE]; + VERTEX(e1, ABOVE, RIGHT, ix, iy); + new_tristrip(&tlist, e0, ix, iy); + next_edge->outp[ABOVE]= e0->outp[ABOVE]; + VERTEX(next_edge, ABOVE, RIGHT, nx, iy); + } + break; + case EMM: + if (p && q) + { + VERTEX(e0, ABOVE, LEFT, ix, iy); + new_tristrip(&tlist, e1, ix, iy); + e0->outp[ABOVE]= e1->outp[ABOVE]; + } + break; + default: + break; + } /* End of switch */ + } /* End of contributing intersection conditional */ + + /* Swap bundle sides in response to edge crossing */ + if (e0->bundle[ABOVE][CLIP]) + e1->bside[CLIP]= !e1->bside[CLIP]; + if (e1->bundle[ABOVE][CLIP]) + e0->bside[CLIP]= !e0->bside[CLIP]; + if (e0->bundle[ABOVE][SUBJ]) + e1->bside[SUBJ]= !e1->bside[SUBJ]; + if (e1->bundle[ABOVE][SUBJ]) + e0->bside[SUBJ]= !e0->bside[SUBJ]; + + /* Swap e0 and e1 bundles in the AET */ + prev_edge= e0->prev; + next_edge= e1->next; + if (e1->next) + e1->next->prev= e0; + + if (e0->bstate[ABOVE] == BUNDLE_HEAD) + { + search= TRUE; + while (search) + { + prev_edge= prev_edge->prev; + if (prev_edge) + { + if (prev_edge->bundle[ABOVE][CLIP] + || prev_edge->bundle[ABOVE][SUBJ] + || (prev_edge->bstate[ABOVE] == BUNDLE_HEAD)) + search= FALSE; + } + else + search= FALSE; + } + } + if (!prev_edge) + { + e1->next= aet; + aet= e0->next; + } + else + { + e1->next= prev_edge->next; + prev_edge->next= e0->next; + } + e0->next->prev= prev_edge; + e1->next->prev= e1; + e0->next= next_edge; + } /* End of IT loop*/ + + /* Prepare for next scanbeam */ + for (edge= aet; edge; edge= next_edge) + { + next_edge= edge->next; + succ_edge= edge->succ; + + if ((edge->top.y == yt) && succ_edge) + { + /* Replace AET edge by its successor */ + succ_edge->outp[BELOW]= edge->outp[ABOVE]; + succ_edge->bstate[BELOW]= edge->bstate[ABOVE]; + succ_edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP]; + succ_edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; + prev_edge= edge->prev; + if (prev_edge) + prev_edge->next= succ_edge; + else + aet= succ_edge; + if (next_edge) + next_edge->prev= succ_edge; + succ_edge->prev= prev_edge; + succ_edge->next= next_edge; + } + else + { + /* Update this edge */ + edge->outp[BELOW]= edge->outp[ABOVE]; + edge->bstate[BELOW]= edge->bstate[ABOVE]; + edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP]; + edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ]; + edge->xb= edge->xt; + } + edge->outp[ABOVE]= NULL; + } + } + } /* === END OF SCANBEAM PROCESSING ================================== */ + + /* Generate result tristrip from tlist */ + result->strip= NULL; + result->num_strips= count_tristrips(tlist); + if (result->num_strips > 0) + { + MALLOC(result->strip, result->num_strips * sizeof(gpc_vertex_list), + "tristrip list creation", gpc_vertex_list); + + s= 0; + for (tn= tlist; tn; tn= tnn) + { + tnn= tn->next; + + if (tn->active > 2) + { + /* Valid tristrip: copy the vertices and free the heap */ + result->strip[s].num_vertices= tn->active; + MALLOC(result->strip[s].vertex, tn->active * sizeof(gpc_vertex), + "tristrip creation", gpc_vertex); + v= 0; + if (INVERT_TRISTRIPS) + { + lt= tn->v[RIGHT]; + rt= tn->v[LEFT]; + } + else + { + lt= tn->v[LEFT]; + rt= tn->v[RIGHT]; + } + while (lt || rt) + { + if (lt) + { + ltn= lt->next; + result->strip[s].vertex[v].x= lt->x; + result->strip[s].vertex[v].y= lt->y; + v++; + FREE(lt); + lt= ltn; + } + if (rt) + { + rtn= rt->next; + result->strip[s].vertex[v].x= rt->x; + result->strip[s].vertex[v].y= rt->y; + v++; + FREE(rt); + rt= rtn; + } + } + s++; + } + else + { + /* Invalid tristrip: just free the heap */ + for (lt= tn->v[LEFT]; lt; lt= ltn) + { + ltn= lt->next; + FREE(lt); + } + for (rt= tn->v[RIGHT]; rt; rt=rtn) + { + rtn= rt->next; + FREE(rt); + } + } + FREE(tn); + } + } + + /* Tidy up */ + reset_it(&it); + reset_lmt(&lmt); + FREE(c_heap); + FREE(s_heap); + FREE(sbt); +} + +/* +=========================================================================== + End of file: gpc.c +=========================================================================== +*/ diff --git a/desmume/src/windows/agg/gpc/gpc.h b/desmume/src/windows/agg/gpc/gpc.h new file mode 100644 index 000000000..593539839 --- /dev/null +++ b/desmume/src/windows/agg/gpc/gpc.h @@ -0,0 +1,133 @@ +/* +=========================================================================== + +Project: Generic Polygon Clipper + + A new algorithm for calculating the difference, intersection, + exclusive-or or union of arbitrary polygon sets. + +File: gpc.h +Author: Alan Murta (email: gpc@cs.man.ac.uk) +Version: 2.32 +Date: 17th December 2004 + +Copyright: (C) 1997-2004, Advanced Interfaces Group, + University of Manchester. + + This software is free for non-commercial use. It may be copied, + modified, and redistributed provided that this copyright notice + is preserved on all copies. The intellectual property rights of + the algorithms used reside with the University of Manchester + Advanced Interfaces Group. + + You may not use this software, in whole or in part, in support + of any commercial product without the express consent of the + author. + + There is no warranty or other guarantee of fitness of this + software for any purpose. It is provided solely "as is". + +=========================================================================== +*/ + +#ifndef __gpc_h +#define __gpc_h + +#include + + +/* +=========================================================================== + Constants +=========================================================================== +*/ + +/* Increase GPC_EPSILON to encourage merging of near coincident edges */ + +#define GPC_EPSILON (DBL_EPSILON) + +#define GPC_VERSION "2.32" + + +/* +=========================================================================== + Public Data Types +=========================================================================== +*/ + +typedef enum /* Set operation type */ +{ + GPC_DIFF, /* Difference */ + GPC_INT, /* Intersection */ + GPC_XOR, /* Exclusive or */ + GPC_UNION /* Union */ +} gpc_op; + +typedef struct /* Polygon vertex structure */ +{ + double x; /* Vertex x component */ + double y; /* vertex y component */ +} gpc_vertex; + +typedef struct /* Vertex list structure */ +{ + int num_vertices; /* Number of vertices in list */ + gpc_vertex *vertex; /* Vertex array pointer */ +} gpc_vertex_list; + +typedef struct /* Polygon set structure */ +{ + int num_contours; /* Number of contours in polygon */ + int *hole; /* Hole / external contour flags */ + gpc_vertex_list *contour; /* Contour array pointer */ +} gpc_polygon; + +typedef struct /* Tristrip set structure */ +{ + int num_strips; /* Number of tristrips */ + gpc_vertex_list *strip; /* Tristrip array pointer */ +} gpc_tristrip; + + +/* +=========================================================================== + Public Function Prototypes +=========================================================================== +*/ + +void gpc_read_polygon (FILE *infile_ptr, + int read_hole_flags, + gpc_polygon *polygon); + +void gpc_write_polygon (FILE *outfile_ptr, + int write_hole_flags, + gpc_polygon *polygon); + +void gpc_add_contour (gpc_polygon *polygon, + gpc_vertex_list *contour, + int hole); + +void gpc_polygon_clip (gpc_op set_operation, + gpc_polygon *subject_polygon, + gpc_polygon *clip_polygon, + gpc_polygon *result_polygon); + +void gpc_tristrip_clip (gpc_op set_operation, + gpc_polygon *subject_polygon, + gpc_polygon *clip_polygon, + gpc_tristrip *result_tristrip); + +void gpc_polygon_to_tristrip (gpc_polygon *polygon, + gpc_tristrip *tristrip); + +void gpc_free_polygon (gpc_polygon *polygon); + +void gpc_free_tristrip (gpc_tristrip *tristrip); + +#endif + +/* +=========================================================================== + End of file: gpc.h +=========================================================================== +*/ diff --git a/desmume/src/windows/agg/include/agg_alpha_mask_u8.h b/desmume/src/windows/agg/include/agg_alpha_mask_u8.h new file mode 100644 index 000000000..4a99a94e1 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_alpha_mask_u8.h @@ -0,0 +1,505 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ALPHA_MASK_U8_INCLUDED +#define AGG_ALPHA_MASK_U8_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + //===================================================one_component_mask_u8 + struct one_component_mask_u8 + { + static unsigned calculate(const int8u* p) { return *p; } + }; + + + //=====================================================rgb_to_gray_mask_u8 + template + struct rgb_to_gray_mask_u8 + { + static unsigned calculate(const int8u* p) + { + return (p[R]*77 + p[G]*150 + p[B]*29) >> 8; + } + }; + + //==========================================================alpha_mask_u8 + template + class alpha_mask_u8 + { + public: + typedef int8u cover_type; + typedef alpha_mask_u8 self_type; + enum cover_scale_e + { + cover_shift = 8, + cover_none = 0, + cover_full = 255 + }; + + alpha_mask_u8() : m_rbuf(0) {} + explicit alpha_mask_u8(rendering_buffer& rbuf) : m_rbuf(&rbuf) {} + + void attach(rendering_buffer& rbuf) { m_rbuf = &rbuf; } + + MaskF& mask_function() { return m_mask_function; } + const MaskF& mask_function() const { return m_mask_function; } + + + //-------------------------------------------------------------------- + cover_type pixel(int x, int y) const + { + if(x >= 0 && y >= 0 && + x < (int)m_rbuf->width() && + y < (int)m_rbuf->height()) + { + return (cover_type)m_mask_function.calculate( + m_rbuf->row_ptr(y) + x * Step + Offset); + } + return 0; + } + + //-------------------------------------------------------------------- + cover_type combine_pixel(int x, int y, cover_type val) const + { + if(x >= 0 && y >= 0 && + x < (int)m_rbuf->width() && + y < (int)m_rbuf->height()) + { + return (cover_type)((cover_full + val * + m_mask_function.calculate( + m_rbuf->row_ptr(y) + x * Step + Offset)) >> + cover_shift); + } + return 0; + } + + + //-------------------------------------------------------------------- + void fill_hspan(int x, int y, cover_type* dst, int num_pix) const + { + int xmax = m_rbuf->width() - 1; + int ymax = m_rbuf->height() - 1; + + int count = num_pix; + cover_type* covers = dst; + + if(y < 0 || y > ymax) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + + if(x < 0) + { + count += x; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers, 0, -x * sizeof(cover_type)); + covers -= x; + x = 0; + } + + if(x + count > xmax) + { + int rest = x + count - xmax - 1; + count -= rest; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers + count, 0, rest * sizeof(cover_type)); + } + + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *covers++ = (cover_type)m_mask_function.calculate(mask); + mask += Step; + } + while(--count); + } + + + //-------------------------------------------------------------------- + void combine_hspan(int x, int y, cover_type* dst, int num_pix) const + { + int xmax = m_rbuf->width() - 1; + int ymax = m_rbuf->height() - 1; + + int count = num_pix; + cover_type* covers = dst; + + if(y < 0 || y > ymax) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + + if(x < 0) + { + count += x; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers, 0, -x * sizeof(cover_type)); + covers -= x; + x = 0; + } + + if(x + count > xmax) + { + int rest = x + count - xmax - 1; + count -= rest; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers + count, 0, rest * sizeof(cover_type)); + } + + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *covers = (cover_type)((cover_full + (*covers) * + m_mask_function.calculate(mask)) >> + cover_shift); + ++covers; + mask += Step; + } + while(--count); + } + + //-------------------------------------------------------------------- + void fill_vspan(int x, int y, cover_type* dst, int num_pix) const + { + int xmax = m_rbuf->width() - 1; + int ymax = m_rbuf->height() - 1; + + int count = num_pix; + cover_type* covers = dst; + + if(x < 0 || x > xmax) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + + if(y < 0) + { + count += y; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers, 0, -y * sizeof(cover_type)); + covers -= y; + y = 0; + } + + if(y + count > ymax) + { + int rest = y + count - ymax - 1; + count -= rest; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers + count, 0, rest * sizeof(cover_type)); + } + + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *covers++ = (cover_type)m_mask_function.calculate(mask); + mask += m_rbuf->stride(); + } + while(--count); + } + + //-------------------------------------------------------------------- + void combine_vspan(int x, int y, cover_type* dst, int num_pix) const + { + int xmax = m_rbuf->width() - 1; + int ymax = m_rbuf->height() - 1; + + int count = num_pix; + cover_type* covers = dst; + + if(x < 0 || x > xmax) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + + if(y < 0) + { + count += y; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers, 0, -y * sizeof(cover_type)); + covers -= y; + y = 0; + } + + if(y + count > ymax) + { + int rest = y + count - ymax - 1; + count -= rest; + if(count <= 0) + { + memset(dst, 0, num_pix * sizeof(cover_type)); + return; + } + memset(covers + count, 0, rest * sizeof(cover_type)); + } + + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *covers = (cover_type)((cover_full + (*covers) * + m_mask_function.calculate(mask)) >> + cover_shift); + ++covers; + mask += m_rbuf->stride(); + } + while(--count); + } + + + private: + alpha_mask_u8(const self_type&); + const self_type& operator = (const self_type&); + + rendering_buffer* m_rbuf; + MaskF m_mask_function; + }; + + + typedef alpha_mask_u8<1, 0> alpha_mask_gray8; //----alpha_mask_gray8 + + typedef alpha_mask_u8<3, 0> alpha_mask_rgb24r; //----alpha_mask_rgb24r + typedef alpha_mask_u8<3, 1> alpha_mask_rgb24g; //----alpha_mask_rgb24g + typedef alpha_mask_u8<3, 2> alpha_mask_rgb24b; //----alpha_mask_rgb24b + + typedef alpha_mask_u8<3, 2> alpha_mask_bgr24r; //----alpha_mask_bgr24r + typedef alpha_mask_u8<3, 1> alpha_mask_bgr24g; //----alpha_mask_bgr24g + typedef alpha_mask_u8<3, 0> alpha_mask_bgr24b; //----alpha_mask_bgr24b + + typedef alpha_mask_u8<4, 0> alpha_mask_rgba32r; //----alpha_mask_rgba32r + typedef alpha_mask_u8<4, 1> alpha_mask_rgba32g; //----alpha_mask_rgba32g + typedef alpha_mask_u8<4, 2> alpha_mask_rgba32b; //----alpha_mask_rgba32b + typedef alpha_mask_u8<4, 3> alpha_mask_rgba32a; //----alpha_mask_rgba32a + + typedef alpha_mask_u8<4, 1> alpha_mask_argb32r; //----alpha_mask_argb32r + typedef alpha_mask_u8<4, 2> alpha_mask_argb32g; //----alpha_mask_argb32g + typedef alpha_mask_u8<4, 3> alpha_mask_argb32b; //----alpha_mask_argb32b + typedef alpha_mask_u8<4, 0> alpha_mask_argb32a; //----alpha_mask_argb32a + + typedef alpha_mask_u8<4, 2> alpha_mask_bgra32r; //----alpha_mask_bgra32r + typedef alpha_mask_u8<4, 1> alpha_mask_bgra32g; //----alpha_mask_bgra32g + typedef alpha_mask_u8<4, 0> alpha_mask_bgra32b; //----alpha_mask_bgra32b + typedef alpha_mask_u8<4, 3> alpha_mask_bgra32a; //----alpha_mask_bgra32a + + typedef alpha_mask_u8<4, 3> alpha_mask_abgr32r; //----alpha_mask_abgr32r + typedef alpha_mask_u8<4, 2> alpha_mask_abgr32g; //----alpha_mask_abgr32g + typedef alpha_mask_u8<4, 1> alpha_mask_abgr32b; //----alpha_mask_abgr32b + typedef alpha_mask_u8<4, 0> alpha_mask_abgr32a; //----alpha_mask_abgr32a + + typedef alpha_mask_u8<3, 0, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_rgb24gray; //----alpha_mask_rgb24gray + typedef alpha_mask_u8<3, 0, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_bgr24gray; //----alpha_mask_bgr24gray + typedef alpha_mask_u8<4, 0, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_rgba32gray; //----alpha_mask_rgba32gray + typedef alpha_mask_u8<4, 1, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_argb32gray; //----alpha_mask_argb32gray + typedef alpha_mask_u8<4, 0, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_bgra32gray; //----alpha_mask_bgra32gray + typedef alpha_mask_u8<4, 1, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_abgr32gray; //----alpha_mask_abgr32gray + + + + //==========================================================amask_no_clip_u8 + template + class amask_no_clip_u8 + { + public: + typedef int8u cover_type; + typedef amask_no_clip_u8 self_type; + enum cover_scale_e + { + cover_shift = 8, + cover_none = 0, + cover_full = 255 + }; + + amask_no_clip_u8() : m_rbuf(0) {} + explicit amask_no_clip_u8(rendering_buffer& rbuf) : m_rbuf(&rbuf) {} + + void attach(rendering_buffer& rbuf) { m_rbuf = &rbuf; } + + MaskF& mask_function() { return m_mask_function; } + const MaskF& mask_function() const { return m_mask_function; } + + + //-------------------------------------------------------------------- + cover_type pixel(int x, int y) const + { + return (cover_type)m_mask_function.calculate( + m_rbuf->row_ptr(y) + x * Step + Offset); + } + + + //-------------------------------------------------------------------- + cover_type combine_pixel(int x, int y, cover_type val) const + { + return (cover_type)((cover_full + val * + m_mask_function.calculate( + m_rbuf->row_ptr(y) + x * Step + Offset)) >> + cover_shift); + } + + + //-------------------------------------------------------------------- + void fill_hspan(int x, int y, cover_type* dst, int num_pix) const + { + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *dst++ = (cover_type)m_mask_function.calculate(mask); + mask += Step; + } + while(--num_pix); + } + + + + //-------------------------------------------------------------------- + void combine_hspan(int x, int y, cover_type* dst, int num_pix) const + { + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *dst = (cover_type)((cover_full + (*dst) * + m_mask_function.calculate(mask)) >> + cover_shift); + ++dst; + mask += Step; + } + while(--num_pix); + } + + + //-------------------------------------------------------------------- + void fill_vspan(int x, int y, cover_type* dst, int num_pix) const + { + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *dst++ = (cover_type)m_mask_function.calculate(mask); + mask += m_rbuf->stride(); + } + while(--num_pix); + } + + + //-------------------------------------------------------------------- + void combine_vspan(int x, int y, cover_type* dst, int num_pix) const + { + const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset; + do + { + *dst = (cover_type)((cover_full + (*dst) * + m_mask_function.calculate(mask)) >> + cover_shift); + ++dst; + mask += m_rbuf->stride(); + } + while(--num_pix); + } + + private: + amask_no_clip_u8(const self_type&); + const self_type& operator = (const self_type&); + + rendering_buffer* m_rbuf; + MaskF m_mask_function; + }; + + + typedef amask_no_clip_u8<1, 0> amask_no_clip_gray8; //----amask_no_clip_gray8 + + typedef amask_no_clip_u8<3, 0> amask_no_clip_rgb24r; //----amask_no_clip_rgb24r + typedef amask_no_clip_u8<3, 1> amask_no_clip_rgb24g; //----amask_no_clip_rgb24g + typedef amask_no_clip_u8<3, 2> amask_no_clip_rgb24b; //----amask_no_clip_rgb24b + + typedef amask_no_clip_u8<3, 2> amask_no_clip_bgr24r; //----amask_no_clip_bgr24r + typedef amask_no_clip_u8<3, 1> amask_no_clip_bgr24g; //----amask_no_clip_bgr24g + typedef amask_no_clip_u8<3, 0> amask_no_clip_bgr24b; //----amask_no_clip_bgr24b + + typedef amask_no_clip_u8<4, 0> amask_no_clip_rgba32r; //----amask_no_clip_rgba32r + typedef amask_no_clip_u8<4, 1> amask_no_clip_rgba32g; //----amask_no_clip_rgba32g + typedef amask_no_clip_u8<4, 2> amask_no_clip_rgba32b; //----amask_no_clip_rgba32b + typedef amask_no_clip_u8<4, 3> amask_no_clip_rgba32a; //----amask_no_clip_rgba32a + + typedef amask_no_clip_u8<4, 1> amask_no_clip_argb32r; //----amask_no_clip_argb32r + typedef amask_no_clip_u8<4, 2> amask_no_clip_argb32g; //----amask_no_clip_argb32g + typedef amask_no_clip_u8<4, 3> amask_no_clip_argb32b; //----amask_no_clip_argb32b + typedef amask_no_clip_u8<4, 0> amask_no_clip_argb32a; //----amask_no_clip_argb32a + + typedef amask_no_clip_u8<4, 2> amask_no_clip_bgra32r; //----amask_no_clip_bgra32r + typedef amask_no_clip_u8<4, 1> amask_no_clip_bgra32g; //----amask_no_clip_bgra32g + typedef amask_no_clip_u8<4, 0> amask_no_clip_bgra32b; //----amask_no_clip_bgra32b + typedef amask_no_clip_u8<4, 3> amask_no_clip_bgra32a; //----amask_no_clip_bgra32a + + typedef amask_no_clip_u8<4, 3> amask_no_clip_abgr32r; //----amask_no_clip_abgr32r + typedef amask_no_clip_u8<4, 2> amask_no_clip_abgr32g; //----amask_no_clip_abgr32g + typedef amask_no_clip_u8<4, 1> amask_no_clip_abgr32b; //----amask_no_clip_abgr32b + typedef amask_no_clip_u8<4, 0> amask_no_clip_abgr32a; //----amask_no_clip_abgr32a + + typedef amask_no_clip_u8<3, 0, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_rgb24gray; //----amask_no_clip_rgb24gray + typedef amask_no_clip_u8<3, 0, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_bgr24gray; //----amask_no_clip_bgr24gray + typedef amask_no_clip_u8<4, 0, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_rgba32gray; //----amask_no_clip_rgba32gray + typedef amask_no_clip_u8<4, 1, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_argb32gray; //----amask_no_clip_argb32gray + typedef amask_no_clip_u8<4, 0, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_bgra32gray; //----amask_no_clip_bgra32gray + typedef amask_no_clip_u8<4, 1, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_abgr32gray; //----amask_no_clip_abgr32gray + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_arc.h b/desmume/src/windows/agg/include/agg_arc.h new file mode 100644 index 000000000..6ff919e58 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_arc.h @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ARC_INCLUDED +#define AGG_ARC_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //=====================================================================arc + // + // See Implementation agg_arc.cpp + // + class arc + { + public: + arc() : m_scale(1.0), m_initialized(false) {} + arc(double x, double y, + double rx, double ry, + double a1, double a2, + bool ccw=true); + + void init(double x, double y, + double rx, double ry, + double a1, double a2, + bool ccw=true); + + void approximation_scale(double s); + double approximation_scale() const { return m_scale; } + + void rewind(unsigned); + unsigned vertex(double* x, double* y); + + private: + void normalize(double a1, double a2, bool ccw); + + double m_x; + double m_y; + double m_rx; + double m_ry; + double m_angle; + double m_start; + double m_end; + double m_scale; + double m_da; + bool m_ccw; + bool m_initialized; + unsigned m_path_cmd; + }; + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_array.h b/desmume/src/windows/agg/include/agg_array.h new file mode 100644 index 000000000..81ed4dd82 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_array.h @@ -0,0 +1,1129 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ARRAY_INCLUDED +#define AGG_ARRAY_INCLUDED + +#include +#include +#include "agg_basics.h" + +namespace agg +{ + + //-------------------------------------------------------pod_array_adaptor + template class pod_array_adaptor + { + public: + typedef T value_type; + pod_array_adaptor(T* array, unsigned size) : + m_array(array), m_size(size) {} + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T* m_array; + unsigned m_size; + }; + + + //---------------------------------------------------------pod_auto_array + template class pod_auto_array + { + public: + typedef T value_type; + typedef pod_auto_array self_type; + + pod_auto_array() {} + explicit pod_auto_array(const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + } + + const self_type& operator = (const T* c) + { + memcpy(m_array, c, sizeof(T) * Size); + return *this; + } + + static unsigned size() { return Size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + }; + + + //--------------------------------------------------------pod_auto_vector + template class pod_auto_vector + { + public: + typedef T value_type; + typedef pod_auto_vector self_type; + + pod_auto_vector() : m_size(0) {} + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void inc_size(unsigned size) { m_size += size; } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + private: + T m_array[Size]; + unsigned m_size; + }; + + + //---------------------------------------------------------------pod_array + template class pod_array + { + public: + typedef T value_type; + typedef pod_array self_type; + + ~pod_array() { pod_allocator::deallocate(m_array, m_size); } + pod_array() : m_array(0), m_size(0) {} + + pod_array(unsigned size) : + m_array(pod_allocator::allocate(size)), + m_size(size) + {} + + pod_array(const self_type& v) : + m_array(pod_allocator::allocate(v.m_size)), + m_size(v.m_size) + { + memcpy(m_array, v.m_array, sizeof(T) * m_size); + } + + void resize(unsigned size) + { + if(size != m_size) + { + pod_allocator::deallocate(m_array, m_size); + m_array = pod_allocator::allocate(m_size = size); + } + } + const self_type& operator = (const self_type& v) + { + resize(v.size()); + memcpy(m_array, v.m_array, sizeof(T) * m_size); + return *this; + } + + unsigned size() const { return m_size; } + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + private: + T* m_array; + unsigned m_size; + }; + + + + //--------------------------------------------------------------pod_vector + // A simple class template to store Plain Old Data, a vector + // of a fixed size. The data is continous in memory + //------------------------------------------------------------------------ + template class pod_vector + { + public: + typedef T value_type; + + ~pod_vector() { pod_allocator::deallocate(m_array, m_capacity); } + pod_vector() : m_size(0), m_capacity(0), m_array(0) {} + pod_vector(unsigned cap, unsigned extra_tail=0); + + // Copying + pod_vector(const pod_vector&); + const pod_vector& operator = (const pod_vector&); + + // Set new capacity. All data is lost, size is set to zero. + void capacity(unsigned cap, unsigned extra_tail=0); + unsigned capacity() const { return m_capacity; } + + // Allocate n elements. All data is lost, + // but elements can be accessed in range 0...size-1. + void allocate(unsigned size, unsigned extra_tail=0); + + // Resize keeping the content. + void resize(unsigned new_size); + + void zero() + { + memset(m_array, 0, sizeof(T) * m_size); + } + + void add(const T& v) { m_array[m_size++] = v; } + void push_back(const T& v) { m_array[m_size++] = v; } + void insert_at(unsigned pos, const T& val); + void inc_size(unsigned size) { m_size += size; } + unsigned size() const { return m_size; } + unsigned byte_size() const { return m_size * sizeof(T); } + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + const T& operator [] (unsigned i) const { return m_array[i]; } + T& operator [] (unsigned i) { return m_array[i]; } + const T& at(unsigned i) const { return m_array[i]; } + T& at(unsigned i) { return m_array[i]; } + T value_at(unsigned i) const { return m_array[i]; } + + const T* data() const { return m_array; } + T* data() { return m_array; } + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void cut_at(unsigned num) { if(num < m_size) m_size = num; } + + private: + unsigned m_size; + unsigned m_capacity; + T* m_array; + }; + + //------------------------------------------------------------------------ + template + void pod_vector::capacity(unsigned cap, unsigned extra_tail) + { + m_size = 0; + if(cap > m_capacity) + { + pod_allocator::deallocate(m_array, m_capacity); + m_capacity = cap + extra_tail; + m_array = m_capacity ? pod_allocator::allocate(m_capacity) : 0; + } + } + + //------------------------------------------------------------------------ + template + void pod_vector::allocate(unsigned size, unsigned extra_tail) + { + capacity(size, extra_tail); + m_size = size; + } + + + //------------------------------------------------------------------------ + template + void pod_vector::resize(unsigned new_size) + { + if(new_size > m_size) + { + if(new_size > m_capacity) + { + T* data = pod_allocator::allocate(new_size); + memcpy(data, m_array, m_size * sizeof(T)); + pod_allocator::deallocate(m_array, m_capacity); + m_array = data; + } + } + else + { + m_size = new_size; + } + } + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(unsigned cap, unsigned extra_tail) : + m_size(0), + m_capacity(cap + extra_tail), + m_array(pod_allocator::allocate(m_capacity)) {} + + //------------------------------------------------------------------------ + template pod_vector::pod_vector(const pod_vector& v) : + m_size(v.m_size), + m_capacity(v.m_capacity), + m_array(v.m_capacity ? pod_allocator::allocate(v.m_capacity) : 0) + { + memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + } + + //------------------------------------------------------------------------ + template const pod_vector& + pod_vector::operator = (const pod_vector&v) + { + allocate(v.m_size); + if(v.m_size) memcpy(m_array, v.m_array, sizeof(T) * v.m_size); + return *this; + } + + //------------------------------------------------------------------------ + template void pod_vector::serialize(int8u* ptr) const + { + if(m_size) memcpy(ptr, m_array, m_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::deserialize(const int8u* data, unsigned byte_size) + { + byte_size /= sizeof(T); + allocate(byte_size); + if(byte_size) memcpy(m_array, data, byte_size * sizeof(T)); + } + + //------------------------------------------------------------------------ + template + void pod_vector::insert_at(unsigned pos, const T& val) + { + if(pos >= m_size) + { + m_array[m_size] = val; + } + else + { + memmove(m_array + pos + 1, m_array + pos, (m_size - pos) * sizeof(T)); + m_array[pos] = val; + } + ++m_size; + } + + //---------------------------------------------------------------pod_bvector + // A simple class template to store Plain Old Data, similar to std::deque + // It doesn't reallocate memory but instead, uses blocks of data of size + // of (1 << S), that is, power of two. The data is NOT contiguous in memory, + // so the only valid access method is operator [] or curr(), prev(), next() + // + // There reallocs occure only when the pool of pointers to blocks needs + // to be extended (it happens very rarely). You can control the value + // of increment to reallocate the pointer buffer. See the second constructor. + // By default, the incremeent value equals (1 << S), i.e., the block size. + //------------------------------------------------------------------------ + template class pod_bvector + { + public: + enum block_scale_e + { + block_shift = S, + block_size = 1 << block_shift, + block_mask = block_size - 1 + }; + + typedef T value_type; + + ~pod_bvector(); + pod_bvector(); + pod_bvector(unsigned block_ptr_inc); + + // Copying + pod_bvector(const pod_bvector& v); + const pod_bvector& operator = (const pod_bvector& v); + + void remove_all() { m_size = 0; } + void clear() { m_size = 0; } + void free_all() { free_tail(0); } + void free_tail(unsigned size); + void add(const T& val); + void push_back(const T& val) { add(val); } + void modify_last(const T& val); + void remove_last(); + + int allocate_continuous_block(unsigned num_elements); + + void add_array(const T* ptr, unsigned num_elem) + { + while(num_elem--) + { + add(*ptr++); + } + } + + template void add_data(DataAccessor& data) + { + while(data.size()) + { + add(*data); + ++data; + } + } + + void cut_at(unsigned size) + { + if(size < m_size) m_size = size; + } + + unsigned size() const { return m_size; } + + const T& operator [] (unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& operator [] (unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T& at(unsigned i) + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + T value_at(unsigned i) const + { + return m_blocks[i >> block_shift][i & block_mask]; + } + + const T& curr(unsigned idx) const + { + return (*this)[idx]; + } + + T& curr(unsigned idx) + { + return (*this)[idx]; + } + + const T& prev(unsigned idx) const + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + T& prev(unsigned idx) + { + return (*this)[(idx + m_size - 1) % m_size]; + } + + const T& next(unsigned idx) const + { + return (*this)[(idx + 1) % m_size]; + } + + T& next(unsigned idx) + { + return (*this)[(idx + 1) % m_size]; + } + + const T& last() const + { + return (*this)[m_size - 1]; + } + + T& last() + { + return (*this)[m_size - 1]; + } + + unsigned byte_size() const; + void serialize(int8u* ptr) const; + void deserialize(const int8u* data, unsigned byte_size); + void deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size); + + template + void deserialize(ByteAccessor data) + { + remove_all(); + unsigned elem_size = data.size() / sizeof(T); + + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr = (int8u*)data_ptr(); + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + ++m_size; + } + } + + template + void deserialize(unsigned start, const T& empty_val, ByteAccessor data) + { + while(m_size < start) + { + add(empty_val); + } + + unsigned elem_size = data.size() / sizeof(T); + for(unsigned i = 0; i < elem_size; ++i) + { + int8u* ptr; + if(start + i < m_size) + { + ptr = (int8u*)(&((*this)[start + i])); + } + else + { + ptr = (int8u*)data_ptr(); + ++m_size; + } + for(unsigned j = 0; j < sizeof(T); ++j) + { + *ptr++ = *data; + ++data; + } + } + } + + const T* block(unsigned nb) const { return m_blocks[nb]; } + + private: + void allocate_block(unsigned nb); + T* data_ptr(); + + unsigned m_size; + unsigned m_num_blocks; + unsigned m_max_blocks; + T** m_blocks; + unsigned m_block_ptr_inc; + }; + + + //------------------------------------------------------------------------ + template pod_bvector::~pod_bvector() + { + if(m_num_blocks) + { + T** blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(*blk, block_size); + --blk; + } + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::free_tail(unsigned size) + { + if(size < m_size) + { + unsigned nb = (size + block_mask) >> block_shift; + while(m_num_blocks > nb) + { + pod_allocator::deallocate(m_blocks[--m_num_blocks], block_size); + } + if(m_num_blocks == 0) + { + pod_allocator::deallocate(m_blocks, m_max_blocks); + m_blocks = 0; + m_max_blocks = 0; + } + m_size = size; + } + } + + + //------------------------------------------------------------------------ + template pod_bvector::pod_bvector() : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_size) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(unsigned block_ptr_inc) : + m_size(0), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_block_ptr_inc(block_ptr_inc) + { + } + + + //------------------------------------------------------------------------ + template + pod_bvector::pod_bvector(const pod_bvector& v) : + m_size(v.m_size), + m_num_blocks(v.m_num_blocks), + m_max_blocks(v.m_max_blocks), + m_blocks(v.m_max_blocks ? + pod_allocator::allocate(v.m_max_blocks) : + 0), + m_block_ptr_inc(v.m_block_ptr_inc) + { + unsigned i; + for(i = 0; i < v.m_num_blocks; ++i) + { + m_blocks[i] = pod_allocator::allocate(block_size); + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + } + + + //------------------------------------------------------------------------ + template + const pod_bvector& + pod_bvector::operator = (const pod_bvector& v) + { + unsigned i; + for(i = m_num_blocks; i < v.m_num_blocks; ++i) + { + allocate_block(i); + } + for(i = 0; i < v.m_num_blocks; ++i) + { + memcpy(m_blocks[i], v.m_blocks[i], block_size * sizeof(T)); + } + m_size = v.m_size; + return *this; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_blocks = pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(T*)); + + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + m_blocks[nb] = pod_allocator::allocate(block_size); + m_num_blocks++; + } + + + + //------------------------------------------------------------------------ + template + inline T* pod_bvector::data_ptr() + { + unsigned nb = m_size >> block_shift; + if(nb >= m_num_blocks) + { + allocate_block(nb); + } + return m_blocks[nb] + (m_size & block_mask); + } + + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::add(const T& val) + { + *data_ptr() = val; + ++m_size; + } + + + //------------------------------------------------------------------------ + template + inline void pod_bvector::remove_last() + { + if(m_size) --m_size; + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::modify_last(const T& val) + { + remove_last(); + add(val); + } + + + //------------------------------------------------------------------------ + template + int pod_bvector::allocate_continuous_block(unsigned num_elements) + { + if(num_elements < block_size) + { + data_ptr(); // Allocate initial block if necessary + unsigned rest = block_size - (m_size & block_mask); + unsigned index; + if(num_elements <= rest) + { + // The rest of the block is good, we can use it + //----------------- + index = m_size; + m_size += num_elements; + return index; + } + + // New block + //--------------- + m_size += rest; + data_ptr(); + index = m_size; + m_size += num_elements; + return index; + } + return -1; // Impossible to allocate + } + + + //------------------------------------------------------------------------ + template + unsigned pod_bvector::byte_size() const + { + return m_size * sizeof(T); + } + + + //------------------------------------------------------------------------ + template + void pod_bvector::serialize(int8u* ptr) const + { + unsigned i; + for(i = 0; i < m_size; i++) + { + memcpy(ptr, &(*this)[i], sizeof(T)); + ptr += sizeof(T); + } + } + + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(const int8u* data, unsigned byte_size) + { + remove_all(); + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + data += sizeof(T); + } + } + + + // Replace or add a number of elements starting from "start" position + //------------------------------------------------------------------------ + template + void pod_bvector::deserialize(unsigned start, const T& empty_val, + const int8u* data, unsigned byte_size) + { + while(m_size < start) + { + add(empty_val); + } + + byte_size /= sizeof(T); + for(unsigned i = 0; i < byte_size; ++i) + { + if(start + i < m_size) + { + memcpy(&((*this)[start + i]), data, sizeof(T)); + } + else + { + T* ptr = data_ptr(); + memcpy(ptr, data, sizeof(T)); + ++m_size; + } + data += sizeof(T); + } + } + + + //---------------------------------------------------------block_allocator + // Allocator for arbitrary POD data. Most usable in different cache + // systems for efficient memory allocations. + // Memory is allocated with blocks of fixed size ("block_size" in + // the constructor). If required size exceeds the block size the allocator + // creates a new block of the required size. However, the most efficient + // use is when the average reqired size is much less than the block size. + //------------------------------------------------------------------------ + class block_allocator + { + struct block_type + { + int8u* data; + unsigned size; + }; + + public: + void remove_all() + { + if(m_num_blocks) + { + block_type* blk = m_blocks + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(blk->data, blk->size); + --blk; + } + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_num_blocks = 0; + m_max_blocks = 0; + m_blocks = 0; + m_buf_ptr = 0; + m_rest = 0; + } + + ~block_allocator() + { + remove_all(); + } + + block_allocator(unsigned block_size, unsigned block_ptr_inc=256-8) : + m_block_size(block_size), + m_block_ptr_inc(block_ptr_inc), + m_num_blocks(0), + m_max_blocks(0), + m_blocks(0), + m_buf_ptr(0), + m_rest(0) + { + } + + + int8u* allocate(unsigned size, unsigned alignment=1) + { + if(size == 0) return 0; + if(size <= m_rest) + { + int8u* ptr = m_buf_ptr; + if(alignment > 1) + { + unsigned align = + (alignment - unsigned((size_t)ptr) % alignment) % alignment; + + size += align; + ptr += align; + if(size <= m_rest) + { + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size); + return allocate(size - align, alignment); + } + m_rest -= size; + m_buf_ptr += size; + return ptr; + } + allocate_block(size + alignment - 1); + return allocate(size, alignment); + } + + + private: + void allocate_block(unsigned size) + { + if(size < m_block_size) size = m_block_size; + if(m_num_blocks >= m_max_blocks) + { + block_type* new_blocks = + pod_allocator::allocate(m_max_blocks + m_block_ptr_inc); + + if(m_blocks) + { + memcpy(new_blocks, + m_blocks, + m_num_blocks * sizeof(block_type)); + pod_allocator::deallocate(m_blocks, m_max_blocks); + } + m_blocks = new_blocks; + m_max_blocks += m_block_ptr_inc; + } + + m_blocks[m_num_blocks].size = size; + m_blocks[m_num_blocks].data = + m_buf_ptr = + pod_allocator::allocate(size); + + m_num_blocks++; + m_rest = size; + } + + unsigned m_block_size; + unsigned m_block_ptr_inc; + unsigned m_num_blocks; + unsigned m_max_blocks; + block_type* m_blocks; + int8u* m_buf_ptr; + unsigned m_rest; + }; + + + + + + + + + //------------------------------------------------------------------------ + enum quick_sort_threshold_e + { + quick_sort_threshold = 9 + }; + + + //-----------------------------------------------------------swap_elements + template inline void swap_elements(T& a, T& b) + { + T temp = a; + a = b; + b = temp; + } + + + //--------------------------------------------------------------quick_sort + template + void quick_sort(Array& arr, Less less) + { + if(arr.size() < 2) return; + + typename Array::value_type* e1; + typename Array::value_type* e2; + + int stack[80]; + int* top = stack; + int limit = arr.size(); + int base = 0; + + for(;;) + { + int len = limit - base; + + int i; + int j; + int pivot; + + if(len > quick_sort_threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + swap_elements(arr[base], arr[pivot]); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + e1 = &(arr[j]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[base]); + e2 = &(arr[i]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + e1 = &(arr[j]); + e2 = &(arr[base]); + if(less(*e1, *e2)) swap_elements(*e1, *e2); + + for(;;) + { + do i++; while( less(arr[i], arr[base]) ); + do j--; while( less(arr[base], arr[j]) ); + + if( i > j ) + { + break; + } + + swap_elements(arr[i], arr[j]); + } + + swap_elements(arr[base], arr[j]); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; less(*(e1 = &(arr[j + 1])), *(e2 = &(arr[j]))); j--) + { + swap_elements(*e1, *e2); + if(j == base) + { + break; + } + } + } + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + } + + + + + //------------------------------------------------------remove_duplicates + // Remove duplicates from a sorted array. It doesn't cut the + // tail of the array, it just returns the number of remaining elements. + //----------------------------------------------------------------------- + template + unsigned remove_duplicates(Array& arr, Equal equal) + { + if(arr.size() < 2) return arr.size(); + + unsigned i, j; + for(i = 1, j = 1; i < arr.size(); i++) + { + typename Array::value_type& e = arr[i]; + if(!equal(e, arr[i - 1])) + { + arr[j++] = e; + } + } + return j; + } + + //--------------------------------------------------------invert_container + template void invert_container(Array& arr) + { + int i = 0; + int j = arr.size() - 1; + while(i < j) + { + swap_elements(arr[i++], arr[j--]); + } + } + + //------------------------------------------------------binary_search_pos + template + unsigned binary_search_pos(const Array& arr, const Value& val, Less less) + { + if(arr.size() == 0) return 0; + + unsigned beg = 0; + unsigned end = arr.size() - 1; + + if(less(val, arr[0])) return 0; + if(less(arr[end], val)) return end + 1; + + while(end - beg > 1) + { + unsigned mid = (end + beg) >> 1; + if(less(val, arr[mid])) end = mid; + else beg = mid; + } + + //if(beg <= 0 && less(val, arr[0])) return 0; + //if(end >= arr.size() - 1 && less(arr[end], val)) ++end; + + return end; + } + + //----------------------------------------------------------range_adaptor + template class range_adaptor + { + public: + typedef typename Array::value_type value_type; + + range_adaptor(Array& array, unsigned start, unsigned size) : + m_array(array), m_start(start), m_size(size) + {} + + unsigned size() const { return m_size; } + const value_type& operator [] (unsigned i) const { return m_array[m_start + i]; } + value_type& operator [] (unsigned i) { return m_array[m_start + i]; } + const value_type& at(unsigned i) const { return m_array[m_start + i]; } + value_type& at(unsigned i) { return m_array[m_start + i]; } + value_type value_at(unsigned i) const { return m_array[m_start + i]; } + + private: + Array& m_array; + unsigned m_start; + unsigned m_size; + }; + + //---------------------------------------------------------------int_less + inline bool int_less(int a, int b) { return a < b; } + + //------------------------------------------------------------int_greater + inline bool int_greater(int a, int b) { return a > b; } + + //----------------------------------------------------------unsigned_less + inline bool unsigned_less(unsigned a, unsigned b) { return a < b; } + + //-------------------------------------------------------unsigned_greater + inline bool unsigned_greater(unsigned a, unsigned b) { return a > b; } +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_arrowhead.h b/desmume/src/windows/agg/include/agg_arrowhead.h new file mode 100644 index 000000000..9c7c97c63 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_arrowhead.h @@ -0,0 +1,88 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ARROWHEAD_INCLUDED +#define AGG_ARROWHEAD_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //===============================================================arrowhead + // + // See implementation agg_arrowhead.cpp + // + class arrowhead + { + public: + arrowhead(); + + void head(double d1, double d2, double d3, double d4) + { + m_head_d1 = d1; + m_head_d2 = d2; + m_head_d3 = d3; + m_head_d4 = d4; + m_head_flag = true; + } + + void head() { m_head_flag = true; } + void no_head() { m_head_flag = false; } + + void tail(double d1, double d2, double d3, double d4) + { + m_tail_d1 = d1; + m_tail_d2 = d2; + m_tail_d3 = d3; + m_tail_d4 = d4; + m_tail_flag = true; + } + + void tail() { m_tail_flag = true; } + void no_tail() { m_tail_flag = false; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + double m_head_d1; + double m_head_d2; + double m_head_d3; + double m_head_d4; + double m_tail_d1; + double m_tail_d2; + double m_tail_d3; + double m_tail_d4; + bool m_head_flag; + bool m_tail_flag; + double m_coord[16]; + unsigned m_cmd[8]; + unsigned m_curr_id; + unsigned m_curr_coord; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_basics.h b/desmume/src/windows/agg/include/agg_basics.h new file mode 100644 index 000000000..f673863b4 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_basics.h @@ -0,0 +1,539 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BASICS_INCLUDED +#define AGG_BASICS_INCLUDED + +#include +#include "agg_config.h" + +//---------------------------------------------------------AGG_CUSTOM_ALLOCATOR +#ifdef AGG_CUSTOM_ALLOCATOR +#include "agg_allocator.h" +#else +namespace agg +{ + // The policy of all AGG containers and memory allocation strategy + // in general is that no allocated data requires explicit construction. + // It means that the allocator can be really simple; you can even + // replace new/delete to malloc/free. The constructors and destructors + // won't be called in this case, however everything will remain working. + // The second argument of deallocate() is the size of the allocated + // block. You can use this information if you wish. + //------------------------------------------------------------pod_allocator + template struct pod_allocator + { + static T* allocate(unsigned num) { return new T [num]; } + static void deallocate(T* ptr, unsigned) { delete [] ptr; } + }; + + // Single object allocator. It's also can be replaced with your custom + // allocator. The difference is that it can only allocate a single + // object and the constructor and destructor must be called. + // In AGG there is no need to allocate an array of objects with + // calling their constructors (only single ones). So that, if you + // replace these new/delete to malloc/free make sure that the in-place + // new is called and take care of calling the destructor too. + //------------------------------------------------------------obj_allocator + template struct obj_allocator + { + static T* allocate() { return new T; } + static void deallocate(T* ptr) { delete ptr; } + }; +} +#endif + + +//-------------------------------------------------------- Default basic types +// +// If the compiler has different capacity of the basic types you can redefine +// them via the compiler command line or by generating agg_config.h that is +// empty by default. +// +#ifndef AGG_INT8 +#define AGG_INT8 signed char +#endif + +#ifndef AGG_INT8U +#define AGG_INT8U unsigned char +#endif + +#ifndef AGG_INT16 +#define AGG_INT16 short +#endif + +#ifndef AGG_INT16U +#define AGG_INT16U unsigned short +#endif + +#ifndef AGG_INT32 +#define AGG_INT32 int +#endif + +#ifndef AGG_INT32U +#define AGG_INT32U unsigned +#endif + +#ifndef AGG_INT64 +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64 signed __int64 +#else +#define AGG_INT64 signed long long +#endif +#endif + +#ifndef AGG_INT64U +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define AGG_INT64U unsigned __int64 +#else +#define AGG_INT64U unsigned long long +#endif +#endif + +//------------------------------------------------ Some fixes for MS Visual C++ +#if defined(_MSC_VER) +#pragma warning(disable:4786) // Identifier was truncated... +#endif + +#if defined(_MSC_VER) +#define AGG_INLINE __forceinline +#else +#define AGG_INLINE inline +#endif + +namespace agg +{ + //------------------------------------------------------------------------- + typedef AGG_INT8 int8; //----int8 + typedef AGG_INT8U int8u; //----int8u + typedef AGG_INT16 int16; //----int16 + typedef AGG_INT16U int16u; //----int16u + typedef AGG_INT32 int32; //----int32 + typedef AGG_INT32U int32u; //----int32u + typedef AGG_INT64 int64; //----int64 + typedef AGG_INT64U int64u; //----int64u + +#if defined(AGG_FISTP) +#pragma warning(push) +#pragma warning(disable : 4035) //Disable warning "no return value" + AGG_INLINE int iround(double v) //-------iround + { + int t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } + AGG_INLINE unsigned uround(double v) //-------uround + { + unsigned t; + __asm fld qword ptr [v] + __asm fistp dword ptr [t] + __asm mov eax, dword ptr [t] + } +#pragma warning(pop) + AGG_INLINE unsigned ufloor(double v) //-------ufloor + { + return unsigned(floor(v)); + } + AGG_INLINE unsigned uceil(double v) //--------uceil + { + return unsigned(ceil(v)); + } +#elif defined(AGG_QIFIST) + AGG_INLINE int iround(double v) + { + return int(v); + } + AGG_INLINE int uround(double v) + { + return unsigned(v); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(floor(v)); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#else + AGG_INLINE int iround(double v) + { + return int((v < 0.0) ? v - 0.5 : v + 0.5); + } + AGG_INLINE int uround(double v) + { + return unsigned(v + 0.5); + } + AGG_INLINE unsigned ufloor(double v) + { + return unsigned(v); + } + AGG_INLINE unsigned uceil(double v) + { + return unsigned(ceil(v)); + } +#endif + + //---------------------------------------------------------------saturation + template struct saturation + { + AGG_INLINE static int iround(double v) + { + if(v < double(-Limit)) return -Limit; + if(v > double( Limit)) return Limit; + return agg::iround(v); + } + }; + + //------------------------------------------------------------------mul_one + template struct mul_one + { + AGG_INLINE static unsigned mul(unsigned a, unsigned b) + { + register unsigned q = a * b + (1 << (Shift-1)); + return (q + (q >> Shift)) >> Shift; + } + }; + + //------------------------------------------------------------------------- + typedef unsigned char cover_type; //----cover_type + enum cover_scale_e + { + cover_shift = 8, //----cover_shift + cover_size = 1 << cover_shift, //----cover_size + cover_mask = cover_size - 1, //----cover_mask + cover_none = 0, //----cover_none + cover_full = cover_mask //----cover_full + }; + + //----------------------------------------------------poly_subpixel_scale_e + // These constants determine the subpixel accuracy, to be more precise, + // the number of bits of the fractional part of the coordinates. + // The possible coordinate capacity in bits can be calculated by formula: + // sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and + // 8-bits fractional part the capacity is 24 bits. + enum poly_subpixel_scale_e + { + poly_subpixel_shift = 8, //----poly_subpixel_shift + poly_subpixel_scale = 1< struct rect_base + { + typedef T value_type; + typedef rect_base self_type; + T x1, y1, x2, y2; + + rect_base() {} + rect_base(T x1_, T y1_, T x2_, T y2_) : + x1(x1_), y1(y1_), x2(x2_), y2(y2_) {} + + void init(T x1_, T y1_, T x2_, T y2_) + { + x1 = x1_; y1 = y1_; x2 = x2_; y2 = y2_; + } + + const self_type& normalize() + { + T t; + if(x1 > x2) { t = x1; x1 = x2; x2 = t; } + if(y1 > y2) { t = y1; y1 = y2; y2 = t; } + return *this; + } + + bool clip(const self_type& r) + { + if(x2 > r.x2) x2 = r.x2; + if(y2 > r.y2) y2 = r.y2; + if(x1 < r.x1) x1 = r.x1; + if(y1 < r.y1) y1 = r.y1; + return x1 <= x2 && y1 <= y2; + } + + bool is_valid() const + { + return x1 <= x2 && y1 <= y2; + } + + bool hit_test(T x, T y) const + { + return (x >= x1 && x <= x2 && y >= y1 && y <= y2); + } + }; + + //-----------------------------------------------------intersect_rectangles + template + inline Rect intersect_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + + // First process x2,y2 because the other order + // results in Internal Compiler Error under + // Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in + // case of "Maximize Speed" optimization option. + //----------------- + if(r.x2 > r2.x2) r.x2 = r2.x2; + if(r.y2 > r2.y2) r.y2 = r2.y2; + if(r.x1 < r2.x1) r.x1 = r2.x1; + if(r.y1 < r2.y1) r.y1 = r2.y1; + return r; + } + + + //---------------------------------------------------------unite_rectangles + template + inline Rect unite_rectangles(const Rect& r1, const Rect& r2) + { + Rect r = r1; + if(r.x2 < r2.x2) r.x2 = r2.x2; + if(r.y2 < r2.y2) r.y2 = r2.y2; + if(r.x1 > r2.x1) r.x1 = r2.x1; + if(r.y1 > r2.y1) r.y1 = r2.y1; + return r; + } + + typedef rect_base rect_i; //----rect_i + typedef rect_base rect_f; //----rect_f + typedef rect_base rect_d; //----rect_d + + //---------------------------------------------------------path_commands_e + enum path_commands_e + { + path_cmd_stop = 0, //----path_cmd_stop + path_cmd_move_to = 1, //----path_cmd_move_to + path_cmd_line_to = 2, //----path_cmd_line_to + path_cmd_curve3 = 3, //----path_cmd_curve3 + path_cmd_curve4 = 4, //----path_cmd_curve4 + path_cmd_curveN = 5, //----path_cmd_curveN + path_cmd_catrom = 6, //----path_cmd_catrom + path_cmd_ubspline = 7, //----path_cmd_ubspline + path_cmd_end_poly = 0x0F, //----path_cmd_end_poly + path_cmd_mask = 0x0F //----path_cmd_mask + }; + + //------------------------------------------------------------path_flags_e + enum path_flags_e + { + path_flags_none = 0, //----path_flags_none + path_flags_ccw = 0x10, //----path_flags_ccw + path_flags_cw = 0x20, //----path_flags_cw + path_flags_close = 0x40, //----path_flags_close + path_flags_mask = 0xF0 //----path_flags_mask + }; + + //---------------------------------------------------------------is_vertex + inline bool is_vertex(unsigned c) + { + return c >= path_cmd_move_to && c < path_cmd_end_poly; + } + + //--------------------------------------------------------------is_drawing + inline bool is_drawing(unsigned c) + { + return c >= path_cmd_line_to && c < path_cmd_end_poly; + } + + //-----------------------------------------------------------------is_stop + inline bool is_stop(unsigned c) + { + return c == path_cmd_stop; + } + + //--------------------------------------------------------------is_move_to + inline bool is_move_to(unsigned c) + { + return c == path_cmd_move_to; + } + + //--------------------------------------------------------------is_line_to + inline bool is_line_to(unsigned c) + { + return c == path_cmd_line_to; + } + + //----------------------------------------------------------------is_curve + inline bool is_curve(unsigned c) + { + return c == path_cmd_curve3 || c == path_cmd_curve4; + } + + //---------------------------------------------------------------is_curve3 + inline bool is_curve3(unsigned c) + { + return c == path_cmd_curve3; + } + + //---------------------------------------------------------------is_curve4 + inline bool is_curve4(unsigned c) + { + return c == path_cmd_curve4; + } + + //-------------------------------------------------------------is_end_poly + inline bool is_end_poly(unsigned c) + { + return (c & path_cmd_mask) == path_cmd_end_poly; + } + + //----------------------------------------------------------------is_close + inline bool is_close(unsigned c) + { + return (c & ~(path_flags_cw | path_flags_ccw)) == + (path_cmd_end_poly | path_flags_close); + } + + //------------------------------------------------------------is_next_poly + inline bool is_next_poly(unsigned c) + { + return is_stop(c) || is_move_to(c) || is_end_poly(c); + } + + //-------------------------------------------------------------------is_cw + inline bool is_cw(unsigned c) + { + return (c & path_flags_cw) != 0; + } + + //------------------------------------------------------------------is_ccw + inline bool is_ccw(unsigned c) + { + return (c & path_flags_ccw) != 0; + } + + //-------------------------------------------------------------is_oriented + inline bool is_oriented(unsigned c) + { + return (c & (path_flags_cw | path_flags_ccw)) != 0; + } + + //---------------------------------------------------------------is_closed + inline bool is_closed(unsigned c) + { + return (c & path_flags_close) != 0; + } + + //----------------------------------------------------------get_close_flag + inline unsigned get_close_flag(unsigned c) + { + return c & path_flags_close; + } + + //-------------------------------------------------------clear_orientation + inline unsigned clear_orientation(unsigned c) + { + return c & ~(path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------get_orientation + inline unsigned get_orientation(unsigned c) + { + return c & (path_flags_cw | path_flags_ccw); + } + + //---------------------------------------------------------set_orientation + inline unsigned set_orientation(unsigned c, unsigned o) + { + return clear_orientation(c) | o; + } + + //--------------------------------------------------------------point_base + template struct point_base + { + typedef T value_type; + T x,y; + point_base() {} + point_base(T x_, T y_) : x(x_), y(y_) {} + }; + typedef point_base point_i; //-----point_i + typedef point_base point_f; //-----point_f + typedef point_base point_d; //-----point_d + + //-------------------------------------------------------------vertex_base + template struct vertex_base + { + typedef T value_type; + T x,y; + unsigned cmd; + vertex_base() {} + vertex_base(T x_, T y_, unsigned cmd_) : x(x_), y(y_), cmd(cmd_) {} + }; + typedef vertex_base vertex_i; //-----vertex_i + typedef vertex_base vertex_f; //-----vertex_f + typedef vertex_base vertex_d; //-----vertex_d + + //----------------------------------------------------------------row_info + template struct row_info + { + int x1, x2; + T* ptr; + row_info() {} + row_info(int x1_, int x2_, T* ptr_) : x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //----------------------------------------------------------const_row_info + template struct const_row_info + { + int x1, x2; + const T* ptr; + const_row_info() {} + const_row_info(int x1_, int x2_, const T* ptr_) : + x1(x1_), x2(x2_), ptr(ptr_) {} + }; + + //------------------------------------------------------------is_equal_eps + template inline bool is_equal_eps(T v1, T v2, T epsilon) + { + return fabs(v1 - v2) <= double(epsilon); + } + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_bezier_arc.h b/desmume/src/windows/agg/include/agg_bezier_arc.h new file mode 100644 index 000000000..faca7b691 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_bezier_arc.h @@ -0,0 +1,163 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BEZIER_ARC_INCLUDED +#define AGG_BEZIER_ARC_INCLUDED + +#include "agg_conv_transform.h" + +namespace agg +{ + + //----------------------------------------------------------------------- + void arc_to_bezier(double cx, double cy, double rx, double ry, + double start_angle, double sweep_angle, + double* curve); + + + //==============================================================bezier_arc + // + // See implemantaion agg_bezier_arc.cpp + // + class bezier_arc + { + public: + //-------------------------------------------------------------------- + bezier_arc() : m_vertex(26), m_num_vertices(0), m_cmd(path_cmd_line_to) {} + bezier_arc(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle) + { + init(x, y, rx, ry, start_angle, sweep_angle); + } + + //-------------------------------------------------------------------- + void init(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle); + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_vertex = 0; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + if(m_vertex >= m_num_vertices) return path_cmd_stop; + *x = m_vertices[m_vertex]; + *y = m_vertices[m_vertex + 1]; + m_vertex += 2; + return (m_vertex == 2) ? path_cmd_move_to : m_cmd; + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_num_vertices; } + const double* vertices() const { return m_vertices; } + double* vertices() { return m_vertices; } + + private: + unsigned m_vertex; + unsigned m_num_vertices; + double m_vertices[26]; + unsigned m_cmd; + }; + + + + //==========================================================bezier_arc_svg + // Compute an SVG-style bezier arc. + // + // Computes an elliptical arc from (x1, y1) to (x2, y2). The size and + // orientation of the ellipse are defined by two radii (rx, ry) + // and an x-axis-rotation, which indicates how the ellipse as a whole + // is rotated relative to the current coordinate system. The center + // (cx, cy) of the ellipse is calculated automatically to satisfy the + // constraints imposed by the other parameters. + // large-arc-flag and sweep-flag contribute to the automatic calculations + // and help determine how the arc is drawn. + class bezier_arc_svg + { + public: + //-------------------------------------------------------------------- + bezier_arc_svg() : m_arc(), m_radii_ok(false) {} + + bezier_arc_svg(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2) : + m_arc(), m_radii_ok(false) + { + init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2); + } + + //-------------------------------------------------------------------- + void init(double x1, double y1, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2); + + //-------------------------------------------------------------------- + bool radii_ok() const { return m_radii_ok; } + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_arc.rewind(0); + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + return m_arc.vertex(x, y); + } + + // Supplemantary functions. num_vertices() actually returns doubled + // number of vertices. That is, for 1 vertex it returns 2. + //-------------------------------------------------------------------- + unsigned num_vertices() const { return m_arc.num_vertices(); } + const double* vertices() const { return m_arc.vertices(); } + double* vertices() { return m_arc.vertices(); } + + private: + bezier_arc m_arc; + bool m_radii_ok; + }; + + + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_bitset_iterator.h b/desmume/src/windows/agg/include/agg_bitset_iterator.h new file mode 100644 index 000000000..1337f7819 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_bitset_iterator.h @@ -0,0 +1,63 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BITSET_ITERATOR_INCLUDED +#define AGG_BITSET_ITERATOR_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + class bitset_iterator + { + public: + bitset_iterator(const int8u* bits, unsigned offset = 0) : + m_bits(bits + (offset >> 3)), + m_mask(0x80 >> (offset & 7)) + {} + + void operator ++ () + { + m_mask >>= 1; + if(m_mask == 0) + { + ++m_bits; + m_mask = 0x80; + } + } + + unsigned bit() const + { + return (*m_bits) & m_mask; + } + + private: + const int8u* m_bits; + int8u m_mask; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_blur.h b/desmume/src/windows/agg/include/agg_blur.h new file mode 100644 index 000000000..26c36a8d2 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_blur.h @@ -0,0 +1,1303 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// The Stack Blur Algorithm was invented by Mario Klingemann, +// mario@quasimondo.com and described here: +// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php +// (search phrase "Stackblur: Fast But Goodlooking"). +// The major improvement is that there's no more division table +// that was very expensive to create for large blur radii. Insted, +// for 8-bit per channel and radius not exceeding 254 the division is +// replaced by multiplication and shift. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_BLUR_INCLUDED +#define AGG_BLUR_INCLUDED + +#include "agg_array.h" +#include "agg_pixfmt_transposer.h" + +namespace agg +{ + + template struct stack_blur_tables + { + static int16u const g_stack_blur8_mul[255]; + static int8u const g_stack_blur8_shr[255]; + }; + + //------------------------------------------------------------------------ + template + int16u const stack_blur_tables::g_stack_blur8_mul[255] = + { + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 + }; + + //------------------------------------------------------------------------ + template + int8u const stack_blur_tables::g_stack_blur8_shr[255] = + { + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 + }; + + + + //==============================================================stack_blur + template class stack_blur + { + public: + typedef ColorT color_type; + typedef CalculatorT calculator_type; + + //-------------------------------------------------------------------- + template void blur_x(Img& img, unsigned radius) + { + if(radius < 1) return; + + unsigned x, y, xp, i; + unsigned stack_ptr; + unsigned stack_start; + + color_type pix; + color_type* stack_pix; + calculator_type sum; + calculator_type sum_in; + calculator_type sum_out; + + unsigned w = img.width(); + unsigned h = img.height(); + unsigned wm = w - 1; + unsigned div = radius * 2 + 1; + + unsigned div_sum = (radius + 1) * (radius + 1); + unsigned mul_sum = 0; + unsigned shr_sum = 0; + unsigned max_val = color_type::base_mask; + + if(max_val <= 255 && radius < 255) + { + mul_sum = stack_blur_tables::g_stack_blur8_mul[radius]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[radius]; + } + + m_buf.allocate(w, 128); + m_stack.allocate(div, 32); + + for(y = 0; y < h; y++) + { + sum.clear(); + sum_in.clear(); + sum_out.clear(); + + pix = img.pixel(0, y); + for(i = 0; i <= radius; i++) + { + m_stack[i] = pix; + sum.add(pix, i + 1); + sum_out.add(pix); + } + for(i = 1; i <= radius; i++) + { + pix = img.pixel((i > wm) ? wm : i, y); + m_stack[i + radius] = pix; + sum.add(pix, radius + 1 - i); + sum_in.add(pix); + } + + stack_ptr = radius; + for(x = 0; x < w; x++) + { + if(mul_sum) sum.calc_pix(m_buf[x], mul_sum, shr_sum); + else sum.calc_pix(m_buf[x], div_sum); + + sum.sub(sum_out); + + stack_start = stack_ptr + div - radius; + if(stack_start >= div) stack_start -= div; + stack_pix = &m_stack[stack_start]; + + sum_out.sub(*stack_pix); + + xp = x + radius + 1; + if(xp > wm) xp = wm; + pix = img.pixel(xp, y); + + *stack_pix = pix; + + sum_in.add(pix); + sum.add(sum_in); + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix = &m_stack[stack_ptr]; + + sum_out.add(*stack_pix); + sum_in.sub(*stack_pix); + } + img.copy_color_hspan(0, y, w, &m_buf[0]); + } + } + + //-------------------------------------------------------------------- + template void blur_y(Img& img, unsigned radius) + { + pixfmt_transposer img2(img); + blur_x(img2, radius); + } + + //-------------------------------------------------------------------- + template void blur(Img& img, unsigned radius) + { + blur_x(img, radius); + pixfmt_transposer img2(img); + blur_x(img2, radius); + } + + private: + pod_vector m_buf; + pod_vector m_stack; + }; + + //====================================================stack_blur_calc_rgba + template struct stack_blur_calc_rgba + { + typedef T value_type; + value_type r,g,b,a; + + AGG_INLINE void clear() + { + r = g = b = a = 0; + } + + template AGG_INLINE void add(const ArgT& v) + { + r += v.r; + g += v.g; + b += v.b; + a += v.a; + } + + template AGG_INLINE void add(const ArgT& v, unsigned k) + { + r += v.r * k; + g += v.g * k; + b += v.b * k; + a += v.a * k; + } + + template AGG_INLINE void sub(const ArgT& v) + { + r -= v.r; + g -= v.g; + b -= v.b; + a -= v.a; + } + + template AGG_INLINE void calc_pix(ArgT& v, unsigned div) + { + typedef typename ArgT::value_type value_type; + v.r = value_type(r / div); + v.g = value_type(g / div); + v.b = value_type(b / div); + v.a = value_type(a / div); + } + + template + AGG_INLINE void calc_pix(ArgT& v, unsigned mul, unsigned shr) + { + typedef typename ArgT::value_type value_type; + v.r = value_type((r * mul) >> shr); + v.g = value_type((g * mul) >> shr); + v.b = value_type((b * mul) >> shr); + v.a = value_type((a * mul) >> shr); + } + }; + + + //=====================================================stack_blur_calc_rgb + template struct stack_blur_calc_rgb + { + typedef T value_type; + value_type r,g,b; + + AGG_INLINE void clear() + { + r = g = b = 0; + } + + template AGG_INLINE void add(const ArgT& v) + { + r += v.r; + g += v.g; + b += v.b; + } + + template AGG_INLINE void add(const ArgT& v, unsigned k) + { + r += v.r * k; + g += v.g * k; + b += v.b * k; + } + + template AGG_INLINE void sub(const ArgT& v) + { + r -= v.r; + g -= v.g; + b -= v.b; + } + + template AGG_INLINE void calc_pix(ArgT& v, unsigned div) + { + typedef typename ArgT::value_type value_type; + v.r = value_type(r / div); + v.g = value_type(g / div); + v.b = value_type(b / div); + } + + template + AGG_INLINE void calc_pix(ArgT& v, unsigned mul, unsigned shr) + { + typedef typename ArgT::value_type value_type; + v.r = value_type((r * mul) >> shr); + v.g = value_type((g * mul) >> shr); + v.b = value_type((b * mul) >> shr); + } + }; + + + //====================================================stack_blur_calc_gray + template struct stack_blur_calc_gray + { + typedef T value_type; + value_type v; + + AGG_INLINE void clear() + { + v = 0; + } + + template AGG_INLINE void add(const ArgT& a) + { + v += a.v; + } + + template AGG_INLINE void add(const ArgT& a, unsigned k) + { + v += a.v * k; + } + + template AGG_INLINE void sub(const ArgT& a) + { + v -= a.v; + } + + template AGG_INLINE void calc_pix(ArgT& a, unsigned div) + { + typedef typename ArgT::value_type value_type; + a.v = value_type(v / div); + } + + template + AGG_INLINE void calc_pix(ArgT& a, unsigned mul, unsigned shr) + { + typedef typename ArgT::value_type value_type; + a.v = value_type((v * mul) >> shr); + } + }; + + + + //========================================================stack_blur_gray8 + template + void stack_blur_gray8(Img& img, unsigned rx, unsigned ry) + { + unsigned x, y, xp, yp, i; + unsigned stack_ptr; + unsigned stack_start; + + const int8u* src_pix_ptr; + int8u* dst_pix_ptr; + unsigned pix; + unsigned stack_pix; + unsigned sum; + unsigned sum_in; + unsigned sum_out; + + unsigned w = img.width(); + unsigned h = img.height(); + unsigned wm = w - 1; + unsigned hm = h - 1; + + unsigned div; + unsigned mul_sum; + unsigned shr_sum; + + pod_vector stack; + + if(rx > 0) + { + if(rx > 254) rx = 254; + div = rx * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[rx]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[rx]; + stack.allocate(div); + + for(y = 0; y < h; y++) + { + sum = sum_in = sum_out = 0; + + src_pix_ptr = img.pix_ptr(0, y); + pix = *src_pix_ptr; + for(i = 0; i <= rx; i++) + { + stack[i] = pix; + sum += pix * (i + 1); + sum_out += pix; + } + for(i = 1; i <= rx; i++) + { + if(i <= wm) src_pix_ptr += Img::pix_step; + pix = *src_pix_ptr; + stack[i + rx] = pix; + sum += pix * (rx + 1 - i); + sum_in += pix; + } + + stack_ptr = rx; + xp = rx; + if(xp > wm) xp = wm; + src_pix_ptr = img.pix_ptr(xp, y); + dst_pix_ptr = img.pix_ptr(0, y); + for(x = 0; x < w; x++) + { + *dst_pix_ptr = (sum * mul_sum) >> shr_sum; + dst_pix_ptr += Img::pix_step; + + sum -= sum_out; + + stack_start = stack_ptr + div - rx; + if(stack_start >= div) stack_start -= div; + sum_out -= stack[stack_start]; + + if(xp < wm) + { + src_pix_ptr += Img::pix_step; + pix = *src_pix_ptr; + ++xp; + } + + stack[stack_start] = pix; + + sum_in += pix; + sum += sum_in; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix = stack[stack_ptr]; + + sum_out += stack_pix; + sum_in -= stack_pix; + } + } + } + + if(ry > 0) + { + if(ry > 254) ry = 254; + div = ry * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[ry]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[ry]; + stack.allocate(div); + + int stride = img.stride(); + for(x = 0; x < w; x++) + { + sum = sum_in = sum_out = 0; + + src_pix_ptr = img.pix_ptr(x, 0); + pix = *src_pix_ptr; + for(i = 0; i <= ry; i++) + { + stack[i] = pix; + sum += pix * (i + 1); + sum_out += pix; + } + for(i = 1; i <= ry; i++) + { + if(i <= hm) src_pix_ptr += stride; + pix = *src_pix_ptr; + stack[i + ry] = pix; + sum += pix * (ry + 1 - i); + sum_in += pix; + } + + stack_ptr = ry; + yp = ry; + if(yp > hm) yp = hm; + src_pix_ptr = img.pix_ptr(x, yp); + dst_pix_ptr = img.pix_ptr(x, 0); + for(y = 0; y < h; y++) + { + *dst_pix_ptr = (sum * mul_sum) >> shr_sum; + dst_pix_ptr += stride; + + sum -= sum_out; + + stack_start = stack_ptr + div - ry; + if(stack_start >= div) stack_start -= div; + sum_out -= stack[stack_start]; + + if(yp < hm) + { + src_pix_ptr += stride; + pix = *src_pix_ptr; + ++yp; + } + + stack[stack_start] = pix; + + sum_in += pix; + sum += sum_in; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix = stack[stack_ptr]; + + sum_out += stack_pix; + sum_in -= stack_pix; + } + } + } + } + + + + //========================================================stack_blur_rgb24 + template + void stack_blur_rgb24(Img& img, unsigned rx, unsigned ry) + { + typedef typename Img::color_type color_type; + typedef typename Img::order_type order_type; + enum order_e + { + R = order_type::R, + G = order_type::G, + B = order_type::B + }; + + unsigned x, y, xp, yp, i; + unsigned stack_ptr; + unsigned stack_start; + + const int8u* src_pix_ptr; + int8u* dst_pix_ptr; + color_type* stack_pix_ptr; + + unsigned sum_r; + unsigned sum_g; + unsigned sum_b; + unsigned sum_in_r; + unsigned sum_in_g; + unsigned sum_in_b; + unsigned sum_out_r; + unsigned sum_out_g; + unsigned sum_out_b; + + unsigned w = img.width(); + unsigned h = img.height(); + unsigned wm = w - 1; + unsigned hm = h - 1; + + unsigned div; + unsigned mul_sum; + unsigned shr_sum; + + pod_vector stack; + + if(rx > 0) + { + if(rx > 254) rx = 254; + div = rx * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[rx]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[rx]; + stack.allocate(div); + + for(y = 0; y < h; y++) + { + sum_r = + sum_g = + sum_b = + sum_in_r = + sum_in_g = + sum_in_b = + sum_out_r = + sum_out_g = + sum_out_b = 0; + + src_pix_ptr = img.pix_ptr(0, y); + for(i = 0; i <= rx; i++) + { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + sum_r += src_pix_ptr[R] * (i + 1); + sum_g += src_pix_ptr[G] * (i + 1); + sum_b += src_pix_ptr[B] * (i + 1); + sum_out_r += src_pix_ptr[R]; + sum_out_g += src_pix_ptr[G]; + sum_out_b += src_pix_ptr[B]; + } + for(i = 1; i <= rx; i++) + { + if(i <= wm) src_pix_ptr += Img::pix_width; + stack_pix_ptr = &stack[i + rx]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + sum_r += src_pix_ptr[R] * (rx + 1 - i); + sum_g += src_pix_ptr[G] * (rx + 1 - i); + sum_b += src_pix_ptr[B] * (rx + 1 - i); + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + } + + stack_ptr = rx; + xp = rx; + if(xp > wm) xp = wm; + src_pix_ptr = img.pix_ptr(xp, y); + dst_pix_ptr = img.pix_ptr(0, y); + for(x = 0; x < w; x++) + { + dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr += Img::pix_width; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + + stack_start = stack_ptr + div - rx; + if(stack_start >= div) stack_start -= div; + stack_pix_ptr = &stack[stack_start]; + + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + + if(xp < wm) + { + src_pix_ptr += Img::pix_width; + ++xp; + } + + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + } + } + } + + if(ry > 0) + { + if(ry > 254) ry = 254; + div = ry * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[ry]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[ry]; + stack.allocate(div); + + int stride = img.stride(); + for(x = 0; x < w; x++) + { + sum_r = + sum_g = + sum_b = + sum_in_r = + sum_in_g = + sum_in_b = + sum_out_r = + sum_out_g = + sum_out_b = 0; + + src_pix_ptr = img.pix_ptr(x, 0); + for(i = 0; i <= ry; i++) + { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + sum_r += src_pix_ptr[R] * (i + 1); + sum_g += src_pix_ptr[G] * (i + 1); + sum_b += src_pix_ptr[B] * (i + 1); + sum_out_r += src_pix_ptr[R]; + sum_out_g += src_pix_ptr[G]; + sum_out_b += src_pix_ptr[B]; + } + for(i = 1; i <= ry; i++) + { + if(i <= hm) src_pix_ptr += stride; + stack_pix_ptr = &stack[i + ry]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + sum_r += src_pix_ptr[R] * (ry + 1 - i); + sum_g += src_pix_ptr[G] * (ry + 1 - i); + sum_b += src_pix_ptr[B] * (ry + 1 - i); + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + } + + stack_ptr = ry; + yp = ry; + if(yp > hm) yp = hm; + src_pix_ptr = img.pix_ptr(x, yp); + dst_pix_ptr = img.pix_ptr(x, 0); + for(y = 0; y < h; y++) + { + dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr += stride; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + + stack_start = stack_ptr + div - ry; + if(stack_start >= div) stack_start -= div; + + stack_pix_ptr = &stack[stack_start]; + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + + if(yp < hm) + { + src_pix_ptr += stride; + ++yp; + } + + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + } + } + } + } + + + + //=======================================================stack_blur_rgba32 + template + void stack_blur_rgba32(Img& img, unsigned rx, unsigned ry) + { + typedef typename Img::color_type color_type; + typedef typename Img::order_type order_type; + enum order_e + { + R = order_type::R, + G = order_type::G, + B = order_type::B, + A = order_type::A + }; + + unsigned x, y, xp, yp, i; + unsigned stack_ptr; + unsigned stack_start; + + const int8u* src_pix_ptr; + int8u* dst_pix_ptr; + color_type* stack_pix_ptr; + + unsigned sum_r; + unsigned sum_g; + unsigned sum_b; + unsigned sum_a; + unsigned sum_in_r; + unsigned sum_in_g; + unsigned sum_in_b; + unsigned sum_in_a; + unsigned sum_out_r; + unsigned sum_out_g; + unsigned sum_out_b; + unsigned sum_out_a; + + unsigned w = img.width(); + unsigned h = img.height(); + unsigned wm = w - 1; + unsigned hm = h - 1; + + unsigned div; + unsigned mul_sum; + unsigned shr_sum; + + pod_vector stack; + + if(rx > 0) + { + if(rx > 254) rx = 254; + div = rx * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[rx]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[rx]; + stack.allocate(div); + + for(y = 0; y < h; y++) + { + sum_r = + sum_g = + sum_b = + sum_a = + sum_in_r = + sum_in_g = + sum_in_b = + sum_in_a = + sum_out_r = + sum_out_g = + sum_out_b = + sum_out_a = 0; + + src_pix_ptr = img.pix_ptr(0, y); + for(i = 0; i <= rx; i++) + { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + sum_r += src_pix_ptr[R] * (i + 1); + sum_g += src_pix_ptr[G] * (i + 1); + sum_b += src_pix_ptr[B] * (i + 1); + sum_a += src_pix_ptr[A] * (i + 1); + sum_out_r += src_pix_ptr[R]; + sum_out_g += src_pix_ptr[G]; + sum_out_b += src_pix_ptr[B]; + sum_out_a += src_pix_ptr[A]; + } + for(i = 1; i <= rx; i++) + { + if(i <= wm) src_pix_ptr += Img::pix_width; + stack_pix_ptr = &stack[i + rx]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + sum_r += src_pix_ptr[R] * (rx + 1 - i); + sum_g += src_pix_ptr[G] * (rx + 1 - i); + sum_b += src_pix_ptr[B] * (rx + 1 - i); + sum_a += src_pix_ptr[A] * (rx + 1 - i); + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_in_a += src_pix_ptr[A]; + } + + stack_ptr = rx; + xp = rx; + if(xp > wm) xp = wm; + src_pix_ptr = img.pix_ptr(xp, y); + dst_pix_ptr = img.pix_ptr(0, y); + for(x = 0; x < w; x++) + { + dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr[A] = (sum_a * mul_sum) >> shr_sum; + dst_pix_ptr += Img::pix_width; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + sum_a -= sum_out_a; + + stack_start = stack_ptr + div - rx; + if(stack_start >= div) stack_start -= div; + stack_pix_ptr = &stack[stack_start]; + + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + sum_out_a -= stack_pix_ptr->a; + + if(xp < wm) + { + src_pix_ptr += Img::pix_width; + ++xp; + } + + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_in_a += src_pix_ptr[A]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + sum_a += sum_in_a; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_out_a += stack_pix_ptr->a; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + sum_in_a -= stack_pix_ptr->a; + } + } + } + + if(ry > 0) + { + if(ry > 254) ry = 254; + div = ry * 2 + 1; + mul_sum = stack_blur_tables::g_stack_blur8_mul[ry]; + shr_sum = stack_blur_tables::g_stack_blur8_shr[ry]; + stack.allocate(div); + + int stride = img.stride(); + for(x = 0; x < w; x++) + { + sum_r = + sum_g = + sum_b = + sum_a = + sum_in_r = + sum_in_g = + sum_in_b = + sum_in_a = + sum_out_r = + sum_out_g = + sum_out_b = + sum_out_a = 0; + + src_pix_ptr = img.pix_ptr(x, 0); + for(i = 0; i <= ry; i++) + { + stack_pix_ptr = &stack[i]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + sum_r += src_pix_ptr[R] * (i + 1); + sum_g += src_pix_ptr[G] * (i + 1); + sum_b += src_pix_ptr[B] * (i + 1); + sum_a += src_pix_ptr[A] * (i + 1); + sum_out_r += src_pix_ptr[R]; + sum_out_g += src_pix_ptr[G]; + sum_out_b += src_pix_ptr[B]; + sum_out_a += src_pix_ptr[A]; + } + for(i = 1; i <= ry; i++) + { + if(i <= hm) src_pix_ptr += stride; + stack_pix_ptr = &stack[i + ry]; + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + sum_r += src_pix_ptr[R] * (ry + 1 - i); + sum_g += src_pix_ptr[G] * (ry + 1 - i); + sum_b += src_pix_ptr[B] * (ry + 1 - i); + sum_a += src_pix_ptr[A] * (ry + 1 - i); + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_in_a += src_pix_ptr[A]; + } + + stack_ptr = ry; + yp = ry; + if(yp > hm) yp = hm; + src_pix_ptr = img.pix_ptr(x, yp); + dst_pix_ptr = img.pix_ptr(x, 0); + for(y = 0; y < h; y++) + { + dst_pix_ptr[R] = (sum_r * mul_sum) >> shr_sum; + dst_pix_ptr[G] = (sum_g * mul_sum) >> shr_sum; + dst_pix_ptr[B] = (sum_b * mul_sum) >> shr_sum; + dst_pix_ptr[A] = (sum_a * mul_sum) >> shr_sum; + dst_pix_ptr += stride; + + sum_r -= sum_out_r; + sum_g -= sum_out_g; + sum_b -= sum_out_b; + sum_a -= sum_out_a; + + stack_start = stack_ptr + div - ry; + if(stack_start >= div) stack_start -= div; + + stack_pix_ptr = &stack[stack_start]; + sum_out_r -= stack_pix_ptr->r; + sum_out_g -= stack_pix_ptr->g; + sum_out_b -= stack_pix_ptr->b; + sum_out_a -= stack_pix_ptr->a; + + if(yp < hm) + { + src_pix_ptr += stride; + ++yp; + } + + stack_pix_ptr->r = src_pix_ptr[R]; + stack_pix_ptr->g = src_pix_ptr[G]; + stack_pix_ptr->b = src_pix_ptr[B]; + stack_pix_ptr->a = src_pix_ptr[A]; + + sum_in_r += src_pix_ptr[R]; + sum_in_g += src_pix_ptr[G]; + sum_in_b += src_pix_ptr[B]; + sum_in_a += src_pix_ptr[A]; + sum_r += sum_in_r; + sum_g += sum_in_g; + sum_b += sum_in_b; + sum_a += sum_in_a; + + ++stack_ptr; + if(stack_ptr >= div) stack_ptr = 0; + stack_pix_ptr = &stack[stack_ptr]; + + sum_out_r += stack_pix_ptr->r; + sum_out_g += stack_pix_ptr->g; + sum_out_b += stack_pix_ptr->b; + sum_out_a += stack_pix_ptr->a; + sum_in_r -= stack_pix_ptr->r; + sum_in_g -= stack_pix_ptr->g; + sum_in_b -= stack_pix_ptr->b; + sum_in_a -= stack_pix_ptr->a; + } + } + } + } + + + + //===========================================================recursive_blur + template class recursive_blur + { + public: + typedef ColorT color_type; + typedef CalculatorT calculator_type; + typedef typename color_type::value_type value_type; + typedef typename calculator_type::value_type calc_type; + + //-------------------------------------------------------------------- + template void blur_x(Img& img, double radius) + { + if(radius < 0.62) return; + if(img.width() < 3) return; + + calc_type s = calc_type(radius * 0.5); + calc_type q = calc_type((s < 2.5) ? + 3.97156 - 4.14554 * sqrt(1 - 0.26891 * s) : + 0.98711 * s - 0.96330); + + calc_type q2 = calc_type(q * q); + calc_type q3 = calc_type(q2 * q); + + calc_type b0 = calc_type(1.0 / (1.578250 + + 2.444130 * q + + 1.428100 * q2 + + 0.422205 * q3)); + + calc_type b1 = calc_type( 2.44413 * q + + 2.85619 * q2 + + 1.26661 * q3); + + calc_type b2 = calc_type(-1.42810 * q2 + + -1.26661 * q3); + + calc_type b3 = calc_type(0.422205 * q3); + + calc_type b = calc_type(1 - (b1 + b2 + b3) * b0); + + b1 *= b0; + b2 *= b0; + b3 *= b0; + + int w = img.width(); + int h = img.height(); + int wm = w-1; + int x, y; + + m_sum1.allocate(w); + m_sum2.allocate(w); + m_buf.allocate(w); + + for(y = 0; y < h; y++) + { + calculator_type c; + c.from_pix(img.pixel(0, y)); + m_sum1[0].calc(b, b1, b2, b3, c, c, c, c); + c.from_pix(img.pixel(1, y)); + m_sum1[1].calc(b, b1, b2, b3, c, m_sum1[0], m_sum1[0], m_sum1[0]); + c.from_pix(img.pixel(2, y)); + m_sum1[2].calc(b, b1, b2, b3, c, m_sum1[1], m_sum1[0], m_sum1[0]); + + for(x = 3; x < w; ++x) + { + c.from_pix(img.pixel(x, y)); + m_sum1[x].calc(b, b1, b2, b3, c, m_sum1[x-1], m_sum1[x-2], m_sum1[x-3]); + } + + m_sum2[wm ].calc(b, b1, b2, b3, m_sum1[wm ], m_sum1[wm ], m_sum1[wm], m_sum1[wm]); + m_sum2[wm-1].calc(b, b1, b2, b3, m_sum1[wm-1], m_sum2[wm ], m_sum2[wm], m_sum2[wm]); + m_sum2[wm-2].calc(b, b1, b2, b3, m_sum1[wm-2], m_sum2[wm-1], m_sum2[wm], m_sum2[wm]); + m_sum2[wm ].to_pix(m_buf[wm ]); + m_sum2[wm-1].to_pix(m_buf[wm-1]); + m_sum2[wm-2].to_pix(m_buf[wm-2]); + + for(x = wm-3; x >= 0; --x) + { + m_sum2[x].calc(b, b1, b2, b3, m_sum1[x], m_sum2[x+1], m_sum2[x+2], m_sum2[x+3]); + m_sum2[x].to_pix(m_buf[x]); + } + img.copy_color_hspan(0, y, w, &m_buf[0]); + } + } + + //-------------------------------------------------------------------- + template void blur_y(Img& img, double radius) + { + pixfmt_transposer img2(img); + blur_x(img2, radius); + } + + //-------------------------------------------------------------------- + template void blur(Img& img, double radius) + { + blur_x(img, radius); + pixfmt_transposer img2(img); + blur_x(img2, radius); + } + + private: + agg::pod_vector m_sum1; + agg::pod_vector m_sum2; + agg::pod_vector m_buf; + }; + + + //=================================================recursive_blur_calc_rgba + template struct recursive_blur_calc_rgba + { + typedef T value_type; + typedef recursive_blur_calc_rgba self_type; + + value_type r,g,b,a; + + template + AGG_INLINE void from_pix(const ColorT& c) + { + r = c.r; + g = c.g; + b = c.b; + a = c.a; + } + + AGG_INLINE void calc(value_type b1, + value_type b2, + value_type b3, + value_type b4, + const self_type& c1, + const self_type& c2, + const self_type& c3, + const self_type& c4) + { + r = b1*c1.r + b2*c2.r + b3*c3.r + b4*c4.r; + g = b1*c1.g + b2*c2.g + b3*c3.g + b4*c4.g; + b = b1*c1.b + b2*c2.b + b3*c3.b + b4*c4.b; + a = b1*c1.a + b2*c2.a + b3*c3.a + b4*c4.a; + } + + template + AGG_INLINE void to_pix(ColorT& c) const + { + typedef typename ColorT::value_type cv_type; + c.r = (cv_type)uround(r); + c.g = (cv_type)uround(g); + c.b = (cv_type)uround(b); + c.a = (cv_type)uround(a); + } + }; + + + //=================================================recursive_blur_calc_rgb + template struct recursive_blur_calc_rgb + { + typedef T value_type; + typedef recursive_blur_calc_rgb self_type; + + value_type r,g,b; + + template + AGG_INLINE void from_pix(const ColorT& c) + { + r = c.r; + g = c.g; + b = c.b; + } + + AGG_INLINE void calc(value_type b1, + value_type b2, + value_type b3, + value_type b4, + const self_type& c1, + const self_type& c2, + const self_type& c3, + const self_type& c4) + { + r = b1*c1.r + b2*c2.r + b3*c3.r + b4*c4.r; + g = b1*c1.g + b2*c2.g + b3*c3.g + b4*c4.g; + b = b1*c1.b + b2*c2.b + b3*c3.b + b4*c4.b; + } + + template + AGG_INLINE void to_pix(ColorT& c) const + { + typedef typename ColorT::value_type cv_type; + c.r = (cv_type)uround(r); + c.g = (cv_type)uround(g); + c.b = (cv_type)uround(b); + } + }; + + + //================================================recursive_blur_calc_gray + template struct recursive_blur_calc_gray + { + typedef T value_type; + typedef recursive_blur_calc_gray self_type; + + value_type v; + + template + AGG_INLINE void from_pix(const ColorT& c) + { + v = c.v; + } + + AGG_INLINE void calc(value_type b1, + value_type b2, + value_type b3, + value_type b4, + const self_type& c1, + const self_type& c2, + const self_type& c3, + const self_type& c4) + { + v = b1*c1.v + b2*c2.v + b3*c3.v + b4*c4.v; + } + + template + AGG_INLINE void to_pix(ColorT& c) const + { + typedef typename ColorT::value_type cv_type; + c.v = (cv_type)uround(v); + } + }; + +} + + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_bounding_rect.h b/desmume/src/windows/agg/include/agg_bounding_rect.h new file mode 100644 index 000000000..eb869eb6d --- /dev/null +++ b/desmume/src/windows/agg/include/agg_bounding_rect.h @@ -0,0 +1,122 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BOUNDING_RECT_INCLUDED +#define AGG_BOUNDING_RECT_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //-----------------------------------------------------------bounding_rect + template + bool bounding_rect(VertexSource& vs, GetId& gi, + unsigned start, unsigned num, + CoordT* x1, CoordT* y1, CoordT* x2, CoordT* y2) + { + unsigned i; + double x; + double y; + bool first = true; + + *x1 = CoordT(1); + *y1 = CoordT(1); + *x2 = CoordT(0); + *y2 = CoordT(0); + + for(i = 0; i < num; i++) + { + vs.rewind(gi[start + i]); + unsigned cmd; + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + if(is_vertex(cmd)) + { + if(first) + { + *x1 = CoordT(x); + *y1 = CoordT(y); + *x2 = CoordT(x); + *y2 = CoordT(y); + first = false; + } + else + { + if(CoordT(x) < *x1) *x1 = CoordT(x); + if(CoordT(y) < *y1) *y1 = CoordT(y); + if(CoordT(x) > *x2) *x2 = CoordT(x); + if(CoordT(y) > *y2) *y2 = CoordT(y); + } + } + } + } + return *x1 <= *x2 && *y1 <= *y2; + } + + + //-----------------------------------------------------bounding_rect_single + template + bool bounding_rect_single(VertexSource& vs, unsigned path_id, + CoordT* x1, CoordT* y1, CoordT* x2, CoordT* y2) + { + double x; + double y; + bool first = true; + + *x1 = CoordT(1); + *y1 = CoordT(1); + *x2 = CoordT(0); + *y2 = CoordT(0); + + vs.rewind(path_id); + unsigned cmd; + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + if(is_vertex(cmd)) + { + if(first) + { + *x1 = CoordT(x); + *y1 = CoordT(y); + *x2 = CoordT(x); + *y2 = CoordT(y); + first = false; + } + else + { + if(CoordT(x) < *x1) *x1 = CoordT(x); + if(CoordT(y) < *y1) *y1 = CoordT(y); + if(CoordT(x) > *x2) *x2 = CoordT(x); + if(CoordT(y) > *y2) *y2 = CoordT(y); + } + } + } + return *x1 <= *x2 && *y1 <= *y2; + } + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_bspline.h b/desmume/src/windows/agg/include/agg_bspline.h new file mode 100644 index 000000000..ff8eda4fc --- /dev/null +++ b/desmume/src/windows/agg/include/agg_bspline.h @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BSPLINE_INCLUDED +#define AGG_BSPLINE_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + //----------------------------------------------------------------bspline + // A very simple class of Bi-cubic Spline interpolation. + // First call init(num, x[], y[]) where num - number of source points, + // x, y - arrays of X and Y values respectively. Here Y must be a function + // of X. It means that all the X-coordinates must be arranged in the ascending + // order. + // Then call get(x) that calculates a value Y for the respective X. + // The class supports extrapolation, i.e. you can call get(x) where x is + // outside the given with init() X-range. Extrapolation is a simple linear + // function. + // + // See Implementation agg_bspline.cpp + //------------------------------------------------------------------------ + class bspline + { + public: + bspline(); + bspline(int num); + bspline(int num, const double* x, const double* y); + + void init(int num); + void add_point(double x, double y); + void prepare(); + + void init(int num, const double* x, const double* y); + + double get(double x) const; + double get_stateful(double x) const; + + private: + bspline(const bspline&); + const bspline& operator = (const bspline&); + + static void bsearch(int n, const double *x, double x0, int *i); + double extrapolation_left(double x) const; + double extrapolation_right(double x) const; + double interpolation(double x, int i) const; + + int m_max; + int m_num; + double* m_x; + double* m_y; + pod_array m_am; + mutable int m_last_idx; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_clip_liang_barsky.h b/desmume/src/windows/agg/include/agg_clip_liang_barsky.h new file mode 100644 index 000000000..a612ddf1f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_clip_liang_barsky.h @@ -0,0 +1,339 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CLIP_LIANG_BARSKY_INCLUDED +#define AGG_CLIP_LIANG_BARSKY_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + enum clipping_flags_e + { + clipping_flags_x1_clipped = 4, + clipping_flags_x2_clipped = 1, + clipping_flags_y1_clipped = 8, + clipping_flags_y2_clipped = 2, + clipping_flags_x_clipped = clipping_flags_x1_clipped | clipping_flags_x2_clipped, + clipping_flags_y_clipped = clipping_flags_y1_clipped | clipping_flags_y2_clipped + }; + + //----------------------------------------------------------clipping_flags + // Determine the clipping code of the vertex according to the + // Cyrus-Beck line clipping algorithm + // + // | | + // 0110 | 0010 | 0011 + // | | + // -------+--------+-------- clip_box.y2 + // | | + // 0100 | 0000 | 0001 + // | | + // -------+--------+-------- clip_box.y1 + // | | + // 1100 | 1000 | 1001 + // | | + // clip_box.x1 clip_box.x2 + // + // + template + inline unsigned clipping_flags(T x, T y, const rect_base& clip_box) + { + return (x > clip_box.x2) | + ((y > clip_box.y2) << 1) | + ((x < clip_box.x1) << 2) | + ((y < clip_box.y1) << 3); + } + + //--------------------------------------------------------clipping_flags_x + template + inline unsigned clipping_flags_x(T x, const rect_base& clip_box) + { + return (x > clip_box.x2) | ((x < clip_box.x1) << 2); + } + + + //--------------------------------------------------------clipping_flags_y + template + inline unsigned clipping_flags_y(T y, const rect_base& clip_box) + { + return ((y > clip_box.y2) << 1) | ((y < clip_box.y1) << 3); + } + + + //-------------------------------------------------------clip_liang_barsky + template + inline unsigned clip_liang_barsky(T x1, T y1, T x2, T y2, + const rect_base& clip_box, + T* x, T* y) + { + const double nearzero = 1e-30; + + double deltax = x2 - x1; + double deltay = y2 - y1; + double xin; + double xout; + double yin; + double yout; + double tinx; + double tiny; + double toutx; + double touty; + double tin1; + double tin2; + double tout1; + unsigned np = 0; + + if(deltax == 0.0) + { + // bump off of the vertical + deltax = (x1 > clip_box.x1) ? -nearzero : nearzero; + } + + if(deltay == 0.0) + { + // bump off of the horizontal + deltay = (y1 > clip_box.y1) ? -nearzero : nearzero; + } + + if(deltax > 0.0) + { + // points to right + xin = clip_box.x1; + xout = clip_box.x2; + } + else + { + xin = clip_box.x2; + xout = clip_box.x1; + } + + if(deltay > 0.0) + { + // points up + yin = clip_box.y1; + yout = clip_box.y2; + } + else + { + yin = clip_box.y2; + yout = clip_box.y1; + } + + tinx = (xin - x1) / deltax; + tiny = (yin - y1) / deltay; + + if (tinx < tiny) + { + // hits x first + tin1 = tinx; + tin2 = tiny; + } + else + { + // hits y first + tin1 = tiny; + tin2 = tinx; + } + + if(tin1 <= 1.0) + { + if(0.0 < tin1) + { + *x++ = (T)xin; + *y++ = (T)yin; + ++np; + } + + if(tin2 <= 1.0) + { + toutx = (xout - x1) / deltax; + touty = (yout - y1) / deltay; + + tout1 = (toutx < touty) ? toutx : touty; + + if(tin2 > 0.0 || tout1 > 0.0) + { + if(tin2 <= tout1) + { + if(tin2 > 0.0) + { + if(tinx > tiny) + { + *x++ = (T)xin; + *y++ = (T)(y1 + tinx * deltay); + } + else + { + *x++ = (T)(x1 + tiny * deltax); + *y++ = (T)yin; + } + ++np; + } + + if(tout1 < 1.0) + { + if(toutx < touty) + { + *x++ = (T)xout; + *y++ = (T)(y1 + toutx * deltay); + } + else + { + *x++ = (T)(x1 + touty * deltax); + *y++ = (T)yout; + } + } + else + { + *x++ = x2; + *y++ = y2; + } + ++np; + } + else + { + if(tinx > tiny) + { + *x++ = (T)xin; + *y++ = (T)yout; + } + else + { + *x++ = (T)xout; + *y++ = (T)yin; + } + ++np; + } + } + } + } + return np; + } + + + //---------------------------------------------------------------------------- + template + bool clip_move_point(T x1, T y1, T x2, T y2, + const rect_base& clip_box, + T* x, T* y, unsigned flags) + { + T bound; + + if(flags & clipping_flags_x_clipped) + { + if(x1 == x2) + { + return false; + } + bound = (flags & clipping_flags_x1_clipped) ? clip_box.x1 : clip_box.x2; + *y = (T)(double(bound - x1) * (y2 - y1) / (x2 - x1) + y1); + *x = bound; + } + + flags = clipping_flags_y(*y, clip_box); + if(flags & clipping_flags_y_clipped) + { + if(y1 == y2) + { + return false; + } + bound = (flags & clipping_flags_y1_clipped) ? clip_box.y1 : clip_box.y2; + *x = (T)(double(bound - y1) * (x2 - x1) / (y2 - y1) + x1); + *y = bound; + } + return true; + } + + //-------------------------------------------------------clip_line_segment + // Returns: ret >= 4 - Fully clipped + // (ret & 1) != 0 - First point has been moved + // (ret & 2) != 0 - Second point has been moved + // + template + unsigned clip_line_segment(T* x1, T* y1, T* x2, T* y2, + const rect_base& clip_box) + { + unsigned f1 = clipping_flags(*x1, *y1, clip_box); + unsigned f2 = clipping_flags(*x2, *y2, clip_box); + unsigned ret = 0; + + if((f2 | f1) == 0) + { + // Fully visible + return 0; + } + + if((f1 & clipping_flags_x_clipped) != 0 && + (f1 & clipping_flags_x_clipped) == (f2 & clipping_flags_x_clipped)) + { + // Fully clipped + return 4; + } + + if((f1 & clipping_flags_y_clipped) != 0 && + (f1 & clipping_flags_y_clipped) == (f2 & clipping_flags_y_clipped)) + { + // Fully clipped + return 4; + } + + T tx1 = *x1; + T ty1 = *y1; + T tx2 = *x2; + T ty2 = *y2; + if(f1) + { + if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x1, y1, f1)) + { + return 4; + } + if(*x1 == *x2 && *y1 == *y2) + { + return 4; + } + ret |= 1; + } + if(f2) + { + if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x2, y2, f2)) + { + return 4; + } + if(*x1 == *x2 && *y1 == *y2) + { + return 4; + } + ret |= 2; + } + return ret; + } + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_color_gray.h b/desmume/src/windows/agg/include/agg_color_gray.h new file mode 100644 index 000000000..74ed66e1d --- /dev/null +++ b/desmume/src/windows/agg/include/agg_color_gray.h @@ -0,0 +1,423 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// +// color types gray8, gray16 +// +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_GRAY_INCLUDED +#define AGG_COLOR_GRAY_INCLUDED + +#include "agg_basics.h" +#include "agg_color_rgba.h" + +namespace agg +{ + + //===================================================================gray8 + struct gray8 + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1 + }; + typedef gray8 self_type; + + value_type v; + value_type a; + + //-------------------------------------------------------------------- + gray8() {} + + //-------------------------------------------------------------------- + gray8(unsigned v_, unsigned a_=base_mask) : + v(int8u(v_)), a(int8u(a_)) {} + + //-------------------------------------------------------------------- + gray8(const self_type& c, unsigned a_) : + v(c.v), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + gray8(const rgba& c) : + v((value_type)uround((0.299*c.r + 0.587*c.g + 0.114*c.b) * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + gray8(const rgba& c, double a_) : + v((value_type)uround((0.299*c.r + 0.587*c.g + 0.114*c.b) * double(base_mask))), + a((value_type)uround(a_ * double(base_mask))) {} + + //-------------------------------------------------------------------- + gray8(const rgba8& c) : + v((c.r*77 + c.g*150 + c.b*29) >> 8), + a(c.a) {} + + //-------------------------------------------------------------------- + gray8(const rgba8& c, unsigned a_) : + v((c.r*77 + c.g*150 + c.b*29) >> 8), + a(a_) {} + + //-------------------------------------------------------------------- + void clear() + { + v = a = 0; + } + + //-------------------------------------------------------------------- + const self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + void opacity(double a_) + { + if(a_ < 0.0) a_ = 0.0; + if(a_ > 1.0) a_ = 1.0; + a = (value_type)uround(a_ * double(base_mask)); + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + + //-------------------------------------------------------------------- + const self_type& premultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + v = 0; + return *this; + } + v = value_type((calc_type(v) * a) >> base_shift); + return *this; + } + + //-------------------------------------------------------------------- + const self_type& premultiply(unsigned a_) + { + if(a == base_mask && a_ >= base_mask) return *this; + if(a == 0 || a_ == 0) + { + v = a = 0; + return *this; + } + calc_type v_ = (calc_type(v) * a_) / a; + v = value_type((v_ > a_) ? a_ : v_); + a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + const self_type& demultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + v = 0; + return *this; + } + calc_type v_ = (calc_type(v) * base_mask) / a; + v = value_type((v_ > base_mask) ? (value_type)base_mask : v_); + return *this; + } + + //-------------------------------------------------------------------- + self_type gradient(self_type c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.v = value_type(calc_type(v) + (((calc_type(c.v) - v) * ik) >> base_shift)); + ret.a = value_type(calc_type(a) + (((calc_type(c.a) - a) * ik) >> base_shift)); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cv, ca; + if(cover == cover_mask) + { + if(c.a == base_mask) + { + *this = c; + } + else + { + cv = v + c.v; v = (cv > calc_type(base_mask)) ? calc_type(base_mask) : cv; + ca = a + c.a; a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + else + { + cv = v + ((c.v * cover + cover_mask/2) >> cover_shift); + ca = a + ((c.a * cover + cover_mask/2) >> cover_shift); + v = (cv > calc_type(base_mask)) ? calc_type(base_mask) : cv; + a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0); } + }; + + + //-------------------------------------------------------------gray8_pre + inline gray8 gray8_pre(unsigned v, unsigned a = gray8::base_mask) + { + return gray8(v,a).premultiply(); + } + inline gray8 gray8_pre(const gray8& c, unsigned a) + { + return gray8(c,a).premultiply(); + } + inline gray8 gray8_pre(const rgba& c) + { + return gray8(c).premultiply(); + } + inline gray8 gray8_pre(const rgba& c, double a) + { + return gray8(c,a).premultiply(); + } + inline gray8 gray8_pre(const rgba8& c) + { + return gray8(c).premultiply(); + } + inline gray8 gray8_pre(const rgba8& c, unsigned a) + { + return gray8(c,a).premultiply(); + } + + + + + //==================================================================gray16 + struct gray16 + { + typedef int16u value_type; + typedef int32u calc_type; + typedef int64 long_type; + enum base_scale_e + { + base_shift = 16, + base_scale = 1 << base_shift, + base_mask = base_scale - 1 + }; + typedef gray16 self_type; + + value_type v; + value_type a; + + //-------------------------------------------------------------------- + gray16() {} + + //-------------------------------------------------------------------- + gray16(unsigned v_, unsigned a_=base_mask) : + v(int16u(v_)), a(int16u(a_)) {} + + //-------------------------------------------------------------------- + gray16(const self_type& c, unsigned a_) : + v(c.v), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + gray16(const rgba& c) : + v((value_type)uround((0.299*c.r + 0.587*c.g + 0.114*c.b) * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + gray16(const rgba& c, double a_) : + v((value_type)uround((0.299*c.r + 0.587*c.g + 0.114*c.b) * double(base_mask))), + a((value_type)uround(a_ * double(base_mask))) {} + + //-------------------------------------------------------------------- + gray16(const rgba8& c) : + v(c.r*77 + c.g*150 + c.b*29), + a((value_type(c.a) << 8) | c.a) {} + + //-------------------------------------------------------------------- + gray16(const rgba8& c, unsigned a_) : + v(c.r*77 + c.g*150 + c.b*29), + a((value_type(a_) << 8) | c.a) {} + + //-------------------------------------------------------------------- + void clear() + { + v = a = 0; + } + + //-------------------------------------------------------------------- + const self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + void opacity(double a_) + { + if(a_ < 0.0) a_ = 0.0; + if(a_ > 1.0) a_ = 1.0; + a = (value_type)uround(a_ * double(base_mask)); + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + + //-------------------------------------------------------------------- + const self_type& premultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + v = 0; + return *this; + } + v = value_type((calc_type(v) * a) >> base_shift); + return *this; + } + + //-------------------------------------------------------------------- + const self_type& premultiply(unsigned a_) + { + if(a == base_mask && a_ >= base_mask) return *this; + if(a == 0 || a_ == 0) + { + v = a = 0; + return *this; + } + calc_type v_ = (calc_type(v) * a_) / a; + v = value_type((v_ > a_) ? a_ : v_); + a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + const self_type& demultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + v = 0; + return *this; + } + calc_type v_ = (calc_type(v) * base_mask) / a; + v = value_type((v_ > base_mask) ? base_mask : v_); + return *this; + } + + //-------------------------------------------------------------------- + self_type gradient(self_type c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.v = value_type(calc_type(v) + (((calc_type(c.v) - v) * ik) >> base_shift)); + ret.a = value_type(calc_type(a) + (((calc_type(c.a) - a) * ik) >> base_shift)); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cv, ca; + if(cover == cover_mask) + { + if(c.a == base_mask) + { + *this = c; + } + else + { + cv = v + c.v; v = (cv > calc_type(base_mask)) ? calc_type(base_mask) : cv; + ca = a + c.a; a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + else + { + cv = v + ((c.v * cover + cover_mask/2) >> cover_shift); + ca = a + ((c.a * cover + cover_mask/2) >> cover_shift); + v = (cv > calc_type(base_mask)) ? calc_type(base_mask) : cv; + a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0); } + }; + + + //------------------------------------------------------------gray16_pre + inline gray16 gray16_pre(unsigned v, unsigned a = gray16::base_mask) + { + return gray16(v,a).premultiply(); + } + inline gray16 gray16_pre(const gray16& c, unsigned a) + { + return gray16(c,a).premultiply(); + } + inline gray16 gray16_pre(const rgba& c) + { + return gray16(c).premultiply(); + } + inline gray16 gray16_pre(const rgba& c, double a) + { + return gray16(c,a).premultiply(); + } + inline gray16 gray16_pre(const rgba8& c) + { + return gray16(c).premultiply(); + } + inline gray16 gray16_pre(const rgba8& c, unsigned a) + { + return gray16(c,a).premultiply(); + } + + +} + + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_color_rgba.h b/desmume/src/windows/agg/include/agg_color_rgba.h new file mode 100644 index 000000000..7cc25b5f0 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_color_rgba.h @@ -0,0 +1,756 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_RGBA_INCLUDED +#define AGG_COLOR_RGBA_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + // Supported byte orders for RGB and RGBA pixel formats + //======================================================================= + struct order_rgb { enum rgb_e { R=0, G=1, B=2, rgb_tag }; }; //----order_rgb + struct order_bgr { enum bgr_e { B=0, G=1, R=2, rgb_tag }; }; //----order_bgr + struct order_rgba { enum rgba_e { R=0, G=1, B=2, A=3, rgba_tag }; }; //----order_rgba + struct order_argb { enum argb_e { A=0, R=1, G=2, B=3, rgba_tag }; }; //----order_argb + struct order_abgr { enum abgr_e { A=0, B=1, G=2, R=3, rgba_tag }; }; //----order_abgr + struct order_bgra { enum bgra_e { B=0, G=1, R=2, A=3, rgba_tag }; }; //----order_bgra + + //====================================================================rgba + struct rgba + { + typedef double value_type; + + double r; + double g; + double b; + double a; + + //-------------------------------------------------------------------- + rgba() {} + + //-------------------------------------------------------------------- + rgba(double r_, double g_, double b_, double a_=1.0) : + r(r_), g(g_), b(b_), a(a_) {} + + //-------------------------------------------------------------------- + rgba(const rgba& c, double a_) : r(c.r), g(c.g), b(c.b), a(a_) {} + + //-------------------------------------------------------------------- + void clear() + { + r = g = b = a = 0; + } + + //-------------------------------------------------------------------- + const rgba& transparent() + { + a = 0.0; + return *this; + } + + //-------------------------------------------------------------------- + const rgba& opacity(double a_) + { + if(a_ < 0.0) a_ = 0.0; + if(a_ > 1.0) a_ = 1.0; + a = a_; + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return a; + } + + //-------------------------------------------------------------------- + const rgba& premultiply() + { + r *= a; + g *= a; + b *= a; + return *this; + } + + //-------------------------------------------------------------------- + const rgba& premultiply(double a_) + { + if(a <= 0.0 || a_ <= 0.0) + { + r = g = b = a = 0.0; + return *this; + } + a_ /= a; + r *= a_; + g *= a_; + b *= a_; + a = a_; + return *this; + } + + //-------------------------------------------------------------------- + const rgba& demultiply() + { + if(a == 0) + { + r = g = b = 0; + return *this; + } + double a_ = 1.0 / a; + r *= a_; + g *= a_; + b *= a_; + return *this; + } + + + //-------------------------------------------------------------------- + rgba gradient(rgba c, double k) const + { + rgba ret; + ret.r = r + (c.r - r) * k; + ret.g = g + (c.g - g) * k; + ret.b = b + (c.b - b) * k; + ret.a = a + (c.a - a) * k; + return ret; + } + + //-------------------------------------------------------------------- + static rgba no_color() { return rgba(0,0,0,0); } + + //-------------------------------------------------------------------- + static rgba from_wavelength(double wl, double gamma = 1.0); + + //-------------------------------------------------------------------- + explicit rgba(double wavelen, double gamma=1.0) + { + *this = from_wavelength(wavelen, gamma); + } + + }; + + //----------------------------------------------------------------rgba_pre + inline rgba rgba_pre(double r, double g, double b, double a=1.0) + { + return rgba(r, g, b, a).premultiply(); + } + inline rgba rgba_pre(const rgba& c) + { + return rgba(c).premultiply(); + } + inline rgba rgba_pre(const rgba& c, double a) + { + return rgba(c, a).premultiply(); + } + + //------------------------------------------------------------------------ + inline rgba rgba::from_wavelength(double wl, double gamma) + { + rgba t(0.0, 0.0, 0.0); + + if(wl >= 380.0 && wl <= 440.0) + { + t.r = -1.0 * (wl - 440.0) / (440.0 - 380.0); + t.b = 1.0; + } + else + if(wl >= 440.0 && wl <= 490.0) + { + t.g = (wl - 440.0) / (490.0 - 440.0); + t.b = 1.0; + } + else + if(wl >= 490.0 && wl <= 510.0) + { + t.g = 1.0; + t.b = -1.0 * (wl - 510.0) / (510.0 - 490.0); + } + else + if(wl >= 510.0 && wl <= 580.0) + { + t.r = (wl - 510.0) / (580.0 - 510.0); + t.g = 1.0; + } + else + if(wl >= 580.0 && wl <= 645.0) + { + t.r = 1.0; + t.g = -1.0 * (wl - 645.0) / (645.0 - 580.0); + } + else + if(wl >= 645.0 && wl <= 780.0) + { + t.r = 1.0; + } + + double s = 1.0; + if(wl > 700.0) s = 0.3 + 0.7 * (780.0 - wl) / (780.0 - 700.0); + else if(wl < 420.0) s = 0.3 + 0.7 * (wl - 380.0) / (420.0 - 380.0); + + t.r = pow(t.r * s, gamma); + t.g = pow(t.g * s, gamma); + t.b = pow(t.b * s, gamma); + return t; + } + + + + + //===================================================================rgba8 + struct rgba8 + { + typedef int8u value_type; + typedef int32u calc_type; + typedef int32 long_type; + enum base_scale_e + { + base_shift = 8, + base_scale = 1 << base_shift, + base_mask = base_scale - 1 + }; + typedef rgba8 self_type; + + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba8() {} + + //-------------------------------------------------------------------- + rgba8(unsigned r_, unsigned g_, unsigned b_, unsigned a_=base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba8(const rgba& c, double a_) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(a_ * double(base_mask))) {} + + //-------------------------------------------------------------------- + rgba8(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba8(const rgba& c) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + void clear() + { + r = g = b = a = 0; + } + + //-------------------------------------------------------------------- + const self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + const self_type& opacity(double a_) + { + if(a_ < 0.0) a_ = 0.0; + if(a_ > 1.0) a_ = 1.0; + a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& premultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + r = g = b = 0; + return *this; + } + r = value_type((calc_type(r) * a) >> base_shift); + g = value_type((calc_type(g) * a) >> base_shift); + b = value_type((calc_type(b) * a) >> base_shift); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& premultiply(unsigned a_) + { + if(a == base_mask && a_ >= base_mask) return *this; + if(a == 0 || a_ == 0) + { + r = g = b = a = 0; + return *this; + } + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& demultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + r = g = b = 0; + return *this; + } + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.r = value_type(calc_type(r) + (((calc_type(c.r) - r) * ik) >> base_shift)); + ret.g = value_type(calc_type(g) + (((calc_type(c.g) - g) * ik) >> base_shift)); + ret.b = value_type(calc_type(b) + (((calc_type(c.b) - b) * ik) >> base_shift)); + ret.a = value_type(calc_type(a) + (((calc_type(c.a) - a) * ik) >> base_shift)); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if(cover == cover_mask) + { + if(c.a == base_mask) + { + *this = c; + } + else + { + cr = r + c.r; r = (cr > calc_type(base_mask)) ? calc_type(base_mask) : cr; + cg = g + c.g; g = (cg > calc_type(base_mask)) ? calc_type(base_mask) : cg; + cb = b + c.b; b = (cb > calc_type(base_mask)) ? calc_type(base_mask) : cb; + ca = a + c.a; a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + else + { + cr = r + ((c.r * cover + cover_mask/2) >> cover_shift); + cg = g + ((c.g * cover + cover_mask/2) >> cover_shift); + cb = b + ((c.b * cover + cover_mask/2) >> cover_shift); + ca = a + ((c.a * cover + cover_mask/2) >> cover_shift); + r = (cr > calc_type(base_mask)) ? calc_type(base_mask) : cr; + g = (cg > calc_type(base_mask)) ? calc_type(base_mask) : cg; + b = (cb > calc_type(base_mask)) ? calc_type(base_mask) : cb; + a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + + //-------------------------------------------------------------rgba8_pre + inline rgba8 rgba8_pre(unsigned r, unsigned g, unsigned b, + unsigned a = rgba8::base_mask) + { + return rgba8(r,g,b,a).premultiply(); + } + inline rgba8 rgba8_pre(const rgba8& c) + { + return rgba8(c).premultiply(); + } + inline rgba8 rgba8_pre(const rgba8& c, unsigned a) + { + return rgba8(c,a).premultiply(); + } + inline rgba8 rgba8_pre(const rgba& c) + { + return rgba8(c).premultiply(); + } + inline rgba8 rgba8_pre(const rgba& c, double a) + { + return rgba8(c,a).premultiply(); + } + + + //-------------------------------------------------------------rgb8_packed + inline rgba8 rgb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); + } + + //-------------------------------------------------------------bgr8_packed + inline rgba8 bgr8_packed(unsigned v) + { + return rgba8(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF); + } + + //------------------------------------------------------------argb8_packed + inline rgba8 argb8_packed(unsigned v) + { + return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, v >> 24); + } + + //---------------------------------------------------------rgba8_gamma_dir + template + rgba8 rgba8_gamma_dir(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //---------------------------------------------------------rgba8_gamma_inv + template + rgba8 rgba8_gamma_inv(rgba8 c, const GammaLUT& gamma) + { + return rgba8(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + + + + + //==================================================================rgba16 + struct rgba16 + { + typedef int16u value_type; + typedef int32u calc_type; + typedef int64 long_type; + enum base_scale_e + { + base_shift = 16, + base_scale = 1 << base_shift, + base_mask = base_scale - 1 + }; + typedef rgba16 self_type; + + value_type r; + value_type g; + value_type b; + value_type a; + + //-------------------------------------------------------------------- + rgba16() {} + + //-------------------------------------------------------------------- + rgba16(unsigned r_, unsigned g_, unsigned b_, unsigned a_=base_mask) : + r(value_type(r_)), + g(value_type(g_)), + b(value_type(b_)), + a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const self_type& c, unsigned a_) : + r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} + + //-------------------------------------------------------------------- + rgba16(const rgba& c) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(c.a * double(base_mask))) {} + + //-------------------------------------------------------------------- + rgba16(const rgba& c, double a_) : + r((value_type)uround(c.r * double(base_mask))), + g((value_type)uround(c.g * double(base_mask))), + b((value_type)uround(c.b * double(base_mask))), + a((value_type)uround(a_ * double(base_mask))) {} + + //-------------------------------------------------------------------- + rgba16(const rgba8& c) : + r(value_type((value_type(c.r) << 8) | c.r)), + g(value_type((value_type(c.g) << 8) | c.g)), + b(value_type((value_type(c.b) << 8) | c.b)), + a(value_type((value_type(c.a) << 8) | c.a)) {} + + //-------------------------------------------------------------------- + rgba16(const rgba8& c, unsigned a_) : + r(value_type((value_type(c.r) << 8) | c.r)), + g(value_type((value_type(c.g) << 8) | c.g)), + b(value_type((value_type(c.b) << 8) | c.b)), + a(value_type(( a_ << 8) | c.a)) {} + + //-------------------------------------------------------------------- + void clear() + { + r = g = b = a = 0; + } + + //-------------------------------------------------------------------- + const self_type& transparent() + { + a = 0; + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& opacity(double a_) + { + if(a_ < 0.0) a_ = 0.0; + if(a_ > 1.0) a_ = 1.0; + a = (value_type)uround(a_ * double(base_mask)); + return *this; + } + + //-------------------------------------------------------------------- + double opacity() const + { + return double(a) / double(base_mask); + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& premultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + r = g = b = 0; + return *this; + } + r = value_type((calc_type(r) * a) >> base_shift); + g = value_type((calc_type(g) * a) >> base_shift); + b = value_type((calc_type(b) * a) >> base_shift); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& premultiply(unsigned a_) + { + if(a == base_mask && a_ >= base_mask) return *this; + if(a == 0 || a_ == 0) + { + r = g = b = a = 0; + return *this; + } + calc_type r_ = (calc_type(r) * a_) / a; + calc_type g_ = (calc_type(g) * a_) / a; + calc_type b_ = (calc_type(b) * a_) / a; + r = value_type((r_ > a_) ? a_ : r_); + g = value_type((g_ > a_) ? a_ : g_); + b = value_type((b_ > a_) ? a_ : b_); + a = value_type(a_); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE const self_type& demultiply() + { + if(a == base_mask) return *this; + if(a == 0) + { + r = g = b = 0; + return *this; + } + calc_type r_ = (calc_type(r) * base_mask) / a; + calc_type g_ = (calc_type(g) * base_mask) / a; + calc_type b_ = (calc_type(b) * base_mask) / a; + r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); + g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); + b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); + return *this; + } + + //-------------------------------------------------------------------- + AGG_INLINE self_type gradient(const self_type& c, double k) const + { + self_type ret; + calc_type ik = uround(k * base_scale); + ret.r = value_type(calc_type(r) + (((calc_type(c.r) - r) * ik) >> base_shift)); + ret.g = value_type(calc_type(g) + (((calc_type(c.g) - g) * ik) >> base_shift)); + ret.b = value_type(calc_type(b) + (((calc_type(c.b) - b) * ik) >> base_shift)); + ret.a = value_type(calc_type(a) + (((calc_type(c.a) - a) * ik) >> base_shift)); + return ret; + } + + //-------------------------------------------------------------------- + AGG_INLINE void add(const self_type& c, unsigned cover) + { + calc_type cr, cg, cb, ca; + if(cover == cover_mask) + { + if(c.a == base_mask) + { + *this = c; + } + else + { + cr = r + c.r; r = (cr > calc_type(base_mask)) ? calc_type(base_mask) : cr; + cg = g + c.g; g = (cg > calc_type(base_mask)) ? calc_type(base_mask) : cg; + cb = b + c.b; b = (cb > calc_type(base_mask)) ? calc_type(base_mask) : cb; + ca = a + c.a; a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + else + { + cr = r + ((c.r * cover + cover_mask) >> cover_shift); + cg = g + ((c.g * cover + cover_mask) >> cover_shift); + cb = b + ((c.b * cover + cover_mask) >> cover_shift); + ca = a + ((c.a * cover + cover_mask) >> cover_shift); + r = (cr > calc_type(base_mask)) ? calc_type(base_mask) : cr; + g = (cg > calc_type(base_mask)) ? calc_type(base_mask) : cg; + b = (cb > calc_type(base_mask)) ? calc_type(base_mask) : cb; + a = (ca > calc_type(base_mask)) ? calc_type(base_mask) : ca; + } + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) + { + r = gamma.dir(r); + g = gamma.dir(g); + b = gamma.dir(b); + } + + //-------------------------------------------------------------------- + template + AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) + { + r = gamma.inv(r); + g = gamma.inv(g); + b = gamma.inv(b); + } + + //-------------------------------------------------------------------- + static self_type no_color() { return self_type(0,0,0,0); } + + //-------------------------------------------------------------------- + static self_type from_wavelength(double wl, double gamma = 1.0) + { + return self_type(rgba::from_wavelength(wl, gamma)); + } + }; + + + + //--------------------------------------------------------------rgba16_pre + inline rgba16 rgba16_pre(unsigned r, unsigned g, unsigned b, + unsigned a = rgba16::base_mask) + { + return rgba16(r,g,b,a).premultiply(); + } + inline rgba16 rgba16_pre(const rgba16& c, unsigned a) + { + return rgba16(c,a).premultiply(); + } + inline rgba16 rgba16_pre(const rgba& c) + { + return rgba16(c).premultiply(); + } + inline rgba16 rgba16_pre(const rgba& c, double a) + { + return rgba16(c,a).premultiply(); + } + inline rgba16 rgba16_pre(const rgba8& c) + { + return rgba16(c).premultiply(); + } + inline rgba16 rgba16_pre(const rgba8& c, unsigned a) + { + return rgba16(c,a).premultiply(); + } + + + //------------------------------------------------------rgba16_gamma_dir + template + rgba16 rgba16_gamma_dir(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); + } + + //------------------------------------------------------rgba16_gamma_inv + template + rgba16 rgba16_gamma_inv(rgba16 c, const GammaLUT& gamma) + { + return rgba16(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); + } + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_config.h b/desmume/src/windows/agg/include/agg_config.h new file mode 100644 index 000000000..372b77bfc --- /dev/null +++ b/desmume/src/windows/agg/include/agg_config.h @@ -0,0 +1,44 @@ +#ifndef AGG_CONFIG_INCLUDED +#define AGG_CONFIG_INCLUDED + +// This file can be used to redefine certain data types. + +//--------------------------------------- +// 1. Default basic types such as: +// +// AGG_INT8 +// AGG_INT8U +// AGG_INT16 +// AGG_INT16U +// AGG_INT32 +// AGG_INT32U +// AGG_INT64 +// AGG_INT64U +// +// Just replace this file with new defines if necessary. +// For example, if your compiler doesn't have a 64 bit integer type +// you can still use AGG if you define the follows: +// +// #define AGG_INT64 int +// #define AGG_INT64U unsigned +// +// It will result in overflow in 16 bit-per-component image/pattern resampling +// but it won't result any crash and the rest of the library will remain +// fully functional. + + +//--------------------------------------- +// 2. Default rendering_buffer type. Can be: +// +// Provides faster access for massive pixel operations, +// such as blur, image filtering: +// #define AGG_RENDERING_BUFFER row_ptr_cache +// +// Provides cheaper creation and destruction (no mem allocs): +// #define AGG_RENDERING_BUFFER row_accessor +// +// You can still use both of them simultaneouslyin your applications +// This #define is used only for default rendering_buffer type, +// in short hand typedefs like pixfmt_rgba32. + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_adaptor_vcgen.h b/desmume/src/windows/agg/include/agg_conv_adaptor_vcgen.h new file mode 100644 index 000000000..9719c715e --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_adaptor_vcgen.h @@ -0,0 +1,166 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_ADAPTOR_VCGEN_INCLUDED +#define AGG_CONV_ADAPTOR_VCGEN_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //------------------------------------------------------------null_markers + struct null_markers + { + void remove_all() {} + void add_vertex(double, double, unsigned) {} + void prepare_src() {} + + void rewind(unsigned) {} + unsigned vertex(double*, double*) { return path_cmd_stop; } + }; + + + //------------------------------------------------------conv_adaptor_vcgen + template class conv_adaptor_vcgen + { + enum status + { + initial, + accumulate, + generate + }; + + public: + explicit conv_adaptor_vcgen(VertexSource& source) : + m_source(&source), + m_status(initial) + {} + void attach(VertexSource& source) { m_source = &source; } + + Generator& generator() { return m_generator; } + const Generator& generator() const { return m_generator; } + + Markers& markers() { return m_markers; } + const Markers& markers() const { return m_markers; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + m_status = initial; + } + + unsigned vertex(double* x, double* y); + + private: + // Prohibit copying + conv_adaptor_vcgen(const conv_adaptor_vcgen&); + const conv_adaptor_vcgen& + operator = (const conv_adaptor_vcgen&); + + VertexSource* m_source; + Generator m_generator; + Markers m_markers; + status m_status; + unsigned m_last_cmd; + double m_start_x; + double m_start_y; + }; + + + + + + //------------------------------------------------------------------------ + template + unsigned conv_adaptor_vcgen::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + bool done = false; + while(!done) + { + switch(m_status) + { + case initial: + m_markers.remove_all(); + m_last_cmd = m_source->vertex(&m_start_x, &m_start_y); + m_status = accumulate; + + case accumulate: + if(is_stop(m_last_cmd)) return path_cmd_stop; + + m_generator.remove_all(); + m_generator.add_vertex(m_start_x, m_start_y, path_cmd_move_to); + m_markers.add_vertex(m_start_x, m_start_y, path_cmd_move_to); + + for(;;) + { + cmd = m_source->vertex(x, y); + if(is_vertex(cmd)) + { + m_last_cmd = cmd; + if(is_move_to(cmd)) + { + m_start_x = *x; + m_start_y = *y; + break; + } + m_generator.add_vertex(*x, *y, cmd); + m_markers.add_vertex(*x, *y, path_cmd_line_to); + } + else + { + if(is_stop(cmd)) + { + m_last_cmd = path_cmd_stop; + break; + } + if(is_end_poly(cmd)) + { + m_generator.add_vertex(*x, *y, cmd); + break; + } + } + } + m_generator.rewind(0); + m_status = generate; + + case generate: + cmd = m_generator.vertex(x, y); + if(is_stop(cmd)) + { + m_status = accumulate; + break; + } + done = true; + break; + } + } + return cmd; + } + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_adaptor_vpgen.h b/desmume/src/windows/agg/include/agg_conv_adaptor_vpgen.h new file mode 100644 index 000000000..2243e1e87 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_adaptor_vpgen.h @@ -0,0 +1,168 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_ADAPTOR_VPGEN_INCLUDED +#define AGG_CONV_ADAPTOR_VPGEN_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //======================================================conv_adaptor_vpgen + template class conv_adaptor_vpgen + { + public: + explicit conv_adaptor_vpgen(VertexSource& source) : m_source(&source) {} + void attach(VertexSource& source) { m_source = &source; } + + VPGen& vpgen() { return m_vpgen; } + const VPGen& vpgen() const { return m_vpgen; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + conv_adaptor_vpgen(const conv_adaptor_vpgen&); + const conv_adaptor_vpgen& + operator = (const conv_adaptor_vpgen&); + + VertexSource* m_source; + VPGen m_vpgen; + double m_start_x; + double m_start_y; + unsigned m_poly_flags; + int m_vertices; + }; + + + + //------------------------------------------------------------------------ + template + void conv_adaptor_vpgen::rewind(unsigned path_id) + { + m_source->rewind(path_id); + m_vpgen.reset(); + m_start_x = 0; + m_start_y = 0; + m_poly_flags = 0; + m_vertices = 0; + } + + + //------------------------------------------------------------------------ + template + unsigned conv_adaptor_vpgen::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + for(;;) + { + cmd = m_vpgen.vertex(x, y); + if(!is_stop(cmd)) break; + + if(m_poly_flags && !m_vpgen.auto_unclose()) + { + *x = 0.0; + *y = 0.0; + cmd = m_poly_flags; + m_poly_flags = 0; + break; + } + + if(m_vertices < 0) + { + if(m_vertices < -1) + { + m_vertices = 0; + return path_cmd_stop; + } + m_vpgen.move_to(m_start_x, m_start_y); + m_vertices = 1; + continue; + } + + double tx, ty; + cmd = m_source->vertex(&tx, &ty); + if(is_vertex(cmd)) + { + if(is_move_to(cmd)) + { + if(m_vpgen.auto_close() && m_vertices > 2) + { + m_vpgen.line_to(m_start_x, m_start_y); + m_poly_flags = path_cmd_end_poly | path_flags_close; + m_start_x = tx; + m_start_y = ty; + m_vertices = -1; + continue; + } + m_vpgen.move_to(tx, ty); + m_start_x = tx; + m_start_y = ty; + m_vertices = 1; + } + else + { + m_vpgen.line_to(tx, ty); + ++m_vertices; + } + } + else + { + if(is_end_poly(cmd)) + { + m_poly_flags = cmd; + if(is_closed(cmd) || m_vpgen.auto_close()) + { + if(m_vpgen.auto_close()) m_poly_flags |= path_flags_close; + if(m_vertices > 2) + { + m_vpgen.line_to(m_start_x, m_start_y); + } + m_vertices = 0; + } + } + else + { + // path_cmd_stop + if(m_vpgen.auto_close() && m_vertices > 2) + { + m_vpgen.line_to(m_start_x, m_start_y); + m_poly_flags = path_cmd_end_poly | path_flags_close; + m_vertices = -2; + continue; + } + break; + } + } + } + return cmd; + } + + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_conv_bspline.h b/desmume/src/windows/agg/include/agg_conv_bspline.h new file mode 100644 index 000000000..ff5fb751f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_bspline.h @@ -0,0 +1,58 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_BSPLINE_INCLUDED +#define AGG_CONV_BSPLINE_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_bspline.h" +#include "agg_conv_adaptor_vcgen.h" + + +namespace agg +{ + + //---------------------------------------------------------conv_bspline + template + struct conv_bspline : public conv_adaptor_vcgen + { + typedef conv_adaptor_vcgen base_type; + + conv_bspline(VertexSource& vs) : + conv_adaptor_vcgen(vs) {} + + void interpolation_step(double v) { base_type::generator().interpolation_step(v); } + double interpolation_step() const { return base_type::generator().interpolation_step(); } + + private: + conv_bspline(const conv_bspline&); + const conv_bspline& + operator = (const conv_bspline&); + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_conv_clip_polygon.h b/desmume/src/windows/agg/include/agg_conv_clip_polygon.h new file mode 100644 index 000000000..e1e9d45a3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_clip_polygon.h @@ -0,0 +1,72 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Polygon clipping converter +// There an optimized Liang-Basky algorithm is used. +// The algorithm doesn't optimize the degenerate edges, i.e. it will never +// break a closed polygon into two or more ones, instead, there will be +// degenerate edges coinciding with the respective clipping boundaries. +// This is a sub-optimal solution, because that optimization would require +// extra, rather expensive math while the rasterizer tolerates it quite well, +// without any considerable overhead. +// +//---------------------------------------------------------------------------- +#ifndef AGG_CONV_CLIP_POLYGON_INCLUDED +#define AGG_CONV_CLIP_POLYGON_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_adaptor_vpgen.h" +#include "agg_vpgen_clip_polygon.h" + +namespace agg +{ + + //=======================================================conv_clip_polygon + template + struct conv_clip_polygon : public conv_adaptor_vpgen + { + typedef conv_adaptor_vpgen base_type; + + conv_clip_polygon(VertexSource& vs) : + conv_adaptor_vpgen(vs) {} + + void clip_box(double x1, double y1, double x2, double y2) + { + base_type::vpgen().clip_box(x1, y1, x2, y2); + } + + double x1() const { return base_type::vpgen().x1(); } + double y1() const { return base_type::vpgen().y1(); } + double x2() const { return base_type::vpgen().x2(); } + double y2() const { return base_type::vpgen().y2(); } + + private: + conv_clip_polygon(const conv_clip_polygon&); + const conv_clip_polygon& + operator = (const conv_clip_polygon&); + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_clip_polyline.h b/desmume/src/windows/agg/include/agg_conv_clip_polyline.h new file mode 100644 index 000000000..7614f7eb3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_clip_polyline.h @@ -0,0 +1,72 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// polyline clipping converter +// There an optimized Liang-Basky algorithm is used. +// The algorithm doesn't optimize the degenerate edges, i.e. it will never +// break a closed polyline into two or more ones, instead, there will be +// degenerate edges coinciding with the respective clipping boundaries. +// This is a sub-optimal solution, because that optimization would require +// extra, rather expensive math while the rasterizer tolerates it quite well, +// without any considerable overhead. +// +//---------------------------------------------------------------------------- +#ifndef AGG_CONV_CLIP_polyline_INCLUDED +#define AGG_CONV_CLIP_polyline_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_adaptor_vpgen.h" +#include "agg_vpgen_clip_polyline.h" + +namespace agg +{ + + //=======================================================conv_clip_polyline + template + struct conv_clip_polyline : public conv_adaptor_vpgen + { + typedef conv_adaptor_vpgen base_type; + + conv_clip_polyline(VertexSource& vs) : + conv_adaptor_vpgen(vs) {} + + void clip_box(double x1, double y1, double x2, double y2) + { + base_type::vpgen().clip_box(x1, y1, x2, y2); + } + + double x1() const { return base_type::vpgen().x1(); } + double y1() const { return base_type::vpgen().y1(); } + double x2() const { return base_type::vpgen().x2(); } + double y2() const { return base_type::vpgen().y2(); } + + private: + conv_clip_polyline(const conv_clip_polyline&); + const conv_clip_polyline& + operator = (const conv_clip_polyline&); + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_close_polygon.h b/desmume/src/windows/agg/include/agg_conv_close_polygon.h new file mode 100644 index 000000000..e50deed62 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_close_polygon.h @@ -0,0 +1,134 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_CLOSE_POLYGON_INCLUDED +#define AGG_CONV_CLOSE_POLYGON_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //======================================================conv_close_polygon + template class conv_close_polygon + { + public: + explicit conv_close_polygon(VertexSource& vs) : m_source(&vs) {} + void attach(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + conv_close_polygon(const conv_close_polygon&); + const conv_close_polygon& + operator = (const conv_close_polygon&); + + VertexSource* m_source; + unsigned m_cmd[2]; + double m_x[2]; + double m_y[2]; + unsigned m_vertex; + bool m_line_to; + }; + + + + //------------------------------------------------------------------------ + template + void conv_close_polygon::rewind(unsigned path_id) + { + m_source->rewind(path_id); + m_vertex = 2; + m_line_to = false; + } + + + + //------------------------------------------------------------------------ + template + unsigned conv_close_polygon::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + for(;;) + { + if(m_vertex < 2) + { + *x = m_x[m_vertex]; + *y = m_y[m_vertex]; + cmd = m_cmd[m_vertex]; + ++m_vertex; + break; + } + + cmd = m_source->vertex(x, y); + + if(is_end_poly(cmd)) + { + cmd |= path_flags_close; + break; + } + + if(is_stop(cmd)) + { + if(m_line_to) + { + m_cmd[0] = path_cmd_end_poly | path_flags_close; + m_cmd[1] = path_cmd_stop; + m_vertex = 0; + m_line_to = false; + continue; + } + break; + } + + if(is_move_to(cmd)) + { + if(m_line_to) + { + m_x[0] = 0.0; + m_y[0] = 0.0; + m_cmd[0] = path_cmd_end_poly | path_flags_close; + m_x[1] = *x; + m_y[1] = *y; + m_cmd[1] = cmd; + m_vertex = 0; + m_line_to = false; + continue; + } + break; + } + + if(is_vertex(cmd)) + { + m_line_to = true; + break; + } + } + return cmd; + } + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_concat.h b/desmume/src/windows/agg/include/agg_conv_concat.h new file mode 100644 index 000000000..72ff0fc68 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_concat.h @@ -0,0 +1,82 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_CONCAT_INCLUDED +#define AGG_CONV_CONCAT_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //=============================================================conv_concat + // Concatenation of two paths. Usually used to combine lines or curves + // with markers such as arrowheads + template class conv_concat + { + public: + conv_concat(VS1& source1, VS2& source2) : + m_source1(&source1), m_source2(&source2), m_status(2) {} + void attach1(VS1& source) { m_source1 = &source; } + void attach2(VS2& source) { m_source2 = &source; } + + + void rewind(unsigned path_id) + { + m_source1->rewind(path_id); + m_source2->rewind(0); + m_status = 0; + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd; + if(m_status == 0) + { + cmd = m_source1->vertex(x, y); + if(!is_stop(cmd)) return cmd; + m_status = 1; + } + if(m_status == 1) + { + cmd = m_source2->vertex(x, y); + if(!is_stop(cmd)) return cmd; + m_status = 2; + } + return path_cmd_stop; + } + + private: + conv_concat(const conv_concat&); + const conv_concat& + operator = (const conv_concat&); + + VS1* m_source1; + VS2* m_source2; + int m_status; + + }; +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_contour.h b/desmume/src/windows/agg/include/agg_conv_contour.h new file mode 100644 index 000000000..ae05b02a1 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_contour.h @@ -0,0 +1,71 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_CONTOUR_INCLUDED +#define AGG_CONV_CONTOUR_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_contour.h" +#include "agg_conv_adaptor_vcgen.h" + +namespace agg +{ + + //-----------------------------------------------------------conv_contour + template + struct conv_contour : public conv_adaptor_vcgen + { + typedef conv_adaptor_vcgen base_type; + + conv_contour(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void line_join(line_join_e lj) { base_type::generator().line_join(lj); } + void inner_join(inner_join_e ij) { base_type::generator().inner_join(ij); } + void width(double w) { base_type::generator().width(w); } + void miter_limit(double ml) { base_type::generator().miter_limit(ml); } + void miter_limit_theta(double t) { base_type::generator().miter_limit_theta(t); } + void inner_miter_limit(double ml) { base_type::generator().inner_miter_limit(ml); } + void approximation_scale(double as) { base_type::generator().approximation_scale(as); } + void auto_detect_orientation(bool v) { base_type::generator().auto_detect_orientation(v); } + + line_join_e line_join() const { return base_type::generator().line_join(); } + inner_join_e inner_join() const { return base_type::generator().inner_join(); } + double width() const { return base_type::generator().width(); } + double miter_limit() const { return base_type::generator().miter_limit(); } + double inner_miter_limit() const { return base_type::generator().inner_miter_limit(); } + double approximation_scale() const { return base_type::generator().approximation_scale(); } + bool auto_detect_orientation() const { return base_type::generator().auto_detect_orientation(); } + + private: + conv_contour(const conv_contour&); + const conv_contour& + operator = (const conv_contour&); + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_curve.h b/desmume/src/windows/agg/include/agg_conv_curve.h new file mode 100644 index 000000000..504bd4709 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_curve.h @@ -0,0 +1,206 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_CURVE_INCLUDED +#define AGG_CONV_CURVE_INCLUDED + +#include "agg_basics.h" +#include "agg_curves.h" + +namespace agg +{ + + + //---------------------------------------------------------------conv_curve + // Curve converter class. Any path storage can have Bezier curves defined + // by their control points. There're two types of curves supported: curve3 + // and curve4. Curve3 is a conic Bezier curve with 2 endpoints and 1 control + // point. Curve4 has 2 control points (4 points in total) and can be used + // to interpolate more complicated curves. Curve4, unlike curve3 can be used + // to approximate arcs, both circular and elliptical. Curves are approximated + // with straight lines and one of the approaches is just to store the whole + // sequence of vertices that approximate our curve. It takes additional + // memory, and at the same time the consecutive vertices can be calculated + // on demand. + // + // Initially, path storages are not suppose to keep all the vertices of the + // curves (although, nothing prevents us from doing so). Instead, path_storage + // keeps only vertices, needed to calculate a curve on demand. Those vertices + // are marked with special commands. So, if the path_storage contains curves + // (which are not real curves yet), and we render this storage directly, + // all we will see is only 2 or 3 straight line segments (for curve3 and + // curve4 respectively). If we need to see real curves drawn we need to + // include this class into the conversion pipeline. + // + // Class conv_curve recognizes commands path_cmd_curve3 and path_cmd_curve4 + // and converts these vertices into a move_to/line_to sequence. + //----------------------------------------------------------------------- + template class conv_curve + { + public: + typedef Curve3 curve3_type; + typedef Curve4 curve4_type; + typedef conv_curve self_type; + + explicit conv_curve(VertexSource& source) : + m_source(&source), m_last_x(0.0), m_last_y(0.0) {} + void attach(VertexSource& source) { m_source = &source; } + + void approximation_method(curve_approximation_method_e v) + { + m_curve3.approximation_method(v); + m_curve4.approximation_method(v); + } + + curve_approximation_method_e approximation_method() const + { + return m_curve4.approximation_method(); + } + + void approximation_scale(double s) + { + m_curve3.approximation_scale(s); + m_curve4.approximation_scale(s); + } + + double approximation_scale() const + { + return m_curve4.approximation_scale(); + } + + void angle_tolerance(double v) + { + m_curve3.angle_tolerance(v); + m_curve4.angle_tolerance(v); + } + + double angle_tolerance() const + { + return m_curve4.angle_tolerance(); + } + + void cusp_limit(double v) + { + m_curve3.cusp_limit(v); + m_curve4.cusp_limit(v); + } + + double cusp_limit() const + { + return m_curve4.cusp_limit(); + } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + conv_curve(const self_type&); + const self_type& operator = (const self_type&); + + VertexSource* m_source; + double m_last_x; + double m_last_y; + curve3_type m_curve3; + curve4_type m_curve4; + }; + + + + //------------------------------------------------------------------------ + template + void conv_curve::rewind(unsigned path_id) + { + m_source->rewind(path_id); + m_last_x = 0.0; + m_last_y = 0.0; + m_curve3.reset(); + m_curve4.reset(); + } + + + //------------------------------------------------------------------------ + template + unsigned conv_curve::vertex(double* x, double* y) + { + if(!is_stop(m_curve3.vertex(x, y))) + { + m_last_x = *x; + m_last_y = *y; + return path_cmd_line_to; + } + + if(!is_stop(m_curve4.vertex(x, y))) + { + m_last_x = *x; + m_last_y = *y; + return path_cmd_line_to; + } + + double ct2_x; + double ct2_y; + double end_x; + double end_y; + + unsigned cmd = m_source->vertex(x, y); + switch(cmd) + { + case path_cmd_curve3: + m_source->vertex(&end_x, &end_y); + + m_curve3.init(m_last_x, m_last_y, + *x, *y, + end_x, end_y); + + m_curve3.vertex(x, y); // First call returns path_cmd_move_to + m_curve3.vertex(x, y); // This is the first vertex of the curve + cmd = path_cmd_line_to; + break; + + case path_cmd_curve4: + m_source->vertex(&ct2_x, &ct2_y); + m_source->vertex(&end_x, &end_y); + + m_curve4.init(m_last_x, m_last_y, + *x, *y, + ct2_x, ct2_y, + end_x, end_y); + + m_curve4.vertex(x, y); // First call returns path_cmd_move_to + m_curve4.vertex(x, y); // This is the first vertex of the curve + cmd = path_cmd_line_to; + break; + } + m_last_x = *x; + m_last_y = *y; + return cmd; + } + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_dash.h b/desmume/src/windows/agg/include/agg_conv_dash.h new file mode 100644 index 000000000..c3f1613b1 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_dash.h @@ -0,0 +1,74 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_DASH_INCLUDED +#define AGG_CONV_DASH_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_dash.h" +#include "agg_conv_adaptor_vcgen.h" + +namespace agg +{ + + //---------------------------------------------------------------conv_dash + template + struct conv_dash : public conv_adaptor_vcgen + { + typedef Markers marker_type; + typedef conv_adaptor_vcgen base_type; + + conv_dash(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void remove_all_dashes() + { + base_type::generator().remove_all_dashes(); + } + + void add_dash(double dash_len, double gap_len) + { + base_type::generator().add_dash(dash_len, gap_len); + } + + void dash_start(double ds) + { + base_type::generator().dash_start(ds); + } + + void shorten(double s) { base_type::generator().shorten(s); } + double shorten() const { return base_type::generator().shorten(); } + + private: + conv_dash(const conv_dash&); + const conv_dash& + operator = (const conv_dash&); + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_gpc.h b/desmume/src/windows/agg/include/agg_conv_gpc.h new file mode 100644 index 000000000..c837c08cc --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_gpc.h @@ -0,0 +1,441 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// General Polygon Clipper based on the GPC library by Alan Murta +// Union, Intersection, XOR, A-B, B-A +// Contact the author if you intend to use it in commercial applications! +// http://www.cs.man.ac.uk/aig/staff/alan/software/ +// Alan Murta (email: gpc@cs.man.ac.uk) +// +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_GPC_INCLUDED +#define AGG_CONV_GPC_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_array.h" + +extern "C" +{ +#include "gpc.h" +} + +namespace agg +{ + enum gpc_op_e + { + gpc_or, + gpc_and, + gpc_xor, + gpc_a_minus_b, + gpc_b_minus_a + }; + + + //================================================================conv_gpc + template class conv_gpc + { + enum status + { + status_move_to, + status_line_to, + status_stop + }; + + struct contour_header_type + { + int num_vertices; + int hole_flag; + gpc_vertex* vertices; + }; + + typedef pod_bvector vertex_array_type; + typedef pod_bvector contour_header_array_type; + + + public: + typedef VSA source_a_type; + typedef VSB source_b_type; + typedef conv_gpc self_type; + + ~conv_gpc() + { + free_gpc_data(); + } + + conv_gpc(source_a_type& a, source_b_type& b, gpc_op_e op = gpc_or) : + m_src_a(&a), + m_src_b(&b), + m_status(status_move_to), + m_vertex(-1), + m_contour(-1), + m_operation(op) + { + memset(&m_poly_a, 0, sizeof(m_poly_a)); + memset(&m_poly_b, 0, sizeof(m_poly_b)); + memset(&m_result, 0, sizeof(m_result)); + } + + void attach1(VSA& source) { m_src_a = &source; } + void attach2(VSB& source) { m_src_b = &source; } + + void operation(gpc_op_e v) { m_operation = v; } + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + conv_gpc(const conv_gpc&); + const conv_gpc& operator = (const conv_gpc&); + + //-------------------------------------------------------------------- + void free_polygon(gpc_polygon& p); + void free_result(); + void free_gpc_data(); + void start_contour(); + void add_vertex(double x, double y); + void end_contour(unsigned orientation); + void make_polygon(gpc_polygon& p); + void start_extracting(); + bool next_contour(); + bool next_vertex(double* x, double* y); + + + //-------------------------------------------------------------------- + template void add(VS& src, gpc_polygon& p) + { + unsigned cmd; + double x, y; + double start_x = 0.0; + double start_y = 0.0; + bool line_to = false; + unsigned orientation = 0; + + m_contour_accumulator.remove_all(); + + while(!is_stop(cmd = src.vertex(&x, &y))) + { + if(is_vertex(cmd)) + { + if(is_move_to(cmd)) + { + if(line_to) + { + end_contour(orientation); + orientation = 0; + } + start_contour(); + start_x = x; + start_y = y; + } + add_vertex(x, y); + line_to = true; + } + else + { + if(is_end_poly(cmd)) + { + orientation = get_orientation(cmd); + if(line_to && is_closed(cmd)) + { + add_vertex(start_x, start_y); + } + } + } + } + if(line_to) + { + end_contour(orientation); + } + make_polygon(p); + } + + + private: + //-------------------------------------------------------------------- + source_a_type* m_src_a; + source_b_type* m_src_b; + status m_status; + int m_vertex; + int m_contour; + gpc_op_e m_operation; + vertex_array_type m_vertex_accumulator; + contour_header_array_type m_contour_accumulator; + gpc_polygon m_poly_a; + gpc_polygon m_poly_b; + gpc_polygon m_result; + }; + + + + + + //------------------------------------------------------------------------ + template + void conv_gpc::free_polygon(gpc_polygon& p) + { + int i; + for(i = 0; i < p.num_contours; i++) + { + pod_allocator::deallocate(p.contour[i].vertex, + p.contour[i].num_vertices); + } + pod_allocator::deallocate(p.contour, p.num_contours); + memset(&p, 0, sizeof(gpc_polygon)); + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::free_result() + { + if(m_result.contour) + { + gpc_free_polygon(&m_result); + } + memset(&m_result, 0, sizeof(m_result)); + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::free_gpc_data() + { + free_polygon(m_poly_a); + free_polygon(m_poly_b); + free_result(); + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::start_contour() + { + contour_header_type h; + memset(&h, 0, sizeof(h)); + m_contour_accumulator.add(h); + m_vertex_accumulator.remove_all(); + } + + + //------------------------------------------------------------------------ + template + inline void conv_gpc::add_vertex(double x, double y) + { + gpc_vertex v; + v.x = x; + v.y = y; + m_vertex_accumulator.add(v); + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::end_contour(unsigned orientation) + { + if(m_contour_accumulator.size()) + { + if(m_vertex_accumulator.size() > 2) + { + contour_header_type& h = + m_contour_accumulator[m_contour_accumulator.size() - 1]; + + h.num_vertices = m_vertex_accumulator.size(); + h.hole_flag = 0; + + // TO DO: Clarify the "holes" + //if(is_cw(orientation)) h.hole_flag = 1; + + h.vertices = pod_allocator::allocate(h.num_vertices); + gpc_vertex* d = h.vertices; + int i; + for(i = 0; i < h.num_vertices; i++) + { + const gpc_vertex& s = m_vertex_accumulator[i]; + d->x = s.x; + d->y = s.y; + ++d; + } + } + else + { + m_vertex_accumulator.remove_last(); + } + } + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::make_polygon(gpc_polygon& p) + { + free_polygon(p); + if(m_contour_accumulator.size()) + { + p.num_contours = m_contour_accumulator.size(); + + p.hole = 0; + p.contour = pod_allocator::allocate(p.num_contours); + + int i; + gpc_vertex_list* pv = p.contour; + for(i = 0; i < p.num_contours; i++) + { + const contour_header_type& h = m_contour_accumulator[i]; + pv->num_vertices = h.num_vertices; + pv->vertex = h.vertices; + ++pv; + } + } + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::start_extracting() + { + m_status = status_move_to; + m_contour = -1; + m_vertex = -1; + } + + + //------------------------------------------------------------------------ + template + bool conv_gpc::next_contour() + { + if(++m_contour < m_result.num_contours) + { + m_vertex = -1; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + template + inline bool conv_gpc::next_vertex(double* x, double* y) + { + const gpc_vertex_list& vlist = m_result.contour[m_contour]; + if(++m_vertex < vlist.num_vertices) + { + const gpc_vertex& v = vlist.vertex[m_vertex]; + *x = v.x; + *y = v.y; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + template + void conv_gpc::rewind(unsigned path_id) + { + free_result(); + m_src_a->rewind(path_id); + m_src_b->rewind(path_id); + add(*m_src_a, m_poly_a); + add(*m_src_b, m_poly_b); + switch(m_operation) + { + case gpc_or: + gpc_polygon_clip(GPC_UNION, + &m_poly_a, + &m_poly_b, + &m_result); + break; + + case gpc_and: + gpc_polygon_clip(GPC_INT, + &m_poly_a, + &m_poly_b, + &m_result); + break; + + case gpc_xor: + gpc_polygon_clip(GPC_XOR, + &m_poly_a, + &m_poly_b, + &m_result); + break; + + case gpc_a_minus_b: + gpc_polygon_clip(GPC_DIFF, + &m_poly_a, + &m_poly_b, + &m_result); + break; + + case gpc_b_minus_a: + gpc_polygon_clip(GPC_DIFF, + &m_poly_b, + &m_poly_a, + &m_result); + break; + } + start_extracting(); + } + + + //------------------------------------------------------------------------ + template + unsigned conv_gpc::vertex(double* x, double* y) + { + if(m_status == status_move_to) + { + if(next_contour()) + { + if(next_vertex(x, y)) + { + m_status = status_line_to; + return path_cmd_move_to; + } + m_status = status_stop; + return path_cmd_end_poly | path_flags_close; + } + } + else + { + if(next_vertex(x, y)) + { + return path_cmd_line_to; + } + else + { + m_status = status_move_to; + } + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_marker.h b/desmume/src/windows/agg/include/agg_conv_marker.h new file mode 100644 index 000000000..69f8f7bde --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_marker.h @@ -0,0 +1,154 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_MARKER_INCLUDED +#define AGG_CONV_MARKER_INCLUDED + +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace agg +{ + //-------------------------------------------------------------conv_marker + template + class conv_marker + { + public: + conv_marker(MarkerLocator& ml, MarkerShapes& ms); + + trans_affine& transform() { return m_transform; } + const trans_affine& transform() const { return m_transform; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + conv_marker(const conv_marker&); + const conv_marker& + operator = (const conv_marker&); + + enum status_e + { + initial, + markers, + polygon, + stop + }; + + MarkerLocator* m_marker_locator; + MarkerShapes* m_marker_shapes; + trans_affine m_transform; + trans_affine m_mtx; + status_e m_status; + unsigned m_marker; + unsigned m_num_markers; + }; + + + //------------------------------------------------------------------------ + template + conv_marker::conv_marker(MarkerLocator& ml, MarkerShapes& ms) : + m_marker_locator(&ml), + m_marker_shapes(&ms), + m_status(initial), + m_marker(0), + m_num_markers(1) + { + } + + + //------------------------------------------------------------------------ + template + void conv_marker::rewind(unsigned) + { + m_status = initial; + m_marker = 0; + m_num_markers = 1; + } + + + //------------------------------------------------------------------------ + template + unsigned conv_marker::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_move_to; + double x1, y1, x2, y2; + + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + if(m_num_markers == 0) + { + cmd = path_cmd_stop; + break; + } + m_marker_locator->rewind(m_marker); + ++m_marker; + m_num_markers = 0; + m_status = markers; + + case markers: + if(is_stop(m_marker_locator->vertex(&x1, &y1))) + { + m_status = initial; + break; + } + if(is_stop(m_marker_locator->vertex(&x2, &y2))) + { + m_status = initial; + break; + } + ++m_num_markers; + m_mtx = m_transform; + m_mtx *= trans_affine_rotation(atan2(y2 - y1, x2 - x1)); + m_mtx *= trans_affine_translation(x1, y1); + m_marker_shapes->rewind(m_marker - 1); + m_status = polygon; + + case polygon: + cmd = m_marker_shapes->vertex(x, y); + if(is_stop(cmd)) + { + cmd = path_cmd_move_to; + m_status = markers; + break; + } + m_mtx.transform(x, y); + return cmd; + + case stop: + cmd = path_cmd_stop; + break; + } + } + return cmd; + } + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_conv_marker_adaptor.h b/desmume/src/windows/agg/include/agg_conv_marker_adaptor.h new file mode 100644 index 000000000..cef4ae99b --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_marker_adaptor.h @@ -0,0 +1,60 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_MARKER_ADAPTOR_INCLUDED +#define AGG_CONV_MARKER_ADAPTOR_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_adaptor_vcgen.h" +#include "agg_vcgen_vertex_sequence.h" + +namespace agg +{ + + //=====================================================conv_marker_adaptor + template + struct conv_marker_adaptor : + public conv_adaptor_vcgen + { + typedef Markers marker_type; + typedef conv_adaptor_vcgen base_type; + + conv_marker_adaptor(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void shorten(double s) { base_type::generator().shorten(s); } + double shorten() const { return base_type::generator().shorten(); } + + private: + conv_marker_adaptor(const conv_marker_adaptor&); + const conv_marker_adaptor& + operator = (const conv_marker_adaptor&); + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_segmentator.h b/desmume/src/windows/agg/include/agg_conv_segmentator.h new file mode 100644 index 000000000..a81c7f152 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_segmentator.h @@ -0,0 +1,57 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_SEGMENTATOR_INCLUDED +#define AGG_CONV_SEGMENTATOR_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_adaptor_vpgen.h" +#include "agg_vpgen_segmentator.h" + +namespace agg +{ + + //========================================================conv_segmentator + template + struct conv_segmentator : public conv_adaptor_vpgen + { + typedef conv_adaptor_vpgen base_type; + + conv_segmentator(VertexSource& vs) : + conv_adaptor_vpgen(vs) {} + + void approximation_scale(double s) { base_type::vpgen().approximation_scale(s); } + double approximation_scale() const { return base_type::vpgen().approximation_scale(); } + + private: + conv_segmentator(const conv_segmentator&); + const conv_segmentator& + operator = (const conv_segmentator&); + }; + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_conv_shorten_path.h b/desmume/src/windows/agg/include/agg_conv_shorten_path.h new file mode 100644 index 000000000..c8c7bcfa6 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_shorten_path.h @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_SHORTEN_PATH_INCLUDED +#define AGG_CONV_SHORTEN_PATH_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_adaptor_vcgen.h" +#include "agg_vcgen_vertex_sequence.h" + +namespace agg +{ + + //=======================================================conv_shorten_path + template class conv_shorten_path : + public conv_adaptor_vcgen + { + public: + typedef conv_adaptor_vcgen base_type; + + conv_shorten_path(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void shorten(double s) { base_type::generator().shorten(s); } + double shorten() const { return base_type::generator().shorten(); } + + private: + conv_shorten_path(const conv_shorten_path&); + const conv_shorten_path& + operator = (const conv_shorten_path&); + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_smooth_poly1.h b/desmume/src/windows/agg/include/agg_conv_smooth_poly1.h new file mode 100644 index 000000000..73d0e1066 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_smooth_poly1.h @@ -0,0 +1,86 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_SMOOTH_POLY1_INCLUDED +#define AGG_CONV_SMOOTH_POLY1_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_smooth_poly1.h" +#include "agg_conv_adaptor_vcgen.h" +#include "agg_conv_curve.h" + + +namespace agg +{ + + //-------------------------------------------------------conv_smooth_poly1 + template + struct conv_smooth_poly1 : + public conv_adaptor_vcgen + { + typedef conv_adaptor_vcgen base_type; + + conv_smooth_poly1(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void smooth_value(double v) { base_type::generator().smooth_value(v); } + double smooth_value() const { return base_type::generator().smooth_value(); } + + private: + conv_smooth_poly1(const conv_smooth_poly1&); + const conv_smooth_poly1& + operator = (const conv_smooth_poly1&); + }; + + + + //-------------------------------------------------conv_smooth_poly1_curve + template + struct conv_smooth_poly1_curve : + public conv_curve > + { + conv_smooth_poly1_curve(VertexSource& vs) : + conv_curve >(m_smooth), + m_smooth(vs) + { + } + + void smooth_value(double v) { m_smooth.generator().smooth_value(v); } + double smooth_value() const { return m_smooth.generator().smooth_value(); } + + private: + conv_smooth_poly1_curve(const conv_smooth_poly1_curve&); + const conv_smooth_poly1_curve& + operator = (const conv_smooth_poly1_curve&); + + conv_smooth_poly1 m_smooth; + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_conv_stroke.h b/desmume/src/windows/agg/include/agg_conv_stroke.h new file mode 100644 index 000000000..73b7801fe --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_stroke.h @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_STROKE_INCLUDED +#define AGG_CONV_STROKE_INCLUDED + +#include "agg_basics.h" +#include "agg_vcgen_stroke.h" +#include "agg_conv_adaptor_vcgen.h" + +namespace agg +{ + + //-------------------------------------------------------------conv_stroke + template + struct conv_stroke : + public conv_adaptor_vcgen + { + typedef Markers marker_type; + typedef conv_adaptor_vcgen base_type; + + conv_stroke(VertexSource& vs) : + conv_adaptor_vcgen(vs) + { + } + + void line_cap(line_cap_e lc) { base_type::generator().line_cap(lc); } + void line_join(line_join_e lj) { base_type::generator().line_join(lj); } + void inner_join(inner_join_e ij) { base_type::generator().inner_join(ij); } + + line_cap_e line_cap() const { return base_type::generator().line_cap(); } + line_join_e line_join() const { return base_type::generator().line_join(); } + inner_join_e inner_join() const { return base_type::generator().inner_join(); } + + void width(double w) { base_type::generator().width(w); } + void miter_limit(double ml) { base_type::generator().miter_limit(ml); } + void miter_limit_theta(double t) { base_type::generator().miter_limit_theta(t); } + void inner_miter_limit(double ml) { base_type::generator().inner_miter_limit(ml); } + void approximation_scale(double as) { base_type::generator().approximation_scale(as); } + + double width() const { return base_type::generator().width(); } + double miter_limit() const { return base_type::generator().miter_limit(); } + double inner_miter_limit() const { return base_type::generator().inner_miter_limit(); } + double approximation_scale() const { return base_type::generator().approximation_scale(); } + + void shorten(double s) { base_type::generator().shorten(s); } + double shorten() const { return base_type::generator().shorten(); } + + private: + conv_stroke(const conv_stroke&); + const conv_stroke& + operator = (const conv_stroke&); + + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_transform.h b/desmume/src/windows/agg/include/agg_conv_transform.h new file mode 100644 index 000000000..3ed2a9841 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_transform.h @@ -0,0 +1,74 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_TRANSFORM_INCLUDED +#define AGG_CONV_TRANSFORM_INCLUDED + +#include "agg_basics.h" +#include "agg_trans_affine.h" + +namespace agg +{ + + //----------------------------------------------------------conv_transform + template class conv_transform + { + public: + conv_transform(VertexSource& source, const Transformer& tr) : + m_source(&source), m_trans(&tr) {} + void attach(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd = m_source->vertex(x, y); + if(is_vertex(cmd)) + { + m_trans->transform(x, y); + } + return cmd; + } + + void transformer(const Transformer& tr) + { + m_trans = &tr; + } + + private: + conv_transform(const conv_transform&); + const conv_transform& + operator = (const conv_transform&); + + VertexSource* m_source; + const Transformer* m_trans; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_conv_unclose_polygon.h b/desmume/src/windows/agg/include/agg_conv_unclose_polygon.h new file mode 100644 index 000000000..76070e92c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_conv_unclose_polygon.h @@ -0,0 +1,61 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CONV_UNCLOSE_POLYGON_INCLUDED +#define AGG_CONV_UNCLOSE_POLYGON_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //====================================================conv_unclose_polygon + template class conv_unclose_polygon + { + public: + explicit conv_unclose_polygon(VertexSource& vs) : m_source(&vs) {} + void attach(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd = m_source->vertex(x, y); + if(is_end_poly(cmd)) cmd &= ~path_flags_close; + return cmd; + } + + private: + conv_unclose_polygon(const conv_unclose_polygon&); + const conv_unclose_polygon& + operator = (const conv_unclose_polygon&); + + VertexSource* m_source; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_curves.h b/desmume/src/windows/agg/include/agg_curves.h new file mode 100644 index 000000000..ca2f5bc65 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_curves.h @@ -0,0 +1,701 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CURVES_INCLUDED +#define AGG_CURVES_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + // See Implementation agg_curves.cpp + + //--------------------------------------------curve_approximation_method_e + enum curve_approximation_method_e + { + curve_inc, + curve_div + }; + + //--------------------------------------------------------------curve3_inc + class curve3_inc + { + public: + curve3_inc() : + m_num_steps(0), m_step(0), m_scale(1.0) { } + + curve3_inc(double x1, double y1, + double x2, double y2, + double x3, double y3) : + m_num_steps(0), m_step(0), m_scale(1.0) + { + init(x1, y1, x2, y2, x3, y3); + } + + void reset() { m_num_steps = 0; m_step = -1; } + void init(double x1, double y1, + double x2, double y2, + double x3, double y3); + + void approximation_method(curve_approximation_method_e) {} + curve_approximation_method_e approximation_method() const { return curve_inc; } + + void approximation_scale(double s); + double approximation_scale() const; + + void angle_tolerance(double) {} + double angle_tolerance() const { return 0.0; } + + void cusp_limit(double) {} + double cusp_limit() const { return 0.0; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + int m_num_steps; + int m_step; + double m_scale; + double m_start_x; + double m_start_y; + double m_end_x; + double m_end_y; + double m_fx; + double m_fy; + double m_dfx; + double m_dfy; + double m_ddfx; + double m_ddfy; + double m_saved_fx; + double m_saved_fy; + double m_saved_dfx; + double m_saved_dfy; + }; + + + + + + //-------------------------------------------------------------curve3_div + class curve3_div + { + public: + curve3_div() : + m_approximation_scale(1.0), + m_angle_tolerance(0.0), + m_count(0) + {} + + curve3_div(double x1, double y1, + double x2, double y2, + double x3, double y3) : + m_approximation_scale(1.0), + m_angle_tolerance(0.0), + m_count(0) + { + init(x1, y1, x2, y2, x3, y3); + } + + void reset() { m_points.remove_all(); m_count = 0; } + void init(double x1, double y1, + double x2, double y2, + double x3, double y3); + + void approximation_method(curve_approximation_method_e) {} + curve_approximation_method_e approximation_method() const { return curve_div; } + + void approximation_scale(double s) { m_approximation_scale = s; } + double approximation_scale() const { return m_approximation_scale; } + + void angle_tolerance(double a) { m_angle_tolerance = a; } + double angle_tolerance() const { return m_angle_tolerance; } + + void cusp_limit(double) {} + double cusp_limit() const { return 0.0; } + + void rewind(unsigned) + { + m_count = 0; + } + + unsigned vertex(double* x, double* y) + { + if(m_count >= m_points.size()) return path_cmd_stop; + const point_d& p = m_points[m_count++]; + *x = p.x; + *y = p.y; + return (m_count == 1) ? path_cmd_move_to : path_cmd_line_to; + } + + private: + void bezier(double x1, double y1, + double x2, double y2, + double x3, double y3); + void recursive_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + unsigned level); + + double m_approximation_scale; + double m_distance_tolerance_square; + double m_angle_tolerance; + unsigned m_count; + pod_bvector m_points; + }; + + + + + + + + //-------------------------------------------------------------curve4_points + struct curve4_points + { + double cp[8]; + curve4_points() {} + curve4_points(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + cp[0] = x1; cp[1] = y1; cp[2] = x2; cp[3] = y2; + cp[4] = x3; cp[5] = y3; cp[6] = x4; cp[7] = y4; + } + void init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + cp[0] = x1; cp[1] = y1; cp[2] = x2; cp[3] = y2; + cp[4] = x3; cp[5] = y3; cp[6] = x4; cp[7] = y4; + } + double operator [] (unsigned i) const { return cp[i]; } + double& operator [] (unsigned i) { return cp[i]; } + }; + + + + //-------------------------------------------------------------curve4_inc + class curve4_inc + { + public: + curve4_inc() : + m_num_steps(0), m_step(0), m_scale(1.0) { } + + curve4_inc(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) : + m_num_steps(0), m_step(0), m_scale(1.0) + { + init(x1, y1, x2, y2, x3, y3, x4, y4); + } + + curve4_inc(const curve4_points& cp) : + m_num_steps(0), m_step(0), m_scale(1.0) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void reset() { m_num_steps = 0; m_step = -1; } + void init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4); + + void init(const curve4_points& cp) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void approximation_method(curve_approximation_method_e) {} + curve_approximation_method_e approximation_method() const { return curve_inc; } + + void approximation_scale(double s); + double approximation_scale() const; + + void angle_tolerance(double) {} + double angle_tolerance() const { return 0.0; } + + void cusp_limit(double) {} + double cusp_limit() const { return 0.0; } + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + int m_num_steps; + int m_step; + double m_scale; + double m_start_x; + double m_start_y; + double m_end_x; + double m_end_y; + double m_fx; + double m_fy; + double m_dfx; + double m_dfy; + double m_ddfx; + double m_ddfy; + double m_dddfx; + double m_dddfy; + double m_saved_fx; + double m_saved_fy; + double m_saved_dfx; + double m_saved_dfy; + double m_saved_ddfx; + double m_saved_ddfy; + }; + + + + //-------------------------------------------------------catrom_to_bezier + inline curve4_points catrom_to_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + // Trans. matrix Catmull-Rom to Bezier + // + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 + // + return curve4_points( + x2, + y2, + (-x1 + 6*x2 + x3) / 6, + (-y1 + 6*y2 + y3) / 6, + ( x2 + 6*x3 - x4) / 6, + ( y2 + 6*y3 - y4) / 6, + x3, + y3); + } + + + //----------------------------------------------------------------------- + inline curve4_points + catrom_to_bezier(const curve4_points& cp) + { + return catrom_to_bezier(cp[0], cp[1], cp[2], cp[3], + cp[4], cp[5], cp[6], cp[7]); + } + + + + //-----------------------------------------------------ubspline_to_bezier + inline curve4_points ubspline_to_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + // Trans. matrix Uniform BSpline to Bezier + // + // 1/6 4/6 1/6 0 + // 0 4/6 2/6 0 + // 0 2/6 4/6 0 + // 0 1/6 4/6 1/6 + // + return curve4_points( + (x1 + 4*x2 + x3) / 6, + (y1 + 4*y2 + y3) / 6, + (4*x2 + 2*x3) / 6, + (4*y2 + 2*y3) / 6, + (2*x2 + 4*x3) / 6, + (2*y2 + 4*y3) / 6, + (x2 + 4*x3 + x4) / 6, + (y2 + 4*y3 + y4) / 6); + } + + + //----------------------------------------------------------------------- + inline curve4_points + ubspline_to_bezier(const curve4_points& cp) + { + return ubspline_to_bezier(cp[0], cp[1], cp[2], cp[3], + cp[4], cp[5], cp[6], cp[7]); + } + + + + + //------------------------------------------------------hermite_to_bezier + inline curve4_points hermite_to_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + // Trans. matrix Hermite to Bezier + // + // 1 0 0 0 + // 1 0 1/3 0 + // 0 1 0 -1/3 + // 0 1 0 0 + // + return curve4_points( + x1, + y1, + (3*x1 + x3) / 3, + (3*y1 + y3) / 3, + (3*x2 - x4) / 3, + (3*y2 - y4) / 3, + x2, + y2); + } + + + + //----------------------------------------------------------------------- + inline curve4_points + hermite_to_bezier(const curve4_points& cp) + { + return hermite_to_bezier(cp[0], cp[1], cp[2], cp[3], + cp[4], cp[5], cp[6], cp[7]); + } + + + //-------------------------------------------------------------curve4_div + class curve4_div + { + public: + curve4_div() : + m_approximation_scale(1.0), + m_angle_tolerance(0.0), + m_cusp_limit(0.0), + m_count(0) + {} + + curve4_div(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) : + m_approximation_scale(1.0), + m_angle_tolerance(0.0), + m_cusp_limit(0.0), + m_count(0) + { + init(x1, y1, x2, y2, x3, y3, x4, y4); + } + + curve4_div(const curve4_points& cp) : + m_approximation_scale(1.0), + m_angle_tolerance(0.0), + m_count(0) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void reset() { m_points.remove_all(); m_count = 0; } + void init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4); + + void init(const curve4_points& cp) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void approximation_method(curve_approximation_method_e) {} + + curve_approximation_method_e approximation_method() const + { + return curve_div; + } + + void approximation_scale(double s) { m_approximation_scale = s; } + double approximation_scale() const { return m_approximation_scale; } + + void angle_tolerance(double a) { m_angle_tolerance = a; } + double angle_tolerance() const { return m_angle_tolerance; } + + void cusp_limit(double v) + { + m_cusp_limit = (v == 0.0) ? 0.0 : pi - v; + } + + double cusp_limit() const + { + return (m_cusp_limit == 0.0) ? 0.0 : pi - m_cusp_limit; + } + + void rewind(unsigned) + { + m_count = 0; + } + + unsigned vertex(double* x, double* y) + { + if(m_count >= m_points.size()) return path_cmd_stop; + const point_d& p = m_points[m_count++]; + *x = p.x; + *y = p.y; + return (m_count == 1) ? path_cmd_move_to : path_cmd_line_to; + } + + private: + void bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4); + + void recursive_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4, + unsigned level); + + double m_approximation_scale; + double m_distance_tolerance_square; + double m_angle_tolerance; + double m_cusp_limit; + unsigned m_count; + pod_bvector m_points; + }; + + + //-----------------------------------------------------------------curve3 + class curve3 + { + public: + curve3() : m_approximation_method(curve_div) {} + curve3(double x1, double y1, + double x2, double y2, + double x3, double y3) : + m_approximation_method(curve_div) + { + init(x1, y1, x2, y2, x3, y3); + } + + void reset() + { + m_curve_inc.reset(); + m_curve_div.reset(); + } + + void init(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + if(m_approximation_method == curve_inc) + { + m_curve_inc.init(x1, y1, x2, y2, x3, y3); + } + else + { + m_curve_div.init(x1, y1, x2, y2, x3, y3); + } + } + + void approximation_method(curve_approximation_method_e v) + { + m_approximation_method = v; + } + + curve_approximation_method_e approximation_method() const + { + return m_approximation_method; + } + + void approximation_scale(double s) + { + m_curve_inc.approximation_scale(s); + m_curve_div.approximation_scale(s); + } + + double approximation_scale() const + { + return m_curve_inc.approximation_scale(); + } + + void angle_tolerance(double a) + { + m_curve_div.angle_tolerance(a); + } + + double angle_tolerance() const + { + return m_curve_div.angle_tolerance(); + } + + void cusp_limit(double v) + { + m_curve_div.cusp_limit(v); + } + + double cusp_limit() const + { + return m_curve_div.cusp_limit(); + } + + void rewind(unsigned path_id) + { + if(m_approximation_method == curve_inc) + { + m_curve_inc.rewind(path_id); + } + else + { + m_curve_div.rewind(path_id); + } + } + + unsigned vertex(double* x, double* y) + { + if(m_approximation_method == curve_inc) + { + return m_curve_inc.vertex(x, y); + } + return m_curve_div.vertex(x, y); + } + + private: + curve3_inc m_curve_inc; + curve3_div m_curve_div; + curve_approximation_method_e m_approximation_method; + }; + + + + + + //-----------------------------------------------------------------curve4 + class curve4 + { + public: + curve4() : m_approximation_method(curve_div) {} + curve4(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) : + m_approximation_method(curve_div) + { + init(x1, y1, x2, y2, x3, y3, x4, y4); + } + + curve4(const curve4_points& cp) : + m_approximation_method(curve_div) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void reset() + { + m_curve_inc.reset(); + m_curve_div.reset(); + } + + void init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + if(m_approximation_method == curve_inc) + { + m_curve_inc.init(x1, y1, x2, y2, x3, y3, x4, y4); + } + else + { + m_curve_div.init(x1, y1, x2, y2, x3, y3, x4, y4); + } + } + + void init(const curve4_points& cp) + { + init(cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + } + + void approximation_method(curve_approximation_method_e v) + { + m_approximation_method = v; + } + + curve_approximation_method_e approximation_method() const + { + return m_approximation_method; + } + + void approximation_scale(double s) + { + m_curve_inc.approximation_scale(s); + m_curve_div.approximation_scale(s); + } + double approximation_scale() const { return m_curve_inc.approximation_scale(); } + + void angle_tolerance(double v) + { + m_curve_div.angle_tolerance(v); + } + + double angle_tolerance() const + { + return m_curve_div.angle_tolerance(); + } + + void cusp_limit(double v) + { + m_curve_div.cusp_limit(v); + } + + double cusp_limit() const + { + return m_curve_div.cusp_limit(); + } + + void rewind(unsigned path_id) + { + if(m_approximation_method == curve_inc) + { + m_curve_inc.rewind(path_id); + } + else + { + m_curve_div.rewind(path_id); + } + } + + unsigned vertex(double* x, double* y) + { + if(m_approximation_method == curve_inc) + { + return m_curve_inc.vertex(x, y); + } + return m_curve_div.vertex(x, y); + } + + private: + curve4_inc m_curve_inc; + curve4_div m_curve_div; + curve_approximation_method_e m_approximation_method; + }; + + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_dda_line.h b/desmume/src/windows/agg/include/agg_dda_line.h new file mode 100644 index 000000000..92bb0462a --- /dev/null +++ b/desmume/src/windows/agg/include/agg_dda_line.h @@ -0,0 +1,295 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_DDA_LINE_INCLUDED +#define AGG_DDA_LINE_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //===================================================dda_line_interpolator + template class dda_line_interpolator + { + public: + //-------------------------------------------------------------------- + dda_line_interpolator() {} + + //-------------------------------------------------------------------- + dda_line_interpolator(int y1, int y2, unsigned count) : + m_y(y1), + m_inc(((y2 - y1) << FractionShift) / int(count)), + m_dy(0) + { + } + + //-------------------------------------------------------------------- + void operator ++ () + { + m_dy += m_inc; + } + + //-------------------------------------------------------------------- + void operator -- () + { + m_dy -= m_inc; + } + + //-------------------------------------------------------------------- + void operator += (unsigned n) + { + m_dy += m_inc * n; + } + + //-------------------------------------------------------------------- + void operator -= (unsigned n) + { + m_dy -= m_inc * n; + } + + + //-------------------------------------------------------------------- + int y() const { return m_y + (m_dy >> (FractionShift-YShift)); } + int dy() const { return m_dy; } + + + private: + int m_y; + int m_inc; + int m_dy; + }; + + + + + + //=================================================dda2_line_interpolator + class dda2_line_interpolator + { + public: + typedef int save_data_type; + enum save_size_e { save_size = 2 }; + + //-------------------------------------------------------------------- + dda2_line_interpolator() {} + + //-------------------------------------------- Forward-adjusted line + dda2_line_interpolator(int y1, int y2, int count) : + m_cnt(count <= 0 ? 1 : count), + m_lft((y2 - y1) / m_cnt), + m_rem((y2 - y1) % m_cnt), + m_mod(m_rem), + m_y(y1) + { + if(m_mod <= 0) + { + m_mod += count; + m_rem += count; + m_lft--; + } + m_mod -= count; + } + + //-------------------------------------------- Backward-adjusted line + dda2_line_interpolator(int y1, int y2, int count, int) : + m_cnt(count <= 0 ? 1 : count), + m_lft((y2 - y1) / m_cnt), + m_rem((y2 - y1) % m_cnt), + m_mod(m_rem), + m_y(y1) + { + if(m_mod <= 0) + { + m_mod += count; + m_rem += count; + m_lft--; + } + } + + //-------------------------------------------- Backward-adjusted line + dda2_line_interpolator(int y, int count) : + m_cnt(count <= 0 ? 1 : count), + m_lft(y / m_cnt), + m_rem(y % m_cnt), + m_mod(m_rem), + m_y(0) + { + if(m_mod <= 0) + { + m_mod += count; + m_rem += count; + m_lft--; + } + } + + + //-------------------------------------------------------------------- + void save(save_data_type* data) const + { + data[0] = m_mod; + data[1] = m_y; + } + + //-------------------------------------------------------------------- + void load(const save_data_type* data) + { + m_mod = data[0]; + m_y = data[1]; + } + + //-------------------------------------------------------------------- + void operator++() + { + m_mod += m_rem; + m_y += m_lft; + if(m_mod > 0) + { + m_mod -= m_cnt; + m_y++; + } + } + + //-------------------------------------------------------------------- + void operator--() + { + if(m_mod <= m_rem) + { + m_mod += m_cnt; + m_y--; + } + m_mod -= m_rem; + m_y -= m_lft; + } + + //-------------------------------------------------------------------- + void adjust_forward() + { + m_mod -= m_cnt; + } + + //-------------------------------------------------------------------- + void adjust_backward() + { + m_mod += m_cnt; + } + + //-------------------------------------------------------------------- + int mod() const { return m_mod; } + int rem() const { return m_rem; } + int lft() const { return m_lft; } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + + private: + int m_cnt; + int m_lft; + int m_rem; + int m_mod; + int m_y; + }; + + + + + + + + //---------------------------------------------line_bresenham_interpolator + class line_bresenham_interpolator + { + public: + enum subpixel_scale_e + { + subpixel_shift = 8, + subpixel_scale = 1 << subpixel_shift, + subpixel_mask = subpixel_scale - 1 + }; + + //-------------------------------------------------------------------- + static int line_lr(int v) { return v >> subpixel_shift; } + + //-------------------------------------------------------------------- + line_bresenham_interpolator(int x1, int y1, int x2, int y2) : + m_x1_lr(line_lr(x1)), + m_y1_lr(line_lr(y1)), + m_x2_lr(line_lr(x2)), + m_y2_lr(line_lr(y2)), + m_ver(abs(m_x2_lr - m_x1_lr) < abs(m_y2_lr - m_y1_lr)), + m_len(m_ver ? abs(m_y2_lr - m_y1_lr) : + abs(m_x2_lr - m_x1_lr)), + m_inc(m_ver ? ((y2 > y1) ? 1 : -1) : ((x2 > x1) ? 1 : -1)), + m_interpolator(m_ver ? x1 : y1, + m_ver ? x2 : y2, + m_len) + { + } + + //-------------------------------------------------------------------- + bool is_ver() const { return m_ver; } + unsigned len() const { return m_len; } + int inc() const { return m_inc; } + + //-------------------------------------------------------------------- + void hstep() + { + ++m_interpolator; + m_x1_lr += m_inc; + } + + //-------------------------------------------------------------------- + void vstep() + { + ++m_interpolator; + m_y1_lr += m_inc; + } + + //-------------------------------------------------------------------- + int x1() const { return m_x1_lr; } + int y1() const { return m_y1_lr; } + int x2() const { return line_lr(m_interpolator.y()); } + int y2() const { return line_lr(m_interpolator.y()); } + int x2_hr() const { return m_interpolator.y(); } + int y2_hr() const { return m_interpolator.y(); } + + private: + int m_x1_lr; + int m_y1_lr; + int m_x2_lr; + int m_y2_lr; + bool m_ver; + unsigned m_len; + int m_inc; + dda2_line_interpolator m_interpolator; + + }; + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_ellipse.h b/desmume/src/windows/agg/include/agg_ellipse.h new file mode 100644 index 000000000..8693a3c38 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_ellipse.h @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ELLIPSE_INCLUDED +#define AGG_ELLIPSE_INCLUDED + +#include "agg_basics.h" +#include + +namespace agg +{ + + //----------------------------------------------------------------ellipse + class ellipse + { + public: + ellipse() : + m_x(0.0), m_y(0.0), m_rx(1.0), m_ry(1.0), m_scale(1.0), + m_num(4), m_step(0), m_cw(false) {} + + ellipse(double x, double y, double rx, double ry, + unsigned num_steps=0, bool cw=false) : + m_x(x), m_y(y), m_rx(rx), m_ry(ry), m_scale(1.0), + m_num(num_steps), m_step(0), m_cw(cw) + { + if(m_num == 0) calc_num_steps(); + } + + void init(double x, double y, double rx, double ry, + unsigned num_steps=0, bool cw=false); + + void approximation_scale(double scale); + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + void calc_num_steps(); + + double m_x; + double m_y; + double m_rx; + double m_ry; + double m_scale; + unsigned m_num; + unsigned m_step; + bool m_cw; + }; + + //------------------------------------------------------------------------ + inline void ellipse::init(double x, double y, double rx, double ry, + unsigned num_steps, bool cw) + { + m_x = x; + m_y = y; + m_rx = rx; + m_ry = ry; + m_num = num_steps; + m_step = 0; + m_cw = cw; + if(m_num == 0) calc_num_steps(); + } + + //------------------------------------------------------------------------ + inline void ellipse::approximation_scale(double scale) + { + m_scale = scale; + calc_num_steps(); + } + + //------------------------------------------------------------------------ + inline void ellipse::calc_num_steps() + { + double ra = (fabs(m_rx) + fabs(m_ry)) / 2; + double da = acos(ra / (ra + 0.125 / m_scale)) * 2; + m_num = uround(2*pi / da); + } + + //------------------------------------------------------------------------ + inline void ellipse::rewind(unsigned) + { + m_step = 0; + } + + //------------------------------------------------------------------------ + inline unsigned ellipse::vertex(double* x, double* y) + { + if(m_step == m_num) + { + ++m_step; + return path_cmd_end_poly | path_flags_close | path_flags_ccw; + } + if(m_step > m_num) return path_cmd_stop; + double angle = double(m_step) / double(m_num) * 2.0 * pi; + if(m_cw) angle = 2.0 * pi - angle; + *x = m_x + cos(angle) * m_rx; + *y = m_y + sin(angle) * m_ry; + m_step++; + return ((m_step == 1) ? path_cmd_move_to : path_cmd_line_to); + } + +} + + + +#endif + + diff --git a/desmume/src/windows/agg/include/agg_ellipse_bresenham.h b/desmume/src/windows/agg/include/agg_ellipse_bresenham.h new file mode 100644 index 000000000..dfb66e874 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_ellipse_bresenham.h @@ -0,0 +1,118 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ELLIPSE_BRESENHAM_INCLUDED +#define AGG_ELLIPSE_BRESENHAM_INCLUDED + + +#include "agg_basics.h" + + +namespace agg +{ + + //------------------------------------------ellipse_bresenham_interpolator + class ellipse_bresenham_interpolator + { + public: + ellipse_bresenham_interpolator(int rx, int ry) : + m_rx2(rx * rx), + m_ry2(ry * ry), + m_two_rx2(m_rx2 << 1), + m_two_ry2(m_ry2 << 1), + m_dx(0), + m_dy(0), + m_inc_x(0), + m_inc_y(-ry * m_two_rx2), + m_cur_f(0) + {} + + int dx() const { return m_dx; } + int dy() const { return m_dy; } + + void operator++ () + { + int mx, my, mxy, min_m; + int fx, fy, fxy; + + mx = fx = m_cur_f + m_inc_x + m_ry2; + if(mx < 0) mx = -mx; + + my = fy = m_cur_f + m_inc_y + m_rx2; + if(my < 0) my = -my; + + mxy = fxy = m_cur_f + m_inc_x + m_ry2 + m_inc_y + m_rx2; + if(mxy < 0) mxy = -mxy; + + min_m = mx; + bool flag = true; + + if(min_m > my) + { + min_m = my; + flag = false; + } + + m_dx = m_dy = 0; + + if(min_m > mxy) + { + m_inc_x += m_two_ry2; + m_inc_y += m_two_rx2; + m_cur_f = fxy; + m_dx = 1; + m_dy = 1; + return; + } + + if(flag) + { + m_inc_x += m_two_ry2; + m_cur_f = fx; + m_dx = 1; + return; + } + + m_inc_y += m_two_rx2; + m_cur_f = fy; + m_dy = 1; + } + + private: + int m_rx2; + int m_ry2; + int m_two_rx2; + int m_two_ry2; + int m_dx; + int m_dy; + int m_inc_x; + int m_inc_y; + int m_cur_f; + + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_embedded_raster_fonts.h b/desmume/src/windows/agg/include/agg_embedded_raster_fonts.h new file mode 100644 index 000000000..795a90d00 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_embedded_raster_fonts.h @@ -0,0 +1,68 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_EMBEDDED_RASTER_FONTS_INCLUDED +#define AGG_EMBEDDED_RASTER_FONTS_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + extern const int8u gse4x6[]; + extern const int8u gse4x8[]; + extern const int8u gse5x7[]; + extern const int8u gse5x9[]; + extern const int8u gse6x12[]; + extern const int8u gse6x9[]; + extern const int8u gse7x11[]; + extern const int8u gse7x11_bold[]; + extern const int8u gse7x15[]; + extern const int8u gse7x15_bold[]; + extern const int8u gse8x16[]; + extern const int8u gse8x16_bold[]; + extern const int8u mcs11_prop[]; + extern const int8u mcs11_prop_condensed[]; + extern const int8u mcs12_prop[]; + extern const int8u mcs13_prop[]; + extern const int8u mcs5x10_mono[]; + extern const int8u mcs5x11_mono[]; + extern const int8u mcs6x10_mono[]; + extern const int8u mcs6x11_mono[]; + extern const int8u mcs7x12_mono_high[]; + extern const int8u mcs7x12_mono_low[]; + extern const int8u verdana12[]; + extern const int8u verdana12_bold[]; + extern const int8u verdana13[]; + extern const int8u verdana13_bold[]; + extern const int8u verdana14[]; + extern const int8u verdana14_bold[]; + extern const int8u verdana16[]; + extern const int8u verdana16_bold[]; + extern const int8u verdana17[]; + extern const int8u verdana17_bold[]; + extern const int8u verdana18[]; + extern const int8u verdana18_bold[]; +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_font_cache_manager.h b/desmume/src/windows/agg/include/agg_font_cache_manager.h new file mode 100644 index 000000000..6513868c7 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_font_cache_manager.h @@ -0,0 +1,418 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_FONT_CACHE_MANAGER_INCLUDED +#define AGG_FONT_CACHE_MANAGER_INCLUDED + +#include +#include "agg_array.h" + +namespace agg +{ + + //---------------------------------------------------------glyph_data_type + enum glyph_data_type + { + glyph_data_invalid = 0, + glyph_data_mono = 1, + glyph_data_gray8 = 2, + glyph_data_outline = 3 + }; + + + //-------------------------------------------------------------glyph_cache + struct glyph_cache + { + unsigned glyph_index; + int8u* data; + unsigned data_size; + glyph_data_type data_type; + rect_i bounds; + double advance_x; + double advance_y; + }; + + + //--------------------------------------------------------------font_cache + class font_cache + { + public: + enum block_size_e { block_size = 16384-16 }; + + //-------------------------------------------------------------------- + font_cache() : + m_allocator(block_size), + m_font_signature(0) + {} + + //-------------------------------------------------------------------- + void signature(const char* font_signature) + { + m_font_signature = (char*)m_allocator.allocate(strlen(font_signature) + 1); + strcpy(m_font_signature, font_signature); + memset(m_glyphs, 0, sizeof(m_glyphs)); + } + + //-------------------------------------------------------------------- + bool font_is(const char* font_signature) const + { + return strcmp(font_signature, m_font_signature) == 0; + } + + //-------------------------------------------------------------------- + const glyph_cache* find_glyph(unsigned glyph_code) const + { + unsigned msb = (glyph_code >> 8) & 0xFF; + if(m_glyphs[msb]) + { + return m_glyphs[msb][glyph_code & 0xFF]; + } + return 0; + } + + //-------------------------------------------------------------------- + glyph_cache* cache_glyph(unsigned glyph_code, + unsigned glyph_index, + unsigned data_size, + glyph_data_type data_type, + const rect_i& bounds, + double advance_x, + double advance_y) + { + unsigned msb = (glyph_code >> 8) & 0xFF; + if(m_glyphs[msb] == 0) + { + m_glyphs[msb] = + (glyph_cache**)m_allocator.allocate(sizeof(glyph_cache*) * 256, + sizeof(glyph_cache*)); + memset(m_glyphs[msb], 0, sizeof(glyph_cache*) * 256); + } + + unsigned lsb = glyph_code & 0xFF; + if(m_glyphs[msb][lsb]) return 0; // Already exists, do not overwrite + + glyph_cache* glyph = + (glyph_cache*)m_allocator.allocate(sizeof(glyph_cache), + sizeof(double)); + + glyph->glyph_index = glyph_index; + glyph->data = m_allocator.allocate(data_size); + glyph->data_size = data_size; + glyph->data_type = data_type; + glyph->bounds = bounds; + glyph->advance_x = advance_x; + glyph->advance_y = advance_y; + return m_glyphs[msb][lsb] = glyph; + } + + private: + block_allocator m_allocator; + glyph_cache** m_glyphs[256]; + char* m_font_signature; + }; + + + + + + + + //---------------------------------------------------------font_cache_pool + class font_cache_pool + { + public: + //-------------------------------------------------------------------- + ~font_cache_pool() + { + unsigned i; + for(i = 0; i < m_num_fonts; ++i) + { + obj_allocator::deallocate(m_fonts[i]); + } + pod_allocator::deallocate(m_fonts, m_max_fonts); + } + + //-------------------------------------------------------------------- + font_cache_pool(unsigned max_fonts=32) : + m_fonts(pod_allocator::allocate(max_fonts)), + m_max_fonts(max_fonts), + m_num_fonts(0), + m_cur_font(0) + {} + + + //-------------------------------------------------------------------- + void font(const char* font_signature, bool reset_cache = false) + { + int idx = find_font(font_signature); + if(idx >= 0) + { + if(reset_cache) + { + obj_allocator::deallocate(m_fonts[idx]); + m_fonts[idx] = obj_allocator::allocate(); + m_fonts[idx]->signature(font_signature); + } + m_cur_font = m_fonts[idx]; + } + else + { + if(m_num_fonts >= m_max_fonts) + { + obj_allocator::deallocate(m_fonts[0]); + memcpy(m_fonts, + m_fonts + 1, + (m_max_fonts - 1) * sizeof(font_cache*)); + m_num_fonts = m_max_fonts - 1; + } + m_fonts[m_num_fonts] = obj_allocator::allocate(); + m_fonts[m_num_fonts]->signature(font_signature); + m_cur_font = m_fonts[m_num_fonts]; + ++m_num_fonts; + } + } + + //-------------------------------------------------------------------- + const font_cache* font() const + { + return m_cur_font; + } + + //-------------------------------------------------------------------- + const glyph_cache* find_glyph(unsigned glyph_code) const + { + if(m_cur_font) return m_cur_font->find_glyph(glyph_code); + return 0; + } + + //-------------------------------------------------------------------- + glyph_cache* cache_glyph(unsigned glyph_code, + unsigned glyph_index, + unsigned data_size, + glyph_data_type data_type, + const rect_i& bounds, + double advance_x, + double advance_y) + { + if(m_cur_font) + { + return m_cur_font->cache_glyph(glyph_code, + glyph_index, + data_size, + data_type, + bounds, + advance_x, + advance_y); + } + return 0; + } + + + //-------------------------------------------------------------------- + int find_font(const char* font_signature) + { + unsigned i; + for(i = 0; i < m_num_fonts; i++) + { + if(m_fonts[i]->font_is(font_signature)) return int(i); + } + return -1; + } + + private: + font_cache** m_fonts; + unsigned m_max_fonts; + unsigned m_num_fonts; + font_cache* m_cur_font; + }; + + + + + //------------------------------------------------------------------------ + enum glyph_rendering + { + glyph_ren_native_mono, + glyph_ren_native_gray8, + glyph_ren_outline, + glyph_ren_agg_mono, + glyph_ren_agg_gray8 + }; + + + + + //------------------------------------------------------font_cache_manager + template class font_cache_manager + { + public: + typedef FontEngine font_engine_type; + typedef font_cache_manager self_type; + typedef typename font_engine_type::path_adaptor_type path_adaptor_type; + typedef typename font_engine_type::gray8_adaptor_type gray8_adaptor_type; + typedef typename gray8_adaptor_type::embedded_scanline gray8_scanline_type; + typedef typename font_engine_type::mono_adaptor_type mono_adaptor_type; + typedef typename mono_adaptor_type::embedded_scanline mono_scanline_type; + + //-------------------------------------------------------------------- + font_cache_manager(font_engine_type& engine, unsigned max_fonts=32) : + m_fonts(max_fonts), + m_engine(engine), + m_change_stamp(-1), + m_prev_glyph(0), + m_last_glyph(0) + {} + + //-------------------------------------------------------------------- + void reset_last_glyph() + { + m_prev_glyph = m_last_glyph = 0; + } + + //-------------------------------------------------------------------- + const glyph_cache* glyph(unsigned glyph_code) + { + synchronize(); + const glyph_cache* gl = m_fonts.find_glyph(glyph_code); + if(gl) + { + m_prev_glyph = m_last_glyph; + return m_last_glyph = gl; + } + else + { + if(m_engine.prepare_glyph(glyph_code)) + { + m_prev_glyph = m_last_glyph; + m_last_glyph = m_fonts.cache_glyph(glyph_code, + m_engine.glyph_index(), + m_engine.data_size(), + m_engine.data_type(), + m_engine.bounds(), + m_engine.advance_x(), + m_engine.advance_y()); + m_engine.write_glyph_to(m_last_glyph->data); + return m_last_glyph; + } + } + return 0; + } + + //-------------------------------------------------------------------- + void init_embedded_adaptors(const glyph_cache* gl, + double x, double y, + double scale=1.0) + { + if(gl) + { + switch(gl->data_type) + { + default: return; + case glyph_data_mono: + m_mono_adaptor.init(gl->data, gl->data_size, x, y); + break; + + case glyph_data_gray8: + m_gray8_adaptor.init(gl->data, gl->data_size, x, y); + break; + + case glyph_data_outline: + m_path_adaptor.init(gl->data, gl->data_size, x, y, scale); + break; + } + } + } + + + //-------------------------------------------------------------------- + path_adaptor_type& path_adaptor() { return m_path_adaptor; } + gray8_adaptor_type& gray8_adaptor() { return m_gray8_adaptor; } + gray8_scanline_type& gray8_scanline() { return m_gray8_scanline; } + mono_adaptor_type& mono_adaptor() { return m_mono_adaptor; } + mono_scanline_type& mono_scanline() { return m_mono_scanline; } + + //-------------------------------------------------------------------- + const glyph_cache* perv_glyph() const { return m_prev_glyph; } + const glyph_cache* last_glyph() const { return m_last_glyph; } + + //-------------------------------------------------------------------- + bool add_kerning(double* x, double* y) + { + if(m_prev_glyph && m_last_glyph) + { + return m_engine.add_kerning(m_prev_glyph->glyph_index, + m_last_glyph->glyph_index, + x, y); + } + return false; + } + + //-------------------------------------------------------------------- + void precache(unsigned from, unsigned to) + { + for(; from <= to; ++from) glyph(from); + } + + //-------------------------------------------------------------------- + void reset_cache() + { + m_fonts.font(m_engine.font_signature(), true); + m_change_stamp = m_engine.change_stamp(); + m_prev_glyph = m_last_glyph = 0; + } + + private: + //-------------------------------------------------------------------- + font_cache_manager(const self_type&); + const self_type& operator = (const self_type&); + + //-------------------------------------------------------------------- + void synchronize() + { + if(m_change_stamp != m_engine.change_stamp()) + { + m_fonts.font(m_engine.font_signature()); + m_change_stamp = m_engine.change_stamp(); + m_prev_glyph = m_last_glyph = 0; + } + } + + font_cache_pool m_fonts; + font_engine_type& m_engine; + int m_change_stamp; + double m_dx; + double m_dy; + const glyph_cache* m_prev_glyph; + const glyph_cache* m_last_glyph; + path_adaptor_type m_path_adaptor; + gray8_adaptor_type m_gray8_adaptor; + gray8_scanline_type m_gray8_scanline; + mono_adaptor_type m_mono_adaptor; + mono_scanline_type m_mono_scanline; + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_gamma_functions.h b/desmume/src/windows/agg/include/agg_gamma_functions.h new file mode 100644 index 000000000..8c49cfa7e --- /dev/null +++ b/desmume/src/windows/agg/include/agg_gamma_functions.h @@ -0,0 +1,132 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_FUNCTIONS_INCLUDED +#define AGG_GAMMA_FUNCTIONS_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + //===============================================================gamma_none + struct gamma_none + { + double operator()(double x) const { return x; } + }; + + + //==============================================================gamma_power + class gamma_power + { + public: + gamma_power() : m_gamma(1.0) {} + gamma_power(double g) : m_gamma(g) {} + + void gamma(double g) { m_gamma = g; } + double gamma() const { return m_gamma; } + + double operator() (double x) const + { + return pow(x, m_gamma); + } + + private: + double m_gamma; + }; + + + //==========================================================gamma_threshold + class gamma_threshold + { + public: + gamma_threshold() : m_threshold(0.5) {} + gamma_threshold(double t) : m_threshold(t) {} + + void threshold(double t) { m_threshold = t; } + double threshold() const { return m_threshold; } + + double operator() (double x) const + { + return (x < m_threshold) ? 0.0 : 1.0; + } + + private: + double m_threshold; + }; + + + //============================================================gamma_linear + class gamma_linear + { + public: + gamma_linear() : m_start(0.0), m_end(1.0) {} + gamma_linear(double s, double e) : m_start(s), m_end(e) {} + + void set(double s, double e) { m_start = s; m_end = e; } + void start(double s) { m_start = s; } + void end(double e) { m_end = e; } + double start() const { return m_start; } + double end() const { return m_end; } + + double operator() (double x) const + { + if(x < m_start) return 0.0; + if(x > m_end) return 1.0; + return (x - m_start) / (m_end - m_start); + } + + private: + double m_start; + double m_end; + }; + + + //==========================================================gamma_multiply + class gamma_multiply + { + public: + gamma_multiply() : m_mul(1.0) {} + gamma_multiply(double v) : m_mul(v) {} + + void value(double v) { m_mul = v; } + double value() const { return m_mul; } + + double operator() (double x) const + { + double y = x * m_mul; + if(y > 1.0) y = 1.0; + return y; + } + + private: + double m_mul; + }; + +} + +#endif + + + diff --git a/desmume/src/windows/agg/include/agg_gamma_lut.h b/desmume/src/windows/agg/include/agg_gamma_lut.h new file mode 100644 index 000000000..e9ffbf580 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_gamma_lut.h @@ -0,0 +1,130 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_LUT_INCLUDED +#define AGG_GAMMA_LUT_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + template class gamma_lut + { + public: + typedef gamma_lut self_type; + + enum gamma_scale_e + { + gamma_shift = GammaShift, + gamma_size = 1 << gamma_shift, + gamma_mask = gamma_size - 1 + }; + + enum hi_res_scale_e + { + hi_res_shift = HiResShift, + hi_res_size = 1 << hi_res_shift, + hi_res_mask = hi_res_size - 1 + }; + + ~gamma_lut() + { + pod_allocator::deallocate(m_inv_gamma, hi_res_size); + pod_allocator::deallocate(m_dir_gamma, gamma_size); + } + + gamma_lut() : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = HiResT(i << (hi_res_shift - gamma_shift)); + } + + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = LoResT(i >> (hi_res_shift - gamma_shift)); + } + } + + gamma_lut(double g) : + m_gamma(1.0), + m_dir_gamma(pod_allocator::allocate(gamma_size)), + m_inv_gamma(pod_allocator::allocate(hi_res_size)) + { + gamma(g); + } + + void gamma(double g) + { + m_gamma = g; + + unsigned i; + for(i = 0; i < gamma_size; i++) + { + m_dir_gamma[i] = (HiResT) + uround(pow(i / double(gamma_mask), m_gamma) * double(hi_res_mask)); + } + + double inv_g = 1.0 / g; + for(i = 0; i < hi_res_size; i++) + { + m_inv_gamma[i] = (LoResT) + uround(pow(i / double(hi_res_mask), inv_g) * double(gamma_mask)); + } + } + + double gamma() const + { + return m_gamma; + } + + HiResT dir(LoResT v) const + { + return m_dir_gamma[unsigned(v)]; + } + + LoResT inv(HiResT v) const + { + return m_inv_gamma[unsigned(v)]; + } + + private: + gamma_lut(const self_type&); + const self_type& operator = (const self_type&); + + double m_gamma; + HiResT* m_dir_gamma; + LoResT* m_inv_gamma; + }; +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_glyph_raster_bin.h b/desmume/src/windows/agg/include/agg_glyph_raster_bin.h new file mode 100644 index 000000000..085f1f21b --- /dev/null +++ b/desmume/src/windows/agg/include/agg_glyph_raster_bin.h @@ -0,0 +1,164 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GLYPH_RASTER_BIN_INCLUDED +#define AGG_GLYPH_RASTER_BIN_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //========================================================glyph_raster_bin + template class glyph_raster_bin + { + public: + typedef ColorT color_type; + + //-------------------------------------------------------------------- + struct glyph_rect + { + int x1,y1,x2,y2; + double dx, dy; + }; + + //-------------------------------------------------------------------- + glyph_raster_bin(const int8u* font) : + m_font(font), + m_big_endian(false) + { + int t = 1; + if(*(char*)&t == 0) m_big_endian = true; + memset(m_span, 0, sizeof(m_span)); + } + + //-------------------------------------------------------------------- + const int8u* font() const { return m_font; } + void font(const int8u* f) { m_font = f; } + + //-------------------------------------------------------------------- + double height() const { return m_font[0]; } + double base_line() const { return m_font[1]; } + + //-------------------------------------------------------------------- + template + double width(const CharT* str) const + { + unsigned start_char = m_font[2]; + unsigned num_chars = m_font[3]; + + unsigned w = 0; + while(*str) + { + unsigned glyph = *str; + const int8u* bits = m_font + 4 + num_chars * 2 + + value(m_font + 4 + (glyph - start_char) * 2); + w += *bits; + ++str; + } + return w; + } + + //-------------------------------------------------------------------- + void prepare(glyph_rect* r, double x, double y, unsigned glyph, bool flip) + { + unsigned start_char = m_font[2]; + unsigned num_chars = m_font[3]; + + m_bits = m_font + 4 + num_chars * 2 + + value(m_font + 4 + (glyph - start_char) * 2); + + m_glyph_width = *m_bits++; + m_glyph_byte_width = (m_glyph_width + 7) >> 3; + + r->x1 = int(x); + r->x2 = r->x1 + m_glyph_width - 1; + if(flip) + { + r->y1 = int(y) - m_font[0] + m_font[1]; + r->y2 = r->y1 + m_font[0] - 1; + } + else + { + r->y1 = int(y) - m_font[1] + 1; + r->y2 = r->y1 + m_font[0] - 1; + } + r->dx = m_glyph_width; + r->dy = 0; + } + + //-------------------------------------------------------------------- + const cover_type* span(unsigned i) + { + i = m_font[0] - i - 1; + const int8u* bits = m_bits + i * m_glyph_byte_width; + unsigned j; + unsigned val = *bits; + unsigned nb = 0; + for(j = 0; j < m_glyph_width; ++j) + { + m_span[j] = (cover_type)((val & 0x80) ? cover_full : cover_none); + val <<= 1; + if(++nb >= 8) + { + val = *++bits; + nb = 0; + } + } + return m_span; + } + + private: + //-------------------------------------------------------------------- + int16u value(const int8u* p) const + { + int16u v; + if(m_big_endian) + { + *(int8u*)&v = p[1]; + *((int8u*)&v + 1) = p[0]; + } + else + { + *(int8u*)&v = p[0]; + *((int8u*)&v + 1) = p[1]; + } + return v; + } + + + //-------------------------------------------------------------------- + const int8u* m_font; + bool m_big_endian; + cover_type m_span[32]; + const int8u* m_bits; + unsigned m_glyph_width; + unsigned m_glyph_byte_width; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_gradient_lut.h b/desmume/src/windows/agg/include/agg_gradient_lut.h new file mode 100644 index 000000000..905780bd3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_gradient_lut.h @@ -0,0 +1,253 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GRADIENT_LUT_INCLUDED +#define AGG_GRADIENT_LUT_INCLUDED + +#include "agg_array.h" +#include "agg_dda_line.h" +#include "agg_color_rgba.h" +#include "agg_color_gray.h" + +namespace agg +{ + + //======================================================color_interpolator + template struct color_interpolator + { + public: + typedef ColorT color_type; + + color_interpolator(const color_type& c1, + const color_type& c2, + unsigned len) : + m_c1(c1), + m_c2(c2), + m_len(len), + m_count(0) + {} + + void operator ++ () + { + ++m_count; + } + + color_type color() const + { + return m_c1.gradient(m_c2, double(m_count) / m_len); + } + + private: + color_type m_c1; + color_type m_c2; + unsigned m_len; + unsigned m_count; + }; + + //======================================================================== + // Fast specialization for rgba8 + template<> struct color_interpolator + { + public: + typedef rgba8 color_type; + + color_interpolator(const color_type& c1, + const color_type& c2, + unsigned len) : + r(c1.r, c2.r, len), + g(c1.g, c2.g, len), + b(c1.b, c2.b, len), + a(c1.a, c2.a, len) + {} + + void operator ++ () + { + ++r; ++g; ++b; ++a; + } + + color_type color() const + { + return color_type(r.y(), g.y(), b.y(), a.y()); + } + + private: + agg::dda_line_interpolator<14> r, g, b, a; + }; + + //======================================================================== + // Fast specialization for gray8 + template<> struct color_interpolator + { + public: + typedef gray8 color_type; + + color_interpolator(const color_type& c1, + const color_type& c2, + unsigned len) : + v(c1.v, c2.v, len), + a(c1.a, c2.a, len) + {} + + void operator ++ () + { + ++v; ++a; + } + + color_type color() const + { + return color_type(v.y(), a.y()); + } + + private: + agg::dda_line_interpolator<14> v,a; + }; + + //============================================================gradient_lut + template class gradient_lut + { + public: + typedef ColorInterpolator interpolator_type; + typedef typename interpolator_type::color_type color_type; + enum { color_lut_size = ColorLutSize }; + + //-------------------------------------------------------------------- + gradient_lut() : m_color_lut(color_lut_size) {} + + // Build Gradient Lut + // First, call remove_all(), then add_color() at least twice, + // then build_lut(). Argument "offset" in add_color must be + // in range [0...1] and defines a color stop as it is described + // in SVG specification, section Gradients and Patterns. + // The simplest linear gradient is: + // gradient_lut.add_color(0.0, start_color); + // gradient_lut.add_color(1.0, end_color); + //-------------------------------------------------------------------- + void remove_all(); + void add_color(double offset, const color_type& color); + void build_lut(); + + // Size-index Interface. This class can be used directly as the + // ColorF in span_gradient. All it needs is two access methods + // size() and operator []. + //-------------------------------------------------------------------- + static unsigned size() + { + return color_lut_size; + } + const color_type& operator [] (unsigned i) const + { + return m_color_lut[i]; + } + + private: + //-------------------------------------------------------------------- + struct color_point + { + double offset; + color_type color; + + color_point() {} + color_point(double off, const color_type& c) : + offset(off), color(c) + { + if(offset < 0.0) offset = 0.0; + if(offset > 1.0) offset = 1.0; + } + }; + typedef agg::pod_bvector color_profile_type; + typedef agg::pod_array color_lut_type; + + static bool offset_less(const color_point& a, const color_point& b) + { + return a.offset < b.offset; + } + static bool offset_equal(const color_point& a, const color_point& b) + { + return a.offset == b.offset; + } + + //-------------------------------------------------------------------- + color_profile_type m_color_profile; + color_lut_type m_color_lut; + }; + + + + //------------------------------------------------------------------------ + template + void gradient_lut::remove_all() + { + m_color_profile.remove_all(); + } + + //------------------------------------------------------------------------ + template + void gradient_lut::add_color(double offset, const color_type& color) + { + m_color_profile.add(color_point(offset, color)); + } + + //------------------------------------------------------------------------ + template + void gradient_lut::build_lut() + { + quick_sort(m_color_profile, offset_less); + m_color_profile.cut_at(remove_duplicates(m_color_profile, offset_equal)); + if(m_color_profile.size() >= 2) + { + unsigned i; + unsigned start = uround(m_color_profile[0].offset * color_lut_size); + unsigned end; + color_type c = m_color_profile[0].color; + for(i = 0; i < start; i++) + { + m_color_lut[i] = c; + } + for(i = 1; i < m_color_profile.size(); i++) + { + end = uround(m_color_profile[i].offset * color_lut_size); + interpolator_type ci(m_color_profile[i-1].color, + m_color_profile[i ].color, + end - start + 1); + while(start < end) + { + m_color_lut[start] = ci.color(); + ++ci; + ++start; + } + } + c = m_color_profile.last().color; + for(; end < m_color_lut.size(); end++) + { + m_color_lut[end] = c; + } + } + } +} + + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_gsv_text.h b/desmume/src/windows/agg/include/agg_gsv_text.h new file mode 100644 index 000000000..adcefb430 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_gsv_text.h @@ -0,0 +1,158 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GSV_TEXT_INCLUDED +#define AGG_GSV_TEXT_INCLUDED + +#include "agg_array.h" +#include "agg_conv_stroke.h" +#include "agg_conv_transform.h" + +namespace agg +{ + + + //---------------------------------------------------------------gsv_text + // + // See Implementation agg_gsv_text.cpp + // + class gsv_text + { + enum status + { + initial, + next_char, + start_glyph, + glyph + }; + + public: + gsv_text(); + + void font(const void* font); + void flip(bool flip_y) { m_flip = flip_y; } + void load_font(const char* file); + void size(double height, double width=0.0); + void space(double space); + void line_space(double line_space); + void start_point(double x, double y); + void text(const char* text); + + double text_width(); + + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + // not supposed to be copied + gsv_text(const gsv_text&); + const gsv_text& operator = (const gsv_text&); + + int16u value(const int8u* p) const + { + int16u v; + if(m_big_endian) + { + *(int8u*)&v = p[1]; + *((int8u*)&v + 1) = p[0]; + } + else + { + *(int8u*)&v = p[0]; + *((int8u*)&v + 1) = p[1]; + } + return v; + } + + private: + double m_x; + double m_y; + double m_start_x; + double m_width; + double m_height; + double m_space; + double m_line_space; + char m_chr[2]; + char* m_text; + pod_array m_text_buf; + char* m_cur_chr; + const void* m_font; + pod_array m_loaded_font; + status m_status; + bool m_big_endian; + bool m_flip; + int8u* m_indices; + int8* m_glyphs; + int8* m_bglyph; + int8* m_eglyph; + double m_w; + double m_h; + }; + + + + + //--------------------------------------------------------gsv_text_outline + template class gsv_text_outline + { + public: + gsv_text_outline(gsv_text& text, const Transformer& trans) : + m_polyline(text), + m_trans(m_polyline, trans) + { + } + + void width(double w) + { + m_polyline.width(w); + } + + void transformer(const Transformer* trans) + { + m_trans->transformer(trans); + } + + void rewind(unsigned path_id) + { + m_trans.rewind(path_id); + m_polyline.line_join(round_join); + m_polyline.line_cap(round_cap); + } + + unsigned vertex(double* x, double* y) + { + return m_trans.vertex(x, y); + } + + private: + conv_stroke m_polyline; + conv_transform, Transformer> m_trans; + }; + + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_image_accessors.h b/desmume/src/windows/agg/include/agg_image_accessors.h new file mode 100644 index 000000000..b4d72a28a --- /dev/null +++ b/desmume/src/windows/agg/include/agg_image_accessors.h @@ -0,0 +1,490 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_IMAGE_ACCESSORS_INCLUDED +#define AGG_IMAGE_ACCESSORS_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //-----------------------------------------------------image_accessor_clip + template class image_accessor_clip + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::order_type order_type; + typedef typename pixfmt_type::value_type value_type; + enum pix_width_e { pix_width = pixfmt_type::pix_width }; + + image_accessor_clip() {} + explicit image_accessor_clip(const pixfmt_type& pixf, + const color_type& bk) : + m_pixf(&pixf) + { + pixfmt_type::make_pix(m_bk_buf, bk); + } + + void attach(const pixfmt_type& pixf) + { + m_pixf = &pixf; + } + + void background_color(const color_type& bk) + { + pixfmt_type::make_pix(m_bk_buf, bk); + } + + private: + AGG_INLINE const int8u* pixel() const + { + if(m_y >= 0 && m_y < (int)m_pixf->height() && + m_x >= 0 && m_x < (int)m_pixf->width()) + { + return m_pixf->pix_ptr(m_x, m_y); + } + return m_bk_buf; + } + + public: + AGG_INLINE const int8u* span(int x, int y, unsigned len) + { + m_x = m_x0 = x; + m_y = y; + if(y >= 0 && y < (int)m_pixf->height() && + x >= 0 && x+(int)len <= (int)m_pixf->width()) + { + return m_pix_ptr = m_pixf->pix_ptr(x, y); + } + m_pix_ptr = 0; + return pixel(); + } + + AGG_INLINE const int8u* next_x() + { + if(m_pix_ptr) return m_pix_ptr += pix_width; + ++m_x; + return pixel(); + } + + AGG_INLINE const int8u* next_y() + { + ++m_y; + m_x = m_x0; + if(m_pix_ptr && + m_y >= 0 && m_y < (int)m_pixf->height()) + { + return m_pix_ptr = m_pixf->pix_ptr(m_x, m_y); + } + m_pix_ptr = 0; + return pixel(); + } + + private: + const pixfmt_type* m_pixf; + int8u m_bk_buf[4]; + int m_x, m_x0, m_y; + const int8u* m_pix_ptr; + }; + + + + + //--------------------------------------------------image_accessor_no_clip + template class image_accessor_no_clip + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::order_type order_type; + typedef typename pixfmt_type::value_type value_type; + enum pix_width_e { pix_width = pixfmt_type::pix_width }; + + image_accessor_no_clip() {} + explicit image_accessor_no_clip(const pixfmt_type& pixf) : + m_pixf(&pixf) + {} + + void attach(const pixfmt_type& pixf) + { + m_pixf = &pixf; + } + + AGG_INLINE const int8u* span(int x, int y, unsigned) + { + m_x = x; + m_y = y; + return m_pix_ptr = m_pixf->pix_ptr(x, y); + } + + AGG_INLINE const int8u* next_x() + { + return m_pix_ptr += pix_width; + } + + AGG_INLINE const int8u* next_y() + { + ++m_y; + return m_pix_ptr = m_pixf->pix_ptr(m_x, m_y); + } + + private: + const pixfmt_type* m_pixf; + int m_x, m_y; + const int8u* m_pix_ptr; + }; + + + + + //----------------------------------------------------image_accessor_clone + template class image_accessor_clone + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::order_type order_type; + typedef typename pixfmt_type::value_type value_type; + enum pix_width_e { pix_width = pixfmt_type::pix_width }; + + image_accessor_clone() {} + explicit image_accessor_clone(const pixfmt_type& pixf) : + m_pixf(&pixf) + {} + + void attach(const pixfmt_type& pixf) + { + m_pixf = &pixf; + } + + private: + AGG_INLINE const int8u* pixel() const + { + register int x = m_x; + register int y = m_y; + if(x < 0) x = 0; + if(y < 0) y = 0; + if(x >= (int)m_pixf->width()) x = m_pixf->width() - 1; + if(y >= (int)m_pixf->height()) y = m_pixf->height() - 1; + return m_pixf->pix_ptr(x, y); + } + + public: + AGG_INLINE const int8u* span(int x, int y, unsigned len) + { + m_x = m_x0 = x; + m_y = y; + if(y >= 0 && y < (int)m_pixf->height() && + x >= 0 && x+len <= (int)m_pixf->width()) + { + return m_pix_ptr = m_pixf->pix_ptr(x, y); + } + m_pix_ptr = 0; + return pixel(); + } + + AGG_INLINE const int8u* next_x() + { + if(m_pix_ptr) return m_pix_ptr += pix_width; + ++m_x; + return pixel(); + } + + AGG_INLINE const int8u* next_y() + { + ++m_y; + m_x = m_x0; + if(m_pix_ptr && + m_y >= 0 && m_y < (int)m_pixf->height()) + { + return m_pix_ptr = m_pixf->pix_ptr(m_x, m_y); + } + m_pix_ptr = 0; + return pixel(); + } + + private: + const pixfmt_type* m_pixf; + int m_x, m_x0, m_y; + const int8u* m_pix_ptr; + }; + + + + + + //-----------------------------------------------------image_accessor_wrap + template class image_accessor_wrap + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::order_type order_type; + typedef typename pixfmt_type::value_type value_type; + enum pix_width_e { pix_width = pixfmt_type::pix_width }; + + image_accessor_wrap() {} + explicit image_accessor_wrap(const pixfmt_type& pixf) : + m_pixf(&pixf), + m_wrap_x(pixf.width()), + m_wrap_y(pixf.height()) + {} + + void attach(const pixfmt_type& pixf) + { + m_pixf = &pixf; + } + + AGG_INLINE const int8u* span(int x, int y, unsigned) + { + m_x = x; + m_row_ptr = m_pixf->row_ptr(m_wrap_y(y)); + return m_row_ptr + m_wrap_x(x) * pix_width; + } + + AGG_INLINE const int8u* next_x() + { + int x = ++m_wrap_x; + return m_row_ptr + x * pix_width; + } + + AGG_INLINE const int8u* next_y() + { + m_row_ptr = m_pixf->row_ptr(++m_wrap_y); + return m_row_ptr + m_wrap_x(m_x) * pix_width; + } + + private: + const pixfmt_type* m_pixf; + const int8u* m_row_ptr; + int m_x; + WrapX m_wrap_x; + WrapY m_wrap_y; + }; + + + + + //--------------------------------------------------------wrap_mode_repeat + class wrap_mode_repeat + { + public: + wrap_mode_repeat() {} + wrap_mode_repeat(unsigned size) : + m_size(size), + m_add(size * (0x3FFFFFFF / size)), + m_value(0) + {} + + AGG_INLINE unsigned operator() (int v) + { + return m_value = (unsigned(v) + m_add) % m_size; + } + + AGG_INLINE unsigned operator++ () + { + ++m_value; + if(m_value >= m_size) m_value = 0; + return m_value; + } + private: + unsigned m_size; + unsigned m_add; + unsigned m_value; + }; + + + //---------------------------------------------------wrap_mode_repeat_pow2 + class wrap_mode_repeat_pow2 + { + public: + wrap_mode_repeat_pow2() {} + wrap_mode_repeat_pow2(unsigned size) : m_value(0) + { + m_mask = 1; + while(m_mask < size) m_mask = (m_mask << 1) | 1; + m_mask >>= 1; + } + AGG_INLINE unsigned operator() (int v) + { + return m_value = unsigned(v) & m_mask; + } + AGG_INLINE unsigned operator++ () + { + ++m_value; + if(m_value > m_mask) m_value = 0; + return m_value; + } + private: + unsigned m_mask; + unsigned m_value; + }; + + + //----------------------------------------------wrap_mode_repeat_auto_pow2 + class wrap_mode_repeat_auto_pow2 + { + public: + wrap_mode_repeat_auto_pow2() {} + wrap_mode_repeat_auto_pow2(unsigned size) : + m_size(size), + m_add(size * (0x3FFFFFFF / size)), + m_mask((m_size & (m_size-1)) ? 0 : m_size-1), + m_value(0) + {} + + AGG_INLINE unsigned operator() (int v) + { + if(m_mask) return m_value = unsigned(v) & m_mask; + return m_value = (unsigned(v) + m_add) % m_size; + } + AGG_INLINE unsigned operator++ () + { + ++m_value; + if(m_value >= m_size) m_value = 0; + return m_value; + } + + private: + unsigned m_size; + unsigned m_add; + unsigned m_mask; + unsigned m_value; + }; + + + //-------------------------------------------------------wrap_mode_reflect + class wrap_mode_reflect + { + public: + wrap_mode_reflect() {} + wrap_mode_reflect(unsigned size) : + m_size(size), + m_size2(size * 2), + m_add(m_size2 * (0x3FFFFFFF / m_size2)), + m_value(0) + {} + + AGG_INLINE unsigned operator() (int v) + { + m_value = (unsigned(v) + m_add) % m_size2; + if(m_value >= m_size) return m_size2 - m_value - 1; + return m_value; + } + + AGG_INLINE unsigned operator++ () + { + ++m_value; + if(m_value >= m_size2) m_value = 0; + if(m_value >= m_size) return m_size2 - m_value - 1; + return m_value; + } + private: + unsigned m_size; + unsigned m_size2; + unsigned m_add; + unsigned m_value; + }; + + + + //--------------------------------------------------wrap_mode_reflect_pow2 + class wrap_mode_reflect_pow2 + { + public: + wrap_mode_reflect_pow2() {} + wrap_mode_reflect_pow2(unsigned size) : m_value(0) + { + m_mask = 1; + m_size = 1; + while(m_mask < size) + { + m_mask = (m_mask << 1) | 1; + m_size <<= 1; + } + } + AGG_INLINE unsigned operator() (int v) + { + m_value = unsigned(v) & m_mask; + if(m_value >= m_size) return m_mask - m_value; + return m_value; + } + AGG_INLINE unsigned operator++ () + { + ++m_value; + m_value &= m_mask; + if(m_value >= m_size) return m_mask - m_value; + return m_value; + } + private: + unsigned m_size; + unsigned m_mask; + unsigned m_value; + }; + + + + //---------------------------------------------wrap_mode_reflect_auto_pow2 + class wrap_mode_reflect_auto_pow2 + { + public: + wrap_mode_reflect_auto_pow2() {} + wrap_mode_reflect_auto_pow2(unsigned size) : + m_size(size), + m_size2(size * 2), + m_add(m_size2 * (0x3FFFFFFF / m_size2)), + m_mask((m_size2 & (m_size2-1)) ? 0 : m_size2-1), + m_value(0) + {} + + AGG_INLINE unsigned operator() (int v) + { + m_value = m_mask ? unsigned(v) & m_mask : + (unsigned(v) + m_add) % m_size2; + if(m_value >= m_size) return m_size2 - m_value - 1; + return m_value; + } + AGG_INLINE unsigned operator++ () + { + ++m_value; + if(m_value >= m_size2) m_value = 0; + if(m_value >= m_size) return m_size2 - m_value - 1; + return m_value; + } + + private: + unsigned m_size; + unsigned m_size2; + unsigned m_add; + unsigned m_mask; + unsigned m_value; + }; + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_image_filters.h b/desmume/src/windows/agg/include/agg_image_filters.h new file mode 100644 index 000000000..e66f8a782 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_image_filters.h @@ -0,0 +1,453 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_IMAGE_FILTERS_INCLUDED +#define AGG_IMAGE_FILTERS_INCLUDED + +#include "agg_array.h" +#include "agg_math.h" + +namespace agg +{ + + // See Implementation agg_image_filters.cpp + + enum image_filter_scale_e + { + image_filter_shift = 14, //----image_filter_shift + image_filter_scale = 1 << image_filter_shift, //----image_filter_scale + image_filter_mask = image_filter_scale - 1 //----image_filter_mask + }; + + enum image_subpixel_scale_e + { + image_subpixel_shift = 8, //----image_subpixel_shift + image_subpixel_scale = 1 << image_subpixel_shift, //----image_subpixel_scale + image_subpixel_mask = image_subpixel_scale - 1 //----image_subpixel_mask + }; + + + //-----------------------------------------------------image_filter_lut + class image_filter_lut + { + public: + template void calculate(const FilterF& filter, + bool normalization=true) + { + double r = filter.radius(); + realloc_lut(r); + unsigned i; + unsigned pivot = diameter() << (image_subpixel_shift - 1); + for(i = 0; i < pivot; i++) + { + double x = double(i) / double(image_subpixel_scale); + double y = filter.calc_weight(x); + m_weight_array[pivot + i] = + m_weight_array[pivot - i] = (int16)iround(y * image_filter_scale); + } + unsigned end = (diameter() << image_subpixel_shift) - 1; + m_weight_array[0] = m_weight_array[end]; + if(normalization) + { + normalize(); + } + } + + image_filter_lut() : m_radius(0), m_diameter(0), m_start(0) {} + + template image_filter_lut(const FilterF& filter, + bool normalization=true) + { + calculate(filter, normalization); + } + + double radius() const { return m_radius; } + unsigned diameter() const { return m_diameter; } + int start() const { return m_start; } + const int16* weight_array() const { return &m_weight_array[0]; } + void normalize(); + + private: + void realloc_lut(double radius); + image_filter_lut(const image_filter_lut&); + const image_filter_lut& operator = (const image_filter_lut&); + + double m_radius; + unsigned m_diameter; + int m_start; + pod_array m_weight_array; + }; + + + + //--------------------------------------------------------image_filter + template class image_filter : public image_filter_lut + { + public: + image_filter() + { + calculate(m_filter_function); + } + private: + FilterF m_filter_function; + }; + + + //-----------------------------------------------image_filter_bilinear + struct image_filter_bilinear + { + static double radius() { return 1.0; } + static double calc_weight(double x) + { + return 1.0 - x; + } + }; + + + //-----------------------------------------------image_filter_hanning + struct image_filter_hanning + { + static double radius() { return 1.0; } + static double calc_weight(double x) + { + return 0.5 + 0.5 * cos(pi * x); + } + }; + + + //-----------------------------------------------image_filter_hamming + struct image_filter_hamming + { + static double radius() { return 1.0; } + static double calc_weight(double x) + { + return 0.54 + 0.46 * cos(pi * x); + } + }; + + //-----------------------------------------------image_filter_hermite + struct image_filter_hermite + { + static double radius() { return 1.0; } + static double calc_weight(double x) + { + return (2.0 * x - 3.0) * x * x + 1.0; + } + }; + + //------------------------------------------------image_filter_quadric + struct image_filter_quadric + { + static double radius() { return 1.5; } + static double calc_weight(double x) + { + double t; + if(x < 0.5) return 0.75 - x * x; + if(x < 1.5) {t = x - 1.5; return 0.5 * t * t;} + return 0.0; + } + }; + + //------------------------------------------------image_filter_bicubic + class image_filter_bicubic + { + static double pow3(double x) + { + return (x <= 0.0) ? 0.0 : x * x * x; + } + + public: + static double radius() { return 2.0; } + static double calc_weight(double x) + { + return + (1.0/6.0) * + (pow3(x + 2) - 4 * pow3(x + 1) + 6 * pow3(x) - 4 * pow3(x - 1)); + } + }; + + //-------------------------------------------------image_filter_kaiser + class image_filter_kaiser + { + double a; + double i0a; + double epsilon; + + public: + image_filter_kaiser(double b = 6.33) : + a(b), epsilon(1e-12) + { + i0a = 1.0 / bessel_i0(b); + } + + static double radius() { return 1.0; } + double calc_weight(double x) const + { + return bessel_i0(a * sqrt(1. - x * x)) * i0a; + } + + private: + double bessel_i0(double x) const + { + int i; + double sum, y, t; + + sum = 1.; + y = x * x / 4.; + t = y; + + for(i = 2; t > epsilon; i++) + { + sum += t; + t *= (double)y / (i * i); + } + return sum; + } + }; + + //----------------------------------------------image_filter_catrom + struct image_filter_catrom + { + static double radius() { return 2.0; } + static double calc_weight(double x) + { + if(x < 1.0) return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0)); + if(x < 2.0) return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x))); + return 0.; + } + }; + + //---------------------------------------------image_filter_mitchell + class image_filter_mitchell + { + double p0, p2, p3; + double q0, q1, q2, q3; + + public: + image_filter_mitchell(double b = 1.0/3.0, double c = 1.0/3.0) : + p0((6.0 - 2.0 * b) / 6.0), + p2((-18.0 + 12.0 * b + 6.0 * c) / 6.0), + p3((12.0 - 9.0 * b - 6.0 * c) / 6.0), + q0((8.0 * b + 24.0 * c) / 6.0), + q1((-12.0 * b - 48.0 * c) / 6.0), + q2((6.0 * b + 30.0 * c) / 6.0), + q3((-b - 6.0 * c) / 6.0) + {} + + static double radius() { return 2.0; } + double calc_weight(double x) const + { + if(x < 1.0) return p0 + x * x * (p2 + x * p3); + if(x < 2.0) return q0 + x * (q1 + x * (q2 + x * q3)); + return 0.0; + } + }; + + + //----------------------------------------------image_filter_spline16 + struct image_filter_spline16 + { + static double radius() { return 2.0; } + static double calc_weight(double x) + { + if(x < 1.0) + { + return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0; + } + return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1); + } + }; + + + //---------------------------------------------image_filter_spline36 + struct image_filter_spline36 + { + static double radius() { return 3.0; } + static double calc_weight(double x) + { + if(x < 1.0) + { + return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0; + } + if(x < 2.0) + { + return ((-6.0/11.0 * (x-1) + 270.0/209.0) * (x-1) - 156.0/ 209.0) * (x-1); + } + return ((1.0/11.0 * (x-2) - 45.0/209.0) * (x-2) + 26.0/209.0) * (x-2); + } + }; + + + //----------------------------------------------image_filter_gaussian + struct image_filter_gaussian + { + static double radius() { return 2.0; } + static double calc_weight(double x) + { + return exp(-2.0 * x * x) * sqrt(2.0 / pi); + } + }; + + + //------------------------------------------------image_filter_bessel + struct image_filter_bessel + { + static double radius() { return 3.2383; } + static double calc_weight(double x) + { + return (x == 0.0) ? pi / 4.0 : besj(pi * x, 1) / (2.0 * x); + } + }; + + + //-------------------------------------------------image_filter_sinc + class image_filter_sinc + { + public: + image_filter_sinc(double r) : m_radius(r < 2.0 ? 2.0 : r) {} + double radius() const { return m_radius; } + double calc_weight(double x) const + { + if(x == 0.0) return 1.0; + x *= pi; + return sin(x) / x; + } + private: + double m_radius; + }; + + + //-----------------------------------------------image_filter_lanczos + class image_filter_lanczos + { + public: + image_filter_lanczos(double r) : m_radius(r < 2.0 ? 2.0 : r) {} + double radius() const { return m_radius; } + double calc_weight(double x) const + { + if(x == 0.0) return 1.0; + if(x > m_radius) return 0.0; + x *= pi; + double xr = x / m_radius; + return (sin(x) / x) * (sin(xr) / xr); + } + private: + double m_radius; + }; + + + //----------------------------------------------image_filter_blackman + class image_filter_blackman + { + public: + image_filter_blackman(double r) : m_radius(r < 2.0 ? 2.0 : r) {} + double radius() const { return m_radius; } + double calc_weight(double x) const + { + if(x == 0.0) return 1.0; + if(x > m_radius) return 0.0; + x *= pi; + double xr = x / m_radius; + return (sin(x) / x) * (0.42 + 0.5*cos(xr) + 0.08*cos(2*xr)); + } + private: + double m_radius; + }; + + //------------------------------------------------image_filter_sinc36 + class image_filter_sinc36 : public image_filter_sinc + { public: image_filter_sinc36() : image_filter_sinc(3.0){} }; + + //------------------------------------------------image_filter_sinc64 + class image_filter_sinc64 : public image_filter_sinc + { public: image_filter_sinc64() : image_filter_sinc(4.0){} }; + + //-----------------------------------------------image_filter_sinc100 + class image_filter_sinc100 : public image_filter_sinc + { public: image_filter_sinc100() : image_filter_sinc(5.0){} }; + + //-----------------------------------------------image_filter_sinc144 + class image_filter_sinc144 : public image_filter_sinc + { public: image_filter_sinc144() : image_filter_sinc(6.0){} }; + + //-----------------------------------------------image_filter_sinc196 + class image_filter_sinc196 : public image_filter_sinc + { public: image_filter_sinc196() : image_filter_sinc(7.0){} }; + + //-----------------------------------------------image_filter_sinc256 + class image_filter_sinc256 : public image_filter_sinc + { public: image_filter_sinc256() : image_filter_sinc(8.0){} }; + + //---------------------------------------------image_filter_lanczos36 + class image_filter_lanczos36 : public image_filter_lanczos + { public: image_filter_lanczos36() : image_filter_lanczos(3.0){} }; + + //---------------------------------------------image_filter_lanczos64 + class image_filter_lanczos64 : public image_filter_lanczos + { public: image_filter_lanczos64() : image_filter_lanczos(4.0){} }; + + //--------------------------------------------image_filter_lanczos100 + class image_filter_lanczos100 : public image_filter_lanczos + { public: image_filter_lanczos100() : image_filter_lanczos(5.0){} }; + + //--------------------------------------------image_filter_lanczos144 + class image_filter_lanczos144 : public image_filter_lanczos + { public: image_filter_lanczos144() : image_filter_lanczos(6.0){} }; + + //--------------------------------------------image_filter_lanczos196 + class image_filter_lanczos196 : public image_filter_lanczos + { public: image_filter_lanczos196() : image_filter_lanczos(7.0){} }; + + //--------------------------------------------image_filter_lanczos256 + class image_filter_lanczos256 : public image_filter_lanczos + { public: image_filter_lanczos256() : image_filter_lanczos(8.0){} }; + + //--------------------------------------------image_filter_blackman36 + class image_filter_blackman36 : public image_filter_blackman + { public: image_filter_blackman36() : image_filter_blackman(3.0){} }; + + //--------------------------------------------image_filter_blackman64 + class image_filter_blackman64 : public image_filter_blackman + { public: image_filter_blackman64() : image_filter_blackman(4.0){} }; + + //-------------------------------------------image_filter_blackman100 + class image_filter_blackman100 : public image_filter_blackman + { public: image_filter_blackman100() : image_filter_blackman(5.0){} }; + + //-------------------------------------------image_filter_blackman144 + class image_filter_blackman144 : public image_filter_blackman + { public: image_filter_blackman144() : image_filter_blackman(6.0){} }; + + //-------------------------------------------image_filter_blackman196 + class image_filter_blackman196 : public image_filter_blackman + { public: image_filter_blackman196() : image_filter_blackman(7.0){} }; + + //-------------------------------------------image_filter_blackman256 + class image_filter_blackman256 : public image_filter_blackman + { public: image_filter_blackman256() : image_filter_blackman(8.0){} }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_line_aa_basics.h b/desmume/src/windows/agg/include/agg_line_aa_basics.h new file mode 100644 index 000000000..8c3d7ee20 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_line_aa_basics.h @@ -0,0 +1,199 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_LINE_AA_BASICS_INCLUDED +#define AGG_LINE_AA_BASICS_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + // See Implementation agg_line_aa_basics.cpp + + //------------------------------------------------------------------------- + enum line_subpixel_scale_e + { + line_subpixel_shift = 8, //----line_subpixel_shift + line_subpixel_scale = 1 << line_subpixel_shift, //----line_subpixel_scale + line_subpixel_mask = line_subpixel_scale - 1, //----line_subpixel_mask + line_max_coord = (1 << 28) - 1, //----line_max_coord + line_max_length = 1 << (line_subpixel_shift + 10) //----line_max_length + }; + + //------------------------------------------------------------------------- + enum line_mr_subpixel_scale_e + { + line_mr_subpixel_shift = 4, //----line_mr_subpixel_shift + line_mr_subpixel_scale = 1 << line_mr_subpixel_shift, //----line_mr_subpixel_scale + line_mr_subpixel_mask = line_mr_subpixel_scale - 1 //----line_mr_subpixel_mask + }; + + //------------------------------------------------------------------line_mr + AGG_INLINE int line_mr(int x) + { + return x >> (line_subpixel_shift - line_mr_subpixel_shift); + } + + //-------------------------------------------------------------------line_hr + AGG_INLINE int line_hr(int x) + { + return x << (line_subpixel_shift - line_mr_subpixel_shift); + } + + //---------------------------------------------------------------line_dbl_hr + AGG_INLINE int line_dbl_hr(int x) + { + return x << line_subpixel_shift; + } + + //---------------------------------------------------------------line_coord + struct line_coord + { + AGG_INLINE static int conv(double x) + { + return iround(x * line_subpixel_scale); + } + }; + + //-----------------------------------------------------------line_coord_sat + struct line_coord_sat + { + AGG_INLINE static int conv(double x) + { + return saturation::iround(x * line_subpixel_scale); + } + }; + + //==========================================================line_parameters + struct line_parameters + { + //--------------------------------------------------------------------- + line_parameters() {} + line_parameters(int x1_, int y1_, int x2_, int y2_, int len_) : + x1(x1_), y1(y1_), x2(x2_), y2(y2_), + dx(abs(x2_ - x1_)), + dy(abs(y2_ - y1_)), + sx((x2_ > x1_) ? 1 : -1), + sy((y2_ > y1_) ? 1 : -1), + vertical(dy >= dx), + inc(vertical ? sy : sx), + len(len_), + octant((sy & 4) | (sx & 2) | int(vertical)) + { + } + + //--------------------------------------------------------------------- + unsigned orthogonal_quadrant() const { return s_orthogonal_quadrant[octant]; } + unsigned diagonal_quadrant() const { return s_diagonal_quadrant[octant]; } + + //--------------------------------------------------------------------- + bool same_orthogonal_quadrant(const line_parameters& lp) const + { + return s_orthogonal_quadrant[octant] == s_orthogonal_quadrant[lp.octant]; + } + + //--------------------------------------------------------------------- + bool same_diagonal_quadrant(const line_parameters& lp) const + { + return s_diagonal_quadrant[octant] == s_diagonal_quadrant[lp.octant]; + } + + //--------------------------------------------------------------------- + void divide(line_parameters& lp1, line_parameters& lp2) const + { + int xmid = (x1 + x2) >> 1; + int ymid = (y1 + y2) >> 1; + int len2 = len >> 1; + + lp1 = *this; + lp2 = *this; + + lp1.x2 = xmid; + lp1.y2 = ymid; + lp1.len = len2; + lp1.dx = abs(lp1.x2 - lp1.x1); + lp1.dy = abs(lp1.y2 - lp1.y1); + + lp2.x1 = xmid; + lp2.y1 = ymid; + lp2.len = len2; + lp2.dx = abs(lp2.x2 - lp2.x1); + lp2.dy = abs(lp2.y2 - lp2.y1); + } + + //--------------------------------------------------------------------- + int x1, y1, x2, y2, dx, dy, sx, sy; + bool vertical; + int inc; + int len; + int octant; + + //--------------------------------------------------------------------- + static const int8u s_orthogonal_quadrant[8]; + static const int8u s_diagonal_quadrant[8]; + }; + + + + // See Implementation agg_line_aa_basics.cpp + + //----------------------------------------------------------------bisectrix + void bisectrix(const line_parameters& l1, + const line_parameters& l2, + int* x, int* y); + + + //-------------------------------------------fix_degenerate_bisectrix_start + void inline fix_degenerate_bisectrix_start(const line_parameters& lp, + int* x, int* y) + { + int d = iround((double(*x - lp.x2) * double(lp.y2 - lp.y1) - + double(*y - lp.y2) * double(lp.x2 - lp.x1)) / lp.len); + if(d < line_subpixel_scale/2) + { + *x = lp.x1 + (lp.y2 - lp.y1); + *y = lp.y1 - (lp.x2 - lp.x1); + } + } + + + //---------------------------------------------fix_degenerate_bisectrix_end + void inline fix_degenerate_bisectrix_end(const line_parameters& lp, + int* x, int* y) + { + int d = iround((double(*x - lp.x2) * double(lp.y2 - lp.y1) - + double(*y - lp.y2) * double(lp.x2 - lp.x1)) / lp.len); + if(d < line_subpixel_scale/2) + { + *x = lp.x2 + (lp.y2 - lp.y1); + *y = lp.y2 - (lp.x2 - lp.x1); + } + } + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_math.h b/desmume/src/windows/agg/include/agg_math.h new file mode 100644 index 000000000..62bc57c3c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_math.h @@ -0,0 +1,446 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// Bessel function (besj) was adapted for use in AGG library by Andy Wilk +// Contact: castor.vulgaris@gmail.com +//---------------------------------------------------------------------------- + +#ifndef AGG_MATH_INCLUDED +#define AGG_MATH_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //------------------------------------------------------vertex_dist_epsilon + // Coinciding points maximal distance (Epsilon) + const double vertex_dist_epsilon = 1e-14; + + //-----------------------------------------------------intersection_epsilon + // See calc_intersection + const double intersection_epsilon = 1.0e-30; + + //------------------------------------------------------------cross_product + AGG_INLINE double cross_product(double x1, double y1, + double x2, double y2, + double x, double y) + { + return (x - x2) * (y2 - y1) - (y - y2) * (x2 - x1); + } + + //--------------------------------------------------------point_in_triangle + AGG_INLINE bool point_in_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x, double y) + { + bool cp1 = cross_product(x1, y1, x2, y2, x, y) < 0.0; + bool cp2 = cross_product(x2, y2, x3, y3, x, y) < 0.0; + bool cp3 = cross_product(x3, y3, x1, y1, x, y) < 0.0; + return cp1 == cp2 && cp2 == cp3 && cp3 == cp1; + } + + //-----------------------------------------------------------calc_distance + AGG_INLINE double calc_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return sqrt(dx * dx + dy * dy); + } + + //--------------------------------------------------------calc_sq_distance + AGG_INLINE double calc_sq_distance(double x1, double y1, double x2, double y2) + { + double dx = x2-x1; + double dy = y2-y1; + return dx * dx + dy * dy; + } + + //------------------------------------------------calc_line_point_distance + AGG_INLINE double calc_line_point_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2-x1; + double dy = y2-y1; + double d = sqrt(dx * dx + dy * dy); + if(d < vertex_dist_epsilon) + { + return calc_distance(x1, y1, x, y); + } + return ((x - x2) * dy - (y - y2) * dx) / d; + } + + //-------------------------------------------------------calc_line_point_u + AGG_INLINE double calc_segment_point_u(double x1, double y1, + double x2, double y2, + double x, double y) + { + double dx = x2 - x1; + double dy = y2 - y1; + + if(dx == 0 && dy == 0) + { + return 0; + } + + double pdx = x - x1; + double pdy = y - y1; + + return (pdx * dx + pdy * dy) / (dx * dx + dy * dy); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y, + double u) + { + if(u <= 0) + { + return calc_sq_distance(x, y, x1, y1); + } + else + if(u >= 1) + { + return calc_sq_distance(x, y, x2, y2); + } + return calc_sq_distance(x, y, x1 + u * (x2 - x1), y1 + u * (y2 - y1)); + } + + //---------------------------------------------calc_line_point_sq_distance + AGG_INLINE double calc_segment_point_sq_distance(double x1, double y1, + double x2, double y2, + double x, double y) + { + return + calc_segment_point_sq_distance( + x1, y1, x2, y2, x, y, + calc_segment_point_u(x1, y1, x2, y2, x, y)); + } + + //-------------------------------------------------------calc_intersection + AGG_INLINE bool calc_intersection(double ax, double ay, double bx, double by, + double cx, double cy, double dx, double dy, + double* x, double* y) + { + double num = (ay-cy) * (dx-cx) - (ax-cx) * (dy-cy); + double den = (bx-ax) * (dy-cy) - (by-ay) * (dx-cx); + if(fabs(den) < intersection_epsilon) return false; + double r = num / den; + *x = ax + r * (bx-ax); + *y = ay + r * (by-ay); + return true; + } + + //-----------------------------------------------------intersection_exists + AGG_INLINE bool intersection_exists(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4) + { + // It's less expensive but you can't control the + // boundary conditions: Less or LessEqual + double dx1 = x2 - x1; + double dy1 = y2 - y1; + double dx2 = x4 - x3; + double dy2 = y4 - y3; + return ((x3 - x2) * dy1 - (y3 - y2) * dx1 < 0.0) != + ((x4 - x2) * dy1 - (y4 - y2) * dx1 < 0.0) && + ((x1 - x4) * dy2 - (y1 - y4) * dx2 < 0.0) != + ((x2 - x4) * dy2 - (y2 - y4) * dx2 < 0.0); + + // It's is more expensive but more flexible + // in terms of boundary conditions. + //-------------------- + //double den = (x2-x1) * (y4-y3) - (y2-y1) * (x4-x3); + //if(fabs(den) < intersection_epsilon) return false; + //double nom1 = (x4-x3) * (y1-y3) - (y4-y3) * (x1-x3); + //double nom2 = (x2-x1) * (y1-y3) - (y2-y1) * (x1-x3); + //double ua = nom1 / den; + //double ub = nom2 / den; + //return ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0; + } + + //--------------------------------------------------------calc_orthogonal + AGG_INLINE void calc_orthogonal(double thickness, + double x1, double y1, + double x2, double y2, + double* x, double* y) + { + double dx = x2 - x1; + double dy = y2 - y1; + double d = sqrt(dx*dx + dy*dy); + *x = thickness * dy / d; + *y = -thickness * dx / d; + } + + //--------------------------------------------------------dilate_triangle + AGG_INLINE void dilate_triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double *x, double* y, + double d) + { + double dx1=0.0; + double dy1=0.0; + double dx2=0.0; + double dy2=0.0; + double dx3=0.0; + double dy3=0.0; + double loc = cross_product(x1, y1, x2, y2, x3, y3); + if(fabs(loc) > intersection_epsilon) + { + if(cross_product(x1, y1, x2, y2, x3, y3) > 0.0) + { + d = -d; + } + calc_orthogonal(d, x1, y1, x2, y2, &dx1, &dy1); + calc_orthogonal(d, x2, y2, x3, y3, &dx2, &dy2); + calc_orthogonal(d, x3, y3, x1, y1, &dx3, &dy3); + } + *x++ = x1 + dx1; *y++ = y1 + dy1; + *x++ = x2 + dx1; *y++ = y2 + dy1; + *x++ = x2 + dx2; *y++ = y2 + dy2; + *x++ = x3 + dx2; *y++ = y3 + dy2; + *x++ = x3 + dx3; *y++ = y3 + dy3; + *x++ = x1 + dx3; *y++ = y1 + dy3; + } + + //------------------------------------------------------calc_triangle_area + AGG_INLINE double calc_triangle_area(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x1*y2 - x2*y1 + x2*y3 - x3*y2 + x3*y1 - x1*y3) * 0.5; + } + + //-------------------------------------------------------calc_polygon_area + template double calc_polygon_area(const Storage& st) + { + unsigned i; + double sum = 0.0; + double x = st[0].x; + double y = st[0].y; + double xs = x; + double ys = y; + + for(i = 1; i < st.size(); i++) + { + const typename Storage::value_type& v = st[i]; + sum += x * v.y - y * v.x; + x = v.x; + y = v.y; + } + return (sum + x * ys - y * xs) * 0.5; + } + + //------------------------------------------------------------------------ + // Tables for fast sqrt + extern int16u g_sqrt_table[1024]; + extern int8 g_elder_bit_table[256]; + + + //---------------------------------------------------------------fast_sqrt + //Fast integer Sqrt - really fast: no cycles, divisions or multiplications + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4035) //Disable warning "no return value" + #endif + AGG_INLINE unsigned fast_sqrt(unsigned val) + { + #if defined(_M_IX86) && defined(_MSC_VER) && !defined(AGG_NO_ASM) + //For Ix86 family processors this assembler code is used. + //The key command here is bsr - determination the number of the most + //significant bit of the value. For other processors + //(and maybe compilers) the pure C "#else" section is used. + __asm + { + mov ebx, val + mov edx, 11 + bsr ecx, ebx + sub ecx, 9 + jle less_than_9_bits + shr ecx, 1 + adc ecx, 0 + sub edx, ecx + shl ecx, 1 + shr ebx, cl + less_than_9_bits: + xor eax, eax + mov ax, g_sqrt_table[ebx*2] + mov ecx, edx + shr eax, cl + } + #else + + //This code is actually pure C and portable to most + //arcitectures including 64bit ones. + unsigned t = val; + int bit=0; + unsigned shift = 11; + + //The following piece of code is just an emulation of the + //Ix86 assembler command "bsr" (see above). However on old + //Intels (like Intel MMX 233MHz) this code is about twice + //faster (sic!) then just one "bsr". On PIII and PIV the + //bsr is optimized quite well. + bit = t >> 24; + if(bit) + { + bit = g_elder_bit_table[bit] + 24; + } + else + { + bit = (t >> 16) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 16; + } + else + { + bit = (t >> 8) & 0xFF; + if(bit) + { + bit = g_elder_bit_table[bit] + 8; + } + else + { + bit = g_elder_bit_table[t]; + } + } + } + + //This code calculates the sqrt. + bit -= 9; + if(bit > 0) + { + bit = (bit >> 1) + (bit & 1); + shift -= bit; + val >>= (bit << 1); + } + return g_sqrt_table[val] >> shift; + #endif + } + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + + + + //--------------------------------------------------------------------besj + // Function BESJ calculates Bessel function of first kind of order n + // Arguments: + // n - an integer (>=0), the order + // x - value at which the Bessel function is required + //-------------------- + // C++ Mathematical Library + // Convereted from equivalent FORTRAN library + // Converetd by Gareth Walker for use by course 392 computational project + // All functions tested and yield the same results as the corresponding + // FORTRAN versions. + // + // If you have any problems using these functions please report them to + // M.Muldoon@UMIST.ac.uk + // + // Documentation available on the web + // http://www.ma.umist.ac.uk/mrm/Teaching/392/libs/392.html + // Version 1.0 8/98 + // 29 October, 1999 + //-------------------- + // Adapted for use in AGG library by Andy Wilk (castor.vulgaris@gmail.com) + //------------------------------------------------------------------------ + inline double besj(double x, int n) + { + if(n < 0) + { + return 0; + } + double d = 1E-6; + double b = 0; + if(fabs(x) <= d) + { + if(n != 0) return 0; + return 1; + } + double b1 = 0; // b1 is the value from the previous iteration + // Set up a starting order for recurrence + int m1 = (int)fabs(x) + 6; + if(fabs(x) > 5) + { + m1 = (int)(fabs(1.4 * x + 60 / x)); + } + int m2 = (int)(n + 2 + fabs(x) / 4); + if (m1 > m2) + { + m2 = m1; + } + + // Apply recurrence down from curent max order + for(;;) + { + double c3 = 0; + double c2 = 1E-30; + double c4 = 0; + int m8 = 1; + if (m2 / 2 * 2 == m2) + { + m8 = -1; + } + int imax = m2 - 2; + for (int i = 1; i <= imax; i++) + { + double c6 = 2 * (m2 - i) * c2 / x - c3; + c3 = c2; + c2 = c6; + if(m2 - i - 1 == n) + { + b = c6; + } + m8 = -1 * m8; + if (m8 > 0) + { + c4 = c4 + 2 * c6; + } + } + double c6 = 2 * c2 / x - c3; + if(n == 0) + { + b = c6; + } + c4 += c6; + b /= c4; + if(fabs(b - b1) < d) + { + return b; + } + b1 = b; + m2 += 3; + } + } + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_math_stroke.h b/desmume/src/windows/agg/include/agg_math_stroke.h new file mode 100644 index 000000000..11e66b12b --- /dev/null +++ b/desmume/src/windows/agg/include/agg_math_stroke.h @@ -0,0 +1,531 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_STROKE_MATH_INCLUDED +#define AGG_STROKE_MATH_INCLUDED + +#include "agg_math.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + //-------------------------------------------------------------line_cap_e + enum line_cap_e + { + butt_cap, + square_cap, + round_cap + }; + + //------------------------------------------------------------line_join_e + enum line_join_e + { + miter_join = 0, + miter_join_revert = 1, + round_join = 2, + bevel_join = 3, + miter_join_round = 4 + }; + + + //-----------------------------------------------------------inner_join_e + enum inner_join_e + { + inner_bevel, + inner_miter, + inner_jag, + inner_round + }; + + //------------------------------------------------------------math_stroke + template class math_stroke + { + public: + typedef typename VertexConsumer::value_type coord_type; + + math_stroke(); + + void line_cap(line_cap_e lc) { m_line_cap = lc; } + void line_join(line_join_e lj) { m_line_join = lj; } + void inner_join(inner_join_e ij) { m_inner_join = ij; } + + line_cap_e line_cap() const { return m_line_cap; } + line_join_e line_join() const { return m_line_join; } + inner_join_e inner_join() const { return m_inner_join; } + + void width(double w); + void miter_limit(double ml) { m_miter_limit = ml; } + void miter_limit_theta(double t); + void inner_miter_limit(double ml) { m_inner_miter_limit = ml; } + void approximation_scale(double as) { m_approx_scale = as; } + + double width() const { return m_width * 2.0; } + double miter_limit() const { return m_miter_limit; } + double inner_miter_limit() const { return m_inner_miter_limit; } + double approximation_scale() const { return m_approx_scale; } + + void calc_cap(VertexConsumer& vc, + const vertex_dist& v0, + const vertex_dist& v1, + double len); + + void calc_join(VertexConsumer& vc, + const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + double len1, + double len2); + + private: + AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y) + { + vc.add(coord_type(x, y)); + } + + void calc_arc(VertexConsumer& vc, + double x, double y, + double dx1, double dy1, + double dx2, double dy2); + + void calc_miter(VertexConsumer& vc, + const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + double dx1, double dy1, + double dx2, double dy2, + line_join_e lj, + double mlimit, + double dbevel); + + double m_width; + double m_width_abs; + double m_width_eps; + int m_width_sign; + double m_miter_limit; + double m_inner_miter_limit; + double m_approx_scale; + line_cap_e m_line_cap; + line_join_e m_line_join; + inner_join_e m_inner_join; + }; + + //----------------------------------------------------------------------- + template math_stroke::math_stroke() : + m_width(0.5), + m_width_abs(0.5), + m_width_eps(0.5/1024.0), + m_width_sign(1), + m_miter_limit(4.0), + m_inner_miter_limit(1.01), + m_approx_scale(1.0), + m_line_cap(butt_cap), + m_line_join(miter_join), + m_inner_join(inner_miter) + { + } + + //----------------------------------------------------------------------- + template void math_stroke::width(double w) + { + m_width = w * 0.5; + if(m_width < 0) + { + m_width_abs = -m_width; + m_width_sign = -1; + } + else + { + m_width_abs = m_width; + m_width_sign = 1; + } + m_width_eps = m_width / 1024.0; + } + + //----------------------------------------------------------------------- + template void math_stroke::miter_limit_theta(double t) + { + m_miter_limit = 1.0 / sin(t * 0.5) ; + } + + //----------------------------------------------------------------------- + template + void math_stroke::calc_arc(VC& vc, + double x, double y, + double dx1, double dy1, + double dx2, double dy2) + { + double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign); + double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign); + double da = a1 - a2; + int i, n; + + da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2; + + add_vertex(vc, x + dx1, y + dy1); + if(m_width_sign > 0) + { + if(a1 > a2) a2 += 2 * pi; + n = int((a2 - a1) / da); + da = (a2 - a1) / (n + 1); + a1 += da; + for(i = 0; i < n; i++) + { + add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width); + a1 += da; + } + } + else + { + if(a1 < a2) a2 -= 2 * pi; + n = int((a1 - a2) / da); + da = (a1 - a2) / (n + 1); + a1 -= da; + for(i = 0; i < n; i++) + { + add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width); + a1 -= da; + } + } + add_vertex(vc, x + dx2, y + dy2); + } + + //----------------------------------------------------------------------- + template + void math_stroke::calc_miter(VC& vc, + const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + double dx1, double dy1, + double dx2, double dy2, + line_join_e lj, + double mlimit, + double dbevel) + { + double xi = v1.x; + double yi = v1.y; + double di = 1; + double lim = m_width_abs * mlimit; + bool miter_limit_exceeded = true; // Assume the worst + bool intersection_failed = true; // Assume the worst + + if(calc_intersection(v0.x + dx1, v0.y - dy1, + v1.x + dx1, v1.y - dy1, + v1.x + dx2, v1.y - dy2, + v2.x + dx2, v2.y - dy2, + &xi, &yi)) + { + // Calculation of the intersection succeeded + //--------------------- + di = calc_distance(v1.x, v1.y, xi, yi); + if(di <= lim) + { + // Inside the miter limit + //--------------------- + add_vertex(vc, xi, yi); + miter_limit_exceeded = false; + } + intersection_failed = false; + } + else + { + // Calculation of the intersection failed, most probably + // the three points lie one straight line. + // First check if v0 and v2 lie on the opposite sides of vector: + // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular + // to the line determined by vertices v0 and v1. + // This condition determines whether the next line segments continues + // the previous one or goes back. + //---------------- + double x2 = v1.x + dx1; + double y2 = v1.y - dy1; + if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) == + (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0)) + { + // This case means that the next segment continues + // the previous one (straight line) + //----------------- + add_vertex(vc, v1.x + dx1, v1.y - dy1); + miter_limit_exceeded = false; + } + } + + if(miter_limit_exceeded) + { + // Miter limit exceeded + //------------------------ + switch(lj) + { + case miter_join_revert: + // For the compatibility with SVG, PDF, etc, + // we use a simple bevel join instead of + // "smart" bevel + //------------------- + add_vertex(vc, v1.x + dx1, v1.y - dy1); + add_vertex(vc, v1.x + dx2, v1.y - dy2); + break; + + case miter_join_round: + calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2); + break; + + default: + // If no miter-revert, calculate new dx1, dy1, dx2, dy2 + //---------------- + if(intersection_failed) + { + mlimit *= m_width_sign; + add_vertex(vc, v1.x + dx1 + dy1 * mlimit, + v1.y - dy1 + dx1 * mlimit); + add_vertex(vc, v1.x + dx2 - dy2 * mlimit, + v1.y - dy2 - dx2 * mlimit); + } + else + { + double x1 = v1.x + dx1; + double y1 = v1.y - dy1; + double x2 = v1.x + dx2; + double y2 = v1.y - dy2; + di = (lim - dbevel) / (di - dbevel); + add_vertex(vc, x1 + (xi - x1) * di, + y1 + (yi - y1) * di); + add_vertex(vc, x2 + (xi - x2) * di, + y2 + (yi - y2) * di); + } + break; + } + } + } + + //--------------------------------------------------------stroke_calc_cap + template + void math_stroke::calc_cap(VC& vc, + const vertex_dist& v0, + const vertex_dist& v1, + double len) + { + vc.remove_all(); + + double dx1 = (v1.y - v0.y) / len; + double dy1 = (v1.x - v0.x) / len; + double dx2 = 0; + double dy2 = 0; + + dx1 *= m_width; + dy1 *= m_width; + + if(m_line_cap != round_cap) + { + if(m_line_cap == square_cap) + { + dx2 = dy1 * m_width_sign; + dy2 = dx1 * m_width_sign; + } + add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2); + add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2); + } + else + { + double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2; + double a1; + int i; + int n = int(pi / da); + + da = pi / (n + 1); + add_vertex(vc, v0.x - dx1, v0.y + dy1); + if(m_width_sign > 0) + { + a1 = atan2(dy1, -dx1); + a1 += da; + for(i = 0; i < n; i++) + { + add_vertex(vc, v0.x + cos(a1) * m_width, + v0.y + sin(a1) * m_width); + a1 += da; + } + } + else + { + a1 = atan2(-dy1, dx1); + a1 -= da; + for(i = 0; i < n; i++) + { + add_vertex(vc, v0.x + cos(a1) * m_width, + v0.y + sin(a1) * m_width); + a1 -= da; + } + } + add_vertex(vc, v0.x + dx1, v0.y - dy1); + } + } + + //----------------------------------------------------------------------- + template + void math_stroke::calc_join(VC& vc, + const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + double len1, + double len2) + { + double dx1 = m_width * (v1.y - v0.y) / len1; + double dy1 = m_width * (v1.x - v0.x) / len1; + double dx2 = m_width * (v2.y - v1.y) / len2; + double dy2 = m_width * (v2.x - v1.x) / len2; + + vc.remove_all(); + + double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y); + if(cp != 0 && (cp > 0) == (m_width > 0)) + { + // Inner join + //--------------- + double limit = ((len1 < len2) ? len1 : len2) / m_width_abs; + if(limit < m_inner_miter_limit) + { + limit = m_inner_miter_limit; + } + + switch(m_inner_join) + { + default: // inner_bevel + add_vertex(vc, v1.x + dx1, v1.y - dy1); + add_vertex(vc, v1.x + dx2, v1.y - dy2); + break; + + case inner_miter: + calc_miter(vc, + v0, v1, v2, dx1, dy1, dx2, dy2, + miter_join_revert, + limit, 0); + break; + + case inner_jag: + case inner_round: + cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2); + if(cp < len1 * len1 && cp < len2 * len2) + { + calc_miter(vc, + v0, v1, v2, dx1, dy1, dx2, dy2, + miter_join_revert, + limit, 0); + } + else + { + if(m_inner_join == inner_jag) + { + add_vertex(vc, v1.x + dx1, v1.y - dy1); + add_vertex(vc, v1.x, v1.y ); + add_vertex(vc, v1.x + dx2, v1.y - dy2); + } + else + { + add_vertex(vc, v1.x + dx1, v1.y - dy1); + add_vertex(vc, v1.x, v1.y ); + calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1); + add_vertex(vc, v1.x, v1.y ); + add_vertex(vc, v1.x + dx2, v1.y - dy2); + } + } + break; + } + } + else + { + // Outer join + //--------------- + + // Calculate the distance between v1 and + // the central point of the bevel line segment + //--------------- + double dx = (dx1 + dx2) / 2; + double dy = (dy1 + dy2) / 2; + double dbevel = sqrt(dx * dx + dy * dy); + + if(m_line_join == round_join || m_line_join == bevel_join) + { + // This is an optimization that reduces the number of points + // in cases of almost collinear segments. If there's no + // visible difference between bevel and miter joins we'd rather + // use miter join because it adds only one point instead of two. + // + // Here we calculate the middle point between the bevel points + // and then, the distance between v1 and this middle point. + // At outer joins this distance always less than stroke width, + // because it's actually the height of an isosceles triangle of + // v1 and its two bevel points. If the difference between this + // width and this value is small (no visible bevel) we can + // add just one point. + // + // The constant in the expression makes the result approximately + // the same as in round joins and caps. You can safely comment + // out this entire "if". + //------------------- + if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps) + { + if(calc_intersection(v0.x + dx1, v0.y - dy1, + v1.x + dx1, v1.y - dy1, + v1.x + dx2, v1.y - dy2, + v2.x + dx2, v2.y - dy2, + &dx, &dy)) + { + add_vertex(vc, dx, dy); + } + else + { + add_vertex(vc, v1.x + dx1, v1.y - dy1); + } + return; + } + } + + switch(m_line_join) + { + case miter_join: + case miter_join_revert: + case miter_join_round: + calc_miter(vc, + v0, v1, v2, dx1, dy1, dx2, dy2, + m_line_join, + m_miter_limit, + dbevel); + break; + + case round_join: + calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2); + break; + + default: // Bevel join + add_vertex(vc, v1.x + dx1, v1.y - dy1); + add_vertex(vc, v1.x + dx2, v1.y - dy2); + break; + } + } + } + + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_path_length.h b/desmume/src/windows/agg/include/agg_path_length.h new file mode 100644 index 000000000..487959e1f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_path_length.h @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PATH_LENGTH_INCLUDED +#define AGG_PATH_LENGTH_INCLUDED + +#include "agg_math.h" + +namespace agg +{ + template + double path_length(VertexSource& vs, unsigned path_id = 0) + { + double len = 0.0; + double start_x = 0.0; + double start_y = 0.0; + double x1 = 0.0; + double y1 = 0.0; + double x2 = 0.0; + double y2 = 0.0; + bool first = true; + + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x2, &y2))) + { + if(is_vertex(cmd)) + { + if(first || is_move_to(cmd)) + { + start_x = x2; + start_y = y2; + } + else + { + len += calc_distance(x1, y1, x2, y2); + } + x1 = x2; + y1 = y2; + first = false; + } + else + { + if(is_close(cmd) && !first) + { + len += calc_distance(x1, y1, start_x, start_y); + } + } + } + return len; + } +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_path_storage.h b/desmume/src/windows/agg/include/agg_path_storage.h new file mode 100644 index 000000000..e8cb7ab48 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_path_storage.h @@ -0,0 +1,1554 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PATH_STORAGE_INCLUDED +#define AGG_PATH_STORAGE_INCLUDED + +#include +#include +#include "agg_math.h" +#include "agg_array.h" +#include "agg_bezier_arc.h" + +namespace agg +{ + + + //----------------------------------------------------vertex_block_storage + template + class vertex_block_storage + { + public: + // Allocation parameters + enum block_scale_e + { + block_shift = BlockShift, + block_size = 1 << block_shift, + block_mask = block_size - 1, + block_pool = BlockPool + }; + + typedef T value_type; + typedef vertex_block_storage self_type; + + ~vertex_block_storage(); + vertex_block_storage(); + vertex_block_storage(const self_type& v); + const self_type& operator = (const self_type& ps); + + void remove_all(); + void free_all(); + + void add_vertex(double x, double y, unsigned cmd); + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + void swap_vertices(unsigned v1, unsigned v2); + + unsigned last_command() const; + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned total_vertices() const; + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + private: + void allocate_block(unsigned nb); + int8u* storage_ptrs(T** xy_ptr); + + private: + unsigned m_total_vertices; + unsigned m_total_blocks; + unsigned m_max_blocks; + T** m_coord_blocks; + int8u** m_cmd_blocks; + }; + + + //------------------------------------------------------------------------ + template + void vertex_block_storage::free_all() + { + if(m_total_blocks) + { + T** coord_blk = m_coord_blocks + m_total_blocks - 1; + while(m_total_blocks--) + { + pod_allocator::deallocate( + *coord_blk, + block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + --coord_blk; + } + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + m_total_blocks = 0; + m_max_blocks = 0; + m_coord_blocks = 0; + m_cmd_blocks = 0; + m_total_vertices = 0; + } + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::~vertex_block_storage() + { + free_all(); + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage() : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + } + + //------------------------------------------------------------------------ + template + vertex_block_storage::vertex_block_storage(const vertex_block_storage& v) : + m_total_vertices(0), + m_total_blocks(0), + m_max_blocks(0), + m_coord_blocks(0), + m_cmd_blocks(0) + { + *this = v; + } + + //------------------------------------------------------------------------ + template + const vertex_block_storage& + vertex_block_storage::operator = (const vertex_block_storage& v) + { + remove_all(); + unsigned i; + for(i = 0; i < v.total_vertices(); i++) + { + double x, y; + unsigned cmd = v.vertex(i, &x, &y); + add_vertex(x, y, cmd); + } + return *this; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::remove_all() + { + m_total_vertices = 0; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::add_vertex(double x, double y, + unsigned cmd) + { + T* coord_ptr = 0; + *storage_ptrs(&coord_ptr) = (int8u)cmd; + coord_ptr[0] = T(x); + coord_ptr[1] = T(y); + m_total_vertices++; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y) + { + T* pv = m_coord_blocks[idx >> block_shift] + ((idx & block_mask) << 1); + pv[0] = T(x); + pv[1] = T(y); + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_vertex(unsigned idx, + double x, double y, + unsigned cmd) + { + unsigned block = idx >> block_shift; + unsigned offset = idx & block_mask; + T* pv = m_coord_blocks[block] + (offset << 1); + pv[0] = T(x); + pv[1] = T(y); + m_cmd_blocks[block][offset] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::modify_command(unsigned idx, + unsigned cmd) + { + m_cmd_blocks[idx >> block_shift][idx & block_mask] = (int8u)cmd; + } + + //------------------------------------------------------------------------ + template + inline void vertex_block_storage::swap_vertices(unsigned v1, unsigned v2) + { + unsigned b1 = v1 >> block_shift; + unsigned b2 = v2 >> block_shift; + unsigned o1 = v1 & block_mask; + unsigned o2 = v2 & block_mask; + T* pv1 = m_coord_blocks[b1] + (o1 << 1); + T* pv2 = m_coord_blocks[b2] + (o2 << 1); + T val; + val = pv1[0]; pv1[0] = pv2[0]; pv2[0] = val; + val = pv1[1]; pv1[1] = pv2[1]; pv2[1] = val; + int8u cmd = m_cmd_blocks[b1][o1]; + m_cmd_blocks[b1][o1] = m_cmd_blocks[b2][o2]; + m_cmd_blocks[b2][o2] = cmd; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_command() const + { + if(m_total_vertices) return command(m_total_vertices - 1); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::last_vertex(double* x, double* y) const + { + if(m_total_vertices) return vertex(m_total_vertices - 1, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::prev_vertex(double* x, double* y) const + { + if(m_total_vertices > 1) return vertex(m_total_vertices - 2, x, y); + return path_cmd_stop; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_x() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][(idx & block_mask) << 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline double vertex_block_storage::last_y() const + { + if(m_total_vertices) + { + unsigned idx = m_total_vertices - 1; + return m_coord_blocks[idx >> block_shift][((idx & block_mask) << 1) + 1]; + } + return 0.0; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::total_vertices() const + { + return m_total_vertices; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::vertex(unsigned idx, + double* x, double* y) const + { + unsigned nb = idx >> block_shift; + const T* pv = m_coord_blocks[nb] + ((idx & block_mask) << 1); + *x = pv[0]; + *y = pv[1]; + return m_cmd_blocks[nb][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + inline unsigned vertex_block_storage::command(unsigned idx) const + { + return m_cmd_blocks[idx >> block_shift][idx & block_mask]; + } + + //------------------------------------------------------------------------ + template + void vertex_block_storage::allocate_block(unsigned nb) + { + if(nb >= m_max_blocks) + { + T** new_coords = + pod_allocator::allocate((m_max_blocks + block_pool) * 2); + + unsigned char** new_cmds = + (unsigned char**)(new_coords + m_max_blocks + block_pool); + + if(m_coord_blocks) + { + memcpy(new_coords, + m_coord_blocks, + m_max_blocks * sizeof(T*)); + + memcpy(new_cmds, + m_cmd_blocks, + m_max_blocks * sizeof(unsigned char*)); + + pod_allocator::deallocate(m_coord_blocks, m_max_blocks * 2); + } + m_coord_blocks = new_coords; + m_cmd_blocks = new_cmds; + m_max_blocks += block_pool; + } + m_coord_blocks[nb] = + pod_allocator::allocate(block_size * 2 + + block_size / (sizeof(T) / sizeof(unsigned char))); + + m_cmd_blocks[nb] = + (unsigned char*)(m_coord_blocks[nb] + block_size * 2); + + m_total_blocks++; + } + + //------------------------------------------------------------------------ + template + int8u* vertex_block_storage::storage_ptrs(T** xy_ptr) + { + unsigned nb = m_total_vertices >> block_shift; + if(nb >= m_total_blocks) + { + allocate_block(nb); + } + *xy_ptr = m_coord_blocks[nb] + ((m_total_vertices & block_mask) << 1); + return m_cmd_blocks[nb] + (m_total_vertices & block_mask); + } + + + + + //-----------------------------------------------------poly_plain_adaptor + template class poly_plain_adaptor + { + public: + typedef T value_type; + + poly_plain_adaptor() : + m_data(0), + m_ptr(0), + m_end(0), + m_closed(false), + m_stop(false) + {} + + poly_plain_adaptor(const T* data, unsigned num_points, bool closed) : + m_data(data), + m_ptr(data), + m_end(data + num_points * 2), + m_closed(closed), + m_stop(false) + {} + + void init(const T* data, unsigned num_points, bool closed) + { + m_data = data; + m_ptr = data; + m_end = data + num_points * 2; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_ptr = m_data; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_ptr < m_end) + { + bool first = m_ptr == m_data; + *x = *m_ptr++; + *y = *m_ptr++; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const T* m_data; + const T* m_ptr; + const T* m_end; + bool m_closed; + bool m_stop; + }; + + + + + + //-------------------------------------------------poly_container_adaptor + template class poly_container_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_adaptor() : + m_container(0), + m_index(0), + m_closed(false), + m_stop(false) + {} + + poly_container_adaptor(const Container& data, bool closed) : + m_container(&data), + m_index(0), + m_closed(closed), + m_stop(false) + {} + + void init(const Container& data, bool closed) + { + m_container = &data; + m_index = 0; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = 0; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index < m_container->size()) + { + bool first = m_index == 0; + const vertex_type& v = (*m_container)[m_index++]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const Container* m_container; + unsigned m_index; + bool m_closed; + bool m_stop; + }; + + + + //-----------------------------------------poly_container_reverse_adaptor + template class poly_container_reverse_adaptor + { + public: + typedef typename Container::value_type vertex_type; + + poly_container_reverse_adaptor() : + m_container(0), + m_index(-1), + m_closed(false), + m_stop(false) + {} + + poly_container_reverse_adaptor(const Container& data, bool closed) : + m_container(&data), + m_index(-1), + m_closed(closed), + m_stop(false) + {} + + void init(const Container& data, bool closed) + { + m_container = &data; + m_index = m_container->size() - 1; + m_closed = closed; + m_stop = false; + } + + void rewind(unsigned) + { + m_index = m_container->size() - 1; + m_stop = false; + } + + unsigned vertex(double* x, double* y) + { + if(m_index >= 0) + { + bool first = m_index == int(m_container->size() - 1); + const vertex_type& v = (*m_container)[m_index--]; + *x = v.x; + *y = v.y; + return first ? path_cmd_move_to : path_cmd_line_to; + } + *x = *y = 0.0; + if(m_closed && !m_stop) + { + m_stop = true; + return path_cmd_end_poly | path_flags_close; + } + return path_cmd_stop; + } + + private: + const Container* m_container; + int m_index; + bool m_closed; + bool m_stop; + }; + + + + + + //--------------------------------------------------------line_adaptor + class line_adaptor + { + public: + typedef double value_type; + + line_adaptor() : m_line(m_coord, 2, false) {} + line_adaptor(double x1, double y1, double x2, double y2) : + m_line(m_coord, 2, false) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + } + + void init(double x1, double y1, double x2, double y2) + { + m_coord[0] = x1; + m_coord[1] = y1; + m_coord[2] = x2; + m_coord[3] = y2; + m_line.rewind(0); + } + + void rewind(unsigned) + { + m_line.rewind(0); + } + + unsigned vertex(double* x, double* y) + { + return m_line.vertex(x, y); + } + + private: + double m_coord[4]; + poly_plain_adaptor m_line; + }; + + + + + + + + + + + + + + //---------------------------------------------------------------path_base + // A container to store vertices with their flags. + // A path consists of a number of contours separated with "move_to" + // commands. The path storage can keep and maintain more than one + // path. + // To navigate to the beginning of a particular path, use rewind(path_id); + // Where path_id is what start_new_path() returns. So, when you call + // start_new_path() you need to store its return value somewhere else + // to navigate to the path afterwards. + // + // See also: vertex_source concept + //------------------------------------------------------------------------ + template class path_base + { + public: + typedef VertexContainer container_type; + typedef path_base self_type; + + //-------------------------------------------------------------------- + path_base() : m_vertices(), m_iterator(0) {} + void remove_all() { m_vertices.remove_all(); m_iterator = 0; } + void free_all() { m_vertices.free_all(); m_iterator = 0; } + + // Make path functions + //-------------------------------------------------------------------- + unsigned start_new_path(); + + void move_to(double x, double y); + void move_rel(double dx, double dy); + + void line_to(double x, double y); + void line_rel(double dx, double dy); + + void hline_to(double x); + void hline_rel(double dx); + + void vline_to(double y); + void vline_rel(double dy); + + void arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y); + + void arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy); + + void curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to); + + void curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to); + + void curve3(double x_to, double y_to); + + void curve3_rel(double dx_to, double dy_to); + + void curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to); + + void curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + void curve4_rel(double x_ctrl2, double y_ctrl2, + double x_to, double y_to); + + + void end_poly(unsigned flags = path_flags_close); + void close_polygon(unsigned flags = path_flags_none); + + // Accessors + //-------------------------------------------------------------------- + const container_type& vertices() const { return m_vertices; } + container_type& vertices() { return m_vertices; } + + unsigned total_vertices() const; + + void rel_to_abs(double* x, double* y) const; + + unsigned last_vertex(double* x, double* y) const; + unsigned prev_vertex(double* x, double* y) const; + + double last_x() const; + double last_y() const; + + unsigned vertex(unsigned idx, double* x, double* y) const; + unsigned command(unsigned idx) const; + + void modify_vertex(unsigned idx, double x, double y); + void modify_vertex(unsigned idx, double x, double y, unsigned cmd); + void modify_command(unsigned idx, unsigned cmd); + + // VertexSource interface + //-------------------------------------------------------------------- + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + // Arrange the orientation of a polygon, all polygons in a path, + // or in all paths. After calling arrange_orientations() or + // arrange_orientations_all_paths(), all the polygons will have + // the same orientation, i.e. path_flags_cw or path_flags_ccw + //-------------------------------------------------------------------- + unsigned arrange_polygon_orientation(unsigned start, path_flags_e orientation); + unsigned arrange_orientations(unsigned path_id, path_flags_e orientation); + void arrange_orientations_all_paths(path_flags_e orientation); + void invert_polygon(unsigned start); + + // Flip all vertices horizontally or vertically, + // between x1 and x2, or between y1 and y2 respectively + //-------------------------------------------------------------------- + void flip_x(double x1, double x2); + void flip_y(double y1, double y2); + + // Concatenate path. The path is added as is. + //-------------------------------------------------------------------- + template + void concat_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + // Join path. The path is joined with the existing one, that is, + // it behaves as if the pen of a plotter was always down (drawing) + template + void join_path(VertexSource& vs, unsigned path_id = 0) + { + double x, y; + unsigned cmd; + vs.rewind(path_id); + cmd = vs.vertex(&x, &y); + if(!is_stop(cmd)) + { + if(is_vertex(cmd)) + { + double x0, y0; + unsigned cmd0 = last_vertex(&x0, &y0); + if(is_vertex(cmd0)) + { + if(calc_distance(x, y, x0, y0) > vertex_dist_epsilon) + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + m_vertices.add_vertex(x, y, cmd); + } + } + else + { + if(is_stop(cmd0)) + { + cmd = path_cmd_move_to; + } + else + { + if(is_move_to(cmd)) cmd = path_cmd_line_to; + } + m_vertices.add_vertex(x, y, cmd); + } + } + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + m_vertices.add_vertex(x, y, is_move_to(cmd) ? + unsigned(path_cmd_line_to) : + cmd); + } + } + } + + // Concatenate polygon/polyline. + //-------------------------------------------------------------------- + template void concat_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + concat_path(poly); + } + + // Join polygon/polyline continuously. + //-------------------------------------------------------------------- + template void join_poly(const T* data, + unsigned num_points, + bool closed) + { + poly_plain_adaptor poly(data, num_points, closed); + join_path(poly); + } + + //-------------------------------------------------------------------- + void translate(double dx, double dy, unsigned path_id=0); + void translate_all_paths(double dx, double dy); + + //-------------------------------------------------------------------- + template + void transform(const Trans& trans, unsigned path_id=0) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //-------------------------------------------------------------------- + template + void transform_all_paths(const Trans& trans) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + trans.transform(&x, &y); + m_vertices.modify_vertex(idx, x, y); + } + } + } + + + + private: + unsigned perceive_polygon_orientation(unsigned start, unsigned end); + void invert_polygon(unsigned start, unsigned end); + + VertexContainer m_vertices; + unsigned m_iterator; + }; + + //------------------------------------------------------------------------ + template + unsigned path_base::start_new_path() + { + if(!is_stop(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_stop); + } + return m_vertices.total_vertices(); + } + + + //------------------------------------------------------------------------ + template + inline void path_base::rel_to_abs(double* x, double* y) const + { + if(m_vertices.total_vertices()) + { + double x2; + double y2; + if(is_vertex(m_vertices.last_vertex(&x2, &y2))) + { + *x += x2; + *y += y2; + } + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::move_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_move_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_to(double x, double y) + { + m_vertices.add_vertex(x, y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::line_rel(double dx, double dy) + { + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_to(double x) + { + m_vertices.add_vertex(x, last_y(), path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::hline_rel(double dx) + { + double dy = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_to(double y) + { + m_vertices.add_vertex(last_x(), y, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::vline_rel(double dy) + { + double dx = 0; + rel_to_abs(&dx, &dy); + m_vertices.add_vertex(dx, dy, path_cmd_line_to); + } + + //------------------------------------------------------------------------ + template + void path_base::arc_to(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x, double y) + { + if(m_vertices.total_vertices() && is_vertex(m_vertices.last_command())) + { + const double epsilon = 1e-30; + double x0 = 0.0; + double y0 = 0.0; + m_vertices.last_vertex(&x0, &y0); + + rx = fabs(rx); + ry = fabs(ry); + + // Ensure radii are valid + //------------------------- + if(rx < epsilon || ry < epsilon) + { + line_to(x, y); + return; + } + + if(calc_distance(x0, y0, x, y) < epsilon) + { + // If the endpoints (x, y) and (x0, y0) are identical, then this + // is equivalent to omitting the elliptical arc segment entirely. + return; + } + bezier_arc_svg a(x0, y0, rx, ry, angle, large_arc_flag, sweep_flag, x, y); + if(a.radii_ok()) + { + join_path(a); + } + else + { + line_to(x, y); + } + } + else + { + move_to(x, y); + } + } + + //------------------------------------------------------------------------ + template + void path_base::arc_rel(double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double dx, double dy) + { + rel_to_abs(&dx, &dy); + arc_to(rx, ry, angle, large_arc_flag, sweep_flag, dx, dy); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_ctrl, double y_ctrl, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl, y_ctrl, path_cmd_curve3); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_ctrl, double dy_ctrl, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl, &dy_ctrl); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl, dy_ctrl, path_cmd_curve3); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve3); + } + + //------------------------------------------------------------------------ + template + void path_base::curve3(double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(m_vertices.last_vertex(&x0, &y0))) + { + double x_ctrl; + double y_ctrl; + unsigned cmd = m_vertices.prev_vertex(&x_ctrl, &y_ctrl); + if(is_curve(cmd)) + { + x_ctrl = x0 + x0 - x_ctrl; + y_ctrl = y0 + y0 - y_ctrl; + } + else + { + x_ctrl = x0; + y_ctrl = y0; + } + curve3(x_ctrl, y_ctrl, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve3_rel(double dx_to, double dy_to) + { + rel_to_abs(&dx_to, &dy_to); + curve3(dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl1, double y_ctrl1, + double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + m_vertices.add_vertex(x_ctrl1, y_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(x_ctrl2, y_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(x_to, y_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl1, double dy_ctrl1, + double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl1, &dy_ctrl1); + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + m_vertices.add_vertex(dx_ctrl1, dy_ctrl1, path_cmd_curve4); + m_vertices.add_vertex(dx_ctrl2, dy_ctrl2, path_cmd_curve4); + m_vertices.add_vertex(dx_to, dy_to, path_cmd_curve4); + } + + //------------------------------------------------------------------------ + template + void path_base::curve4(double x_ctrl2, double y_ctrl2, + double x_to, double y_to) + { + double x0; + double y0; + if(is_vertex(last_vertex(&x0, &y0))) + { + double x_ctrl1; + double y_ctrl1; + unsigned cmd = prev_vertex(&x_ctrl1, &y_ctrl1); + if(is_curve(cmd)) + { + x_ctrl1 = x0 + x0 - x_ctrl1; + y_ctrl1 = y0 + y0 - y_ctrl1; + } + else + { + x_ctrl1 = x0; + y_ctrl1 = y0; + } + curve4(x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to); + } + } + + //------------------------------------------------------------------------ + template + void path_base::curve4_rel(double dx_ctrl2, double dy_ctrl2, + double dx_to, double dy_to) + { + rel_to_abs(&dx_ctrl2, &dy_ctrl2); + rel_to_abs(&dx_to, &dy_to); + curve4(dx_ctrl2, dy_ctrl2, dx_to, dy_to); + } + + //------------------------------------------------------------------------ + template + inline void path_base::end_poly(unsigned flags) + { + if(is_vertex(m_vertices.last_command())) + { + m_vertices.add_vertex(0.0, 0.0, path_cmd_end_poly | flags); + } + } + + //------------------------------------------------------------------------ + template + inline void path_base::close_polygon(unsigned flags) + { + end_poly(path_flags_close | flags); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::total_vertices() const + { + return m_vertices.total_vertices(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::last_vertex(double* x, double* y) const + { + return m_vertices.last_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::prev_vertex(double* x, double* y) const + { + return m_vertices.prev_vertex(x, y); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_x() const + { + return m_vertices.last_x(); + } + + //------------------------------------------------------------------------ + template + inline double path_base::last_y() const + { + return m_vertices.last_y(); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(unsigned idx, double* x, double* y) const + { + return m_vertices.vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::command(unsigned idx) const + { + return m_vertices.command(idx); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y) + { + m_vertices.modify_vertex(idx, x, y); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + m_vertices.modify_vertex(idx, x, y, cmd); + } + + //------------------------------------------------------------------------ + template + void path_base::modify_command(unsigned idx, unsigned cmd) + { + m_vertices.modify_command(idx, cmd); + } + + //------------------------------------------------------------------------ + template + inline void path_base::rewind(unsigned path_id) + { + m_iterator = path_id; + } + + //------------------------------------------------------------------------ + template + inline unsigned path_base::vertex(double* x, double* y) + { + if(m_iterator >= m_vertices.total_vertices()) return path_cmd_stop; + return m_vertices.vertex(m_iterator++, x, y); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::perceive_polygon_orientation(unsigned start, + unsigned end) + { + // Calculate signed area (double area to be exact) + //--------------------- + unsigned np = end - start; + double area = 0.0; + unsigned i; + for(i = 0; i < np; i++) + { + double x1, y1, x2, y2; + m_vertices.vertex(start + i, &x1, &y1); + m_vertices.vertex(start + (i + 1) % np, &x2, &y2); + area += x1 * y2 - y1 * x2; + } + return (area < 0.0) ? path_flags_cw : path_flags_ccw; + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start, unsigned end) + { + unsigned i; + unsigned tmp_cmd = m_vertices.command(start); + + --end; // Make "end" inclusive + + // Shift all commands to one position + for(i = start; i < end; i++) + { + m_vertices.modify_command(i, m_vertices.command(i + 1)); + } + + // Assign starting command to the ending command + m_vertices.modify_command(end, tmp_cmd); + + // Reverse the polygon + while(end > start) + { + m_vertices.swap_vertices(start++, end--); + } + } + + //------------------------------------------------------------------------ + template + void path_base::invert_polygon(unsigned start) + { + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + invert_polygon(start, end); + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_polygon_orientation(unsigned start, + path_flags_e orientation) + { + if(orientation == path_flags_none) return start; + + // Skip all non-vertices at the beginning + while(start < m_vertices.total_vertices() && + !is_vertex(m_vertices.command(start))) ++start; + + // Skip all insignificant move_to + while(start+1 < m_vertices.total_vertices() && + is_move_to(m_vertices.command(start)) && + is_move_to(m_vertices.command(start+1))) ++start; + + // Find the last vertex + unsigned end = start + 1; + while(end < m_vertices.total_vertices() && + !is_next_poly(m_vertices.command(end))) ++end; + + if(end - start > 2) + { + if(perceive_polygon_orientation(start, end) != unsigned(orientation)) + { + // Invert polygon, set orientation flag, and skip all end_poly + invert_polygon(start, end); + unsigned cmd; + while(end < m_vertices.total_vertices() && + is_end_poly(cmd = m_vertices.command(end))) + { + m_vertices.modify_command(end++, set_orientation(cmd, orientation)); + } + } + } + return end; + } + + //------------------------------------------------------------------------ + template + unsigned path_base::arrange_orientations(unsigned start, + path_flags_e orientation) + { + if(orientation != path_flags_none) + { + while(start < m_vertices.total_vertices()) + { + start = arrange_polygon_orientation(start, orientation); + if(is_stop(m_vertices.command(start))) + { + ++start; + break; + } + } + } + return start; + } + + //------------------------------------------------------------------------ + template + void path_base::arrange_orientations_all_paths(path_flags_e orientation) + { + if(orientation != path_flags_none) + { + unsigned start = 0; + while(start < m_vertices.total_vertices()) + { + start = arrange_orientations(start, orientation); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_x(double x1, double x2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); i++) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x2 - x + x1, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::flip_y(double y1, double y2) + { + unsigned i; + double x, y; + for(i = 0; i < m_vertices.total_vertices(); i++) + { + unsigned cmd = m_vertices.vertex(i, &x, &y); + if(is_vertex(cmd)) + { + m_vertices.modify_vertex(i, x, y2 - y + y1); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate(double dx, double dy, unsigned path_id) + { + unsigned num_ver = m_vertices.total_vertices(); + for(; path_id < num_ver; path_id++) + { + double x, y; + unsigned cmd = m_vertices.vertex(path_id, &x, &y); + if(is_stop(cmd)) break; + if(is_vertex(cmd)) + { + x += dx; + y += dy; + m_vertices.modify_vertex(path_id, x, y); + } + } + } + + //------------------------------------------------------------------------ + template + void path_base::translate_all_paths(double dx, double dy) + { + unsigned idx; + unsigned num_ver = m_vertices.total_vertices(); + for(idx = 0; idx < num_ver; idx++) + { + double x, y; + if(is_vertex(m_vertices.vertex(idx, &x, &y))) + { + x += dx; + y += dy; + m_vertices.modify_vertex(idx, x, y); + } + } + } + + //-----------------------------------------------------vertex_stl_storage + template class vertex_stl_storage + { + public: + typedef typename Container::value_type vertex_type; + typedef typename vertex_type::value_type value_type; + + void remove_all() { m_vertices.clear(); } + void free_all() { m_vertices.clear(); } + + void add_vertex(double x, double y, unsigned cmd) + { + m_vertices.push_back(vertex_type(value_type(x), + value_type(y), + int8u(cmd))); + } + + void modify_vertex(unsigned idx, double x, double y) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + } + + void modify_vertex(unsigned idx, double x, double y, unsigned cmd) + { + vertex_type& v = m_vertices[idx]; + v.x = value_type(x); + v.y = value_type(y); + v.cmd = int8u(cmd); + } + + void modify_command(unsigned idx, unsigned cmd) + { + m_vertices[idx].cmd = int8u(cmd); + } + + void swap_vertices(unsigned v1, unsigned v2) + { + vertex_type t = m_vertices[v1]; + m_vertices[v1] = m_vertices[v2]; + m_vertices[v2] = t; + } + + unsigned last_command() const + { + return m_vertices.size() ? + m_vertices[m_vertices.size() - 1].cmd : + path_cmd_stop; + } + + unsigned last_vertex(double* x, double* y) const + { + if(m_vertices.size() == 0) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 1, x, y); + } + + unsigned prev_vertex(double* x, double* y) const + { + if(m_vertices.size() < 2) + { + *x = *y = 0.0; + return path_cmd_stop; + } + return vertex(m_vertices.size() - 2, x, y); + } + + double last_x() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].x : 0.0; + } + + double last_y() const + { + return m_vertices.size() ? m_vertices[m_vertices.size() - 1].y : 0.0; + } + + unsigned total_vertices() const + { + return m_vertices.size(); + } + + unsigned vertex(unsigned idx, double* x, double* y) const + { + const vertex_type& v = m_vertices[idx]; + *x = v.x; + *y = v.y; + return v.cmd; + } + + unsigned command(unsigned idx) const + { + return m_vertices[idx].cmd; + } + + private: + Container m_vertices; + }; + + //-----------------------------------------------------------path_storage + typedef path_base > path_storage; + + // Example of declarations path_storage with pod_bvector as a container + //----------------------------------------------------------------------- + //typedef path_base > > path_storage; + +} + + + +// Example of declarations path_storage with std::vector as a container +//--------------------------------------------------------------------------- +//#include +//namespace agg +//{ +// typedef path_base > > stl_path_storage; +//} + + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_path_storage_integer.h b/desmume/src/windows/agg/include/agg_path_storage_integer.h new file mode 100644 index 000000000..301e5f8af --- /dev/null +++ b/desmume/src/windows/agg/include/agg_path_storage_integer.h @@ -0,0 +1,304 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PATH_STORAGE_INTEGER_INCLUDED +#define AGG_PATH_STORAGE_INTEGER_INCLUDED + +#include +#include "agg_array.h" + +namespace agg +{ + //---------------------------------------------------------vertex_integer + template struct vertex_integer + { + enum path_cmd + { + cmd_move_to = 0, + cmd_line_to = 1, + cmd_curve3 = 2, + cmd_curve4 = 3 + }; + + enum coord_scale_e + { + coord_shift = CoordShift, + coord_scale = 1 << coord_shift + }; + + T x,y; + vertex_integer() {} + vertex_integer(T x_, T y_, unsigned flag) : + x(((x_ << 1) & ~1) | (flag & 1)), + y(((y_ << 1) & ~1) | (flag >> 1)) {} + + unsigned vertex(double* x_, double* y_, + double dx=0, double dy=0, + double scale=1.0) const + { + *x_ = dx + (double(x >> 1) / coord_scale) * scale; + *y_ = dy + (double(y >> 1) / coord_scale) * scale; + switch(((y & 1) << 1) | (x & 1)) + { + case cmd_move_to: return path_cmd_move_to; + case cmd_line_to: return path_cmd_line_to; + case cmd_curve3: return path_cmd_curve3; + case cmd_curve4: return path_cmd_curve4; + } + return path_cmd_stop; + } + }; + + + //---------------------------------------------------path_storage_integer + template class path_storage_integer + { + public: + typedef T value_type; + typedef vertex_integer vertex_integer_type; + + //-------------------------------------------------------------------- + path_storage_integer() : m_storage(), m_vertex_idx(0), m_closed(true) {} + + //-------------------------------------------------------------------- + void remove_all() { m_storage.remove_all(); } + + //-------------------------------------------------------------------- + void move_to(T x, T y) + { + m_storage.add(vertex_integer_type(x, y, vertex_integer_type::cmd_move_to)); + } + + //-------------------------------------------------------------------- + void line_to(T x, T y) + { + m_storage.add(vertex_integer_type(x, y, vertex_integer_type::cmd_line_to)); + } + + //-------------------------------------------------------------------- + void curve3(T x_ctrl, T y_ctrl, + T x_to, T y_to) + { + m_storage.add(vertex_integer_type(x_ctrl, y_ctrl, vertex_integer_type::cmd_curve3)); + m_storage.add(vertex_integer_type(x_to, y_to, vertex_integer_type::cmd_curve3)); + } + + //-------------------------------------------------------------------- + void curve4(T x_ctrl1, T y_ctrl1, + T x_ctrl2, T y_ctrl2, + T x_to, T y_to) + { + m_storage.add(vertex_integer_type(x_ctrl1, y_ctrl1, vertex_integer_type::cmd_curve4)); + m_storage.add(vertex_integer_type(x_ctrl2, y_ctrl2, vertex_integer_type::cmd_curve4)); + m_storage.add(vertex_integer_type(x_to, y_to, vertex_integer_type::cmd_curve4)); + } + + //-------------------------------------------------------------------- + void close_polygon() {} + + //-------------------------------------------------------------------- + unsigned size() const { return m_storage.size(); } + unsigned vertex(unsigned idx, double* x, double* y) const + { + return m_storage[idx].vertex(x, y); + } + + //-------------------------------------------------------------------- + unsigned byte_size() const { return m_storage.size() * sizeof(vertex_integer_type); } + void serialize(int8u* ptr) const + { + unsigned i; + for(i = 0; i < m_storage.size(); i++) + { + memcpy(ptr, &m_storage[i], sizeof(vertex_integer_type)); + ptr += sizeof(vertex_integer_type); + } + } + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_vertex_idx = 0; + m_closed = true; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + if(m_storage.size() < 2 || m_vertex_idx > m_storage.size()) + { + *x = 0; + *y = 0; + return path_cmd_stop; + } + if(m_vertex_idx == m_storage.size()) + { + *x = 0; + *y = 0; + ++m_vertex_idx; + return path_cmd_end_poly | path_flags_close; + } + unsigned cmd = m_storage[m_vertex_idx].vertex(x, y); + if(is_move_to(cmd) && !m_closed) + { + *x = 0; + *y = 0; + m_closed = true; + return path_cmd_end_poly | path_flags_close; + } + m_closed = false; + ++m_vertex_idx; + return cmd; + } + + //-------------------------------------------------------------------- + rect_d bounding_rect() const + { + rect_d bounds(1e100, 1e100, -1e100, -1e100); + if(m_storage.size() == 0) + { + bounds.x1 = bounds.y1 = bounds.x2 = bounds.y2 = 0.0; + } + else + { + unsigned i; + for(i = 0; i < m_storage.size(); i++) + { + double x, y; + m_storage[i].vertex(&x, &y); + if(x < bounds.x1) bounds.x1 = x; + if(y < bounds.y1) bounds.y1 = y; + if(x > bounds.x2) bounds.x2 = x; + if(y > bounds.y2) bounds.y2 = y; + } + } + return bounds; + } + + private: + pod_bvector m_storage; + unsigned m_vertex_idx; + bool m_closed; + }; + + + + + //-----------------------------------------serialized_integer_path_adaptor + template class serialized_integer_path_adaptor + { + public: + typedef vertex_integer vertex_integer_type; + + //-------------------------------------------------------------------- + serialized_integer_path_adaptor() : + m_data(0), + m_end(0), + m_ptr(0), + m_dx(0.0), + m_dy(0.0), + m_scale(1.0), + m_vertices(0) + {} + + //-------------------------------------------------------------------- + serialized_integer_path_adaptor(const int8u* data, unsigned size, + double dx, double dy) : + m_data(data), + m_end(data + size), + m_ptr(data), + m_dx(dx), + m_dy(dy), + m_vertices(0) + {} + + //-------------------------------------------------------------------- + void init(const int8u* data, unsigned size, + double dx, double dy, double scale=1.0) + { + m_data = data; + m_end = data + size; + m_ptr = data; + m_dx = dx; + m_dy = dy; + m_scale = scale; + m_vertices = 0; + } + + + //-------------------------------------------------------------------- + void rewind(unsigned) + { + m_ptr = m_data; + m_vertices = 0; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + if(m_data == 0 || m_ptr > m_end) + { + *x = 0; + *y = 0; + return path_cmd_stop; + } + + if(m_ptr == m_end) + { + *x = 0; + *y = 0; + m_ptr += sizeof(vertex_integer_type); + return path_cmd_end_poly | path_flags_close; + } + + vertex_integer_type v; + memcpy(&v, m_ptr, sizeof(vertex_integer_type)); + unsigned cmd = v.vertex(x, y, m_dx, m_dy, m_scale); + if(is_move_to(cmd) && m_vertices > 2) + { + *x = 0; + *y = 0; + m_vertices = 0; + return path_cmd_end_poly | path_flags_close; + } + ++m_vertices; + m_ptr += sizeof(vertex_integer_type); + return cmd; + } + + private: + const int8u* m_data; + const int8u* m_end; + const int8u* m_ptr; + double m_dx; + double m_dy; + double m_scale; + unsigned m_vertices; + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pattern_filters_rgba.h b/desmume/src/windows/agg/include/agg_pattern_filters_rgba.h new file mode 100644 index 000000000..a58cb66f5 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pattern_filters_rgba.h @@ -0,0 +1,132 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PATTERN_FILTERS_RGBA8_INCLUDED +#define AGG_PATTERN_FILTERS_RGBA8_INCLUDED + +#include "agg_basics.h" +#include "agg_line_aa_basics.h" +#include "agg_color_rgba.h" + + +namespace agg +{ + + //=======================================================pattern_filter_nn + template struct pattern_filter_nn + { + typedef ColorT color_type; + static unsigned dilation() { return 0; } + + static void AGG_INLINE pixel_low_res(color_type const* const* buf, + color_type* p, int x, int y) + { + *p = buf[y][x]; + } + + static void AGG_INLINE pixel_high_res(color_type const* const* buf, + color_type* p, int x, int y) + { + *p = buf[y >> line_subpixel_shift] + [x >> line_subpixel_shift]; + } + }; + + typedef pattern_filter_nn pattern_filter_nn_rgba8; + typedef pattern_filter_nn pattern_filter_nn_rgba16; + + + //===========================================pattern_filter_bilinear_rgba + template struct pattern_filter_bilinear_rgba + { + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + + static unsigned dilation() { return 1; } + + static AGG_INLINE void pixel_low_res(color_type const* const* buf, + color_type* p, int x, int y) + { + *p = buf[y][x]; + } + + static AGG_INLINE void pixel_high_res(color_type const* const* buf, + color_type* p, int x, int y) + { + calc_type r, g, b, a; + r = g = b = a = line_subpixel_scale * line_subpixel_scale / 2; + + calc_type weight; + int x_lr = x >> line_subpixel_shift; + int y_lr = y >> line_subpixel_shift; + + x &= line_subpixel_mask; + y &= line_subpixel_mask; + const color_type* ptr = buf[y_lr] + x_lr; + + weight = (line_subpixel_scale - x) * + (line_subpixel_scale - y); + r += weight * ptr->r; + g += weight * ptr->g; + b += weight * ptr->b; + a += weight * ptr->a; + + ++ptr; + + weight = x * (line_subpixel_scale - y); + r += weight * ptr->r; + g += weight * ptr->g; + b += weight * ptr->b; + a += weight * ptr->a; + + ptr = buf[y_lr + 1] + x_lr; + + weight = (line_subpixel_scale - x) * y; + r += weight * ptr->r; + g += weight * ptr->g; + b += weight * ptr->b; + a += weight * ptr->a; + + ++ptr; + + weight = x * y; + r += weight * ptr->r; + g += weight * ptr->g; + b += weight * ptr->b; + a += weight * ptr->a; + + p->r = (value_type)(r >> line_subpixel_shift * 2); + p->g = (value_type)(g >> line_subpixel_shift * 2); + p->b = (value_type)(b >> line_subpixel_shift * 2); + p->a = (value_type)(a >> line_subpixel_shift * 2); + } + }; + + typedef pattern_filter_bilinear_rgba pattern_filter_bilinear_rgba8; + typedef pattern_filter_bilinear_rgba pattern_filter_bilinear_rgba16; +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_pixfmt_amask_adaptor.h b/desmume/src/windows/agg/include/agg_pixfmt_amask_adaptor.h new file mode 100644 index 000000000..66d7bb1b3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_amask_adaptor.h @@ -0,0 +1,249 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_AMASK_ADAPTOR_INCLUDED +#define AGG_PIXFMT_AMASK_ADAPTOR_INCLUDED + + +#include +#include "agg_array.h" +#include "agg_rendering_buffer.h" + + +namespace agg +{ + //==================================================pixfmt_amask_adaptor + template class pixfmt_amask_adaptor + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::row_data row_data; + typedef AlphaMask amask_type; + typedef typename amask_type::cover_type cover_type; + + private: + enum span_extra_tail_e { span_extra_tail = 256 }; + + void realloc_span(unsigned len) + { + if(len > m_span.size()) + { + m_span.resize(len + span_extra_tail); + } + } + + void init_span(unsigned len) + { + realloc_span(len); + memset(&m_span[0], amask_type::cover_full, len * sizeof(cover_type)); + } + + void init_span(unsigned len, const cover_type* covers) + { + realloc_span(len); + memcpy(&m_span[0], covers, len * sizeof(cover_type)); + } + + + public: + pixfmt_amask_adaptor(pixfmt_type& pixf, const amask_type& mask) : + m_pixf(&pixf), m_mask(&mask), m_span() + {} + + void attach_pixfmt(pixfmt_type& pixf) { m_pixf = &pixf; } + void attach_alpha_mask(const amask_type& mask) { m_mask = &mask; } + + //-------------------------------------------------------------------- + template + bool attach_pixfmt(PixFmt2& pixf, int x1, int y1, int x2, int y2) + { + return m_pixf->attach(pixf, x1, y1, x2, y2); + } + + //-------------------------------------------------------------------- + unsigned width() const { return m_pixf->width(); } + unsigned height() const { return m_pixf->height(); } + + //-------------------------------------------------------------------- + color_type pixel(int x, int y) + { + return m_pixf->pixel(x, y); + } + + //-------------------------------------------------------------------- + void copy_pixel(int x, int y, const color_type& c) + { + m_pixf->blend_pixel(x, y, c, m_mask->pixel(x, y)); + } + + //-------------------------------------------------------------------- + void blend_pixel(int x, int y, const color_type& c, cover_type cover) + { + m_pixf->blend_pixel(x, y, c, m_mask->combine_pixel(x, y, cover)); + } + + //-------------------------------------------------------------------- + void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + realloc_span(len); + m_mask->fill_hspan(x, y, &m_span[0], len); + m_pixf->blend_solid_hspan(x, y, len, c, &m_span[0]); + } + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + cover_type cover) + { + init_span(len); + m_mask->combine_hspan(x, y, &m_span[0], len); + m_pixf->blend_solid_hspan(x, y, len, c, &m_span[0]); + } + + //-------------------------------------------------------------------- + void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + realloc_span(len); + m_mask->fill_vspan(x, y, &m_span[0], len); + m_pixf->blend_solid_vspan(x, y, len, c, &m_span[0]); + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + cover_type cover) + { + init_span(len); + m_mask->combine_vspan(x, y, &m_span[0], len); + m_pixf->blend_solid_vspan(x, y, len, c, &m_span[0]); + } + + //-------------------------------------------------------------------- + void copy_from(const rendering_buffer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + m_pixf->copy_from(from, xdst, ydst, xsrc, ysrc, len); + } + + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const cover_type* covers) + { + init_span(len, covers); + m_mask->combine_hspan(x, y, &m_span[0], len); + m_pixf->blend_solid_hspan(x, y, len, c, &m_span[0]); + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const cover_type* covers) + { + init_span(len, covers); + m_mask->combine_vspan(x, y, &m_span[0], len); + m_pixf->blend_solid_vspan(x, y, len, c, &m_span[0]); + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, unsigned len, const color_type* colors) + { + realloc_span(len); + m_mask->fill_hspan(x, y, &m_span[0], len); + m_pixf->blend_color_hspan(x, y, len, colors, &m_span[0], cover_full); + } + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, unsigned len, const color_type* colors) + { + realloc_span(len); + m_mask->fill_vspan(x, y, &m_span[0], len); + m_pixf->blend_color_vspan(x, y, len, colors, &m_span[0], cover_full); + } + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const cover_type* covers, + cover_type cover = cover_full) + { + if(covers) + { + init_span(len, covers); + m_mask->combine_hspan(x, y, &m_span[0], len); + } + else + { + realloc_span(len); + m_mask->fill_hspan(x, y, &m_span[0], len); + } + m_pixf->blend_color_hspan(x, y, len, colors, &m_span[0], cover); + } + + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const cover_type* covers, + cover_type cover = cover_full) + { + if(covers) + { + init_span(len, covers); + m_mask->combine_vspan(x, y, &m_span[0], len); + } + else + { + realloc_span(len); + m_mask->fill_vspan(x, y, &m_span[0], len); + } + m_pixf->blend_color_vspan(x, y, len, colors, &m_span[0], cover); + } + + private: + pixfmt_type* m_pixf; + const amask_type* m_mask; + pod_array m_span; + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pixfmt_gray.h b/desmume/src/windows/agg/include/agg_pixfmt_gray.h new file mode 100644 index 000000000..5870b9523 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_gray.h @@ -0,0 +1,679 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_GRAY_INCLUDED +#define AGG_PIXFMT_GRAY_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_color_gray.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //============================================================blender_gray + template struct blender_gray + { + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + static AGG_INLINE void blend_pix(value_type* p, unsigned cv, + unsigned alpha, unsigned cover=0) + { + *p = (value_type)((((cv - calc_type(*p)) * alpha) + (calc_type(*p) << base_shift)) >> base_shift); + } + }; + + + //======================================================blender_gray_pre + template struct blender_gray_pre + { + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + static AGG_INLINE void blend_pix(value_type* p, unsigned cv, + unsigned alpha, unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (base_shift - 8); + *p = (value_type)((*p * alpha + cv * cover) >> base_shift); + } + + static AGG_INLINE void blend_pix(value_type* p, unsigned cv, + unsigned alpha) + { + *p = (value_type)(((*p * (color_type::base_mask - alpha)) >> base_shift) + cv); + } + }; + + + + //=====================================================apply_gamma_dir_gray + template class apply_gamma_dir_gray + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_dir_gray(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + *p = m_gamma.dir(*p); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=====================================================apply_gamma_inv_gray + template class apply_gamma_inv_gray + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_inv_gray(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + *p = m_gamma.inv(*p); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=================================================pixfmt_alpha_blend_gray + template + class pixfmt_alpha_blend_gray + { + public: + typedef RenBuf rbuf_type; + typedef typename rbuf_type::row_data row_data; + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef int order_type; // A fake one + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask, + pix_width = sizeof(value_type), + pix_step = Step, + pix_offset = Offset + }; + + private: + //-------------------------------------------------------------------- + static AGG_INLINE void copy_or_blend_pix(value_type* p, + const color_type& c, + unsigned cover) + { + if (c.a) + { + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + *p = c.v; + } + else + { + Blender::blend_pix(p, c.v, alpha, cover); + } + } + } + + + static AGG_INLINE void copy_or_blend_pix(value_type* p, + const color_type& c) + { + if (c.a) + { + if(c.a == base_mask) + { + *p = c.v; + } + else + { + Blender::blend_pix(p, c.v, c.a); + } + } + } + + + public: + //-------------------------------------------------------------------- + explicit pixfmt_alpha_blend_gray(rbuf_type& rb) : + m_rbuf(&rb) + {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + //-------------------------------------------------------------------- + + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if(r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + row_data row(int y) const { return m_rbuf->row(y); } + + const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + x * Step + Offset; + } + + int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + x * Step + Offset; + } + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + *(value_type*)p = c.v; + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + value_type* p = (value_type*)m_rbuf->row_ptr(y) + x * Step + Offset; + return color_type(*p); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + *((value_type*)m_rbuf->row_ptr(x, y, 1) + x * Step + Offset) = c.v; + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + copy_or_blend_pix((value_type*) + m_rbuf->row_ptr(x, y, 1) + x * Step + Offset, + c, + cover); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x * Step + Offset; + + do + { + *p = c.v; + p += Step; + } + while(--len); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + do + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + *p = c.v; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x * Step + Offset; + + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + do + { + *p = c.v; + p += Step; + } + while(--len); + } + else + { + do + { + Blender::blend_pix(p, c.v, alpha, cover); + p += Step; + } + while(--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p; + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + *p = c.v; + } + while(--len); + } + else + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + Blender::blend_pix(p, c.v, alpha, cover); + } + while(--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x * Step + Offset; + + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == base_mask) + { + *p = c.v; + } + else + { + Blender::blend_pix(p, c.v, alpha, *covers); + } + p += Step; + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + if(alpha == base_mask) + { + *p = c.v; + } + else + { + Blender::blend_pix(p, c.v, alpha, *covers); + } + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x * Step + Offset; + + do + { + *p = colors->v; + p += Step; + ++colors; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + *p = colors->v; + ++colors; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x * Step + Offset; + + if(covers) + { + do + { + copy_or_blend_pix(p, *colors++, *covers++); + p += Step; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + if(colors->a == base_mask) + { + *p = colors->v; + } + else + { + copy_or_blend_pix(p, *colors); + } + p += Step; + ++colors; + } + while(--len); + } + else + { + do + { + copy_or_blend_pix(p, *colors++, cover); + p += Step; + } + while(--len); + } + } + } + + + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p; + if(covers) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + copy_or_blend_pix(p, *colors++, *covers++); + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + if(colors->a == base_mask) + { + *p = colors->v; + } + else + { + copy_or_blend_pix(p, *colors); + } + ++colors; + } + while(--len); + } + else + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x * Step + Offset; + + copy_or_blend_pix(p, *colors++, cover); + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + unsigned y; + for(y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if(r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + + value_type* p = (value_type*) + m_rbuf->row_ptr(r.x1, y, len) + r.x1 * Step + Offset; + + do + { + f(p); + p += Step; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_gray(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_gray(g)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + const int8u* p = from.row_ptr(ysrc); + if(p) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst; + do + { + copy_or_blend_pix(pdst, + color, + (*psrc * cover + base_mask) >> base_shift); + ++psrc; + ++pdst; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst; + do + { + copy_or_blend_pix(pdst, color_lut[*psrc], cover); + ++psrc; + ++pdst; + } + while(--len); + } + } + + private: + rbuf_type* m_rbuf; + }; + + typedef blender_gray blender_gray8; + typedef blender_gray_pre blender_gray8_pre; + typedef blender_gray blender_gray16; + typedef blender_gray_pre blender_gray16_pre; + + typedef pixfmt_alpha_blend_gray pixfmt_gray8; //----pixfmt_gray8 + typedef pixfmt_alpha_blend_gray pixfmt_gray8_pre; //----pixfmt_gray8_pre + typedef pixfmt_alpha_blend_gray pixfmt_gray16; //----pixfmt_gray16 + typedef pixfmt_alpha_blend_gray pixfmt_gray16_pre; //----pixfmt_gray16_pre +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pixfmt_rgb.h b/desmume/src/windows/agg/include/agg_pixfmt_rgb.h new file mode 100644 index 000000000..7f0ed184f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_rgb.h @@ -0,0 +1,869 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_RGB_INCLUDED +#define AGG_PIXFMT_RGB_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //=====================================================apply_gamma_dir_rgb + template class apply_gamma_dir_rgb + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_dir_rgb(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.dir(p[Order::R]); + p[Order::G] = m_gamma.dir(p[Order::G]); + p[Order::B] = m_gamma.dir(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + + + //=====================================================apply_gamma_inv_rgb + template class apply_gamma_inv_rgb + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_inv_rgb(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.inv(p[Order::R]); + p[Order::G] = m_gamma.inv(p[Order::G]); + p[Order::B] = m_gamma.inv(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + + //=========================================================blender_rgb + template struct blender_rgb + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover=0) + { + p[Order::R] += (value_type)(((cr - p[Order::R]) * alpha) >> base_shift); + p[Order::G] += (value_type)(((cg - p[Order::G]) * alpha) >> base_shift); + p[Order::B] += (value_type)(((cb - p[Order::B]) * alpha) >> base_shift); + } + }; + + + //======================================================blender_rgb_pre + template struct blender_rgb_pre + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (base_shift - 8); + p[Order::R] = (value_type)((p[Order::R] * alpha + cr * cover) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * alpha + cg * cover) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * alpha + cb * cover) >> base_shift); + } + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha) + { + alpha = color_type::base_mask - alpha; + p[Order::R] = (value_type)(((p[Order::R] * alpha) >> base_shift) + cr); + p[Order::G] = (value_type)(((p[Order::G] * alpha) >> base_shift) + cg); + p[Order::B] = (value_type)(((p[Order::B] * alpha) >> base_shift) + cb); + } + + }; + + + + //===================================================blender_rgb_gamma + template class blender_rgb_gamma + { + public: + typedef ColorT color_type; + typedef Order order_type; + typedef Gamma gamma_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + //-------------------------------------------------------------------- + blender_rgb_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover=0) + { + calc_type r = m_gamma->dir(p[Order::R]); + calc_type g = m_gamma->dir(p[Order::G]); + calc_type b = m_gamma->dir(p[Order::B]); + p[Order::R] = m_gamma->inv((((m_gamma->dir(cr) - r) * alpha) >> base_shift) + r); + p[Order::G] = m_gamma->inv((((m_gamma->dir(cg) - g) * alpha) >> base_shift) + g); + p[Order::B] = m_gamma->inv((((m_gamma->dir(cb) - b) * alpha) >> base_shift) + b); + } + + private: + const gamma_type* m_gamma; + }; + + + + + //==================================================pixfmt_alpha_blend_rgb + template class pixfmt_alpha_blend_rgb + { + public: + typedef RenBuf rbuf_type; + typedef Blender blender_type; + typedef typename rbuf_type::row_data row_data; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask, + pix_width = sizeof(value_type) * 3 + }; + + private: + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(value_type* p, + const color_type& c, + unsigned cover) + { + if (c.a) + { + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + else + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, cover); + } + } + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(value_type* p, + const color_type& c) + { + if (c.a) + { + if(c.a == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + else + { + m_blender.blend_pix(p, c.r, c.g, c.b, c.a); + } + } + } + + + public: + //-------------------------------------------------------------------- + explicit pixfmt_alpha_blend_rgb(rbuf_type& rb) : + m_rbuf(&rb) + {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + + //-------------------------------------------------------------------- + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if(r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + Blender& blender() { return m_blender; } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + AGG_INLINE const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + AGG_INLINE row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + ((value_type*)p)[order_type::R] = c.r; + ((value_type*)p)[order_type::G] = c.g; + ((value_type*)p)[order_type::B] = c.b; + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + value_type* p = (value_type*)m_rbuf->row_ptr(y) + x + x + x; + return color_type(p[order_type::R], + p[order_type::G], + p[order_type::B]); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, 1) + x + x + x; + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + copy_or_blend_pix((value_type*)m_rbuf->row_ptr(x, y, 1) + x + x + x, c, cover); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + x + x + x; + do + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + p += 3; + } + while(--len); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + do + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x + x + x; + + calc_type alpha = (calc_type(c.a) * (calc_type(cover) + 1)) >> 8; + if(alpha == base_mask) + { + do + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + p += 3; + } + while(--len); + } + else + { + do + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, cover); + p += 3; + } + while(--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p; + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + while(--len); + } + else + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, cover); + } + while(--len); + } + } + } + + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x + x + x; + + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + else + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, *covers); + } + p += 3; + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + do + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + } + else + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, *covers); + } + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x + x + x; + + do + { + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + ++colors; + p += 3; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + ++colors; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p = (value_type*) + m_rbuf->row_ptr(x, y, len) + x + x + x; + + if(covers) + { + do + { + copy_or_blend_pix(p, *colors++, *covers++); + p += 3; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + copy_or_blend_pix(p, *colors++); + p += 3; + } + while(--len); + } + else + { + do + { + copy_or_blend_pix(p, *colors++, cover); + p += 3; + } + while(--len); + } + } + } + + + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p; + if(covers) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + copy_or_blend_pix(p, *colors++, *covers++); + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + copy_or_blend_pix(p, *colors++); + } + while(--len); + } + else + { + do + { + p = (value_type*) + m_rbuf->row_ptr(x, y++, 1) + x + x + x; + + copy_or_blend_pix(p, *colors++, cover); + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + unsigned y; + for(y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if(r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + value_type* p = (value_type*) + m_rbuf->row_ptr(r.x1, y, len) + r.x1 * 3; + do + { + f(p); + p += 3; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_rgb(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_rgb(g)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + const int8u* p = from.row_ptr(ysrc); + if(p) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::order_type src_order; + + const value_type* psrc = (const value_type*)from.row_ptr(ysrc); + if(psrc) + { + psrc += xsrc * 4; + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst * 3; + + if(cover == 255) + { + do + { + value_type alpha = psrc[src_order::A]; + if(alpha) + { + if(alpha == base_mask) + { + pdst[order_type::R] = psrc[src_order::R]; + pdst[order_type::G] = psrc[src_order::G]; + pdst[order_type::B] = psrc[src_order::B]; + } + else + { + m_blender.blend_pix(pdst, + psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B], + alpha); + } + } + psrc += 4; + pdst += 3; + } + while(--len); + } + else + { + color_type color; + do + { + color.r = psrc[src_order::R]; + color.g = psrc[src_order::G]; + color.b = psrc[src_order::B]; + color.a = psrc[src_order::A]; + copy_or_blend_pix(pdst, color, cover); + psrc += 4; + pdst += 3; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst * 3; + do + { + copy_or_blend_pix(pdst, + color, + (*psrc * cover + base_mask) >> base_shift); + ++psrc; + pdst += 3; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst * 3; + + if(cover == 255) + { + do + { + const color_type& color = color_lut[*psrc]; + m_blender.blend_pix(pdst, + color.r, color.g, color.b, color.a); + ++psrc; + pdst += 3; + } + while(--len); + } + else + { + do + { + copy_or_blend_pix(pdst, color_lut[*psrc], cover); + ++psrc; + pdst += 3; + } + while(--len); + } + } + } + + private: + rbuf_type* m_rbuf; + Blender m_blender; + }; + + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_rgb24; //----pixfmt_rgb24 + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_bgr24; //----pixfmt_bgr24 + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_rgb48; //----pixfmt_rgb48 + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_bgr48; //----pixfmt_bgr48 + + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_rgb24_pre; //----pixfmt_rgb24_pre + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_bgr24_pre; //----pixfmt_bgr24_pre + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_rgb48_pre; //----pixfmt_rgb48_pre + typedef pixfmt_alpha_blend_rgb, rendering_buffer> pixfmt_bgr48_pre; //----pixfmt_bgr48_pre + + //-----------------------------------------------------pixfmt_rgb24_gamma + template class pixfmt_rgb24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer> + { + public: + pixfmt_rgb24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_bgr24_gamma + template class pixfmt_bgr24_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer> + { + public: + pixfmt_bgr24_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_rgb48_gamma + template class pixfmt_rgb48_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer> + { + public: + pixfmt_rgb48_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + //-----------------------------------------------------pixfmt_bgr48_gamma + template class pixfmt_bgr48_gamma : + public pixfmt_alpha_blend_rgb, rendering_buffer> + { + public: + pixfmt_bgr48_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb, rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pixfmt_rgb_packed.h b/desmume/src/windows/agg/include/agg_pixfmt_rgb_packed.h new file mode 100644 index 000000000..28bb79d4f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_rgb_packed.h @@ -0,0 +1,1318 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_RGB_PACKED_INCLUDED +#define AGG_PIXFMT_RGB_PACKED_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + //=========================================================blender_rgb555 + struct blender_rgb555 + { + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = (rgb >> 7) & 0xF8; + calc_type g = (rgb >> 2) & 0xF8; + calc_type b = (rgb << 3) & 0xF8; + *p = (pixel_type) + (((((cr - r) * alpha + (r << 8)) >> 1) & 0x7C00) | + ((((cg - g) * alpha + (g << 8)) >> 6) & 0x03E0) | + (((cb - b) * alpha + (b << 8)) >> 11) | 0x8000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 7) | + ((g & 0xF8) << 2) | + (b >> 3) | 0x8000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 7) & 0xF8, + (p >> 2) & 0xF8, + (p << 3) & 0xF8); + } + }; + + + //=====================================================blender_rgb555_pre + struct blender_rgb555_pre + { + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + pixel_type rgb = *p; + calc_type r = (rgb >> 7) & 0xF8; + calc_type g = (rgb >> 2) & 0xF8; + calc_type b = (rgb << 3) & 0xF8; + *p = (pixel_type) + ((((r * alpha + cr * cover) >> 1) & 0x7C00) | + (((g * alpha + cg * cover) >> 6) & 0x03E0) | + ((b * alpha + cb * cover) >> 11) | 0x8000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 7) | + ((g & 0xF8) << 2) | + (b >> 3) | 0x8000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 7) & 0xF8, + (p >> 2) & 0xF8, + (p << 3) & 0xF8); + } + }; + + + + + //=====================================================blender_rgb555_gamma + template class blender_rgb555_gamma + { + public: + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + typedef Gamma gamma_type; + + blender_rgb555_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = m_gamma->dir((rgb >> 7) & 0xF8); + calc_type g = m_gamma->dir((rgb >> 2) & 0xF8); + calc_type b = m_gamma->dir((rgb << 3) & 0xF8); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 8)) >> 8) << 7) & 0x7C00) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 8)) >> 8) << 2) & 0x03E0) | + (m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 8)) >> 8) >> 3) | 0x8000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 7) | + ((g & 0xF8) << 2) | + (b >> 3) | 0x8000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 7) & 0xF8, + (p >> 2) & 0xF8, + (p << 3) & 0xF8); + } + + private: + const Gamma* m_gamma; + }; + + + + + + //=========================================================blender_rgb565 + struct blender_rgb565 + { + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = (rgb >> 8) & 0xF8; + calc_type g = (rgb >> 3) & 0xFC; + calc_type b = (rgb << 3) & 0xF8; + *p = (pixel_type) + (((((cr - r) * alpha + (r << 8)) ) & 0xF800) | + ((((cg - g) * alpha + (g << 8)) >> 5) & 0x07E0) | + (((cb - b) * alpha + (b << 8)) >> 11)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 8) & 0xF8, + (p >> 3) & 0xFC, + (p << 3) & 0xF8); + } + }; + + + + //=====================================================blender_rgb565_pre + struct blender_rgb565_pre + { + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + pixel_type rgb = *p; + calc_type r = (rgb >> 8) & 0xF8; + calc_type g = (rgb >> 3) & 0xFC; + calc_type b = (rgb << 3) & 0xF8; + *p = (pixel_type) + ((((r * alpha + cr * cover) ) & 0xF800) | + (((g * alpha + cg * cover) >> 5 ) & 0x07E0) | + ((b * alpha + cb * cover) >> 11)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 8) & 0xF8, + (p >> 3) & 0xFC, + (p << 3) & 0xF8); + } + }; + + + + //=====================================================blender_rgb565_gamma + template class blender_rgb565_gamma + { + public: + typedef rgba8 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int16u pixel_type; + typedef Gamma gamma_type; + + blender_rgb565_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = m_gamma->dir((rgb >> 8) & 0xF8); + calc_type g = m_gamma->dir((rgb >> 3) & 0xFC); + calc_type b = m_gamma->dir((rgb << 3) & 0xF8); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 8)) >> 8) << 8) & 0xF800) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 8)) >> 8) << 3) & 0x07E0) | + (m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 8)) >> 8) >> 3)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 8) & 0xF8, + (p >> 3) & 0xFC, + (p << 3) & 0xF8); + } + + private: + const Gamma* m_gamma; + }; + + + + //=====================================================blender_rgbAAA + struct blender_rgbAAA + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = (rgb >> 14) & 0xFFC0; + calc_type g = (rgb >> 4) & 0xFFC0; + calc_type b = (rgb << 6) & 0xFFC0; + *p = (pixel_type) + (((((cr - r) * alpha + (r << 16)) >> 2) & 0x3FF00000) | + ((((cg - g) * alpha + (g << 16)) >> 12) & 0x000FFC00) | + (((cb - b) * alpha + (b << 16)) >> 22) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (b >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 14) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p << 6) & 0xFFC0); + } + }; + + + + //==================================================blender_rgbAAA_pre + struct blender_rgbAAA_pre + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (color_type::base_shift - 8); + pixel_type rgb = *p; + calc_type r = (rgb >> 14) & 0xFFC0; + calc_type g = (rgb >> 4) & 0xFFC0; + calc_type b = (rgb << 6) & 0xFFC0; + *p = (pixel_type) + ((((r * alpha + cr * cover) >> 2) & 0x3FF00000) | + (((g * alpha + cg * cover) >> 12) & 0x000FFC00) | + ((b * alpha + cb * cover) >> 22) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (b >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 14) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p << 6) & 0xFFC0); + } + }; + + + + //=================================================blender_rgbAAA_gamma + template class blender_rgbAAA_gamma + { + public: + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + typedef Gamma gamma_type; + + blender_rgbAAA_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = m_gamma->dir((rgb >> 14) & 0xFFC0); + calc_type g = m_gamma->dir((rgb >> 4) & 0xFFC0); + calc_type b = m_gamma->dir((rgb << 6) & 0xFFC0); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 16)) >> 16) << 14) & 0x3FF00000) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 16)) >> 16) << 4 ) & 0x000FFC00) | + (m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 16)) >> 16) >> 6 ) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (b >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 14) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p << 6) & 0xFFC0); + } + private: + const Gamma* m_gamma; + }; + + + //=====================================================blender_bgrAAA + struct blender_bgrAAA + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type bgr = *p; + calc_type b = (bgr >> 14) & 0xFFC0; + calc_type g = (bgr >> 4) & 0xFFC0; + calc_type r = (bgr << 6) & 0xFFC0; + *p = (pixel_type) + (((((cb - b) * alpha + (b << 16)) >> 2) & 0x3FF00000) | + ((((cg - g) * alpha + (g << 16)) >> 12) & 0x000FFC00) | + (((cr - r) * alpha + (r << 16)) >> 22) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (r >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 6) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p >> 14) & 0xFFC0); + } + }; + + + + //=================================================blender_bgrAAA_pre + struct blender_bgrAAA_pre + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (color_type::base_shift - 8); + pixel_type bgr = *p; + calc_type b = (bgr >> 14) & 0xFFC0; + calc_type g = (bgr >> 4) & 0xFFC0; + calc_type r = (bgr << 6) & 0xFFC0; + *p = (pixel_type) + ((((b * alpha + cb * cover) >> 2) & 0x3FF00000) | + (((g * alpha + cg * cover) >> 12) & 0x000FFC00) | + ((r * alpha + cr * cover) >> 22) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (r >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 6) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p >> 14) & 0xFFC0); + } + }; + + + + //=================================================blender_bgrAAA_gamma + template class blender_bgrAAA_gamma + { + public: + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + typedef Gamma gamma_type; + + blender_bgrAAA_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type bgr = *p; + calc_type b = m_gamma->dir((bgr >> 14) & 0xFFC0); + calc_type g = m_gamma->dir((bgr >> 4) & 0xFFC0); + calc_type r = m_gamma->dir((bgr << 6) & 0xFFC0); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 16)) >> 16) << 14) & 0x3FF00000) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 16)) >> 16) << 4 ) & 0x000FFC00) | + (m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 16)) >> 16) >> 6 ) | 0xC0000000); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 14) | + ((g & 0xFFC0) << 4) | + (r >> 6) | 0xC0000000); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 6) & 0xFFC0, + (p >> 4) & 0xFFC0, + (p >> 14) & 0xFFC0); + } + + private: + const Gamma* m_gamma; + }; + + + + //=====================================================blender_rgbBBA + struct blender_rgbBBA + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = (rgb >> 16) & 0xFFE0; + calc_type g = (rgb >> 5) & 0xFFE0; + calc_type b = (rgb << 6) & 0xFFC0; + *p = (pixel_type) + (((((cr - r) * alpha + (r << 16)) ) & 0xFFE00000) | + ((((cg - g) * alpha + (g << 16)) >> 11) & 0x001FFC00) | + (((cb - b) * alpha + (b << 16)) >> 22)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFE0) << 16) | ((g & 0xFFE0) << 5) | (b >> 6)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 16) & 0xFFE0, + (p >> 5) & 0xFFE0, + (p << 6) & 0xFFC0); + } + }; + + + //=================================================blender_rgbBBA_pre + struct blender_rgbBBA_pre + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (color_type::base_shift - 8); + pixel_type rgb = *p; + calc_type r = (rgb >> 16) & 0xFFE0; + calc_type g = (rgb >> 5) & 0xFFE0; + calc_type b = (rgb << 6) & 0xFFC0; + *p = (pixel_type) + ((((r * alpha + cr * cover) ) & 0xFFE00000) | + (((g * alpha + cg * cover) >> 11) & 0x001FFC00) | + ((b * alpha + cb * cover) >> 22)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFE0) << 16) | ((g & 0xFFE0) << 5) | (b >> 6)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 16) & 0xFFE0, + (p >> 5) & 0xFFE0, + (p << 6) & 0xFFC0); + } + }; + + + + //=================================================blender_rgbBBA_gamma + template class blender_rgbBBA_gamma + { + public: + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + typedef Gamma gamma_type; + + blender_rgbBBA_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type rgb = *p; + calc_type r = m_gamma->dir((rgb >> 16) & 0xFFE0); + calc_type g = m_gamma->dir((rgb >> 5) & 0xFFE0); + calc_type b = m_gamma->dir((rgb << 6) & 0xFFC0); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 16)) >> 16) << 16) & 0xFFE00000) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 16)) >> 16) << 5 ) & 0x001FFC00) | + (m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 16)) >> 16) >> 6 )); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((r & 0xFFE0) << 16) | ((g & 0xFFE0) << 5) | (b >> 6)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p >> 16) & 0xFFE0, + (p >> 5) & 0xFFE0, + (p << 6) & 0xFFC0); + } + + private: + const Gamma* m_gamma; + }; + + + //=====================================================blender_bgrABB + struct blender_bgrABB + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type bgr = *p; + calc_type b = (bgr >> 16) & 0xFFC0; + calc_type g = (bgr >> 6) & 0xFFE0; + calc_type r = (bgr << 5) & 0xFFE0; + *p = (pixel_type) + (((((cb - b) * alpha + (b << 16)) ) & 0xFFC00000) | + ((((cg - g) * alpha + (g << 16)) >> 10) & 0x003FF800) | + (((cr - r) * alpha + (r << 16)) >> 21)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 16) | ((g & 0xFFE0) << 6) | (r >> 5)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 5) & 0xFFE0, + (p >> 6) & 0xFFE0, + (p >> 16) & 0xFFC0); + } + }; + + + //=================================================blender_bgrABB_pre + struct blender_bgrABB_pre + { + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + + static AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (color_type::base_shift - 8); + pixel_type bgr = *p; + calc_type b = (bgr >> 16) & 0xFFC0; + calc_type g = (bgr >> 6) & 0xFFE0; + calc_type r = (bgr << 5) & 0xFFE0; + *p = (pixel_type) + ((((b * alpha + cb * cover) ) & 0xFFC00000) | + (((g * alpha + cg * cover) >> 10) & 0x003FF800) | + ((r * alpha + cr * cover) >> 21)); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 16) | ((g & 0xFFE0) << 6) | (r >> 5)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 5) & 0xFFE0, + (p >> 6) & 0xFFE0, + (p >> 16) & 0xFFC0); + } + }; + + + + //=================================================blender_bgrABB_gamma + template class blender_bgrABB_gamma + { + public: + typedef rgba16 color_type; + typedef color_type::value_type value_type; + typedef color_type::calc_type calc_type; + typedef int32u pixel_type; + typedef Gamma gamma_type; + + blender_bgrABB_gamma() : m_gamma(0) {} + void gamma(const gamma_type& g) { m_gamma = &g; } + + AGG_INLINE void blend_pix(pixel_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned) + { + pixel_type bgr = *p; + calc_type b = m_gamma->dir((bgr >> 16) & 0xFFC0); + calc_type g = m_gamma->dir((bgr >> 6) & 0xFFE0); + calc_type r = m_gamma->dir((bgr << 5) & 0xFFE0); + *p = (pixel_type) + (((m_gamma->inv(((m_gamma->dir(cb) - b) * alpha + (b << 16)) >> 16) << 16) & 0xFFC00000) | + ((m_gamma->inv(((m_gamma->dir(cg) - g) * alpha + (g << 16)) >> 16) << 6 ) & 0x003FF800) | + (m_gamma->inv(((m_gamma->dir(cr) - r) * alpha + (r << 16)) >> 16) >> 5 )); + } + + static AGG_INLINE pixel_type make_pix(unsigned r, unsigned g, unsigned b) + { + return (pixel_type)(((b & 0xFFC0) << 16) | ((g & 0xFFE0) << 6) | (r >> 5)); + } + + static AGG_INLINE color_type make_color(pixel_type p) + { + return color_type((p << 5) & 0xFFE0, + (p >> 6) & 0xFFE0, + (p >> 16) & 0xFFC0); + } + + private: + const Gamma* m_gamma; + }; + + + + //===========================================pixfmt_alpha_blend_rgb_packed + template class pixfmt_alpha_blend_rgb_packed + { + public: + typedef RenBuf rbuf_type; + typedef typename rbuf_type::row_data row_data; + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::pixel_type pixel_type; + typedef int order_type; // A fake one + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask, + pix_width = sizeof(pixel_type) + }; + + private: + //-------------------------------------------------------------------- + AGG_INLINE void copy_or_blend_pix(pixel_type* p, const color_type& c, unsigned cover) + { + if (c.a) + { + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + *p = m_blender.make_pix(c.r, c.g, c.b); + } + else + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, cover); + } + } + } + + public: + //-------------------------------------------------------------------- + explicit pixfmt_alpha_blend_rgb_packed(rbuf_type& rb) : m_rbuf(&rb) {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + + //-------------------------------------------------------------------- + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if(r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + Blender& blender() { return m_blender; } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + AGG_INLINE const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + AGG_INLINE row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + //-------------------------------------------------------------------- + AGG_INLINE void make_pix(int8u* p, const color_type& c) + { + *(pixel_type*)p = m_blender.make_pix(c.r, c.g, c.b); + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + return m_blender.make_color(((pixel_type*)m_rbuf->row_ptr(y))[x]); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + ((pixel_type*) + m_rbuf->row_ptr(x, y, 1))[x] = + m_blender.make_pix(c.r, c.g, c.b); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + copy_or_blend_pix((pixel_type*)m_rbuf->row_ptr(x, y, 1) + x, c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y, len) + x; + pixel_type v = m_blender.make_pix(c.r, c.g, c.b); + do + { + *p++ = v; + } + while(--len); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + pixel_type v = m_blender.make_pix(c.r, c.g, c.b); + do + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y++, 1) + x; + *p = v; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y, len) + x; + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + pixel_type v = m_blender.make_pix(c.r, c.g, c.b); + do + { + *p++ = v; + } + while(--len); + } + else + { + do + { + m_blender.blend_pix(p, c.r, c.g, c.b, alpha, cover); + ++p; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + pixel_type v = m_blender.make_pix(c.r, c.g, c.b); + do + { + ((pixel_type*)m_rbuf->row_ptr(x, y++, 1))[x] = v; + } + while(--len); + } + else + { + do + { + m_blender.blend_pix( + (pixel_type*)m_rbuf->row_ptr(x, y++, 1), + c.r, c.g, c.b, alpha, cover); + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y, len) + x; + do + { + copy_or_blend_pix(p, c, *covers++); + ++p; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + do + { + copy_or_blend_pix((pixel_type*)m_rbuf->row_ptr(x, y++, 1) + x, + c, *covers++); + } + while(--len); + } + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y, len) + x; + do + { + *p++ = m_blender.make_pix(colors->r, colors->g, colors->b); + ++colors; + } + while(--len); + } + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y++, 1) + x; + *p = m_blender.make_pix(colors->r, colors->g, colors->b); + ++colors; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + pixel_type* p = (pixel_type*)m_rbuf->row_ptr(x, y, len) + x; + do + { + copy_or_blend_pix(p++, *colors++, covers ? *covers++ : cover); + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + do + { + copy_or_blend_pix((pixel_type*)m_rbuf->row_ptr(x, y++, 1) + x, + *colors++, covers ? *covers++ : cover); + } + while(--len); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + const int8u* p = from.row_ptr(ysrc); + if(p) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::order_type src_order; + + const value_type* psrc = (const value_type*)from.row_ptr(ysrc); + if(psrc) + { + psrc += xsrc * 4; + pixel_type* pdst = + (pixel_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst; + do + { + value_type alpha = psrc[src_order::A]; + if(alpha) + { + if(alpha == base_mask && cover == 255) + { + *pdst = m_blender.make_pix(psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B]); + } + else + { + m_blender.blend_pix(pdst, + psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B], + alpha, + cover); + } + } + psrc += 4; + ++pdst; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + pixel_type* pdst = + (pixel_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst; + + do + { + m_blender.blend_pix(pdst, + color.r, color.g, color.b, color.a, + cover); + ++psrc; + ++pdst; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + pixel_type* pdst = + (pixel_type*)m_rbuf->row_ptr(xdst, ydst, len) + xdst; + + do + { + const color_type& color = color_lut[*psrc]; + m_blender.blend_pix(pdst, + color.r, color.g, color.b, color.a, + cover); + ++psrc; + ++pdst; + } + while(--len); + } + } + + + + private: + rbuf_type* m_rbuf; + Blender m_blender; + }; + + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgb555; //----pixfmt_rgb555 + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgb565; //----pixfmt_rgb565 + + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgb555_pre; //----pixfmt_rgb555_pre + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgb565_pre; //----pixfmt_rgb565_pre + + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgbAAA; //----pixfmt_rgbAAA + typedef pixfmt_alpha_blend_rgb_packed pixfmt_bgrAAA; //----pixfmt_bgrAAA + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgbBBA; //----pixfmt_rgbBBA + typedef pixfmt_alpha_blend_rgb_packed pixfmt_bgrABB; //----pixfmt_bgrABB + + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgbAAA_pre; //----pixfmt_rgbAAA_pre + typedef pixfmt_alpha_blend_rgb_packed pixfmt_bgrAAA_pre; //----pixfmt_bgrAAA_pre + typedef pixfmt_alpha_blend_rgb_packed pixfmt_rgbBBA_pre; //----pixfmt_rgbBBA_pre + typedef pixfmt_alpha_blend_rgb_packed pixfmt_bgrABB_pre; //----pixfmt_bgrABB_pre + + + //-----------------------------------------------------pixfmt_rgb555_gamma + template class pixfmt_rgb555_gamma : + public pixfmt_alpha_blend_rgb_packed, + rendering_buffer> + { + public: + pixfmt_rgb555_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, + rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + + //-----------------------------------------------------pixfmt_rgb565_gamma + template class pixfmt_rgb565_gamma : + public pixfmt_alpha_blend_rgb_packed, rendering_buffer> + { + public: + pixfmt_rgb565_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + + //-----------------------------------------------------pixfmt_rgbAAA_gamma + template class pixfmt_rgbAAA_gamma : + public pixfmt_alpha_blend_rgb_packed, + rendering_buffer> + { + public: + pixfmt_rgbAAA_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, + rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + + //-----------------------------------------------------pixfmt_bgrAAA_gamma + template class pixfmt_bgrAAA_gamma : + public pixfmt_alpha_blend_rgb_packed, + rendering_buffer> + { + public: + pixfmt_bgrAAA_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, + rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + + //-----------------------------------------------------pixfmt_rgbBBA_gamma + template class pixfmt_rgbBBA_gamma : + public pixfmt_alpha_blend_rgb_packed, + rendering_buffer> + { + public: + pixfmt_rgbBBA_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, + rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + + //-----------------------------------------------------pixfmt_bgrABB_gamma + template class pixfmt_bgrABB_gamma : + public pixfmt_alpha_blend_rgb_packed, + rendering_buffer> + { + public: + pixfmt_bgrABB_gamma(rendering_buffer& rb, const Gamma& g) : + pixfmt_alpha_blend_rgb_packed, + rendering_buffer>(rb) + { + this->blender().gamma(g); + } + }; + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pixfmt_rgba.h b/desmume/src/windows/agg/include/agg_pixfmt_rgba.h new file mode 100644 index 000000000..a6424c918 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_rgba.h @@ -0,0 +1,2911 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_RGBA_INCLUDED +#define AGG_PIXFMT_RGBA_INCLUDED + +#include +#include +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //=========================================================multiplier_rgba + template struct multiplier_rgba + { + typedef typename ColorT::value_type value_type; + typedef typename ColorT::calc_type calc_type; + + //-------------------------------------------------------------------- + static AGG_INLINE void premultiply(value_type* p) + { + calc_type a = p[Order::A]; + if(a < ColorT::base_mask) + { + if(a == 0) + { + p[Order::R] = p[Order::G] = p[Order::B] = 0; + return; + } + p[Order::R] = value_type((p[Order::R] * a + ColorT::base_mask) >> ColorT::base_shift); + p[Order::G] = value_type((p[Order::G] * a + ColorT::base_mask) >> ColorT::base_shift); + p[Order::B] = value_type((p[Order::B] * a + ColorT::base_mask) >> ColorT::base_shift); + } + } + + + //-------------------------------------------------------------------- + static AGG_INLINE void demultiply(value_type* p) + { + calc_type a = p[Order::A]; + if(a < ColorT::base_mask) + { + if(a == 0) + { + p[Order::R] = p[Order::G] = p[Order::B] = 0; + return; + } + calc_type r = (calc_type(p[Order::R]) * ColorT::base_mask) / a; + calc_type g = (calc_type(p[Order::G]) * ColorT::base_mask) / a; + calc_type b = (calc_type(p[Order::B]) * ColorT::base_mask) / a; + p[Order::R] = value_type((r > ColorT::base_mask) ? ColorT::base_mask : r); + p[Order::G] = value_type((g > ColorT::base_mask) ? ColorT::base_mask : g); + p[Order::B] = value_type((b > ColorT::base_mask) ? ColorT::base_mask : b); + } + } + }; + + //=====================================================apply_gamma_dir_rgba + template class apply_gamma_dir_rgba + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_dir_rgba(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.dir(p[Order::R]); + p[Order::G] = m_gamma.dir(p[Order::G]); + p[Order::B] = m_gamma.dir(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + //=====================================================apply_gamma_inv_rgba + template class apply_gamma_inv_rgba + { + public: + typedef typename ColorT::value_type value_type; + + apply_gamma_inv_rgba(const GammaLut& gamma) : m_gamma(gamma) {} + + AGG_INLINE void operator () (value_type* p) + { + p[Order::R] = m_gamma.inv(p[Order::R]); + p[Order::G] = m_gamma.inv(p[Order::G]); + p[Order::B] = m_gamma.inv(p[Order::B]); + } + + private: + const GammaLut& m_gamma; + }; + + + + + + + + + + + //=============================================================blender_rgba + template struct blender_rgba + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover=0) + { + calc_type r = p[Order::R]; + calc_type g = p[Order::G]; + calc_type b = p[Order::B]; + calc_type a = p[Order::A]; + p[Order::R] = (value_type)(((cr - r) * alpha + (r << base_shift)) >> base_shift); + p[Order::G] = (value_type)(((cg - g) * alpha + (g << base_shift)) >> base_shift); + p[Order::B] = (value_type)(((cb - b) * alpha + (b << base_shift)) >> base_shift); + p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift)); + } + }; + + //=========================================================blender_rgba_pre + template struct blender_rgba_pre + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + alpha = color_type::base_mask - alpha; + cover = (cover + 1) << (base_shift - 8); + p[Order::R] = (value_type)((p[Order::R] * alpha + cr * cover) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * alpha + cg * cover) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * alpha + cb * cover) >> base_shift); + p[Order::A] = (value_type)(base_mask - ((alpha * (base_mask - p[Order::A])) >> base_shift)); + } + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha) + { + alpha = color_type::base_mask - alpha; + p[Order::R] = (value_type)(((p[Order::R] * alpha) >> base_shift) + cr); + p[Order::G] = (value_type)(((p[Order::G] * alpha) >> base_shift) + cg); + p[Order::B] = (value_type)(((p[Order::B] * alpha) >> base_shift) + cb); + p[Order::A] = (value_type)(base_mask - ((alpha * (base_mask - p[Order::A])) >> base_shift)); + } + }; + + //======================================================blender_rgba_plain + template struct blender_rgba_plain + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e { base_shift = color_type::base_shift }; + + //-------------------------------------------------------------------- + static AGG_INLINE void blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover=0) + { + if(alpha == 0) return; + calc_type a = p[Order::A]; + calc_type r = p[Order::R] * a; + calc_type g = p[Order::G] * a; + calc_type b = p[Order::B] * a; + a = ((alpha + a) << base_shift) - alpha * a; + p[Order::A] = (value_type)(a >> base_shift); + p[Order::R] = (value_type)((((cr << base_shift) - r) * alpha + (r << base_shift)) / a); + p[Order::G] = (value_type)((((cg << base_shift) - g) * alpha + (g << base_shift)) / a); + p[Order::B] = (value_type)((((cb << base_shift) - b) * alpha + (b << base_shift)) / a); + } + }; + + + + + + + + + + + + //=========================================================comp_op_rgba_clear + template struct comp_op_rgba_clear + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(value_type* p, + unsigned, unsigned, unsigned, unsigned, + unsigned cover) + { + if(cover < 255) + { + cover = 255 - cover; + p[Order::R] = (value_type)((p[Order::R] * cover + 255) >> 8); + p[Order::G] = (value_type)((p[Order::G] * cover + 255) >> 8); + p[Order::B] = (value_type)((p[Order::B] * cover + 255) >> 8); + p[Order::A] = (value_type)((p[Order::A] * cover + 255) >> 8); + } + else + { + p[0] = p[1] = p[2] = p[3] = 0; + } + } + }; + + //===========================================================comp_op_rgba_src + template struct comp_op_rgba_src + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + unsigned alpha = 255 - cover; + p[Order::R] = (value_type)(((p[Order::R] * alpha + 255) >> 8) + ((sr * cover + 255) >> 8)); + p[Order::G] = (value_type)(((p[Order::G] * alpha + 255) >> 8) + ((sg * cover + 255) >> 8)); + p[Order::B] = (value_type)(((p[Order::B] * alpha + 255) >> 8) + ((sb * cover + 255) >> 8)); + p[Order::A] = (value_type)(((p[Order::A] * alpha + 255) >> 8) + ((sa * cover + 255) >> 8)); + } + else + { + p[Order::R] = sr; + p[Order::G] = sg; + p[Order::B] = sb; + p[Order::A] = sa; + } + } + }; + + //===========================================================comp_op_rgba_dst + template struct comp_op_rgba_dst + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + + static AGG_INLINE void blend_pix(value_type*, + unsigned, unsigned, unsigned, + unsigned, unsigned) + { + } + }; + + //======================================================comp_op_rgba_src_over + template struct comp_op_rgba_src_over + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + calc_type s1a = base_mask - sa; + p[Order::R] = (value_type)(sr + ((p[Order::R] * s1a + base_mask) >> base_shift)); + p[Order::G] = (value_type)(sg + ((p[Order::G] * s1a + base_mask) >> base_shift)); + p[Order::B] = (value_type)(sb + ((p[Order::B] * s1a + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift)); + } + }; + + //======================================================comp_op_rgba_dst_over + template struct comp_op_rgba_dst_over + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Dca + Sca.(1 - Da) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + calc_type d1a = base_mask - p[Order::A]; + p[Order::R] = (value_type)(p[Order::R] + ((sr * d1a + base_mask) >> base_shift)); + p[Order::G] = (value_type)(p[Order::G] + ((sg * d1a + base_mask) >> base_shift)); + p[Order::B] = (value_type)(p[Order::B] + ((sb * d1a + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift)); + } + }; + + //======================================================comp_op_rgba_src_in + template struct comp_op_rgba_src_in + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca.Da + // Da' = Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + calc_type da = p[Order::A]; + if(cover < 255) + { + unsigned alpha = 255 - cover; + p[Order::R] = (value_type)(((p[Order::R] * alpha + 255) >> 8) + ((((sr * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::G] = (value_type)(((p[Order::G] * alpha + 255) >> 8) + ((((sg * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::B] = (value_type)(((p[Order::B] * alpha + 255) >> 8) + ((((sb * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::A] = (value_type)(((p[Order::A] * alpha + 255) >> 8) + ((((sa * da + base_mask) >> base_shift) * cover + 255) >> 8)); + } + else + { + p[Order::R] = (value_type)((sr * da + base_mask) >> base_shift); + p[Order::G] = (value_type)((sg * da + base_mask) >> base_shift); + p[Order::B] = (value_type)((sb * da + base_mask) >> base_shift); + p[Order::A] = (value_type)((sa * da + base_mask) >> base_shift); + } + } + }; + + //======================================================comp_op_rgba_dst_in + template struct comp_op_rgba_dst_in + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Dca.Sa + // Da' = Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned, unsigned, unsigned, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sa = base_mask - ((cover * (base_mask - sa) + 255) >> 8); + } + p[Order::R] = (value_type)((p[Order::R] * sa + base_mask) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * sa + base_mask) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * sa + base_mask) >> base_shift); + p[Order::A] = (value_type)((p[Order::A] * sa + base_mask) >> base_shift); + } + }; + + //======================================================comp_op_rgba_src_out + template struct comp_op_rgba_src_out + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca.(1 - Da) + // Da' = Sa.(1 - Da) + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + calc_type da = base_mask - p[Order::A]; + if(cover < 255) + { + unsigned alpha = 255 - cover; + p[Order::R] = (value_type)(((p[Order::R] * alpha + 255) >> 8) + ((((sr * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::G] = (value_type)(((p[Order::G] * alpha + 255) >> 8) + ((((sg * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::B] = (value_type)(((p[Order::B] * alpha + 255) >> 8) + ((((sb * da + base_mask) >> base_shift) * cover + 255) >> 8)); + p[Order::A] = (value_type)(((p[Order::A] * alpha + 255) >> 8) + ((((sa * da + base_mask) >> base_shift) * cover + 255) >> 8)); + } + else + { + p[Order::R] = (value_type)((sr * da + base_mask) >> base_shift); + p[Order::G] = (value_type)((sg * da + base_mask) >> base_shift); + p[Order::B] = (value_type)((sb * da + base_mask) >> base_shift); + p[Order::A] = (value_type)((sa * da + base_mask) >> base_shift); + } + } + }; + + //======================================================comp_op_rgba_dst_out + template struct comp_op_rgba_dst_out + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Dca.(1 - Sa) + // Da' = Da.(1 - Sa) + static AGG_INLINE void blend_pix(value_type* p, + unsigned, unsigned, unsigned, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sa = (sa * cover + 255) >> 8; + } + sa = base_mask - sa; + p[Order::R] = (value_type)((p[Order::R] * sa + base_shift) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * sa + base_shift) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * sa + base_shift) >> base_shift); + p[Order::A] = (value_type)((p[Order::A] * sa + base_shift) >> base_shift); + } + }; + + //=====================================================comp_op_rgba_src_atop + template struct comp_op_rgba_src_atop + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca.Da + Dca.(1 - Sa) + // Da' = Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + calc_type da = p[Order::A]; + sa = base_mask - sa; + p[Order::R] = (value_type)((sr * da + p[Order::R] * sa + base_mask) >> base_shift); + p[Order::G] = (value_type)((sg * da + p[Order::G] * sa + base_mask) >> base_shift); + p[Order::B] = (value_type)((sb * da + p[Order::B] * sa + base_mask) >> base_shift); + } + }; + + //=====================================================comp_op_rgba_dst_atop + template struct comp_op_rgba_dst_atop + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Dca.Sa + Sca.(1 - Da) + // Da' = Sa + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + calc_type da = base_mask - p[Order::A]; + if(cover < 255) + { + unsigned alpha = 255 - cover; + sr = (p[Order::R] * sa + sr * da + base_mask) >> base_shift; + sg = (p[Order::G] * sa + sg * da + base_mask) >> base_shift; + sb = (p[Order::B] * sa + sb * da + base_mask) >> base_shift; + p[Order::R] = (value_type)(((p[Order::R] * alpha + 255) >> 8) + ((sr * cover + 255) >> 8)); + p[Order::G] = (value_type)(((p[Order::G] * alpha + 255) >> 8) + ((sg * cover + 255) >> 8)); + p[Order::B] = (value_type)(((p[Order::B] * alpha + 255) >> 8) + ((sb * cover + 255) >> 8)); + p[Order::A] = (value_type)(((p[Order::A] * alpha + 255) >> 8) + ((sa * cover + 255) >> 8)); + + } + else + { + p[Order::R] = (value_type)((p[Order::R] * sa + sr * da + base_mask) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * sa + sg * da + base_mask) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * sa + sb * da + base_mask) >> base_shift); + p[Order::A] = (value_type)sa; + } + } + }; + + //=========================================================comp_op_rgba_xor + template struct comp_op_rgba_xor + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca.(1 - Da) + Dca.(1 - Sa) + // Da' = Sa + Da - 2.Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type s1a = base_mask - sa; + calc_type d1a = base_mask - p[Order::A]; + p[Order::R] = (value_type)((p[Order::R] * s1a + sr * d1a + base_mask) >> base_shift); + p[Order::G] = (value_type)((p[Order::G] * s1a + sg * d1a + base_mask) >> base_shift); + p[Order::B] = (value_type)((p[Order::B] * s1a + sb * d1a + base_mask) >> base_shift); + p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask/2) >> (base_shift - 1))); + } + } + }; + + //=========================================================comp_op_rgba_plus + template struct comp_op_rgba_plus + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca + Dca + // Da' = Sa + Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type dr = p[Order::R] + sr; + calc_type dg = p[Order::G] + sg; + calc_type db = p[Order::B] + sb; + calc_type da = p[Order::A] + sa; + p[Order::R] = (dr > base_mask) ? (value_type)base_mask : dr; + p[Order::G] = (dg > base_mask) ? (value_type)base_mask : dg; + p[Order::B] = (db > base_mask) ? (value_type)base_mask : db; + p[Order::A] = (da > base_mask) ? (value_type)base_mask : da; + } + } + }; + + //========================================================comp_op_rgba_minus + template struct comp_op_rgba_minus + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Dca - Sca + // Da' = 1 - (1 - Sa).(1 - Da) + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type dr = p[Order::R] - sr; + calc_type dg = p[Order::G] - sg; + calc_type db = p[Order::B] - sb; + p[Order::R] = (dr > base_mask) ? 0 : dr; + p[Order::G] = (dg > base_mask) ? 0 : dg; + p[Order::B] = (db > base_mask) ? 0 : db; + p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift)); + //p[Order::A] = (value_type)(base_mask - (((base_mask - sa) * (base_mask - p[Order::A]) + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_multiply + template struct comp_op_rgba_multiply + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type s1a = base_mask - sa; + calc_type d1a = base_mask - p[Order::A]; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + p[Order::R] = (value_type)((sr * dr + sr * d1a + dr * s1a + base_mask) >> base_shift); + p[Order::G] = (value_type)((sg * dg + sg * d1a + dg * s1a + base_mask) >> base_shift); + p[Order::B] = (value_type)((sb * db + sb * d1a + db * s1a + base_mask) >> base_shift); + p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_screen + template struct comp_op_rgba_screen + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = Sca + Dca - Sca.Dca + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + p[Order::R] = (value_type)(sr + dr - ((sr * dr + base_mask) >> base_shift)); + p[Order::G] = (value_type)(sg + dg - ((sg * dg + base_mask) >> base_shift)); + p[Order::B] = (value_type)(sb + db - ((sb * db + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_overlay + template struct comp_op_rgba_overlay + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // if 2.Dca < Da + // Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise + // Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa) + // + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + calc_type sada = sa * p[Order::A]; + + p[Order::R] = (value_type)(((2*dr < da) ? + 2*sr*dr + sr*d1a + dr*s1a : + sada - 2*(da - dr)*(sa - sr) + sr*d1a + dr*s1a + base_mask) >> base_shift); + + p[Order::G] = (value_type)(((2*dg < da) ? + 2*sg*dg + sg*d1a + dg*s1a : + sada - 2*(da - dg)*(sa - sg) + sg*d1a + dg*s1a + base_mask) >> base_shift); + + p[Order::B] = (value_type)(((2*db < da) ? + 2*sb*db + sb*d1a + db*s1a : + sada - 2*(da - db)*(sa - sb) + sb*d1a + db*s1a + base_mask) >> base_shift); + + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + + template inline T sd_min(T a, T b) { return (a < b) ? a : b; } + template inline T sd_max(T a, T b) { return (a > b) ? a : b; } + + //=====================================================comp_op_rgba_darken + template struct comp_op_rgba_darken + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + + p[Order::R] = (value_type)((sd_min(sr * da, dr * sa) + sr * d1a + dr * s1a + base_mask) >> base_shift); + p[Order::G] = (value_type)((sd_min(sg * da, dg * sa) + sg * d1a + dg * s1a + base_mask) >> base_shift); + p[Order::B] = (value_type)((sd_min(sb * da, db * sa) + sb * d1a + db * s1a + base_mask) >> base_shift); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_lighten + template struct comp_op_rgba_lighten + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + + p[Order::R] = (value_type)((sd_max(sr * da, dr * sa) + sr * d1a + dr * s1a + base_mask) >> base_shift); + p[Order::G] = (value_type)((sd_max(sg * da, dg * sa) + sg * d1a + dg * s1a + base_mask) >> base_shift); + p[Order::B] = (value_type)((sd_max(sb * da, db * sa) + sb * d1a + db * s1a + base_mask) >> base_shift); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_color_dodge + template struct comp_op_rgba_color_dodge + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // if Sca.Da + Dca.Sa >= Sa.Da + // Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise + // Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa) + // + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + long_type drsa = dr * sa; + long_type dgsa = dg * sa; + long_type dbsa = db * sa; + long_type srda = sr * da; + long_type sgda = sg * da; + long_type sbda = sb * da; + long_type sada = sa * da; + + p[Order::R] = (value_type)((srda + drsa >= sada) ? + (sada + sr * d1a + dr * s1a + base_mask) >> base_shift : + drsa / (base_mask - (sr << base_shift) / sa) + ((sr * d1a + dr * s1a + base_mask) >> base_shift)); + + p[Order::G] = (value_type)((sgda + dgsa >= sada) ? + (sada + sg * d1a + dg * s1a + base_mask) >> base_shift : + dgsa / (base_mask - (sg << base_shift) / sa) + ((sg * d1a + dg * s1a + base_mask) >> base_shift)); + + p[Order::B] = (value_type)((sbda + dbsa >= sada) ? + (sada + sb * d1a + db * s1a + base_mask) >> base_shift : + dbsa / (base_mask - (sb << base_shift) / sa) + ((sb * d1a + db * s1a + base_mask) >> base_shift)); + + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_color_burn + template struct comp_op_rgba_color_burn + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // if Sca.Da + Dca.Sa <= Sa.Da + // Dca' = Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise + // Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa) + // + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + long_type drsa = dr * sa; + long_type dgsa = dg * sa; + long_type dbsa = db * sa; + long_type srda = sr * da; + long_type sgda = sg * da; + long_type sbda = sb * da; + long_type sada = sa * da; + + p[Order::R] = (value_type)(((srda + drsa <= sada) ? + sr * d1a + dr * s1a : + sa * (srda + drsa - sada) / sr + sr * d1a + dr * s1a + base_mask) >> base_shift); + + p[Order::G] = (value_type)(((sgda + dgsa <= sada) ? + sg * d1a + dg * s1a : + sa * (sgda + dgsa - sada) / sg + sg * d1a + dg * s1a + base_mask) >> base_shift); + + p[Order::B] = (value_type)(((sbda + dbsa <= sada) ? + sb * d1a + db * s1a : + sa * (sbda + dbsa - sada) / sb + sb * d1a + db * s1a + base_mask) >> base_shift); + + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_hard_light + template struct comp_op_rgba_hard_light + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // if 2.Sca < Sa + // Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise + // Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa) + // + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + calc_type sada = sa * da; + + p[Order::R] = (value_type)(((2*sr < sa) ? + 2*sr*dr + sr*d1a + dr*s1a : + sada - 2*(da - dr)*(sa - sr) + sr*d1a + dr*s1a + base_mask) >> base_shift); + + p[Order::G] = (value_type)(((2*sg < sa) ? + 2*sg*dg + sg*d1a + dg*s1a : + sada - 2*(da - dg)*(sa - sg) + sg*d1a + dg*s1a + base_mask) >> base_shift); + + p[Order::B] = (value_type)(((2*sb < sa) ? + 2*sb*db + sb*d1a + db*s1a : + sada - 2*(da - db)*(sa - sb) + sb*d1a + db*s1a + base_mask) >> base_shift); + + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_soft_light + template struct comp_op_rgba_soft_light + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // if 2.Sca < Sa + // Dca' = Dca.(Sa + (1 - Dca/Da).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise if 8.Dca <= Da + // Dca' = Dca.(Sa + (1 - Dca/Da).(2.Sca - Sa).(3 - 8.Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa) + // otherwise + // Dca' = (Dca.Sa + ((Dca/Da)^(0.5).Da - Dca).(2.Sca - Sa)) + Sca.(1 - Da) + Dca.(1 - Sa) + // + // Da' = Sa + Da - Sa.Da + + static AGG_INLINE void blend_pix(value_type* p, + unsigned r, unsigned g, unsigned b, + unsigned a, unsigned cover) + { + double sr = double(r * cover) / (base_mask * 255); + double sg = double(g * cover) / (base_mask * 255); + double sb = double(b * cover) / (base_mask * 255); + double sa = double(a * cover) / (base_mask * 255); + if(sa > 0) + { + double dr = double(p[Order::R]) / base_mask; + double dg = double(p[Order::G]) / base_mask; + double db = double(p[Order::B]) / base_mask; + double da = double(p[Order::A] ? p[Order::A] : 1) / base_mask; + if(cover < 255) + { + a = (a * cover + 255) >> 8; + } + + if(2*sr < sa) dr = dr*(sa + (1 - dr/da)*(2*sr - sa)) + sr*(1 - da) + dr*(1 - sa); + else if(8*dr <= da) dr = dr*(sa + (1 - dr/da)*(2*sr - sa)*(3 - 8*dr/da)) + sr*(1 - da) + dr*(1 - sa); + else dr = (dr*sa + (sqrt(dr/da)*da - dr)*(2*sr - sa)) + sr*(1 - da) + dr*(1 - sa); + + if(2*sg < sa) dg = dg*(sa + (1 - dg/da)*(2*sg - sa)) + sg*(1 - da) + dg*(1 - sa); + else if(8*dg <= da) dg = dg*(sa + (1 - dg/da)*(2*sg - sa)*(3 - 8*dg/da)) + sg*(1 - da) + dg*(1 - sa); + else dg = (dg*sa + (sqrt(dg/da)*da - dg)*(2*sg - sa)) + sg*(1 - da) + dg*(1 - sa); + + if(2*sb < sa) db = db*(sa + (1 - db/da)*(2*sb - sa)) + sb*(1 - da) + db*(1 - sa); + else if(8*db <= da) db = db*(sa + (1 - db/da)*(2*sb - sa)*(3 - 8*db/da)) + sb*(1 - da) + db*(1 - sa); + else db = (db*sa + (sqrt(db/da)*da - db)*(2*sb - sa)) + sb*(1 - da) + db*(1 - sa); + + p[Order::R] = (value_type)uround(dr * base_mask); + p[Order::G] = (value_type)uround(dg * base_mask); + p[Order::B] = (value_type)uround(db * base_mask); + p[Order::A] = (value_type)(a + p[Order::A] - ((a * p[Order::A] + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_difference + template struct comp_op_rgba_difference + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask + }; + + // Dca' = Sca + Dca - 2.min(Sca.Da, Dca.Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + p[Order::R] = (value_type)(sr + dr - ((2 * sd_min(sr*da, dr*sa) + base_mask) >> base_shift)); + p[Order::G] = (value_type)(sg + dg - ((2 * sd_min(sg*da, dg*sa) + base_mask) >> base_shift)); + p[Order::B] = (value_type)(sb + db - ((2 * sd_min(sb*da, db*sa) + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_exclusion + template struct comp_op_rgba_exclusion + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type d1a = base_mask - p[Order::A]; + calc_type s1a = base_mask - sa; + calc_type dr = p[Order::R]; + calc_type dg = p[Order::G]; + calc_type db = p[Order::B]; + calc_type da = p[Order::A]; + p[Order::R] = (value_type)((sr*da + dr*sa - 2*sr*dr + sr*d1a + dr*s1a + base_mask) >> base_shift); + p[Order::G] = (value_type)((sg*da + dg*sa - 2*sg*dg + sg*d1a + dg*s1a + base_mask) >> base_shift); + p[Order::B] = (value_type)((sb*da + db*sa - 2*sb*db + sb*d1a + db*s1a + base_mask) >> base_shift); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=====================================================comp_op_rgba_contrast + template struct comp_op_rgba_contrast + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + long_type dr = p[Order::R]; + long_type dg = p[Order::G]; + long_type db = p[Order::B]; + int da = p[Order::A]; + long_type d2a = da >> 1; + unsigned s2a = sa >> 1; + + int r = (int)((((dr - d2a) * int((sr - s2a)*2 + base_mask)) >> base_shift) + d2a); + int g = (int)((((dg - d2a) * int((sg - s2a)*2 + base_mask)) >> base_shift) + d2a); + int b = (int)((((db - d2a) * int((sb - s2a)*2 + base_mask)) >> base_shift) + d2a); + + r = (r < 0) ? 0 : r; + g = (g < 0) ? 0 : g; + b = (b < 0) ? 0 : b; + + p[Order::R] = (value_type)((r > da) ? da : r); + p[Order::G] = (value_type)((g > da) ? da : g); + p[Order::B] = (value_type)((b > da) ? da : b); + } + }; + + //=====================================================comp_op_rgba_invert + template struct comp_op_rgba_invert + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = (Da - Dca) * Sa + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + sa = (sa * cover + 255) >> 8; + if(sa) + { + calc_type da = p[Order::A]; + calc_type dr = ((da - p[Order::R]) * sa + base_mask) >> base_shift; + calc_type dg = ((da - p[Order::G]) * sa + base_mask) >> base_shift; + calc_type db = ((da - p[Order::B]) * sa + base_mask) >> base_shift; + calc_type s1a = base_mask - sa; + p[Order::R] = (value_type)(dr + ((p[Order::R] * s1a + base_mask) >> base_shift)); + p[Order::G] = (value_type)(dg + ((p[Order::G] * s1a + base_mask) >> base_shift)); + p[Order::B] = (value_type)(db + ((p[Order::B] * s1a + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + //=================================================comp_op_rgba_invert_rgb + template struct comp_op_rgba_invert_rgb + { + typedef ColorT color_type; + typedef Order order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + // Dca' = (Da - Dca) * Sca + Dca.(1 - Sa) + // Da' = Sa + Da - Sa.Da + static AGG_INLINE void blend_pix(value_type* p, + unsigned sr, unsigned sg, unsigned sb, + unsigned sa, unsigned cover) + { + if(cover < 255) + { + sr = (sr * cover + 255) >> 8; + sg = (sg * cover + 255) >> 8; + sb = (sb * cover + 255) >> 8; + sa = (sa * cover + 255) >> 8; + } + if(sa) + { + calc_type da = p[Order::A]; + calc_type dr = ((da - p[Order::R]) * sr + base_mask) >> base_shift; + calc_type dg = ((da - p[Order::G]) * sg + base_mask) >> base_shift; + calc_type db = ((da - p[Order::B]) * sb + base_mask) >> base_shift; + calc_type s1a = base_mask - sa; + p[Order::R] = (value_type)(dr + ((p[Order::R] * s1a + base_mask) >> base_shift)); + p[Order::G] = (value_type)(dg + ((p[Order::G] * s1a + base_mask) >> base_shift)); + p[Order::B] = (value_type)(db + ((p[Order::B] * s1a + base_mask) >> base_shift)); + p[Order::A] = (value_type)(sa + da - ((sa * da + base_mask) >> base_shift)); + } + } + }; + + + + + + //======================================================comp_op_table_rgba + template struct comp_op_table_rgba + { + typedef typename ColorT::value_type value_type; + typedef void (*comp_op_func_type)(value_type* p, + unsigned cr, + unsigned cg, + unsigned cb, + unsigned ca, + unsigned cover); + static comp_op_func_type g_comp_op_func[]; + }; + + //==========================================================g_comp_op_func + template + typename comp_op_table_rgba::comp_op_func_type + comp_op_table_rgba::g_comp_op_func[] = + { + comp_op_rgba_clear ::blend_pix, + comp_op_rgba_src ::blend_pix, + comp_op_rgba_dst ::blend_pix, + comp_op_rgba_src_over ::blend_pix, + comp_op_rgba_dst_over ::blend_pix, + comp_op_rgba_src_in ::blend_pix, + comp_op_rgba_dst_in ::blend_pix, + comp_op_rgba_src_out ::blend_pix, + comp_op_rgba_dst_out ::blend_pix, + comp_op_rgba_src_atop ::blend_pix, + comp_op_rgba_dst_atop ::blend_pix, + comp_op_rgba_xor ::blend_pix, + comp_op_rgba_plus ::blend_pix, + comp_op_rgba_minus ::blend_pix, + comp_op_rgba_multiply ::blend_pix, + comp_op_rgba_screen ::blend_pix, + comp_op_rgba_overlay ::blend_pix, + comp_op_rgba_darken ::blend_pix, + comp_op_rgba_lighten ::blend_pix, + comp_op_rgba_color_dodge::blend_pix, + comp_op_rgba_color_burn ::blend_pix, + comp_op_rgba_hard_light ::blend_pix, + comp_op_rgba_soft_light ::blend_pix, + comp_op_rgba_difference ::blend_pix, + comp_op_rgba_exclusion ::blend_pix, + comp_op_rgba_contrast ::blend_pix, + comp_op_rgba_invert ::blend_pix, + comp_op_rgba_invert_rgb ::blend_pix, + 0 + }; + + + //==============================================================comp_op_e + enum comp_op_e + { + comp_op_clear, //----comp_op_clear + comp_op_src, //----comp_op_src + comp_op_dst, //----comp_op_dst + comp_op_src_over, //----comp_op_src_over + comp_op_dst_over, //----comp_op_dst_over + comp_op_src_in, //----comp_op_src_in + comp_op_dst_in, //----comp_op_dst_in + comp_op_src_out, //----comp_op_src_out + comp_op_dst_out, //----comp_op_dst_out + comp_op_src_atop, //----comp_op_src_atop + comp_op_dst_atop, //----comp_op_dst_atop + comp_op_xor, //----comp_op_xor + comp_op_plus, //----comp_op_plus + comp_op_minus, //----comp_op_minus + comp_op_multiply, //----comp_op_multiply + comp_op_screen, //----comp_op_screen + comp_op_overlay, //----comp_op_overlay + comp_op_darken, //----comp_op_darken + comp_op_lighten, //----comp_op_lighten + comp_op_color_dodge, //----comp_op_color_dodge + comp_op_color_burn, //----comp_op_color_burn + comp_op_hard_light, //----comp_op_hard_light + comp_op_soft_light, //----comp_op_soft_light + comp_op_difference, //----comp_op_difference + comp_op_exclusion, //----comp_op_exclusion + comp_op_contrast, //----comp_op_contrast + comp_op_invert, //----comp_op_invert + comp_op_invert_rgb, //----comp_op_invert_rgb + + end_of_comp_op_e + }; + + + + + + + + //====================================================comp_op_adaptor_rgba + template struct comp_op_adaptor_rgba + { + typedef Order order_type; + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + comp_op_table_rgba::g_comp_op_func[op] + (p, (cr * ca + base_mask) >> base_shift, + (cg * ca + base_mask) >> base_shift, + (cb * ca + base_mask) >> base_shift, + ca, cover); + } + }; + + //=========================================comp_op_adaptor_clip_to_dst_rgba + template struct comp_op_adaptor_clip_to_dst_rgba + { + typedef Order order_type; + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + cr = (cr * ca + base_mask) >> base_shift; + cg = (cg * ca + base_mask) >> base_shift; + cb = (cb * ca + base_mask) >> base_shift; + unsigned da = p[Order::A]; + comp_op_table_rgba::g_comp_op_func[op] + (p, (cr * da + base_mask) >> base_shift, + (cg * da + base_mask) >> base_shift, + (cb * da + base_mask) >> base_shift, + (ca * da + base_mask) >> base_shift, + cover); + } + }; + + //================================================comp_op_adaptor_rgba_pre + template struct comp_op_adaptor_rgba_pre + { + typedef Order order_type; + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + comp_op_table_rgba::g_comp_op_func[op](p, cr, cg, cb, ca, cover); + } + }; + + //=====================================comp_op_adaptor_clip_to_dst_rgba_pre + template struct comp_op_adaptor_clip_to_dst_rgba_pre + { + typedef Order order_type; + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + unsigned da = p[Order::A]; + comp_op_table_rgba::g_comp_op_func[op] + (p, (cr * da + base_mask) >> base_shift, + (cg * da + base_mask) >> base_shift, + (cb * da + base_mask) >> base_shift, + (ca * da + base_mask) >> base_shift, + cover); + } + }; + + //=======================================================comp_adaptor_rgba + template struct comp_adaptor_rgba + { + typedef typename BlenderPre::order_type order_type; + typedef typename BlenderPre::color_type color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + BlenderPre::blend_pix(p, + (cr * ca + base_mask) >> base_shift, + (cg * ca + base_mask) >> base_shift, + (cb * ca + base_mask) >> base_shift, + ca, cover); + } + }; + + //==========================================comp_adaptor_clip_to_dst_rgba + template struct comp_adaptor_clip_to_dst_rgba + { + typedef typename BlenderPre::order_type order_type; + typedef typename BlenderPre::color_type color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + cr = (cr * ca + base_mask) >> base_shift; + cg = (cg * ca + base_mask) >> base_shift; + cb = (cb * ca + base_mask) >> base_shift; + unsigned da = p[order_type::A]; + BlenderPre::blend_pix(p, + (cr * da + base_mask) >> base_shift, + (cg * da + base_mask) >> base_shift, + (cb * da + base_mask) >> base_shift, + (ca * da + base_mask) >> base_shift, + cover); + } + }; + + //======================================comp_adaptor_clip_to_dst_rgba_pre + template struct comp_adaptor_clip_to_dst_rgba_pre + { + typedef typename BlenderPre::order_type order_type; + typedef typename BlenderPre::color_type color_type; + typedef typename color_type::value_type value_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + static AGG_INLINE void blend_pix(unsigned op, value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned ca, + unsigned cover) + { + unsigned da = p[order_type::A]; + BlenderPre::blend_pix(p, + (cr * da + base_mask) >> base_shift, + (cg * da + base_mask) >> base_shift, + (cb * da + base_mask) >> base_shift, + (ca * da + base_mask) >> base_shift, + cover); + } + }; + + + + + + + //===============================================copy_or_blend_rgba_wrapper + template struct copy_or_blend_rgba_wrapper + { + typedef typename Blender::color_type color_type; + typedef typename Blender::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + static AGG_INLINE void copy_or_blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha) + { + if(alpha) + { + if(alpha == base_mask) + { + p[order_type::R] = cr; + p[order_type::G] = cg; + p[order_type::B] = cb; + p[order_type::A] = base_mask; + } + else + { + Blender::blend_pix(p, cr, cg, cb, alpha); + } + } + } + + //-------------------------------------------------------------------- + static AGG_INLINE void copy_or_blend_pix(value_type* p, + unsigned cr, unsigned cg, unsigned cb, + unsigned alpha, + unsigned cover) + { + if(cover == 255) + { + copy_or_blend_pix(p, cr, cg, cb, alpha); + } + else + { + if(alpha) + { + alpha = (alpha * (cover + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = cr; + p[order_type::G] = cg; + p[order_type::B] = cb; + p[order_type::A] = base_mask; + } + else + { + Blender::blend_pix(p, cr, cg, cb, alpha, cover); + } + } + } + } + }; + + + + + + + //=================================================pixfmt_alpha_blend_rgba + template + class pixfmt_alpha_blend_rgba + { + public: + typedef RenBuf rbuf_type; + typedef typename rbuf_type::row_data row_data; + typedef PixelT pixel_type; + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + typedef copy_or_blend_rgba_wrapper cob_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask, + pix_width = sizeof(pixel_type) + }; + + //-------------------------------------------------------------------- + pixfmt_alpha_blend_rgba() : m_rbuf(0) {} + explicit pixfmt_alpha_blend_rgba(rbuf_type& rb) : m_rbuf(&rb) {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + + //-------------------------------------------------------------------- + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if(r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + AGG_INLINE const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + AGG_INLINE row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + ((value_type*)p)[order_type::R] = c.r; + ((value_type*)p)[order_type::G] = c.g; + ((value_type*)p)[order_type::B] = c.b; + ((value_type*)p)[order_type::A] = c.a; + } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + const value_type* p = (const value_type*)m_rbuf->row_ptr(y); + if(p) + { + p += x << 2; + return color_type(p[order_type::R], + p[order_type::G], + p[order_type::B], + p[order_type::A]); + } + return color_type::no_color(); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, 1) + (x << 2); + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + p[order_type::A] = c.a; + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + cob_type::copy_or_blend_pix( + (value_type*)m_rbuf->row_ptr(x, y, 1) + (x << 2), + c.r, c.g, c.b, c.a, + cover); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + pixel_type v; + ((value_type*)&v)[order_type::R] = c.r; + ((value_type*)&v)[order_type::G] = c.g; + ((value_type*)&v)[order_type::B] = c.b; + ((value_type*)&v)[order_type::A] = c.a; + do + { + *(pixel_type*)p = v; + p += 4; + } + while(--len); + } + + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + pixel_type v; + ((value_type*)&v)[order_type::R] = c.r; + ((value_type*)&v)[order_type::G] = c.g; + ((value_type*)&v)[order_type::B] = c.b; + ((value_type*)&v)[order_type::A] = c.a; + do + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + *(pixel_type*)p = v; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + pixel_type v; + ((value_type*)&v)[order_type::R] = c.r; + ((value_type*)&v)[order_type::G] = c.g; + ((value_type*)&v)[order_type::B] = c.b; + ((value_type*)&v)[order_type::A] = c.a; + do + { + *(pixel_type*)p = v; + p += 4; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + blender_type::blend_pix(p, c.r, c.g, c.b, alpha); + p += 4; + } + while(--len); + } + else + { + do + { + blender_type::blend_pix(p, c.r, c.g, c.b, alpha, cover); + p += 4; + } + while(--len); + } + } + } + } + + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + if (c.a) + { + value_type* p; + calc_type alpha = (calc_type(c.a) * (cover + 1)) >> 8; + if(alpha == base_mask) + { + pixel_type v; + ((value_type*)&v)[order_type::R] = c.r; + ((value_type*)&v)[order_type::G] = c.g; + ((value_type*)&v)[order_type::B] = c.b; + ((value_type*)&v)[order_type::A] = c.a; + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + *(pixel_type*)p = v; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + blender_type::blend_pix(p, c.r, c.g, c.b, alpha); + } + while(--len); + } + else + { + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + blender_type::blend_pix(p, c.r, c.g, c.b, alpha, cover); + } + while(--len); + } + } + } + } + + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + p[order_type::A] = base_mask; + } + else + { + blender_type::blend_pix(p, c.r, c.g, c.b, alpha, *covers); + } + p += 4; + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + if (c.a) + { + do + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; + if(alpha == base_mask) + { + p[order_type::R] = c.r; + p[order_type::G] = c.g; + p[order_type::B] = c.b; + p[order_type::A] = base_mask; + } + else + { + blender_type::blend_pix(p, c.r, c.g, c.b, alpha, *covers); + } + ++covers; + } + while(--len); + } + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + p[order_type::A] = colors->a; + ++colors; + p += 4; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + p[order_type::A] = colors->a; + ++colors; + } + while(--len); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + if(covers) + { + do + { + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a, + *covers++); + p += 4; + ++colors; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a); + p += 4; + ++colors; + } + while(--len); + } + else + { + do + { + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a, + cover); + p += 4; + ++colors; + } + while(--len); + } + } + } + + + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p; + if(covers) + { + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a, + *covers++); + ++colors; + } + while(--len); + } + else + { + if(cover == 255) + { + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a); + ++colors; + } + while(--len); + } + else + { + do + { + p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + cob_type::copy_or_blend_pix(p, + colors->r, + colors->g, + colors->b, + colors->a, + cover); + ++colors; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + unsigned y; + for(y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if(r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + value_type* p = + (value_type*)m_rbuf->row_ptr(r.x1, y, len) + (r.x1 << 2); + do + { + f(p); + p += 4; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + void premultiply() + { + for_each_pixel(multiplier_rgba::premultiply); + } + + //-------------------------------------------------------------------- + void demultiply() + { + for_each_pixel(multiplier_rgba::demultiply); + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_rgba(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_rgba(g)); + } + + //-------------------------------------------------------------------- + template void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + const int8u* p = from.row_ptr(ysrc); + if(p) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::order_type src_order; + const value_type* psrc = (value_type*)from.row_ptr(ysrc); + if(psrc) + { + psrc += xsrc << 2; + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + int incp = 4; + if(xdst > xsrc) + { + psrc += (len-1) << 2; + pdst += (len-1) << 2; + incp = -4; + } + + if(cover == 255) + { + do + { + cob_type::copy_or_blend_pix(pdst, + psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B], + psrc[src_order::A]); + psrc += incp; + pdst += incp; + } + while(--len); + } + else + { + do + { + cob_type::copy_or_blend_pix(pdst, + psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B], + psrc[src_order::A], + cover); + psrc += incp; + pdst += incp; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + do + { + cob_type::copy_or_blend_pix(pdst, + color.r, color.g, color.b, color.a, + (*psrc * cover + base_mask) >> base_shift); + ++psrc; + pdst += 4; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + + if(cover == 255) + { + do + { + const color_type& color = color_lut[*psrc]; + cob_type::copy_or_blend_pix(pdst, + color.r, color.g, color.b, color.a); + ++psrc; + pdst += 4; + } + while(--len); + } + else + { + do + { + const color_type& color = color_lut[*psrc]; + cob_type::copy_or_blend_pix(pdst, + color.r, color.g, color.b, color.a, + cover); + ++psrc; + pdst += 4; + } + while(--len); + } + } + } + + private: + rbuf_type* m_rbuf; + }; + + + + + //================================================pixfmt_custom_blend_rgba + template class pixfmt_custom_blend_rgba + { + public: + typedef RenBuf rbuf_type; + typedef typename rbuf_type::row_data row_data; + typedef Blender blender_type; + typedef typename blender_type::color_type color_type; + typedef typename blender_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_scale = color_type::base_scale, + base_mask = color_type::base_mask, + pix_width = sizeof(value_type) * 4 + }; + + + //-------------------------------------------------------------------- + pixfmt_custom_blend_rgba() : m_rbuf(0), m_comp_op(3) {} + explicit pixfmt_custom_blend_rgba(rbuf_type& rb, unsigned comp_op=3) : + m_rbuf(&rb), + m_comp_op(comp_op) + {} + void attach(rbuf_type& rb) { m_rbuf = &rb; } + + //-------------------------------------------------------------------- + template + bool attach(PixFmt& pixf, int x1, int y1, int x2, int y2) + { + rect_i r(x1, y1, x2, y2); + if(r.clip(rect_i(0, 0, pixf.width()-1, pixf.height()-1))) + { + int stride = pixf.stride(); + m_rbuf->attach(pixf.pix_ptr(r.x1, stride < 0 ? r.y2 : r.y1), + (r.x2 - r.x1) + 1, + (r.y2 - r.y1) + 1, + stride); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_rbuf->width(); } + AGG_INLINE unsigned height() const { return m_rbuf->height(); } + AGG_INLINE int stride() const { return m_rbuf->stride(); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* row_ptr(int y) { return m_rbuf->row_ptr(y); } + AGG_INLINE const int8u* row_ptr(int y) const { return m_rbuf->row_ptr(y); } + AGG_INLINE row_data row(int y) const { return m_rbuf->row(y); } + + //-------------------------------------------------------------------- + AGG_INLINE int8u* pix_ptr(int x, int y) + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + AGG_INLINE const int8u* pix_ptr(int x, int y) const + { + return m_rbuf->row_ptr(y) + x * pix_width; + } + + //-------------------------------------------------------------------- + void comp_op(unsigned op) { m_comp_op = op; } + unsigned comp_op() const { return m_comp_op; } + + //-------------------------------------------------------------------- + AGG_INLINE static void make_pix(int8u* p, const color_type& c) + { + ((value_type*)p)[order_type::R] = c.r; + ((value_type*)p)[order_type::G] = c.g; + ((value_type*)p)[order_type::B] = c.b; + ((value_type*)p)[order_type::A] = c.a; + } + + //-------------------------------------------------------------------- + color_type pixel(int x, int y) const + { + const value_type* p = (value_type*)m_rbuf->row_ptr(y) + (x << 2); + return color_type(p[order_type::R], + p[order_type::G], + p[order_type::B], + p[order_type::A]); + } + + //-------------------------------------------------------------------- + void copy_pixel(int x, int y, const color_type& c) + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y, 1) + (x << 2), + c.r, c.g, c.b, c.a, 255); + } + + //-------------------------------------------------------------------- + void blend_pixel(int x, int y, const color_type& c, int8u cover) + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y, 1) + (x << 2), + c.r, c.g, c.b, c.a, + cover); + } + + //-------------------------------------------------------------------- + void copy_hline(int x, int y, unsigned len, const color_type& c) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2);; + do + { + blender_type::blend_pix(m_comp_op, p, c.r, c.g, c.b, c.a, 255); + p += 4; + } + while(--len); + } + + //-------------------------------------------------------------------- + void copy_vline(int x, int y, unsigned len, const color_type& c) + { + do + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2), + c.r, c.g, c.b, c.a, 255); + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_hline(int x, int y, unsigned len, + const color_type& c, int8u cover) + { + + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + blender_type::blend_pix(m_comp_op, p, c.r, c.g, c.b, c.a, cover); + p += 4; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y, unsigned len, + const color_type& c, int8u cover) + { + + do + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2), + c.r, c.g, c.b, c.a, + cover); + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, unsigned len, + const color_type& c, const int8u* covers) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + blender_type::blend_pix(m_comp_op, + p, c.r, c.g, c.b, c.a, + *covers++); + p += 4; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, unsigned len, + const color_type& c, const int8u* covers) + { + do + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2), + c.r, c.g, c.b, c.a, + *covers++); + } + while(--len); + } + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + p[order_type::A] = colors->a; + ++colors; + p += 4; + } + while(--len); + } + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + do + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2); + p[order_type::R] = colors->r; + p[order_type::G] = colors->g; + p[order_type::B] = colors->b; + p[order_type::A] = colors->a; + ++colors; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + value_type* p = (value_type*)m_rbuf->row_ptr(x, y, len) + (x << 2); + do + { + blender_type::blend_pix(m_comp_op, + p, + colors->r, + colors->g, + colors->b, + colors->a, + covers ? *covers++ : cover); + p += 4; + ++colors; + } + while(--len); + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + do + { + blender_type::blend_pix( + m_comp_op, + (value_type*)m_rbuf->row_ptr(x, y++, 1) + (x << 2), + colors->r, + colors->g, + colors->b, + colors->a, + covers ? *covers++ : cover); + ++colors; + } + while(--len); + + } + + //-------------------------------------------------------------------- + template void for_each_pixel(Function f) + { + unsigned y; + for(y = 0; y < height(); ++y) + { + row_data r = m_rbuf->row(y); + if(r.ptr) + { + unsigned len = r.x2 - r.x1 + 1; + value_type* p = + (value_type*)m_rbuf->row_ptr(r.x1, y, len) + (r.x1 << 2); + do + { + f(p); + p += 4; + } + while(--len); + } + } + } + + //-------------------------------------------------------------------- + void premultiply() + { + for_each_pixel(multiplier_rgba::premultiply); + } + + //-------------------------------------------------------------------- + void demultiply() + { + for_each_pixel(multiplier_rgba::demultiply); + } + + //-------------------------------------------------------------------- + template void apply_gamma_dir(const GammaLut& g) + { + for_each_pixel(apply_gamma_dir_rgba(g)); + } + + //-------------------------------------------------------------------- + template void apply_gamma_inv(const GammaLut& g) + { + for_each_pixel(apply_gamma_inv_rgba(g)); + } + + //-------------------------------------------------------------------- + template void copy_from(const RenBuf2& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len) + { + const int8u* p = from.row_ptr(ysrc); + if(p) + { + memmove(m_rbuf->row_ptr(xdst, ydst, len) + xdst * pix_width, + p + xsrc * pix_width, + len * pix_width); + } + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& from, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::order_type src_order; + const value_type* psrc = (const value_type*)from.row_ptr(ysrc); + if(psrc) + { + psrc += xsrc << 2; + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + + int incp = 4; + if(xdst > xsrc) + { + psrc += (len-1) << 2; + pdst += (len-1) << 2; + incp = -4; + } + + do + { + blender_type::blend_pix(m_comp_op, + pdst, + psrc[src_order::R], + psrc[src_order::G], + psrc[src_order::B], + psrc[src_order::A], + cover); + psrc += incp; + pdst += incp; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& from, + const color_type& color, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + do + { + blender_type::blend_pix(m_comp_op, + pdst, + color.r, color.g, color.b, color.a, + (*psrc * cover + base_mask) >> base_shift); + ++psrc; + pdst += 4; + } + while(--len); + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& from, + const color_type* color_lut, + int xdst, int ydst, + int xsrc, int ysrc, + unsigned len, + int8u cover) + { + typedef typename SrcPixelFormatRenderer::value_type src_value_type; + const src_value_type* psrc = (src_value_type*)from.row_ptr(ysrc); + if(psrc) + { + value_type* pdst = + (value_type*)m_rbuf->row_ptr(xdst, ydst, len) + (xdst << 2); + do + { + const color_type& color = color_lut[*psrc]; + blender_type::blend_pix(m_comp_op, + pdst, + color.r, color.g, color.b, color.a, + cover); + ++psrc; + pdst += 4; + } + while(--len); + } + } + + private: + rbuf_type* m_rbuf; + unsigned m_comp_op; + }; + + + + + //----------------------------------------------------------------------- + typedef blender_rgba blender_rgba32; //----blender_rgba32 + typedef blender_rgba blender_argb32; //----blender_argb32 + typedef blender_rgba blender_abgr32; //----blender_abgr32 + typedef blender_rgba blender_bgra32; //----blender_bgra32 + + typedef blender_rgba_pre blender_rgba32_pre; //----blender_rgba32_pre + typedef blender_rgba_pre blender_argb32_pre; //----blender_argb32_pre + typedef blender_rgba_pre blender_abgr32_pre; //----blender_abgr32_pre + typedef blender_rgba_pre blender_bgra32_pre; //----blender_bgra32_pre + + typedef blender_rgba_plain blender_rgba32_plain; //----blender_rgba32_plain + typedef blender_rgba_plain blender_argb32_plain; //----blender_argb32_plain + typedef blender_rgba_plain blender_abgr32_plain; //----blender_abgr32_plain + typedef blender_rgba_plain blender_bgra32_plain; //----blender_bgra32_plain + + typedef blender_rgba blender_rgba64; //----blender_rgba64 + typedef blender_rgba blender_argb64; //----blender_argb64 + typedef blender_rgba blender_abgr64; //----blender_abgr64 + typedef blender_rgba blender_bgra64; //----blender_bgra64 + + typedef blender_rgba_pre blender_rgba64_pre; //----blender_rgba64_pre + typedef blender_rgba_pre blender_argb64_pre; //----blender_argb64_pre + typedef blender_rgba_pre blender_abgr64_pre; //----blender_abgr64_pre + typedef blender_rgba_pre blender_bgra64_pre; //----blender_bgra64_pre + + + //----------------------------------------------------------------------- + typedef int32u pixel32_type; + typedef pixfmt_alpha_blend_rgba pixfmt_rgba32; //----pixfmt_rgba32 + typedef pixfmt_alpha_blend_rgba pixfmt_argb32; //----pixfmt_argb32 + typedef pixfmt_alpha_blend_rgba pixfmt_abgr32; //----pixfmt_abgr32 + typedef pixfmt_alpha_blend_rgba pixfmt_bgra32; //----pixfmt_bgra32 + + typedef pixfmt_alpha_blend_rgba pixfmt_rgba32_pre; //----pixfmt_rgba32_pre + typedef pixfmt_alpha_blend_rgba pixfmt_argb32_pre; //----pixfmt_argb32_pre + typedef pixfmt_alpha_blend_rgba pixfmt_abgr32_pre; //----pixfmt_abgr32_pre + typedef pixfmt_alpha_blend_rgba pixfmt_bgra32_pre; //----pixfmt_bgra32_pre + + typedef pixfmt_alpha_blend_rgba pixfmt_rgba32_plain; //----pixfmt_rgba32_plain + typedef pixfmt_alpha_blend_rgba pixfmt_argb32_plain; //----pixfmt_argb32_plain + typedef pixfmt_alpha_blend_rgba pixfmt_abgr32_plain; //----pixfmt_abgr32_plain + typedef pixfmt_alpha_blend_rgba pixfmt_bgra32_plain; //----pixfmt_bgra32_plain + + struct pixel64_type { int16u c[4]; }; + typedef pixfmt_alpha_blend_rgba pixfmt_rgba64; //----pixfmt_rgba64 + typedef pixfmt_alpha_blend_rgba pixfmt_argb64; //----pixfmt_argb64 + typedef pixfmt_alpha_blend_rgba pixfmt_abgr64; //----pixfmt_abgr64 + typedef pixfmt_alpha_blend_rgba pixfmt_bgra64; //----pixfmt_bgra64 + + typedef pixfmt_alpha_blend_rgba pixfmt_rgba64_pre; //----pixfmt_rgba64_pre + typedef pixfmt_alpha_blend_rgba pixfmt_argb64_pre; //----pixfmt_argb64_pre + typedef pixfmt_alpha_blend_rgba pixfmt_abgr64_pre; //----pixfmt_abgr64_pre + typedef pixfmt_alpha_blend_rgba pixfmt_bgra64_pre; //----pixfmt_bgra64_pre +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_pixfmt_transposer.h b/desmume/src/windows/agg/include/agg_pixfmt_transposer.h new file mode 100644 index 000000000..c5038d49f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_pixfmt_transposer.h @@ -0,0 +1,166 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_PIXFMT_TRANSPOSER_INCLUDED +#define AGG_PIXFMT_TRANSPOSER_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //=======================================================pixfmt_transposer + template class pixfmt_transposer + { + public: + typedef PixFmt pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::row_data row_data; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + pixfmt_transposer() : m_pixf(0) {} + explicit pixfmt_transposer(pixfmt_type& pixf) : m_pixf(&pixf) {} + void attach(pixfmt_type& pixf) { m_pixf = &pixf; } + + //-------------------------------------------------------------------- + AGG_INLINE unsigned width() const { return m_pixf->height(); } + AGG_INLINE unsigned height() const { return m_pixf->width(); } + + //-------------------------------------------------------------------- + AGG_INLINE color_type pixel(int x, int y) const + { + return m_pixf->pixel(y, x); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_pixel(int x, int y, const color_type& c) + { + m_pixf->copy_pixel(y, x, c); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_pixel(int x, int y, + const color_type& c, + int8u cover) + { + m_pixf->blend_pixel(y, x, c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_hline(int x, int y, + unsigned len, + const color_type& c) + { + m_pixf->copy_vline(y, x, len, c); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_vline(int x, int y, + unsigned len, + const color_type& c) + { + m_pixf->copy_hline(y, x, len, c); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_hline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + m_pixf->blend_vline(y, x, len, c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_vline(int x, int y, + unsigned len, + const color_type& c, + int8u cover) + { + m_pixf->blend_hline(y, x, len, c, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_solid_hspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + m_pixf->blend_solid_vspan(y, x, len, c, covers); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_solid_vspan(int x, int y, + unsigned len, + const color_type& c, + const int8u* covers) + { + m_pixf->blend_solid_hspan(y, x, len, c, covers); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_color_hspan(int x, int y, + unsigned len, + const color_type* colors) + { + m_pixf->copy_color_vspan(y, x, len, colors); + } + + //-------------------------------------------------------------------- + AGG_INLINE void copy_color_vspan(int x, int y, + unsigned len, + const color_type* colors) + { + m_pixf->copy_color_hspan(y, x, len, colors); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_color_hspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + m_pixf->blend_color_vspan(y, x, len, colors, covers, cover); + } + + //-------------------------------------------------------------------- + AGG_INLINE void blend_color_vspan(int x, int y, + unsigned len, + const color_type* colors, + const int8u* covers, + int8u cover) + { + m_pixf->blend_color_hspan(y, x, len, colors, covers, cover); + } + + private: + pixfmt_type* m_pixf; + }; +} + +#endif + + diff --git a/desmume/src/windows/agg/include/agg_rasterizer_cells_aa.h b/desmume/src/windows/agg/include/agg_rasterizer_cells_aa.h new file mode 100644 index 000000000..789f2b787 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_cells_aa.h @@ -0,0 +1,764 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_RASTERIZER_CELLS_AA_INCLUDED +#define AGG_RASTERIZER_CELLS_AA_INCLUDED + +#include +#include +#include "agg_math.h" +#include "agg_array.h" + + +namespace agg +{ + + //-----------------------------------------------------rasterizer_cells_aa + // An internal class that implements the main rasterization algorithm. + // Used in the rasterizer. Should not be used direcly. + template class rasterizer_cells_aa + { + enum cell_block_scale_e + { + cell_block_shift = 12, + cell_block_size = 1 << cell_block_shift, + cell_block_mask = cell_block_size - 1, + cell_block_pool = 256, + cell_block_limit = 1024 + }; + + struct sorted_y + { + unsigned start; + unsigned num; + }; + + public: + typedef Cell cell_type; + typedef rasterizer_cells_aa self_type; + + ~rasterizer_cells_aa(); + rasterizer_cells_aa(); + + void reset(); + void style(const cell_type& style_cell); + void line(int x1, int y1, int x2, int y2); + + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + void sort_cells(); + + unsigned total_cells() const + { + return m_num_cells; + } + + unsigned scanline_num_cells(unsigned y) const + { + return m_sorted_y[y - m_min_y].num; + } + + const cell_type* const* scanline_cells(unsigned y) const + { + return m_sorted_cells.data() + m_sorted_y[y - m_min_y].start; + } + + bool sorted() const { return m_sorted; } + + private: + rasterizer_cells_aa(const self_type&); + const self_type& operator = (const self_type&); + + void set_curr_cell(int x, int y); + void add_curr_cell(); + void render_hline(int ey, int x1, int y1, int x2, int y2); + void allocate_block(); + + private: + unsigned m_num_blocks; + unsigned m_max_blocks; + unsigned m_curr_block; + unsigned m_num_cells; + cell_type** m_cells; + cell_type* m_curr_cell_ptr; + pod_vector m_sorted_cells; + pod_vector m_sorted_y; + cell_type m_curr_cell; + cell_type m_style_cell; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + bool m_sorted; + }; + + + + + //------------------------------------------------------------------------ + template + rasterizer_cells_aa::~rasterizer_cells_aa() + { + if(m_num_blocks) + { + cell_type** ptr = m_cells + m_num_blocks - 1; + while(m_num_blocks--) + { + pod_allocator::deallocate(*ptr, cell_block_size); + ptr--; + } + pod_allocator::deallocate(m_cells, m_max_blocks); + } + } + + //------------------------------------------------------------------------ + template + rasterizer_cells_aa::rasterizer_cells_aa() : + m_num_blocks(0), + m_max_blocks(0), + m_curr_block(0), + m_num_cells(0), + m_cells(0), + m_curr_cell_ptr(0), + m_sorted_cells(), + m_sorted_y(), + m_min_x(0x7FFFFFFF), + m_min_y(0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF), + m_sorted(false) + { + m_style_cell.initial(); + m_curr_cell.initial(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::reset() + { + m_num_cells = 0; + m_curr_block = 0; + m_curr_cell.initial(); + m_style_cell.initial(); + m_sorted = false; + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; + m_max_x = -0x7FFFFFFF; + m_max_y = -0x7FFFFFFF; + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::add_curr_cell() + { + if(m_curr_cell.area | m_curr_cell.cover) + { + if((m_num_cells & cell_block_mask) == 0) + { + if(m_num_blocks >= cell_block_limit) return; + allocate_block(); + } + *m_curr_cell_ptr++ = m_curr_cell; + ++m_num_cells; + } + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::set_curr_cell(int x, int y) + { + if(m_curr_cell.not_equal(x, y, m_style_cell)) + { + add_curr_cell(); + m_curr_cell.style(m_style_cell); + m_curr_cell.x = x; + m_curr_cell.y = y; + m_curr_cell.cover = 0; + m_curr_cell.area = 0; + } + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::render_hline(int ey, + int x1, int y1, + int x2, int y2) + { + int ex1 = x1 >> poly_subpixel_shift; + int ex2 = x2 >> poly_subpixel_shift; + int fx1 = x1 & poly_subpixel_mask; + int fx2 = x2 & poly_subpixel_mask; + + int delta, p, first, dx; + int incr, lift, mod, rem; + + //trivial case. Happens often + if(y1 == y2) + { + set_curr_cell(ex2, ey); + return; + } + + //everything is located in a single cell. That is easy! + if(ex1 == ex2) + { + delta = y2 - y1; + m_curr_cell.cover += delta; + m_curr_cell.area += (fx1 + fx2) * delta; + return; + } + + //ok, we'll have to render a run of adjacent cells on the same + //hline... + p = (poly_subpixel_scale - fx1) * (y2 - y1); + first = poly_subpixel_scale; + incr = 1; + + dx = x2 - x1; + + if(dx < 0) + { + p = fx1 * (y2 - y1); + first = 0; + incr = -1; + dx = -dx; + } + + delta = p / dx; + mod = p % dx; + + if(mod < 0) + { + delta--; + mod += dx; + } + + m_curr_cell.cover += delta; + m_curr_cell.area += (fx1 + first) * delta; + + ex1 += incr; + set_curr_cell(ex1, ey); + y1 += delta; + + if(ex1 != ex2) + { + p = poly_subpixel_scale * (y2 - y1 + delta); + lift = p / dx; + rem = p % dx; + + if (rem < 0) + { + lift--; + rem += dx; + } + + mod -= dx; + + while (ex1 != ex2) + { + delta = lift; + mod += rem; + if(mod >= 0) + { + mod -= dx; + delta++; + } + + m_curr_cell.cover += delta; + m_curr_cell.area += poly_subpixel_scale * delta; + y1 += delta; + ex1 += incr; + set_curr_cell(ex1, ey); + } + } + delta = y2 - y1; + m_curr_cell.cover += delta; + m_curr_cell.area += (fx2 + poly_subpixel_scale - first) * delta; + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_cells_aa::style(const cell_type& style_cell) + { + m_style_cell.style(style_cell); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::line(int x1, int y1, int x2, int y2) + { + enum dx_limit_e { dx_limit = 16384 << poly_subpixel_shift }; + + int dx = x2 - x1; + + if(dx >= dx_limit || dx <= -dx_limit) + { + int cx = (x1 + x2) >> 1; + int cy = (y1 + y2) >> 1; + line(x1, y1, cx, cy); + line(cx, cy, x2, y2); + } + + int dy = y2 - y1; + int ex1 = x1 >> poly_subpixel_shift; + int ex2 = x2 >> poly_subpixel_shift; + int ey1 = y1 >> poly_subpixel_shift; + int ey2 = y2 >> poly_subpixel_shift; + int fy1 = y1 & poly_subpixel_mask; + int fy2 = y2 & poly_subpixel_mask; + + int x_from, x_to; + int p, rem, mod, lift, delta, first, incr; + + if(ex1 < m_min_x) m_min_x = ex1; + if(ex1 > m_max_x) m_max_x = ex1; + if(ey1 < m_min_y) m_min_y = ey1; + if(ey1 > m_max_y) m_max_y = ey1; + if(ex2 < m_min_x) m_min_x = ex2; + if(ex2 > m_max_x) m_max_x = ex2; + if(ey2 < m_min_y) m_min_y = ey2; + if(ey2 > m_max_y) m_max_y = ey2; + + set_curr_cell(ex1, ey1); + + //everything is on a single hline + if(ey1 == ey2) + { + render_hline(ey1, x1, fy1, x2, fy2); + return; + } + + //Vertical line - we have to calculate start and end cells, + //and then - the common values of the area and coverage for + //all cells of the line. We know exactly there's only one + //cell, so, we don't have to call render_hline(). + incr = 1; + if(dx == 0) + { + int ex = x1 >> poly_subpixel_shift; + int two_fx = (x1 - (ex << poly_subpixel_shift)) << 1; + int area; + + first = poly_subpixel_scale; + if(dy < 0) + { + first = 0; + incr = -1; + } + + x_from = x1; + + //render_hline(ey1, x_from, fy1, x_from, first); + delta = first - fy1; + m_curr_cell.cover += delta; + m_curr_cell.area += two_fx * delta; + + ey1 += incr; + set_curr_cell(ex, ey1); + + delta = first + first - poly_subpixel_scale; + area = two_fx * delta; + while(ey1 != ey2) + { + //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, first); + m_curr_cell.cover = delta; + m_curr_cell.area = area; + ey1 += incr; + set_curr_cell(ex, ey1); + } + //render_hline(ey1, x_from, poly_subpixel_scale - first, x_from, fy2); + delta = fy2 - poly_subpixel_scale + first; + m_curr_cell.cover += delta; + m_curr_cell.area += two_fx * delta; + return; + } + + //ok, we have to render several hlines + p = (poly_subpixel_scale - fy1) * dx; + first = poly_subpixel_scale; + + if(dy < 0) + { + p = fy1 * dx; + first = 0; + incr = -1; + dy = -dy; + } + + delta = p / dy; + mod = p % dy; + + if(mod < 0) + { + delta--; + mod += dy; + } + + x_from = x1 + delta; + render_hline(ey1, x1, fy1, x_from, first); + + ey1 += incr; + set_curr_cell(x_from >> poly_subpixel_shift, ey1); + + if(ey1 != ey2) + { + p = poly_subpixel_scale * dx; + lift = p / dy; + rem = p % dy; + + if(rem < 0) + { + lift--; + rem += dy; + } + mod -= dy; + + while(ey1 != ey2) + { + delta = lift; + mod += rem; + if (mod >= 0) + { + mod -= dy; + delta++; + } + + x_to = x_from + delta; + render_hline(ey1, x_from, poly_subpixel_scale - first, x_to, first); + x_from = x_to; + + ey1 += incr; + set_curr_cell(x_from >> poly_subpixel_shift, ey1); + } + } + render_hline(ey1, x_from, poly_subpixel_scale - first, x2, fy2); + } + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::allocate_block() + { + if(m_curr_block >= m_num_blocks) + { + if(m_num_blocks >= m_max_blocks) + { + cell_type** new_cells = + pod_allocator::allocate(m_max_blocks + + cell_block_pool); + + if(m_cells) + { + memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_type*)); + pod_allocator::deallocate(m_cells, m_max_blocks); + } + m_cells = new_cells; + m_max_blocks += cell_block_pool; + } + + m_cells[m_num_blocks++] = + pod_allocator::allocate(cell_block_size); + + } + m_curr_cell_ptr = m_cells[m_curr_block++]; + } + + + + //------------------------------------------------------------------------ + template static AGG_INLINE void swap_cells(T* a, T* b) + { + T temp = *a; + *a = *b; + *b = temp; + } + + + //------------------------------------------------------------------------ + enum + { + qsort_threshold = 9 + }; + + + //------------------------------------------------------------------------ + template + void qsort_cells(Cell** start, unsigned num) + { + Cell** stack[80]; + Cell*** top; + Cell** limit; + Cell** base; + + limit = start + num; + base = start; + top = stack; + + for (;;) + { + int len = int(limit - base); + + Cell** i; + Cell** j; + Cell** pivot; + + if(len > qsort_threshold) + { + // we use base + len/2 as the pivot + pivot = base + len / 2; + swap_cells(base, pivot); + + i = base + 1; + j = limit - 1; + + // now ensure that *i <= *base <= *j + if((*j)->x < (*i)->x) + { + swap_cells(i, j); + } + + if((*base)->x < (*i)->x) + { + swap_cells(base, i); + } + + if((*j)->x < (*base)->x) + { + swap_cells(base, j); + } + + for(;;) + { + int x = (*base)->x; + do i++; while( (*i)->x < x ); + do j--; while( x < (*j)->x ); + + if(i > j) + { + break; + } + + swap_cells(i, j); + } + + swap_cells(base, j); + + // now, push the largest sub-array + if(j - base > limit - i) + { + top[0] = base; + top[1] = j; + base = i; + } + else + { + top[0] = i; + top[1] = limit; + limit = j; + } + top += 2; + } + else + { + // the sub-array is small, perform insertion sort + j = base; + i = j + 1; + + for(; i < limit; j = i, i++) + { + for(; j[1]->x < (*j)->x; j--) + { + swap_cells(j + 1, j); + if (j == base) + { + break; + } + } + } + + if(top > stack) + { + top -= 2; + base = top[0]; + limit = top[1]; + } + else + { + break; + } + } + } + } + + + //------------------------------------------------------------------------ + template + void rasterizer_cells_aa::sort_cells() + { + if(m_sorted) return; //Perform sort only the first time. + + add_curr_cell(); + m_curr_cell.x = 0x7FFFFFFF; + m_curr_cell.y = 0x7FFFFFFF; + m_curr_cell.cover = 0; + m_curr_cell.area = 0; + + if(m_num_cells == 0) return; + +// DBG: Check to see if min/max works well. +//for(unsigned nc = 0; nc < m_num_cells; nc++) +//{ +// cell_type* cell = m_cells[nc >> cell_block_shift] + (nc & cell_block_mask); +// if(cell->x < m_min_x || +// cell->y < m_min_y || +// cell->x > m_max_x || +// cell->y > m_max_y) +// { +// cell = cell; // Breakpoint here +// } +//} + // Allocate the array of cell pointers + m_sorted_cells.allocate(m_num_cells, 16); + + // Allocate and zero the Y array + m_sorted_y.allocate(m_max_y - m_min_y + 1, 16); + m_sorted_y.zero(); + + // Create the Y-histogram (count the numbers of cells for each Y) + cell_type** block_ptr = m_cells; + cell_type* cell_ptr; + unsigned nb = m_num_cells >> cell_block_shift; + unsigned i; + while(nb--) + { + cell_ptr = *block_ptr++; + i = cell_block_size; + while(i--) + { + m_sorted_y[cell_ptr->y - m_min_y].start++; + ++cell_ptr; + } + } + + cell_ptr = *block_ptr++; + i = m_num_cells & cell_block_mask; + while(i--) + { + m_sorted_y[cell_ptr->y - m_min_y].start++; + ++cell_ptr; + } + + // Convert the Y-histogram into the array of starting indexes + unsigned start = 0; + for(i = 0; i < m_sorted_y.size(); i++) + { + unsigned v = m_sorted_y[i].start; + m_sorted_y[i].start = start; + start += v; + } + + // Fill the cell pointer array sorted by Y + block_ptr = m_cells; + nb = m_num_cells >> cell_block_shift; + while(nb--) + { + cell_ptr = *block_ptr++; + i = cell_block_size; + while(i--) + { + sorted_y& curr_y = m_sorted_y[cell_ptr->y - m_min_y]; + m_sorted_cells[curr_y.start + curr_y.num] = cell_ptr; + ++curr_y.num; + ++cell_ptr; + } + } + + cell_ptr = *block_ptr++; + i = m_num_cells & cell_block_mask; + while(i--) + { + sorted_y& curr_y = m_sorted_y[cell_ptr->y - m_min_y]; + m_sorted_cells[curr_y.start + curr_y.num] = cell_ptr; + ++curr_y.num; + ++cell_ptr; + } + + // Finally arrange the X-arrays + for(i = 0; i < m_sorted_y.size(); i++) + { + const sorted_y& curr_y = m_sorted_y[i]; + if(curr_y.num) + { + qsort_cells(m_sorted_cells.data() + curr_y.start, curr_y.num); + } + } + m_sorted = true; + } + + + + //------------------------------------------------------scanline_hit_test + class scanline_hit_test + { + public: + scanline_hit_test(int x) : m_x(x), m_hit(false) {} + + void reset_spans() {} + void finalize(int) {} + void add_cell(int x, int) + { + if(m_x == x) m_hit = true; + } + void add_span(int x, int len, int) + { + if(m_x >= x && m_x < x+len) m_hit = true; + } + unsigned num_spans() const { return 1; } + bool hit() const { return m_hit; } + + private: + int m_x; + bool m_hit; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_rasterizer_compound_aa.h b/desmume/src/windows/agg/include/agg_rasterizer_compound_aa.h new file mode 100644 index 000000000..c37b031c5 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_compound_aa.h @@ -0,0 +1,698 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.3 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_RASTERIZER_COMPOUND_AA_INCLUDED +#define AGG_RASTERIZER_COMPOUND_AA_INCLUDED + +#include "agg_rasterizer_cells_aa.h" +#include "agg_rasterizer_sl_clip.h" + +namespace agg +{ + + //-----------------------------------------------------------cell_style_aa + // A pixel cell. There're no constructors defined and it was done + // intentionally in order to avoid extra overhead when allocating an + // array of cells. + struct cell_style_aa + { + int x; + int y; + int cover; + int area; + int16 left, right; + + void initial() + { + x = 0x7FFFFFFF; + y = 0x7FFFFFFF; + cover = 0; + area = 0; + left = -1; + right = -1; + } + + void style(const cell_style_aa& c) + { + left = c.left; + right = c.right; + } + + int not_equal(int ex, int ey, const cell_style_aa& c) const + { + return (ex - x) | (ey - y) | (left - c.left) | (right - c.right); + } + }; + + + //===========================================================layer_order_e + enum layer_order_e + { + layer_unsorted, //------layer_unsorted + layer_direct, //------layer_direct + layer_inverse //------layer_inverse + }; + + + //==================================================rasterizer_compound_aa + template class rasterizer_compound_aa + { + struct style_info + { + unsigned start_cell; + unsigned num_cells; + int last_x; + }; + + struct cell_info + { + int x, area, cover; + }; + + public: + typedef Clip clip_type; + typedef typename Clip::conv_type conv_type; + typedef typename Clip::coord_type coord_type; + + enum aa_scale_e + { + aa_shift = 8, + aa_scale = 1 << aa_shift, + aa_mask = aa_scale - 1, + aa_scale2 = aa_scale * 2, + aa_mask2 = aa_scale2 - 1 + }; + + //-------------------------------------------------------------------- + rasterizer_compound_aa() : + m_outline(), + m_clipper(), + m_filling_rule(fill_non_zero), + m_layer_order(layer_direct), + m_styles(), // Active Styles + m_ast(), // Active Style Table (unique values) + m_asm(), // Active Style Mask + m_cells(), + m_cover_buf(), + m_master_alpha(), + m_min_style(0x7FFFFFFF), + m_max_style(-0x7FFFFFFF), + m_start_x(0), + m_start_y(0), + m_scan_y(0x7FFFFFFF), + m_sl_start(0), + m_sl_len(0) + {} + + //-------------------------------------------------------------------- + void reset(); + void reset_clipping(); + void clip_box(double x1, double y1, double x2, double y2); + void filling_rule(filling_rule_e filling_rule); + void layer_order(layer_order_e order); + void master_alpha(int style, double alpha); + + //-------------------------------------------------------------------- + void styles(int left, int right); + void move_to(int x, int y); + void line_to(int x, int y); + void move_to_d(double x, double y); + void line_to_d(double x, double y); + void add_vertex(double x, double y, unsigned cmd); + + void edge(int x1, int y1, int x2, int y2); + void edge_d(double x1, double y1, double x2, double y2); + + //------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + if(m_outline.sorted()) reset(); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + } + + + //-------------------------------------------------------------------- + int min_x() const { return m_outline.min_x(); } + int min_y() const { return m_outline.min_y(); } + int max_x() const { return m_outline.max_x(); } + int max_y() const { return m_outline.max_y(); } + int min_style() const { return m_min_style; } + int max_style() const { return m_max_style; } + + //-------------------------------------------------------------------- + void sort(); + bool rewind_scanlines(); + unsigned sweep_styles(); + int scanline_start() const { return m_sl_start; } + unsigned scanline_length() const { return m_sl_len; } + unsigned style(unsigned style_idx) const; + + cover_type* allocate_cover_buffer(unsigned len); + + //-------------------------------------------------------------------- + bool navigate_scanline(int y); + bool hit_test(int tx, int ty); + + //-------------------------------------------------------------------- + AGG_INLINE unsigned calculate_alpha(int area, unsigned master_alpha) const + { + int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift); + if(cover < 0) cover = -cover; + if(m_filling_rule == fill_even_odd) + { + cover &= aa_mask2; + if(cover > aa_scale) + { + cover = aa_scale2 - cover; + } + } + if(cover > aa_mask) cover = aa_mask; + return (cover * master_alpha + aa_mask) >> aa_shift; + } + + //-------------------------------------------------------------------- + // Sweeps one scanline with one style index. The style ID can be + // determined by calling style(). + template bool sweep_scanline(Scanline& sl, int style_idx) + { + int scan_y = m_scan_y - 1; + if(scan_y > m_outline.max_y()) return false; + + sl.reset_spans(); + + unsigned master_alpha = aa_mask; + + if(style_idx < 0) + { + style_idx = 0; + } + else + { + style_idx++; + master_alpha = m_master_alpha[m_ast[style_idx] + m_min_style - 1]; + } + + const style_info& st = m_styles[m_ast[style_idx]]; + + unsigned num_cells = st.num_cells; + cell_info* cell = &m_cells[st.start_cell]; + + int cover = 0; + while(num_cells--) + { + unsigned alpha; + int x = cell->x; + int area = cell->area; + + cover += cell->cover; + + ++cell; + + if(area) + { + alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area, + master_alpha); + sl.add_cell(x, alpha); + x++; + } + + if(num_cells && cell->x > x) + { + alpha = calculate_alpha(cover << (poly_subpixel_shift + 1), + master_alpha); + if(alpha) + { + sl.add_span(x, cell->x - x, alpha); + } + } + } + + if(sl.num_spans() == 0) return false; + sl.finalize(scan_y); + return true; + } + + private: + void add_style(int style_id); + void allocate_master_alpha(); + + //-------------------------------------------------------------------- + // Disable copying + rasterizer_compound_aa(const rasterizer_compound_aa&); + const rasterizer_compound_aa& + operator = (const rasterizer_compound_aa&); + + private: + rasterizer_cells_aa m_outline; + clip_type m_clipper; + filling_rule_e m_filling_rule; + layer_order_e m_layer_order; + pod_vector m_styles; // Active Styles + pod_vector m_ast; // Active Style Table (unique values) + pod_vector m_asm; // Active Style Mask + pod_vector m_cells; + pod_vector m_cover_buf; + pod_bvector m_master_alpha; + + int m_min_style; + int m_max_style; + coord_type m_start_x; + coord_type m_start_y; + int m_scan_y; + int m_sl_start; + unsigned m_sl_len; + }; + + + + + + + + + + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::reset() + { + m_outline.reset(); + m_min_style = 0x7FFFFFFF; + m_max_style = -0x7FFFFFFF; + m_scan_y = 0x7FFFFFFF; + m_sl_start = 0; + m_sl_len = 0; + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::filling_rule(filling_rule_e filling_rule) + { + m_filling_rule = filling_rule; + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::layer_order(layer_order_e order) + { + m_layer_order = order; + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::clip_box(double x1, double y1, + double x2, double y2) + { + reset(); + m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1), + conv_type::upscale(x2), conv_type::upscale(y2)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::reset_clipping() + { + reset(); + m_clipper.reset_clipping(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::styles(int left, int right) + { + cell_style_aa cell; + cell.initial(); + cell.left = (int16)left; + cell.right = (int16)right; + m_outline.style(cell); + if(left >= 0 && left < m_min_style) m_min_style = left; + if(left >= 0 && left > m_max_style) m_max_style = left; + if(right >= 0 && right < m_min_style) m_min_style = right; + if(right >= 0 && right > m_max_style) m_max_style = right; + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::move_to(int x, int y) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(m_start_x = conv_type::downscale(x), + m_start_y = conv_type::downscale(y)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::line_to(int x, int y) + { + m_clipper.line_to(m_outline, + conv_type::downscale(x), + conv_type::downscale(y)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::move_to_d(double x, double y) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(m_start_x = conv_type::upscale(x), + m_start_y = conv_type::upscale(y)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::line_to_d(double x, double y) + { + m_clipper.line_to(m_outline, + conv_type::upscale(x), + conv_type::upscale(y)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + move_to_d(x, y); + } + else + if(is_vertex(cmd)) + { + line_to_d(x, y); + } + else + if(is_close(cmd)) + { + m_clipper.line_to(m_outline, m_start_x, m_start_y); + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::edge(int x1, int y1, int x2, int y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1)); + m_clipper.line_to(m_outline, + conv_type::downscale(x2), + conv_type::downscale(y2)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::edge_d(double x1, double y1, + double x2, double y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1)); + m_clipper.line_to(m_outline, + conv_type::upscale(x2), + conv_type::upscale(y2)); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_compound_aa::sort() + { + m_outline.sort_cells(); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_compound_aa::rewind_scanlines() + { + m_outline.sort_cells(); + if(m_outline.total_cells() == 0) + { + return false; + } + if(m_max_style < m_min_style) + { + return false; + } + m_scan_y = m_outline.min_y(); + m_styles.allocate(m_max_style - m_min_style + 2, 128); + allocate_master_alpha(); + return true; + } + + //------------------------------------------------------------------------ + template + AGG_INLINE void rasterizer_compound_aa::add_style(int style_id) + { + if(style_id < 0) style_id = 0; + else style_id -= m_min_style - 1; + + unsigned nbyte = style_id >> 3; + unsigned mask = 1 << (style_id & 7); + + style_info* style = &m_styles[style_id]; + if((m_asm[nbyte] & mask) == 0) + { + m_ast.add(style_id); + m_asm[nbyte] |= mask; + style->start_cell = 0; + style->num_cells = 0; + style->last_x = -0x7FFFFFFF; + } + ++style->start_cell; + } + + //------------------------------------------------------------------------ + // Returns the number of styles + template + unsigned rasterizer_compound_aa::sweep_styles() + { + for(;;) + { + if(m_scan_y > m_outline.max_y()) return 0; + unsigned num_cells = m_outline.scanline_num_cells(m_scan_y); + const cell_style_aa* const* cells = m_outline.scanline_cells(m_scan_y); + unsigned num_styles = m_max_style - m_min_style + 2; + const cell_style_aa* curr_cell; + unsigned style_id; + style_info* style; + cell_info* cell; + + m_cells.allocate(num_cells * 2, 256); // Each cell can have two styles + m_ast.capacity(num_styles, 64); + m_asm.allocate((num_styles + 7) >> 3, 8); + m_asm.zero(); + + if(num_cells) + { + // Pre-add zero (for no-fill style, that is, -1). + // We need that to ensure that the "-1 style" would go first. + m_asm[0] |= 1; + m_ast.add(0); + style = &m_styles[0]; + style->start_cell = 0; + style->num_cells = 0; + style->last_x = -0x7FFFFFFF; + + m_sl_start = cells[0]->x; + m_sl_len = cells[num_cells-1]->x - m_sl_start + 1; + while(num_cells--) + { + curr_cell = *cells++; + add_style(curr_cell->left); + add_style(curr_cell->right); + } + + // Convert the Y-histogram into the array of starting indexes + unsigned i; + unsigned start_cell = 0; + for(i = 0; i < m_ast.size(); i++) + { + style_info& st = m_styles[m_ast[i]]; + unsigned v = st.start_cell; + st.start_cell = start_cell; + start_cell += v; + } + + cells = m_outline.scanline_cells(m_scan_y); + num_cells = m_outline.scanline_num_cells(m_scan_y); + + while(num_cells--) + { + curr_cell = *cells++; + style_id = (curr_cell->left < 0) ? 0 : + curr_cell->left - m_min_style + 1; + + style = &m_styles[style_id]; + if(curr_cell->x == style->last_x) + { + cell = &m_cells[style->start_cell + style->num_cells - 1]; + cell->area += curr_cell->area; + cell->cover += curr_cell->cover; + } + else + { + cell = &m_cells[style->start_cell + style->num_cells]; + cell->x = curr_cell->x; + cell->area = curr_cell->area; + cell->cover = curr_cell->cover; + style->last_x = curr_cell->x; + style->num_cells++; + } + + style_id = (curr_cell->right < 0) ? 0 : + curr_cell->right - m_min_style + 1; + + style = &m_styles[style_id]; + if(curr_cell->x == style->last_x) + { + cell = &m_cells[style->start_cell + style->num_cells - 1]; + cell->area -= curr_cell->area; + cell->cover -= curr_cell->cover; + } + else + { + cell = &m_cells[style->start_cell + style->num_cells]; + cell->x = curr_cell->x; + cell->area = -curr_cell->area; + cell->cover = -curr_cell->cover; + style->last_x = curr_cell->x; + style->num_cells++; + } + } + } + if(m_ast.size() > 1) break; + ++m_scan_y; + } + ++m_scan_y; + + if(m_layer_order != layer_unsorted) + { + range_adaptor > ra(m_ast, 1, m_ast.size() - 1); + if(m_layer_order == layer_direct) quick_sort(ra, unsigned_greater); + else quick_sort(ra, unsigned_less); + } + + return m_ast.size() - 1; + } + + //------------------------------------------------------------------------ + // Returns style ID depending of the existing style index + template + AGG_INLINE + unsigned rasterizer_compound_aa::style(unsigned style_idx) const + { + return m_ast[style_idx + 1] + m_min_style - 1; + } + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_compound_aa::navigate_scanline(int y) + { + m_outline.sort_cells(); + if(m_outline.total_cells() == 0) + { + return false; + } + if(m_max_style < m_min_style) + { + return false; + } + if(y < m_outline.min_y() || y > m_outline.max_y()) + { + return false; + } + m_scan_y = y; + m_styles.allocate(m_max_style - m_min_style + 2, 128); + allocate_master_alpha(); + return true; + } + + //------------------------------------------------------------------------ + template + bool rasterizer_compound_aa::hit_test(int tx, int ty) + { + if(!navigate_scanline(ty)) + { + return false; + } + + unsigned num_styles = sweep_styles(); + if(num_styles <= 0) + { + return false; + } + + scanline_hit_test sl(tx); + sweep_scanline(sl, -1); + return sl.hit(); + } + + //------------------------------------------------------------------------ + template + cover_type* rasterizer_compound_aa::allocate_cover_buffer(unsigned len) + { + m_cover_buf.allocate(len, 256); + return &m_cover_buf[0]; + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::allocate_master_alpha() + { + while((int)m_master_alpha.size() <= m_max_style) + { + m_master_alpha.add(aa_mask); + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_compound_aa::master_alpha(int style, double alpha) + { + if(style >= 0) + { + while((int)m_master_alpha.size() <= style) + { + m_master_alpha.add(aa_mask); + } + m_master_alpha[style] = uround(alpha * aa_mask); + } + } + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_rasterizer_outline.h b/desmume/src/windows/agg/include/agg_rasterizer_outline.h new file mode 100644 index 000000000..57cb3e0a7 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_outline.h @@ -0,0 +1,157 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RASTERIZER_OUTLINE_INCLUDED +#define AGG_RASTERIZER_OUTLINE_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //======================================================rasterizer_outline + template class rasterizer_outline + { + public: + explicit rasterizer_outline(Renderer& ren) : + m_ren(&ren), + m_start_x(0), + m_start_y(0), + m_vertices(0) + {} + void attach(Renderer& ren) { m_ren = &ren; } + + + //-------------------------------------------------------------------- + void move_to(int x, int y) + { + m_vertices = 1; + m_ren->move_to(m_start_x = x, m_start_y = y); + } + + //-------------------------------------------------------------------- + void line_to(int x, int y) + { + ++m_vertices; + m_ren->line_to(x, y); + } + + //-------------------------------------------------------------------- + void move_to_d(double x, double y) + { + move_to(m_ren->coord(x), m_ren->coord(y)); + } + + //-------------------------------------------------------------------- + void line_to_d(double x, double y) + { + line_to(m_ren->coord(x), m_ren->coord(y)); + } + + //-------------------------------------------------------------------- + void close() + { + if(m_vertices > 2) + { + line_to(m_start_x, m_start_y); + } + m_vertices = 0; + } + + //-------------------------------------------------------------------- + void add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + move_to_d(x, y); + } + else + { + if(is_end_poly(cmd)) + { + if(is_closed(cmd)) close(); + } + else + { + line_to_d(x, y); + } + } + } + + + //-------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + } + + + //-------------------------------------------------------------------- + template + void render_all_paths(VertexSource& vs, + const ColorStorage& colors, + const PathId& path_id, + unsigned num_paths) + { + for(unsigned i = 0; i < num_paths; i++) + { + m_ren->line_color(colors[i]); + add_path(vs, path_id[i]); + } + } + + + //-------------------------------------------------------------------- + template void render_ctrl(Ctrl& c) + { + unsigned i; + for(i = 0; i < c.num_paths(); i++) + { + m_ren->line_color(c.color(i)); + add_path(c, i); + } + } + + + private: + Renderer* m_ren; + int m_start_x; + int m_start_y; + unsigned m_vertices; + }; + + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_rasterizer_outline_aa.h b/desmume/src/windows/agg/include/agg_rasterizer_outline_aa.h new file mode 100644 index 000000000..6347cdfe8 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_outline_aa.h @@ -0,0 +1,609 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RASTERIZER_OUTLINE_AA_INCLUDED +#define AGG_RASTERIZER_OUTLINE_AA_INCLUDED + +#include "agg_basics.h" +#include "agg_line_aa_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + //------------------------------------------------------------------------- + inline bool cmp_dist_start(int d) { return d > 0; } + inline bool cmp_dist_end(int d) { return d <= 0; } + + + + //-----------------------------------------------------------line_aa_vertex + // Vertex (x, y) with the distance to the next one. The last vertex has + // the distance between the last and the first points + struct line_aa_vertex + { + int x; + int y; + int len; + + line_aa_vertex() {} + line_aa_vertex(int x_, int y_) : + x(x_), + y(y_), + len(0) + { + } + + bool operator () (const line_aa_vertex& val) + { + double dx = val.x - x; + double dy = val.y - y; + return (len = uround(sqrt(dx * dx + dy * dy))) > + (line_subpixel_scale + line_subpixel_scale / 2); + } + }; + + + //----------------------------------------------------------outline_aa_join_e + enum outline_aa_join_e + { + outline_no_join, //-----outline_no_join + outline_miter_join, //-----outline_miter_join + outline_round_join, //-----outline_round_join + outline_miter_accurate_join //-----outline_accurate_join + }; + + //=======================================================rasterizer_outline_aa + template class rasterizer_outline_aa + { + private: + //------------------------------------------------------------------------ + struct draw_vars + { + unsigned idx; + int x1, y1, x2, y2; + line_parameters curr, next; + int lcurr, lnext; + int xb1, yb1, xb2, yb2; + unsigned flags; + }; + + void draw(draw_vars& dv, unsigned start, unsigned end); + + public: + typedef line_aa_vertex vertex_type; + typedef vertex_sequence vertex_storage_type; + + explicit rasterizer_outline_aa(Renderer& ren) : + m_ren(&ren), + m_line_join(ren.accurate_join_only() ? + outline_miter_accurate_join : + outline_round_join), + m_round_cap(false), + m_start_x(0), + m_start_y(0) + {} + void attach(Renderer& ren) { m_ren = &ren; } + + //------------------------------------------------------------------------ + void line_join(outline_aa_join_e join) + { + m_line_join = m_ren->accurate_join_only() ? + outline_miter_accurate_join : + join; + } + bool line_join() const { return m_line_join; } + + //------------------------------------------------------------------------ + void round_cap(bool v) { m_round_cap = v; } + bool round_cap() const { return m_round_cap; } + + //------------------------------------------------------------------------ + void move_to(int x, int y) + { + m_src_vertices.modify_last(vertex_type(m_start_x = x, m_start_y = y)); + } + + //------------------------------------------------------------------------ + void line_to(int x, int y) + { + m_src_vertices.add(vertex_type(x, y)); + } + + //------------------------------------------------------------------------ + void move_to_d(double x, double y) + { + move_to(Coord::conv(x), Coord::conv(y)); + } + + //------------------------------------------------------------------------ + void line_to_d(double x, double y) + { + line_to(Coord::conv(x), Coord::conv(y)); + } + + //------------------------------------------------------------------------ + void render(bool close_polygon); + + //------------------------------------------------------------------------ + void add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + render(false); + move_to_d(x, y); + } + else + { + if(is_end_poly(cmd)) + { + render(is_closed(cmd)); + if(is_closed(cmd)) + { + move_to(m_start_x, m_start_y); + } + } + else + { + line_to_d(x, y); + } + } + } + + //------------------------------------------------------------------------ + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + render(false); + } + + + //------------------------------------------------------------------------ + template + void render_all_paths(VertexSource& vs, + const ColorStorage& colors, + const PathId& path_id, + unsigned num_paths) + { + for(unsigned i = 0; i < num_paths; i++) + { + m_ren->color(colors[i]); + add_path(vs, path_id[i]); + } + } + + + //------------------------------------------------------------------------ + template void render_ctrl(Ctrl& c) + { + unsigned i; + for(i = 0; i < c.num_paths(); i++) + { + m_ren->color(c.color(i)); + add_path(c, i); + } + } + + private: + rasterizer_outline_aa(const rasterizer_outline_aa&); + const rasterizer_outline_aa& operator = + (const rasterizer_outline_aa&); + + Renderer* m_ren; + vertex_storage_type m_src_vertices; + outline_aa_join_e m_line_join; + bool m_round_cap; + int m_start_x; + int m_start_y; + }; + + + + + + + + + //---------------------------------------------------------------------------- + template + void rasterizer_outline_aa::draw(draw_vars& dv, + unsigned start, + unsigned end) + { + unsigned i; + const vertex_storage_type::value_type* v; + + for(i = start; i < end; i++) + { + if(m_line_join == outline_round_join) + { + dv.xb1 = dv.curr.x1 + (dv.curr.y2 - dv.curr.y1); + dv.yb1 = dv.curr.y1 - (dv.curr.x2 - dv.curr.x1); + dv.xb2 = dv.curr.x2 + (dv.curr.y2 - dv.curr.y1); + dv.yb2 = dv.curr.y2 - (dv.curr.x2 - dv.curr.x1); + } + + switch(dv.flags) + { + case 0: m_ren->line3(dv.curr, dv.xb1, dv.yb1, dv.xb2, dv.yb2); break; + case 1: m_ren->line2(dv.curr, dv.xb2, dv.yb2); break; + case 2: m_ren->line1(dv.curr, dv.xb1, dv.yb1); break; + case 3: m_ren->line0(dv.curr); break; + } + + if(m_line_join == outline_round_join && (dv.flags & 2) == 0) + { + m_ren->pie(dv.curr.x2, dv.curr.y2, + dv.curr.x2 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y2 - (dv.curr.x2 - dv.curr.x1), + dv.curr.x2 + (dv.next.y2 - dv.next.y1), + dv.curr.y2 - (dv.next.x2 - dv.next.x1)); + } + + dv.x1 = dv.x2; + dv.y1 = dv.y2; + dv.lcurr = dv.lnext; + dv.lnext = m_src_vertices[dv.idx].len; + + ++dv.idx; + if(dv.idx >= m_src_vertices.size()) dv.idx = 0; + + v = &m_src_vertices[dv.idx]; + dv.x2 = v->x; + dv.y2 = v->y; + + dv.curr = dv.next; + dv.next = line_parameters(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext); + dv.xb1 = dv.xb2; + dv.yb1 = dv.yb2; + + switch(m_line_join) + { + case outline_no_join: + dv.flags = 3; + break; + + case outline_miter_join: + dv.flags >>= 1; + dv.flags |= ((dv.curr.diagonal_quadrant() == + dv.next.diagonal_quadrant()) << 1); + if((dv.flags & 2) == 0) + { + bisectrix(dv.curr, dv.next, &dv.xb2, &dv.yb2); + } + break; + + case outline_round_join: + dv.flags >>= 1; + dv.flags |= ((dv.curr.diagonal_quadrant() == + dv.next.diagonal_quadrant()) << 1); + break; + + case outline_miter_accurate_join: + dv.flags = 0; + bisectrix(dv.curr, dv.next, &dv.xb2, &dv.yb2); + break; + } + } + } + + + + + //---------------------------------------------------------------------------- + template + void rasterizer_outline_aa::render(bool close_polygon) + { + m_src_vertices.close(close_polygon); + draw_vars dv; + const vertex_storage_type::value_type* v; + int x1; + int y1; + int x2; + int y2; + int lprev; + + if(close_polygon) + { + if(m_src_vertices.size() >= 3) + { + dv.idx = 2; + + v = &m_src_vertices[m_src_vertices.size() - 1]; + x1 = v->x; + y1 = v->y; + lprev = v->len; + + v = &m_src_vertices[0]; + x2 = v->x; + y2 = v->y; + dv.lcurr = v->len; + line_parameters prev(x1, y1, x2, y2, lprev); + + v = &m_src_vertices[1]; + dv.x1 = v->x; + dv.y1 = v->y; + dv.lnext = v->len; + dv.curr = line_parameters(x2, y2, dv.x1, dv.y1, dv.lcurr); + + v = &m_src_vertices[dv.idx]; + dv.x2 = v->x; + dv.y2 = v->y; + dv.next = line_parameters(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext); + + dv.xb1 = 0; + dv.yb1 = 0; + dv.xb2 = 0; + dv.yb2 = 0; + + switch(m_line_join) + { + case outline_no_join: + dv.flags = 3; + break; + + case outline_miter_join: + case outline_round_join: + dv.flags = + (prev.diagonal_quadrant() == dv.curr.diagonal_quadrant()) | + ((dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant()) << 1); + break; + + case outline_miter_accurate_join: + dv.flags = 0; + break; + } + + if((dv.flags & 1) == 0 && m_line_join != outline_round_join) + { + bisectrix(prev, dv.curr, &dv.xb1, &dv.yb1); + } + + if((dv.flags & 2) == 0 && m_line_join != outline_round_join) + { + bisectrix(dv.curr, dv.next, &dv.xb2, &dv.yb2); + } + draw(dv, 0, m_src_vertices.size()); + } + } + else + { + switch(m_src_vertices.size()) + { + case 0: + case 1: + break; + + case 2: + { + v = &m_src_vertices[0]; + x1 = v->x; + y1 = v->y; + lprev = v->len; + v = &m_src_vertices[1]; + x2 = v->x; + y2 = v->y; + line_parameters lp(x1, y1, x2, y2, lprev); + if(m_round_cap) + { + m_ren->semidot(cmp_dist_start, x1, y1, x1 + (y2 - y1), y1 - (x2 - x1)); + } + m_ren->line3(lp, + x1 + (y2 - y1), + y1 - (x2 - x1), + x2 + (y2 - y1), + y2 - (x2 - x1)); + if(m_round_cap) + { + m_ren->semidot(cmp_dist_end, x2, y2, x2 + (y2 - y1), y2 - (x2 - x1)); + } + } + break; + + case 3: + { + int x3, y3; + int lnext; + v = &m_src_vertices[0]; + x1 = v->x; + y1 = v->y; + lprev = v->len; + v = &m_src_vertices[1]; + x2 = v->x; + y2 = v->y; + lnext = v->len; + v = &m_src_vertices[2]; + x3 = v->x; + y3 = v->y; + line_parameters lp1(x1, y1, x2, y2, lprev); + line_parameters lp2(x2, y2, x3, y3, lnext); + + if(m_round_cap) + { + m_ren->semidot(cmp_dist_start, x1, y1, x1 + (y2 - y1), y1 - (x2 - x1)); + } + + if(m_line_join == outline_round_join) + { + m_ren->line3(lp1, x1 + (y2 - y1), y1 - (x2 - x1), + x2 + (y2 - y1), y2 - (x2 - x1)); + + m_ren->pie(x2, y2, x2 + (y2 - y1), y2 - (x2 - x1), + x2 + (y3 - y2), y2 - (x3 - x2)); + + m_ren->line3(lp2, x2 + (y3 - y2), y2 - (x3 - x2), + x3 + (y3 - y2), y3 - (x3 - x2)); + } + else + { + bisectrix(lp1, lp2, &dv.xb1, &dv.yb1); + m_ren->line3(lp1, x1 + (y2 - y1), y1 - (x2 - x1), + dv.xb1, dv.yb1); + + m_ren->line3(lp2, dv.xb1, dv.yb1, + x3 + (y3 - y2), y3 - (x3 - x2)); + } + if(m_round_cap) + { + m_ren->semidot(cmp_dist_end, x3, y3, x3 + (y3 - y2), y3 - (x3 - x2)); + } + } + break; + + default: + { + dv.idx = 3; + + v = &m_src_vertices[0]; + x1 = v->x; + y1 = v->y; + lprev = v->len; + + v = &m_src_vertices[1]; + x2 = v->x; + y2 = v->y; + dv.lcurr = v->len; + line_parameters prev(x1, y1, x2, y2, lprev); + + v = &m_src_vertices[2]; + dv.x1 = v->x; + dv.y1 = v->y; + dv.lnext = v->len; + dv.curr = line_parameters(x2, y2, dv.x1, dv.y1, dv.lcurr); + + v = &m_src_vertices[dv.idx]; + dv.x2 = v->x; + dv.y2 = v->y; + dv.next = line_parameters(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext); + + dv.xb1 = 0; + dv.yb1 = 0; + dv.xb2 = 0; + dv.yb2 = 0; + + switch(m_line_join) + { + case outline_no_join: + dv.flags = 3; + break; + + case outline_miter_join: + case outline_round_join: + dv.flags = + (prev.diagonal_quadrant() == dv.curr.diagonal_quadrant()) | + ((dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant()) << 1); + break; + + case outline_miter_accurate_join: + dv.flags = 0; + break; + } + + if(m_round_cap) + { + m_ren->semidot(cmp_dist_start, x1, y1, x1 + (y2 - y1), y1 - (x2 - x1)); + } + if((dv.flags & 1) == 0) + { + if(m_line_join == outline_round_join) + { + m_ren->line3(prev, x1 + (y2 - y1), y1 - (x2 - x1), + x2 + (y2 - y1), y2 - (x2 - x1)); + m_ren->pie(prev.x2, prev.y2, + x2 + (y2 - y1), y2 - (x2 - x1), + dv.curr.x1 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y1 - (dv.curr.x2 - dv.curr.x1)); + } + else + { + bisectrix(prev, dv.curr, &dv.xb1, &dv.yb1); + m_ren->line3(prev, x1 + (y2 - y1), y1 - (x2 - x1), + dv.xb1, dv.yb1); + } + } + else + { + m_ren->line1(prev, + x1 + (y2 - y1), + y1 - (x2 - x1)); + } + if((dv.flags & 2) == 0 && m_line_join != outline_round_join) + { + bisectrix(dv.curr, dv.next, &dv.xb2, &dv.yb2); + } + + draw(dv, 1, m_src_vertices.size() - 2); + + if((dv.flags & 1) == 0) + { + if(m_line_join == outline_round_join) + { + m_ren->line3(dv.curr, + dv.curr.x1 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y1 - (dv.curr.x2 - dv.curr.x1), + dv.curr.x2 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y2 - (dv.curr.x2 - dv.curr.x1)); + } + else + { + m_ren->line3(dv.curr, dv.xb1, dv.yb1, + dv.curr.x2 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y2 - (dv.curr.x2 - dv.curr.x1)); + } + } + else + { + m_ren->line2(dv.curr, + dv.curr.x2 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y2 - (dv.curr.x2 - dv.curr.x1)); + } + if(m_round_cap) + { + m_ren->semidot(cmp_dist_end, dv.curr.x2, dv.curr.y2, + dv.curr.x2 + (dv.curr.y2 - dv.curr.y1), + dv.curr.y2 - (dv.curr.x2 - dv.curr.x1)); + } + + } + break; + } + } + m_src_vertices.remove_all(); + } + + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_rasterizer_scanline_aa.h b/desmume/src/windows/agg/include/agg_rasterizer_scanline_aa.h new file mode 100644 index 000000000..b5d363add --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_scanline_aa.h @@ -0,0 +1,520 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// The author gratefully acknowleges the support of David Turner, +// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType +// libray - in producing this work. See http://www.freetype.org for details. +// +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_RASTERIZER_SCANLINE_AA_INCLUDED +#define AGG_RASTERIZER_SCANLINE_AA_INCLUDED + +#include "agg_rasterizer_cells_aa.h" +#include "agg_rasterizer_sl_clip.h" +#include "agg_gamma_functions.h" + + +namespace agg +{ + + + //-----------------------------------------------------------------cell_aa + // A pixel cell. There're no constructors defined and it was done + // intentionally in order to avoid extra overhead when allocating an + // array of cells. + struct cell_aa + { + int x; + int y; + int cover; + int area; + + void initial() + { + x = 0x7FFFFFFF; + y = 0x7FFFFFFF; + cover = 0; + area = 0; + } + + void style(const cell_aa&) {} + + int not_equal(int ex, int ey, const cell_aa&) const + { + return (ex - x) | (ey - y); + } + }; + + + //==================================================rasterizer_scanline_aa + // Polygon rasterizer that is used to render filled polygons with + // high-quality Anti-Aliasing. Internally, by default, the class uses + // integer coordinates in format 24.8, i.e. 24 bits for integer part + // and 8 bits for fractional - see poly_subpixel_shift. This class can be + // used in the following way: + // + // 1. filling_rule(filling_rule_e ft) - optional. + // + // 2. gamma() - optional. + // + // 3. reset() + // + // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create + // more than one contour, but each contour must consist of at least 3 + // vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3); + // is the absolute minimum of vertices that define a triangle. + // The algorithm does not check either the number of vertices nor + // coincidence of their coordinates, but in the worst case it just + // won't draw anything. + // The orger of the vertices (clockwise or counterclockwise) + // is important when using the non-zero filling rule (fill_non_zero). + // In this case the vertex order of all the contours must be the same + // if you want your intersecting polygons to be without "holes". + // You actually can use different vertices order. If the contours do not + // intersect each other the order is not important anyway. If they do, + // contours with the same vertex order will be rendered without "holes" + // while the intersecting contours with different orders will have "holes". + // + // filling_rule() and gamma() can be called anytime before "sweeping". + //------------------------------------------------------------------------ + template class rasterizer_scanline_aa + { + enum status + { + status_initial, + status_move_to, + status_line_to, + status_closed + }; + + public: + typedef Clip clip_type; + typedef typename Clip::conv_type conv_type; + typedef typename Clip::coord_type coord_type; + + enum aa_scale_e + { + aa_shift = 8, + aa_scale = 1 << aa_shift, + aa_mask = aa_scale - 1, + aa_scale2 = aa_scale * 2, + aa_mask2 = aa_scale2 - 1 + }; + + //-------------------------------------------------------------------- + rasterizer_scanline_aa() : + m_outline(), + m_clipper(), + m_filling_rule(fill_non_zero), + m_auto_close(true), + m_start_x(0), + m_start_y(0), + m_status(status_initial) + { + int i; + for(i = 0; i < aa_scale; i++) m_gamma[i] = i; + } + + //-------------------------------------------------------------------- + template + rasterizer_scanline_aa(const GammaF& gamma_function) : + m_outline(), + m_clipper(m_outline), + m_filling_rule(fill_non_zero), + m_auto_close(true), + m_start_x(0), + m_start_y(0), + m_status(status_initial) + { + gamma(gamma_function); + } + + //-------------------------------------------------------------------- + void reset(); + void reset_clipping(); + void clip_box(double x1, double y1, double x2, double y2); + void filling_rule(filling_rule_e filling_rule); + void auto_close(bool flag) { m_auto_close = flag; } + + //-------------------------------------------------------------------- + template void gamma(const GammaF& gamma_function) + { + int i; + for(i = 0; i < aa_scale; i++) + { + m_gamma[i] = uround(gamma_function(double(i) / aa_mask) * aa_mask); + } + } + + //-------------------------------------------------------------------- + unsigned apply_gamma(unsigned cover) const + { + return m_gamma[cover]; + } + + //-------------------------------------------------------------------- + void move_to(int x, int y); + void line_to(int x, int y); + void move_to_d(double x, double y); + void line_to_d(double x, double y); + void close_polygon(); + void add_vertex(double x, double y, unsigned cmd); + + void edge(int x1, int y1, int x2, int y2); + void edge_d(double x1, double y1, double x2, double y2); + + //------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + if(m_outline.sorted()) reset(); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + add_vertex(x, y, cmd); + } + } + + //-------------------------------------------------------------------- + int min_x() const { return m_outline.min_x(); } + int min_y() const { return m_outline.min_y(); } + int max_x() const { return m_outline.max_x(); } + int max_y() const { return m_outline.max_y(); } + + //-------------------------------------------------------------------- + void sort(); + bool rewind_scanlines(); + bool navigate_scanline(int y); + + //-------------------------------------------------------------------- + AGG_INLINE unsigned calculate_alpha(int area) const + { + int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift); + + if(cover < 0) cover = -cover; + if(m_filling_rule == fill_even_odd) + { + cover &= aa_mask2; + if(cover > aa_scale) + { + cover = aa_scale2 - cover; + } + } + if(cover > aa_mask) cover = aa_mask; + return m_gamma[cover]; + } + + //-------------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + for(;;) + { + if(m_scan_y > m_outline.max_y()) return false; + sl.reset_spans(); + unsigned num_cells = m_outline.scanline_num_cells(m_scan_y); + const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y); + int cover = 0; + + while(num_cells) + { + const cell_aa* cur_cell = *cells; + int x = cur_cell->x; + int area = cur_cell->area; + unsigned alpha; + + cover += cur_cell->cover; + + //accumulate all cells with the same X + while(--num_cells) + { + cur_cell = *++cells; + if(cur_cell->x != x) break; + area += cur_cell->area; + cover += cur_cell->cover; + } + + if(area) + { + alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area); + if(alpha) + { + sl.add_cell(x, alpha); + } + x++; + } + + if(num_cells && cur_cell->x > x) + { + alpha = calculate_alpha(cover << (poly_subpixel_shift + 1)); + if(alpha) + { + sl.add_span(x, cur_cell->x - x, alpha); + } + } + } + + if(sl.num_spans()) break; + ++m_scan_y; + } + + sl.finalize(m_scan_y); + ++m_scan_y; + return true; + } + + //-------------------------------------------------------------------- + bool hit_test(int tx, int ty); + + + private: + //-------------------------------------------------------------------- + // Disable copying + rasterizer_scanline_aa(const rasterizer_scanline_aa&); + const rasterizer_scanline_aa& + operator = (const rasterizer_scanline_aa&); + + private: + rasterizer_cells_aa m_outline; + clip_type m_clipper; + int m_gamma[aa_scale]; + filling_rule_e m_filling_rule; + bool m_auto_close; + coord_type m_start_x; + coord_type m_start_y; + unsigned m_status; + int m_scan_y; + }; + + + + + + + + + + + + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::reset() + { + m_outline.reset(); + m_status = status_initial; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::filling_rule(filling_rule_e filling_rule) + { + m_filling_rule = filling_rule; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::clip_box(double x1, double y1, + double x2, double y2) + { + reset(); + m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1), + conv_type::upscale(x2), conv_type::upscale(y2)); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::reset_clipping() + { + reset(); + m_clipper.reset_clipping(); + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::close_polygon() + { + if(m_status == status_line_to) + { + m_clipper.line_to(m_outline, m_start_x, m_start_y); + m_status = status_closed; + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::move_to(int x, int y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::downscale(x), + m_start_y = conv_type::downscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::line_to(int x, int y) + { + m_clipper.line_to(m_outline, + conv_type::downscale(x), + conv_type::downscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::move_to_d(double x, double y) + { + if(m_outline.sorted()) reset(); + if(m_auto_close) close_polygon(); + m_clipper.move_to(m_start_x = conv_type::upscale(x), + m_start_y = conv_type::upscale(y)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::line_to_d(double x, double y) + { + m_clipper.line_to(m_outline, + conv_type::upscale(x), + conv_type::upscale(y)); + m_status = status_line_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + move_to_d(x, y); + } + else + if(is_vertex(cmd)) + { + line_to_d(x, y); + } + else + if(is_close(cmd)) + { + close_polygon(); + } + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::edge(int x1, int y1, int x2, int y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1)); + m_clipper.line_to(m_outline, + conv_type::downscale(x2), + conv_type::downscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::edge_d(double x1, double y1, + double x2, double y2) + { + if(m_outline.sorted()) reset(); + m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1)); + m_clipper.line_to(m_outline, + conv_type::upscale(x2), + conv_type::upscale(y2)); + m_status = status_move_to; + } + + //------------------------------------------------------------------------ + template + void rasterizer_scanline_aa::sort() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + } + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa::rewind_scanlines() + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0) + { + return false; + } + m_scan_y = m_outline.min_y(); + return true; + } + + + //------------------------------------------------------------------------ + template + AGG_INLINE bool rasterizer_scanline_aa::navigate_scanline(int y) + { + if(m_auto_close) close_polygon(); + m_outline.sort_cells(); + if(m_outline.total_cells() == 0 || + y < m_outline.min_y() || + y > m_outline.max_y()) + { + return false; + } + m_scan_y = y; + return true; + } + + //------------------------------------------------------------------------ + template + bool rasterizer_scanline_aa::hit_test(int tx, int ty) + { + if(!navigate_scanline(ty)) return false; + scanline_hit_test sl(tx); + sweep_scanline(sl); + return sl.hit(); + } + + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_rasterizer_sl_clip.h b/desmume/src/windows/agg/include/agg_rasterizer_sl_clip.h new file mode 100644 index 000000000..c11e6da76 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rasterizer_sl_clip.h @@ -0,0 +1,361 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RASTERIZER_SL_CLIP_INCLUDED +#define AGG_RASTERIZER_SL_CLIP_INCLUDED + +#include "agg_clip_liang_barsky.h" + +namespace agg +{ + //--------------------------------------------------------poly_max_coord_e + enum poly_max_coord_e + { + poly_max_coord = (1 << 30) - 1 //----poly_max_coord + }; + + //------------------------------------------------------------ras_conv_int + struct ras_conv_int + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return iround(a * b / c); + } + static int xi(int v) { return v; } + static int yi(int v) { return v; } + static int upscale(double v) { return iround(v * poly_subpixel_scale); } + static int downscale(int v) { return v; } + }; + + //--------------------------------------------------------ras_conv_int_sat + struct ras_conv_int_sat + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return saturation::iround(a * b / c); + } + static int xi(int v) { return v; } + static int yi(int v) { return v; } + static int upscale(double v) + { + return saturation::iround(v * poly_subpixel_scale); + } + static int downscale(int v) { return v; } + }; + + //---------------------------------------------------------ras_conv_int_3x + struct ras_conv_int_3x + { + typedef int coord_type; + static AGG_INLINE int mul_div(double a, double b, double c) + { + return iround(a * b / c); + } + static int xi(int v) { return v * 3; } + static int yi(int v) { return v; } + static int upscale(double v) { return iround(v * poly_subpixel_scale); } + static int downscale(int v) { return v; } + }; + + //-----------------------------------------------------------ras_conv_dbl + struct ras_conv_dbl + { + typedef double coord_type; + static AGG_INLINE double mul_div(double a, double b, double c) + { + return a * b / c; + } + static int xi(double v) { return iround(v * poly_subpixel_scale); } + static int yi(double v) { return iround(v * poly_subpixel_scale); } + static double upscale(double v) { return v; } + static double downscale(int v) { return v / double(poly_subpixel_scale); } + }; + + //--------------------------------------------------------ras_conv_dbl_3x + struct ras_conv_dbl_3x + { + typedef double coord_type; + static AGG_INLINE double mul_div(double a, double b, double c) + { + return a * b / c; + } + static int xi(double v) { return iround(v * poly_subpixel_scale * 3); } + static int yi(double v) { return iround(v * poly_subpixel_scale); } + static double upscale(double v) { return v; } + static double downscale(int v) { return v / double(poly_subpixel_scale); } + }; + + + + + + //------------------------------------------------------rasterizer_sl_clip + template class rasterizer_sl_clip + { + public: + typedef Conv conv_type; + typedef typename Conv::coord_type coord_type; + typedef rect_base rect_type; + + //-------------------------------------------------------------------- + rasterizer_sl_clip() : + m_clip_box(0,0,0,0), + m_x1(0), + m_y1(0), + m_f1(0), + m_clipping(false) + {} + + //-------------------------------------------------------------------- + void reset_clipping() + { + m_clipping = false; + } + + //-------------------------------------------------------------------- + void clip_box(coord_type x1, coord_type y1, coord_type x2, coord_type y2) + { + m_clip_box = rect_type(x1, y1, x2, y2); + m_clip_box.normalize(); + m_clipping = true; + } + + //-------------------------------------------------------------------- + void move_to(coord_type x1, coord_type y1) + { + m_x1 = x1; + m_y1 = y1; + if(m_clipping) m_f1 = clipping_flags(x1, y1, m_clip_box); + } + + private: + //------------------------------------------------------------------------ + template + AGG_INLINE void line_clip_y(Rasterizer& ras, + coord_type x1, coord_type y1, + coord_type x2, coord_type y2, + unsigned f1, unsigned f2) const + { + f1 &= 10; + f2 &= 10; + if((f1 | f2) == 0) + { + // Fully visible + ras.line(Conv::xi(x1), Conv::yi(y1), Conv::xi(x2), Conv::yi(y2)); + } + else + { + if(f1 == f2) + { + // Invisible by Y + return; + } + + coord_type tx1 = x1; + coord_type ty1 = y1; + coord_type tx2 = x2; + coord_type ty2 = y2; + + if(f1 & 8) // y1 < clip.y1 + { + tx1 = x1 + Conv::mul_div(m_clip_box.y1-y1, x2-x1, y2-y1); + ty1 = m_clip_box.y1; + } + + if(f1 & 2) // y1 > clip.y2 + { + tx1 = x1 + Conv::mul_div(m_clip_box.y2-y1, x2-x1, y2-y1); + ty1 = m_clip_box.y2; + } + + if(f2 & 8) // y2 < clip.y1 + { + tx2 = x1 + Conv::mul_div(m_clip_box.y1-y1, x2-x1, y2-y1); + ty2 = m_clip_box.y1; + } + + if(f2 & 2) // y2 > clip.y2 + { + tx2 = x1 + Conv::mul_div(m_clip_box.y2-y1, x2-x1, y2-y1); + ty2 = m_clip_box.y2; + } + ras.line(Conv::xi(tx1), Conv::yi(ty1), + Conv::xi(tx2), Conv::yi(ty2)); + } + } + + + public: + //-------------------------------------------------------------------- + template + void line_to(Rasterizer& ras, coord_type x2, coord_type y2) + { + if(m_clipping) + { + unsigned f2 = clipping_flags(x2, y2, m_clip_box); + + if((m_f1 & 10) == (f2 & 10) && (m_f1 & 10) != 0) + { + // Invisible by Y + m_x1 = x2; + m_y1 = y2; + m_f1 = f2; + return; + } + + coord_type x1 = m_x1; + coord_type y1 = m_y1; + unsigned f1 = m_f1; + coord_type y3, y4; + unsigned f3, f4; + + switch(((f1 & 5) << 1) | (f2 & 5)) + { + case 0: // Visible by X + line_clip_y(ras, x1, y1, x2, y2, f1, f2); + break; + + case 1: // x2 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, x1, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, m_clip_box.x2, y2, f3, f2); + break; + + case 2: // x1 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, x2, y2, f3, f2); + break; + + case 3: // x1 > clip.x2 && x2 > clip.x2 + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y2, f1, f2); + break; + + case 4: // x2 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, m_clip_box.x1, y2, f3, f2); + break; + + case 6: // x1 > clip.x2 && x2 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + y4 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + f4 = clipping_flags_y(y4, m_clip_box); + line_clip_y(ras, m_clip_box.x2, y1, m_clip_box.x2, y3, f1, f3); + line_clip_y(ras, m_clip_box.x2, y3, m_clip_box.x1, y4, f3, f4); + line_clip_y(ras, m_clip_box.x1, y4, m_clip_box.x1, y2, f4, f2); + break; + + case 8: // x1 < clip.x1 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, x2, y2, f3, f2); + break; + + case 9: // x1 < clip.x1 && x2 > clip.x2 + y3 = y1 + Conv::mul_div(m_clip_box.x1-x1, y2-y1, x2-x1); + y4 = y1 + Conv::mul_div(m_clip_box.x2-x1, y2-y1, x2-x1); + f3 = clipping_flags_y(y3, m_clip_box); + f4 = clipping_flags_y(y4, m_clip_box); + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y3, f1, f3); + line_clip_y(ras, m_clip_box.x1, y3, m_clip_box.x2, y4, f3, f4); + line_clip_y(ras, m_clip_box.x2, y4, m_clip_box.x2, y2, f4, f2); + break; + + case 12: // x1 < clip.x1 && x2 < clip.x1 + line_clip_y(ras, m_clip_box.x1, y1, m_clip_box.x1, y2, f1, f2); + break; + } + m_f1 = f2; + } + else + { + ras.line(Conv::xi(m_x1), Conv::yi(m_y1), + Conv::xi(x2), Conv::yi(y2)); + } + m_x1 = x2; + m_y1 = y2; + } + + + private: + rect_type m_clip_box; + coord_type m_x1; + coord_type m_y1; + unsigned m_f1; + bool m_clipping; + }; + + + + + //---------------------------------------------------rasterizer_sl_no_clip + class rasterizer_sl_no_clip + { + public: + typedef ras_conv_int conv_type; + typedef int coord_type; + + rasterizer_sl_no_clip() : m_x1(0), m_y1(0) {} + + void reset_clipping() {} + void clip_box(coord_type x1, coord_type y1, coord_type x2, coord_type y2) {} + void move_to(coord_type x1, coord_type y1) { m_x1 = x1; m_y1 = y1; } + + template + void line_to(Rasterizer& ras, coord_type x2, coord_type y2) + { + ras.line(m_x1, m_y1, x2, y2); + m_x1 = x2; + m_y1 = y2; + } + + private: + int m_x1, m_y1; + }; + + + // -----rasterizer_sl_clip_int + // -----rasterizer_sl_clip_int_sat + // -----rasterizer_sl_clip_int_3x + // -----rasterizer_sl_clip_dbl + // -----rasterizer_sl_clip_dbl_3x + //------------------------------------------------------------------------ + typedef rasterizer_sl_clip rasterizer_sl_clip_int; + typedef rasterizer_sl_clip rasterizer_sl_clip_int_sat; + typedef rasterizer_sl_clip rasterizer_sl_clip_int_3x; + typedef rasterizer_sl_clip rasterizer_sl_clip_dbl; + typedef rasterizer_sl_clip rasterizer_sl_clip_dbl_3x; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_base.h b/desmume/src/windows/agg/include/agg_renderer_base.h new file mode 100644 index 000000000..9d9c1be96 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_base.h @@ -0,0 +1,723 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_BASE_INCLUDED +#define AGG_RENDERER_BASE_INCLUDED + +#include "agg_basics.h" +#include "agg_rendering_buffer.h" + +namespace agg +{ + + //-----------------------------------------------------------renderer_base + template class renderer_base + { + public: + typedef PixelFormat pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::row_data row_data; + + //-------------------------------------------------------------------- + renderer_base() : m_ren(0), m_clip_box(1, 1, 0, 0) {} + explicit renderer_base(pixfmt_type& ren) : + m_ren(&ren), + m_clip_box(0, 0, ren.width() - 1, ren.height() - 1) + {} + void attach(pixfmt_type& ren) + { + m_ren = &ren; + m_clip_box = rect_i(0, 0, ren.width() - 1, ren.height() - 1); + } + + //-------------------------------------------------------------------- + const pixfmt_type& ren() const { return *m_ren; } + pixfmt_type& ren() { return *m_ren; } + + //-------------------------------------------------------------------- + unsigned width() const { return m_ren->width(); } + unsigned height() const { return m_ren->height(); } + + //-------------------------------------------------------------------- + bool clip_box(int x1, int y1, int x2, int y2) + { + rect_i cb(x1, y1, x2, y2); + cb.normalize(); + if(cb.clip(rect_i(0, 0, width() - 1, height() - 1))) + { + m_clip_box = cb; + return true; + } + m_clip_box.x1 = 1; + m_clip_box.y1 = 1; + m_clip_box.x2 = 0; + m_clip_box.y2 = 0; + return false; + } + + //-------------------------------------------------------------------- + void reset_clipping(bool visibility) + { + if(visibility) + { + m_clip_box.x1 = 0; + m_clip_box.y1 = 0; + m_clip_box.x2 = width() - 1; + m_clip_box.y2 = height() - 1; + } + else + { + m_clip_box.x1 = 1; + m_clip_box.y1 = 1; + m_clip_box.x2 = 0; + m_clip_box.y2 = 0; + } + } + + //-------------------------------------------------------------------- + void clip_box_naked(int x1, int y1, int x2, int y2) + { + m_clip_box.x1 = x1; + m_clip_box.y1 = y1; + m_clip_box.x2 = x2; + m_clip_box.y2 = y2; + } + + //-------------------------------------------------------------------- + bool inbox(int x, int y) const + { + return x >= m_clip_box.x1 && y >= m_clip_box.y1 && + x <= m_clip_box.x2 && y <= m_clip_box.y2; + } + + //-------------------------------------------------------------------- + const rect_i& clip_box() const { return m_clip_box; } + int xmin() const { return m_clip_box.x1; } + int ymin() const { return m_clip_box.y1; } + int xmax() const { return m_clip_box.x2; } + int ymax() const { return m_clip_box.y2; } + + //-------------------------------------------------------------------- + const rect_i& bounding_clip_box() const { return m_clip_box; } + int bounding_xmin() const { return m_clip_box.x1; } + int bounding_ymin() const { return m_clip_box.y1; } + int bounding_xmax() const { return m_clip_box.x2; } + int bounding_ymax() const { return m_clip_box.y2; } + + //-------------------------------------------------------------------- + void clear(const color_type& c) + { + unsigned y; + if(width()) + { + for(y = 0; y < height(); y++) + { + m_ren->copy_hline(0, y, width(), c); + } + } + } + + + //-------------------------------------------------------------------- + void copy_pixel(int x, int y, const color_type& c) + { + if(inbox(x, y)) + { + m_ren->copy_pixel(x, y, c); + } + } + + //-------------------------------------------------------------------- + void blend_pixel(int x, int y, const color_type& c, cover_type cover) + { + if(inbox(x, y)) + { + m_ren->blend_pixel(x, y, c, cover); + } + } + + //-------------------------------------------------------------------- + color_type pixel(int x, int y) const + { + return inbox(x, y) ? + m_ren->pixel(x, y) : + color_type::no_color(); + } + + //-------------------------------------------------------------------- + void copy_hline(int x1, int y, int x2, const color_type& c) + { + if(x1 > x2) { int t = x2; x2 = x1; x1 = t; } + if(y > ymax()) return; + if(y < ymin()) return; + if(x1 > xmax()) return; + if(x2 < xmin()) return; + + if(x1 < xmin()) x1 = xmin(); + if(x2 > xmax()) x2 = xmax(); + + m_ren->copy_hline(x1, y, x2 - x1 + 1, c); + } + + //-------------------------------------------------------------------- + void copy_vline(int x, int y1, int y2, const color_type& c) + { + if(y1 > y2) { int t = y2; y2 = y1; y1 = t; } + if(x > xmax()) return; + if(x < xmin()) return; + if(y1 > ymax()) return; + if(y2 < ymin()) return; + + if(y1 < ymin()) y1 = ymin(); + if(y2 > ymax()) y2 = ymax(); + + m_ren->copy_vline(x, y1, y2 - y1 + 1, c); + } + + //-------------------------------------------------------------------- + void blend_hline(int x1, int y, int x2, + const color_type& c, cover_type cover) + { + if(x1 > x2) { int t = x2; x2 = x1; x1 = t; } + if(y > ymax()) return; + if(y < ymin()) return; + if(x1 > xmax()) return; + if(x2 < xmin()) return; + + if(x1 < xmin()) x1 = xmin(); + if(x2 > xmax()) x2 = xmax(); + + m_ren->blend_hline(x1, y, x2 - x1 + 1, c, cover); + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y1, int y2, + const color_type& c, cover_type cover) + { + if(y1 > y2) { int t = y2; y2 = y1; y1 = t; } + if(x > xmax()) return; + if(x < xmin()) return; + if(y1 > ymax()) return; + if(y2 < ymin()) return; + + if(y1 < ymin()) y1 = ymin(); + if(y2 > ymax()) y2 = ymax(); + + m_ren->blend_vline(x, y1, y2 - y1 + 1, c, cover); + } + + + //-------------------------------------------------------------------- + void copy_bar(int x1, int y1, int x2, int y2, const color_type& c) + { + rect_i rc(x1, y1, x2, y2); + rc.normalize(); + if(rc.clip(clip_box())) + { + int y; + for(y = rc.y1; y <= rc.y2; y++) + { + m_ren->copy_hline(rc.x1, y, unsigned(rc.x2 - rc.x1 + 1), c); + } + } + } + + //-------------------------------------------------------------------- + void blend_bar(int x1, int y1, int x2, int y2, + const color_type& c, cover_type cover) + { + rect_i rc(x1, y1, x2, y2); + rc.normalize(); + if(rc.clip(clip_box())) + { + int y; + for(y = rc.y1; y <= rc.y2; y++) + { + m_ren->blend_hline(rc.x1, + y, + unsigned(rc.x2 - rc.x1 + 1), + c, + cover); + } + } + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, int len, + const color_type& c, + const cover_type* covers) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + len -= xmin() - x; + if(len <= 0) return; + covers += xmin() - x; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->blend_solid_hspan(x, y, len, c, covers); + } + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, int len, + const color_type& c, + const cover_type* covers) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + len -= ymin() - y; + if(len <= 0) return; + covers += ymin() - y; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->blend_solid_vspan(x, y, len, c, covers); + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, int len, const color_type* colors) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + int d = xmin() - x; + len -= d; + if(len <= 0) return; + colors += d; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->copy_color_hspan(x, y, len, colors); + } + + + //-------------------------------------------------------------------- + void copy_color_vspan(int x, int y, int len, const color_type* colors) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + int d = ymin() - y; + len -= d; + if(len <= 0) return; + colors += d; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->copy_color_vspan(x, y, len, colors); + } + + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = agg::cover_full) + { + if(y > ymax()) return; + if(y < ymin()) return; + + if(x < xmin()) + { + int d = xmin() - x; + len -= d; + if(len <= 0) return; + if(covers) covers += d; + colors += d; + x = xmin(); + } + if(x + len > xmax()) + { + len = xmax() - x + 1; + if(len <= 0) return; + } + m_ren->blend_color_hspan(x, y, len, colors, covers, cover); + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = agg::cover_full) + { + if(x > xmax()) return; + if(x < xmin()) return; + + if(y < ymin()) + { + int d = ymin() - y; + len -= d; + if(len <= 0) return; + if(covers) covers += d; + colors += d; + y = ymin(); + } + if(y + len > ymax()) + { + len = ymax() - y + 1; + if(len <= 0) return; + } + m_ren->blend_color_vspan(x, y, len, colors, covers, cover); + } + + //-------------------------------------------------------------------- + rect_i clip_rect_area(rect_i& dst, rect_i& src, int wsrc, int hsrc) const + { + rect_i rc(0,0,0,0); + rect_i cb = clip_box(); + ++cb.x2; + ++cb.y2; + + if(src.x1 < 0) + { + dst.x1 -= src.x1; + src.x1 = 0; + } + if(src.y1 < 0) + { + dst.y1 -= src.y1; + src.y1 = 0; + } + + if(src.x2 > wsrc) src.x2 = wsrc; + if(src.y2 > hsrc) src.y2 = hsrc; + + if(dst.x1 < cb.x1) + { + src.x1 += cb.x1 - dst.x1; + dst.x1 = cb.x1; + } + if(dst.y1 < cb.y1) + { + src.y1 += cb.y1 - dst.y1; + dst.y1 = cb.y1; + } + + if(dst.x2 > cb.x2) dst.x2 = cb.x2; + if(dst.y2 > cb.y2) dst.y2 = cb.y2; + + rc.x2 = dst.x2 - dst.x1; + rc.y2 = dst.y2 - dst.y1; + + if(rc.x2 > src.x2 - src.x1) rc.x2 = src.x2 - src.x1; + if(rc.y2 > src.y2 - src.y1) rc.y2 = src.y2 - src.y1; + return rc; + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + m_ren->copy_from(src, + rdst.x1, rdst.y1, + rsrc.x1, rsrc.y1, + rc.x2); + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& src, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from(src, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_color(const SrcPixelFormatRenderer& src, + const color_type& color, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from_color(src, + color, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + //-------------------------------------------------------------------- + template + void blend_from_lut(const SrcPixelFormatRenderer& src, + const color_type* color_lut, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = agg::cover_full) + { + rect_i rsrc(0, 0, src.width(), src.height()); + if(rect_src_ptr) + { + rsrc.x1 = rect_src_ptr->x1; + rsrc.y1 = rect_src_ptr->y1; + rsrc.x2 = rect_src_ptr->x2 + 1; + rsrc.y2 = rect_src_ptr->y2 + 1; + } + + // Version with xdst, ydst (absolute positioning) + //rect_i rdst(xdst, ydst, xdst + rsrc.x2 - rsrc.x1, ydst + rsrc.y2 - rsrc.y1); + + // Version with dx, dy (relative positioning) + rect_i rdst(rsrc.x1 + dx, rsrc.y1 + dy, rsrc.x2 + dx, rsrc.y2 + dy); + rect_i rc = clip_rect_area(rdst, rsrc, src.width(), src.height()); + + if(rc.x2 > 0) + { + int incy = 1; + if(rdst.y1 > rsrc.y1) + { + rsrc.y1 += rc.y2 - 1; + rdst.y1 += rc.y2 - 1; + incy = -1; + } + while(rc.y2 > 0) + { + typename SrcPixelFormatRenderer::row_data rw = src.row(rsrc.y1); + if(rw.ptr) + { + int x1src = rsrc.x1; + int x1dst = rdst.x1; + int len = rc.x2; + if(rw.x1 > x1src) + { + x1dst += rw.x1 - x1src; + len -= rw.x1 - x1src; + x1src = rw.x1; + } + if(len > 0) + { + if(x1src + len-1 > rw.x2) + { + len -= x1src + len - rw.x2 - 1; + } + if(len > 0) + { + m_ren->blend_from_lut(src, + color_lut, + x1dst, rdst.y1, + x1src, rsrc.y1, + len, + cover); + } + } + } + rdst.y1 += incy; + rsrc.y1 += incy; + --rc.y2; + } + } + } + + private: + pixfmt_type* m_ren; + rect_i m_clip_box; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_markers.h b/desmume/src/windows/agg/include/agg_renderer_markers.h new file mode 100644 index 000000000..40d34da4f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_markers.h @@ -0,0 +1,711 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_MARKERS_INCLUDED +#define AGG_RENDERER_MARKERS_INCLUDED + +#include "agg_basics.h" +#include "agg_renderer_primitives.h" + +namespace agg +{ + + //---------------------------------------------------------------marker_e + enum marker_e + { + marker_square, + marker_diamond, + marker_circle, + marker_crossed_circle, + marker_semiellipse_left, + marker_semiellipse_right, + marker_semiellipse_up, + marker_semiellipse_down, + marker_triangle_left, + marker_triangle_right, + marker_triangle_up, + marker_triangle_down, + marker_four_rays, + marker_cross, + marker_x, + marker_dash, + marker_dot, + marker_pixel, + + end_of_markers + }; + + + + //--------------------------------------------------------renderer_markers + template class renderer_markers : + public renderer_primitives + { + public: + typedef renderer_primitives base_type; + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + renderer_markers(base_ren_type& rbuf) : + base_type(rbuf) + {} + + //-------------------------------------------------------------------- + bool visible(int x, int y, int r) const + { + rect_i rc(x-r, y-r, x+y, y+r); + return rc.clip(base_type::ren().bounding_clip_box()); + } + + //-------------------------------------------------------------------- + void square(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) base_type::outlined_rectangle(x-r, y-r, x+r, y+r); + else base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + + //-------------------------------------------------------------------- + void diamond(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + do + { + base_type::ren().blend_pixel(x - dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dx, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y - dy, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_hline(x-dx+1, y+dy, x+dx-1, base_type::fill_color(), cover_full); + base_type::ren().blend_hline(x-dx+1, y-dy, x+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + ++dx; + } + while(dy <= 0); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + //-------------------------------------------------------------------- + void circle(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) base_type::outlined_ellipse(x, y, r, r); + else base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + + + + //-------------------------------------------------------------------- + void crossed_circle(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + base_type::outlined_ellipse(x, y, r, r); + int r6 = r + (r >> 1); + if(r <= 2) r6++; + r >>= 1; + base_type::ren().blend_hline(x-r6, y, x-r, base_type::line_color(), cover_full); + base_type::ren().blend_hline(x+r, y, x+r6, base_type::line_color(), cover_full); + base_type::ren().blend_vline(x, y-r6, y-r, base_type::line_color(), cover_full); + base_type::ren().blend_vline(x, y+r, y+r6, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //------------------------------------------------------------------------ + void semiellipse_left(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int r8 = r * 4 / 5; + int dy = -r; + int dx = 0; + ellipse_bresenham_interpolator ei(r * 3 / 5, r+r8); + do + { + dx += ei.dx(); + dy += ei.dy(); + + base_type::ren().blend_pixel(x + dy, y + dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dy, y - dx, base_type::line_color(), cover_full); + + if(ei.dy() && dx) + { + base_type::ren().blend_vline(x+dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + } + ++ei; + } + while(dy < r8); + base_type::ren().blend_vline(x+dy, y-dx, y+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void semiellipse_right(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int r8 = r * 4 / 5; + int dy = -r; + int dx = 0; + ellipse_bresenham_interpolator ei(r * 3 / 5, r+r8); + do + { + dx += ei.dx(); + dy += ei.dy(); + + base_type::ren().blend_pixel(x - dy, y + dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y - dx, base_type::line_color(), cover_full); + + if(ei.dy() && dx) + { + base_type::ren().blend_vline(x-dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + } + ++ei; + } + while(dy < r8); + base_type::ren().blend_vline(x-dy, y-dx, y+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void semiellipse_up(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int r8 = r * 4 / 5; + int dy = -r; + int dx = 0; + ellipse_bresenham_interpolator ei(r * 3 / 5, r+r8); + do + { + dx += ei.dx(); + dy += ei.dy(); + + base_type::ren().blend_pixel(x + dx, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dx, y - dy, base_type::line_color(), cover_full); + + if(ei.dy() && dx) + { + base_type::ren().blend_hline(x-dx+1, y-dy, x+dx-1, base_type::fill_color(), cover_full); + } + ++ei; + } + while(dy < r8); + base_type::ren().blend_hline(x-dx, y-dy-1, x+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void semiellipse_down(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int r8 = r * 4 / 5; + int dy = -r; + int dx = 0; + ellipse_bresenham_interpolator ei(r * 3 / 5, r+r8); + do + { + dx += ei.dx(); + dy += ei.dy(); + + base_type::ren().blend_pixel(x + dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dx, y + dy, base_type::line_color(), cover_full); + + if(ei.dy() && dx) + { + base_type::ren().blend_hline(x-dx+1, y+dy, x+dx-1, base_type::fill_color(), cover_full); + } + ++ei; + } + while(dy < r8); + base_type::ren().blend_hline(x-dx, y+dy+1, x+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void triangle_left(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + int flip = 0; + int r6 = r * 3 / 5; + do + { + base_type::ren().blend_pixel(x + dy, y - dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dy, y + dx, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_vline(x+dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + dx += flip; + flip ^= 1; + } + while(dy < r6); + base_type::ren().blend_vline(x+dy, y-dx, y+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void triangle_right(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + int flip = 0; + int r6 = r * 3 / 5; + do + { + base_type::ren().blend_pixel(x - dy, y - dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y + dx, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_vline(x-dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + dx += flip; + flip ^= 1; + } + while(dy < r6); + base_type::ren().blend_vline(x-dy, y-dx, y+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void triangle_up(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + int flip = 0; + int r6 = r * 3 / 5; + do + { + base_type::ren().blend_pixel(x - dx, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y - dy, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_hline(x-dx+1, y-dy, x+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + dx += flip; + flip ^= 1; + } + while(dy < r6); + base_type::ren().blend_hline(x-dx, y-dy, x+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void triangle_down(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + int flip = 0; + int r6 = r * 3 / 5; + do + { + base_type::ren().blend_pixel(x - dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y + dy, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_hline(x-dx+1, y+dy, x+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + dx += flip; + flip ^= 1; + } + while(dy < r6); + base_type::ren().blend_hline(x-dx, y+dy, x+dx, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void four_rays(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r; + int dx = 0; + int flip = 0; + int r3 = -(r / 3); + do + { + base_type::ren().blend_pixel(x - dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dx, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dx, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dy, y - dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dy, y + dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y - dx, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y + dx, base_type::line_color(), cover_full); + + if(dx) + { + base_type::ren().blend_hline(x-dx+1, y+dy, x+dx-1, base_type::fill_color(), cover_full); + base_type::ren().blend_hline(x-dx+1, y-dy, x+dx-1, base_type::fill_color(), cover_full); + base_type::ren().blend_vline(x+dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + base_type::ren().blend_vline(x-dy, y-dx+1, y+dx-1, base_type::fill_color(), cover_full); + } + ++dy; + dx += flip; + flip ^= 1; + } + while(dy <= r3); + base_type::solid_rectangle(x+r3+1, y+r3+1, x-r3-1, y-r3-1); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void cross(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + base_type::ren().blend_vline(x, y-r, y+r, base_type::line_color(), cover_full); + base_type::ren().blend_hline(x-r, y, x+r, base_type::line_color(), cover_full); + } + else + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + } + + + //-------------------------------------------------------------------- + void xing(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) + { + int dy = -r * 7 / 10; + do + { + base_type::ren().blend_pixel(x + dy, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y + dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x + dy, y - dy, base_type::line_color(), cover_full); + base_type::ren().blend_pixel(x - dy, y - dy, base_type::line_color(), cover_full); + ++dy; + } + while(dy < 0); + } + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + + + //-------------------------------------------------------------------- + void dash(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) base_type::ren().blend_hline(x-r, y, x+r, base_type::line_color(), cover_full); + else base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + + + //-------------------------------------------------------------------- + void dot(int x, int y, int r) + { + if(visible(x, y, r)) + { + if(r) base_type::solid_ellipse(x, y, r, r); + else base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + } + + //-------------------------------------------------------------------- + void pixel(int x, int y, int) + { + base_type::ren().blend_pixel(x, y, base_type::fill_color(), cover_full); + } + + //-------------------------------------------------------------------- + void marker(int x, int y, int r, marker_e type) + { + switch(type) + { + case marker_square: square(x, y, r); break; + case marker_diamond: diamond(x, y, r); break; + case marker_circle: circle(x, y, r); break; + case marker_crossed_circle: crossed_circle(x, y, r); break; + case marker_semiellipse_left: semiellipse_left(x, y, r); break; + case marker_semiellipse_right: semiellipse_right(x, y, r); break; + case marker_semiellipse_up: semiellipse_up(x, y, r); break; + case marker_semiellipse_down: semiellipse_down(x, y, r); break; + case marker_triangle_left: triangle_left(x, y, r); break; + case marker_triangle_right: triangle_right(x, y, r); break; + case marker_triangle_up: triangle_up(x, y, r); break; + case marker_triangle_down: triangle_down(x, y, r); break; + case marker_four_rays: four_rays(x, y, r); break; + case marker_cross: cross(x, y, r); break; + case marker_x: xing(x, y, r); break; + case marker_dash: dash(x, y, r); break; + case marker_dot: dot(x, y, r); break; + case marker_pixel: pixel(x, y, r); break; + } + } + + + //-------------------------------------------------------------------- + template + void markers(int n, const T* x, const T* y, T r, marker_e type) + { + if(n <= 0) return; + if(r == 0) + { + do + { + base_type::ren().blend_pixel(int(*x), int(*y), base_type::fill_color(), cover_full); + ++x; + ++y; + } + while(--n); + return; + } + + switch(type) + { + case marker_square: do { square (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_diamond: do { diamond (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_circle: do { circle (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_crossed_circle: do { crossed_circle (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_semiellipse_left: do { semiellipse_left (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_semiellipse_right: do { semiellipse_right(int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_semiellipse_up: do { semiellipse_up (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_semiellipse_down: do { semiellipse_down (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_triangle_left: do { triangle_left (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_triangle_right: do { triangle_right (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_triangle_up: do { triangle_up (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_triangle_down: do { triangle_down (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_four_rays: do { four_rays (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_cross: do { cross (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_x: do { xing (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_dash: do { dash (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_dot: do { dot (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + case marker_pixel: do { pixel (int(*x), int(*y), int(r)); ++x; ++y; } while(--n); break; + } + } + + //-------------------------------------------------------------------- + template + void markers(int n, const T* x, const T* y, const T* r, marker_e type) + { + if(n <= 0) return; + switch(type) + { + case marker_square: do { square (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_diamond: do { diamond (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_circle: do { circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_crossed_circle: do { crossed_circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_semiellipse_left: do { semiellipse_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_semiellipse_right: do { semiellipse_right(int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_semiellipse_up: do { semiellipse_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_semiellipse_down: do { semiellipse_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_triangle_left: do { triangle_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_triangle_right: do { triangle_right (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_triangle_up: do { triangle_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_triangle_down: do { triangle_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_four_rays: do { four_rays (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_cross: do { cross (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_x: do { xing (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_dash: do { dash (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_dot: do { dot (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + case marker_pixel: do { pixel (int(*x), int(*y), int(*r)); ++x; ++y; ++r; } while(--n); break; + } + } + + //-------------------------------------------------------------------- + template + void markers(int n, const T* x, const T* y, const T* r, const color_type* fc, marker_e type) + { + if(n <= 0) return; + switch(type) + { + case marker_square: do { base_type::fill_color(*fc); square (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_diamond: do { base_type::fill_color(*fc); diamond (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_circle: do { base_type::fill_color(*fc); circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_crossed_circle: do { base_type::fill_color(*fc); crossed_circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_semiellipse_left: do { base_type::fill_color(*fc); semiellipse_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_semiellipse_right: do { base_type::fill_color(*fc); semiellipse_right(int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_semiellipse_up: do { base_type::fill_color(*fc); semiellipse_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_semiellipse_down: do { base_type::fill_color(*fc); semiellipse_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_triangle_left: do { base_type::fill_color(*fc); triangle_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_triangle_right: do { base_type::fill_color(*fc); triangle_right (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_triangle_up: do { base_type::fill_color(*fc); triangle_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_triangle_down: do { base_type::fill_color(*fc); triangle_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_four_rays: do { base_type::fill_color(*fc); four_rays (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_cross: do { base_type::fill_color(*fc); cross (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_x: do { base_type::fill_color(*fc); xing (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_dash: do { base_type::fill_color(*fc); dash (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_dot: do { base_type::fill_color(*fc); dot (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + case marker_pixel: do { base_type::fill_color(*fc); pixel (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; } while(--n); break; + } + } + + //-------------------------------------------------------------------- + template + void markers(int n, const T* x, const T* y, const T* r, const color_type* fc, const color_type* lc, marker_e type) + { + if(n <= 0) return; + switch(type) + { + case marker_square: do { base_type::fill_color(*fc); base_type::line_color(*lc); square (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_diamond: do { base_type::fill_color(*fc); base_type::line_color(*lc); diamond (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_circle: do { base_type::fill_color(*fc); base_type::line_color(*lc); circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_crossed_circle: do { base_type::fill_color(*fc); base_type::line_color(*lc); crossed_circle (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_semiellipse_left: do { base_type::fill_color(*fc); base_type::line_color(*lc); semiellipse_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_semiellipse_right: do { base_type::fill_color(*fc); base_type::line_color(*lc); semiellipse_right(int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_semiellipse_up: do { base_type::fill_color(*fc); base_type::line_color(*lc); semiellipse_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_semiellipse_down: do { base_type::fill_color(*fc); base_type::line_color(*lc); semiellipse_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_triangle_left: do { base_type::fill_color(*fc); base_type::line_color(*lc); triangle_left (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_triangle_right: do { base_type::fill_color(*fc); base_type::line_color(*lc); triangle_right (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_triangle_up: do { base_type::fill_color(*fc); base_type::line_color(*lc); triangle_up (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_triangle_down: do { base_type::fill_color(*fc); base_type::line_color(*lc); triangle_down (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_four_rays: do { base_type::fill_color(*fc); base_type::line_color(*lc); four_rays (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_cross: do { base_type::fill_color(*fc); base_type::line_color(*lc); cross (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_x: do { base_type::fill_color(*fc); base_type::line_color(*lc); xing (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_dash: do { base_type::fill_color(*fc); base_type::line_color(*lc); dash (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_dot: do { base_type::fill_color(*fc); base_type::line_color(*lc); dot (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + case marker_pixel: do { base_type::fill_color(*fc); base_type::line_color(*lc); pixel (int(*x), int(*y), int(*r)); ++x; ++y; ++r; ++fc; ++lc; } while(--n); break; + } + } + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_mclip.h b/desmume/src/windows/agg/include/agg_renderer_mclip.h new file mode 100644 index 000000000..a2a5b6569 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_mclip.h @@ -0,0 +1,349 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_MCLIP_INCLUDED +#define AGG_RENDERER_MCLIP_INCLUDED + +#include "agg_basics.h" +#include "agg_array.h" +#include "agg_renderer_base.h" + +namespace agg +{ + + //----------------------------------------------------------renderer_mclip + template class renderer_mclip + { + public: + typedef PixelFormat pixfmt_type; + typedef typename pixfmt_type::color_type color_type; + typedef typename pixfmt_type::row_data row_data; + typedef renderer_base base_ren_type; + + //-------------------------------------------------------------------- + explicit renderer_mclip(pixfmt_type& pixf) : + m_ren(pixf), + m_curr_cb(0), + m_bounds(m_ren.xmin(), m_ren.ymin(), m_ren.xmax(), m_ren.ymax()) + {} + void attach(pixfmt_type& pixf) + { + m_ren.attach(pixf); + reset_clipping(true); + } + + //-------------------------------------------------------------------- + const pixfmt_type& ren() const { return m_ren.ren(); } + pixfmt_type& ren() { return m_ren.ren(); } + + //-------------------------------------------------------------------- + unsigned width() const { return m_ren.width(); } + unsigned height() const { return m_ren.height(); } + + //-------------------------------------------------------------------- + const rect_i& clip_box() const { return m_ren.clip_box(); } + int xmin() const { return m_ren.xmin(); } + int ymin() const { return m_ren.ymin(); } + int xmax() const { return m_ren.xmax(); } + int ymax() const { return m_ren.ymax(); } + + //-------------------------------------------------------------------- + const rect_i& bounding_clip_box() const { return m_bounds; } + int bounding_xmin() const { return m_bounds.x1; } + int bounding_ymin() const { return m_bounds.y1; } + int bounding_xmax() const { return m_bounds.x2; } + int bounding_ymax() const { return m_bounds.y2; } + + //-------------------------------------------------------------------- + void first_clip_box() + { + m_curr_cb = 0; + if(m_clip.size()) + { + const rect_i& cb = m_clip[0]; + m_ren.clip_box_naked(cb.x1, cb.y1, cb.x2, cb.y2); + } + } + + //-------------------------------------------------------------------- + bool next_clip_box() + { + if(++m_curr_cb < m_clip.size()) + { + const rect_i& cb = m_clip[m_curr_cb]; + m_ren.clip_box_naked(cb.x1, cb.y1, cb.x2, cb.y2); + return true; + } + return false; + } + + //-------------------------------------------------------------------- + void reset_clipping(bool visibility) + { + m_ren.reset_clipping(visibility); + m_clip.remove_all(); + m_curr_cb = 0; + m_bounds = m_ren.clip_box(); + } + + //-------------------------------------------------------------------- + void add_clip_box(int x1, int y1, int x2, int y2) + { + rect_i cb(x1, y1, x2, y2); + cb.normalize(); + if(cb.clip(rect_i(0, 0, width() - 1, height() - 1))) + { + m_clip.add(cb); + if(cb.x1 < m_bounds.x1) m_bounds.x1 = cb.x1; + if(cb.y1 < m_bounds.y1) m_bounds.y1 = cb.y1; + if(cb.x2 > m_bounds.x2) m_bounds.x2 = cb.x2; + if(cb.y2 > m_bounds.y2) m_bounds.y2 = cb.y2; + } + } + + //-------------------------------------------------------------------- + void clear(const color_type& c) + { + m_ren.clear(c); + } + + //-------------------------------------------------------------------- + void copy_pixel(int x, int y, const color_type& c) + { + first_clip_box(); + do + { + if(m_ren.inbox(x, y)) + { + m_ren.ren().copy_pixel(x, y, c); + break; + } + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_pixel(int x, int y, const color_type& c, cover_type cover) + { + first_clip_box(); + do + { + if(m_ren.inbox(x, y)) + { + m_ren.ren().blend_pixel(x, y, c, cover); + break; + } + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + color_type pixel(int x, int y) const + { + if(m_ren.inbox(x, y)) + { + return m_ren.ren().pixel(x, y); + } + return color_type::no_color(); + } + + //-------------------------------------------------------------------- + void copy_hline(int x1, int y, int x2, const color_type& c) + { + first_clip_box(); + do + { + m_ren.copy_hline(x1, y, x2, c); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void copy_vline(int x, int y1, int y2, const color_type& c) + { + first_clip_box(); + do + { + m_ren.copy_vline(x, y1, y2, c); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_hline(int x1, int y, int x2, + const color_type& c, cover_type cover) + { + first_clip_box(); + do + { + m_ren.blend_hline(x1, y, x2, c, cover); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_vline(int x, int y1, int y2, + const color_type& c, cover_type cover) + { + first_clip_box(); + do + { + m_ren.blend_vline(x, y1, y2, c, cover); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void copy_bar(int x1, int y1, int x2, int y2, const color_type& c) + { + first_clip_box(); + do + { + m_ren.copy_bar(x1, y1, x2, y2, c); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_bar(int x1, int y1, int x2, int y2, + const color_type& c, cover_type cover) + { + first_clip_box(); + do + { + m_ren.blend_bar(x1, y1, x2, y2, c, cover); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, int len, + const color_type& c, const cover_type* covers) + { + first_clip_box(); + do + { + m_ren.blend_solid_hspan(x, y, len, c, covers); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, int len, + const color_type& c, const cover_type* covers) + { + first_clip_box(); + do + { + m_ren.blend_solid_vspan(x, y, len, c, covers); + } + while(next_clip_box()); + } + + + //-------------------------------------------------------------------- + void copy_color_hspan(int x, int y, int len, const color_type* colors) + { + first_clip_box(); + do + { + m_ren.copy_color_hspan(x, y, len, colors); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_color_hspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = cover_full) + { + first_clip_box(); + do + { + m_ren.blend_color_hspan(x, y, len, colors, covers, cover); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void blend_color_vspan(int x, int y, int len, + const color_type* colors, + const cover_type* covers, + cover_type cover = cover_full) + { + first_clip_box(); + do + { + m_ren.blend_color_vspan(x, y, len, colors, covers, cover); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + void copy_from(const rendering_buffer& from, + const rect_i* rc=0, + int x_to=0, + int y_to=0) + { + first_clip_box(); + do + { + m_ren.copy_from(from, rc, x_to, y_to); + } + while(next_clip_box()); + } + + //-------------------------------------------------------------------- + template + void blend_from(const SrcPixelFormatRenderer& src, + const rect_i* rect_src_ptr = 0, + int dx = 0, + int dy = 0, + cover_type cover = cover_full) + { + first_clip_box(); + do + { + m_ren.blend_from(src, rect_src_ptr, dx, dy, cover); + } + while(next_clip_box()); + } + + + private: + renderer_mclip(const renderer_mclip&); + const renderer_mclip& + operator = (const renderer_mclip&); + + base_ren_type m_ren; + pod_bvector m_clip; + unsigned m_curr_cb; + rect_i m_bounds; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_outline_aa.h b/desmume/src/windows/agg/include/agg_renderer_outline_aa.h new file mode 100644 index 000000000..70b4c9b7e --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_outline_aa.h @@ -0,0 +1,1847 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_OUTLINE_AA_INCLUDED +#define AGG_RENDERER_OUTLINE_AA_INCLUDED + +#include "agg_array.h" +#include "agg_math.h" +#include "agg_line_aa_basics.h" +#include "agg_dda_line.h" +#include "agg_ellipse_bresenham.h" +#include "agg_renderer_base.h" +#include "agg_gamma_functions.h" +#include "agg_clip_liang_barsky.h" + +namespace agg +{ + + //===================================================distance_interpolator0 + class distance_interpolator0 + { + public: + //--------------------------------------------------------------------- + distance_interpolator0() {} + distance_interpolator0(int x1, int y1, int x2, int y2, int x, int y) : + m_dx(line_mr(x2) - line_mr(x1)), + m_dy(line_mr(y2) - line_mr(y1)), + m_dist((line_mr(x + line_subpixel_scale/2) - line_mr(x2)) * m_dy - + (line_mr(y + line_subpixel_scale/2) - line_mr(y2)) * m_dx) + { + m_dx <<= line_mr_subpixel_shift; + m_dy <<= line_mr_subpixel_shift; + } + + //--------------------------------------------------------------------- + void inc_x() { m_dist += m_dy; } + int dist() const { return m_dist; } + + private: + //--------------------------------------------------------------------- + int m_dx; + int m_dy; + int m_dist; + }; + + //==================================================distance_interpolator00 + class distance_interpolator00 + { + public: + //--------------------------------------------------------------------- + distance_interpolator00() {} + distance_interpolator00(int xc, int yc, + int x1, int y1, int x2, int y2, + int x, int y) : + m_dx1(line_mr(x1) - line_mr(xc)), + m_dy1(line_mr(y1) - line_mr(yc)), + m_dx2(line_mr(x2) - line_mr(xc)), + m_dy2(line_mr(y2) - line_mr(yc)), + m_dist1((line_mr(x + line_subpixel_scale/2) - line_mr(x1)) * m_dy1 - + (line_mr(y + line_subpixel_scale/2) - line_mr(y1)) * m_dx1), + m_dist2((line_mr(x + line_subpixel_scale/2) - line_mr(x2)) * m_dy2 - + (line_mr(y + line_subpixel_scale/2) - line_mr(y2)) * m_dx2) + { + m_dx1 <<= line_mr_subpixel_shift; + m_dy1 <<= line_mr_subpixel_shift; + m_dx2 <<= line_mr_subpixel_shift; + m_dy2 <<= line_mr_subpixel_shift; + } + + //--------------------------------------------------------------------- + void inc_x() { m_dist1 += m_dy1; m_dist2 += m_dy2; } + int dist1() const { return m_dist1; } + int dist2() const { return m_dist2; } + + private: + //--------------------------------------------------------------------- + int m_dx1; + int m_dy1; + int m_dx2; + int m_dy2; + int m_dist1; + int m_dist2; + }; + + //===================================================distance_interpolator1 + class distance_interpolator1 + { + public: + //--------------------------------------------------------------------- + distance_interpolator1() {} + distance_interpolator1(int x1, int y1, int x2, int y2, int x, int y) : + m_dx(x2 - x1), + m_dy(y2 - y1), + m_dist(iround(double(x + line_subpixel_scale/2 - x2) * double(m_dy) - + double(y + line_subpixel_scale/2 - y2) * double(m_dx))) + { + m_dx <<= line_subpixel_shift; + m_dy <<= line_subpixel_shift; + } + + //--------------------------------------------------------------------- + void inc_x() { m_dist += m_dy; } + void dec_x() { m_dist -= m_dy; } + void inc_y() { m_dist -= m_dx; } + void dec_y() { m_dist += m_dx; } + + //--------------------------------------------------------------------- + void inc_x(int dy) + { + m_dist += m_dy; + if(dy > 0) m_dist -= m_dx; + if(dy < 0) m_dist += m_dx; + } + + //--------------------------------------------------------------------- + void dec_x(int dy) + { + m_dist -= m_dy; + if(dy > 0) m_dist -= m_dx; + if(dy < 0) m_dist += m_dx; + } + + //--------------------------------------------------------------------- + void inc_y(int dx) + { + m_dist -= m_dx; + if(dx > 0) m_dist += m_dy; + if(dx < 0) m_dist -= m_dy; + } + + void dec_y(int dx) + //--------------------------------------------------------------------- + { + m_dist += m_dx; + if(dx > 0) m_dist += m_dy; + if(dx < 0) m_dist -= m_dy; + } + + //--------------------------------------------------------------------- + int dist() const { return m_dist; } + int dx() const { return m_dx; } + int dy() const { return m_dy; } + + private: + //--------------------------------------------------------------------- + int m_dx; + int m_dy; + int m_dist; + }; + + + + + + //===================================================distance_interpolator2 + class distance_interpolator2 + { + public: + //--------------------------------------------------------------------- + distance_interpolator2() {} + distance_interpolator2(int x1, int y1, int x2, int y2, + int sx, int sy, int x, int y) : + m_dx(x2 - x1), + m_dy(y2 - y1), + m_dx_start(line_mr(sx) - line_mr(x1)), + m_dy_start(line_mr(sy) - line_mr(y1)), + + m_dist(iround(double(x + line_subpixel_scale/2 - x2) * double(m_dy) - + double(y + line_subpixel_scale/2 - y2) * double(m_dx))), + + m_dist_start((line_mr(x + line_subpixel_scale/2) - line_mr(sx)) * m_dy_start - + (line_mr(y + line_subpixel_scale/2) - line_mr(sy)) * m_dx_start) + { + m_dx <<= line_subpixel_shift; + m_dy <<= line_subpixel_shift; + m_dx_start <<= line_mr_subpixel_shift; + m_dy_start <<= line_mr_subpixel_shift; + } + + distance_interpolator2(int x1, int y1, int x2, int y2, + int ex, int ey, int x, int y, int) : + m_dx(x2 - x1), + m_dy(y2 - y1), + m_dx_start(line_mr(ex) - line_mr(x2)), + m_dy_start(line_mr(ey) - line_mr(y2)), + + m_dist(iround(double(x + line_subpixel_scale/2 - x2) * double(m_dy) - + double(y + line_subpixel_scale/2 - y2) * double(m_dx))), + + m_dist_start((line_mr(x + line_subpixel_scale/2) - line_mr(ex)) * m_dy_start - + (line_mr(y + line_subpixel_scale/2) - line_mr(ey)) * m_dx_start) + { + m_dx <<= line_subpixel_shift; + m_dy <<= line_subpixel_shift; + m_dx_start <<= line_mr_subpixel_shift; + m_dy_start <<= line_mr_subpixel_shift; + } + + + //--------------------------------------------------------------------- + void inc_x() { m_dist += m_dy; m_dist_start += m_dy_start; } + void dec_x() { m_dist -= m_dy; m_dist_start -= m_dy_start; } + void inc_y() { m_dist -= m_dx; m_dist_start -= m_dx_start; } + void dec_y() { m_dist += m_dx; m_dist_start += m_dx_start; } + + //--------------------------------------------------------------------- + void inc_x(int dy) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + } + } + + //--------------------------------------------------------------------- + void dec_x(int dy) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + } + } + + //--------------------------------------------------------------------- + void inc_y(int dx) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + } + } + + //--------------------------------------------------------------------- + void dec_y(int dx) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + } + } + + //--------------------------------------------------------------------- + int dist() const { return m_dist; } + int dist_start() const { return m_dist_start; } + int dist_end() const { return m_dist_start; } + + //--------------------------------------------------------------------- + int dx() const { return m_dx; } + int dy() const { return m_dy; } + int dx_start() const { return m_dx_start; } + int dy_start() const { return m_dy_start; } + int dx_end() const { return m_dx_start; } + int dy_end() const { return m_dy_start; } + + private: + //--------------------------------------------------------------------- + int m_dx; + int m_dy; + int m_dx_start; + int m_dy_start; + + int m_dist; + int m_dist_start; + }; + + + + + + //===================================================distance_interpolator3 + class distance_interpolator3 + { + public: + //--------------------------------------------------------------------- + distance_interpolator3() {} + distance_interpolator3(int x1, int y1, int x2, int y2, + int sx, int sy, int ex, int ey, + int x, int y) : + m_dx(x2 - x1), + m_dy(y2 - y1), + m_dx_start(line_mr(sx) - line_mr(x1)), + m_dy_start(line_mr(sy) - line_mr(y1)), + m_dx_end(line_mr(ex) - line_mr(x2)), + m_dy_end(line_mr(ey) - line_mr(y2)), + + m_dist(iround(double(x + line_subpixel_scale/2 - x2) * double(m_dy) - + double(y + line_subpixel_scale/2 - y2) * double(m_dx))), + + m_dist_start((line_mr(x + line_subpixel_scale/2) - line_mr(sx)) * m_dy_start - + (line_mr(y + line_subpixel_scale/2) - line_mr(sy)) * m_dx_start), + + m_dist_end((line_mr(x + line_subpixel_scale/2) - line_mr(ex)) * m_dy_end - + (line_mr(y + line_subpixel_scale/2) - line_mr(ey)) * m_dx_end) + { + m_dx <<= line_subpixel_shift; + m_dy <<= line_subpixel_shift; + m_dx_start <<= line_mr_subpixel_shift; + m_dy_start <<= line_mr_subpixel_shift; + m_dx_end <<= line_mr_subpixel_shift; + m_dy_end <<= line_mr_subpixel_shift; + } + + //--------------------------------------------------------------------- + void inc_x() { m_dist += m_dy; m_dist_start += m_dy_start; m_dist_end += m_dy_end; } + void dec_x() { m_dist -= m_dy; m_dist_start -= m_dy_start; m_dist_end -= m_dy_end; } + void inc_y() { m_dist -= m_dx; m_dist_start -= m_dx_start; m_dist_end -= m_dx_end; } + void dec_y() { m_dist += m_dx; m_dist_start += m_dx_start; m_dist_end += m_dx_end; } + + //--------------------------------------------------------------------- + void inc_x(int dy) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_end += m_dy_end; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_end -= m_dx_end; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_end += m_dx_end; + } + } + + //--------------------------------------------------------------------- + void dec_x(int dy) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_end -= m_dy_end; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_end -= m_dx_end; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_end += m_dx_end; + } + } + + //--------------------------------------------------------------------- + void inc_y(int dx) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_end -= m_dx_end; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_end += m_dy_end; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_end -= m_dy_end; + } + } + + //--------------------------------------------------------------------- + void dec_y(int dx) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_end += m_dx_end; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_end += m_dy_end; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_end -= m_dy_end; + } + } + + //--------------------------------------------------------------------- + int dist() const { return m_dist; } + int dist_start() const { return m_dist_start; } + int dist_end() const { return m_dist_end; } + + //--------------------------------------------------------------------- + int dx() const { return m_dx; } + int dy() const { return m_dy; } + int dx_start() const { return m_dx_start; } + int dy_start() const { return m_dy_start; } + int dx_end() const { return m_dx_end; } + int dy_end() const { return m_dy_end; } + + private: + //--------------------------------------------------------------------- + int m_dx; + int m_dy; + int m_dx_start; + int m_dy_start; + int m_dx_end; + int m_dy_end; + + int m_dist; + int m_dist_start; + int m_dist_end; + }; + + + + + + //================================================line_interpolator_aa_base + template class line_interpolator_aa_base + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + + //--------------------------------------------------------------------- + enum max_half_width_e + { + max_half_width = 64 + }; + + //--------------------------------------------------------------------- + line_interpolator_aa_base(renderer_type& ren, const line_parameters& lp) : + m_lp(&lp), + m_li(lp.vertical ? line_dbl_hr(lp.x2 - lp.x1) : + line_dbl_hr(lp.y2 - lp.y1), + lp.vertical ? abs(lp.y2 - lp.y1) : + abs(lp.x2 - lp.x1) + 1), + m_ren(ren), + m_len((lp.vertical == (lp.inc > 0)) ? -lp.len : lp.len), + m_x(lp.x1 >> line_subpixel_shift), + m_y(lp.y1 >> line_subpixel_shift), + m_old_x(m_x), + m_old_y(m_y), + m_count((lp.vertical ? abs((lp.y2 >> line_subpixel_shift) - m_y) : + abs((lp.x2 >> line_subpixel_shift) - m_x))), + m_width(ren.subpixel_width()), + //m_max_extent(m_width >> (line_subpixel_shift - 2)), + m_max_extent((m_width + line_subpixel_mask) >> line_subpixel_shift), + m_step(0) + { + agg::dda2_line_interpolator li(0, lp.vertical ? + (lp.dy << agg::line_subpixel_shift) : + (lp.dx << agg::line_subpixel_shift), + lp.len); + + unsigned i; + int stop = m_width + line_subpixel_scale * 2; + for(i = 0; i < max_half_width; ++i) + { + m_dist[i] = li.y(); + if(m_dist[i] >= stop) break; + ++li; + } + m_dist[i++] = 0x7FFF0000; + } + + //--------------------------------------------------------------------- + template int step_hor_base(DI& di) + { + ++m_li; + m_x += m_lp->inc; + m_y = (m_lp->y1 + m_li.y()) >> line_subpixel_shift; + + if(m_lp->inc > 0) di.inc_x(m_y - m_old_y); + else di.dec_x(m_y - m_old_y); + + m_old_y = m_y; + + return di.dist() / m_len; + } + + //--------------------------------------------------------------------- + template int step_ver_base(DI& di) + { + ++m_li; + m_y += m_lp->inc; + m_x = (m_lp->x1 + m_li.y()) >> line_subpixel_shift; + + if(m_lp->inc > 0) di.inc_y(m_x - m_old_x); + else di.dec_y(m_x - m_old_x); + + m_old_x = m_x; + + return di.dist() / m_len; + } + + //--------------------------------------------------------------------- + bool vertical() const { return m_lp->vertical; } + int width() const { return m_width; } + int count() const { return m_count; } + + private: + line_interpolator_aa_base(const line_interpolator_aa_base&); + const line_interpolator_aa_base& + operator = (const line_interpolator_aa_base&); + + protected: + const line_parameters* m_lp; + dda2_line_interpolator m_li; + renderer_type& m_ren; + int m_len; + int m_x; + int m_y; + int m_old_x; + int m_old_y; + int m_count; + int m_width; + int m_max_extent; + int m_step; + int m_dist[max_half_width + 1]; + cover_type m_covers[max_half_width * 2 + 4]; + }; + + + + + + + + //====================================================line_interpolator_aa0 + template class line_interpolator_aa0 : + public line_interpolator_aa_base + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + typedef line_interpolator_aa_base base_type; + + //--------------------------------------------------------------------- + line_interpolator_aa0(renderer_type& ren, const line_parameters& lp) : + line_interpolator_aa_base(ren, lp), + m_di(lp.x1, lp.y1, lp.x2, lp.y2, + lp.x1 & ~line_subpixel_mask, lp.y1 & ~line_subpixel_mask) + { + base_type::m_li.adjust_forward(); + } + + //--------------------------------------------------------------------- + bool step_hor() + { + int dist; + int dy; + int s1 = base_type::step_hor_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + *p1++ = (cover_type)base_type::m_ren.cover(s1); + + dy = 1; + while((dist = base_type::m_dist[dy] - s1) <= base_type::m_width) + { + *p1++ = (cover_type)base_type::m_ren.cover(dist); + ++dy; + } + + dy = 1; + while((dist = base_type::m_dist[dy] + s1) <= base_type::m_width) + { + *--p0 = (cover_type)base_type::m_ren.cover(dist); + ++dy; + } + base_type::m_ren.blend_solid_vspan(base_type::m_x, + base_type::m_y - dy + 1, + unsigned(p1 - p0), + p0); + return ++base_type::m_step < base_type::m_count; + } + + //--------------------------------------------------------------------- + bool step_ver() + { + int dist; + int dx; + int s1 = base_type::step_ver_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + *p1++ = (cover_type)base_type::m_ren.cover(s1); + + dx = 1; + while((dist = base_type::m_dist[dx] - s1) <= base_type::m_width) + { + *p1++ = (cover_type)base_type::m_ren.cover(dist); + ++dx; + } + + dx = 1; + while((dist = base_type::m_dist[dx] + s1) <= base_type::m_width) + { + *--p0 = (cover_type)base_type::m_ren.cover(dist); + ++dx; + } + base_type::m_ren.blend_solid_hspan(base_type::m_x - dx + 1, + base_type::m_y, + unsigned(p1 - p0), + p0); + return ++base_type::m_step < base_type::m_count; + } + + private: + line_interpolator_aa0(const line_interpolator_aa0&); + const line_interpolator_aa0& + operator = (const line_interpolator_aa0&); + + //--------------------------------------------------------------------- + distance_interpolator1 m_di; + }; + + + + + + + //====================================================line_interpolator_aa1 + template class line_interpolator_aa1 : + public line_interpolator_aa_base + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + typedef line_interpolator_aa_base base_type; + + //--------------------------------------------------------------------- + line_interpolator_aa1(renderer_type& ren, const line_parameters& lp, + int sx, int sy) : + line_interpolator_aa_base(ren, lp), + m_di(lp.x1, lp.y1, lp.x2, lp.y2, sx, sy, + lp.x1 & ~line_subpixel_mask, lp.y1 & ~line_subpixel_mask) + { + int dist1_start; + int dist2_start; + + int npix = 1; + + if(lp.vertical) + { + do + { + --base_type::m_li; + base_type::m_y -= lp.inc; + base_type::m_x = (base_type::m_lp->x1 + base_type::m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_y(base_type::m_x - base_type::m_old_x); + else m_di.inc_y(base_type::m_x - base_type::m_old_x); + + base_type::m_old_x = base_type::m_x; + + dist1_start = dist2_start = m_di.dist_start(); + + int dx = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start += m_di.dy_start(); + dist2_start -= m_di.dy_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dx; + } + while(base_type::m_dist[dx] <= base_type::m_width); + --base_type::m_step; + if(npix == 0) break; + npix = 0; + } + while(base_type::m_step >= -base_type::m_max_extent); + } + else + { + do + { + --base_type::m_li; + base_type::m_x -= lp.inc; + base_type::m_y = (base_type::m_lp->y1 + base_type::m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_x(base_type::m_y - base_type::m_old_y); + else m_di.inc_x(base_type::m_y - base_type::m_old_y); + + base_type::m_old_y = base_type::m_y; + + dist1_start = dist2_start = m_di.dist_start(); + + int dy = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start -= m_di.dx_start(); + dist2_start += m_di.dx_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dy; + } + while(base_type::m_dist[dy] <= base_type::m_width); + --base_type::m_step; + if(npix == 0) break; + npix = 0; + } + while(base_type::m_step >= -base_type::m_max_extent); + } + base_type::m_li.adjust_forward(); + } + + //--------------------------------------------------------------------- + bool step_hor() + { + int dist_start; + int dist; + int dy; + int s1 = base_type::step_hor_base(m_di); + + dist_start = m_di.dist_start(); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + *p1 = 0; + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + } + ++p1; + + dy = 1; + while((dist = base_type::m_dist[dy] - s1) <= base_type::m_width) + { + dist_start -= m_di.dx_start(); + *p1 = 0; + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + } + ++p1; + ++dy; + } + + dy = 1; + dist_start = m_di.dist_start(); + while((dist = base_type::m_dist[dy] + s1) <= base_type::m_width) + { + dist_start += m_di.dx_start(); + *--p0 = 0; + if(dist_start <= 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + } + ++dy; + } + + base_type::m_ren.blend_solid_vspan(base_type::m_x, + base_type::m_y - dy + 1, + unsigned(p1 - p0), + p0); + return ++base_type::m_step < base_type::m_count; + } + + //--------------------------------------------------------------------- + bool step_ver() + { + int dist_start; + int dist; + int dx; + int s1 = base_type::step_ver_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + dist_start = m_di.dist_start(); + + *p1 = 0; + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + } + ++p1; + + dx = 1; + while((dist = base_type::m_dist[dx] - s1) <= base_type::m_width) + { + dist_start += m_di.dy_start(); + *p1 = 0; + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + } + ++p1; + ++dx; + } + + dx = 1; + dist_start = m_di.dist_start(); + while((dist = base_type::m_dist[dx] + s1) <= base_type::m_width) + { + dist_start -= m_di.dy_start(); + *--p0 = 0; + if(dist_start <= 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + } + ++dx; + } + base_type::m_ren.blend_solid_hspan(base_type::m_x - dx + 1, + base_type::m_y, + unsigned(p1 - p0), + p0); + return ++base_type::m_step < base_type::m_count; + } + + private: + line_interpolator_aa1(const line_interpolator_aa1&); + const line_interpolator_aa1& + operator = (const line_interpolator_aa1&); + + //--------------------------------------------------------------------- + distance_interpolator2 m_di; + }; + + + + + + + + + + + + + //====================================================line_interpolator_aa2 + template class line_interpolator_aa2 : + public line_interpolator_aa_base + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + typedef line_interpolator_aa_base base_type; + + //--------------------------------------------------------------------- + line_interpolator_aa2(renderer_type& ren, const line_parameters& lp, + int ex, int ey) : + line_interpolator_aa_base(ren, lp), + m_di(lp.x1, lp.y1, lp.x2, lp.y2, ex, ey, + lp.x1 & ~line_subpixel_mask, lp.y1 & ~line_subpixel_mask, + 0) + { + base_type::m_li.adjust_forward(); + base_type::m_step -= base_type::m_max_extent; + } + + //--------------------------------------------------------------------- + bool step_hor() + { + int dist_end; + int dist; + int dy; + int s1 = base_type::step_hor_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + dist_end = m_di.dist_end(); + + int npix = 0; + *p1 = 0; + if(dist_end > 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + ++npix; + } + ++p1; + + dy = 1; + while((dist = base_type::m_dist[dy] - s1) <= base_type::m_width) + { + dist_end -= m_di.dx_end(); + *p1 = 0; + if(dist_end > 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++p1; + ++dy; + } + + dy = 1; + dist_end = m_di.dist_end(); + while((dist = base_type::m_dist[dy] + s1) <= base_type::m_width) + { + dist_end += m_di.dx_end(); + *--p0 = 0; + if(dist_end > 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++dy; + } + base_type::m_ren.blend_solid_vspan(base_type::m_x, + base_type::m_y - dy + 1, + unsigned(p1 - p0), + p0); + return npix && ++base_type::m_step < base_type::m_count; + } + + //--------------------------------------------------------------------- + bool step_ver() + { + int dist_end; + int dist; + int dx; + int s1 = base_type::step_ver_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + dist_end = m_di.dist_end(); + + int npix = 0; + *p1 = 0; + if(dist_end > 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + ++npix; + } + ++p1; + + dx = 1; + while((dist = base_type::m_dist[dx] - s1) <= base_type::m_width) + { + dist_end += m_di.dy_end(); + *p1 = 0; + if(dist_end > 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++p1; + ++dx; + } + + dx = 1; + dist_end = m_di.dist_end(); + while((dist = base_type::m_dist[dx] + s1) <= base_type::m_width) + { + dist_end -= m_di.dy_end(); + *--p0 = 0; + if(dist_end > 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++dx; + } + base_type::m_ren.blend_solid_hspan(base_type::m_x - dx + 1, + base_type::m_y, + unsigned(p1 - p0), + p0); + return npix && ++base_type::m_step < base_type::m_count; + } + + private: + line_interpolator_aa2(const line_interpolator_aa2&); + const line_interpolator_aa2& + operator = (const line_interpolator_aa2&); + + //--------------------------------------------------------------------- + distance_interpolator2 m_di; + }; + + + + + + + + + + + //====================================================line_interpolator_aa3 + template class line_interpolator_aa3 : + public line_interpolator_aa_base + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + typedef line_interpolator_aa_base base_type; + + //--------------------------------------------------------------------- + line_interpolator_aa3(renderer_type& ren, const line_parameters& lp, + int sx, int sy, int ex, int ey) : + line_interpolator_aa_base(ren, lp), + m_di(lp.x1, lp.y1, lp.x2, lp.y2, sx, sy, ex, ey, + lp.x1 & ~line_subpixel_mask, lp.y1 & ~line_subpixel_mask) + { + int dist1_start; + int dist2_start; + int npix = 1; + if(lp.vertical) + { + do + { + --base_type::m_li; + base_type::m_y -= lp.inc; + base_type::m_x = (base_type::m_lp->x1 + base_type::m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_y(base_type::m_x - base_type::m_old_x); + else m_di.inc_y(base_type::m_x - base_type::m_old_x); + + base_type::m_old_x = base_type::m_x; + + dist1_start = dist2_start = m_di.dist_start(); + + int dx = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start += m_di.dy_start(); + dist2_start -= m_di.dy_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dx; + } + while(base_type::m_dist[dx] <= base_type::m_width); + if(npix == 0) break; + npix = 0; + } + while(--base_type::m_step >= -base_type::m_max_extent); + } + else + { + do + { + --base_type::m_li; + base_type::m_x -= lp.inc; + base_type::m_y = (base_type::m_lp->y1 + base_type::m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_x(base_type::m_y - base_type::m_old_y); + else m_di.inc_x(base_type::m_y - base_type::m_old_y); + + base_type::m_old_y = base_type::m_y; + + dist1_start = dist2_start = m_di.dist_start(); + + int dy = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start -= m_di.dx_start(); + dist2_start += m_di.dx_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dy; + } + while(base_type::m_dist[dy] <= base_type::m_width); + if(npix == 0) break; + npix = 0; + } + while(--base_type::m_step >= -base_type::m_max_extent); + } + base_type::m_li.adjust_forward(); + base_type::m_step -= base_type::m_max_extent; + } + + + //--------------------------------------------------------------------- + bool step_hor() + { + int dist_start; + int dist_end; + int dist; + int dy; + int s1 = base_type::step_hor_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + dist_start = m_di.dist_start(); + dist_end = m_di.dist_end(); + + int npix = 0; + *p1 = 0; + if(dist_end > 0) + { + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + } + ++npix; + } + ++p1; + + dy = 1; + while((dist = base_type::m_dist[dy] - s1) <= base_type::m_width) + { + dist_start -= m_di.dx_start(); + dist_end -= m_di.dx_end(); + *p1 = 0; + if(dist_end > 0 && dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++p1; + ++dy; + } + + dy = 1; + dist_start = m_di.dist_start(); + dist_end = m_di.dist_end(); + while((dist = base_type::m_dist[dy] + s1) <= base_type::m_width) + { + dist_start += m_di.dx_start(); + dist_end += m_di.dx_end(); + *--p0 = 0; + if(dist_end > 0 && dist_start <= 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++dy; + } + base_type::m_ren.blend_solid_vspan(base_type::m_x, + base_type::m_y - dy + 1, + unsigned(p1 - p0), + p0); + return npix && ++base_type::m_step < base_type::m_count; + } + + //--------------------------------------------------------------------- + bool step_ver() + { + int dist_start; + int dist_end; + int dist; + int dx; + int s1 = base_type::step_ver_base(m_di); + cover_type* p0 = base_type::m_covers + base_type::max_half_width + 2; + cover_type* p1 = p0; + + dist_start = m_di.dist_start(); + dist_end = m_di.dist_end(); + + int npix = 0; + *p1 = 0; + if(dist_end > 0) + { + if(dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(s1); + } + ++npix; + } + ++p1; + + dx = 1; + while((dist = base_type::m_dist[dx] - s1) <= base_type::m_width) + { + dist_start += m_di.dy_start(); + dist_end += m_di.dy_end(); + *p1 = 0; + if(dist_end > 0 && dist_start <= 0) + { + *p1 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++p1; + ++dx; + } + + dx = 1; + dist_start = m_di.dist_start(); + dist_end = m_di.dist_end(); + while((dist = base_type::m_dist[dx] + s1) <= base_type::m_width) + { + dist_start -= m_di.dy_start(); + dist_end -= m_di.dy_end(); + *--p0 = 0; + if(dist_end > 0 && dist_start <= 0) + { + *p0 = (cover_type)base_type::m_ren.cover(dist); + ++npix; + } + ++dx; + } + base_type::m_ren.blend_solid_hspan(base_type::m_x - dx + 1, + base_type::m_y, + unsigned(p1 - p0), + p0); + return npix && ++base_type::m_step < base_type::m_count; + } + + private: + line_interpolator_aa3(const line_interpolator_aa3&); + const line_interpolator_aa3& + operator = (const line_interpolator_aa3&); + + //--------------------------------------------------------------------- + distance_interpolator3 m_di; + }; + + + + + //==========================================================line_profile_aa + // + // See Implementation agg_line_profile_aa.cpp + // + class line_profile_aa + { + public: + //--------------------------------------------------------------------- + typedef int8u value_type; + enum subpixel_scale_e + { + subpixel_shift = line_subpixel_shift, + subpixel_scale = 1 << subpixel_shift, + subpixel_mask = subpixel_scale - 1 + }; + + enum aa_scale_e + { + aa_shift = 8, + aa_scale = 1 << aa_shift, + aa_mask = aa_scale - 1 + }; + + //--------------------------------------------------------------------- + line_profile_aa() : + m_subpixel_width(0), + m_min_width(1.0), + m_smoother_width(1.0) + { + int i; + for(i = 0; i < aa_scale; i++) m_gamma[i] = (value_type)i; + } + + //--------------------------------------------------------------------- + template + line_profile_aa(double w, const GammaF& gamma_function) : + m_subpixel_width(0), + m_min_width(1.0), + m_smoother_width(1.0) + { + gamma(gamma_function); + width(w); + } + + //--------------------------------------------------------------------- + void min_width(double w) { m_min_width = w; } + void smoother_width(double w) { m_smoother_width = w; } + + //--------------------------------------------------------------------- + template void gamma(const GammaF& gamma_function) + { + int i; + for(i = 0; i < aa_scale; i++) + { + m_gamma[i] = value_type( + uround(gamma_function(double(i) / aa_mask) * aa_mask)); + } + } + + void width(double w); + + unsigned profile_size() const { return m_profile.size(); } + int subpixel_width() const { return m_subpixel_width; } + + //--------------------------------------------------------------------- + double min_width() const { return m_min_width; } + double smoother_width() const { return m_smoother_width; } + + //--------------------------------------------------------------------- + value_type value(int dist) const + { + return m_profile[dist + subpixel_scale*2]; + } + + private: + line_profile_aa(const line_profile_aa&); + const line_profile_aa& operator = (const line_profile_aa&); + + value_type* profile(double w); + void set(double center_width, double smoother_width); + + //--------------------------------------------------------------------- + pod_array m_profile; + value_type m_gamma[aa_scale]; + int m_subpixel_width; + double m_min_width; + double m_smoother_width; + }; + + + //======================================================renderer_outline_aa + template class renderer_outline_aa + { + public: + //--------------------------------------------------------------------- + typedef BaseRenderer base_ren_type; + typedef renderer_outline_aa self_type; + typedef typename base_ren_type::color_type color_type; + + //--------------------------------------------------------------------- + renderer_outline_aa(base_ren_type& ren, const line_profile_aa& prof) : + m_ren(&ren), + m_profile(&prof), + m_clip_box(0,0,0,0), + m_clipping(false) + {} + void attach(base_ren_type& ren) { m_ren = &ren; } + + //--------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //--------------------------------------------------------------------- + void profile(const line_profile_aa& prof) { m_profile = &prof; } + const line_profile_aa& profile() const { return *m_profile; } + line_profile_aa& profile() { return *m_profile; } + + //--------------------------------------------------------------------- + int subpixel_width() const { return m_profile->subpixel_width(); } + + //--------------------------------------------------------------------- + void reset_clipping() { m_clipping = false; } + void clip_box(double x1, double y1, double x2, double y2) + { + m_clip_box.x1 = line_coord_sat::conv(x1); + m_clip_box.y1 = line_coord_sat::conv(y1); + m_clip_box.x2 = line_coord_sat::conv(x2); + m_clip_box.y2 = line_coord_sat::conv(y2); + m_clipping = true; + } + + //--------------------------------------------------------------------- + int cover(int d) const + { + return m_profile->value(d); + } + + //------------------------------------------------------------------------- + void blend_solid_hspan(int x, int y, unsigned len, const cover_type* covers) + { + m_ren->blend_solid_hspan(x, y, len, m_color, covers); + } + + //------------------------------------------------------------------------- + void blend_solid_vspan(int x, int y, unsigned len, const cover_type* covers) + { + m_ren->blend_solid_vspan(x, y, len, m_color, covers); + } + + //------------------------------------------------------------------------- + static bool accurate_join_only() { return false; } + + //------------------------------------------------------------------------- + template + void semidot_hline(Cmp cmp, + int xc1, int yc1, int xc2, int yc2, + int x1, int y1, int x2) + { + cover_type covers[line_interpolator_aa_base::max_half_width * 2 + 4]; + cover_type* p0 = covers; + cover_type* p1 = covers; + int x = x1 << line_subpixel_shift; + int y = y1 << line_subpixel_shift; + int w = subpixel_width(); + distance_interpolator0 di(xc1, yc1, xc2, yc2, x, y); + x += line_subpixel_scale/2; + y += line_subpixel_scale/2; + + int x0 = x1; + int dx = x - xc1; + int dy = y - yc1; + do + { + int d = int(fast_sqrt(dx*dx + dy*dy)); + *p1 = 0; + if(cmp(di.dist()) && d <= w) + { + *p1 = (cover_type)cover(d); + } + ++p1; + dx += line_subpixel_scale; + di.inc_x(); + } + while(++x1 <= x2); + m_ren->blend_solid_hspan(x0, y1, + unsigned(p1 - p0), + color(), + p0); + } + + //------------------------------------------------------------------------- + template + void semidot(Cmp cmp, int xc1, int yc1, int xc2, int yc2) + { + if(m_clipping && clipping_flags(xc1, yc1, m_clip_box)) return; + + int r = ((subpixel_width() + line_subpixel_mask) >> line_subpixel_shift); + if(r < 1) r = 1; + ellipse_bresenham_interpolator ei(r, r); + int dx = 0; + int dy = -r; + int dy0 = dy; + int dx0 = dx; + int x = xc1 >> line_subpixel_shift; + int y = yc1 >> line_subpixel_shift; + + do + { + dx += ei.dx(); + dy += ei.dy(); + + if(dy != dy0) + { + semidot_hline(cmp, xc1, yc1, xc2, yc2, x-dx0, y+dy0, x+dx0); + semidot_hline(cmp, xc1, yc1, xc2, yc2, x-dx0, y-dy0, x+dx0); + } + dx0 = dx; + dy0 = dy; + ++ei; + } + while(dy < 0); + semidot_hline(cmp, xc1, yc1, xc2, yc2, x-dx0, y+dy0, x+dx0); + } + + //------------------------------------------------------------------------- + void pie_hline(int xc, int yc, int xp1, int yp1, int xp2, int yp2, + int xh1, int yh1, int xh2) + { + if(m_clipping && clipping_flags(xc, yc, m_clip_box)) return; + + cover_type covers[line_interpolator_aa_base::max_half_width * 2 + 4]; + cover_type* p0 = covers; + cover_type* p1 = covers; + int x = xh1 << line_subpixel_shift; + int y = yh1 << line_subpixel_shift; + int w = subpixel_width(); + + distance_interpolator00 di(xc, yc, xp1, yp1, xp2, yp2, x, y); + x += line_subpixel_scale/2; + y += line_subpixel_scale/2; + + int xh0 = xh1; + int dx = x - xc; + int dy = y - yc; + do + { + int d = int(fast_sqrt(dx*dx + dy*dy)); + *p1 = 0; + if(di.dist1() <= 0 && di.dist2() > 0 && d <= w) + { + *p1 = (cover_type)cover(d); + } + ++p1; + dx += line_subpixel_scale; + di.inc_x(); + } + while(++xh1 <= xh2); + m_ren->blend_solid_hspan(xh0, yh1, + unsigned(p1 - p0), + color(), + p0); + } + + + //------------------------------------------------------------------------- + void pie(int xc, int yc, int x1, int y1, int x2, int y2) + { + int r = ((subpixel_width() + line_subpixel_mask) >> line_subpixel_shift); + if(r < 1) r = 1; + ellipse_bresenham_interpolator ei(r, r); + int dx = 0; + int dy = -r; + int dy0 = dy; + int dx0 = dx; + int x = xc >> line_subpixel_shift; + int y = yc >> line_subpixel_shift; + + do + { + dx += ei.dx(); + dy += ei.dy(); + + if(dy != dy0) + { + pie_hline(xc, yc, x1, y1, x2, y2, x-dx0, y+dy0, x+dx0); + pie_hline(xc, yc, x1, y1, x2, y2, x-dx0, y-dy0, x+dx0); + } + dx0 = dx; + dy0 = dy; + ++ei; + } + while(dy < 0); + pie_hline(xc, yc, x1, y1, x2, y2, x-dx0, y+dy0, x+dx0); + } + + //------------------------------------------------------------------------- + void line0_no_clip(const line_parameters& lp) + { + if(lp.len > line_max_length) + { + line_parameters lp1, lp2; + lp.divide(lp1, lp2); + line0_no_clip(lp1); + line0_no_clip(lp2); + return; + } + + line_interpolator_aa0 li(*this, lp); + if(li.count()) + { + if(li.vertical()) + { + while(li.step_ver()); + } + else + { + while(li.step_hor()); + } + } + } + + //------------------------------------------------------------------------- + void line0(const line_parameters& lp) + { + if(m_clipping) + { + int x1 = lp.x1; + int y1 = lp.y1; + int x2 = lp.x2; + int y2 = lp.y2; + unsigned flags = clip_line_segment(&x1, &y1, &x2, &y2, m_clip_box); + if((flags & 4) == 0) + { + if(flags) + { + line_parameters lp2(x1, y1, x2, y2, + uround(calc_distance(x1, y1, x2, y2))); + line0_no_clip(lp2); + } + else + { + line0_no_clip(lp); + } + } + } + else + { + line0_no_clip(lp); + } + } + + //------------------------------------------------------------------------- + void line1_no_clip(const line_parameters& lp, int sx, int sy) + { + if(lp.len > line_max_length) + { + line_parameters lp1, lp2; + lp.divide(lp1, lp2); + line1_no_clip(lp1, (lp.x1 + sx) >> 1, (lp.y1 + sy) >> 1); + line1_no_clip(lp2, lp1.x2 + (lp1.y2 - lp1.y1), lp1.y2 - (lp1.x2 - lp1.x1)); + return; + } + + fix_degenerate_bisectrix_start(lp, &sx, &sy); + line_interpolator_aa1 li(*this, lp, sx, sy); + if(li.vertical()) + { + while(li.step_ver()); + } + else + { + while(li.step_hor()); + } + } + + + //------------------------------------------------------------------------- + void line1(const line_parameters& lp, int sx, int sy) + { + if(m_clipping) + { + int x1 = lp.x1; + int y1 = lp.y1; + int x2 = lp.x2; + int y2 = lp.y2; + unsigned flags = clip_line_segment(&x1, &y1, &x2, &y2, m_clip_box); + if((flags & 4) == 0) + { + if(flags) + { + line_parameters lp2(x1, y1, x2, y2, + uround(calc_distance(x1, y1, x2, y2))); + if(flags & 1) + { + sx = x1 + (y2 - y1); + sy = y1 - (x2 - x1); + } + else + { + while(abs(sx - lp.x1) + abs(sy - lp.y1) > lp2.len) + { + sx = (lp.x1 + sx) >> 1; + sy = (lp.y1 + sy) >> 1; + } + } + line1_no_clip(lp2, sx, sy); + } + else + { + line1_no_clip(lp, sx, sy); + } + } + } + else + { + line1_no_clip(lp, sx, sy); + } + } + + //------------------------------------------------------------------------- + void line2_no_clip(const line_parameters& lp, int ex, int ey) + { + if(lp.len > line_max_length) + { + line_parameters lp1, lp2; + lp.divide(lp1, lp2); + line2_no_clip(lp1, lp1.x2 + (lp1.y2 - lp1.y1), lp1.y2 - (lp1.x2 - lp1.x1)); + line2_no_clip(lp2, (lp.x2 + ex) >> 1, (lp.y2 + ey) >> 1); + return; + } + + fix_degenerate_bisectrix_end(lp, &ex, &ey); + line_interpolator_aa2 li(*this, lp, ex, ey); + if(li.vertical()) + { + while(li.step_ver()); + } + else + { + while(li.step_hor()); + } + } + + //------------------------------------------------------------------------- + void line2(const line_parameters& lp, int ex, int ey) + { + if(m_clipping) + { + int x1 = lp.x1; + int y1 = lp.y1; + int x2 = lp.x2; + int y2 = lp.y2; + unsigned flags = clip_line_segment(&x1, &y1, &x2, &y2, m_clip_box); + if((flags & 4) == 0) + { + if(flags) + { + line_parameters lp2(x1, y1, x2, y2, + uround(calc_distance(x1, y1, x2, y2))); + if(flags & 2) + { + ex = x2 + (y2 - y1); + ey = y2 - (x2 - x1); + } + else + { + while(abs(ex - lp.x2) + abs(ey - lp.y2) > lp2.len) + { + ex = (lp.x2 + ex) >> 1; + ey = (lp.y2 + ey) >> 1; + } + } + line2_no_clip(lp2, ex, ey); + } + else + { + line2_no_clip(lp, ex, ey); + } + } + } + else + { + line2_no_clip(lp, ex, ey); + } + } + + //------------------------------------------------------------------------- + void line3_no_clip(const line_parameters& lp, + int sx, int sy, int ex, int ey) + { + if(lp.len > line_max_length) + { + line_parameters lp1, lp2; + lp.divide(lp1, lp2); + int mx = lp1.x2 + (lp1.y2 - lp1.y1); + int my = lp1.y2 - (lp1.x2 - lp1.x1); + line3_no_clip(lp1, (lp.x1 + sx) >> 1, (lp.y1 + sy) >> 1, mx, my); + line3_no_clip(lp2, mx, my, (lp.x2 + ex) >> 1, (lp.y2 + ey) >> 1); + return; + } + + fix_degenerate_bisectrix_start(lp, &sx, &sy); + fix_degenerate_bisectrix_end(lp, &ex, &ey); + line_interpolator_aa3 li(*this, lp, sx, sy, ex, ey); + if(li.vertical()) + { + while(li.step_ver()); + } + else + { + while(li.step_hor()); + } + } + + //------------------------------------------------------------------------- + void line3(const line_parameters& lp, + int sx, int sy, int ex, int ey) + { + if(m_clipping) + { + int x1 = lp.x1; + int y1 = lp.y1; + int x2 = lp.x2; + int y2 = lp.y2; + unsigned flags = clip_line_segment(&x1, &y1, &x2, &y2, m_clip_box); + if((flags & 4) == 0) + { + if(flags) + { + line_parameters lp2(x1, y1, x2, y2, + uround(calc_distance(x1, y1, x2, y2))); + if(flags & 1) + { + sx = x1 + (y2 - y1); + sy = y1 - (x2 - x1); + } + else + { + while(abs(sx - lp.x1) + abs(sy - lp.y1) > lp2.len) + { + sx = (lp.x1 + sx) >> 1; + sy = (lp.y1 + sy) >> 1; + } + } + if(flags & 2) + { + ex = x2 + (y2 - y1); + ey = y2 - (x2 - x1); + } + else + { + while(abs(ex - lp.x2) + abs(ey - lp.y2) > lp2.len) + { + ex = (lp.x2 + ex) >> 1; + ey = (lp.y2 + ey) >> 1; + } + } + line3_no_clip(lp2, sx, sy, ex, ey); + } + else + { + line3_no_clip(lp, sx, sy, ex, ey); + } + } + } + else + { + line3_no_clip(lp, sx, sy, ex, ey); + } + } + + + private: + base_ren_type* m_ren; + const line_profile_aa* m_profile; + color_type m_color; + rect_i m_clip_box; + bool m_clipping; + }; + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_outline_image.h b/desmume/src/windows/agg/include/agg_renderer_outline_image.h new file mode 100644 index 000000000..cedf6e83f --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_outline_image.h @@ -0,0 +1,1023 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_OUTLINE_IMAGE_INCLUDED +#define AGG_RENDERER_OUTLINE_IMAGE_INCLUDED + +#include "agg_array.h" +#include "agg_math.h" +#include "agg_line_aa_basics.h" +#include "agg_dda_line.h" +#include "agg_rendering_buffer.h" +#include "agg_clip_liang_barsky.h" + + +namespace agg +{ + //========================================================line_image_scale + template class line_image_scale + { + public: + typedef typename Source::color_type color_type; + + line_image_scale(const Source& src, double height) : + m_source(src), + m_height(height), + m_scale(src.height() / height) + { + } + + double width() const { return m_source.width(); } + double height() const { return m_height; } + + color_type pixel(int x, int y) const + { + double src_y = (y + 0.5) * m_scale - 0.5; + int h = m_source.height() - 1; + int y1 = ufloor(src_y); + int y2 = y1 + 1; + color_type pix1 = (y1 < 0) ? color_type::no_color() : m_source.pixel(x, y1); + color_type pix2 = (y2 > h) ? color_type::no_color() : m_source.pixel(x, y2); + return pix1.gradient(pix2, src_y - y1); + } + + private: + line_image_scale(const line_image_scale&); + const line_image_scale& operator = (const line_image_scale&); + + const Source& m_source; + double m_height; + double m_scale; + }; + + + + //======================================================line_image_pattern + template class line_image_pattern + { + public: + typedef Filter filter_type; + typedef typename filter_type::color_type color_type; + + //-------------------------------------------------------------------- + line_image_pattern(const Filter& filter) : + m_filter(&filter), + m_dilation(filter.dilation() + 1), + m_dilation_hr(m_dilation << line_subpixel_shift), + m_data(), + m_width(0), + m_height(0), + m_width_hr(0), + m_half_height_hr(0), + m_offset_y_hr(0) + { + } + + // Create + //-------------------------------------------------------------------- + template + line_image_pattern(const Filter& filter, const Source& src) : + m_filter(&filter), + m_dilation(filter.dilation() + 1), + m_dilation_hr(m_dilation << line_subpixel_shift), + m_data(), + m_width(0), + m_height(0), + m_width_hr(0), + m_half_height_hr(0), + m_offset_y_hr(0) + { + create(src); + } + + // Create + //-------------------------------------------------------------------- + template void create(const Source& src) + { + m_height = uceil(src.height()); + m_width = uceil(src.width()); + m_width_hr = uround(src.width() * line_subpixel_scale); + m_half_height_hr = uround(src.height() * line_subpixel_scale/2); + m_offset_y_hr = m_dilation_hr + m_half_height_hr - line_subpixel_scale/2; + m_half_height_hr += line_subpixel_scale/2; + + m_data.resize((m_width + m_dilation * 2) * (m_height + m_dilation * 2)); + + m_buf.attach(&m_data[0], m_width + m_dilation * 2, + m_height + m_dilation * 2, + m_width + m_dilation * 2); + unsigned x, y; + color_type* d1; + color_type* d2; + for(y = 0; y < m_height; y++) + { + d1 = m_buf.row_ptr(y + m_dilation) + m_dilation; + for(x = 0; x < m_width; x++) + { + *d1++ = src.pixel(x, y); + } + } + + const color_type* s1; + const color_type* s2; + for(y = 0; y < m_dilation; y++) + { + //s1 = m_buf.row_ptr(m_height + m_dilation - 1) + m_dilation; + //s2 = m_buf.row_ptr(m_dilation) + m_dilation; + d1 = m_buf.row_ptr(m_dilation + m_height + y) + m_dilation; + d2 = m_buf.row_ptr(m_dilation - y - 1) + m_dilation; + for(x = 0; x < m_width; x++) + { + //*d1++ = color_type(*s1++, 0); + //*d2++ = color_type(*s2++, 0); + *d1++ = color_type::no_color(); + *d2++ = color_type::no_color(); + } + } + + unsigned h = m_height + m_dilation * 2; + for(y = 0; y < h; y++) + { + s1 = m_buf.row_ptr(y) + m_dilation; + s2 = m_buf.row_ptr(y) + m_dilation + m_width; + d1 = m_buf.row_ptr(y) + m_dilation + m_width; + d2 = m_buf.row_ptr(y) + m_dilation; + + for(x = 0; x < m_dilation; x++) + { + *d1++ = *s1++; + *--d2 = *--s2; + } + } + } + + //-------------------------------------------------------------------- + int pattern_width() const { return m_width_hr; } + int line_width() const { return m_half_height_hr; } + double width() const { return m_height; } + + //-------------------------------------------------------------------- + void pixel(color_type* p, int x, int y) const + { + m_filter->pixel_high_res(m_buf.rows(), + p, + x % m_width_hr + m_dilation_hr, + y + m_offset_y_hr); + } + + //-------------------------------------------------------------------- + const filter_type& filter() const { return *m_filter; } + + private: + line_image_pattern(const line_image_pattern&); + const line_image_pattern& + operator = (const line_image_pattern&); + + protected: + row_ptr_cache m_buf; + const filter_type* m_filter; + unsigned m_dilation; + int m_dilation_hr; + pod_array m_data; + unsigned m_width; + unsigned m_height; + int m_width_hr; + int m_half_height_hr; + int m_offset_y_hr; + }; + + + + + + + //=================================================line_image_pattern_pow2 + template class line_image_pattern_pow2 : + public line_image_pattern + { + public: + typedef Filter filter_type; + typedef typename filter_type::color_type color_type; + typedef line_image_pattern base_type; + + //-------------------------------------------------------------------- + line_image_pattern_pow2(const Filter& filter) : + line_image_pattern(filter), m_mask(line_subpixel_mask) {} + + //-------------------------------------------------------------------- + template + line_image_pattern_pow2(const Filter& filter, const Source& src) : + line_image_pattern(filter), m_mask(line_subpixel_mask) + { + create(src); + } + + //-------------------------------------------------------------------- + template void create(const Source& src) + { + line_image_pattern::create(src); + m_mask = 1; + while(m_mask < base_type::m_width) + { + m_mask <<= 1; + m_mask |= 1; + } + m_mask <<= line_subpixel_shift - 1; + m_mask |= line_subpixel_mask; + base_type::m_width_hr = m_mask + 1; + } + + //-------------------------------------------------------------------- + void pixel(color_type* p, int x, int y) const + { + base_type::m_filter->pixel_high_res( + base_type::m_buf.rows(), + p, + (x & m_mask) + base_type::m_dilation_hr, + y + base_type::m_offset_y_hr); + } + private: + unsigned m_mask; + }; + + + + + + + + //===================================================distance_interpolator4 + class distance_interpolator4 + { + public: + //--------------------------------------------------------------------- + distance_interpolator4() {} + distance_interpolator4(int x1, int y1, int x2, int y2, + int sx, int sy, int ex, int ey, + int len, double scale, int x, int y) : + m_dx(x2 - x1), + m_dy(y2 - y1), + m_dx_start(line_mr(sx) - line_mr(x1)), + m_dy_start(line_mr(sy) - line_mr(y1)), + m_dx_end(line_mr(ex) - line_mr(x2)), + m_dy_end(line_mr(ey) - line_mr(y2)), + + m_dist(iround(double(x + line_subpixel_scale/2 - x2) * double(m_dy) - + double(y + line_subpixel_scale/2 - y2) * double(m_dx))), + + m_dist_start((line_mr(x + line_subpixel_scale/2) - line_mr(sx)) * m_dy_start - + (line_mr(y + line_subpixel_scale/2) - line_mr(sy)) * m_dx_start), + + m_dist_end((line_mr(x + line_subpixel_scale/2) - line_mr(ex)) * m_dy_end - + (line_mr(y + line_subpixel_scale/2) - line_mr(ey)) * m_dx_end), + m_len(uround(len / scale)) + { + double d = len * scale; + int dx = iround(((x2 - x1) << line_subpixel_shift) / d); + int dy = iround(((y2 - y1) << line_subpixel_shift) / d); + m_dx_pict = -dy; + m_dy_pict = dx; + m_dist_pict = ((x + line_subpixel_scale/2 - (x1 - dy)) * m_dy_pict - + (y + line_subpixel_scale/2 - (y1 + dx)) * m_dx_pict) >> + line_subpixel_shift; + + m_dx <<= line_subpixel_shift; + m_dy <<= line_subpixel_shift; + m_dx_start <<= line_mr_subpixel_shift; + m_dy_start <<= line_mr_subpixel_shift; + m_dx_end <<= line_mr_subpixel_shift; + m_dy_end <<= line_mr_subpixel_shift; + } + + //--------------------------------------------------------------------- + void inc_x() + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_pict += m_dy_pict; + m_dist_end += m_dy_end; + } + + //--------------------------------------------------------------------- + void dec_x() + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_pict -= m_dy_pict; + m_dist_end -= m_dy_end; + } + + //--------------------------------------------------------------------- + void inc_y() + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_pict -= m_dx_pict; + m_dist_end -= m_dx_end; + } + + //--------------------------------------------------------------------- + void dec_y() + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_pict += m_dx_pict; + m_dist_end += m_dx_end; + } + + //--------------------------------------------------------------------- + void inc_x(int dy) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_pict += m_dy_pict; + m_dist_end += m_dy_end; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_pict -= m_dx_pict; + m_dist_end -= m_dx_end; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_pict += m_dx_pict; + m_dist_end += m_dx_end; + } + } + + //--------------------------------------------------------------------- + void dec_x(int dy) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_pict -= m_dy_pict; + m_dist_end -= m_dy_end; + if(dy > 0) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_pict -= m_dx_pict; + m_dist_end -= m_dx_end; + } + if(dy < 0) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_pict += m_dx_pict; + m_dist_end += m_dx_end; + } + } + + //--------------------------------------------------------------------- + void inc_y(int dx) + { + m_dist -= m_dx; + m_dist_start -= m_dx_start; + m_dist_pict -= m_dx_pict; + m_dist_end -= m_dx_end; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_pict += m_dy_pict; + m_dist_end += m_dy_end; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_pict -= m_dy_pict; + m_dist_end -= m_dy_end; + } + } + + //--------------------------------------------------------------------- + void dec_y(int dx) + { + m_dist += m_dx; + m_dist_start += m_dx_start; + m_dist_pict += m_dx_pict; + m_dist_end += m_dx_end; + if(dx > 0) + { + m_dist += m_dy; + m_dist_start += m_dy_start; + m_dist_pict += m_dy_pict; + m_dist_end += m_dy_end; + } + if(dx < 0) + { + m_dist -= m_dy; + m_dist_start -= m_dy_start; + m_dist_pict -= m_dy_pict; + m_dist_end -= m_dy_end; + } + } + + //--------------------------------------------------------------------- + int dist() const { return m_dist; } + int dist_start() const { return m_dist_start; } + int dist_pict() const { return m_dist_pict; } + int dist_end() const { return m_dist_end; } + + //--------------------------------------------------------------------- + int dx() const { return m_dx; } + int dy() const { return m_dy; } + int dx_start() const { return m_dx_start; } + int dy_start() const { return m_dy_start; } + int dx_pict() const { return m_dx_pict; } + int dy_pict() const { return m_dy_pict; } + int dx_end() const { return m_dx_end; } + int dy_end() const { return m_dy_end; } + int len() const { return m_len; } + + private: + //--------------------------------------------------------------------- + int m_dx; + int m_dy; + int m_dx_start; + int m_dy_start; + int m_dx_pict; + int m_dy_pict; + int m_dx_end; + int m_dy_end; + + int m_dist; + int m_dist_start; + int m_dist_pict; + int m_dist_end; + int m_len; + }; + + + + + + //==================================================line_interpolator_image + template class line_interpolator_image + { + public: + typedef Renderer renderer_type; + typedef typename Renderer::color_type color_type; + + //--------------------------------------------------------------------- + enum max_half_width_e + { + max_half_width = 64 + }; + + //--------------------------------------------------------------------- + line_interpolator_image(renderer_type& ren, const line_parameters& lp, + int sx, int sy, int ex, int ey, + int pattern_start, + double scale_x) : + m_lp(lp), + m_li(lp.vertical ? line_dbl_hr(lp.x2 - lp.x1) : + line_dbl_hr(lp.y2 - lp.y1), + lp.vertical ? abs(lp.y2 - lp.y1) : + abs(lp.x2 - lp.x1) + 1), + m_di(lp.x1, lp.y1, lp.x2, lp.y2, sx, sy, ex, ey, lp.len, scale_x, + lp.x1 & ~line_subpixel_mask, lp.y1 & ~line_subpixel_mask), + m_ren(ren), + m_x(lp.x1 >> line_subpixel_shift), + m_y(lp.y1 >> line_subpixel_shift), + m_old_x(m_x), + m_old_y(m_y), + m_count((lp.vertical ? abs((lp.y2 >> line_subpixel_shift) - m_y) : + abs((lp.x2 >> line_subpixel_shift) - m_x))), + m_width(ren.subpixel_width()), + //m_max_extent(m_width >> (line_subpixel_shift - 2)), + m_max_extent((m_width + line_subpixel_scale) >> line_subpixel_shift), + m_start(pattern_start + (m_max_extent + 2) * ren.pattern_width()), + m_step(0) + { + agg::dda2_line_interpolator li(0, lp.vertical ? + (lp.dy << agg::line_subpixel_shift) : + (lp.dx << agg::line_subpixel_shift), + lp.len); + + unsigned i; + int stop = m_width + line_subpixel_scale * 2; + for(i = 0; i < max_half_width; ++i) + { + m_dist_pos[i] = li.y(); + if(m_dist_pos[i] >= stop) break; + ++li; + } + m_dist_pos[i] = 0x7FFF0000; + + int dist1_start; + int dist2_start; + int npix = 1; + + if(lp.vertical) + { + do + { + --m_li; + m_y -= lp.inc; + m_x = (m_lp.x1 + m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_y(m_x - m_old_x); + else m_di.inc_y(m_x - m_old_x); + + m_old_x = m_x; + + dist1_start = dist2_start = m_di.dist_start(); + + int dx = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start += m_di.dy_start(); + dist2_start -= m_di.dy_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dx; + } + while(m_dist_pos[dx] <= m_width); + if(npix == 0) break; + + npix = 0; + } + while(--m_step >= -m_max_extent); + } + else + { + do + { + --m_li; + + m_x -= lp.inc; + m_y = (m_lp.y1 + m_li.y()) >> line_subpixel_shift; + + if(lp.inc > 0) m_di.dec_x(m_y - m_old_y); + else m_di.inc_x(m_y - m_old_y); + + m_old_y = m_y; + + dist1_start = dist2_start = m_di.dist_start(); + + int dy = 0; + if(dist1_start < 0) ++npix; + do + { + dist1_start -= m_di.dx_start(); + dist2_start += m_di.dx_start(); + if(dist1_start < 0) ++npix; + if(dist2_start < 0) ++npix; + ++dy; + } + while(m_dist_pos[dy] <= m_width); + if(npix == 0) break; + + npix = 0; + } + while(--m_step >= -m_max_extent); + } + m_li.adjust_forward(); + m_step -= m_max_extent; + } + + //--------------------------------------------------------------------- + bool step_hor() + { + ++m_li; + m_x += m_lp.inc; + m_y = (m_lp.y1 + m_li.y()) >> line_subpixel_shift; + + if(m_lp.inc > 0) m_di.inc_x(m_y - m_old_y); + else m_di.dec_x(m_y - m_old_y); + + m_old_y = m_y; + + int s1 = m_di.dist() / m_lp.len; + int s2 = -s1; + + if(m_lp.inc < 0) s1 = -s1; + + int dist_start; + int dist_pict; + int dist_end; + int dy; + int dist; + + dist_start = m_di.dist_start(); + dist_pict = m_di.dist_pict() + m_start; + dist_end = m_di.dist_end(); + color_type* p0 = m_colors + max_half_width + 2; + color_type* p1 = p0; + + int npix = 0; + p1->clear(); + if(dist_end > 0) + { + if(dist_start <= 0) + { + m_ren.pixel(p1, dist_pict, s2); + } + ++npix; + } + ++p1; + + dy = 1; + while((dist = m_dist_pos[dy]) - s1 <= m_width) + { + dist_start -= m_di.dx_start(); + dist_pict -= m_di.dx_pict(); + dist_end -= m_di.dx_end(); + p1->clear(); + if(dist_end > 0 && dist_start <= 0) + { + if(m_lp.inc > 0) dist = -dist; + m_ren.pixel(p1, dist_pict, s2 - dist); + ++npix; + } + ++p1; + ++dy; + } + + dy = 1; + dist_start = m_di.dist_start(); + dist_pict = m_di.dist_pict() + m_start; + dist_end = m_di.dist_end(); + while((dist = m_dist_pos[dy]) + s1 <= m_width) + { + dist_start += m_di.dx_start(); + dist_pict += m_di.dx_pict(); + dist_end += m_di.dx_end(); + --p0; + p0->clear(); + if(dist_end > 0 && dist_start <= 0) + { + if(m_lp.inc > 0) dist = -dist; + m_ren.pixel(p0, dist_pict, s2 + dist); + ++npix; + } + ++dy; + } + m_ren.blend_color_vspan(m_x, + m_y - dy + 1, + unsigned(p1 - p0), + p0); + return npix && ++m_step < m_count; + } + + + + //--------------------------------------------------------------------- + bool step_ver() + { + ++m_li; + m_y += m_lp.inc; + m_x = (m_lp.x1 + m_li.y()) >> line_subpixel_shift; + + if(m_lp.inc > 0) m_di.inc_y(m_x - m_old_x); + else m_di.dec_y(m_x - m_old_x); + + m_old_x = m_x; + + int s1 = m_di.dist() / m_lp.len; + int s2 = -s1; + + if(m_lp.inc > 0) s1 = -s1; + + int dist_start; + int dist_pict; + int dist_end; + int dist; + int dx; + + dist_start = m_di.dist_start(); + dist_pict = m_di.dist_pict() + m_start; + dist_end = m_di.dist_end(); + color_type* p0 = m_colors + max_half_width + 2; + color_type* p1 = p0; + + int npix = 0; + p1->clear(); + if(dist_end > 0) + { + if(dist_start <= 0) + { + m_ren.pixel(p1, dist_pict, s2); + } + ++npix; + } + ++p1; + + dx = 1; + while((dist = m_dist_pos[dx]) - s1 <= m_width) + { + dist_start += m_di.dy_start(); + dist_pict += m_di.dy_pict(); + dist_end += m_di.dy_end(); + p1->clear(); + if(dist_end > 0 && dist_start <= 0) + { + if(m_lp.inc > 0) dist = -dist; + m_ren.pixel(p1, dist_pict, s2 + dist); + ++npix; + } + ++p1; + ++dx; + } + + dx = 1; + dist_start = m_di.dist_start(); + dist_pict = m_di.dist_pict() + m_start; + dist_end = m_di.dist_end(); + while((dist = m_dist_pos[dx]) + s1 <= m_width) + { + dist_start -= m_di.dy_start(); + dist_pict -= m_di.dy_pict(); + dist_end -= m_di.dy_end(); + --p0; + p0->clear(); + if(dist_end > 0 && dist_start <= 0) + { + if(m_lp.inc > 0) dist = -dist; + m_ren.pixel(p0, dist_pict, s2 - dist); + ++npix; + } + ++dx; + } + m_ren.blend_color_hspan(m_x - dx + 1, + m_y, + unsigned(p1 - p0), + p0); + return npix && ++m_step < m_count; + } + + + //--------------------------------------------------------------------- + int pattern_end() const { return m_start + m_di.len(); } + + //--------------------------------------------------------------------- + bool vertical() const { return m_lp.vertical; } + int width() const { return m_width; } + int count() const { return m_count; } + + private: + line_interpolator_image(const line_interpolator_image&); + const line_interpolator_image& + operator = (const line_interpolator_image&); + + protected: + const line_parameters& m_lp; + dda2_line_interpolator m_li; + distance_interpolator4 m_di; + renderer_type& m_ren; + int m_plen; + int m_x; + int m_y; + int m_old_x; + int m_old_y; + int m_count; + int m_width; + int m_max_extent; + int m_start; + int m_step; + int m_dist_pos[max_half_width + 1]; + color_type m_colors[max_half_width * 2 + 4]; + }; + + + + + + + + + //===================================================renderer_outline_image + template + class renderer_outline_image + { + public: + //--------------------------------------------------------------------- + typedef BaseRenderer base_ren_type; + typedef renderer_outline_image self_type; + typedef typename base_ren_type::color_type color_type; + typedef ImagePattern pattern_type; + + + //--------------------------------------------------------------------- + renderer_outline_image(base_ren_type& ren, const pattern_type& patt) : + m_ren(&ren), + m_pattern(&patt), + m_start(0), + m_scale_x(1.0), + m_clip_box(0,0,0,0), + m_clipping(false) + {} + void attach(base_ren_type& ren) { m_ren = &ren; } + + //--------------------------------------------------------------------- + void pattern(const pattern_type& p) { m_pattern = &p; } + const pattern_type& pattern() const { return *m_pattern; } + + //--------------------------------------------------------------------- + void reset_clipping() { m_clipping = false; } + void clip_box(double x1, double y1, double x2, double y2) + { + m_clip_box.x1 = line_coord_sat::conv(x1); + m_clip_box.y1 = line_coord_sat::conv(y1); + m_clip_box.x2 = line_coord_sat::conv(x2); + m_clip_box.y2 = line_coord_sat::conv(y2); + m_clipping = true; + } + + //--------------------------------------------------------------------- + void scale_x(double s) { m_scale_x = s; } + double scale_x() const { return m_scale_x; } + + //--------------------------------------------------------------------- + void start_x(double s) { m_start = iround(s * line_subpixel_scale); } + double start_x() const { return double(m_start) / line_subpixel_scale; } + + //--------------------------------------------------------------------- + int subpixel_width() const { return m_pattern->line_width(); } + int pattern_width() const { return m_pattern->pattern_width(); } + double width() const { return double(subpixel_width()) / line_subpixel_scale; } + + //------------------------------------------------------------------------- + void pixel(color_type* p, int x, int y) const + { + m_pattern->pixel(p, x, y); + } + + //------------------------------------------------------------------------- + void blend_color_hspan(int x, int y, unsigned len, const color_type* colors) + { + m_ren->blend_color_hspan(x, y, len, colors, 0); + } + + //------------------------------------------------------------------------- + void blend_color_vspan(int x, int y, unsigned len, const color_type* colors) + { + m_ren->blend_color_vspan(x, y, len, colors, 0); + } + + //------------------------------------------------------------------------- + static bool accurate_join_only() { return true; } + + //------------------------------------------------------------------------- + template + void semidot(Cmp, int, int, int, int) + { + } + + //------------------------------------------------------------------------- + void pie(int, int, int, int, int, int) + { + } + + //------------------------------------------------------------------------- + void line0(const line_parameters&) + { + } + + //------------------------------------------------------------------------- + void line1(const line_parameters&, int, int) + { + } + + //------------------------------------------------------------------------- + void line2(const line_parameters&, int, int) + { + } + + //------------------------------------------------------------------------- + void line3_no_clip(const line_parameters& lp, + int sx, int sy, int ex, int ey) + { + if(lp.len > line_max_length) + { + line_parameters lp1, lp2; + lp.divide(lp1, lp2); + int mx = lp1.x2 + (lp1.y2 - lp1.y1); + int my = lp1.y2 - (lp1.x2 - lp1.x1); + line3_no_clip(lp1, (lp.x1 + sx) >> 1, (lp.y1 + sy) >> 1, mx, my); + line3_no_clip(lp2, mx, my, (lp.x2 + ex) >> 1, (lp.y2 + ey) >> 1); + return; + } + + fix_degenerate_bisectrix_start(lp, &sx, &sy); + fix_degenerate_bisectrix_end(lp, &ex, &ey); + line_interpolator_image li(*this, lp, + sx, sy, + ex, ey, + m_start, m_scale_x); + if(li.vertical()) + { + while(li.step_ver()); + } + else + { + while(li.step_hor()); + } + m_start += uround(lp.len / m_scale_x); + } + + //------------------------------------------------------------------------- + void line3(const line_parameters& lp, + int sx, int sy, int ex, int ey) + { + if(m_clipping) + { + int x1 = lp.x1; + int y1 = lp.y1; + int x2 = lp.x2; + int y2 = lp.y2; + unsigned flags = clip_line_segment(&x1, &y1, &x2, &y2, m_clip_box); + int start = m_start; + if((flags & 4) == 0) + { + if(flags) + { + line_parameters lp2(x1, y1, x2, y2, + uround(calc_distance(x1, y1, x2, y2))); + if(flags & 1) + { + m_start += uround(calc_distance(lp.x1, lp.y1, x1, y1) / m_scale_x); + sx = x1 + (y2 - y1); + sy = y1 - (x2 - x1); + } + else + { + while(abs(sx - lp.x1) + abs(sy - lp.y1) > lp2.len) + { + sx = (lp.x1 + sx) >> 1; + sy = (lp.y1 + sy) >> 1; + } + } + if(flags & 2) + { + ex = x2 + (y2 - y1); + ey = y2 - (x2 - x1); + } + else + { + while(abs(ex - lp.x2) + abs(ey - lp.y2) > lp2.len) + { + ex = (lp.x2 + ex) >> 1; + ey = (lp.y2 + ey) >> 1; + } + } + line3_no_clip(lp2, sx, sy, ex, ey); + } + else + { + line3_no_clip(lp, sx, sy, ex, ey); + } + } + m_start = start + uround(lp.len / m_scale_x); + } + else + { + line3_no_clip(lp, sx, sy, ex, ey); + } + } + + private: + base_ren_type* m_ren; + const pattern_type* m_pattern; + int m_start; + double m_scale_x; + rect_i m_clip_box; + bool m_clipping; + }; + + + + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_primitives.h b/desmume/src/windows/agg/include/agg_renderer_primitives.h new file mode 100644 index 000000000..d999f8b56 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_primitives.h @@ -0,0 +1,229 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_PRIMITIVES_INCLUDED +#define AGG_RENDERER_PRIMITIVES_INCLUDED + +#include "agg_basics.h" +#include "agg_renderer_base.h" +#include "agg_dda_line.h" +#include "agg_ellipse_bresenham.h" + +namespace agg +{ + //-----------------------------------------------------renderer_primitives + template class renderer_primitives + { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + explicit renderer_primitives(base_ren_type& ren) : + m_ren(&ren), + m_fill_color(), + m_line_color(), + m_curr_x(0), + m_curr_y(0) + {} + void attach(base_ren_type& ren) { m_ren = &ren; } + + //-------------------------------------------------------------------- + static int coord(double c) + { + return iround(c * line_bresenham_interpolator::subpixel_scale); + } + + //-------------------------------------------------------------------- + void fill_color(const color_type& c) { m_fill_color = c; } + void line_color(const color_type& c) { m_line_color = c; } + const color_type& fill_color() const { return m_fill_color; } + const color_type& line_color() const { return m_line_color; } + + //-------------------------------------------------------------------- + void rectangle(int x1, int y1, int x2, int y2) + { + m_ren->blend_hline(x1, y1, x2-1, m_line_color, cover_full); + m_ren->blend_vline(x2, y1, y2-1, m_line_color, cover_full); + m_ren->blend_hline(x1+1, y2, x2, m_line_color, cover_full); + m_ren->blend_vline(x1, y1+1, y2, m_line_color, cover_full); + } + + //-------------------------------------------------------------------- + void solid_rectangle(int x1, int y1, int x2, int y2) + { + m_ren->blend_bar(x1, y1, x2, y2, m_fill_color, cover_full); + } + + //-------------------------------------------------------------------- + void outlined_rectangle(int x1, int y1, int x2, int y2) + { + rectangle(x1, y1, x2, y2); + m_ren->blend_bar(x1+1, y1+1, x2-1, y2-1, m_fill_color, cover_full); + } + + //-------------------------------------------------------------------- + void ellipse(int x, int y, int rx, int ry) + { + ellipse_bresenham_interpolator ei(rx, ry); + int dx = 0; + int dy = -ry; + do + { + dx += ei.dx(); + dy += ei.dy(); + m_ren->blend_pixel(x + dx, y + dy, m_line_color, cover_full); + m_ren->blend_pixel(x + dx, y - dy, m_line_color, cover_full); + m_ren->blend_pixel(x - dx, y - dy, m_line_color, cover_full); + m_ren->blend_pixel(x - dx, y + dy, m_line_color, cover_full); + ++ei; + } + while(dy < 0); + } + + //-------------------------------------------------------------------- + void solid_ellipse(int x, int y, int rx, int ry) + { + ellipse_bresenham_interpolator ei(rx, ry); + int dx = 0; + int dy = -ry; + int dy0 = dy; + int dx0 = dx; + + do + { + dx += ei.dx(); + dy += ei.dy(); + + if(dy != dy0) + { + m_ren->blend_hline(x-dx0, y+dy0, x+dx0, m_fill_color, cover_full); + m_ren->blend_hline(x-dx0, y-dy0, x+dx0, m_fill_color, cover_full); + } + dx0 = dx; + dy0 = dy; + ++ei; + } + while(dy < 0); + m_ren->blend_hline(x-dx0, y+dy0, x+dx0, m_fill_color, cover_full); + } + + //-------------------------------------------------------------------- + void outlined_ellipse(int x, int y, int rx, int ry) + { + ellipse_bresenham_interpolator ei(rx, ry); + int dx = 0; + int dy = -ry; + + do + { + dx += ei.dx(); + dy += ei.dy(); + + m_ren->blend_pixel(x + dx, y + dy, m_line_color, cover_full); + m_ren->blend_pixel(x + dx, y - dy, m_line_color, cover_full); + m_ren->blend_pixel(x - dx, y - dy, m_line_color, cover_full); + m_ren->blend_pixel(x - dx, y + dy, m_line_color, cover_full); + + if(ei.dy() && dx) + { + m_ren->blend_hline(x-dx+1, y+dy, x+dx-1, m_fill_color, cover_full); + m_ren->blend_hline(x-dx+1, y-dy, x+dx-1, m_fill_color, cover_full); + } + ++ei; + } + while(dy < 0); + } + + //-------------------------------------------------------------------- + void line(int x1, int y1, int x2, int y2, bool last=false) + { + line_bresenham_interpolator li(x1, y1, x2, y2); + + unsigned len = li.len(); + if(len == 0) + { + if(last) + { + m_ren->blend_pixel(li.line_lr(x1), li.line_lr(y1), m_line_color, cover_full); + } + return; + } + + if(last) ++len; + + if(li.is_ver()) + { + do + { + m_ren->blend_pixel(li.x2(), li.y1(), m_line_color, cover_full); + li.vstep(); + } + while(--len); + } + else + { + do + { + m_ren->blend_pixel(li.x1(), li.y2(), m_line_color, cover_full); + li.hstep(); + } + while(--len); + } + } + + //-------------------------------------------------------------------- + void move_to(int x, int y) + { + m_curr_x = x; + m_curr_y = y; + } + + //-------------------------------------------------------------------- + void line_to(int x, int y, bool last=false) + { + line(m_curr_x, m_curr_y, x, y, last); + m_curr_x = x; + m_curr_y = y; + } + + //-------------------------------------------------------------------- + const base_ren_type& ren() const { return *m_ren; } + base_ren_type& ren() { return *m_ren; } + + //-------------------------------------------------------------------- + const rendering_buffer& rbuf() const { return m_ren->rbuf(); } + rendering_buffer& rbuf() { return m_ren->rbuf(); } + + private: + base_ren_type* m_ren; + color_type m_fill_color; + color_type m_line_color; + int m_curr_x; + int m_curr_y; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_renderer_raster_text.h b/desmume/src/windows/agg/include/agg_renderer_raster_text.h new file mode 100644 index 000000000..304554250 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_raster_text.h @@ -0,0 +1,273 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_RASTER_TEXT_INCLUDED +#define AGG_RENDERER_RASTER_TEXT_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //==============================================renderer_raster_htext_solid + template + class renderer_raster_htext_solid + { + public: + typedef BaseRenderer ren_type; + typedef GlyphGenerator glyph_gen_type; + typedef typename glyph_gen_type::glyph_rect glyph_rect; + typedef typename ren_type::color_type color_type; + + renderer_raster_htext_solid(ren_type& ren, glyph_gen_type& glyph) : + m_ren(&ren), + m_glyph(&glyph) + {} + void attach(ren_type& ren) { m_ren = &ren; } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + template + void render_text(double x, double y, const CharT* str, bool flip=false) + { + glyph_rect r; + while(*str) + { + m_glyph->prepare(&r, x, y, *str, flip); + if(r.x2 >= r.x1) + { + int i; + if(flip) + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->blend_solid_hspan(r.x1, i, (r.x2 - r.x1 + 1), + m_color, + m_glyph->span(r.y2 - i)); + } + } + else + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->blend_solid_hspan(r.x1, i, (r.x2 - r.x1 + 1), + m_color, + m_glyph->span(i - r.y1)); + } + } + } + x += r.dx; + y += r.dy; + ++str; + } + } + + private: + ren_type* m_ren; + glyph_gen_type* m_glyph; + color_type m_color; + }; + + + + //=============================================renderer_raster_vtext_solid + template + class renderer_raster_vtext_solid + { + public: + typedef BaseRenderer ren_type; + typedef GlyphGenerator glyph_gen_type; + typedef typename glyph_gen_type::glyph_rect glyph_rect; + typedef typename ren_type::color_type color_type; + + renderer_raster_vtext_solid(ren_type& ren, glyph_gen_type& glyph) : + m_ren(&ren), + m_glyph(&glyph) + { + } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + template + void render_text(double x, double y, const CharT* str, bool flip=false) + { + glyph_rect r; + while(*str) + { + m_glyph->prepare(&r, x, y, *str, !flip); + if(r.x2 >= r.x1) + { + int i; + if(flip) + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->blend_solid_vspan(i, r.x1, (r.x2 - r.x1 + 1), + m_color, + m_glyph->span(i - r.y1)); + } + } + else + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->blend_solid_vspan(i, r.x1, (r.x2 - r.x1 + 1), + m_color, + m_glyph->span(r.y2 - i)); + } + } + } + x += r.dx; + y += r.dy; + ++str; + } + } + + private: + ren_type* m_ren; + glyph_gen_type* m_glyph; + color_type m_color; + }; + + + + + + + //===================================================renderer_raster_htext + template + class renderer_raster_htext + { + public: + typedef ScanlineRenderer ren_type; + typedef GlyphGenerator glyph_gen_type; + typedef typename glyph_gen_type::glyph_rect glyph_rect; + + class scanline_single_span + { + public: + typedef agg::cover_type cover_type; + + //---------------------------------------------------------------- + struct const_span + { + int x; + unsigned len; + const cover_type* covers; + + const_span() {} + const_span(int x_, unsigned len_, const cover_type* covers_) : + x(x_), len(len_), covers(covers_) + {} + }; + + typedef const const_span* const_iterator; + + //---------------------------------------------------------------- + scanline_single_span(int x, int y, unsigned len, + const cover_type* covers) : + m_y(y), + m_span(x, len, covers) + {} + + //---------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return 1; } + const_iterator begin() const { return &m_span; } + + private: + //---------------------------------------------------------------- + int m_y; + const_span m_span; + }; + + + + //-------------------------------------------------------------------- + renderer_raster_htext(ren_type& ren, glyph_gen_type& glyph) : + m_ren(&ren), + m_glyph(&glyph) + { + } + + + //-------------------------------------------------------------------- + template + void render_text(double x, double y, const CharT* str, bool flip=false) + { + glyph_rect r; + while(*str) + { + m_glyph->prepare(&r, x, y, *str, flip); + if(r.x2 >= r.x1) + { + m_ren->prepare(); + int i; + if(flip) + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->render( + scanline_single_span(r.x1, + i, + (r.x2 - r.x1 + 1), + m_glyph->span(r.y2 - i))); + } + } + else + { + for(i = r.y1; i <= r.y2; i++) + { + m_ren->render( + scanline_single_span(r.x1, + i, + (r.x2 - r.x1 + 1), + m_glyph->span(i - r.y1))); + } + } + } + x += r.dx; + y += r.dy; + ++str; + } + } + + private: + ren_type* m_ren; + glyph_gen_type* m_glyph; + }; + + + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_renderer_scanline.h b/desmume/src/windows/agg/include/agg_renderer_scanline.h new file mode 100644 index 000000000..c0b6df6f8 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_renderer_scanline.h @@ -0,0 +1,861 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERER_SCANLINE_INCLUDED +#define AGG_RENDERER_SCANLINE_INCLUDED + +#include "agg_basics.h" +#include "agg_renderer_base.h" + +namespace agg +{ + + //================================================render_scanline_aa_solid + template + void render_scanline_aa_solid(const Scanline& sl, + BaseRenderer& ren, + const ColorT& color) + { + int y = sl.y(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + + for(;;) + { + int x = span->x; + if(span->len > 0) + { + ren.blend_solid_hspan(x, y, (unsigned)span->len, + color, + span->covers); + } + else + { + ren.blend_hline(x, y, (unsigned)(x - span->len - 1), + color, + *(span->covers)); + } + if(--num_spans == 0) break; + ++span; + } + } + + //===============================================render_scanlines_aa_solid + template + void render_scanlines_aa_solid(Rasterizer& ras, Scanline& sl, + BaseRenderer& ren, const ColorT& color) + { + if(ras.rewind_scanlines()) + { + // Explicitly convert "color" to the BaseRenderer color type. + // For example, it can be called with color type "rgba", while + // "rgba8" is needed. Otherwise it will be implicitly + // converted in the loop many times. + //---------------------- + typename BaseRenderer::color_type ren_color(color); + + sl.reset(ras.min_x(), ras.max_x()); + while(ras.sweep_scanline(sl)) + { + //render_scanline_aa_solid(sl, ren, ren_color); + + // This code is equivalent to the above call (copy/paste). + // It's just a "manual" optimization for old compilers, + // like Microsoft Visual C++ v6.0 + //------------------------------- + int y = sl.y(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + + for(;;) + { + int x = span->x; + if(span->len > 0) + { + ren.blend_solid_hspan(x, y, (unsigned)span->len, + ren_color, + span->covers); + } + else + { + ren.blend_hline(x, y, (unsigned)(x - span->len - 1), + ren_color, + *(span->covers)); + } + if(--num_spans == 0) break; + ++span; + } + } + } + } + + //==============================================renderer_scanline_aa_solid + template class renderer_scanline_aa_solid + { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + renderer_scanline_aa_solid() : m_ren(0) {} + explicit renderer_scanline_aa_solid(base_ren_type& ren) : m_ren(&ren) {} + void attach(base_ren_type& ren) + { + m_ren = &ren; + } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_aa_solid(sl, *m_ren, m_color); + } + + private: + base_ren_type* m_ren; + color_type m_color; + }; + + + + + + + + + + + + + + //======================================================render_scanline_aa + template + void render_scanline_aa(const Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + int y = sl.y(); + + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + int x = span->x; + int len = span->len; + const typename Scanline::cover_type* covers = span->covers; + + if(len < 0) len = -len; + typename BaseRenderer::color_type* colors = alloc.allocate(len); + span_gen.generate(colors, x, y, len); + ren.blend_color_hspan(x, y, len, colors, + (span->len < 0) ? 0 : covers, *covers); + + if(--num_spans == 0) break; + ++span; + } + } + + //=====================================================render_scanlines_aa + template + void render_scanlines_aa(Rasterizer& ras, Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + span_gen.prepare(); + while(ras.sweep_scanline(sl)) + { + render_scanline_aa(sl, ren, alloc, span_gen); + } + } + } + + //====================================================renderer_scanline_aa + template + class renderer_scanline_aa + { + public: + typedef BaseRenderer base_ren_type; + typedef SpanAllocator alloc_type; + typedef SpanGenerator span_gen_type; + + //-------------------------------------------------------------------- + renderer_scanline_aa() : m_ren(0), m_alloc(0), m_span_gen(0) {} + renderer_scanline_aa(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) : + m_ren(&ren), + m_alloc(&alloc), + m_span_gen(&span_gen) + {} + void attach(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) + { + m_ren = &ren; + m_alloc = &alloc; + m_span_gen = &span_gen; + } + + //-------------------------------------------------------------------- + void prepare() { m_span_gen->prepare(); } + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_aa(sl, *m_ren, *m_alloc, *m_span_gen); + } + + private: + base_ren_type* m_ren; + alloc_type* m_alloc; + span_gen_type* m_span_gen; + }; + + + + + + + //===============================================render_scanline_bin_solid + template + void render_scanline_bin_solid(const Scanline& sl, + BaseRenderer& ren, + const ColorT& color) + { + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + ren.blend_hline(span->x, + sl.y(), + span->x - 1 + ((span->len < 0) ? + -span->len : + span->len), + color, + cover_full); + if(--num_spans == 0) break; + ++span; + } + } + + //==============================================render_scanlines_bin_solid + template + void render_scanlines_bin_solid(Rasterizer& ras, Scanline& sl, + BaseRenderer& ren, const ColorT& color) + { + if(ras.rewind_scanlines()) + { + // Explicitly convert "color" to the BaseRenderer color type. + // For example, it can be called with color type "rgba", while + // "rgba8" is needed. Otherwise it will be implicitly + // converted in the loop many times. + //---------------------- + typename BaseRenderer::color_type ren_color(color); + + sl.reset(ras.min_x(), ras.max_x()); + while(ras.sweep_scanline(sl)) + { + //render_scanline_bin_solid(sl, ren, ren_color); + + // This code is equivalent to the above call (copy/paste). + // It's just a "manual" optimization for old compilers, + // like Microsoft Visual C++ v6.0 + //------------------------------- + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + ren.blend_hline(span->x, + sl.y(), + span->x - 1 + ((span->len < 0) ? + -span->len : + span->len), + ren_color, + cover_full); + if(--num_spans == 0) break; + ++span; + } + } + } + } + + //=============================================renderer_scanline_bin_solid + template class renderer_scanline_bin_solid + { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; + + //-------------------------------------------------------------------- + renderer_scanline_bin_solid() : m_ren(0) {} + explicit renderer_scanline_bin_solid(base_ren_type& ren) : m_ren(&ren) {} + void attach(base_ren_type& ren) + { + m_ren = &ren; + } + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_bin_solid(sl, *m_ren, m_color); + } + + private: + base_ren_type* m_ren; + color_type m_color; + }; + + + + + + + + + //======================================================render_scanline_bin + template + void render_scanline_bin(const Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + int y = sl.y(); + + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); + for(;;) + { + int x = span->x; + int len = span->len; + if(len < 0) len = -len; + typename BaseRenderer::color_type* colors = alloc.allocate(len); + span_gen.generate(colors, x, y, len); + ren.blend_color_hspan(x, y, len, colors, 0, cover_full); + if(--num_spans == 0) break; + ++span; + } + } + + //=====================================================render_scanlines_bin + template + void render_scanlines_bin(Rasterizer& ras, Scanline& sl, BaseRenderer& ren, + SpanAllocator& alloc, SpanGenerator& span_gen) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + span_gen.prepare(); + while(ras.sweep_scanline(sl)) + { + render_scanline_bin(sl, ren, alloc, span_gen); + } + } + } + + //====================================================renderer_scanline_bin + template + class renderer_scanline_bin + { + public: + typedef BaseRenderer base_ren_type; + typedef SpanAllocator alloc_type; + typedef SpanGenerator span_gen_type; + + //-------------------------------------------------------------------- + renderer_scanline_bin() : m_ren(0), m_alloc(0), m_span_gen(0) {} + renderer_scanline_bin(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) : + m_ren(&ren), + m_alloc(&alloc), + m_span_gen(&span_gen) + {} + void attach(base_ren_type& ren, + alloc_type& alloc, + span_gen_type& span_gen) + { + m_ren = &ren; + m_alloc = &alloc; + m_span_gen = &span_gen; + } + + //-------------------------------------------------------------------- + void prepare() { m_span_gen->prepare(); } + + //-------------------------------------------------------------------- + template void render(const Scanline& sl) + { + render_scanline_bin(sl, *m_ren, *m_alloc, *m_span_gen); + } + + private: + base_ren_type* m_ren; + alloc_type* m_alloc; + span_gen_type* m_span_gen; + }; + + + + + + + + + + + //========================================================render_scanlines + template + void render_scanlines(Rasterizer& ras, Scanline& sl, Renderer& ren) + { + if(ras.rewind_scanlines()) + { + sl.reset(ras.min_x(), ras.max_x()); + ren.prepare(); + while(ras.sweep_scanline(sl)) + { + ren.render(sl); + } + } + } + + //========================================================render_all_paths + template + void render_all_paths(Rasterizer& ras, + Scanline& sl, + Renderer& r, + VertexSource& vs, + const ColorStorage& as, + const PathId& path_id, + unsigned num_paths) + { + for(unsigned i = 0; i < num_paths; i++) + { + ras.reset(); + ras.add_path(vs, path_id[i]); + r.color(as[i]); + render_scanlines(ras, sl, r); + } + } + + + + + + + //=============================================render_scanlines_compound + template + void render_scanlines_compound(Rasterizer& ras, + ScanlineAA& sl_aa, + ScanlineBin& sl_bin, + BaseRenderer& ren, + SpanAllocator& alloc, + StyleHandler& sh) + { + if(ras.rewind_scanlines()) + { + int min_x = ras.min_x(); + int len = ras.max_x() - min_x + 2; + sl_aa.reset(min_x, ras.max_x()); + sl_bin.reset(min_x, ras.max_x()); + + typedef typename BaseRenderer::color_type color_type; + color_type* color_span = alloc.allocate(len * 2); + color_type* mix_buffer = color_span + len; + unsigned num_spans; + + unsigned num_styles; + unsigned style; + bool solid; + while((num_styles = ras.sweep_styles()) > 0) + { + typename ScanlineAA::const_iterator span_aa; + if(num_styles == 1) + { + // Optimization for a single style. Happens often + //------------------------- + if(ras.sweep_scanline(sl_aa, 0)) + { + style = ras.style(0); + if(sh.is_solid(style)) + { + // Just solid fill + //----------------------- + render_scanline_aa_solid(sl_aa, ren, sh.color(style)); + } + else + { + // Arbitrary span generator + //----------------------- + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + for(;;) + { + len = span_aa->len; + sh.generate_span(color_span, + span_aa->x, + sl_aa.y(), + len, + style); + + ren.blend_color_hspan(span_aa->x, + sl_aa.y(), + span_aa->len, + color_span, + span_aa->covers); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + else + { + if(ras.sweep_scanline(sl_bin, -1)) + { + // Clear the spans of the mix_buffer + //-------------------- + typename ScanlineBin::const_iterator span_bin = sl_bin.begin(); + num_spans = sl_bin.num_spans(); + for(;;) + { + memset(mix_buffer + span_bin->x - min_x, + 0, + span_bin->len * sizeof(color_type)); + + if(--num_spans == 0) break; + ++span_bin; + } + + unsigned i; + for(i = 0; i < num_styles; i++) + { + style = ras.style(i); + solid = sh.is_solid(style); + + if(ras.sweep_scanline(sl_aa, i)) + { + color_type* colors; + color_type* cspan; + typename ScanlineAA::cover_type* covers; + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + if(solid) + { + // Just solid fill + //----------------------- + for(;;) + { + color_type c = sh.color(style); + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + covers = span_aa->covers; + do + { + if(*covers == cover_full) + { + *colors = c; + } + else + { + colors->add(c, *covers); + } + ++colors; + ++covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + else + { + // Arbitrary span generator + //----------------------- + for(;;) + { + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + cspan = color_span; + sh.generate_span(cspan, + span_aa->x, + sl_aa.y(), + len, + style); + covers = span_aa->covers; + do + { + if(*covers == cover_full) + { + *colors = *cspan; + } + else + { + colors->add(*cspan, *covers); + } + ++cspan; + ++colors; + ++covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + + // Emit the blended result as a color hspan + //------------------------- + span_bin = sl_bin.begin(); + num_spans = sl_bin.num_spans(); + for(;;) + { + ren.blend_color_hspan(span_bin->x, + sl_bin.y(), + span_bin->len, + mix_buffer + span_bin->x - min_x, + 0, + cover_full); + if(--num_spans == 0) break; + ++span_bin; + } + } // if(ras.sweep_scanline(sl_bin, -1)) + } // if(num_styles == 1) ... else + } // while((num_styles = ras.sweep_styles()) > 0) + } // if(ras.rewind_scanlines()) + } + + //=======================================render_scanlines_compound_layered + template + void render_scanlines_compound_layered(Rasterizer& ras, + ScanlineAA& sl_aa, + BaseRenderer& ren, + SpanAllocator& alloc, + StyleHandler& sh) + { + if(ras.rewind_scanlines()) + { + int min_x = ras.min_x(); + int len = ras.max_x() - min_x + 2; + sl_aa.reset(min_x, ras.max_x()); + + typedef typename BaseRenderer::color_type color_type; + color_type* color_span = alloc.allocate(len * 2); + color_type* mix_buffer = color_span + len; + cover_type* cover_buffer = ras.allocate_cover_buffer(len); + unsigned num_spans; + + unsigned num_styles; + unsigned style; + bool solid; + while((num_styles = ras.sweep_styles()) > 0) + { + typename ScanlineAA::const_iterator span_aa; + if(num_styles == 1) + { + // Optimization for a single style. Happens often + //------------------------- + if(ras.sweep_scanline(sl_aa, 0)) + { + style = ras.style(0); + if(sh.is_solid(style)) + { + // Just solid fill + //----------------------- + render_scanline_aa_solid(sl_aa, ren, sh.color(style)); + } + else + { + // Arbitrary span generator + //----------------------- + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + for(;;) + { + len = span_aa->len; + sh.generate_span(color_span, + span_aa->x, + sl_aa.y(), + len, + style); + + ren.blend_color_hspan(span_aa->x, + sl_aa.y(), + span_aa->len, + color_span, + span_aa->covers); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + else + { + int sl_start = ras.scanline_start(); + unsigned sl_len = ras.scanline_length(); + + if(sl_len) + { + memset(mix_buffer + sl_start - min_x, + 0, + sl_len * sizeof(color_type)); + + memset(cover_buffer + sl_start - min_x, + 0, + sl_len * sizeof(cover_type)); + + int sl_y = 0x7FFFFFFF; + unsigned i; + for(i = 0; i < num_styles; i++) + { + style = ras.style(i); + solid = sh.is_solid(style); + + if(ras.sweep_scanline(sl_aa, i)) + { + unsigned cover; + color_type* colors; + color_type* cspan; + cover_type* src_covers; + cover_type* dst_covers; + span_aa = sl_aa.begin(); + num_spans = sl_aa.num_spans(); + sl_y = sl_aa.y(); + if(solid) + { + // Just solid fill + //----------------------- + for(;;) + { + color_type c = sh.color(style); + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + src_covers = span_aa->covers; + dst_covers = cover_buffer + span_aa->x - min_x; + do + { + cover = *src_covers; + if(*dst_covers + cover > cover_full) + { + cover = cover_full - *dst_covers; + } + if(cover) + { + colors->add(c, cover); + *dst_covers += cover; + } + ++colors; + ++src_covers; + ++dst_covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + else + { + // Arbitrary span generator + //----------------------- + for(;;) + { + len = span_aa->len; + colors = mix_buffer + span_aa->x - min_x; + cspan = color_span; + sh.generate_span(cspan, + span_aa->x, + sl_aa.y(), + len, + style); + src_covers = span_aa->covers; + dst_covers = cover_buffer + span_aa->x - min_x; + do + { + cover = *src_covers; + if(*dst_covers + cover > cover_full) + { + cover = cover_full - *dst_covers; + } + if(cover) + { + colors->add(*cspan, cover); + *dst_covers += cover; + } + ++cspan; + ++colors; + ++src_covers; + ++dst_covers; + } + while(--len); + if(--num_spans == 0) break; + ++span_aa; + } + } + } + } + ren.blend_color_hspan(sl_start, + sl_y, + sl_len, + mix_buffer + sl_start - min_x, + 0, + cover_full); + } //if(sl_len) + } //if(num_styles == 1) ... else + } //while((num_styles = ras.sweep_styles()) > 0) + } //if(ras.rewind_scanlines()) + } + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_rendering_buffer.h b/desmume/src/windows/agg/include/agg_rendering_buffer.h new file mode 100644 index 000000000..5a06c8a00 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rendering_buffer.h @@ -0,0 +1,305 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERING_BUFFER_INCLUDED +#define AGG_RENDERING_BUFFER_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //===========================================================row_accessor + template class row_accessor + { + public: + typedef const_row_info row_data; + + //------------------------------------------------------------------- + row_accessor() : + m_buf(0), + m_start(0), + m_width(0), + m_height(0), + m_stride(0) + { + } + + //-------------------------------------------------------------------- + row_accessor(T* buf, unsigned width, unsigned height, int stride) : + m_buf(0), + m_start(0), + m_width(0), + m_height(0), + m_stride(0) + { + attach(buf, width, height, stride); + } + + + //-------------------------------------------------------------------- + void attach(T* buf, unsigned width, unsigned height, int stride) + { + m_buf = m_start = buf; + m_width = width; + m_height = height; + m_stride = stride; + if(stride < 0) + { + m_start = m_buf - int(height - 1) * stride; + } + } + + //-------------------------------------------------------------------- + AGG_INLINE T* buf() { return m_buf; } + AGG_INLINE const T* buf() const { return m_buf; } + AGG_INLINE unsigned width() const { return m_width; } + AGG_INLINE unsigned height() const { return m_height; } + AGG_INLINE int stride() const { return m_stride; } + AGG_INLINE unsigned stride_abs() const + { + return (m_stride < 0) ? unsigned(-m_stride) : unsigned(m_stride); + } + + //-------------------------------------------------------------------- + AGG_INLINE T* row_ptr(int, int y, unsigned) + { + return m_start + y * m_stride; + } + AGG_INLINE T* row_ptr(int y) { return m_start + y * m_stride; } + AGG_INLINE const T* row_ptr(int y) const { return m_start + y * m_stride; } + AGG_INLINE row_data row (int y) const + { + return row_data(0, m_width-1, row_ptr(y)); + } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src) + { + unsigned h = height(); + if(src.height() < h) h = src.height(); + + unsigned l = stride_abs(); + if(src.stride_abs() < l) l = src.stride_abs(); + + l *= sizeof(T); + + unsigned y; + unsigned w = width(); + for (y = 0; y < h; y++) + { + memcpy(row_ptr(0, y, w), src.row_ptr(y), l); + } + } + + //-------------------------------------------------------------------- + void clear(T value) + { + unsigned y; + unsigned w = width(); + unsigned stride = stride_abs(); + for(y = 0; y < height(); y++) + { + T* p = row_ptr(0, y, w); + unsigned x; + for(x = 0; x < stride; x++) + { + *p++ = value; + } + } + } + + private: + //-------------------------------------------------------------------- + T* m_buf; // Pointer to renrdering buffer + T* m_start; // Pointer to first pixel depending on stride + unsigned m_width; // Width in pixels + unsigned m_height; // Height in pixels + int m_stride; // Number of bytes per row. Can be < 0 + }; + + + + + //==========================================================row_ptr_cache + template class row_ptr_cache + { + public: + typedef const_row_info row_data; + + //------------------------------------------------------------------- + row_ptr_cache() : + m_buf(0), + m_rows(), + m_width(0), + m_height(0), + m_stride(0) + { + } + + //-------------------------------------------------------------------- + row_ptr_cache(T* buf, unsigned width, unsigned height, int stride) : + m_buf(0), + m_rows(), + m_width(0), + m_height(0), + m_stride(0) + { + attach(buf, width, height, stride); + } + + //-------------------------------------------------------------------- + void attach(T* buf, unsigned width, unsigned height, int stride) + { + m_buf = buf; + m_width = width; + m_height = height; + m_stride = stride; + if(height > m_rows.size()) + { + m_rows.resize(height); + } + + T* row_ptr = m_buf; + + if(stride < 0) + { + row_ptr = m_buf - int(height - 1) * stride; + } + + T** rows = &m_rows[0]; + + while(height--) + { + *rows++ = row_ptr; + row_ptr += stride; + } + } + + //-------------------------------------------------------------------- + AGG_INLINE T* buf() { return m_buf; } + AGG_INLINE const T* buf() const { return m_buf; } + AGG_INLINE unsigned width() const { return m_width; } + AGG_INLINE unsigned height() const { return m_height; } + AGG_INLINE int stride() const { return m_stride; } + AGG_INLINE unsigned stride_abs() const + { + return (m_stride < 0) ? unsigned(-m_stride) : unsigned(m_stride); + } + + //-------------------------------------------------------------------- + AGG_INLINE T* row_ptr(int, int y, unsigned) + { + return m_rows[y]; + } + AGG_INLINE T* row_ptr(int y) { return m_rows[y]; } + AGG_INLINE const T* row_ptr(int y) const { return m_rows[y]; } + AGG_INLINE row_data row (int y) const + { + return row_data(0, m_width-1, m_rows[y]); + } + + //-------------------------------------------------------------------- + T const* const* rows() const { return &m_rows[0]; } + + //-------------------------------------------------------------------- + template + void copy_from(const RenBuf& src) + { + unsigned h = height(); + if(src.height() < h) h = src.height(); + + unsigned l = stride_abs(); + if(src.stride_abs() < l) l = src.stride_abs(); + + l *= sizeof(T); + + unsigned y; + unsigned w = width(); + for (y = 0; y < h; y++) + { + memcpy(row_ptr(0, y, w), src.row_ptr(y), l); + } + } + + //-------------------------------------------------------------------- + void clear(T value) + { + unsigned y; + unsigned w = width(); + unsigned stride = stride_abs(); + for(y = 0; y < height(); y++) + { + T* p = row_ptr(0, y, w); + unsigned x; + for(x = 0; x < stride; x++) + { + *p++ = value; + } + } + } + + private: + //-------------------------------------------------------------------- + T* m_buf; // Pointer to renrdering buffer + pod_array m_rows; // Pointers to each row of the buffer + unsigned m_width; // Width in pixels + unsigned m_height; // Height in pixels + int m_stride; // Number of bytes per row. Can be < 0 + }; + + + + + //========================================================rendering_buffer + // + // The definition of the main type for accessing the rows in the frame + // buffer. It provides functionality to navigate to the rows in a + // rectangular matrix, from top to bottom or from bottom to top depending + // on stride. + // + // row_accessor is cheap to create/destroy, but performs one multiplication + // when calling row_ptr(). + // + // row_ptr_cache creates an array of pointers to rows, so, the access + // via row_ptr() may be faster. But it requires memory allocation + // when creating. For example, on typical Intel Pentium hardware + // row_ptr_cache speeds span_image_filter_rgb_nn up to 10% + // + // It's used only in short hand typedefs like pixfmt_rgba32 and can be + // redefined in agg_config.h + // In real applications you can use both, depending on your needs + //------------------------------------------------------------------------ +#ifdef AGG_RENDERING_BUFFER + typedef AGG_RENDERING_BUFFER rendering_buffer; +#else +// typedef row_ptr_cache rendering_buffer; + typedef row_accessor rendering_buffer; +#endif + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_rendering_buffer_dynarow.h b/desmume/src/windows/agg/include/agg_rendering_buffer_dynarow.h new file mode 100644 index 000000000..bc0ad4d21 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rendering_buffer_dynarow.h @@ -0,0 +1,142 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RENDERING_BUFFER_DYNAROW_INCLUDED +#define AGG_RENDERING_BUFFER_DYNAROW_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //===============================================rendering_buffer_dynarow + // Rendering buffer class with dynamic allocation of the rows. + // The rows are allocated as needed when requesting for span_ptr(). + // The class automatically calculates min_x and max_x for each row. + // Generally it's more efficient to use this class as a temporary buffer + // for rendering a few lines and then to blend it with another buffer. + // + class rendering_buffer_dynarow + { + public: + typedef row_info row_data; + + //------------------------------------------------------------------- + ~rendering_buffer_dynarow() + { + init(0,0,0); + } + + //------------------------------------------------------------------- + rendering_buffer_dynarow() : + m_rows(), + m_width(0), + m_height(0), + m_byte_width(0) + { + } + + // Allocate and clear the buffer + //-------------------------------------------------------------------- + rendering_buffer_dynarow(unsigned width, unsigned height, + unsigned byte_width) : + m_rows(height), + m_width(width), + m_height(height), + m_byte_width(byte_width) + { + memset(&m_rows[0], 0, sizeof(row_data) * height); + } + + // Allocate and clear the buffer + //-------------------------------------------------------------------- + void init(unsigned width, unsigned height, unsigned byte_width) + { + unsigned i; + for(i = 0; i < m_height; ++i) + { + pod_allocator::deallocate((int8u*)m_rows[i].ptr, m_byte_width); + } + if(width && height) + { + m_width = width; + m_height = height; + m_byte_width = byte_width; + m_rows.resize(height); + memset(&m_rows[0], 0, sizeof(row_data) * height); + } + } + + //-------------------------------------------------------------------- + unsigned width() const { return m_width; } + unsigned height() const { return m_height; } + unsigned byte_width() const { return m_byte_width; } + + // The main function used for rendering. Returns pointer to the + // pre-allocated span. Memory for the row is allocated as needed. + //-------------------------------------------------------------------- + int8u* row_ptr(int x, int y, unsigned len) + { + row_data* r = &m_rows[y]; + int x2 = x + len - 1; + if(r->ptr) + { + if(x < r->x1) { r->x1 = x; } + if(x2 > r->x2) { r->x2 = x2; } + } + else + { + int8u* p = pod_allocator::allocate(m_byte_width); + r->ptr = p; + r->x1 = x; + r->x2 = x2; + memset(p, 0, m_byte_width); + } + return (int8u*)r->ptr; + } + + //-------------------------------------------------------------------- + const int8u* row_ptr(int y) const { return m_rows[y].ptr; } + int8u* row_ptr(int y) { return row_ptr(0, y, m_width); } + row_data row (int y) const { return m_rows[y]; } + + private: + //-------------------------------------------------------------------- + // Prohibit copying + rendering_buffer_dynarow(const rendering_buffer_dynarow&); + const rendering_buffer_dynarow& operator = (const rendering_buffer_dynarow&); + + private: + //-------------------------------------------------------------------- + pod_array m_rows; // Pointers to each row of the buffer + unsigned m_width; // Width in pixels + unsigned m_height; // Height in pixels + unsigned m_byte_width; // Width in bytes + }; + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_rounded_rect.h b/desmume/src/windows/agg/include/agg_rounded_rect.h new file mode 100644 index 000000000..ecaa370ea --- /dev/null +++ b/desmume/src/windows/agg/include/agg_rounded_rect.h @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_ROUNDED_RECT_INCLUDED +#define AGG_ROUNDED_RECT_INCLUDED + +#include "agg_basics.h" +#include "agg_arc.h" + +namespace agg +{ + //------------------------------------------------------------rounded_rect + // + // See Implemantation agg_rounded_rect.cpp + // + class rounded_rect + { + public: + rounded_rect() {} + rounded_rect(double x1, double y1, double x2, double y2, double r); + + void rect(double x1, double y1, double x2, double y2); + void radius(double r); + void radius(double rx, double ry); + void radius(double rx_bottom, double ry_bottom, double rx_top, double ry_top); + void radius(double rx1, double ry1, double rx2, double ry2, + double rx3, double ry3, double rx4, double ry4); + void normalize_radius(); + + void approximation_scale(double s) { m_arc.approximation_scale(s); } + double approximation_scale() const { return m_arc.approximation_scale(); } + + void rewind(unsigned); + unsigned vertex(double* x, double* y); + + private: + double m_x1; + double m_y1; + double m_x2; + double m_y2; + double m_rx1; + double m_ry1; + double m_rx2; + double m_ry2; + double m_rx3; + double m_ry3; + double m_rx4; + double m_ry4; + unsigned m_status; + arc m_arc; + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_scanline_bin.h b/desmume/src/windows/agg/include/agg_scanline_bin.h new file mode 100644 index 000000000..dd4cdf694 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_bin.h @@ -0,0 +1,269 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates (scanline32_bin) has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SCANLINE_BIN_INCLUDED +#define AGG_SCANLINE_BIN_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //=============================================================scanline_bin + // + // This is binary scaline container which supports the interface + // used in the rasterizer::render(). See description of agg_scanline_u8 + // for details. + // + //------------------------------------------------------------------------ + class scanline_bin + { + public: + typedef int32 coord_type; + + struct span + { + int16 x; + int16 len; + }; + + typedef const span* const_iterator; + + //-------------------------------------------------------------------- + scanline_bin() : + m_last_x(0x7FFFFFF0), + m_spans(), + m_cur_span(0) + { + } + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 3; + if(max_len > m_spans.size()) + { + m_spans.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_cur_span = &m_spans[0]; + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned) + { + if(x == m_last_x+1) + { + m_cur_span->len++; + } + else + { + ++m_cur_span; + m_cur_span->x = (int16)x; + m_cur_span->len = 1; + } + m_last_x = x; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned) + { + if(x == m_last_x+1) + { + m_cur_span->len = (int16)(m_cur_span->len + len); + } + else + { + ++m_cur_span; + m_cur_span->x = (int16)x; + m_cur_span->len = (int16)len; + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const void*) + { + add_span(x, len, 0); + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cur_span = &m_spans[0]; + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return unsigned(m_cur_span - &m_spans[0]); } + const_iterator begin() const { return &m_spans[1]; } + + private: + scanline_bin(const scanline_bin&); + const scanline_bin operator = (const scanline_bin&); + + int m_last_x; + int m_y; + pod_array m_spans; + span* m_cur_span; + }; + + + + + + + //===========================================================scanline32_bin + class scanline32_bin + { + public: + typedef int32 coord_type; + + //-------------------------------------------------------------------- + struct span + { + span() {} + span(coord_type x_, coord_type len_) : x(x_), len(len_) {} + + coord_type x; + coord_type len; + }; + typedef pod_bvector span_array_type; + + + //-------------------------------------------------------------------- + class const_iterator + { + public: + const_iterator(const span_array_type& spans) : + m_spans(spans), + m_span_idx(0) + {} + + const span& operator*() const { return m_spans[m_span_idx]; } + const span* operator->() const { return &m_spans[m_span_idx]; } + + void operator ++ () { ++m_span_idx; } + + private: + const span_array_type& m_spans; + unsigned m_span_idx; + }; + + + //-------------------------------------------------------------------- + scanline32_bin() : m_max_len(0), m_last_x(0x7FFFFFF0) {} + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + m_last_x = 0x7FFFFFF0; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned) + { + if(x == m_last_x+1) + { + m_spans.last().len++; + } + else + { + m_spans.add(span(coord_type(x), 1)); + } + m_last_x = x; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned) + { + if(x == m_last_x+1) + { + m_spans.last().len += coord_type(len); + } + else + { + m_spans.add(span(coord_type(x), coord_type(len))); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const void*) + { + add_span(x, len, 0); + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return m_spans.size(); } + const_iterator begin() const { return const_iterator(m_spans); } + + private: + scanline32_bin(const scanline32_bin&); + const scanline32_bin operator = (const scanline32_bin&); + + unsigned m_max_len; + int m_last_x; + int m_y; + span_array_type m_spans; + }; + + + + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_scanline_boolean_algebra.h b/desmume/src/windows/agg/include/agg_scanline_boolean_algebra.h new file mode 100644 index 000000000..7e0970bc5 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_boolean_algebra.h @@ -0,0 +1,1576 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED +#define AGG_SCANLINE_BOOLEAN_ALGEBRA_INCLUDED + +#include +#include +#include "agg_basics.h" + + +namespace agg +{ + + //-----------------------------------------------sbool_combine_spans_bin + // Functor. + // Combine two binary encoded spans, i.e., when we don't have any + // anti-aliasing information, but only X and Length. The function + // is compatible with any type of scanlines. + //---------------- + template + struct sbool_combine_spans_bin + { + void operator () (const typename Scanline1::const_iterator&, + const typename Scanline2::const_iterator&, + int x, unsigned len, + Scanline& sl) const + { + sl.add_span(x, len, cover_full); + } + }; + + + + //---------------------------------------------sbool_combine_spans_empty + // Functor. + // Combine two spans as empty ones. The functor does nothing + // and is used to XOR binary spans. + //---------------- + template + struct sbool_combine_spans_empty + { + void operator () (const typename Scanline1::const_iterator&, + const typename Scanline2::const_iterator&, + int, unsigned, + Scanline&) const + {} + }; + + + + //--------------------------------------------------sbool_add_span_empty + // Functor. + // Add nothing. Used in conbine_shapes_sub + //---------------- + template + struct sbool_add_span_empty + { + void operator () (const typename Scanline1::const_iterator&, + int, unsigned, + Scanline&) const + {} + }; + + + //----------------------------------------------------sbool_add_span_bin + // Functor. + // Add a binary span + //---------------- + template + struct sbool_add_span_bin + { + void operator () (const typename Scanline1::const_iterator&, + int x, unsigned len, + Scanline& sl) const + { + sl.add_span(x, len, cover_full); + } + }; + + + + + //-----------------------------------------------------sbool_add_span_aa + // Functor. + // Add an anti-aliased span + // anti-aliasing information, but only X and Length. The function + // is compatible with any type of scanlines. + //---------------- + template + struct sbool_add_span_aa + { + void operator () (const typename Scanline1::const_iterator& span, + int x, unsigned len, + Scanline& sl) const + { + if(span->len < 0) + { + sl.add_span(x, len, *span->covers); + } + else + if(span->len > 0) + { + const typename Scanline1::cover_type* covers = span->covers; + if(span->x < x) covers += x - span->x; + sl.add_cells(x, len, covers); + } + } + }; + + + + + //----------------------------------------------sbool_intersect_spans_aa + // Functor. + // Intersect two spans preserving the anti-aliasing information. + // The result is added to the "sl" scanline. + //------------------ + template + struct sbool_intersect_spans_aa + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1, + cover_full = cover_mask + }; + + + void operator () (const typename Scanline1::const_iterator& span1, + const typename Scanline2::const_iterator& span2, + int x, unsigned len, + Scanline& sl) const + { + unsigned cover; + const typename Scanline1::cover_type* covers1; + const typename Scanline2::cover_type* covers2; + + // Calculate the operation code and choose the + // proper combination algorithm. + // 0 = Both spans are of AA type + // 1 = span1 is solid, span2 is AA + // 2 = span1 is AA, span2 is solid + // 3 = Both spans are of solid type + //----------------- + switch((span1->len < 0) | ((span2->len < 0) << 1)) + { + case 0: // Both are AA spans + covers1 = span1->covers; + covers2 = span2->covers; + if(span1->x < x) covers1 += x - span1->x; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = *covers1++ * *covers2++; + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + break; + + case 1: // span1 is solid, span2 is AA + covers2 = span2->covers; + if(span2->x < x) covers2 += x - span2->x; + if(*(span1->covers) == cover_full) + { + sl.add_cells(x, len, covers2); + } + else + { + do + { + cover = *(span1->covers) * *covers2++; + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + } + break; + + case 2: // span1 is AA, span2 is solid + covers1 = span1->covers; + if(span1->x < x) covers1 += x - span1->x; + if(*(span2->covers) == cover_full) + { + sl.add_cells(x, len, covers1); + } + else + { + do + { + cover = *covers1++ * *(span2->covers); + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + } + break; + + case 3: // Both are solid spans + cover = *(span1->covers) * *(span2->covers); + sl.add_span(x, len, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + break; + } + } + }; + + + + + + + //--------------------------------------------------sbool_unite_spans_aa + // Functor. + // Unite two spans preserving the anti-aliasing information. + // The result is added to the "sl" scanline. + //------------------ + template + struct sbool_unite_spans_aa + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1, + cover_full = cover_mask + }; + + + void operator () (const typename Scanline1::const_iterator& span1, + const typename Scanline2::const_iterator& span2, + int x, unsigned len, + Scanline& sl) const + { + unsigned cover; + const typename Scanline1::cover_type* covers1; + const typename Scanline2::cover_type* covers2; + + // Calculate the operation code and choose the + // proper combination algorithm. + // 0 = Both spans are of AA type + // 1 = span1 is solid, span2 is AA + // 2 = span1 is AA, span2 is solid + // 3 = Both spans are of solid type + //----------------- + switch((span1->len < 0) | ((span2->len < 0) << 1)) + { + case 0: // Both are AA spans + covers1 = span1->covers; + covers2 = span2->covers; + if(span1->x < x) covers1 += x - span1->x; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = cover_mask * cover_mask - + (cover_mask - *covers1++) * + (cover_mask - *covers2++); + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + break; + + case 1: // span1 is solid, span2 is AA + covers2 = span2->covers; + if(span2->x < x) covers2 += x - span2->x; + if(*(span1->covers) == cover_full) + { + sl.add_span(x, len, cover_full); + } + else + { + do + { + cover = cover_mask * cover_mask - + (cover_mask - *(span1->covers)) * + (cover_mask - *covers2++); + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + } + break; + + case 2: // span1 is AA, span2 is solid + covers1 = span1->covers; + if(span1->x < x) covers1 += x - span1->x; + if(*(span2->covers) == cover_full) + { + sl.add_span(x, len, cover_full); + } + else + { + do + { + cover = cover_mask * cover_mask - + (cover_mask - *covers1++) * + (cover_mask - *(span2->covers)); + sl.add_cell(x++, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + while(--len); + } + break; + + case 3: // Both are solid spans + cover = cover_mask * cover_mask - + (cover_mask - *(span1->covers)) * + (cover_mask - *(span2->covers)); + sl.add_span(x, len, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + break; + } + } + }; + + + //---------------------------------------------sbool_xor_formula_linear + template + struct sbool_xor_formula_linear + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1 + }; + + static AGG_INLINE unsigned calculate(unsigned a, unsigned b) + { + unsigned cover = a + b; + if(cover > cover_mask) cover = cover_mask + cover_mask - cover; + return cover; + } + }; + + + //---------------------------------------------sbool_xor_formula_saddle + template + struct sbool_xor_formula_saddle + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1 + }; + + static AGG_INLINE unsigned calculate(unsigned a, unsigned b) + { + unsigned k = a * b; + if(k == cover_mask * cover_mask) return 0; + + a = (cover_mask * cover_mask - (a << cover_shift) + k) >> cover_shift; + b = (cover_mask * cover_mask - (b << cover_shift) + k) >> cover_shift; + return cover_mask - ((a * b) >> cover_shift); + } + }; + + + //-------------------------------------------sbool_xor_formula_abs_diff + struct sbool_xor_formula_abs_diff + { + static AGG_INLINE unsigned calculate(unsigned a, unsigned b) + { + return unsigned(abs(int(a) - int(b))); + } + }; + + + + //----------------------------------------------------sbool_xor_spans_aa + // Functor. + // XOR two spans preserving the anti-aliasing information. + // The result is added to the "sl" scanline. + //------------------ + template + struct sbool_xor_spans_aa + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1, + cover_full = cover_mask + }; + + + void operator () (const typename Scanline1::const_iterator& span1, + const typename Scanline2::const_iterator& span2, + int x, unsigned len, + Scanline& sl) const + { + unsigned cover; + const typename Scanline1::cover_type* covers1; + const typename Scanline2::cover_type* covers2; + + // Calculate the operation code and choose the + // proper combination algorithm. + // 0 = Both spans are of AA type + // 1 = span1 is solid, span2 is AA + // 2 = span1 is AA, span2 is solid + // 3 = Both spans are of solid type + //----------------- + switch((span1->len < 0) | ((span2->len < 0) << 1)) + { + case 0: // Both are AA spans + covers1 = span1->covers; + covers2 = span2->covers; + if(span1->x < x) covers1 += x - span1->x; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = XorFormula::calculate(*covers1++, *covers2++); + if(cover) sl.add_cell(x, cover); + ++x; + } + while(--len); + break; + + case 1: // span1 is solid, span2 is AA + covers2 = span2->covers; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = XorFormula::calculate(*(span1->covers), *covers2++); + if(cover) sl.add_cell(x, cover); + ++x; + } + while(--len); + break; + + case 2: // span1 is AA, span2 is solid + covers1 = span1->covers; + if(span1->x < x) covers1 += x - span1->x; + do + { + cover = XorFormula::calculate(*covers1++, *(span2->covers)); + if(cover) sl.add_cell(x, cover); + ++x; + } + while(--len); + break; + + case 3: // Both are solid spans + cover = XorFormula::calculate(*(span1->covers), *(span2->covers)); + if(cover) sl.add_span(x, len, cover); + break; + + } + } + }; + + + + + + //-----------------------------------------------sbool_subtract_spans_aa + // Functor. + // Unite two spans preserving the anti-aliasing information. + // The result is added to the "sl" scanline. + //------------------ + template + struct sbool_subtract_spans_aa + { + enum cover_scale_e + { + cover_shift = CoverShift, + cover_size = 1 << cover_shift, + cover_mask = cover_size - 1, + cover_full = cover_mask + }; + + + void operator () (const typename Scanline1::const_iterator& span1, + const typename Scanline2::const_iterator& span2, + int x, unsigned len, + Scanline& sl) const + { + unsigned cover; + const typename Scanline1::cover_type* covers1; + const typename Scanline2::cover_type* covers2; + + // Calculate the operation code and choose the + // proper combination algorithm. + // 0 = Both spans are of AA type + // 1 = span1 is solid, span2 is AA + // 2 = span1 is AA, span2 is solid + // 3 = Both spans are of solid type + //----------------- + switch((span1->len < 0) | ((span2->len < 0) << 1)) + { + case 0: // Both are AA spans + covers1 = span1->covers; + covers2 = span2->covers; + if(span1->x < x) covers1 += x - span1->x; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = *covers1++ * (cover_mask - *covers2++); + if(cover) + { + sl.add_cell(x, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + ++x; + } + while(--len); + break; + + case 1: // span1 is solid, span2 is AA + covers2 = span2->covers; + if(span2->x < x) covers2 += x - span2->x; + do + { + cover = *(span1->covers) * (cover_mask - *covers2++); + if(cover) + { + sl.add_cell(x, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + ++x; + } + while(--len); + break; + + case 2: // span1 is AA, span2 is solid + covers1 = span1->covers; + if(span1->x < x) covers1 += x - span1->x; + if(*(span2->covers) != cover_full) + { + do + { + cover = *covers1++ * (cover_mask - *(span2->covers)); + if(cover) + { + sl.add_cell(x, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + ++x; + } + while(--len); + } + break; + + case 3: // Both are solid spans + cover = *(span1->covers) * (cover_mask - *(span2->covers)); + if(cover) + { + sl.add_span(x, len, + (cover == cover_full * cover_full) ? + cover_full : + (cover >> cover_shift)); + } + break; + } + } + }; + + + + + + + //--------------------------------------------sbool_add_spans_and_render + template + void sbool_add_spans_and_render(const Scanline1& sl1, + Scanline& sl, + Renderer& ren, + AddSpanFunctor add_span) + { + sl.reset_spans(); + typename Scanline1::const_iterator span = sl1.begin(); + unsigned num_spans = sl1.num_spans(); + for(;;) + { + add_span(span, span->x, abs((int)span->len), sl); + if(--num_spans == 0) break; + ++span; + } + sl.finalize(sl1.y()); + ren.render(sl); + } + + + + + + + + //---------------------------------------------sbool_intersect_scanlines + // Intersect two scanlines, "sl1" and "sl2" and generate a new "sl" one. + // The combine_spans functor can be of type sbool_combine_spans_bin or + // sbool_intersect_spans_aa. First is a general functor to combine + // two spans without Anti-Aliasing, the second preserves the AA + // information, but works slower + // + template + void sbool_intersect_scanlines(const Scanline1& sl1, + const Scanline2& sl2, + Scanline& sl, + CombineSpansFunctor combine_spans) + { + sl.reset_spans(); + + unsigned num1 = sl1.num_spans(); + if(num1 == 0) return; + + unsigned num2 = sl2.num_spans(); + if(num2 == 0) return; + + typename Scanline1::const_iterator span1 = sl1.begin(); + typename Scanline2::const_iterator span2 = sl2.begin(); + + while(num1 && num2) + { + int xb1 = span1->x; + int xb2 = span2->x; + int xe1 = xb1 + abs((int)span1->len) - 1; + int xe2 = xb2 + abs((int)span2->len) - 1; + + // Determine what spans we should advance in the next step + // The span with the least ending X should be advanced + // advance_both is just an optimization when we ending + // coordinates are the same and we can advance both + //-------------- + bool advance_span1 = xe1 < xe2; + bool advance_both = xe1 == xe2; + + // Find the intersection of the spans + // and check if they intersect + //-------------- + if(xb1 < xb2) xb1 = xb2; + if(xe1 > xe2) xe1 = xe2; + if(xb1 <= xe1) + { + combine_spans(span1, span2, xb1, xe1 - xb1 + 1, sl); + } + + // Advance the spans + //-------------- + if(advance_both) + { + --num1; + --num2; + if(num1) ++span1; + if(num2) ++span2; + } + else + { + if(advance_span1) + { + --num1; + if(num1) ++span1; + } + else + { + --num2; + if(num2) ++span2; + } + } + } + } + + + + + + + + + //------------------------------------------------sbool_intersect_shapes + // Intersect the scanline shapes. Here the "Scanline Generator" + // abstraction is used. ScanlineGen1 and ScanlineGen2 are + // the generators, and can be of type rasterizer_scanline_aa<>. + // There function requires three scanline containers that can be of + // different types. + // "sl1" and "sl2" are used to retrieve scanlines from the generators, + // "sl" is ised as the resulting scanline to render it. + // The external "sl1" and "sl2" are used only for the sake of + // optimization and reusing of the scanline objects. + // the function calls sbool_intersect_scanlines with CombineSpansFunctor + // as the last argument. See sbool_intersect_scanlines for details. + //---------- + template + void sbool_intersect_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren, + CombineSpansFunctor combine_spans) + { + // Prepare the scanline generators. + // If anyone of them doesn't contain + // any scanlines, then return. + //----------------- + if(!sg1.rewind_scanlines()) return; + if(!sg2.rewind_scanlines()) return; + + // Get the bounding boxes + //---------------- + rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y()); + rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y()); + + // Calculate the intersection of the bounding + // boxes and return if they don't intersect. + //----------------- + rect_i ir = intersect_rectangles(r1, r2); + if(!ir.is_valid()) return; + + // Reset the scanlines and get two first ones + //----------------- + sl.reset(ir.x1, ir.x2); + sl1.reset(sg1.min_x(), sg1.max_x()); + sl2.reset(sg2.min_x(), sg2.max_x()); + if(!sg1.sweep_scanline(sl1)) return; + if(!sg2.sweep_scanline(sl2)) return; + + ren.prepare(); + + // The main loop + // Here we synchronize the scanlines with + // the same Y coordinate, ignoring all other ones. + // Only scanlines having the same Y-coordinate + // are to be combined. + //----------------- + for(;;) + { + while(sl1.y() < sl2.y()) + { + if(!sg1.sweep_scanline(sl1)) return; + } + while(sl2.y() < sl1.y()) + { + if(!sg2.sweep_scanline(sl2)) return; + } + + if(sl1.y() == sl2.y()) + { + // The Y coordinates are the same. + // Combine the scanlines, render if they contain any spans, + // and advance both generators to the next scanlines + //---------------------- + sbool_intersect_scanlines(sl1, sl2, sl, combine_spans); + if(sl.num_spans()) + { + sl.finalize(sl1.y()); + ren.render(sl); + } + if(!sg1.sweep_scanline(sl1)) return; + if(!sg2.sweep_scanline(sl2)) return; + } + } + } + + + + + + + + //-------------------------------------------------sbool_unite_scanlines + // Unite two scanlines, "sl1" and "sl2" and generate a new "sl" one. + // The combine_spans functor can be of type sbool_combine_spans_bin or + // sbool_intersect_spans_aa. First is a general functor to combine + // two spans without Anti-Aliasing, the second preserves the AA + // information, but works slower + // + template + void sbool_unite_scanlines(const Scanline1& sl1, + const Scanline2& sl2, + Scanline& sl, + AddSpanFunctor1 add_span1, + AddSpanFunctor2 add_span2, + CombineSpansFunctor combine_spans) + { + sl.reset_spans(); + + unsigned num1 = sl1.num_spans(); + unsigned num2 = sl2.num_spans(); + + typename Scanline1::const_iterator span1;// = sl1.begin(); + typename Scanline2::const_iterator span2;// = sl2.begin(); + + enum invalidation_e + { + invalid_b = 0xFFFFFFF, + invalid_e = invalid_b - 1 + }; + + // Initialize the spans as invalid + //--------------- + int xb1 = invalid_b; + int xb2 = invalid_b; + int xe1 = invalid_e; + int xe2 = invalid_e; + + // Initialize span1 if there are spans + //--------------- + if(num1) + { + span1 = sl1.begin(); + xb1 = span1->x; + xe1 = xb1 + abs((int)span1->len) - 1; + --num1; + } + + // Initialize span2 if there are spans + //--------------- + if(num2) + { + span2 = sl2.begin(); + xb2 = span2->x; + xe2 = xb2 + abs((int)span2->len) - 1; + --num2; + } + + + for(;;) + { + // Retrieve a new span1 if it's invalid + //---------------- + if(num1 && xb1 > xe1) + { + --num1; + ++span1; + xb1 = span1->x; + xe1 = xb1 + abs((int)span1->len) - 1; + } + + // Retrieve a new span2 if it's invalid + //---------------- + if(num2 && xb2 > xe2) + { + --num2; + ++span2; + xb2 = span2->x; + xe2 = xb2 + abs((int)span2->len) - 1; + } + + if(xb1 > xe1 && xb2 > xe2) break; + + // Calculate the intersection + //---------------- + int xb = xb1; + int xe = xe1; + if(xb < xb2) xb = xb2; + if(xe > xe2) xe = xe2; + int len = xe - xb + 1; // The length of the intersection + if(len > 0) + { + // The spans intersect, + // add the beginning of the span + //---------------- + if(xb1 < xb2) + { + add_span1(span1, xb1, xb2 - xb1, sl); + xb1 = xb2; + } + else + if(xb2 < xb1) + { + add_span2(span2, xb2, xb1 - xb2, sl); + xb2 = xb1; + } + + // Add the combination part of the spans + //---------------- + combine_spans(span1, span2, xb, len, sl); + + + // Invalidate the fully processed span or both + //---------------- + if(xe1 < xe2) + { + // Invalidate span1 and eat + // the processed part of span2 + //-------------- + xb1 = invalid_b; + xe1 = invalid_e; + xb2 += len; + } + else + if(xe2 < xe1) + { + // Invalidate span2 and eat + // the processed part of span1 + //-------------- + xb2 = invalid_b; + xe2 = invalid_e; + xb1 += len; + } + else + { + xb1 = invalid_b; // Invalidate both + xb2 = invalid_b; + xe1 = invalid_e; + xe2 = invalid_e; + } + } + else + { + // The spans do not intersect + //-------------- + if(xb1 < xb2) + { + // Advance span1 + //--------------- + if(xb1 <= xe1) + { + add_span1(span1, xb1, xe1 - xb1 + 1, sl); + } + xb1 = invalid_b; // Invalidate + xe1 = invalid_e; + } + else + { + // Advance span2 + //--------------- + if(xb2 <= xe2) + { + add_span2(span2, xb2, xe2 - xb2 + 1, sl); + } + xb2 = invalid_b; // Invalidate + xe2 = invalid_e; + } + } + } + } + + + + + //----------------------------------------------------sbool_unite_shapes + // Unite the scanline shapes. Here the "Scanline Generator" + // abstraction is used. ScanlineGen1 and ScanlineGen2 are + // the generators, and can be of type rasterizer_scanline_aa<>. + // There function requires three scanline containers that can be + // of different type. + // "sl1" and "sl2" are used to retrieve scanlines from the generators, + // "sl" is ised as the resulting scanline to render it. + // The external "sl1" and "sl2" are used only for the sake of + // optimization and reusing of the scanline objects. + // the function calls sbool_unite_scanlines with CombineSpansFunctor + // as the last argument. See sbool_unite_scanlines for details. + //---------- + template + void sbool_unite_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren, + AddSpanFunctor1 add_span1, + AddSpanFunctor2 add_span2, + CombineSpansFunctor combine_spans) + { + // Prepare the scanline generators. + // If anyone of them doesn't contain + // any scanlines, then return. + //----------------- + bool flag1 = sg1.rewind_scanlines(); + bool flag2 = sg2.rewind_scanlines(); + if(!flag1 && !flag2) return; + + // Get the bounding boxes + //---------------- + rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y()); + rect_i r2(sg2.min_x(), sg2.min_y(), sg2.max_x(), sg2.max_y()); + + // Calculate the union of the bounding boxes + //----------------- + rect_i ur(1,1,0,0); + if(flag1 && flag2) ur = unite_rectangles(r1, r2); + else if(flag1) ur = r1; + else if(flag2) ur = r2; + + if(!ur.is_valid()) return; + + ren.prepare(); + + // Reset the scanlines and get two first ones + //----------------- + sl.reset(ur.x1, ur.x2); + if(flag1) + { + sl1.reset(sg1.min_x(), sg1.max_x()); + flag1 = sg1.sweep_scanline(sl1); + } + + if(flag2) + { + sl2.reset(sg2.min_x(), sg2.max_x()); + flag2 = sg2.sweep_scanline(sl2); + } + + // The main loop + // Here we synchronize the scanlines with + // the same Y coordinate. + //----------------- + while(flag1 || flag2) + { + if(flag1 && flag2) + { + if(sl1.y() == sl2.y()) + { + // The Y coordinates are the same. + // Combine the scanlines, render if they contain any spans, + // and advance both generators to the next scanlines + //---------------------- + sbool_unite_scanlines(sl1, sl2, sl, + add_span1, add_span2, combine_spans); + if(sl.num_spans()) + { + sl.finalize(sl1.y()); + ren.render(sl); + } + flag1 = sg1.sweep_scanline(sl1); + flag2 = sg2.sweep_scanline(sl2); + } + else + { + if(sl1.y() < sl2.y()) + { + sbool_add_spans_and_render(sl1, sl, ren, add_span1); + flag1 = sg1.sweep_scanline(sl1); + } + else + { + sbool_add_spans_and_render(sl2, sl, ren, add_span2); + flag2 = sg2.sweep_scanline(sl2); + } + } + } + else + { + if(flag1) + { + sbool_add_spans_and_render(sl1, sl, ren, add_span1); + flag1 = sg1.sweep_scanline(sl1); + } + if(flag2) + { + sbool_add_spans_and_render(sl2, sl, ren, add_span2); + flag2 = sg2.sweep_scanline(sl2); + } + } + } + } + + + + + + + + + //-------------------------------------------------sbool_subtract_shapes + // Subtract the scanline shapes, "sg1-sg2". Here the "Scanline Generator" + // abstraction is used. ScanlineGen1 and ScanlineGen2 are + // the generators, and can be of type rasterizer_scanline_aa<>. + // There function requires three scanline containers that can be of + // different types. + // "sl1" and "sl2" are used to retrieve scanlines from the generators, + // "sl" is ised as the resulting scanline to render it. + // The external "sl1" and "sl2" are used only for the sake of + // optimization and reusing of the scanline objects. + // the function calls sbool_intersect_scanlines with CombineSpansFunctor + // as the last argument. See combine_scanlines_sub for details. + //---------- + template + void sbool_subtract_shapes(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren, + AddSpanFunctor1 add_span1, + CombineSpansFunctor combine_spans) + { + // Prepare the scanline generators. + // Here "sg1" is master, "sg2" is slave. + //----------------- + if(!sg1.rewind_scanlines()) return; + bool flag2 = sg2.rewind_scanlines(); + + // Get the bounding box + //---------------- + rect_i r1(sg1.min_x(), sg1.min_y(), sg1.max_x(), sg1.max_y()); + + // Reset the scanlines and get two first ones + //----------------- + sl.reset(sg1.min_x(), sg1.max_x()); + sl1.reset(sg1.min_x(), sg1.max_x()); + sl2.reset(sg2.min_x(), sg2.max_x()); + if(!sg1.sweep_scanline(sl1)) return; + + if(flag2) flag2 = sg2.sweep_scanline(sl2); + + ren.prepare(); + + // A fake span2 processor + sbool_add_span_empty add_span2; + + // The main loop + // Here we synchronize the scanlines with + // the same Y coordinate, ignoring all other ones. + // Only scanlines having the same Y-coordinate + // are to be combined. + //----------------- + bool flag1 = true; + do + { + // Synchronize "slave" with "master" + //----------------- + while(flag2 && sl2.y() < sl1.y()) + { + flag2 = sg2.sweep_scanline(sl2); + } + + + if(flag2 && sl2.y() == sl1.y()) + { + // The Y coordinates are the same. + // Combine the scanlines and render if they contain any spans. + //---------------------- + sbool_unite_scanlines(sl1, sl2, sl, add_span1, add_span2, combine_spans); + if(sl.num_spans()) + { + sl.finalize(sl1.y()); + ren.render(sl); + } + } + else + { + sbool_add_spans_and_render(sl1, sl, ren, add_span1); + } + + // Advance the "master" + flag1 = sg1.sweep_scanline(sl1); + } + while(flag1); + } + + + + + + + + //---------------------------------------------sbool_intersect_shapes_aa + // Intersect two anti-aliased scanline shapes. + // Here the "Scanline Generator" abstraction is used. + // ScanlineGen1 and ScanlineGen2 are the generators, and can be of + // type rasterizer_scanline_aa<>. There function requires three + // scanline containers that can be of different types. + // "sl1" and "sl2" are used to retrieve scanlines from the generators, + // "sl" is ised as the resulting scanline to render it. + // The external "sl1" and "sl2" are used only for the sake of + // optimization and reusing of the scanline objects. + //---------- + template + void sbool_intersect_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_intersect_spans_aa combine_functor; + sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor); + } + + + + + + //--------------------------------------------sbool_intersect_shapes_bin + // Intersect two binary scanline shapes (without anti-aliasing). + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_intersect_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_combine_spans_bin combine_functor; + sbool_intersect_shapes(sg1, sg2, sl1, sl2, sl, ren, combine_functor); + } + + + + + + //-------------------------------------------------sbool_unite_shapes_aa + // Unite two anti-aliased scanline shapes + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_unite_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_aa add_functor1; + sbool_add_span_aa add_functor2; + sbool_unite_spans_aa combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + + + + //------------------------------------------------sbool_unite_shapes_bin + // Unite two binary scanline shapes (without anti-aliasing). + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_unite_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_bin add_functor1; + sbool_add_span_bin add_functor2; + sbool_combine_spans_bin combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + + + + + + + + //---------------------------------------------------sbool_xor_shapes_aa + // Apply eXclusive OR to two anti-aliased scanline shapes. There's + // a modified "Linear" XOR used instead of classical "Saddle" one. + // The reason is to have the result absolutely conststent with what + // the scanline rasterizer produces. + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_xor_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_aa add_functor1; + sbool_add_span_aa add_functor2; + sbool_xor_spans_aa > combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + + //------------------------------------------sbool_xor_shapes_saddle_aa + // Apply eXclusive OR to two anti-aliased scanline shapes. + // There's the classical "Saddle" used to calculate the + // Anti-Aliasing values, that is: + // a XOR b : 1-((1-a+a*b)*(1-b+a*b)) + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_xor_shapes_saddle_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_aa add_functor1; + sbool_add_span_aa add_functor2; + sbool_xor_spans_aa > combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + //--------------------------------------sbool_xor_shapes_abs_diff_aa + // Apply eXclusive OR to two anti-aliased scanline shapes. + // There's the absolute difference used to calculate + // Anti-Aliasing values, that is: + // a XOR b : abs(a-b) + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_xor_shapes_abs_diff_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_aa add_functor1; + sbool_add_span_aa add_functor2; + sbool_xor_spans_aa combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + + //--------------------------------------------------sbool_xor_shapes_bin + // Apply eXclusive OR to two binary scanline shapes (without anti-aliasing). + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_xor_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_bin add_functor1; + sbool_add_span_bin add_functor2; + sbool_combine_spans_empty combine_functor; + sbool_unite_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor1, add_functor2, combine_functor); + } + + + + + + + //----------------------------------------------sbool_subtract_shapes_aa + // Subtract shapes "sg1-sg2" with anti-aliasing + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_subtract_shapes_aa(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_aa add_functor; + sbool_subtract_spans_aa combine_functor; + sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor, combine_functor); + } + + + + + + //---------------------------------------------sbool_subtract_shapes_bin + // Subtract binary shapes "sg1-sg2" without anti-aliasing + // See intersect_shapes_aa for more comments + //---------- + template + void sbool_subtract_shapes_bin(ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + sbool_add_span_bin add_functor; + sbool_combine_spans_empty combine_functor; + sbool_subtract_shapes(sg1, sg2, sl1, sl2, sl, ren, + add_functor, combine_functor); + } + + + + + + + //------------------------------------------------------------sbool_op_e + enum sbool_op_e + { + sbool_or, //----sbool_or + sbool_and, //----sbool_and + sbool_xor, //----sbool_xor + sbool_xor_saddle, //----sbool_xor_saddle + sbool_xor_abs_diff, //----sbool_xor_abs_diff + sbool_a_minus_b, //----sbool_a_minus_b + sbool_b_minus_a //----sbool_b_minus_a + }; + + + + + + + //----------------------------------------------sbool_combine_shapes_bin + template + void sbool_combine_shapes_bin(sbool_op_e op, + ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + switch(op) + { + case sbool_or : sbool_unite_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_and : sbool_intersect_shapes_bin(sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_xor : + case sbool_xor_saddle : + case sbool_xor_abs_diff: sbool_xor_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_a_minus_b : sbool_subtract_shapes_bin (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_b_minus_a : sbool_subtract_shapes_bin (sg2, sg1, sl2, sl1, sl, ren); break; + } + } + + + + + //-----------------------------------------------sbool_combine_shapes_aa + template + void sbool_combine_shapes_aa(sbool_op_e op, + ScanlineGen1& sg1, ScanlineGen2& sg2, + Scanline1& sl1, Scanline2& sl2, + Scanline& sl, Renderer& ren) + { + switch(op) + { + case sbool_or : sbool_unite_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_and : sbool_intersect_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_xor : sbool_xor_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_xor_saddle : sbool_xor_shapes_saddle_aa (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_xor_abs_diff: sbool_xor_shapes_abs_diff_aa(sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_a_minus_b : sbool_subtract_shapes_aa (sg1, sg2, sl1, sl2, sl, ren); break; + case sbool_b_minus_a : sbool_subtract_shapes_aa (sg2, sg1, sl2, sl1, sl, ren); break; + } + } + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_scanline_p.h b/desmume/src/windows/agg/include/agg_scanline_p.h new file mode 100644 index 000000000..2380bcefb --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_p.h @@ -0,0 +1,334 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates (scanline32_p) has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_SCANLINE_P_INCLUDED +#define AGG_SCANLINE_P_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + + //=============================================================scanline_p8 + // + // This is a general purpose scaline container which supports the interface + // used in the rasterizer::render(). See description of scanline_u8 + // for details. + // + //------------------------------------------------------------------------ + class scanline_p8 + { + public: + typedef scanline_p8 self_type; + typedef int8u cover_type; + typedef int16 coord_type; + + //-------------------------------------------------------------------- + struct span + { + coord_type x; + coord_type len; // If negative, it's a solid span, covers is valid + const cover_type* covers; + }; + + typedef span* iterator; + typedef const span* const_iterator; + + scanline_p8() : + m_last_x(0x7FFFFFF0), + m_covers(), + m_cover_ptr(0), + m_spans(), + m_cur_span(0) + { + } + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 3; + if(max_len > m_spans.size()) + { + m_spans.resize(max_len); + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_cur_span = &m_spans[0]; + m_cur_span->len = 0; + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + *m_cover_ptr = (cover_type)cover; + if(x == m_last_x+1 && m_cur_span->len > 0) + { + m_cur_span->len++; + } + else + { + m_cur_span++; + m_cur_span->covers = m_cover_ptr; + m_cur_span->x = (int16)x; + m_cur_span->len = 1; + } + m_last_x = x; + m_cover_ptr++; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + memcpy(m_cover_ptr, covers, len * sizeof(cover_type)); + if(x == m_last_x+1 && m_cur_span->len > 0) + { + m_cur_span->len += (int16)len; + } + else + { + m_cur_span++; + m_cur_span->covers = m_cover_ptr; + m_cur_span->x = (int16)x; + m_cur_span->len = (int16)len; + } + m_cover_ptr += len; + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + if(x == m_last_x+1 && + m_cur_span->len < 0 && + cover == *m_cur_span->covers) + { + m_cur_span->len -= (int16)len; + } + else + { + *m_cover_ptr = (cover_type)cover; + m_cur_span++; + m_cur_span->covers = m_cover_ptr++; + m_cur_span->x = (int16)x; + m_cur_span->len = (int16)(-int(len)); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_cur_span = &m_spans[0]; + m_cur_span->len = 0; + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return unsigned(m_cur_span - &m_spans[0]); } + const_iterator begin() const { return &m_spans[1]; } + + private: + scanline_p8(const self_type&); + const self_type& operator = (const self_type&); + + int m_last_x; + int m_y; + pod_array m_covers; + cover_type* m_cover_ptr; + pod_array m_spans; + span* m_cur_span; + }; + + + + + + + + + //==========================================================scanline32_p8 + class scanline32_p8 + { + public: + typedef scanline32_p8 self_type; + typedef int8u cover_type; + typedef int32 coord_type; + + struct span + { + span() {} + span(coord_type x_, coord_type len_, const cover_type* covers_) : + x(x_), len(len_), covers(covers_) {} + + coord_type x; + coord_type len; // If negative, it's a solid span, covers is valid + const cover_type* covers; + }; + typedef pod_bvector span_array_type; + + + //-------------------------------------------------------------------- + class const_iterator + { + public: + const_iterator(const span_array_type& spans) : + m_spans(spans), + m_span_idx(0) + {} + + const span& operator*() const { return m_spans[m_span_idx]; } + const span* operator->() const { return &m_spans[m_span_idx]; } + + void operator ++ () { ++m_span_idx; } + + private: + const span_array_type& m_spans; + unsigned m_span_idx; + }; + + //-------------------------------------------------------------------- + scanline32_p8() : + m_max_len(0), + m_last_x(0x7FFFFFF0), + m_covers(), + m_cover_ptr(0) + { + } + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 3; + if(max_len > m_covers.size()) + { + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + *m_cover_ptr = cover_type(cover); + if(x == m_last_x+1 && m_spans.size() && m_spans.last().len > 0) + { + m_spans.last().len++; + } + else + { + m_spans.add(span(coord_type(x), 1, m_cover_ptr)); + } + m_last_x = x; + m_cover_ptr++; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + memcpy(m_cover_ptr, covers, len * sizeof(cover_type)); + if(x == m_last_x+1 && m_spans.size() && m_spans.last().len > 0) + { + m_spans.last().len += coord_type(len); + } + else + { + m_spans.add(span(coord_type(x), coord_type(len), m_cover_ptr)); + } + m_cover_ptr += len; + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + if(x == m_last_x+1 && + m_spans.size() && + m_spans.last().len < 0 && + cover == *m_spans.last().covers) + { + m_spans.last().len -= coord_type(len); + } + else + { + *m_cover_ptr = cover_type(cover); + m_spans.add(span(coord_type(x), -coord_type(len), m_cover_ptr++)); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cover_ptr = &m_covers[0]; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return m_spans.size(); } + const_iterator begin() const { return const_iterator(m_spans); } + + private: + scanline32_p8(const self_type&); + const self_type& operator = (const self_type&); + + unsigned m_max_len; + int m_last_x; + int m_y; + pod_array m_covers; + cover_type* m_cover_ptr; + span_array_type m_spans; + }; + + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_scanline_storage_aa.h b/desmume/src/windows/agg/include/agg_scanline_storage_aa.h new file mode 100644 index 000000000..09e3434e1 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_storage_aa.h @@ -0,0 +1,824 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SCANLINE_STORAGE_AA_INCLUDED +#define AGG_SCANLINE_STORAGE_AA_INCLUDED + +#include +#include +#include +#include "agg_array.h" + + +namespace agg +{ + + //----------------------------------------------scanline_cell_storage + template class scanline_cell_storage + { + struct extra_span + { + unsigned len; + T* ptr; + }; + + public: + typedef T value_type; + + //--------------------------------------------------------------- + ~scanline_cell_storage() + { + remove_all(); + } + + //--------------------------------------------------------------- + scanline_cell_storage() : + m_cells(128-2), + m_extra_storage() + {} + + + // Copying + //--------------------------------------------------------------- + scanline_cell_storage(const scanline_cell_storage& v) : + m_cells(v.m_cells), + m_extra_storage() + { + copy_extra_storage(v); + } + + //--------------------------------------------------------------- + const scanline_cell_storage& + operator = (const scanline_cell_storage& v) + { + remove_all(); + m_cells = v.m_cells; + copy_extra_storage(v); + return *this; + } + + //--------------------------------------------------------------- + void remove_all() + { + int i; + for(i = m_extra_storage.size()-1; i >= 0; --i) + { + pod_allocator::deallocate(m_extra_storage[i].ptr, + m_extra_storage[i].len); + } + m_extra_storage.remove_all(); + m_cells.remove_all(); + } + + //--------------------------------------------------------------- + int add_cells(const T* cells, unsigned num_cells) + { + int idx = m_cells.allocate_continuous_block(num_cells); + if(idx >= 0) + { + T* ptr = &m_cells[idx]; + memcpy(ptr, cells, sizeof(T) * num_cells); + return idx; + } + extra_span s; + s.len = num_cells; + s.ptr = pod_allocator::allocate(num_cells); + memcpy(s.ptr, cells, sizeof(T) * num_cells); + m_extra_storage.add(s); + return -int(m_extra_storage.size()); + } + + //--------------------------------------------------------------- + const T* operator [] (int idx) const + { + if(idx >= 0) + { + if((unsigned)idx >= m_cells.size()) return 0; + return &m_cells[(unsigned)idx]; + } + unsigned i = unsigned(-idx - 1); + if(i >= m_extra_storage.size()) return 0; + return m_extra_storage[i].ptr; + } + + //--------------------------------------------------------------- + T* operator [] (int idx) + { + if(idx >= 0) + { + if((unsigned)idx >= m_cells.size()) return 0; + return &m_cells[(unsigned)idx]; + } + unsigned i = unsigned(-idx - 1); + if(i >= m_extra_storage.size()) return 0; + return m_extra_storage[i].ptr; + } + + private: + void copy_extra_storage(const scanline_cell_storage& v) + { + unsigned i; + for(i = 0; i < v.m_extra_storage.size(); ++i) + { + const extra_span& src = v.m_extra_storage[i]; + extra_span dst; + dst.len = src.len; + dst.ptr = pod_allocator::allocate(dst.len); + memcpy(dst.ptr, src.ptr, dst.len * sizeof(T)); + m_extra_storage.add(dst); + } + } + + pod_bvector m_cells; + pod_bvector m_extra_storage; + }; + + + + + + + //-----------------------------------------------scanline_storage_aa + template class scanline_storage_aa + { + public: + typedef T cover_type; + + //--------------------------------------------------------------- + struct span_data + { + int32 x; + int32 len; // If negative, it's a solid span, covers is valid + int covers_id; // The index of the cells in the scanline_cell_storage + }; + + //--------------------------------------------------------------- + struct scanline_data + { + int y; + unsigned num_spans; + unsigned start_span; + }; + + + //--------------------------------------------------------------- + class embedded_scanline + { + public: + + //----------------------------------------------------------- + class const_iterator + { + public: + struct span + { + int32 x; + int32 len; // If negative, it's a solid span, covers is valid + const T* covers; + }; + + const_iterator() : m_storage(0) {} + const_iterator(const embedded_scanline& sl) : + m_storage(sl.m_storage), + m_span_idx(sl.m_scanline.start_span) + { + init_span(); + } + + const span& operator*() const { return m_span; } + const span* operator->() const { return &m_span; } + + void operator ++ () + { + ++m_span_idx; + init_span(); + } + + private: + void init_span() + { + const span_data& s = m_storage->span_by_index(m_span_idx); + m_span.x = s.x; + m_span.len = s.len; + m_span.covers = m_storage->covers_by_index(s.covers_id); + } + + const scanline_storage_aa* m_storage; + unsigned m_span_idx; + span m_span; + }; + + friend class const_iterator; + + + //----------------------------------------------------------- + embedded_scanline(const scanline_storage_aa& storage) : + m_storage(&storage) + { + init(0); + } + + //----------------------------------------------------------- + void reset(int, int) {} + unsigned num_spans() const { return m_scanline.num_spans; } + int y() const { return m_scanline.y; } + const_iterator begin() const { return const_iterator(*this); } + + //----------------------------------------------------------- + void init(unsigned scanline_idx) + { + m_scanline_idx = scanline_idx; + m_scanline = m_storage->scanline_by_index(m_scanline_idx); + } + + private: + const scanline_storage_aa* m_storage; + scanline_data m_scanline; + unsigned m_scanline_idx; + }; + + + //--------------------------------------------------------------- + scanline_storage_aa() : + m_covers(), + m_spans(256-2), // Block increment size + m_scanlines(), + m_min_x( 0x7FFFFFFF), + m_min_y( 0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF), + m_cur_scanline(0) + { + m_fake_scanline.y = 0; + m_fake_scanline.num_spans = 0; + m_fake_scanline.start_span = 0; + m_fake_span.x = 0; + m_fake_span.len = 0; + m_fake_span.covers_id = 0; + } + + // Renderer Interface + //--------------------------------------------------------------- + void prepare() + { + m_covers.remove_all(); + m_scanlines.remove_all(); + m_spans.remove_all(); + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; + m_max_x = -0x7FFFFFFF; + m_max_y = -0x7FFFFFFF; + m_cur_scanline = 0; + } + + //--------------------------------------------------------------- + template void render(const Scanline& sl) + { + scanline_data sl_this; + + int y = sl.y(); + if(y < m_min_y) m_min_y = y; + if(y > m_max_y) m_max_y = y; + + sl_this.y = y; + sl_this.num_spans = sl.num_spans(); + sl_this.start_span = m_spans.size(); + typename Scanline::const_iterator span_iterator = sl.begin(); + + unsigned num_spans = sl_this.num_spans; + for(;;) + { + span_data sp; + + sp.x = span_iterator->x; + sp.len = span_iterator->len; + int len = abs(int(sp.len)); + sp.covers_id = + m_covers.add_cells(span_iterator->covers, + unsigned(len)); + m_spans.add(sp); + int x1 = sp.x; + int x2 = sp.x + len - 1; + if(x1 < m_min_x) m_min_x = x1; + if(x2 > m_max_x) m_max_x = x2; + if(--num_spans == 0) break; + ++span_iterator; + } + m_scanlines.add(sl_this); + } + + + //--------------------------------------------------------------- + // Iterate scanlines interface + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + //--------------------------------------------------------------- + bool rewind_scanlines() + { + m_cur_scanline = 0; + return m_scanlines.size() > 0; + } + + + //--------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + sl.reset_spans(); + for(;;) + { + if(m_cur_scanline >= m_scanlines.size()) return false; + const scanline_data& sl_this = m_scanlines[m_cur_scanline]; + + unsigned num_spans = sl_this.num_spans; + unsigned span_idx = sl_this.start_span; + do + { + const span_data& sp = m_spans[span_idx++]; + const T* covers = covers_by_index(sp.covers_id); + if(sp.len < 0) + { + sl.add_span(sp.x, unsigned(-sp.len), *covers); + } + else + { + sl.add_cells(sp.x, sp.len, covers); + } + } + while(--num_spans); + ++m_cur_scanline; + if(sl.num_spans()) + { + sl.finalize(sl_this.y); + break; + } + } + return true; + } + + + //--------------------------------------------------------------- + // Specialization for embedded_scanline + bool sweep_scanline(embedded_scanline& sl) + { + do + { + if(m_cur_scanline >= m_scanlines.size()) return false; + sl.init(m_cur_scanline); + ++m_cur_scanline; + } + while(sl.num_spans() == 0); + return true; + } + + //--------------------------------------------------------------- + unsigned byte_size() const + { + unsigned i; + unsigned size = sizeof(int32) * 4; // min_x, min_y, max_x, max_y + + for(i = 0; i < m_scanlines.size(); ++i) + { + size += sizeof(int32) * 3; // scanline size in bytes, Y, num_spans + + const scanline_data& sl_this = m_scanlines[i]; + + unsigned num_spans = sl_this.num_spans; + unsigned span_idx = sl_this.start_span; + do + { + const span_data& sp = m_spans[span_idx++]; + + size += sizeof(int32) * 2; // X, span_len + if(sp.len < 0) + { + size += sizeof(T); // cover + } + else + { + size += sizeof(T) * unsigned(sp.len); // covers + } + } + while(--num_spans); + } + return size; + } + + + //--------------------------------------------------------------- + static void write_int32(int8u* dst, int32 val) + { + dst[0] = ((const int8u*)&val)[0]; + dst[1] = ((const int8u*)&val)[1]; + dst[2] = ((const int8u*)&val)[2]; + dst[3] = ((const int8u*)&val)[3]; + } + + + //--------------------------------------------------------------- + void serialize(int8u* data) const + { + unsigned i; + + write_int32(data, min_x()); // min_x + data += sizeof(int32); + write_int32(data, min_y()); // min_y + data += sizeof(int32); + write_int32(data, max_x()); // max_x + data += sizeof(int32); + write_int32(data, max_y()); // max_y + data += sizeof(int32); + + for(i = 0; i < m_scanlines.size(); ++i) + { + const scanline_data& sl_this = m_scanlines[i]; + + int8u* size_ptr = data; + data += sizeof(int32); // Reserve space for scanline size in bytes + + write_int32(data, sl_this.y); // Y + data += sizeof(int32); + + write_int32(data, sl_this.num_spans); // num_spans + data += sizeof(int32); + + unsigned num_spans = sl_this.num_spans; + unsigned span_idx = sl_this.start_span; + do + { + const span_data& sp = m_spans[span_idx++]; + const T* covers = covers_by_index(sp.covers_id); + + write_int32(data, sp.x); // X + data += sizeof(int32); + + write_int32(data, sp.len); // span_len + data += sizeof(int32); + + if(sp.len < 0) + { + memcpy(data, covers, sizeof(T)); + data += sizeof(T); + } + else + { + memcpy(data, covers, unsigned(sp.len) * sizeof(T)); + data += sizeof(T) * unsigned(sp.len); + } + } + while(--num_spans); + write_int32(size_ptr, int32(unsigned(data - size_ptr))); + } + } + + + //--------------------------------------------------------------- + const scanline_data& scanline_by_index(unsigned i) const + { + return (i < m_scanlines.size()) ? m_scanlines[i] : m_fake_scanline; + } + + //--------------------------------------------------------------- + const span_data& span_by_index(unsigned i) const + { + return (i < m_spans.size()) ? m_spans[i] : m_fake_span; + } + + //--------------------------------------------------------------- + const T* covers_by_index(int i) const + { + return m_covers[i]; + } + + private: + scanline_cell_storage m_covers; + pod_bvector m_spans; + pod_bvector m_scanlines; + span_data m_fake_span; + scanline_data m_fake_scanline; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + unsigned m_cur_scanline; + }; + + + typedef scanline_storage_aa scanline_storage_aa8; //--------scanline_storage_aa8 + typedef scanline_storage_aa scanline_storage_aa16; //--------scanline_storage_aa16 + typedef scanline_storage_aa scanline_storage_aa32; //--------scanline_storage_aa32 + + + + + //------------------------------------------serialized_scanlines_adaptor_aa + template class serialized_scanlines_adaptor_aa + { + public: + typedef T cover_type; + + //--------------------------------------------------------------------- + class embedded_scanline + { + public: + typedef T cover_type; + + //----------------------------------------------------------------- + class const_iterator + { + public: + struct span + { + int32 x; + int32 len; // If negative, it's a solid span, "covers" is valid + const T* covers; + }; + + const_iterator() : m_ptr(0) {} + const_iterator(const embedded_scanline& sl) : + m_ptr(sl.m_ptr), + m_dx(sl.m_dx) + { + init_span(); + } + + const span& operator*() const { return m_span; } + const span* operator->() const { return &m_span; } + + void operator ++ () + { + if(m_span.len < 0) + { + m_ptr += sizeof(T); + } + else + { + m_ptr += m_span.len * sizeof(T); + } + init_span(); + } + + private: + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + void init_span() + { + m_span.x = read_int32() + m_dx; + m_span.len = read_int32(); + m_span.covers = m_ptr; + } + + const int8u* m_ptr; + span m_span; + int m_dx; + }; + + friend class const_iterator; + + + //----------------------------------------------------------------- + embedded_scanline() : m_ptr(0), m_y(0), m_num_spans(0) {} + + //----------------------------------------------------------------- + void reset(int, int) {} + unsigned num_spans() const { return m_num_spans; } + int y() const { return m_y; } + const_iterator begin() const { return const_iterator(*this); } + + + private: + //----------------------------------------------------------------- + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + public: + //----------------------------------------------------------------- + void init(const int8u* ptr, int dx, int dy) + { + m_ptr = ptr; + m_y = read_int32() + dy; + m_num_spans = unsigned(read_int32()); + m_dx = dx; + } + + private: + const int8u* m_ptr; + int m_y; + unsigned m_num_spans; + int m_dx; + }; + + + + public: + //-------------------------------------------------------------------- + serialized_scanlines_adaptor_aa() : + m_data(0), + m_end(0), + m_ptr(0), + m_dx(0), + m_dy(0), + m_min_x(0x7FFFFFFF), + m_min_y(0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF) + {} + + //-------------------------------------------------------------------- + serialized_scanlines_adaptor_aa(const int8u* data, unsigned size, + double dx, double dy) : + m_data(data), + m_end(data + size), + m_ptr(data), + m_dx(iround(dx)), + m_dy(iround(dy)), + m_min_x(0x7FFFFFFF), + m_min_y(0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF) + {} + + //-------------------------------------------------------------------- + void init(const int8u* data, unsigned size, double dx, double dy) + { + m_data = data; + m_end = data + size; + m_ptr = data; + m_dx = iround(dx); + m_dy = iround(dy); + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; + m_max_x = -0x7FFFFFFF; + m_max_y = -0x7FFFFFFF; + } + + private: + //-------------------------------------------------------------------- + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + //-------------------------------------------------------------------- + unsigned read_int32u() + { + int32u val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + public: + // Iterate scanlines interface + //-------------------------------------------------------------------- + bool rewind_scanlines() + { + m_ptr = m_data; + if(m_ptr < m_end) + { + m_min_x = read_int32() + m_dx; + m_min_y = read_int32() + m_dy; + m_max_x = read_int32() + m_dx; + m_max_y = read_int32() + m_dy; + } + return m_ptr < m_end; + } + + //-------------------------------------------------------------------- + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + //-------------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + sl.reset_spans(); + for(;;) + { + if(m_ptr >= m_end) return false; + + read_int32(); // Skip scanline size in bytes + int y = read_int32() + m_dy; + unsigned num_spans = read_int32(); + + do + { + int x = read_int32() + m_dx; + int len = read_int32(); + + if(len < 0) + { + sl.add_span(x, unsigned(-len), *m_ptr); + m_ptr += sizeof(T); + } + else + { + sl.add_cells(x, len, m_ptr); + m_ptr += len * sizeof(T); + } + } + while(--num_spans); + + if(sl.num_spans()) + { + sl.finalize(y); + break; + } + } + return true; + } + + + //-------------------------------------------------------------------- + // Specialization for embedded_scanline + bool sweep_scanline(embedded_scanline& sl) + { + do + { + if(m_ptr >= m_end) return false; + + unsigned byte_size = read_int32u(); + sl.init(m_ptr, m_dx, m_dy); + m_ptr += byte_size - sizeof(int32); + } + while(sl.num_spans() == 0); + return true; + } + + private: + const int8u* m_data; + const int8u* m_end; + const int8u* m_ptr; + int m_dx; + int m_dy; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + }; + + + + typedef serialized_scanlines_adaptor_aa serialized_scanlines_adaptor_aa8; //----serialized_scanlines_adaptor_aa8 + typedef serialized_scanlines_adaptor_aa serialized_scanlines_adaptor_aa16; //----serialized_scanlines_adaptor_aa16 + typedef serialized_scanlines_adaptor_aa serialized_scanlines_adaptor_aa32; //----serialized_scanlines_adaptor_aa32 + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_scanline_storage_bin.h b/desmume/src/windows/agg/include/agg_scanline_storage_bin.h new file mode 100644 index 000000000..bdbbfa47e --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_storage_bin.h @@ -0,0 +1,595 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + + +#ifndef AGG_SCANLINE_STORAGE_BIN_INCLUDED +#define AGG_SCANLINE_STORAGE_BIN_INCLUDED + +#include +#include +#include +#include "agg_array.h" + + +namespace agg +{ + + //-----------------------------------------------scanline_storage_bin + class scanline_storage_bin + { + public: + //--------------------------------------------------------------- + struct span_data + { + int32 x; + int32 len; + }; + + //--------------------------------------------------------------- + struct scanline_data + { + int y; + unsigned num_spans; + unsigned start_span; + }; + + + //--------------------------------------------------------------- + class embedded_scanline + { + public: + + //----------------------------------------------------------- + class const_iterator + { + public: + const_iterator() : m_storage(0) {} + const_iterator(const embedded_scanline& sl) : + m_storage(sl.m_storage), + m_span_idx(sl.m_scanline.start_span) + { + m_span = m_storage->span_by_index(m_span_idx); + } + + const span_data& operator*() const { return m_span; } + const span_data* operator->() const { return &m_span; } + + void operator ++ () + { + ++m_span_idx; + m_span = m_storage->span_by_index(m_span_idx); + } + + private: + const scanline_storage_bin* m_storage; + unsigned m_span_idx; + span_data m_span; + }; + + friend class const_iterator; + + + //----------------------------------------------------------- + embedded_scanline(const scanline_storage_bin& storage) : + m_storage(&storage) + { + setup(0); + } + + //----------------------------------------------------------- + void reset(int, int) {} + unsigned num_spans() const { return m_scanline.num_spans; } + int y() const { return m_scanline.y; } + const_iterator begin() const { return const_iterator(*this); } + + //----------------------------------------------------------- + void setup(unsigned scanline_idx) + { + m_scanline_idx = scanline_idx; + m_scanline = m_storage->scanline_by_index(m_scanline_idx); + } + + private: + const scanline_storage_bin* m_storage; + scanline_data m_scanline; + unsigned m_scanline_idx; + }; + + + //--------------------------------------------------------------- + scanline_storage_bin() : + m_spans(256-2), // Block increment size + m_scanlines(), + m_min_x( 0x7FFFFFFF), + m_min_y( 0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF), + m_cur_scanline(0) + { + m_fake_scanline.y = 0; + m_fake_scanline.num_spans = 0; + m_fake_scanline.start_span = 0; + m_fake_span.x = 0; + m_fake_span.len = 0; + } + + // Renderer Interface + //--------------------------------------------------------------- + void prepare() + { + m_scanlines.remove_all(); + m_spans.remove_all(); + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; + m_max_x = -0x7FFFFFFF; + m_max_y = -0x7FFFFFFF; + m_cur_scanline = 0; + } + + //--------------------------------------------------------------- + template void render(const Scanline& sl) + { + scanline_data sl_this; + + int y = sl.y(); + if(y < m_min_y) m_min_y = y; + if(y > m_max_y) m_max_y = y; + + sl_this.y = y; + sl_this.num_spans = sl.num_spans(); + sl_this.start_span = m_spans.size(); + typename Scanline::const_iterator span_iterator = sl.begin(); + + unsigned num_spans = sl_this.num_spans; + for(;;) + { + span_data sp; + sp.x = span_iterator->x; + sp.len = (int32)abs((int)(span_iterator->len)); + m_spans.add(sp); + int x1 = sp.x; + int x2 = sp.x + sp.len - 1; + if(x1 < m_min_x) m_min_x = x1; + if(x2 > m_max_x) m_max_x = x2; + if(--num_spans == 0) break; + ++span_iterator; + } + m_scanlines.add(sl_this); + } + + + //--------------------------------------------------------------- + // Iterate scanlines interface + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + //--------------------------------------------------------------- + bool rewind_scanlines() + { + m_cur_scanline = 0; + return m_scanlines.size() > 0; + } + + + //--------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + sl.reset_spans(); + for(;;) + { + if(m_cur_scanline >= m_scanlines.size()) return false; + const scanline_data& sl_this = m_scanlines[m_cur_scanline]; + + unsigned num_spans = sl_this.num_spans; + unsigned span_idx = sl_this.start_span; + do + { + const span_data& sp = m_spans[span_idx++]; + sl.add_span(sp.x, sp.len, cover_full); + } + while(--num_spans); + + ++m_cur_scanline; + if(sl.num_spans()) + { + sl.finalize(sl_this.y); + break; + } + } + return true; + } + + + //--------------------------------------------------------------- + // Specialization for embedded_scanline + bool sweep_scanline(embedded_scanline& sl) + { + do + { + if(m_cur_scanline >= m_scanlines.size()) return false; + sl.setup(m_cur_scanline); + ++m_cur_scanline; + } + while(sl.num_spans() == 0); + return true; + } + + + //--------------------------------------------------------------- + unsigned byte_size() const + { + unsigned i; + unsigned size = sizeof(int32) * 4; // min_x, min_y, max_x, max_y + + for(i = 0; i < m_scanlines.size(); ++i) + { + size += sizeof(int32) * 2 + // Y, num_spans + unsigned(m_scanlines[i].num_spans) * sizeof(int32) * 2; // X, span_len + } + return size; + } + + + //--------------------------------------------------------------- + static void write_int32(int8u* dst, int32 val) + { + dst[0] = ((const int8u*)&val)[0]; + dst[1] = ((const int8u*)&val)[1]; + dst[2] = ((const int8u*)&val)[2]; + dst[3] = ((const int8u*)&val)[3]; + } + + + //--------------------------------------------------------------- + void serialize(int8u* data) const + { + unsigned i; + + write_int32(data, min_x()); // min_x + data += sizeof(int32); + write_int32(data, min_y()); // min_y + data += sizeof(int32); + write_int32(data, max_x()); // max_x + data += sizeof(int32); + write_int32(data, max_y()); // max_y + data += sizeof(int32); + + for(i = 0; i < m_scanlines.size(); ++i) + { + const scanline_data& sl_this = m_scanlines[i]; + + write_int32(data, sl_this.y); // Y + data += sizeof(int32); + + write_int32(data, sl_this.num_spans); // num_spans + data += sizeof(int32); + + unsigned num_spans = sl_this.num_spans; + unsigned span_idx = sl_this.start_span; + do + { + const span_data& sp = m_spans[span_idx++]; + + write_int32(data, sp.x); // X + data += sizeof(int32); + + write_int32(data, sp.len); // len + data += sizeof(int32); + } + while(--num_spans); + } + } + + + //--------------------------------------------------------------- + const scanline_data& scanline_by_index(unsigned i) const + { + return (i < m_scanlines.size()) ? m_scanlines[i] : m_fake_scanline; + } + + //--------------------------------------------------------------- + const span_data& span_by_index(unsigned i) const + { + return (i < m_spans.size()) ? m_spans[i] : m_fake_span; + } + + + private: + pod_bvector m_spans; + pod_bvector m_scanlines; + span_data m_fake_span; + scanline_data m_fake_scanline; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + unsigned m_cur_scanline; + }; + + + + + + + + + + + + + + //---------------------------------------serialized_scanlines_adaptor_bin + class serialized_scanlines_adaptor_bin + { + public: + typedef bool cover_type; + + //-------------------------------------------------------------------- + class embedded_scanline + { + public: + + //---------------------------------------------------------------- + class const_iterator + { + public: + struct span + { + int32 x; + int32 len; + }; + + const_iterator() : m_ptr(0) {} + const_iterator(const embedded_scanline& sl) : + m_ptr(sl.m_ptr), + m_dx(sl.m_dx) + { + m_span.x = read_int32() + m_dx; + m_span.len = read_int32(); + } + + const span& operator*() const { return m_span; } + const span* operator->() const { return &m_span; } + + void operator ++ () + { + m_span.x = read_int32() + m_dx; + m_span.len = read_int32(); + } + + private: + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + const int8u* m_ptr; + span m_span; + int m_dx; + }; + + friend class const_iterator; + + + //---------------------------------------------------------------- + embedded_scanline() : m_ptr(0), m_y(0), m_num_spans(0) {} + + //---------------------------------------------------------------- + void reset(int, int) {} + unsigned num_spans() const { return m_num_spans; } + int y() const { return m_y; } + const_iterator begin() const { return const_iterator(*this); } + + + private: + //---------------------------------------------------------------- + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + public: + //---------------------------------------------------------------- + void init(const int8u* ptr, int dx, int dy) + { + m_ptr = ptr; + m_y = read_int32() + dy; + m_num_spans = unsigned(read_int32()); + m_dx = dx; + } + + private: + const int8u* m_ptr; + int m_y; + unsigned m_num_spans; + int m_dx; + }; + + + + public: + //-------------------------------------------------------------------- + serialized_scanlines_adaptor_bin() : + m_data(0), + m_end(0), + m_ptr(0), + m_dx(0), + m_dy(0), + m_min_x(0x7FFFFFFF), + m_min_y(0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF) + {} + + //-------------------------------------------------------------------- + serialized_scanlines_adaptor_bin(const int8u* data, unsigned size, + double dx, double dy) : + m_data(data), + m_end(data + size), + m_ptr(data), + m_dx(iround(dx)), + m_dy(iround(dy)), + m_min_x(0x7FFFFFFF), + m_min_y(0x7FFFFFFF), + m_max_x(-0x7FFFFFFF), + m_max_y(-0x7FFFFFFF) + {} + + //-------------------------------------------------------------------- + void init(const int8u* data, unsigned size, double dx, double dy) + { + m_data = data; + m_end = data + size; + m_ptr = data; + m_dx = iround(dx); + m_dy = iround(dy); + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; + m_max_x = -0x7FFFFFFF; + m_max_y = -0x7FFFFFFF; + } + + private: + //-------------------------------------------------------------------- + int read_int32() + { + int32 val; + ((int8u*)&val)[0] = *m_ptr++; + ((int8u*)&val)[1] = *m_ptr++; + ((int8u*)&val)[2] = *m_ptr++; + ((int8u*)&val)[3] = *m_ptr++; + return val; + } + + public: + // Iterate scanlines interface + //-------------------------------------------------------------------- + bool rewind_scanlines() + { + m_ptr = m_data; + if(m_ptr < m_end) + { + m_min_x = read_int32() + m_dx; + m_min_y = read_int32() + m_dy; + m_max_x = read_int32() + m_dx; + m_max_y = read_int32() + m_dy; + } + return m_ptr < m_end; + } + + //-------------------------------------------------------------------- + int min_x() const { return m_min_x; } + int min_y() const { return m_min_y; } + int max_x() const { return m_max_x; } + int max_y() const { return m_max_y; } + + //-------------------------------------------------------------------- + template bool sweep_scanline(Scanline& sl) + { + sl.reset_spans(); + for(;;) + { + if(m_ptr >= m_end) return false; + + int y = read_int32() + m_dy; + unsigned num_spans = read_int32(); + + do + { + int x = read_int32() + m_dx; + int len = read_int32(); + + if(len < 0) len = -len; + sl.add_span(x, unsigned(len), cover_full); + } + while(--num_spans); + + if(sl.num_spans()) + { + sl.finalize(y); + break; + } + } + return true; + } + + + //-------------------------------------------------------------------- + // Specialization for embedded_scanline + bool sweep_scanline(embedded_scanline& sl) + { + do + { + if(m_ptr >= m_end) return false; + + sl.init(m_ptr, m_dx, m_dy); + + // Jump to the next scanline + //-------------------------- + read_int32(); // Y + int num_spans = read_int32(); // num_spans + m_ptr += num_spans * sizeof(int32) * 2; + } + while(sl.num_spans() == 0); + return true; + } + + private: + const int8u* m_data; + const int8u* m_end; + const int8u* m_ptr; + int m_dx; + int m_dy; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + }; + + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_scanline_u.h b/desmume/src/windows/agg/include/agg_scanline_u.h new file mode 100644 index 000000000..c3443b5f2 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_scanline_u.h @@ -0,0 +1,508 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for 32-bit screen coordinates (scanline32_u) has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SCANLINE_U_INCLUDED +#define AGG_SCANLINE_U_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + //=============================================================scanline_u8 + // + // Unpacked scanline container class + // + // This class is used to transfer data from a scanline rasterizer + // to the rendering buffer. It's organized very simple. The class stores + // information of horizontal spans to render it into a pixel-map buffer. + // Each span has staring X, length, and an array of bytes that determine the + // cover-values for each pixel. + // Before using this class you should know the minimal and maximal pixel + // coordinates of your scanline. The protocol of using is: + // 1. reset(min_x, max_x) + // 2. add_cell() / add_span() - accumulate scanline. + // When forming one scanline the next X coordinate must be always greater + // than the last stored one, i.e. it works only with ordered coordinates. + // 3. Call finalize(y) and render the scanline. + // 3. Call reset_spans() to prepare for the new scanline. + // + // 4. Rendering: + // + // Scanline provides an iterator class that allows you to extract + // the spans and the cover values for each pixel. Be aware that clipping + // has not been done yet, so you should perform it yourself. + // Use scanline_u8::iterator to render spans: + //------------------------------------------------------------------------- + // + // int y = sl.y(); // Y-coordinate of the scanline + // + // ************************************ + // ...Perform vertical clipping here... + // ************************************ + // + // scanline_u8::const_iterator span = sl.begin(); + // + // unsigned char* row = m_rbuf->row(y); // The the address of the beginning + // // of the current row + // + // unsigned num_spans = sl.num_spans(); // Number of spans. It's guaranteed that + // // num_spans is always greater than 0. + // + // do + // { + // const scanline_u8::cover_type* covers = + // span->covers; // The array of the cover values + // + // int num_pix = span->len; // Number of pixels of the span. + // // Always greater than 0, still it's + // // better to use "int" instead of + // // "unsigned" because it's more + // // convenient for clipping + // int x = span->x; + // + // ************************************** + // ...Perform horizontal clipping here... + // ...you have x, covers, and pix_count.. + // ************************************** + // + // unsigned char* dst = row + x; // Calculate the start address of the row. + // // In this case we assume a simple + // // grayscale image 1-byte per pixel. + // do + // { + // *dst++ = *covers++; // Hypotetical rendering. + // } + // while(--num_pix); + // + // ++span; + // } + // while(--num_spans); // num_spans cannot be 0, so this loop is quite safe + //------------------------------------------------------------------------ + // + // The question is: why should we accumulate the whole scanline when we + // could render just separate spans when they're ready? + // That's because using the scanline is generally faster. When is consists + // of more than one span the conditions for the processor cash system + // are better, because switching between two different areas of memory + // (that can be very large) occurs less frequently. + //------------------------------------------------------------------------ + class scanline_u8 + { + public: + typedef scanline_u8 self_type; + typedef int8u cover_type; + typedef int16 coord_type; + + //-------------------------------------------------------------------- + struct span + { + coord_type x; + coord_type len; + cover_type* covers; + }; + + typedef span* iterator; + typedef const span* const_iterator; + + //-------------------------------------------------------------------- + scanline_u8() : + m_min_x(0), + m_last_x(0x7FFFFFF0), + m_cur_span(0) + {} + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 2; + if(max_len > m_spans.size()) + { + m_spans.resize(max_len); + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_min_x = min_x; + m_cur_span = &m_spans[0]; + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + x -= m_min_x; + m_covers[x] = (cover_type)cover; + if(x == m_last_x+1) + { + m_cur_span->len++; + } + else + { + m_cur_span++; + m_cur_span->x = (coord_type)(x + m_min_x); + m_cur_span->len = 1; + m_cur_span->covers = &m_covers[x]; + } + m_last_x = x; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + x -= m_min_x; + memcpy(&m_covers[x], covers, len * sizeof(cover_type)); + if(x == m_last_x+1) + { + m_cur_span->len += (coord_type)len; + } + else + { + m_cur_span++; + m_cur_span->x = (coord_type)(x + m_min_x); + m_cur_span->len = (coord_type)len; + m_cur_span->covers = &m_covers[x]; + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + x -= m_min_x; + memset(&m_covers[x], cover, len); + if(x == m_last_x+1) + { + m_cur_span->len += (coord_type)len; + } + else + { + m_cur_span++; + m_cur_span->x = (coord_type)(x + m_min_x); + m_cur_span->len = (coord_type)len; + m_cur_span->covers = &m_covers[x]; + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_cur_span = &m_spans[0]; + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return unsigned(m_cur_span - &m_spans[0]); } + const_iterator begin() const { return &m_spans[1]; } + iterator begin() { return &m_spans[1]; } + + private: + scanline_u8(const self_type&); + const self_type& operator = (const self_type&); + + private: + int m_min_x; + int m_last_x; + int m_y; + pod_array m_covers; + pod_array m_spans; + span* m_cur_span; + }; + + + + + //==========================================================scanline_u8_am + // + // The scanline container with alpha-masking + // + //------------------------------------------------------------------------ + template + class scanline_u8_am : public scanline_u8 + { + public: + typedef scanline_u8 base_type; + typedef AlphaMask alpha_mask_type; + typedef base_type::cover_type cover_type; + typedef base_type::coord_type coord_type; + + scanline_u8_am() : base_type(), m_alpha_mask(0) {} + scanline_u8_am(const AlphaMask& am) : base_type(), m_alpha_mask(&am) {} + + //-------------------------------------------------------------------- + void finalize(int span_y) + { + base_type::finalize(span_y); + if(m_alpha_mask) + { + typename base_type::iterator span = base_type::begin(); + unsigned count = base_type::num_spans(); + do + { + m_alpha_mask->combine_hspan(span->x, + base_type::y(), + span->covers, + span->len); + ++span; + } + while(--count); + } + } + + private: + const AlphaMask* m_alpha_mask; + }; + + + + + //===========================================================scanline32_u8 + class scanline32_u8 + { + public: + typedef scanline32_u8 self_type; + typedef int8u cover_type; + typedef int32 coord_type; + + //-------------------------------------------------------------------- + struct span + { + span() {} + span(coord_type x_, coord_type len_, cover_type* covers_) : + x(x_), len(len_), covers(covers_) {} + + coord_type x; + coord_type len; + cover_type* covers; + }; + + typedef pod_bvector span_array_type; + + //-------------------------------------------------------------------- + class const_iterator + { + public: + const_iterator(const span_array_type& spans) : + m_spans(spans), + m_span_idx(0) + {} + + const span& operator*() const { return m_spans[m_span_idx]; } + const span* operator->() const { return &m_spans[m_span_idx]; } + + void operator ++ () { ++m_span_idx; } + + private: + const span_array_type& m_spans; + unsigned m_span_idx; + }; + + //-------------------------------------------------------------------- + class iterator + { + public: + iterator(span_array_type& spans) : + m_spans(spans), + m_span_idx(0) + {} + + span& operator*() { return m_spans[m_span_idx]; } + span* operator->() { return &m_spans[m_span_idx]; } + + void operator ++ () { ++m_span_idx; } + + private: + span_array_type& m_spans; + unsigned m_span_idx; + }; + + + + //-------------------------------------------------------------------- + scanline32_u8() : + m_min_x(0), + m_last_x(0x7FFFFFF0), + m_covers() + {} + + //-------------------------------------------------------------------- + void reset(int min_x, int max_x) + { + unsigned max_len = max_x - min_x + 2; + if(max_len > m_covers.size()) + { + m_covers.resize(max_len); + } + m_last_x = 0x7FFFFFF0; + m_min_x = min_x; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + void add_cell(int x, unsigned cover) + { + x -= m_min_x; + m_covers[x] = cover_type(cover); + if(x == m_last_x+1) + { + m_spans.last().len++; + } + else + { + m_spans.add(span(coord_type(x + m_min_x), 1, &m_covers[x])); + } + m_last_x = x; + } + + //-------------------------------------------------------------------- + void add_cells(int x, unsigned len, const cover_type* covers) + { + x -= m_min_x; + memcpy(&m_covers[x], covers, len * sizeof(cover_type)); + if(x == m_last_x+1) + { + m_spans.last().len += coord_type(len); + } + else + { + m_spans.add(span(coord_type(x + m_min_x), + coord_type(len), + &m_covers[x])); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void add_span(int x, unsigned len, unsigned cover) + { + x -= m_min_x; + memset(&m_covers[x], cover, len); + if(x == m_last_x+1) + { + m_spans.last().len += coord_type(len); + } + else + { + m_spans.add(span(coord_type(x + m_min_x), + coord_type(len), + &m_covers[x])); + } + m_last_x = x + len - 1; + } + + //-------------------------------------------------------------------- + void finalize(int y) + { + m_y = y; + } + + //-------------------------------------------------------------------- + void reset_spans() + { + m_last_x = 0x7FFFFFF0; + m_spans.remove_all(); + } + + //-------------------------------------------------------------------- + int y() const { return m_y; } + unsigned num_spans() const { return m_spans.size(); } + const_iterator begin() const { return const_iterator(m_spans); } + iterator begin() { return iterator(m_spans); } + + private: + scanline32_u8(const self_type&); + const self_type& operator = (const self_type&); + + private: + int m_min_x; + int m_last_x; + int m_y; + pod_array m_covers; + span_array_type m_spans; + }; + + + + + //========================================================scanline32_u8_am + // + // The scanline container with alpha-masking + // + //------------------------------------------------------------------------ + template + class scanline32_u8_am : public scanline32_u8 + { + public: + typedef scanline32_u8 base_type; + typedef AlphaMask alpha_mask_type; + typedef base_type::cover_type cover_type; + typedef base_type::coord_type coord_type; + + + scanline32_u8_am() : base_type(), m_alpha_mask(0) {} + scanline32_u8_am(const AlphaMask& am) : base_type(), m_alpha_mask(&am) {} + + //-------------------------------------------------------------------- + void finalize(int span_y) + { + base_type::finalize(span_y); + if(m_alpha_mask) + { + typename base_type::iterator span = base_type::begin(); + unsigned count = base_type::num_spans(); + do + { + m_alpha_mask->combine_hspan(span->x, + base_type::y(), + span->covers, + span->len); + ++span; + } + while(--count); + } + } + + private: + const AlphaMask* m_alpha_mask; + }; + + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_shorten_path.h b/desmume/src/windows/agg/include/agg_shorten_path.h new file mode 100644 index 000000000..c914c42b7 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_shorten_path.h @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SHORTEN_PATH_INCLUDED +#define AGG_SHORTEN_PATH_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + //===========================================================shorten_path + template + void shorten_path(VertexSequence& vs, double s, unsigned closed = 0) + { + typedef typename VertexSequence::value_type vertex_type; + + if(s > 0.0 && vs.size() > 1) + { + double d; + int n = int(vs.size() - 2); + while(n) + { + d = vs[n].dist; + if(d > s) break; + vs.remove_last(); + s -= d; + --n; + } + if(vs.size() < 2) + { + vs.remove_all(); + } + else + { + n = vs.size() - 1; + vertex_type& prev = vs[n-1]; + vertex_type& last = vs[n]; + d = (prev.dist - s) / prev.dist; + double x = prev.x + (last.x - prev.x) * d; + double y = prev.y + (last.y - prev.y) * d; + last.x = x; + last.y = y; + if(!prev(last)) vs.remove_last(); + vs.close(closed != 0); + } + } + } + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_simul_eq.h b/desmume/src/windows/agg/include/agg_simul_eq.h new file mode 100644 index 000000000..c45f5ca33 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_simul_eq.h @@ -0,0 +1,153 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SIMUL_EQ_INCLUDED +#define AGG_SIMUL_EQ_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //=============================================================swap_arrays + template void swap_arrays(T* a1, T* a2, unsigned n) + { + unsigned i; + for(i = 0; i < n; i++) + { + T tmp = *a1; + *a1++ = *a2; + *a2++ = tmp; + } + } + + + //============================================================matrix_pivot + template + struct matrix_pivot + { + static int pivot(double m[Rows][Cols], unsigned row) + { + int k = int(row); + double max_val, tmp; + + max_val = -1.0; + unsigned i; + for(i = row; i < Rows; i++) + { + if((tmp = fabs(m[i][row])) > max_val && tmp != 0.0) + { + max_val = tmp; + k = i; + } + } + + if(m[k][row] == 0.0) + { + return -1; + } + + if(k != int(row)) + { + swap_arrays(m[k], m[row], Cols); + return k; + } + return 0; + } + }; + + + + //===============================================================simul_eq + template + struct simul_eq + { + static bool solve(const double left[Size][Size], + const double right[Size][RightCols], + double result[Size][RightCols]) + { + unsigned i, j, k; + double a1; + + double tmp[Size][Size + RightCols]; + + for(i = 0; i < Size; i++) + { + for(j = 0; j < Size; j++) + { + tmp[i][j] = left[i][j]; + } + for(j = 0; j < RightCols; j++) + { + tmp[i][Size + j] = right[i][j]; + } + } + + for(k = 0; k < Size; k++) + { + if(matrix_pivot::pivot(tmp, k) < 0) + { + return false; // Singularity.... + } + + a1 = tmp[k][k]; + + for(j = k; j < Size + RightCols; j++) + { + tmp[k][j] /= a1; + } + + for(i = k + 1; i < Size; i++) + { + a1 = tmp[i][k]; + for (j = k; j < Size + RightCols; j++) + { + tmp[i][j] -= a1 * tmp[k][j]; + } + } + } + + + for(k = 0; k < RightCols; k++) + { + int m; + for(m = int(Size - 1); m >= 0; m--) + { + result[m][k] = tmp[m][Size + k]; + for(j = m + 1; j < Size; j++) + { + result[m][k] -= tmp[m][j] * result[j][k]; + } + } + } + return true; + } + + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_allocator.h b/desmume/src/windows/agg/include/agg_span_allocator.h new file mode 100644 index 000000000..6a9c3835a --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_allocator.h @@ -0,0 +1,63 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_ALLOCATOR_INCLUDED +#define AGG_SPAN_ALLOCATOR_INCLUDED + +#include "agg_array.h" + +namespace agg +{ + //----------------------------------------------------------span_allocator + template class span_allocator + { + public: + typedef ColorT color_type; + + //-------------------------------------------------------------------- + AGG_INLINE color_type* allocate(unsigned span_len) + { + if(span_len > m_span.size()) + { + // To reduce the number of reallocs we align the + // span_len to 256 color elements. + // Well, I just like this number and it looks reasonable. + //----------------------- + m_span.resize(((span_len + 255) >> 8) << 8); + } + return &m_span[0]; + } + + AGG_INLINE color_type* span() { return &m_span[0]; } + AGG_INLINE unsigned max_span_len() const { return m_span.size(); } + + private: + pod_array m_span; + }; +} + + +#endif + + diff --git a/desmume/src/windows/agg/include/agg_span_converter.h b/desmume/src/windows/agg/include/agg_span_converter.h new file mode 100644 index 000000000..99f1a2398 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_converter.h @@ -0,0 +1,65 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_CONVERTER_INCLUDED +#define AGG_SPAN_CONVERTER_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //----------------------------------------------------------span_converter + template class span_converter + { + public: + typedef typename SpanGenerator::color_type color_type; + + span_converter(SpanGenerator& span_gen, SpanConverter& span_cnv) : + m_span_gen(&span_gen), m_span_cnv(&span_cnv) {} + + void attach_generator(SpanGenerator& span_gen) { m_span_gen = &span_gen; } + void attach_converter(SpanConverter& span_cnv) { m_span_cnv = &span_cnv; } + + //-------------------------------------------------------------------- + void prepare() + { + m_span_gen->prepare(); + m_span_cnv->prepare(); + } + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + m_span_gen->generate(span, x, y, len); + m_span_cnv->generate(span, x, y, len); + } + + private: + SpanGenerator* m_span_gen; + SpanConverter* m_span_cnv; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_gouraud.h b/desmume/src/windows/agg/include/agg_span_gouraud.h new file mode 100644 index 000000000..9af2ab001 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_gouraud.h @@ -0,0 +1,181 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_GOURAUD_INCLUDED +#define AGG_SPAN_GOURAUD_INCLUDED + +#include "agg_basics.h" +#include "agg_math.h" + +namespace agg +{ + + //============================================================span_gouraud + template class span_gouraud + { + public: + typedef ColorT color_type; + + struct coord_type + { + double x; + double y; + color_type color; + }; + + //-------------------------------------------------------------------- + span_gouraud() : + m_vertex(0) + { + m_cmd[0] = path_cmd_stop; + } + + //-------------------------------------------------------------------- + span_gouraud(const color_type& c1, + const color_type& c2, + const color_type& c3, + double x1, double y1, + double x2, double y2, + double x3, double y3, + double d) : + m_vertex(0) + { + colors(c1, c2, c3); + triangle(x1, y1, x2, y2, x3, y3, d); + } + + //-------------------------------------------------------------------- + void colors(ColorT c1, ColorT c2, ColorT c3) + { + m_coord[0].color = c1; + m_coord[1].color = c2; + m_coord[2].color = c3; + } + + //-------------------------------------------------------------------- + // Sets the triangle and dilates it if needed. + // The trick here is to calculate beveled joins in the vertices of the + // triangle and render it as a 6-vertex polygon. + // It's necessary to achieve numerical stability. + // However, the coordinates to interpolate colors are calculated + // as miter joins (calc_intersection). + void triangle(double x1, double y1, + double x2, double y2, + double x3, double y3, + double d) + { + m_coord[0].x = m_x[0] = x1; + m_coord[0].y = m_y[0] = y1; + m_coord[1].x = m_x[1] = x2; + m_coord[1].y = m_y[1] = y2; + m_coord[2].x = m_x[2] = x3; + m_coord[2].y = m_y[2] = y3; + m_cmd[0] = path_cmd_move_to; + m_cmd[1] = path_cmd_line_to; + m_cmd[2] = path_cmd_line_to; + m_cmd[3] = path_cmd_stop; + + if(d != 0.0) + { + dilate_triangle(m_coord[0].x, m_coord[0].y, + m_coord[1].x, m_coord[1].y, + m_coord[2].x, m_coord[2].y, + m_x, m_y, d); + + calc_intersection(m_x[4], m_y[4], m_x[5], m_y[5], + m_x[0], m_y[0], m_x[1], m_y[1], + &m_coord[0].x, &m_coord[0].y); + + calc_intersection(m_x[0], m_y[0], m_x[1], m_y[1], + m_x[2], m_y[2], m_x[3], m_y[3], + &m_coord[1].x, &m_coord[1].y); + + calc_intersection(m_x[2], m_y[2], m_x[3], m_y[3], + m_x[4], m_y[4], m_x[5], m_y[5], + &m_coord[2].x, &m_coord[2].y); + m_cmd[3] = path_cmd_line_to; + m_cmd[4] = path_cmd_line_to; + m_cmd[5] = path_cmd_line_to; + m_cmd[6] = path_cmd_stop; + } + } + + //-------------------------------------------------------------------- + // Vertex Source Interface to feed the coordinates to the rasterizer + void rewind(unsigned) + { + m_vertex = 0; + } + + //-------------------------------------------------------------------- + unsigned vertex(double* x, double* y) + { + *x = m_x[m_vertex]; + *y = m_y[m_vertex]; + return m_cmd[m_vertex++]; + } + + protected: + //-------------------------------------------------------------------- + void arrange_vertices(coord_type* coord) const + { + coord[0] = m_coord[0]; + coord[1] = m_coord[1]; + coord[2] = m_coord[2]; + + if(m_coord[0].y > m_coord[2].y) + { + coord[0] = m_coord[2]; + coord[2] = m_coord[0]; + } + + coord_type tmp; + if(coord[0].y > coord[1].y) + { + tmp = coord[1]; + coord[1] = coord[0]; + coord[0] = tmp; + } + + if(coord[1].y > coord[2].y) + { + tmp = coord[2]; + coord[2] = coord[1]; + coord[1] = tmp; + } + } + + private: + //-------------------------------------------------------------------- + coord_type m_coord[3]; + double m_x[8]; + double m_y[8]; + unsigned m_cmd[8]; + unsigned m_vertex; + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_span_gouraud_gray.h b/desmume/src/windows/agg/include/agg_span_gouraud_gray.h new file mode 100644 index 000000000..46a446ce0 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_gouraud_gray.h @@ -0,0 +1,250 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_GOURAUD_GRAY_INCLUDED +#define AGG_SPAN_GOURAUD_GRAY_INCLUDED + +#include "agg_basics.h" +#include "agg_color_gray.h" +#include "agg_dda_line.h" +#include "agg_span_gouraud.h" + +namespace agg +{ + + //=======================================================span_gouraud_gray + template class span_gouraud_gray : public span_gouraud + { + public: + typedef ColorT color_type; + typedef typename color_type::value_type value_type; + typedef span_gouraud base_type; + typedef typename base_type::coord_type coord_type; + enum subpixel_scale_e + { + subpixel_shift = 4, + subpixel_scale = 1 << subpixel_shift + }; + + private: + //-------------------------------------------------------------------- + struct gray_calc + { + void init(const coord_type& c1, const coord_type& c2) + { + m_x1 = c1.x - 0.5; + m_y1 = c1.y - 0.5; + m_dx = c2.x - c1.x; + double dy = c2.y - c1.y; + m_1dy = (fabs(dy) < 1e-10) ? 1e10 : 1.0 / dy; + m_v1 = c1.color.v; + m_a1 = c1.color.a; + m_dv = c2.color.v - m_v1; + m_da = c2.color.a - m_a1; + } + + void calc(double y) + { + double k = (y - m_y1) * m_1dy; + if(k < 0.0) k = 0.0; + if(k > 1.0) k = 1.0; + m_v = m_v1 + iround(m_dv * k); + m_a = m_a1 + iround(m_da * k); + m_x = iround((m_x1 + m_dx * k) * subpixel_scale); + } + + double m_x1; + double m_y1; + double m_dx; + double m_1dy; + int m_v1; + int m_a1; + int m_dv; + int m_da; + int m_v; + int m_a; + int m_x; + }; + + + public: + //-------------------------------------------------------------------- + span_gouraud_gray() {} + span_gouraud_gray(const color_type& c1, + const color_type& c2, + const color_type& c3, + double x1, double y1, + double x2, double y2, + double x3, double y3, + double d = 0) : + base_type(c1, c2, c3, x1, y1, x2, y2, x3, y3, d) + {} + + //-------------------------------------------------------------------- + void prepare() + { + coord_type coord[3]; + base_type::arrange_vertices(coord); + + m_y2 = int(coord[1].y); + + m_swap = cross_product(coord[0].x, coord[0].y, + coord[2].x, coord[2].y, + coord[1].x, coord[1].y) < 0.0; + + m_c1.init(coord[0], coord[2]); + m_c2.init(coord[0], coord[1]); + m_c3.init(coord[1], coord[2]); + } + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + m_c1.calc(y); + const gray_calc* pc1 = &m_c1; + const gray_calc* pc2 = &m_c2; + + if(y < m_y2) + { + // Bottom part of the triangle (first subtriangle) + //------------------------- + m_c2.calc(y + m_c2.m_1dy); + } + else + { + // Upper part (second subtriangle) + //------------------------- + m_c3.calc(y - m_c3.m_1dy); + pc2 = &m_c3; + } + + if(m_swap) + { + // It means that the triangle is oriented clockwise, + // so that we need to swap the controlling structures + //------------------------- + const gray_calc* t = pc2; + pc2 = pc1; + pc1 = t; + } + + // Get the horizontal length with subpixel accuracy + // and protect it from division by zero + //------------------------- + int nlen = abs(pc2->m_x - pc1->m_x); + if(nlen <= 0) nlen = 1; + + dda_line_interpolator<14> v(pc1->m_v, pc2->m_v, nlen); + dda_line_interpolator<14> a(pc1->m_a, pc2->m_a, nlen); + + // Calculate the starting point of the gradient with subpixel + // accuracy and correct (roll back) the interpolators. + // This operation will also clip the beginning of the span + // if necessary. + //------------------------- + int start = pc1->m_x - (x << subpixel_shift); + v -= start; + a -= start; + nlen += start; + + int vv, va; + enum lim_e { lim = color_type::base_mask }; + + // Beginning part of the span. Since we rolled back the + // interpolators, the color values may have overflow. + // So that, we render the beginning part with checking + // for overflow. It lasts until "start" is positive; + // typically it's 1-2 pixels, but may be more in some cases. + //------------------------- + while(len && start > 0) + { + vv = v.y(); + va = a.y(); + if(vv < 0) vv = 0; if(vv > lim) vv = lim; + if(va < 0) va = 0; if(va > lim) va = lim; + span->v = (value_type)vv; + span->a = (value_type)va; + v += subpixel_scale; + a += subpixel_scale; + nlen -= subpixel_scale; + start -= subpixel_scale; + ++span; + --len; + } + + // Middle part, no checking for overflow. + // Actual spans can be longer than the calculated length + // because of anti-aliasing, thus, the interpolators can + // overflow. But while "nlen" is positive we are safe. + //------------------------- + while(len && nlen > 0) + { + span->v = (value_type)v.y(); + span->a = (value_type)a.y(); + v += subpixel_scale; + a += subpixel_scale; + nlen -= subpixel_scale; + ++span; + --len; + } + + // Ending part; checking for overflow. + // Typically it's 1-2 pixels, but may be more in some cases. + //------------------------- + while(len) + { + vv = v.y(); + va = a.y(); + if(vv < 0) vv = 0; if(vv > lim) vv = lim; + if(va < 0) va = 0; if(va > lim) va = lim; + span->v = (value_type)vv; + span->a = (value_type)va; + v += subpixel_scale; + a += subpixel_scale; + ++span; + --len; + } + } + + + private: + bool m_swap; + int m_y2; + gray_calc m_c1; + gray_calc m_c2; + gray_calc m_c3; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_gouraud_rgba.h b/desmume/src/windows/agg/include/agg_span_gouraud_rgba.h new file mode 100644 index 000000000..a379ddd95 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_gouraud_rgba.h @@ -0,0 +1,286 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_GOURAUD_RGBA_INCLUDED +#define AGG_SPAN_GOURAUD_RGBA_INCLUDED + +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_dda_line.h" +#include "agg_span_gouraud.h" + +namespace agg +{ + + //=======================================================span_gouraud_rgba + template class span_gouraud_rgba : public span_gouraud + { + public: + typedef ColorT color_type; + typedef typename ColorT::value_type value_type; + typedef span_gouraud base_type; + typedef typename base_type::coord_type coord_type; + enum subpixel_scale_e + { + subpixel_shift = 4, + subpixel_scale = 1 << subpixel_shift + }; + + private: + //-------------------------------------------------------------------- + struct rgba_calc + { + void init(const coord_type& c1, const coord_type& c2) + { + m_x1 = c1.x - 0.5; + m_y1 = c1.y - 0.5; + m_dx = c2.x - c1.x; + double dy = c2.y - c1.y; + m_1dy = (dy < 1e-5) ? 1e5 : 1.0 / dy; + m_r1 = c1.color.r; + m_g1 = c1.color.g; + m_b1 = c1.color.b; + m_a1 = c1.color.a; + m_dr = c2.color.r - m_r1; + m_dg = c2.color.g - m_g1; + m_db = c2.color.b - m_b1; + m_da = c2.color.a - m_a1; + } + + void calc(double y) + { + double k = (y - m_y1) * m_1dy; + if(k < 0.0) k = 0.0; + if(k > 1.0) k = 1.0; + m_r = m_r1 + iround(m_dr * k); + m_g = m_g1 + iround(m_dg * k); + m_b = m_b1 + iround(m_db * k); + m_a = m_a1 + iround(m_da * k); + m_x = iround((m_x1 + m_dx * k) * subpixel_scale); + } + + double m_x1; + double m_y1; + double m_dx; + double m_1dy; + int m_r1; + int m_g1; + int m_b1; + int m_a1; + int m_dr; + int m_dg; + int m_db; + int m_da; + int m_r; + int m_g; + int m_b; + int m_a; + int m_x; + }; + + public: + + //-------------------------------------------------------------------- + span_gouraud_rgba() {} + span_gouraud_rgba(const color_type& c1, + const color_type& c2, + const color_type& c3, + double x1, double y1, + double x2, double y2, + double x3, double y3, + double d = 0) : + base_type(c1, c2, c3, x1, y1, x2, y2, x3, y3, d) + {} + + //-------------------------------------------------------------------- + void prepare() + { + coord_type coord[3]; + base_type::arrange_vertices(coord); + + m_y2 = int(coord[1].y); + + m_swap = cross_product(coord[0].x, coord[0].y, + coord[2].x, coord[2].y, + coord[1].x, coord[1].y) < 0.0; + + m_rgba1.init(coord[0], coord[2]); + m_rgba2.init(coord[0], coord[1]); + m_rgba3.init(coord[1], coord[2]); + } + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + m_rgba1.calc(y);//(m_rgba1.m_1dy > 2) ? m_rgba1.m_y1 : y); + const rgba_calc* pc1 = &m_rgba1; + const rgba_calc* pc2 = &m_rgba2; + + if(y <= m_y2) + { + // Bottom part of the triangle (first subtriangle) + //------------------------- + m_rgba2.calc(y + m_rgba2.m_1dy); + } + else + { + // Upper part (second subtriangle) + m_rgba3.calc(y - m_rgba3.m_1dy); + //------------------------- + pc2 = &m_rgba3; + } + + if(m_swap) + { + // It means that the triangle is oriented clockwise, + // so that we need to swap the controlling structures + //------------------------- + const rgba_calc* t = pc2; + pc2 = pc1; + pc1 = t; + } + + // Get the horizontal length with subpixel accuracy + // and protect it from division by zero + //------------------------- + int nlen = abs(pc2->m_x - pc1->m_x); + if(nlen <= 0) nlen = 1; + + dda_line_interpolator<14> r(pc1->m_r, pc2->m_r, nlen); + dda_line_interpolator<14> g(pc1->m_g, pc2->m_g, nlen); + dda_line_interpolator<14> b(pc1->m_b, pc2->m_b, nlen); + dda_line_interpolator<14> a(pc1->m_a, pc2->m_a, nlen); + + // Calculate the starting point of the gradient with subpixel + // accuracy and correct (roll back) the interpolators. + // This operation will also clip the beginning of the span + // if necessary. + //------------------------- + int start = pc1->m_x - (x << subpixel_shift); + r -= start; + g -= start; + b -= start; + a -= start; + nlen += start; + + int vr, vg, vb, va; + enum lim_e { lim = color_type::base_mask }; + + // Beginning part of the span. Since we rolled back the + // interpolators, the color values may have overflow. + // So that, we render the beginning part with checking + // for overflow. It lasts until "start" is positive; + // typically it's 1-2 pixels, but may be more in some cases. + //------------------------- + while(len && start > 0) + { + vr = r.y(); + vg = g.y(); + vb = b.y(); + va = a.y(); + if(vr < 0) vr = 0; if(vr > lim) vr = lim; + if(vg < 0) vg = 0; if(vg > lim) vg = lim; + if(vb < 0) vb = 0; if(vb > lim) vb = lim; + if(va < 0) va = 0; if(va > lim) va = lim; + span->r = (value_type)vr; + span->g = (value_type)vg; + span->b = (value_type)vb; + span->a = (value_type)va; + r += subpixel_scale; + g += subpixel_scale; + b += subpixel_scale; + a += subpixel_scale; + nlen -= subpixel_scale; + start -= subpixel_scale; + ++span; + --len; + } + + // Middle part, no checking for overflow. + // Actual spans can be longer than the calculated length + // because of anti-aliasing, thus, the interpolators can + // overflow. But while "nlen" is positive we are safe. + //------------------------- + while(len && nlen > 0) + { + span->r = (value_type)r.y(); + span->g = (value_type)g.y(); + span->b = (value_type)b.y(); + span->a = (value_type)a.y(); + r += subpixel_scale; + g += subpixel_scale; + b += subpixel_scale; + a += subpixel_scale; + nlen -= subpixel_scale; + ++span; + --len; + } + + // Ending part; checking for overflow. + // Typically it's 1-2 pixels, but may be more in some cases. + //------------------------- + while(len) + { + vr = r.y(); + vg = g.y(); + vb = b.y(); + va = a.y(); + if(vr < 0) vr = 0; if(vr > lim) vr = lim; + if(vg < 0) vg = 0; if(vg > lim) vg = lim; + if(vb < 0) vb = 0; if(vb > lim) vb = lim; + if(va < 0) va = 0; if(va > lim) va = lim; + span->r = (value_type)vr; + span->g = (value_type)vg; + span->b = (value_type)vb; + span->a = (value_type)va; + r += subpixel_scale; + g += subpixel_scale; + b += subpixel_scale; + a += subpixel_scale; + ++span; + --len; + } + } + + private: + bool m_swap; + int m_y2; + rgba_calc m_rgba1; + rgba_calc m_rgba2; + rgba_calc m_rgba3; + }; + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_gradient.h b/desmume/src/windows/agg/include/agg_span_gradient.h new file mode 100644 index 000000000..382d1d979 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_gradient.h @@ -0,0 +1,373 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_GRADIENT_INCLUDED +#define AGG_SPAN_GRADIENT_INCLUDED + +#include +#include +#include +#include "agg_basics.h" +#include "agg_math.h" +#include "agg_array.h" + + +namespace agg +{ + + enum gradient_subpixel_scale_e + { + gradient_subpixel_shift = 4, //-----gradient_subpixel_shift + gradient_subpixel_scale = 1 << gradient_subpixel_shift, //-----gradient_subpixel_scale + gradient_subpixel_mask = gradient_subpixel_scale - 1 //-----gradient_subpixel_mask + }; + + + + //==========================================================span_gradient + template + class span_gradient + { + public: + typedef Interpolator interpolator_type; + typedef ColorT color_type; + + enum downscale_shift_e + { + downscale_shift = interpolator_type::subpixel_shift - + gradient_subpixel_shift + }; + + //-------------------------------------------------------------------- + span_gradient() {} + + //-------------------------------------------------------------------- + span_gradient(interpolator_type& inter, + const GradientF& gradient_function, + const ColorF& color_function, + double d1, double d2) : + m_interpolator(&inter), + m_gradient_function(&gradient_function), + m_color_function(&color_function), + m_d1(iround(d1 * gradient_subpixel_scale)), + m_d2(iround(d2 * gradient_subpixel_scale)) + {} + + //-------------------------------------------------------------------- + interpolator_type& interpolator() { return *m_interpolator; } + const GradientF& gradient_function() const { return *m_gradient_function; } + const ColorF& color_function() const { return *m_color_function; } + double d1() const { return double(m_d1) / gradient_subpixel_scale; } + double d2() const { return double(m_d2) / gradient_subpixel_scale; } + + //-------------------------------------------------------------------- + void interpolator(interpolator_type& i) { m_interpolator = &i; } + void gradient_function(const GradientF& gf) { m_gradient_function = &gf; } + void color_function(const ColorF& cf) { m_color_function = &cf; } + void d1(double v) { m_d1 = iround(v * gradient_subpixel_scale); } + void d2(double v) { m_d2 = iround(v * gradient_subpixel_scale); } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + int dd = m_d2 - m_d1; + if(dd < 1) dd = 1; + m_interpolator->begin(x+0.5, y+0.5, len); + do + { + m_interpolator->coordinates(&x, &y); + int d = m_gradient_function->calculate(x >> downscale_shift, + y >> downscale_shift, m_d2); + d = ((d - m_d1) * (int)m_color_function->size()) / dd; + if(d < 0) d = 0; + if(d >= (int)m_color_function->size()) d = m_color_function->size() - 1; + *span++ = (*m_color_function)[d]; + ++(*m_interpolator); + } + while(--len); + } + + private: + interpolator_type* m_interpolator; + const GradientF* m_gradient_function; + const ColorF* m_color_function; + int m_d1; + int m_d2; + }; + + + + + //=====================================================gradient_linear_color + template + struct gradient_linear_color + { + typedef ColorT color_type; + + gradient_linear_color() {} + gradient_linear_color(const color_type& c1, const color_type& c2, + unsigned size = 256) : + m_c1(c1), m_c2(c2), m_size(size) {} + + unsigned size() const { return m_size; } + color_type operator [] (unsigned v) const + { + return m_c1.gradient(m_c2, double(v) / double(m_size - 1)); + } + + void colors(const color_type& c1, const color_type& c2, unsigned size = 256) + { + m_c1 = c1; + m_c2 = c2; + m_size = size; + } + + color_type m_c1; + color_type m_c2; + unsigned m_size; + }; + + + + + + + //==========================================================gradient_circle + class gradient_circle + { + // Actually the same as radial. Just for compatibility + public: + static AGG_INLINE int calculate(int x, int y, int) + { + return int(fast_sqrt(x*x + y*y)); + } + }; + + + //==========================================================gradient_radial + class gradient_radial + { + public: + static AGG_INLINE int calculate(int x, int y, int) + { + return int(fast_sqrt(x*x + y*y)); + } + }; + + //========================================================gradient_radial_d + class gradient_radial_d + { + public: + static AGG_INLINE int calculate(int x, int y, int) + { + return uround(sqrt(double(x)*double(x) + double(y)*double(y))); + } + }; + + //====================================================gradient_radial_focus + class gradient_radial_focus + { + public: + //--------------------------------------------------------------------- + gradient_radial_focus() : + m_r(100 * gradient_subpixel_scale), + m_fx(0), + m_fy(0) + { + update_values(); + } + + //--------------------------------------------------------------------- + gradient_radial_focus(double r, double fx, double fy) : + m_r (iround(r * gradient_subpixel_scale)), + m_fx(iround(fx * gradient_subpixel_scale)), + m_fy(iround(fy * gradient_subpixel_scale)) + { + update_values(); + } + + //--------------------------------------------------------------------- + void init(double r, double fx, double fy) + { + m_r = iround(r * gradient_subpixel_scale); + m_fx = iround(fx * gradient_subpixel_scale); + m_fy = iround(fy * gradient_subpixel_scale); + update_values(); + } + + //--------------------------------------------------------------------- + double radius() const { return double(m_r) / gradient_subpixel_scale; } + double focus_x() const { return double(m_fx) / gradient_subpixel_scale; } + double focus_y() const { return double(m_fy) / gradient_subpixel_scale; } + + //--------------------------------------------------------------------- + int calculate(int x, int y, int) const + { + double dx = x - m_fx; + double dy = y - m_fy; + double d2 = dx * m_fy - dy * m_fx; + double d3 = m_r2 * (dx * dx + dy * dy) - d2 * d2; + return iround((dx * m_fx + dy * m_fy + sqrt(fabs(d3))) * m_mul); + } + + private: + //--------------------------------------------------------------------- + void update_values() + { + // Calculate the invariant values. In case the focal center + // lies exactly on the gradient circle the divisor degenerates + // into zero. In this case we just move the focal center by + // one subpixel unit possibly in the direction to the origin (0,0) + // and calculate the values again. + //------------------------- + m_r2 = double(m_r) * double(m_r); + m_fx2 = double(m_fx) * double(m_fx); + m_fy2 = double(m_fy) * double(m_fy); + double d = (m_r2 - (m_fx2 + m_fy2)); + if(d == 0) + { + if(m_fx) { if(m_fx < 0) ++m_fx; else --m_fx; } + if(m_fy) { if(m_fy < 0) ++m_fy; else --m_fy; } + m_fx2 = double(m_fx) * double(m_fx); + m_fy2 = double(m_fy) * double(m_fy); + d = (m_r2 - (m_fx2 + m_fy2)); + } + m_mul = m_r / d; + } + + int m_r; + int m_fx; + int m_fy; + double m_r2; + double m_fx2; + double m_fy2; + double m_mul; + }; + + + //==============================================================gradient_x + class gradient_x + { + public: + static int calculate(int x, int, int) { return x; } + }; + + + //==============================================================gradient_y + class gradient_y + { + public: + static int calculate(int, int y, int) { return y; } + }; + + //========================================================gradient_diamond + class gradient_diamond + { + public: + static AGG_INLINE int calculate(int x, int y, int) + { + int ax = abs(x); + int ay = abs(y); + return ax > ay ? ax : ay; + } + }; + + //=============================================================gradient_xy + class gradient_xy + { + public: + static AGG_INLINE int calculate(int x, int y, int d) + { + return abs(x) * abs(y) / d; + } + }; + + //========================================================gradient_sqrt_xy + class gradient_sqrt_xy + { + public: + static AGG_INLINE int calculate(int x, int y, int) + { + return fast_sqrt(abs(x) * abs(y)); + } + }; + + //==========================================================gradient_conic + class gradient_conic + { + public: + static AGG_INLINE int calculate(int x, int y, int d) + { + return uround(fabs(atan2(double(y), double(x))) * double(d) / pi); + } + }; + + //=================================================gradient_repeat_adaptor + template class gradient_repeat_adaptor + { + public: + gradient_repeat_adaptor(const GradientF& gradient) : + m_gradient(&gradient) {} + + AGG_INLINE int calculate(int x, int y, int d) const + { + int ret = m_gradient->calculate(x, y, d) % d; + if(ret < 0) ret += d; + return ret; + } + + private: + const GradientF* m_gradient; + }; + + //================================================gradient_reflect_adaptor + template class gradient_reflect_adaptor + { + public: + gradient_reflect_adaptor(const GradientF& gradient) : + m_gradient(&gradient) {} + + AGG_INLINE int calculate(int x, int y, int d) const + { + int d2 = d << 1; + int ret = m_gradient->calculate(x, y, d) % d2; + if(ret < 0) ret += d2; + if(ret >= d) ret = d2 - ret; + return ret; + } + + private: + const GradientF* m_gradient; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_gradient_alpha.h b/desmume/src/windows/agg/include/agg_span_gradient_alpha.h new file mode 100644 index 000000000..862957686 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_gradient_alpha.h @@ -0,0 +1,135 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_GRADIENT_ALPHA_INCLUDED +#define AGG_SPAN_GRADIENT_ALPHA_INCLUDED + +#include "agg_span_gradient.h" + +namespace agg +{ + //======================================================span_gradient_alpha + template + class span_gradient_alpha + { + public: + typedef Interpolator interpolator_type; + typedef ColorT color_type; + typedef typename color_type::value_type alpha_type; + + enum downscale_shift_e + { + downscale_shift = interpolator_type::subpixel_shift - gradient_subpixel_shift + }; + + + //-------------------------------------------------------------------- + span_gradient_alpha() {} + + //-------------------------------------------------------------------- + span_gradient_alpha(interpolator_type& inter, + const GradientF& gradient_function, + const AlphaF& alpha_function, + double d1, double d2) : + m_interpolator(&inter), + m_gradient_function(&gradient_function), + m_alpha_function(&alpha_function), + m_d1(iround(d1 * gradient_subpixel_scale)), + m_d2(iround(d2 * gradient_subpixel_scale)) + {} + + //-------------------------------------------------------------------- + interpolator_type& interpolator() { return *m_interpolator; } + const GradientF& gradient_function() const { return *m_gradient_function; } + const AlphaF& alpha_function() const { return *m_alpha_function; } + double d1() const { return double(m_d1) / gradient_subpixel_scale; } + double d2() const { return double(m_d2) / gradient_subpixel_scale; } + + //-------------------------------------------------------------------- + void interpolator(interpolator_type& i) { m_interpolator = &i; } + void gradient_function(const GradientF& gf) { m_gradient_function = &gf; } + void alpha_function(const AlphaF& af) { m_alpha_function = ⁡ } + void d1(double v) { m_d1 = iround(v * gradient_subpixel_scale); } + void d2(double v) { m_d2 = iround(v * gradient_subpixel_scale); } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + int dd = m_d2 - m_d1; + if(dd < 1) dd = 1; + m_interpolator->begin(x+0.5, y+0.5, len); + do + { + m_interpolator->coordinates(&x, &y); + int d = m_gradient_function->calculate(x >> downscale_shift, + y >> downscale_shift, m_d2); + d = ((d - m_d1) * (int)m_alpha_function->size()) / dd; + if(d < 0) d = 0; + if(d >= (int)m_alpha_function->size()) d = m_alpha_function->size() - 1; + span->a = (*m_alpha_function)[d]; + ++span; + ++(*m_interpolator); + } + while(--len); + } + + private: + interpolator_type* m_interpolator; + const GradientF* m_gradient_function; + const AlphaF* m_alpha_function; + int m_d1; + int m_d2; + }; + + + //=======================================================gradient_alpha_x + template struct gradient_alpha_x + { + typedef typename ColorT::value_type alpha_type; + alpha_type operator [] (alpha_type x) const { return x; } + }; + + //====================================================gradient_alpha_x_u8 + struct gradient_alpha_x_u8 + { + typedef int8u alpha_type; + alpha_type operator [] (alpha_type x) const { return x; } + }; + + //==========================================gradient_alpha_one_munus_x_u8 + struct gradient_alpha_one_munus_x_u8 + { + typedef int8u alpha_type; + alpha_type operator [] (alpha_type x) const { return 255-x; } + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_image_filter.h b/desmume/src/windows/agg/include/agg_span_image_filter.h new file mode 100644 index 000000000..d0b1b9048 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_image_filter.h @@ -0,0 +1,252 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_IMAGE_FILTER_INCLUDED +#define AGG_SPAN_IMAGE_FILTER_INCLUDED + +#include "agg_basics.h" +#include "agg_image_filters.h" +#include "agg_span_interpolator_linear.h" + +namespace agg +{ + + //-------------------------------------------------------span_image_filter + template class span_image_filter + { + public: + typedef Source source_type; + typedef Interpolator interpolator_type; + + //-------------------------------------------------------------------- + span_image_filter() {} + span_image_filter(source_type& src, + interpolator_type& interpolator, + const image_filter_lut* filter) : + m_src(&src), + m_interpolator(&interpolator), + m_filter(filter), + m_dx_dbl(0.5), + m_dy_dbl(0.5), + m_dx_int(image_subpixel_scale / 2), + m_dy_int(image_subpixel_scale / 2) + {} + void attach(source_type& v) { m_src = &v; } + + //-------------------------------------------------------------------- + source_type& source() { return *m_src; } + const source_type& source() const { return *m_src; } + const image_filter_lut& filter() const { return *m_filter; } + int filter_dx_int() const { return m_dx_int; } + int filter_dy_int() const { return m_dy_int; } + double filter_dx_dbl() const { return m_dx_dbl; } + double filter_dy_dbl() const { return m_dy_dbl; } + + //-------------------------------------------------------------------- + void interpolator(interpolator_type& v) { m_interpolator = &v; } + void filter(const image_filter_lut& v) { m_filter = &v; } + void filter_offset(double dx, double dy) + { + m_dx_dbl = dx; + m_dy_dbl = dy; + m_dx_int = iround(dx * image_subpixel_scale); + m_dy_int = iround(dy * image_subpixel_scale); + } + void filter_offset(double d) { filter_offset(d, d); } + + //-------------------------------------------------------------------- + interpolator_type& interpolator() { return *m_interpolator; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + private: + source_type* m_src; + interpolator_type* m_interpolator; + const image_filter_lut* m_filter; + double m_dx_dbl; + double m_dy_dbl; + unsigned m_dx_int; + unsigned m_dy_int; + }; + + + + + //==============================================span_image_resample_affine + template + class span_image_resample_affine : + public span_image_filter > + { + public: + typedef Source source_type; + typedef span_interpolator_linear interpolator_type; + typedef span_image_filter base_type; + + //-------------------------------------------------------------------- + span_image_resample_affine() : + m_scale_limit(200.0), + m_blur_x(1.0), + m_blur_y(1.0) + {} + + //-------------------------------------------------------------------- + span_image_resample_affine(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter), + m_scale_limit(200.0), + m_blur_x(1.0), + m_blur_y(1.0) + {} + + + //-------------------------------------------------------------------- + int scale_limit() const { return uround(m_scale_limit); } + void scale_limit(int v) { m_scale_limit = v; } + + //-------------------------------------------------------------------- + double blur_x() const { return m_blur_x; } + double blur_y() const { return m_blur_y; } + void blur_x(double v) { m_blur_x = v; } + void blur_y(double v) { m_blur_y = v; } + void blur(double v) { m_blur_x = m_blur_y = v; } + + //-------------------------------------------------------------------- + void prepare() + { + double scale_x; + double scale_y; + + base_type::interpolator().transformer().scaling_abs(&scale_x, &scale_y); + + if(scale_x * scale_y > m_scale_limit) + { + scale_x = scale_x * m_scale_limit / (scale_x * scale_y); + scale_y = scale_y * m_scale_limit / (scale_x * scale_y); + } + + if(scale_x < 1) scale_x = 1; + if(scale_y < 1) scale_y = 1; + + if(scale_x > m_scale_limit) scale_x = m_scale_limit; + if(scale_y > m_scale_limit) scale_y = m_scale_limit; + + scale_x *= m_blur_x; + scale_y *= m_blur_y; + + if(scale_x < 1) scale_x = 1; + if(scale_y < 1) scale_y = 1; + + m_rx = uround( scale_x * double(image_subpixel_scale)); + m_rx_inv = uround(1.0/scale_x * double(image_subpixel_scale)); + + m_ry = uround( scale_y * double(image_subpixel_scale)); + m_ry_inv = uround(1.0/scale_y * double(image_subpixel_scale)); + } + + protected: + int m_rx; + int m_ry; + int m_rx_inv; + int m_ry_inv; + + private: + double m_scale_limit; + double m_blur_x; + double m_blur_y; + }; + + + + //=====================================================span_image_resample + template + class span_image_resample : + public span_image_filter + { + public: + typedef Source source_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + + //-------------------------------------------------------------------- + span_image_resample() : + m_scale_limit(20), + m_blur_x(image_subpixel_scale), + m_blur_y(image_subpixel_scale) + {} + + //-------------------------------------------------------------------- + span_image_resample(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter), + m_scale_limit(20), + m_blur_x(image_subpixel_scale), + m_blur_y(image_subpixel_scale) + {} + + //-------------------------------------------------------------------- + int scale_limit() const { return m_scale_limit; } + void scale_limit(int v) { m_scale_limit = v; } + + //-------------------------------------------------------------------- + double blur_x() const { return double(m_blur_x) / double(image_subpixel_scale); } + double blur_y() const { return double(m_blur_y) / double(image_subpixel_scale); } + void blur_x(double v) { m_blur_x = uround(v * double(image_subpixel_scale)); } + void blur_y(double v) { m_blur_y = uround(v * double(image_subpixel_scale)); } + void blur(double v) { m_blur_x = + m_blur_y = uround(v * double(image_subpixel_scale)); } + + protected: + AGG_INLINE void adjust_scale(int* rx, int* ry) + { + if(*rx < image_subpixel_scale) *rx = image_subpixel_scale; + if(*ry < image_subpixel_scale) *ry = image_subpixel_scale; + if(*rx > image_subpixel_scale * m_scale_limit) + { + *rx = image_subpixel_scale * m_scale_limit; + } + if(*ry > image_subpixel_scale * m_scale_limit) + { + *ry = image_subpixel_scale * m_scale_limit; + } + *rx = (*rx * m_blur_x) >> image_subpixel_shift; + *ry = (*ry * m_blur_y) >> image_subpixel_shift; + if(*rx < image_subpixel_scale) *rx = image_subpixel_scale; + if(*ry < image_subpixel_scale) *ry = image_subpixel_scale; + } + + int m_scale_limit; + int m_blur_x; + int m_blur_y; + }; + + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_image_filter_gray.h b/desmume/src/windows/agg/include/agg_span_image_filter_gray.h new file mode 100644 index 000000000..e5293e1e3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_image_filter_gray.h @@ -0,0 +1,757 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_SPAN_IMAGE_FILTER_GRAY_INCLUDED +#define AGG_SPAN_IMAGE_FILTER_GRAY_INCLUDED + +#include "agg_basics.h" +#include "agg_color_gray.h" +#include "agg_span_image_filter.h" + + +namespace agg +{ + + //==============================================span_image_filter_gray_nn + template + class span_image_filter_gray_nn : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_gray_nn() {} + span_image_filter_gray_nn(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + do + { + base_type::interpolator().coordinates(&x, &y); + span->v = *(const value_type*) + base_type::source().span(x >> image_subpixel_shift, + y >> image_subpixel_shift, + 1); + span->a = base_mask; + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + + + //=========================================span_image_filter_gray_bilinear + template + class span_image_filter_gray_bilinear : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_gray_bilinear() {} + span_image_filter_gray_bilinear(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + calc_type fg; + const value_type *fg_ptr; + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + fg = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + fg += *fg_ptr * (image_subpixel_scale - x_hr) * (image_subpixel_scale - y_hr); + + fg_ptr = (const value_type*)base_type::source().next_x(); + fg += *fg_ptr * x_hr * (image_subpixel_scale - y_hr); + + fg_ptr = (const value_type*)base_type::source().next_y(); + fg += *fg_ptr * (image_subpixel_scale - x_hr) * y_hr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + fg += *fg_ptr * x_hr * y_hr; + + span->v = value_type(fg >> (image_subpixel_shift * 2)); + span->a = base_mask; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + //====================================span_image_filter_gray_bilinear_clip + template + class span_image_filter_gray_bilinear_clip : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_gray_bilinear_clip() {} + span_image_filter_gray_bilinear_clip(source_type& src, + const color_type& back_color, + interpolator_type& inter) : + base_type(src, inter, 0), + m_back_color(back_color) + {} + const color_type& background_color() const { return m_back_color; } + void background_color(const color_type& v) { m_back_color = v; } + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + calc_type fg; + calc_type src_alpha; + value_type back_v = m_back_color.v; + value_type back_a = m_back_color.a; + + const value_type *fg_ptr; + + int maxx = base_type::source().width() - 1; + int maxy = base_type::source().height() - 1; + + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + if(x_lr >= 0 && y_lr >= 0 && + x_lr < maxx && y_lr < maxy) + { + fg = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + fg_ptr = (const value_type*)base_type::source().row_ptr(y_lr) + x_lr; + + fg += *fg_ptr++ * (image_subpixel_scale - x_hr) * (image_subpixel_scale - y_hr); + fg += *fg_ptr++ * (image_subpixel_scale - y_hr) * x_hr; + + ++y_lr; + fg_ptr = (const value_type*)base_type::source().row_ptr(y_lr) + x_lr; + + fg += *fg_ptr++ * (image_subpixel_scale - x_hr) * y_hr; + fg += *fg_ptr++ * x_hr * y_hr; + + fg >>= image_subpixel_shift * 2; + src_alpha = base_mask; + } + else + { + unsigned weight; + if(x_lr < -1 || y_lr < -1 || + x_lr > maxx || y_lr > maxy) + { + fg = back_v; + src_alpha = back_a; + } + else + { + fg = + src_alpha = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg += weight * + *((const value_type*)base_type::source().row_ptr(y_lr) + x_lr); + src_alpha += weight * base_mask; + } + else + { + fg += back_v * weight; + src_alpha += back_a * weight; + } + + x_lr++; + + weight = x_hr * (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg += weight * + *((const value_type*)base_type::source().row_ptr(y_lr) + x_lr); + src_alpha += weight * base_mask; + } + else + { + fg += back_v * weight; + src_alpha += back_a * weight; + } + + x_lr--; + y_lr++; + + weight = (image_subpixel_scale - x_hr) * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg += weight * + *((const value_type*)base_type::source().row_ptr(y_lr) + x_lr); + src_alpha += weight * base_mask; + } + else + { + fg += back_v * weight; + src_alpha += back_a * weight; + } + + x_lr++; + + weight = x_hr * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg += weight * + *((const value_type*)base_type::source().row_ptr(y_lr) + x_lr); + src_alpha += weight * base_mask; + } + else + { + fg += back_v * weight; + src_alpha += back_a * weight; + } + + fg >>= image_subpixel_shift * 2; + src_alpha >>= image_subpixel_shift * 2; + } + } + + span->v = (value_type)fg; + span->a = (value_type)src_alpha; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + private: + color_type m_back_color; + }; + + + + //==============================================span_image_filter_gray_2x2 + template + class span_image_filter_gray_2x2 : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_gray_2x2() {} + span_image_filter_gray_2x2(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + calc_type fg; + + const value_type *fg_ptr; + const int16* weight_array = base_type::filter().weight_array() + + ((base_type::filter().diameter()/2 - 1) << + image_subpixel_shift); + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + fg = image_filter_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_y(); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg += weight * *fg_ptr; + + fg >>= image_filter_shift; + if(fg > base_mask) fg = base_mask; + + span->v = (value_type)fg; + span->a = base_mask; + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + + + //==================================================span_image_filter_gray + template + class span_image_filter_gray : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_gray() {} + span_image_filter_gray(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + int fg; + const value_type *fg_ptr; + + unsigned diameter = base_type::filter().diameter(); + int start = base_type::filter().start(); + const int16* weight_array = base_type::filter().weight_array(); + + int x_count; + int weight_y; + + do + { + base_type::interpolator().coordinates(&x, &y); + + x -= base_type::filter_dx_int(); + y -= base_type::filter_dy_int(); + + int x_hr = x; + int y_hr = y; + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + fg = image_filter_scale / 2; + + int x_fract = x_hr & image_subpixel_mask; + unsigned y_count = diameter; + + y_hr = image_subpixel_mask - (y_hr & image_subpixel_mask); + fg_ptr = (const value_type*)base_type::source().span(x_lr + start, + y_lr + start, + diameter); + for(;;) + { + x_count = diameter; + weight_y = weight_array[y_hr]; + x_hr = image_subpixel_mask - x_fract; + for(;;) + { + fg += *fg_ptr * + ((weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + image_filter_shift); + if(--x_count == 0) break; + x_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + + if(--y_count == 0) break; + y_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg >>= image_filter_shift; + if(fg < 0) fg = 0; + if(fg > base_mask) fg = base_mask; + span->v = (value_type)fg; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //=========================================span_image_resample_gray_affine + template + class span_image_resample_gray_affine : + public span_image_resample_affine + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef span_image_resample_affine base_type; + typedef typename base_type::interpolator_type interpolator_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_gray_affine() {} + span_image_resample_gray_affine(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + long_type fg; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + int radius_x = (diameter * base_type::m_rx) >> 1; + int radius_y = (diameter * base_type::m_ry) >> 1; + int len_x_lr = + (diameter * base_type::m_rx + image_subpixel_mask) >> + image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + + do + { + base_type::interpolator().coordinates(&x, &y); + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + base_type::m_ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + base_type::m_rx_inv) >> + image_subpixel_shift; + + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + + fg += *fg_ptr * weight; + total_weight += weight; + x_hr += base_type::m_rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += base_type::m_ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg /= total_weight; + if(fg < 0) fg = 0; + if(fg > base_mask) fg = base_mask; + + span->v = (value_type)fg; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + + + //================================================span_image_resample_gray + template + class span_image_resample_gray : + public span_image_resample + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef Interpolator interpolator_type; + typedef span_image_resample base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_gray() {} + span_image_resample_gray(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + long_type fg; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + do + { + int rx; + int ry; + int rx_inv = image_subpixel_scale; + int ry_inv = image_subpixel_scale; + base_type::interpolator().coordinates(&x, &y); + base_type::interpolator().local_scale(&rx, &ry); + base_type::adjust_scale(&rx, &ry); + + rx_inv = image_subpixel_scale * image_subpixel_scale / rx; + ry_inv = image_subpixel_scale * image_subpixel_scale / ry; + + int radius_x = (diameter * rx) >> 1; + int radius_y = (diameter * ry) >> 1; + int len_x_lr = + (diameter * rx + image_subpixel_mask) >> + image_subpixel_shift; + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + rx_inv) >> + image_subpixel_shift; + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + fg += *fg_ptr * weight; + total_weight += weight; + x_hr += rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg /= total_weight; + if(fg < 0) fg = 0; + if(fg > base_mask) fg = base_mask; + + span->v = (value_type)fg; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + +} + + +#endif + + + diff --git a/desmume/src/windows/agg/include/agg_span_image_filter_rgb.h b/desmume/src/windows/agg/include/agg_span_image_filter_rgb.h new file mode 100644 index 000000000..870b5c36c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_image_filter_rgb.h @@ -0,0 +1,901 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_SPAN_IMAGE_FILTER_RGB_INCLUDED +#define AGG_SPAN_IMAGE_FILTER_RGB_INCLUDED + +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_span_image_filter.h" + + +namespace agg +{ + + //===============================================span_image_filter_rgb_nn + template + class span_image_filter_rgb_nn : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgb_nn() {} + span_image_filter_rgb_nn(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + do + { + base_type::interpolator().coordinates(&x, &y); + const value_type* fg_ptr = (const value_type*) + base_type::source().span(x >> image_subpixel_shift, + y >> image_subpixel_shift, + 1); + span->r = fg_ptr[order_type::R]; + span->g = fg_ptr[order_type::G]; + span->b = fg_ptr[order_type::B]; + span->a = base_mask; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //==========================================span_image_filter_rgb_bilinear + template + class span_image_filter_rgb_bilinear : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgb_bilinear() {} + span_image_filter_rgb_bilinear(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + calc_type fg[3]; + const value_type *fg_ptr; + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + + fg[0] = + fg[1] = + fg[2] = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = x_hr * (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_y(); + weight = (image_subpixel_scale - x_hr) * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = x_hr * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + span->r = value_type(fg[order_type::R] >> (image_subpixel_shift * 2)); + span->g = value_type(fg[order_type::G] >> (image_subpixel_shift * 2)); + span->b = value_type(fg[order_type::B] >> (image_subpixel_shift * 2)); + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //=====================================span_image_filter_rgb_bilinear_clip + template + class span_image_filter_rgb_bilinear_clip : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgb_bilinear_clip() {} + span_image_filter_rgb_bilinear_clip(source_type& src, + const color_type& back_color, + interpolator_type& inter) : + base_type(src, inter, 0), + m_back_color(back_color) + {} + const color_type& background_color() const { return m_back_color; } + void background_color(const color_type& v) { m_back_color = v; } + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + calc_type fg[3]; + calc_type src_alpha; + value_type back_r = m_back_color.r; + value_type back_g = m_back_color.g; + value_type back_b = m_back_color.b; + value_type back_a = m_back_color.a; + + const value_type *fg_ptr; + + int maxx = base_type::source().width() - 1; + int maxy = base_type::source().height() - 1; + + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + unsigned weight; + + if(x_lr >= 0 && y_lr >= 0 && + x_lr < maxx && y_lr < maxy) + { + fg[0] = + fg[1] = + fg[2] = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + + weight = x_hr * (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + + ++y_lr; + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + weight = (image_subpixel_scale - x_hr) * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + + weight = x_hr * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + + fg[0] >>= image_subpixel_shift * 2; + fg[1] >>= image_subpixel_shift * 2; + fg[2] >>= image_subpixel_shift * 2; + src_alpha = base_mask; + } + else + { + if(x_lr < -1 || y_lr < -1 || + x_lr > maxx || y_lr > maxy) + { + fg[order_type::R] = back_r; + fg[order_type::G] = back_g; + fg[order_type::B] = back_b; + src_alpha = back_a; + } + else + { + fg[0] = + fg[1] = + fg[2] = + src_alpha = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + src_alpha += weight * base_mask; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + src_alpha += back_a * weight; + } + + x_lr++; + + weight = x_hr * (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + src_alpha += weight * base_mask; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + src_alpha += back_a * weight; + } + + x_lr--; + y_lr++; + + weight = (image_subpixel_scale - x_hr) * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + src_alpha += weight * base_mask; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + src_alpha += back_a * weight; + } + + x_lr++; + + weight = x_hr * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + x_lr + x_lr + x_lr; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + src_alpha += weight * base_mask; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + src_alpha += back_a * weight; + } + + fg[0] >>= image_subpixel_shift * 2; + fg[1] >>= image_subpixel_shift * 2; + fg[2] >>= image_subpixel_shift * 2; + src_alpha >>= image_subpixel_shift * 2; + } + } + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)src_alpha; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + private: + color_type m_back_color; + }; + + + + //===============================================span_image_filter_rgb_2x2 + template + class span_image_filter_rgb_2x2 : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgb_2x2() {} + span_image_filter_rgb_2x2(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + calc_type fg[3]; + + const value_type *fg_ptr; + const int16* weight_array = base_type::filter().weight_array() + + ((base_type::filter().diameter()/2 - 1) << + image_subpixel_shift); + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + fg[0] = fg[1] = fg[2] = image_filter_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_y(); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + fg[0] >>= image_filter_shift; + fg[1] >>= image_filter_shift; + fg[2] >>= image_filter_shift; + + if(fg[order_type::R] > base_mask) fg[order_type::R] = base_mask; + if(fg[order_type::G] > base_mask) fg[order_type::G] = base_mask; + if(fg[order_type::B] > base_mask) fg[order_type::B] = base_mask; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //===================================================span_image_filter_rgb + template + class span_image_filter_rgb : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgb() {} + span_image_filter_rgb(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + int fg[3]; + const value_type *fg_ptr; + + unsigned diameter = base_type::filter().diameter(); + int start = base_type::filter().start(); + const int16* weight_array = base_type::filter().weight_array(); + + int x_count; + int weight_y; + + do + { + base_type::interpolator().coordinates(&x, &y); + + x -= base_type::filter_dx_int(); + y -= base_type::filter_dy_int(); + + int x_hr = x; + int y_hr = y; + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + fg[0] = fg[1] = fg[2] = image_filter_scale / 2; + + int x_fract = x_hr & image_subpixel_mask; + unsigned y_count = diameter; + + y_hr = image_subpixel_mask - (y_hr & image_subpixel_mask); + fg_ptr = (const value_type*)base_type::source().span(x_lr + start, + y_lr + start, + diameter); + for(;;) + { + x_count = diameter; + weight_y = weight_array[y_hr]; + x_hr = image_subpixel_mask - x_fract; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + image_filter_shift; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr; + + if(--x_count == 0) break; + x_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + + if(--y_count == 0) break; + y_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] >>= image_filter_shift; + fg[1] >>= image_filter_shift; + fg[2] >>= image_filter_shift; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + + if(fg[order_type::R] > base_mask) fg[order_type::R] = base_mask; + if(fg[order_type::G] > base_mask) fg[order_type::G] = base_mask; + if(fg[order_type::B] > base_mask) fg[order_type::B] = base_mask; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //==========================================span_image_resample_rgb_affine + template + class span_image_resample_rgb_affine : + public span_image_resample_affine + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef span_image_resample_affine base_type; + typedef typename base_type::interpolator_type interpolator_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_rgb_affine() {} + span_image_resample_rgb_affine(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + long_type fg[3]; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + int radius_x = (diameter * base_type::m_rx) >> 1; + int radius_y = (diameter * base_type::m_ry) >> 1; + int len_x_lr = + (diameter * base_type::m_rx + image_subpixel_mask) >> + image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + + do + { + base_type::interpolator().coordinates(&x, &y); + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg[0] = fg[1] = fg[2] = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + base_type::m_ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + base_type::m_rx_inv) >> + image_subpixel_shift; + + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + + fg[0] += *fg_ptr++ * weight; + fg[1] += *fg_ptr++ * weight; + fg[2] += *fg_ptr * weight; + total_weight += weight; + x_hr += base_type::m_rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += base_type::m_ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] /= total_weight; + fg[1] /= total_weight; + fg[2] /= total_weight; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + + if(fg[order_type::R] > base_mask) fg[order_type::R] = base_mask; + if(fg[order_type::G] > base_mask) fg[order_type::G] = base_mask; + if(fg[order_type::B] > base_mask) fg[order_type::B] = base_mask; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + + + //=================================================span_image_resample_rgb + template + class span_image_resample_rgb : + public span_image_resample + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_resample base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_rgb() {} + span_image_resample_rgb(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + long_type fg[3]; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + do + { + int rx; + int ry; + int rx_inv = image_subpixel_scale; + int ry_inv = image_subpixel_scale; + base_type::interpolator().coordinates(&x, &y); + base_type::interpolator().local_scale(&rx, &ry); + base_type::adjust_scale(&rx, &ry); + + rx_inv = image_subpixel_scale * image_subpixel_scale / rx; + ry_inv = image_subpixel_scale * image_subpixel_scale / ry; + + int radius_x = (diameter * rx) >> 1; + int radius_y = (diameter * ry) >> 1; + int len_x_lr = + (diameter * rx + image_subpixel_mask) >> + image_subpixel_shift; + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg[0] = fg[1] = fg[2] = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + rx_inv) >> + image_subpixel_shift; + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + fg[0] += *fg_ptr++ * weight; + fg[1] += *fg_ptr++ * weight; + fg[2] += *fg_ptr * weight; + total_weight += weight; + x_hr += rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] /= total_weight; + fg[1] /= total_weight; + fg[2] /= total_weight; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + + if(fg[order_type::R] > base_mask) fg[order_type::R] = base_mask; + if(fg[order_type::G] > base_mask) fg[order_type::G] = base_mask; + if(fg[order_type::B] > base_mask) fg[order_type::B] = base_mask; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = base_mask; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + +} + + +#endif + + + diff --git a/desmume/src/windows/agg/include/agg_span_image_filter_rgba.h b/desmume/src/windows/agg/include/agg_span_image_filter_rgba.h new file mode 100644 index 000000000..598ca3780 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_image_filter_rgba.h @@ -0,0 +1,929 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +#ifndef AGG_SPAN_IMAGE_FILTER_RGBA_INCLUDED +#define AGG_SPAN_IMAGE_FILTER_RGBA_INCLUDED + +#include "agg_basics.h" +#include "agg_color_rgba.h" +#include "agg_span_image_filter.h" + + +namespace agg +{ + + //==============================================span_image_filter_rgba_nn + template + class span_image_filter_rgba_nn : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgba_nn() {} + span_image_filter_rgba_nn(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + do + { + base_type::interpolator().coordinates(&x, &y); + const value_type* fg_ptr = (const value_type*) + base_type::source().span(x >> image_subpixel_shift, + y >> image_subpixel_shift, + 1); + span->r = fg_ptr[order_type::R]; + span->g = fg_ptr[order_type::G]; + span->b = fg_ptr[order_type::B]; + span->a = fg_ptr[order_type::A]; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //=========================================span_image_filter_rgba_bilinear + template + class span_image_filter_rgba_bilinear : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgba_bilinear() {} + span_image_filter_rgba_bilinear(source_type& src, + interpolator_type& inter) : + base_type(src, inter, 0) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + calc_type fg[4]; + const value_type *fg_ptr; + + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + + fg[0] = + fg[1] = + fg[2] = + fg[3] = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = x_hr * (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_y(); + weight = (image_subpixel_scale - x_hr) * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = x_hr * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + span->r = value_type(fg[order_type::R] >> (image_subpixel_shift * 2)); + span->g = value_type(fg[order_type::G] >> (image_subpixel_shift * 2)); + span->b = value_type(fg[order_type::B] >> (image_subpixel_shift * 2)); + span->a = value_type(fg[order_type::A] >> (image_subpixel_shift * 2)); + + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + //====================================span_image_filter_rgba_bilinear_clip + template + class span_image_filter_rgba_bilinear_clip : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgba_bilinear_clip() {} + span_image_filter_rgba_bilinear_clip(source_type& src, + const color_type& back_color, + interpolator_type& inter) : + base_type(src, inter, 0), + m_back_color(back_color) + {} + const color_type& background_color() const { return m_back_color; } + void background_color(const color_type& v) { m_back_color = v; } + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + calc_type fg[4]; + value_type back_r = m_back_color.r; + value_type back_g = m_back_color.g; + value_type back_b = m_back_color.b; + value_type back_a = m_back_color.a; + + const value_type *fg_ptr; + int maxx = base_type::source().width() - 1; + int maxy = base_type::source().height() - 1; + + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + + if(x_lr >= 0 && y_lr >= 0 && + x_lr < maxx && y_lr < maxy) + { + fg[0] = + fg[1] = + fg[2] = + fg[3] = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + + weight = x_hr * (image_subpixel_scale - y_hr); + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + + ++y_lr; + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + weight = (image_subpixel_scale - x_hr) * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + + weight = x_hr * y_hr; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + + fg[0] >>= image_subpixel_shift * 2; + fg[1] >>= image_subpixel_shift * 2; + fg[2] >>= image_subpixel_shift * 2; + fg[3] >>= image_subpixel_shift * 2; + } + else + { + if(x_lr < -1 || y_lr < -1 || + x_lr > maxx || y_lr > maxy) + { + fg[order_type::R] = back_r; + fg[order_type::G] = back_g; + fg[order_type::B] = back_b; + fg[order_type::A] = back_a; + } + else + { + fg[0] = + fg[1] = + fg[2] = + fg[3] = image_subpixel_scale * image_subpixel_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + weight = (image_subpixel_scale - x_hr) * + (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + fg[order_type::A] += back_a * weight; + } + + x_lr++; + + weight = x_hr * (image_subpixel_scale - y_hr); + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + fg[order_type::A] += back_a * weight; + } + + x_lr--; + y_lr++; + + weight = (image_subpixel_scale - x_hr) * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + fg[order_type::A] += back_a * weight; + } + + x_lr++; + + weight = x_hr * y_hr; + if(x_lr >= 0 && y_lr >= 0 && + x_lr <= maxx && y_lr <= maxy) + { + fg_ptr = (const value_type*) + base_type::source().row_ptr(y_lr) + (x_lr << 2); + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr++; + } + else + { + fg[order_type::R] += back_r * weight; + fg[order_type::G] += back_g * weight; + fg[order_type::B] += back_b * weight; + fg[order_type::A] += back_a * weight; + } + + fg[0] >>= image_subpixel_shift * 2; + fg[1] >>= image_subpixel_shift * 2; + fg[2] >>= image_subpixel_shift * 2; + fg[3] >>= image_subpixel_shift * 2; + } + } + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)fg[order_type::A]; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + private: + color_type m_back_color; + }; + + + //==============================================span_image_filter_rgba_2x2 + template + class span_image_filter_rgba_2x2 : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgba_2x2() {} + span_image_filter_rgba_2x2(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + calc_type fg[4]; + + const value_type *fg_ptr; + const int16* weight_array = base_type::filter().weight_array() + + ((base_type::filter().diameter()/2 - 1) << + image_subpixel_shift); + + do + { + int x_hr; + int y_hr; + + base_type::interpolator().coordinates(&x_hr, &y_hr); + + x_hr -= base_type::filter_dx_int(); + y_hr -= base_type::filter_dy_int(); + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + unsigned weight; + fg[0] = fg[1] = fg[2] = fg[3] = image_filter_scale / 2; + + x_hr &= image_subpixel_mask; + y_hr &= image_subpixel_mask; + + fg_ptr = (const value_type*)base_type::source().span(x_lr, y_lr, 2); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr + image_subpixel_scale] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_y(); + weight = (weight_array[x_hr + image_subpixel_scale] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg_ptr = (const value_type*)base_type::source().next_x(); + weight = (weight_array[x_hr] * + weight_array[y_hr] + + image_filter_scale / 2) >> + image_filter_shift; + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + fg[0] >>= image_filter_shift; + fg[1] >>= image_filter_shift; + fg[2] >>= image_filter_shift; + fg[3] >>= image_filter_shift; + + if(fg[order_type::A] > base_mask) fg[order_type::A] = base_mask; + if(fg[order_type::R] > fg[order_type::A]) fg[order_type::R] = fg[order_type::A]; + if(fg[order_type::G] > fg[order_type::A]) fg[order_type::G] = fg[order_type::A]; + if(fg[order_type::B] > fg[order_type::A]) fg[order_type::B] = fg[order_type::A]; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)fg[order_type::A]; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //==================================================span_image_filter_rgba + template + class span_image_filter_rgba : + public span_image_filter + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_filter base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask + }; + + //-------------------------------------------------------------------- + span_image_filter_rgba() {} + span_image_filter_rgba(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, &filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + int fg[4]; + const value_type *fg_ptr; + + unsigned diameter = base_type::filter().diameter(); + int start = base_type::filter().start(); + const int16* weight_array = base_type::filter().weight_array(); + + int x_count; + int weight_y; + + do + { + base_type::interpolator().coordinates(&x, &y); + + x -= base_type::filter_dx_int(); + y -= base_type::filter_dy_int(); + + int x_hr = x; + int y_hr = y; + + int x_lr = x_hr >> image_subpixel_shift; + int y_lr = y_hr >> image_subpixel_shift; + + fg[0] = fg[1] = fg[2] = fg[3] = image_filter_scale / 2; + + int x_fract = x_hr & image_subpixel_mask; + unsigned y_count = diameter; + + y_hr = image_subpixel_mask - (y_hr & image_subpixel_mask); + fg_ptr = (const value_type*)base_type::source().span(x_lr + start, + y_lr + start, + diameter); + for(;;) + { + x_count = diameter; + weight_y = weight_array[y_hr]; + x_hr = image_subpixel_mask - x_fract; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + image_filter_shift; + + fg[0] += weight * *fg_ptr++; + fg[1] += weight * *fg_ptr++; + fg[2] += weight * *fg_ptr++; + fg[3] += weight * *fg_ptr; + + if(--x_count == 0) break; + x_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + + if(--y_count == 0) break; + y_hr += image_subpixel_scale; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] >>= image_filter_shift; + fg[1] >>= image_filter_shift; + fg[2] >>= image_filter_shift; + fg[3] >>= image_filter_shift; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + if(fg[3] < 0) fg[3] = 0; + + if(fg[order_type::A] > base_mask) fg[order_type::A] = base_mask; + if(fg[order_type::R] > fg[order_type::A]) fg[order_type::R] = fg[order_type::A]; + if(fg[order_type::G] > fg[order_type::A]) fg[order_type::G] = fg[order_type::A]; + if(fg[order_type::B] > fg[order_type::A]) fg[order_type::B] = fg[order_type::A]; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)fg[order_type::A]; + ++span; + ++base_type::interpolator(); + + } while(--len); + } + }; + + + + //========================================span_image_resample_rgba_affine + template + class span_image_resample_rgba_affine : + public span_image_resample_affine + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef span_image_resample_affine base_type; + typedef typename base_type::interpolator_type interpolator_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_rgba_affine() {} + span_image_resample_rgba_affine(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + + long_type fg[4]; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + int radius_x = (diameter * base_type::m_rx) >> 1; + int radius_y = (diameter * base_type::m_ry) >> 1; + int len_x_lr = + (diameter * base_type::m_rx + image_subpixel_mask) >> + image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + + do + { + base_type::interpolator().coordinates(&x, &y); + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg[0] = fg[1] = fg[2] = fg[3] = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + base_type::m_ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + base_type::m_rx_inv) >> + image_subpixel_shift; + + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + + fg[0] += *fg_ptr++ * weight; + fg[1] += *fg_ptr++ * weight; + fg[2] += *fg_ptr++ * weight; + fg[3] += *fg_ptr++ * weight; + total_weight += weight; + x_hr += base_type::m_rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += base_type::m_ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] /= total_weight; + fg[1] /= total_weight; + fg[2] /= total_weight; + fg[3] /= total_weight; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + if(fg[3] < 0) fg[3] = 0; + + if(fg[order_type::A] > base_mask) fg[order_type::A] = base_mask; + if(fg[order_type::R] > fg[order_type::A]) fg[order_type::R] = fg[order_type::A]; + if(fg[order_type::G] > fg[order_type::A]) fg[order_type::G] = fg[order_type::A]; + if(fg[order_type::B] > fg[order_type::A]) fg[order_type::B] = fg[order_type::A]; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)fg[order_type::A]; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + + + //==============================================span_image_resample_rgba + template + class span_image_resample_rgba : + public span_image_resample + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef Interpolator interpolator_type; + typedef span_image_resample base_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::long_type long_type; + enum base_scale_e + { + base_shift = color_type::base_shift, + base_mask = color_type::base_mask, + downscale_shift = image_filter_shift + }; + + //-------------------------------------------------------------------- + span_image_resample_rgba() {} + span_image_resample_rgba(source_type& src, + interpolator_type& inter, + const image_filter_lut& filter) : + base_type(src, inter, filter) + {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + base_type::interpolator().begin(x + base_type::filter_dx_dbl(), + y + base_type::filter_dy_dbl(), len); + long_type fg[4]; + + int diameter = base_type::filter().diameter(); + int filter_scale = diameter << image_subpixel_shift; + + const int16* weight_array = base_type::filter().weight_array(); + do + { + int rx; + int ry; + int rx_inv = image_subpixel_scale; + int ry_inv = image_subpixel_scale; + base_type::interpolator().coordinates(&x, &y); + base_type::interpolator().local_scale(&rx, &ry); + base_type::adjust_scale(&rx, &ry); + + rx_inv = image_subpixel_scale * image_subpixel_scale / rx; + ry_inv = image_subpixel_scale * image_subpixel_scale / ry; + + int radius_x = (diameter * rx) >> 1; + int radius_y = (diameter * ry) >> 1; + int len_x_lr = + (diameter * rx + image_subpixel_mask) >> + image_subpixel_shift; + + x += base_type::filter_dx_int() - radius_x; + y += base_type::filter_dy_int() - radius_y; + + fg[0] = fg[1] = fg[2] = fg[3] = image_filter_scale / 2; + + int y_lr = y >> image_subpixel_shift; + int y_hr = ((image_subpixel_mask - (y & image_subpixel_mask)) * + ry_inv) >> + image_subpixel_shift; + int total_weight = 0; + int x_lr = x >> image_subpixel_shift; + int x_hr = ((image_subpixel_mask - (x & image_subpixel_mask)) * + rx_inv) >> + image_subpixel_shift; + int x_hr2 = x_hr; + const value_type* fg_ptr = + (const value_type*)base_type::source().span(x_lr, y_lr, len_x_lr); + + for(;;) + { + int weight_y = weight_array[y_hr]; + x_hr = x_hr2; + for(;;) + { + int weight = (weight_y * weight_array[x_hr] + + image_filter_scale / 2) >> + downscale_shift; + fg[0] += *fg_ptr++ * weight; + fg[1] += *fg_ptr++ * weight; + fg[2] += *fg_ptr++ * weight; + fg[3] += *fg_ptr++ * weight; + total_weight += weight; + x_hr += rx_inv; + if(x_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_x(); + } + y_hr += ry_inv; + if(y_hr >= filter_scale) break; + fg_ptr = (const value_type*)base_type::source().next_y(); + } + + fg[0] /= total_weight; + fg[1] /= total_weight; + fg[2] /= total_weight; + fg[3] /= total_weight; + + if(fg[0] < 0) fg[0] = 0; + if(fg[1] < 0) fg[1] = 0; + if(fg[2] < 0) fg[2] = 0; + if(fg[3] < 0) fg[3] = 0; + + if(fg[order_type::A] > base_mask) fg[order_type::A] = base_mask; + if(fg[order_type::R] > fg[order_type::R]) fg[order_type::R] = fg[order_type::R]; + if(fg[order_type::G] > fg[order_type::G]) fg[order_type::G] = fg[order_type::G]; + if(fg[order_type::B] > fg[order_type::B]) fg[order_type::B] = fg[order_type::B]; + + span->r = (value_type)fg[order_type::R]; + span->g = (value_type)fg[order_type::G]; + span->b = (value_type)fg[order_type::B]; + span->a = (value_type)fg[order_type::A]; + + ++span; + ++base_type::interpolator(); + } while(--len); + } + }; + + +} + + +#endif + + + diff --git a/desmume/src/windows/agg/include/agg_span_interpolator_adaptor.h b/desmume/src/windows/agg/include/agg_span_interpolator_adaptor.h new file mode 100644 index 000000000..b3ff22e23 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_interpolator_adaptor.h @@ -0,0 +1,86 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_INTERPOLATOR_ADAPTOR_INCLUDED +#define AGG_SPAN_INTERPOLATOR_ADAPTOR_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //===============================================span_interpolator_adaptor + template + class span_interpolator_adaptor : public Interpolator + { + public: + typedef Interpolator base_type; + typedef typename base_type::trans_type trans_type; + typedef Distortion distortion_type; + + //-------------------------------------------------------------------- + span_interpolator_adaptor() {} + span_interpolator_adaptor(const trans_type& trans, + const distortion_type& dist) : + base_type(trans), + m_distortion(&dist) + { + } + + //-------------------------------------------------------------------- + span_interpolator_adaptor(const trans_type& trans, + const distortion_type& dist, + double x, double y, unsigned len) : + base_type(trans, x, y, len), + m_distortion(&dist) + { + } + + //-------------------------------------------------------------------- + const distortion_type& distortion() const + { + return *m_distortion; + } + + //-------------------------------------------------------------------- + void distortion(const distortion_type& dist) + { + m_distortion = dist; + } + + //-------------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + base_type::coordinates(x, y); + m_distortion->calculate(x, y); + } + + private: + //-------------------------------------------------------------------- + const distortion_type* m_distortion; + }; +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_interpolator_linear.h b/desmume/src/windows/agg/include/agg_span_interpolator_linear.h new file mode 100644 index 000000000..c9bfafdd3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_interpolator_linear.h @@ -0,0 +1,241 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_INTERPOLATOR_LINEAR_INCLUDED +#define AGG_SPAN_INTERPOLATOR_LINEAR_INCLUDED + +#include "agg_basics.h" +#include "agg_dda_line.h" +#include "agg_trans_affine.h" + +namespace agg +{ + + //================================================span_interpolator_linear + template + class span_interpolator_linear + { + public: + typedef Transformer trans_type; + + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + //-------------------------------------------------------------------- + span_interpolator_linear() {} + span_interpolator_linear(const trans_type& trans) : m_trans(&trans) {} + span_interpolator_linear(const trans_type& trans, + double x, double y, unsigned len) : + m_trans(&trans) + { + begin(x, y, len); + } + + //---------------------------------------------------------------- + const trans_type& transformer() const { return *m_trans; } + void transformer(const trans_type& trans) { m_trans = &trans; } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + double tx; + double ty; + + tx = x; + ty = y; + m_trans->transform(&tx, &ty); + int x1 = iround(tx * subpixel_scale); + int y1 = iround(ty * subpixel_scale); + + tx = x + len; + ty = y; + m_trans->transform(&tx, &ty); + int x2 = iround(tx * subpixel_scale); + int y2 = iround(ty * subpixel_scale); + + m_li_x = dda2_line_interpolator(x1, x2, len); + m_li_y = dda2_line_interpolator(y1, y2, len); + } + + //---------------------------------------------------------------- + void resynchronize(double xe, double ye, unsigned len) + { + m_trans->transform(&xe, &ye); + m_li_x = dda2_line_interpolator(m_li_x.y(), iround(xe * subpixel_scale), len); + m_li_y = dda2_line_interpolator(m_li_y.y(), iround(ye * subpixel_scale), len); + } + + //---------------------------------------------------------------- + void operator++() + { + ++m_li_x; + ++m_li_y; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + *x = m_li_x.y(); + *y = m_li_y.y(); + } + + private: + const trans_type* m_trans; + dda2_line_interpolator m_li_x; + dda2_line_interpolator m_li_y; + }; + + + + + + + //=====================================span_interpolator_linear_subdiv + template + class span_interpolator_linear_subdiv + { + public: + typedef Transformer trans_type; + + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + + //---------------------------------------------------------------- + span_interpolator_linear_subdiv() : + m_subdiv_shift(4), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1) {} + + span_interpolator_linear_subdiv(const trans_type& trans, + unsigned subdiv_shift = 4) : + m_subdiv_shift(subdiv_shift), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1), + m_trans(&trans) {} + + span_interpolator_linear_subdiv(const trans_type& trans, + double x, double y, unsigned len, + unsigned subdiv_shift = 4) : + m_subdiv_shift(subdiv_shift), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1), + m_trans(&trans) + { + begin(x, y, len); + } + + //---------------------------------------------------------------- + const trans_type& transformer() const { return *m_trans; } + void transformer(const trans_type& trans) { m_trans = &trans; } + + //---------------------------------------------------------------- + unsigned subdiv_shift() const { return m_subdiv_shift; } + void subdiv_shift(unsigned shift) + { + m_subdiv_shift = shift; + m_subdiv_size = 1 << m_subdiv_shift; + m_subdiv_mask = m_subdiv_size - 1; + } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + double tx; + double ty; + m_pos = 1; + m_src_x = iround(x * subpixel_scale) + subpixel_scale; + m_src_y = y; + m_len = len; + + if(len > m_subdiv_size) len = m_subdiv_size; + tx = x; + ty = y; + m_trans->transform(&tx, &ty); + int x1 = iround(tx * subpixel_scale); + int y1 = iround(ty * subpixel_scale); + + tx = x + len; + ty = y; + m_trans->transform(&tx, &ty); + + m_li_x = dda2_line_interpolator(x1, iround(tx * subpixel_scale), len); + m_li_y = dda2_line_interpolator(y1, iround(ty * subpixel_scale), len); + } + + //---------------------------------------------------------------- + void operator++() + { + ++m_li_x; + ++m_li_y; + if(m_pos >= m_subdiv_size) + { + unsigned len = m_len; + if(len > m_subdiv_size) len = m_subdiv_size; + double tx = double(m_src_x) / double(subpixel_scale) + len; + double ty = m_src_y; + m_trans->transform(&tx, &ty); + m_li_x = dda2_line_interpolator(m_li_x.y(), iround(tx * subpixel_scale), len); + m_li_y = dda2_line_interpolator(m_li_y.y(), iround(ty * subpixel_scale), len); + m_pos = 0; + } + m_src_x += subpixel_scale; + ++m_pos; + --m_len; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + *x = m_li_x.y(); + *y = m_li_y.y(); + } + + private: + unsigned m_subdiv_shift; + unsigned m_subdiv_size; + unsigned m_subdiv_mask; + const trans_type* m_trans; + dda2_line_interpolator m_li_x; + dda2_line_interpolator m_li_y; + int m_src_x; + double m_src_y; + unsigned m_pos; + unsigned m_len; + }; + + +} + + + +#endif + + diff --git a/desmume/src/windows/agg/include/agg_span_interpolator_persp.h b/desmume/src/windows/agg/include/agg_span_interpolator_persp.h new file mode 100644 index 000000000..1d9863a43 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_interpolator_persp.h @@ -0,0 +1,472 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_INTERPOLATOR_PERSP_INCLUDED +#define AGG_SPAN_INTERPOLATOR_PERSP_INCLUDED + +#include "agg_trans_perspective.h" +#include "agg_dda_line.h" + +namespace agg +{ + + + + //===========================================span_interpolator_persp_exact + template + class span_interpolator_persp_exact + { + public: + typedef trans_perspective trans_type; + typedef trans_perspective::iterator_x iterator_type; + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + //-------------------------------------------------------------------- + span_interpolator_persp_exact() {} + + //-------------------------------------------------------------------- + // Arbitrary quadrangle transformations + span_interpolator_persp_exact(const double* src, const double* dst) + { + quad_to_quad(src, dst); + } + + //-------------------------------------------------------------------- + // Direct transformations + span_interpolator_persp_exact(double x1, double y1, + double x2, double y2, + const double* quad) + { + rect_to_quad(x1, y1, x2, y2, quad); + } + + //-------------------------------------------------------------------- + // Reverse transformations + span_interpolator_persp_exact(const double* quad, + double x1, double y1, + double x2, double y2) + { + quad_to_rect(quad, x1, y1, x2, y2); + } + + //-------------------------------------------------------------------- + // Set the transformations using two arbitrary quadrangles. + void quad_to_quad(const double* src, const double* dst) + { + m_trans_dir.quad_to_quad(src, dst); + m_trans_inv.quad_to_quad(dst, src); + } + + //-------------------------------------------------------------------- + // Set the direct transformations, i.e., rectangle -> quadrangle + void rect_to_quad(double x1, double y1, double x2, double y2, + const double* quad) + { + double src[8]; + src[0] = src[6] = x1; + src[2] = src[4] = x2; + src[1] = src[3] = y1; + src[5] = src[7] = y2; + quad_to_quad(src, quad); + } + + + //-------------------------------------------------------------------- + // Set the reverse transformations, i.e., quadrangle -> rectangle + void quad_to_rect(const double* quad, + double x1, double y1, double x2, double y2) + { + double dst[8]; + dst[0] = dst[6] = x1; + dst[2] = dst[4] = x2; + dst[1] = dst[3] = y1; + dst[5] = dst[7] = y2; + quad_to_quad(quad, dst); + } + + //-------------------------------------------------------------------- + // Check if the equations were solved successfully + bool is_valid() const { return m_trans_dir.is_valid(); } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + m_iterator = m_trans_dir.begin(x, y, 1.0); + double xt = m_iterator.x; + double yt = m_iterator.y; + + double dx; + double dy; + const double delta = 1/double(subpixel_scale); + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sx1 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sy1 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + x += len; + xt = x; + yt = y; + m_trans_dir.transform(&xt, &yt); + + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sx2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sy2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + m_scale_x = dda2_line_interpolator(sx1, sx2, len); + m_scale_y = dda2_line_interpolator(sy1, sy2, len); + } + + + //---------------------------------------------------------------- + void resynchronize(double xe, double ye, unsigned len) + { + // Assume x1,y1 are equal to the ones at the previous end point + int sx1 = m_scale_x.y(); + int sy1 = m_scale_y.y(); + + // Calculate transformed coordinates at x2,y2 + double xt = xe; + double yt = ye; + m_trans_dir.transform(&xt, &yt); + + const double delta = 1/double(subpixel_scale); + double dx; + double dy; + + // Calculate scale by X at x2,y2 + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= xe; + dy -= ye; + int sx2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Calculate scale by Y at x2,y2 + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= xe; + dy -= ye; + int sy2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Initialize the interpolators + m_scale_x = dda2_line_interpolator(sx1, sx2, len); + m_scale_y = dda2_line_interpolator(sy1, sy2, len); + } + + + + //---------------------------------------------------------------- + void operator++() + { + ++m_iterator; + ++m_scale_x; + ++m_scale_y; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + *x = iround(m_iterator.x * subpixel_scale); + *y = iround(m_iterator.y * subpixel_scale); + } + + //---------------------------------------------------------------- + void local_scale(int* x, int* y) + { + *x = m_scale_x.y(); + *y = m_scale_y.y(); + } + + //---------------------------------------------------------------- + void transform(double* x, double* y) const + { + m_trans_dir.transform(x, y); + } + + private: + trans_type m_trans_dir; + trans_type m_trans_inv; + iterator_type m_iterator; + dda2_line_interpolator m_scale_x; + dda2_line_interpolator m_scale_y; + }; + + + + + + + + + + + + //============================================span_interpolator_persp_lerp + template + class span_interpolator_persp_lerp + { + public: + typedef trans_perspective trans_type; + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + //-------------------------------------------------------------------- + span_interpolator_persp_lerp() {} + + //-------------------------------------------------------------------- + // Arbitrary quadrangle transformations + span_interpolator_persp_lerp(const double* src, const double* dst) + { + quad_to_quad(src, dst); + } + + //-------------------------------------------------------------------- + // Direct transformations + span_interpolator_persp_lerp(double x1, double y1, + double x2, double y2, + const double* quad) + { + rect_to_quad(x1, y1, x2, y2, quad); + } + + //-------------------------------------------------------------------- + // Reverse transformations + span_interpolator_persp_lerp(const double* quad, + double x1, double y1, + double x2, double y2) + { + quad_to_rect(quad, x1, y1, x2, y2); + } + + //-------------------------------------------------------------------- + // Set the transformations using two arbitrary quadrangles. + void quad_to_quad(const double* src, const double* dst) + { + m_trans_dir.quad_to_quad(src, dst); + m_trans_inv.quad_to_quad(dst, src); + } + + //-------------------------------------------------------------------- + // Set the direct transformations, i.e., rectangle -> quadrangle + void rect_to_quad(double x1, double y1, double x2, double y2, + const double* quad) + { + double src[8]; + src[0] = src[6] = x1; + src[2] = src[4] = x2; + src[1] = src[3] = y1; + src[5] = src[7] = y2; + quad_to_quad(src, quad); + } + + + //-------------------------------------------------------------------- + // Set the reverse transformations, i.e., quadrangle -> rectangle + void quad_to_rect(const double* quad, + double x1, double y1, double x2, double y2) + { + double dst[8]; + dst[0] = dst[6] = x1; + dst[2] = dst[4] = x2; + dst[1] = dst[3] = y1; + dst[5] = dst[7] = y2; + quad_to_quad(quad, dst); + } + + //-------------------------------------------------------------------- + // Check if the equations were solved successfully + bool is_valid() const { return m_trans_dir.is_valid(); } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + // Calculate transformed coordinates at x1,y1 + double xt = x; + double yt = y; + m_trans_dir.transform(&xt, &yt); + int x1 = iround(xt * subpixel_scale); + int y1 = iround(yt * subpixel_scale); + + double dx; + double dy; + const double delta = 1/double(subpixel_scale); + + // Calculate scale by X at x1,y1 + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sx1 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Calculate scale by Y at x1,y1 + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sy1 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Calculate transformed coordinates at x2,y2 + x += len; + xt = x; + yt = y; + m_trans_dir.transform(&xt, &yt); + int x2 = iround(xt * subpixel_scale); + int y2 = iround(yt * subpixel_scale); + + // Calculate scale by X at x2,y2 + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sx2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Calculate scale by Y at x2,y2 + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= x; + dy -= y; + int sy2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Initialize the interpolators + m_coord_x = dda2_line_interpolator(x1, x2, len); + m_coord_y = dda2_line_interpolator(y1, y2, len); + m_scale_x = dda2_line_interpolator(sx1, sx2, len); + m_scale_y = dda2_line_interpolator(sy1, sy2, len); + } + + + //---------------------------------------------------------------- + void resynchronize(double xe, double ye, unsigned len) + { + // Assume x1,y1 are equal to the ones at the previous end point + int x1 = m_coord_x.y(); + int y1 = m_coord_y.y(); + int sx1 = m_scale_x.y(); + int sy1 = m_scale_y.y(); + + // Calculate transformed coordinates at x2,y2 + double xt = xe; + double yt = ye; + m_trans_dir.transform(&xt, &yt); + int x2 = iround(xt * subpixel_scale); + int y2 = iround(yt * subpixel_scale); + + const double delta = 1/double(subpixel_scale); + double dx; + double dy; + + // Calculate scale by X at x2,y2 + dx = xt + delta; + dy = yt; + m_trans_inv.transform(&dx, &dy); + dx -= xe; + dy -= ye; + int sx2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Calculate scale by Y at x2,y2 + dx = xt; + dy = yt + delta; + m_trans_inv.transform(&dx, &dy); + dx -= xe; + dy -= ye; + int sy2 = uround(subpixel_scale/sqrt(dx*dx + dy*dy)) >> subpixel_shift; + + // Initialize the interpolators + m_coord_x = dda2_line_interpolator(x1, x2, len); + m_coord_y = dda2_line_interpolator(y1, y2, len); + m_scale_x = dda2_line_interpolator(sx1, sx2, len); + m_scale_y = dda2_line_interpolator(sy1, sy2, len); + } + + + //---------------------------------------------------------------- + void operator++() + { + ++m_coord_x; + ++m_coord_y; + ++m_scale_x; + ++m_scale_y; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + *x = m_coord_x.y(); + *y = m_coord_y.y(); + } + + //---------------------------------------------------------------- + void local_scale(int* x, int* y) + { + *x = m_scale_x.y(); + *y = m_scale_y.y(); + } + + //---------------------------------------------------------------- + void transform(double* x, double* y) const + { + m_trans_dir.transform(x, y); + } + + private: + trans_type m_trans_dir; + trans_type m_trans_inv; + dda2_line_interpolator m_coord_x; + dda2_line_interpolator m_coord_y; + dda2_line_interpolator m_scale_x; + dda2_line_interpolator m_scale_y; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_interpolator_trans.h b/desmume/src/windows/agg/include/agg_span_interpolator_trans.h new file mode 100644 index 000000000..0de1c19dc --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_interpolator_trans.h @@ -0,0 +1,101 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Horizontal span interpolator for use with an arbitrary transformer +// The efficiency highly depends on the operations done in the transformer +// +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_INTERPOLATOR_TRANS_INCLUDED +#define AGG_SPAN_INTERPOLATOR_TRANS_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //=================================================span_interpolator_trans + template + class span_interpolator_trans + { + public: + typedef Transformer trans_type; + enum subpixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + //-------------------------------------------------------------------- + span_interpolator_trans() {} + span_interpolator_trans(const trans_type& trans) : m_trans(&trans) {} + span_interpolator_trans(const trans_type& trans, + double x, double y, unsigned) : + m_trans(&trans) + { + begin(x, y, 0); + } + + //---------------------------------------------------------------- + const trans_type& transformer() const { return *m_trans; } + void transformer(const trans_type& trans) { m_trans = &trans; } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned) + { + m_x = x; + m_y = y; + m_trans->transform(&x, &y); + m_ix = iround(x * subpixel_scale); + m_iy = iround(y * subpixel_scale); + } + + //---------------------------------------------------------------- + void operator++() + { + m_x += 1.0; + double x = m_x; + double y = m_y; + m_trans->transform(&x, &y); + m_ix = iround(x * subpixel_scale); + m_iy = iround(y * subpixel_scale); + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + *x = m_ix; + *y = m_iy; + } + + private: + const trans_type* m_trans; + double m_x; + double m_y; + int m_ix; + int m_iy; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_pattern_gray.h b/desmume/src/windows/agg/include/agg_span_pattern_gray.h new file mode 100644 index 000000000..72d2c2708 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_pattern_gray.h @@ -0,0 +1,102 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + + +#ifndef AGG_SPAN_PATTERN_GRAY_INCLUDED +#define AGG_SPAN_PATTERN_GRAY_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //=======================================================span_pattern_gray + template class span_pattern_gray + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + span_pattern_gray() {} + span_pattern_gray(source_type& src, + unsigned offset_x, unsigned offset_y) : + m_src(&src), + m_offset_x(offset_x), + m_offset_y(offset_y), + m_alpha(color_type::base_mask) + {} + + //-------------------------------------------------------------------- + void attach(source_type& v) { m_src = &v; } + source_type& source() { return *m_src; } + const source_type& source() const { return *m_src; } + + //-------------------------------------------------------------------- + void offset_x(unsigned v) { m_offset_x = v; } + void offset_y(unsigned v) { m_offset_y = v; } + unsigned offset_x() const { return m_offset_x; } + unsigned offset_y() const { return m_offset_y; } + void alpha(value_type v) { m_alpha = v; } + value_type alpha() const { return m_alpha; } + + //-------------------------------------------------------------------- + void prepare() {} + void generate(color_type* span, int x, int y, unsigned len) + { + x += m_offset_x; + y += m_offset_y; + const value_type* p = (const value_type*)m_src->span(x, y, len); + do + { + span->v = *p; + span->a = m_alpha; + p = m_src->next_x(); + ++span; + } + while(--len); + } + + private: + source_type* m_src; + unsigned m_offset_x; + unsigned m_offset_y; + value_type m_alpha; + + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_span_pattern_rgb.h b/desmume/src/windows/agg/include/agg_span_pattern_rgb.h new file mode 100644 index 000000000..a2e272f0b --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_pattern_rgb.h @@ -0,0 +1,105 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + + +#ifndef AGG_SPAN_PATTERN_RGB_INCLUDED +#define AGG_SPAN_PATTERN_RGB_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //========================================================span_pattern_rgb + template class span_pattern_rgb + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + span_pattern_rgb() {} + span_pattern_rgb(source_type& src, + unsigned offset_x, unsigned offset_y) : + m_src(&src), + m_offset_x(offset_x), + m_offset_y(offset_y), + m_alpha(color_type::base_mask) + {} + + //-------------------------------------------------------------------- + void attach(source_type& v) { m_src = &v; } + source_type& source() { return *m_src; } + const source_type& source() const { return *m_src; } + + //-------------------------------------------------------------------- + void offset_x(unsigned v) { m_offset_x = v; } + void offset_y(unsigned v) { m_offset_y = v; } + unsigned offset_x() const { return m_offset_x; } + unsigned offset_y() const { return m_offset_y; } + void alpha(value_type v) { m_alpha = v; } + value_type alpha() const { return m_alpha; } + + //-------------------------------------------------------------------- + void prepare() {} + void generate(color_type* span, int x, int y, unsigned len) + { + x += m_offset_x; + y += m_offset_y; + const value_type* p = (const value_type*)m_src->span(x, y, len); + do + { + span->r = p[order_type::R]; + span->g = p[order_type::G]; + span->b = p[order_type::B]; + span->a = m_alpha; + p = m_src->next_x(); + ++span; + } + while(--len); + } + + private: + source_type* m_src; + unsigned m_offset_x; + unsigned m_offset_y; + value_type m_alpha; + + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_span_pattern_rgba.h b/desmume/src/windows/agg/include/agg_span_pattern_rgba.h new file mode 100644 index 000000000..248ae265c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_pattern_rgba.h @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Adaptation for high precision colors has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- + + +#ifndef AGG_SPAN_PATTERN_RGBA_INCLUDED +#define AGG_SPAN_PATTERN_RGBA_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //======================================================span_pattern_rgba + template class span_pattern_rgba + { + public: + typedef Source source_type; + typedef typename source_type::color_type color_type; + typedef typename source_type::order_type order_type; + typedef typename color_type::value_type value_type; + typedef typename color_type::calc_type calc_type; + + //-------------------------------------------------------------------- + span_pattern_rgba() {} + span_pattern_rgba(source_type& src, + unsigned offset_x, unsigned offset_y) : + m_src(&src), + m_offset_x(offset_x), + m_offset_y(offset_y) + {} + + //-------------------------------------------------------------------- + void attach(source_type& v) { m_src = &v; } + source_type& source() { return *m_src; } + const source_type& source() const { return *m_src; } + + //-------------------------------------------------------------------- + void offset_x(unsigned v) { m_offset_x = v; } + void offset_y(unsigned v) { m_offset_y = v; } + unsigned offset_x() const { return m_offset_x; } + unsigned offset_y() const { return m_offset_y; } + void alpha(value_type) {} + value_type alpha() const { return 0; } + + //-------------------------------------------------------------------- + void prepare() {} + void generate(color_type* span, int x, int y, unsigned len) + { + x += m_offset_x; + y += m_offset_y; + const value_type* p = (const value_type*)m_src->span(x, y, len); + do + { + span->r = p[order_type::R]; + span->g = p[order_type::G]; + span->b = p[order_type::B]; + span->a = p[order_type::A]; + p = (const value_type*)m_src->next_x(); + ++span; + } + while(--len); + } + + private: + source_type* m_src; + unsigned m_offset_x; + unsigned m_offset_y; + + }; + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_span_solid.h b/desmume/src/windows/agg/include/agg_span_solid.h new file mode 100644 index 000000000..f82f5dc2c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_solid.h @@ -0,0 +1,58 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_SOLID_INCLUDED +#define AGG_SPAN_SOLID_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + //--------------------------------------------------------------span_solid + template class span_solid + { + public: + typedef ColorT color_type; + + //-------------------------------------------------------------------- + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } + + //-------------------------------------------------------------------- + void prepare() {} + + //-------------------------------------------------------------------- + void generate(color_type* span, int x, int y, unsigned len) + { + do { *span++ = m_color; } while(--len); + } + + private: + color_type m_color; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_span_subdiv_adaptor.h b/desmume/src/windows/agg/include/agg_span_subdiv_adaptor.h new file mode 100644 index 000000000..5edab67b6 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_span_subdiv_adaptor.h @@ -0,0 +1,151 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPAN_SUBDIV_ADAPTOR_INCLUDED +#define AGG_SPAN_SUBDIV_ADAPTOR_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //=================================================span_subdiv_adaptor + template + class span_subdiv_adaptor + { + public: + typedef Interpolator interpolator_type; + typedef typename interpolator_type::trans_type trans_type; + + enum sublixel_scale_e + { + subpixel_shift = SubpixelShift, + subpixel_scale = 1 << subpixel_shift + }; + + + //---------------------------------------------------------------- + span_subdiv_adaptor() : + m_subdiv_shift(4), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1) {} + + span_subdiv_adaptor(interpolator_type& interpolator, + unsigned subdiv_shift = 4) : + m_subdiv_shift(subdiv_shift), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1), + m_interpolator(&interpolator) {} + + span_subdiv_adaptor(interpolator_type& interpolator, + double x, double y, unsigned len, + unsigned subdiv_shift = 4) : + m_subdiv_shift(subdiv_shift), + m_subdiv_size(1 << m_subdiv_shift), + m_subdiv_mask(m_subdiv_size - 1), + m_interpolator(&interpolator) + { + begin(x, y, len); + } + + + //---------------------------------------------------------------- + const interpolator_type& interpolator() const { return *m_interpolator; } + void interpolator(interpolator_type& intr) { m_interpolator = &intr; } + + //---------------------------------------------------------------- + const trans_type& transformer() const + { + return *m_interpolator->transformer(); + } + void transformer(const trans_type& trans) + { + m_interpolator->transformer(trans); + } + + //---------------------------------------------------------------- + unsigned subdiv_shift() const { return m_subdiv_shift; } + void subdiv_shift(unsigned shift) + { + m_subdiv_shift = shift; + m_subdiv_size = 1 << m_subdiv_shift; + m_subdiv_mask = m_subdiv_size - 1; + } + + //---------------------------------------------------------------- + void begin(double x, double y, unsigned len) + { + m_pos = 1; + m_src_x = iround(x * subpixel_scale) + subpixel_scale; + m_src_y = y; + m_len = len; + if(len > m_subdiv_size) len = m_subdiv_size; + m_interpolator->begin(x, y, len); + } + + //---------------------------------------------------------------- + void operator++() + { + ++(*m_interpolator); + if(m_pos >= m_subdiv_size) + { + unsigned len = m_len; + if(len > m_subdiv_size) len = m_subdiv_size; + m_interpolator->resynchronize(double(m_src_x) / double(subpixel_scale) + len, + m_src_y, + len); + m_pos = 0; + } + m_src_x += subpixel_scale; + ++m_pos; + --m_len; + } + + //---------------------------------------------------------------- + void coordinates(int* x, int* y) const + { + m_interpolator->coordinates(x, y); + } + + //---------------------------------------------------------------- + void local_scale(int* x, int* y) const + { + m_interpolator->local_scale(x, y); + } + + + private: + unsigned m_subdiv_shift; + unsigned m_subdiv_size; + unsigned m_subdiv_mask; + interpolator_type* m_interpolator; + int m_src_x; + double m_src_y; + unsigned m_pos; + unsigned m_len; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_trans_affine.h b/desmume/src/windows/agg/include/agg_trans_affine.h new file mode 100644 index 000000000..a9d2bd0a3 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_affine.h @@ -0,0 +1,524 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_AFFINE_INCLUDED +#define AGG_TRANS_AFFINE_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + const double affine_epsilon = 1e-14; + + //============================================================trans_affine + // + // See Implementation agg_trans_affine.cpp + // + // Affine transformation are linear transformations in Cartesian coordinates + // (strictly speaking not only in Cartesian, but for the beginning we will + // think so). They are rotation, scaling, translation and skewing. + // After any affine transformation a line segment remains a line segment + // and it will never become a curve. + // + // There will be no math about matrix calculations, since it has been + // described many times. Ask yourself a very simple question: + // "why do we need to understand and use some matrix stuff instead of just + // rotating, scaling and so on". The answers are: + // + // 1. Any combination of transformations can be done by only 4 multiplications + // and 4 additions in floating point. + // 2. One matrix transformation is equivalent to the number of consecutive + // discrete transformations, i.e. the matrix "accumulates" all transformations + // in the order of their settings. Suppose we have 4 transformations: + // * rotate by 30 degrees, + // * scale X to 2.0, + // * scale Y to 1.5, + // * move to (100, 100). + // The result will depend on the order of these transformations, + // and the advantage of matrix is that the sequence of discret calls: + // rotate(30), scaleX(2.0), scaleY(1.5), move(100,100) + // will have exactly the same result as the following matrix transformations: + // + // affine_matrix m; + // m *= rotate_matrix(30); + // m *= scaleX_matrix(2.0); + // m *= scaleY_matrix(1.5); + // m *= move_matrix(100,100); + // + // m.transform_my_point_at_last(x, y); + // + // What is the good of it? In real life we will set-up the matrix only once + // and then transform many points, let alone the convenience to set any + // combination of transformations. + // + // So, how to use it? Very easy - literally as it's shown above. Not quite, + // let us write a correct example: + // + // agg::trans_affine m; + // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); + // m *= agg::trans_affine_scaling(2.0, 1.5); + // m *= agg::trans_affine_translation(100.0, 100.0); + // m.transform(&x, &y); + // + // The affine matrix is all you need to perform any linear transformation, + // but all transformations have origin point (0,0). It means that we need to + // use 2 translations if we want to rotate someting around (100,100): + // + // m *= agg::trans_affine_translation(-100.0, -100.0); // move to (0,0) + // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate + // m *= agg::trans_affine_translation(100.0, 100.0); // move back to (100,100) + //---------------------------------------------------------------------- + struct trans_affine + { + double sx, shy, shx, sy, tx, ty; + + //------------------------------------------ Construction + // Identity matrix + trans_affine() : + sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0) + {} + + // Custom matrix. Usually used in derived classes + trans_affine(double v0, double v1, double v2, + double v3, double v4, double v5) : + sx(v0), shy(v1), shx(v2), sy(v3), tx(v4), ty(v5) + {} + + // Custom matrix from m[6] + explicit trans_affine(const double* m) : + sx(m[0]), shy(m[1]), shx(m[2]), sy(m[3]), tx(m[4]), ty(m[5]) + {} + + // Rectangle to a parallelogram. + trans_affine(double x1, double y1, double x2, double y2, + const double* parl) + { + rect_to_parl(x1, y1, x2, y2, parl); + } + + // Parallelogram to a rectangle. + trans_affine(const double* parl, + double x1, double y1, double x2, double y2) + { + parl_to_rect(parl, x1, y1, x2, y2); + } + + // Arbitrary parallelogram transformation. + trans_affine(const double* src, const double* dst) + { + parl_to_parl(src, dst); + } + + //---------------------------------- Parellelogram transformations + // transform a parallelogram to another one. Src and dst are + // pointers to arrays of three points (double[6], x1,y1,...) that + // identify three corners of the parallelograms assuming implicit + // fourth point. The arguments are arrays of double[6] mapped + // to x1,y1, x2,y2, x3,y3 where the coordinates are: + // *-----------------* + // / (x3,y3)/ + // / / + // /(x1,y1) (x2,y2)/ + // *-----------------* + const trans_affine& parl_to_parl(const double* src, + const double* dst); + + const trans_affine& rect_to_parl(double x1, double y1, + double x2, double y2, + const double* parl); + + const trans_affine& parl_to_rect(const double* parl, + double x1, double y1, + double x2, double y2); + + + //------------------------------------------ Operations + // Reset - load an identity matrix + const trans_affine& reset(); + + // Direct transformations operations + const trans_affine& translate(double x, double y); + const trans_affine& rotate(double a); + const trans_affine& scale(double s); + const trans_affine& scale(double x, double y); + + // Multiply matrix to another one + const trans_affine& multiply(const trans_affine& m); + + // Multiply "m" to "this" and assign the result to "this" + const trans_affine& premultiply(const trans_affine& m); + + // Multiply matrix to inverse of another one + const trans_affine& multiply_inv(const trans_affine& m); + + // Multiply inverse of "m" to "this" and assign the result to "this" + const trans_affine& premultiply_inv(const trans_affine& m); + + // Invert matrix. Do not try to invert degenerate matrices, + // there's no check for validity. If you set scale to 0 and + // then try to invert matrix, expect unpredictable result. + const trans_affine& invert(); + + // Mirroring around X + const trans_affine& flip_x(); + + // Mirroring around Y + const trans_affine& flip_y(); + + //------------------------------------------- Load/Store + // Store matrix to an array [6] of double + void store_to(double* m) const + { + *m++ = sx; *m++ = shy; *m++ = shx; *m++ = sy; *m++ = tx; *m++ = ty; + } + + // Load matrix from an array [6] of double + const trans_affine& load_from(const double* m) + { + sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++; + return *this; + } + + //------------------------------------------- Operators + + // Multiply the matrix by another one + const trans_affine& operator *= (const trans_affine& m) + { + return multiply(m); + } + + // Multiply the matrix by inverse of another one + const trans_affine& operator /= (const trans_affine& m) + { + return multiply_inv(m); + } + + // Multiply the matrix by another one and return + // the result in a separete matrix. + trans_affine operator * (const trans_affine& m) + { + return trans_affine(*this).multiply(m); + } + + // Multiply the matrix by inverse of another one + // and return the result in a separete matrix. + trans_affine operator / (const trans_affine& m) + { + return trans_affine(*this).multiply_inv(m); + } + + // Calculate and return the inverse matrix + trans_affine operator ~ () const + { + trans_affine ret = *this; + return ret.invert(); + } + + // Equal operator with default epsilon + bool operator == (const trans_affine& m) const + { + return is_equal(m, affine_epsilon); + } + + // Not Equal operator with default epsilon + bool operator != (const trans_affine& m) const + { + return !is_equal(m, affine_epsilon); + } + + //-------------------------------------------- Transformations + // Direct transformation of x and y + void transform(double* x, double* y) const; + + // Direct transformation of x and y, 2x2 matrix only, no translation + void transform_2x2(double* x, double* y) const; + + // Inverse transformation of x and y. It works slower than the + // direct transformation. For massive operations it's better to + // invert() the matrix and then use direct transformations. + void inverse_transform(double* x, double* y) const; + + //-------------------------------------------- Auxiliary + // Calculate the determinant of matrix + double determinant() const + { + return sx * sy - shy * shx; + } + + // Calculate the reciprocal of the determinant + double determinant_reciprocal() const + { + return 1.0 / (sx * sy - shy * shx); + } + + // Get the average scale (by X and Y). + // Basically used to calculate the approximation_scale when + // decomposinting curves into line segments. + double scale() const; + + // Check to see if the matrix is not degenerate + bool is_valid(double epsilon = affine_epsilon) const; + + // Check to see if it's an identity matrix + bool is_identity(double epsilon = affine_epsilon) const; + + // Check to see if two matrices are equal + bool is_equal(const trans_affine& m, double epsilon = affine_epsilon) const; + + // Determine the major parameters. Use with caution considering + // possible degenerate cases. + double rotation() const; + void translation(double* dx, double* dy) const; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; + }; + + //------------------------------------------------------------------------ + inline void trans_affine::transform(double* x, double* y) const + { + register double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; + } + + //------------------------------------------------------------------------ + inline void trans_affine::transform_2x2(double* x, double* y) const + { + register double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; + } + + //------------------------------------------------------------------------ + inline void trans_affine::inverse_transform(double* x, double* y) const + { + register double d = determinant_reciprocal(); + register double a = (*x - tx) * d; + register double b = (*y - ty) * d; + *x = a * sy - b * shx; + *y = b * sx - a * shy; + } + + //------------------------------------------------------------------------ + inline double trans_affine::scale() const + { + double x = 0.707106781 * sx + 0.707106781 * shx; + double y = 0.707106781 * shy + 0.707106781 * sy; + return sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::rotate(double a) + { + double ca = cos(a); + double sa = sin(a); + double t0 = sx * ca - shy * sa; + double t2 = shx * ca - sy * sa; + double t4 = tx * ca - ty * sa; + shy = sx * sa + shy * ca; + sy = shx * sa + sy * ca; + ty = tx * sa + ty * ca; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double x, double y) + { + double mm0 = x; // Possible hint for the optimizer + double mm3 = y; + sx *= mm0; + shx *= mm0; + tx *= mm0; + shy *= mm3; + sy *= mm3; + ty *= mm3; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double s) + { + double m = s; // Possible hint for the optimizer + sx *= m; + shx *= m; + tx *= m; + shy *= m; + sy *= m; + ty *= m; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply(const trans_affine& m) + { + trans_affine t = m; + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::multiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::premultiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline void trans_affine::scaling_abs(double* x, double* y) const + { + // Used to calculate scaling coefficients in image resampling. + // When there is considerable shear this method gives us much + // better estimation than just sx, sy. + *x = sqrt(sx * sx + shx * shx); + *y = sqrt(shy * shy + sy * sy); + } + + //====================================================trans_affine_rotation + // Rotation matrix. sin() and cos() are calculated twice for the same angle. + // There's no harm because the performance of sin()/cos() is very good on all + // modern processors. Besides, this operation is not going to be invoked too + // often. + class trans_affine_rotation : public trans_affine + { + public: + trans_affine_rotation(double a) : + trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0) + {} + }; + + //====================================================trans_affine_scaling + // Scaling matrix. x, y - scale coefficients by X and Y respectively + class trans_affine_scaling : public trans_affine + { + public: + trans_affine_scaling(double x, double y) : + trans_affine(x, 0.0, 0.0, y, 0.0, 0.0) + {} + + trans_affine_scaling(double s) : + trans_affine(s, 0.0, 0.0, s, 0.0, 0.0) + {} + }; + + //================================================trans_affine_translation + // Translation matrix + class trans_affine_translation : public trans_affine + { + public: + trans_affine_translation(double x, double y) : + trans_affine(1.0, 0.0, 0.0, 1.0, x, y) + {} + }; + + //====================================================trans_affine_skewing + // Sckewing (shear) matrix + class trans_affine_skewing : public trans_affine + { + public: + trans_affine_skewing(double x, double y) : + trans_affine(1.0, tan(y), tan(x), 1.0, 0.0, 0.0) + {} + }; + + + //===============================================trans_affine_line_segment + // Rotate, Scale and Translate, associating 0...dist with line segment + // x1,y1,x2,y2 + class trans_affine_line_segment : public trans_affine + { + public: + trans_affine_line_segment(double x1, double y1, double x2, double y2, + double dist) + { + double dx = x2 - x1; + double dy = y2 - y1; + if(dist > 0.0) + { + multiply(trans_affine_scaling(sqrt(dx * dx + dy * dy) / dist)); + } + multiply(trans_affine_rotation(atan2(dy, dx))); + multiply(trans_affine_translation(x1, y1)); + } + }; + + + //============================================trans_affine_reflection_unit + // Reflection matrix. Reflect coordinates across the line through + // the origin containing the unit vector (ux, uy). + // Contributed by John Horigan + class trans_affine_reflection_unit : public trans_affine + { + public: + trans_affine_reflection_unit(double ux, double uy) : + trans_affine(2.0 * ux * ux - 1.0, + 2.0 * ux * uy, + 2.0 * ux * uy, + 2.0 * uy * uy - 1.0, + 0.0, 0.0) + {} + }; + + + //=================================================trans_affine_reflection + // Reflection matrix. Reflect coordinates across the line through + // the origin at the angle a or containing the non-unit vector (x, y). + // Contributed by John Horigan + class trans_affine_reflection : public trans_affine_reflection_unit + { + public: + trans_affine_reflection(double a) : + trans_affine_reflection_unit(cos(a), sin(a)) + {} + + + trans_affine_reflection(double x, double y) : + trans_affine_reflection_unit(x / sqrt(x * x + y * y), y / sqrt(x * x + y * y)) + {} + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_trans_bilinear.h b/desmume/src/windows/agg/include/agg_trans_bilinear.h new file mode 100644 index 000000000..215026224 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_bilinear.h @@ -0,0 +1,172 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_BILINEAR_INCLUDED +#define AGG_TRANS_BILINEAR_INCLUDED + +#include "agg_basics.h" +#include "agg_simul_eq.h" + +namespace agg +{ + + //==========================================================trans_bilinear + class trans_bilinear + { + public: + //-------------------------------------------------------------------- + trans_bilinear() : m_valid(false) {} + + //-------------------------------------------------------------------- + // Arbitrary quadrangle transformations + trans_bilinear(const double* src, const double* dst) + { + quad_to_quad(src, dst); + } + + + //-------------------------------------------------------------------- + // Direct transformations + trans_bilinear(double x1, double y1, double x2, double y2, + const double* quad) + { + rect_to_quad(x1, y1, x2, y2, quad); + } + + + //-------------------------------------------------------------------- + // Reverse transformations + trans_bilinear(const double* quad, + double x1, double y1, double x2, double y2) + { + quad_to_rect(quad, x1, y1, x2, y2); + } + + + //-------------------------------------------------------------------- + // Set the transformations using two arbitrary quadrangles. + void quad_to_quad(const double* src, const double* dst) + { + double left[4][4]; + double right[4][2]; + + unsigned i; + for(i = 0; i < 4; i++) + { + unsigned ix = i * 2; + unsigned iy = ix + 1; + left[i][0] = 1.0; + left[i][1] = src[ix] * src[iy]; + left[i][2] = src[ix]; + left[i][3] = src[iy]; + + right[i][0] = dst[ix]; + right[i][1] = dst[iy]; + } + m_valid = simul_eq<4, 2>::solve(left, right, m_mtx); + } + + + //-------------------------------------------------------------------- + // Set the direct transformations, i.e., rectangle -> quadrangle + void rect_to_quad(double x1, double y1, double x2, double y2, + const double* quad) + { + double src[8]; + src[0] = src[6] = x1; + src[2] = src[4] = x2; + src[1] = src[3] = y1; + src[5] = src[7] = y2; + quad_to_quad(src, quad); + } + + + //-------------------------------------------------------------------- + // Set the reverse transformations, i.e., quadrangle -> rectangle + void quad_to_rect(const double* quad, + double x1, double y1, double x2, double y2) + { + double dst[8]; + dst[0] = dst[6] = x1; + dst[2] = dst[4] = x2; + dst[1] = dst[3] = y1; + dst[5] = dst[7] = y2; + quad_to_quad(quad, dst); + } + + //-------------------------------------------------------------------- + // Check if the equations were solved successfully + bool is_valid() const { return m_valid; } + + //-------------------------------------------------------------------- + // Transform a point (x, y) + void transform(double* x, double* y) const + { + double tx = *x; + double ty = *y; + double xy = tx * ty; + *x = m_mtx[0][0] + m_mtx[1][0] * xy + m_mtx[2][0] * tx + m_mtx[3][0] * ty; + *y = m_mtx[0][1] + m_mtx[1][1] * xy + m_mtx[2][1] * tx + m_mtx[3][1] * ty; + } + + + //-------------------------------------------------------------------- + class iterator_x + { + double inc_x; + double inc_y; + + public: + double x; + double y; + + iterator_x() {} + iterator_x(double tx, double ty, double step, const double m[4][2]) : + inc_x(m[1][0] * step * ty + m[2][0] * step), + inc_y(m[1][1] * step * ty + m[2][1] * step), + x(m[0][0] + m[1][0] * tx * ty + m[2][0] * tx + m[3][0] * ty), + y(m[0][1] + m[1][1] * tx * ty + m[2][1] * tx + m[3][1] * ty) + { + } + + void operator ++ () + { + x += inc_x; + y += inc_y; + } + }; + + iterator_x begin(double x, double y, double step) const + { + return iterator_x(x, y, step, m_mtx); + } + + private: + double m_mtx[4][2]; + bool m_valid; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_trans_double_path.h b/desmume/src/windows/agg/include/agg_trans_double_path.h new file mode 100644 index 000000000..736e921b7 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_double_path.h @@ -0,0 +1,140 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_DOUBLE_PATH_INCLUDED +#define AGG_TRANS_DOUBLE_PATH_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + // See also: agg_trans_double_path.cpp + // + //-------------------------------------------------------trans_double_path + class trans_double_path + { + enum status_e + { + initial, + making_path, + ready + }; + + public: + typedef vertex_sequence vertex_storage; + + trans_double_path(); + + //-------------------------------------------------------------------- + void base_length(double v) { m_base_length = v; } + double base_length() const { return m_base_length; } + + //-------------------------------------------------------------------- + void base_height(double v) { m_base_height = v; } + double base_height() const { return m_base_height; } + + //-------------------------------------------------------------------- + void preserve_x_scale(bool f) { m_preserve_x_scale = f; } + bool preserve_x_scale() const { return m_preserve_x_scale; } + + //-------------------------------------------------------------------- + void reset(); + void move_to1(double x, double y); + void line_to1(double x, double y); + void move_to2(double x, double y); + void line_to2(double x, double y); + void finalize_paths(); + + //-------------------------------------------------------------------- + template + void add_paths(VertexSource1& vs1, VertexSource2& vs2, + unsigned path1_id=0, unsigned path2_id=0) + { + double x; + double y; + + unsigned cmd; + + vs1.rewind(path1_id); + while(!is_stop(cmd = vs1.vertex(&x, &y))) + { + if(is_move_to(cmd)) + { + move_to1(x, y); + } + else + { + if(is_vertex(cmd)) + { + line_to1(x, y); + } + } + } + + vs2.rewind(path2_id); + while(!is_stop(cmd = vs2.vertex(&x, &y))) + { + if(is_move_to(cmd)) + { + move_to2(x, y); + } + else + { + if(is_vertex(cmd)) + { + line_to2(x, y); + } + } + } + finalize_paths(); + } + + //-------------------------------------------------------------------- + double total_length1() const; + double total_length2() const; + void transform(double *x, double *y) const; + + private: + double finalize_path(vertex_storage& vertices); + void transform1(const vertex_storage& vertices, + double kindex, double kx, + double *x, double* y) const; + + vertex_storage m_src_vertices1; + vertex_storage m_src_vertices2; + double m_base_length; + double m_base_height; + double m_kindex1; + double m_kindex2; + status_e m_status1; + status_e m_status2; + bool m_preserve_x_scale; + }; + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_trans_perspective.h b/desmume/src/windows/agg/include/agg_trans_perspective.h new file mode 100644 index 000000000..48b9ac8af --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_perspective.h @@ -0,0 +1,737 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_PERSPECTIVE_INCLUDED +#define AGG_TRANS_PERSPECTIVE_INCLUDED + +#include "agg_trans_affine.h" + +namespace agg +{ + //=======================================================trans_perspective + struct trans_perspective + { + double sx, shy, w0, shx, sy, w1, tx, ty, w2; + + //------------------------------------------------------- Construction + // Identity matrix + trans_perspective() : + sx (1), shy(0), w0(0), + shx(0), sy (1), w1(0), + tx (0), ty (0), w2(1) {} + + // Custom matrix + trans_perspective(double v0, double v1, double v2, + double v3, double v4, double v5, + double v6, double v7, double v8) : + sx (v0), shy(v1), w0(v2), + shx(v3), sy (v4), w1(v5), + tx (v6), ty (v7), w2(v8) {} + + // Custom matrix from m[9] + explicit trans_perspective(const double* m) : + sx (m[0]), shy(m[1]), w0(m[2]), + shx(m[3]), sy (m[4]), w1(m[5]), + tx (m[6]), ty (m[7]), w2(m[8]) {} + + // From affine + explicit trans_perspective(const trans_affine& a) : + sx (a.sx ), shy(a.shy), w0(0), + shx(a.shx), sy (a.sy ), w1(0), + tx (a.tx ), ty (a.ty ), w2(1) {} + + // Rectangle to quadrilateral + trans_perspective(double x1, double y1, double x2, double y2, + const double* quad); + + // Quadrilateral to rectangle + trans_perspective(const double* quad, + double x1, double y1, double x2, double y2); + + // Arbitrary quadrilateral transformations + trans_perspective(const double* src, const double* dst); + + //-------------------------------------- Quadrilateral transformations + // The arguments are double[8] that are mapped to quadrilaterals: + // x1,y1, x2,y2, x3,y3, x4,y4 + bool quad_to_quad(const double* qs, const double* qd); + + bool rect_to_quad(double x1, double y1, + double x2, double y2, + const double* q); + + bool quad_to_rect(const double* q, + double x1, double y1, + double x2, double y2); + + // Map square (0,0,1,1) to the quadrilateral and vice versa + bool square_to_quad(const double* q); + bool quad_to_square(const double* q); + + + //--------------------------------------------------------- Operations + // Reset - load an identity matrix + const trans_perspective& reset(); + + // Invert matrix. Returns false in degenerate case + bool invert(); + + // Direct transformations operations + const trans_perspective& translate(double x, double y); + const trans_perspective& rotate(double a); + const trans_perspective& scale(double s); + const trans_perspective& scale(double x, double y); + + // Multiply the matrix by another one + const trans_perspective& multiply(const trans_perspective& m); + + // Multiply "m" by "this" and assign the result to "this" + const trans_perspective& premultiply(const trans_perspective& m); + + // Multiply matrix to inverse of another one + const trans_perspective& multiply_inv(const trans_perspective& m); + + // Multiply inverse of "m" by "this" and assign the result to "this" + const trans_perspective& premultiply_inv(const trans_perspective& m); + + // Multiply the matrix by another one + const trans_perspective& multiply(const trans_affine& m); + + // Multiply "m" by "this" and assign the result to "this" + const trans_perspective& premultiply(const trans_affine& m); + + // Multiply the matrix by inverse of another one + const trans_perspective& multiply_inv(const trans_affine& m); + + // Multiply inverse of "m" by "this" and assign the result to "this" + const trans_perspective& premultiply_inv(const trans_affine& m); + + //--------------------------------------------------------- Load/Store + void store_to(double* m) const; + const trans_perspective& load_from(const double* m); + + //---------------------------------------------------------- Operators + // Multiply the matrix by another one + const trans_perspective& operator *= (const trans_perspective& m) + { + return multiply(m); + } + const trans_perspective& operator *= (const trans_affine& m) + { + return multiply(m); + } + + // Multiply the matrix by inverse of another one + const trans_perspective& operator /= (const trans_perspective& m) + { + return multiply_inv(m); + } + const trans_perspective& operator /= (const trans_affine& m) + { + return multiply_inv(m); + } + + // Multiply the matrix by another one and return + // the result in a separete matrix. + trans_perspective operator * (const trans_perspective& m) + { + return trans_perspective(*this).multiply(m); + } + trans_perspective operator * (const trans_affine& m) + { + return trans_perspective(*this).multiply(m); + } + + // Multiply the matrix by inverse of another one + // and return the result in a separete matrix. + trans_perspective operator / (const trans_perspective& m) + { + return trans_perspective(*this).multiply_inv(m); + } + trans_perspective operator / (const trans_affine& m) + { + return trans_perspective(*this).multiply_inv(m); + } + + // Calculate and return the inverse matrix + trans_perspective operator ~ () const + { + trans_perspective ret = *this; + ret.invert(); + return ret; + } + + // Equal operator with default epsilon + bool operator == (const trans_perspective& m) const + { + return is_equal(m, affine_epsilon); + } + + // Not Equal operator with default epsilon + bool operator != (const trans_perspective& m) const + { + return !is_equal(m, affine_epsilon); + } + + //---------------------------------------------------- Transformations + // Direct transformation of x and y + void transform(double* x, double* y) const; + + // Direct transformation of x and y, affine part only + void transform_affine(double* x, double* y) const; + + // Direct transformation of x and y, 2x2 matrix only, no translation + void transform_2x2(double* x, double* y) const; + + // Inverse transformation of x and y. It works slow because + // it explicitly inverts the matrix on every call. For massive + // operations it's better to invert() the matrix and then use + // direct transformations. + void inverse_transform(double* x, double* y) const; + + + //---------------------------------------------------------- Auxiliary + const trans_perspective& from_affine(const trans_affine& a); + double determinant() const; + double determinant_reciprocal() const; + + bool is_valid(double epsilon = affine_epsilon) const; + bool is_identity(double epsilon = affine_epsilon) const; + bool is_equal(const trans_perspective& m, + double epsilon = affine_epsilon) const; + + // Determine the major affine parameters. Use with caution + // considering possible degenerate cases. + double scale() const; + double rotation() const; + void translation(double* dx, double* dy) const; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; + + + + //-------------------------------------------------------------------- + class iterator_x + { + double den; + double den_step; + double nom_x; + double nom_x_step; + double nom_y; + double nom_y_step; + + public: + double x; + double y; + + iterator_x() {} + iterator_x(double px, double py, double step, const trans_perspective& m) : + den(px * m.w0 + py * m.w1 + m.w2), + den_step(m.w0 * step), + nom_x(px * m.sx + py * m.shx + m.tx), + nom_x_step(step * m.sx), + nom_y(px * m.shy + py * m.sy + m.ty), + nom_y_step(step * m.shy), + x(nom_x / den), + y(nom_y / den) + {} + + void operator ++ () + { + den += den_step; + nom_x += nom_x_step; + nom_y += nom_y_step; + double d = 1.0 / den; + x = nom_x * d; + y = nom_y * d; + } + }; + + //-------------------------------------------------------------------- + iterator_x begin(double x, double y, double step) const + { + return iterator_x(x, y, step, *this); + } + }; + + + + + + + + + + + + + + + //------------------------------------------------------------------------ + inline bool trans_perspective::square_to_quad(const double* q) + { + double dx = q[0] - q[2] + q[4] - q[6]; + double dy = q[1] - q[3] + q[5] - q[7]; + if(dx == 0.0 && dy == 0.0) + { + // Affine case (parallelogram) + //--------------- + sx = q[2] - q[0]; + shy = q[3] - q[1]; + w0 = 0.0; + shx = q[4] - q[2]; + sy = q[5] - q[3]; + w1 = 0.0; + tx = q[0]; + ty = q[1]; + w2 = 1.0; + } + else + { + double dx1 = q[2] - q[4]; + double dy1 = q[3] - q[5]; + double dx2 = q[6] - q[4]; + double dy2 = q[7] - q[5]; + double den = dx1 * dy2 - dx2 * dy1; + if(den == 0.0) + { + // Singular case + //--------------- + sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0; + return false; + } + // General case + //--------------- + double u = (dx * dy2 - dy * dx2) / den; + double v = (dy * dx1 - dx * dy1) / den; + sx = q[2] - q[0] + u * q[2]; + shy = q[3] - q[1] + u * q[3]; + w0 = u; + shx = q[6] - q[0] + v * q[6]; + sy = q[7] - q[1] + v * q[7]; + w1 = v; + tx = q[0]; + ty = q[1]; + w2 = 1.0; + } + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::invert() + { + double d0 = sy * w2 - w1 * ty; + double d1 = w0 * ty - shy * w2; + double d2 = shy * w1 - w0 * sy; + double d = sx * d0 + shx * d1 + tx * d2; + if(d == 0.0) + { + sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0; + return false; + } + d = 1.0 / d; + trans_perspective a = *this; + sx = d * d0; + shy = d * d1; + w0 = d * d2; + shx = d * (a.w1 *a.tx - a.shx*a.w2); + sy = d * (a.sx *a.w2 - a.w0 *a.tx); + w1 = d * (a.w0 *a.shx - a.sx *a.w1); + tx = d * (a.shx*a.ty - a.sy *a.tx); + ty = d * (a.shy*a.tx - a.sx *a.ty); + w2 = d * (a.sx *a.sy - a.shy*a.shx); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_square(const double* q) + { + if(!square_to_quad(q)) return false; + invert(); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_quad(const double* qs, + const double* qd) + { + trans_perspective p; + if(! quad_to_square(qs)) return false; + if(!p.square_to_quad(qd)) return false; + multiply(p); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::rect_to_quad(double x1, double y1, + double x2, double y2, + const double* q) + { + double r[8]; + r[0] = r[6] = x1; + r[2] = r[4] = x2; + r[1] = r[3] = y1; + r[5] = r[7] = y2; + return quad_to_quad(r, q); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_rect(const double* q, + double x1, double y1, + double x2, double y2) + { + double r[8]; + r[0] = r[6] = x1; + r[2] = r[4] = x2; + r[1] = r[3] = y1; + r[5] = r[7] = y2; + return quad_to_quad(q, r); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(double x1, double y1, + double x2, double y2, + const double* quad) + { + rect_to_quad(x1, y1, x2, y2, quad); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(const double* quad, + double x1, double y1, + double x2, double y2) + { + quad_to_rect(quad, x1, y1, x2, y2); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(const double* src, + const double* dst) + { + quad_to_quad(src, dst); + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::reset() + { + sx = 1; shy = 0; w0 = 0; + shx = 0; sy = 1; w1 = 0; + tx = 0; ty = 0; w2 = 1; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::multiply(const trans_perspective& a) + { + trans_perspective b = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0; + w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::multiply(const trans_affine& a) + { + trans_perspective b = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::premultiply(const trans_perspective& b) + { + trans_perspective a = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0; + w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::premultiply(const trans_affine& b) + { + trans_perspective a = *this; + sx = a.sx *b.sx + a.shx*b.shy; + shx = a.sx *b.shx + a.shx*b.sy; + tx = a.sx *b.tx + a.shx*b.ty + a.tx; + shy = a.shy*b.sx + a.sy *b.shy; + sy = a.shy*b.shx + a.sy *b.sy; + ty = a.shy*b.tx + a.sy *b.ty + a.ty; + w0 = a.w0 *b.sx + a.w1 *b.shy; + w1 = a.w0 *b.shx + a.w1 *b.sy; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2; + return *this; + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::multiply_inv(const trans_perspective& m) + { + trans_perspective t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::multiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::premultiply_inv(const trans_perspective& m) + { + trans_perspective t = m; + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::premultiply_inv(const trans_affine& m) + { + trans_perspective t(m); + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::rotate(double a) + { + multiply(trans_affine_rotation(a)); + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::scale(double s) + { + multiply(trans_affine_scaling(s)); + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::scale(double x, double y) + { + multiply(trans_affine_scaling(x, y)); + return *this; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform(double* px, double* py) const + { + double x = *px; + double y = *py; + double m = 1.0 / (x*w0 + y*w1 + w2); + *px = m * (x*sx + y*shx + tx); + *py = m * (x*shy + y*sy + ty); + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform_affine(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform_2x2(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::inverse_transform(double* x, double* y) const + { + trans_perspective t(*this); + if(t.invert()) t.transform(x, y); + } + + //------------------------------------------------------------------------ + inline void trans_perspective::store_to(double* m) const + { + *m++ = sx; *m++ = shy; *m++ = w0; + *m++ = shx; *m++ = sy; *m++ = w1; + *m++ = tx; *m++ = ty; *m++ = w2; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::load_from(const double* m) + { + sx = *m++; shy = *m++; w0 = *m++; + shx = *m++; sy = *m++; w1 = *m++; + tx = *m++; ty = *m++; w2 = *m++; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::from_affine(const trans_affine& a) + { + sx = a.sx; shy = a.shy; w0 = 0; + shx = a.shx; sy = a.sy; w1 = 0; + tx = a.tx; ty = a.ty; w2 = 1; + return *this; + } + + //------------------------------------------------------------------------ + inline double trans_perspective::determinant() const + { + return sx * (sy * w2 - ty * w1) + + shx * (ty * w0 - shy * w2) + + tx * (shy * w1 - sy * w0); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::determinant_reciprocal() const + { + return 1.0 / determinant(); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_valid(double epsilon) const + { + return fabs(sx) > epsilon && fabs(sy) > epsilon && fabs(w2) > epsilon; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_identity(double epsilon) const + { + return is_equal_eps(sx, 1.0, epsilon) && + is_equal_eps(shy, 0.0, epsilon) && + is_equal_eps(w0, 0.0, epsilon) && + is_equal_eps(shx, 0.0, epsilon) && + is_equal_eps(sy, 1.0, epsilon) && + is_equal_eps(w1, 0.0, epsilon) && + is_equal_eps(tx, 0.0, epsilon) && + is_equal_eps(ty, 0.0, epsilon) && + is_equal_eps(w2, 1.0, epsilon); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_equal(const trans_perspective& m, + double epsilon) const + { + return is_equal_eps(sx, m.sx, epsilon) && + is_equal_eps(shy, m.shy, epsilon) && + is_equal_eps(w0, m.w0, epsilon) && + is_equal_eps(shx, m.shx, epsilon) && + is_equal_eps(sy, m.sy, epsilon) && + is_equal_eps(w1, m.w1, epsilon) && + is_equal_eps(tx, m.tx, epsilon) && + is_equal_eps(ty, m.ty, epsilon) && + is_equal_eps(w2, m.w2, epsilon); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::scale() const + { + double x = 0.707106781 * sx + 0.707106781 * shx; + double y = 0.707106781 * shy + 0.707106781 * sy; + return sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::rotation() const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 0.0; + transform(&x1, &y1); + transform(&x2, &y2); + return atan2(y2-y1, x2-x1); + } + + //------------------------------------------------------------------------ + void trans_perspective::translation(double* dx, double* dy) const + { + *dx = tx; + *dy = ty; + } + + //------------------------------------------------------------------------ + void trans_perspective::scaling(double* x, double* y) const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 1.0; + trans_perspective t(*this); + t *= trans_affine_rotation(-rotation()); + t.transform(&x1, &y1); + t.transform(&x2, &y2); + *x = x2 - x1; + *y = y2 - y1; + } + + //------------------------------------------------------------------------ + void trans_perspective::scaling_abs(double* x, double* y) const + { + *x = sqrt(sx * sx + shx * shx); + *y = sqrt(shy * shy + sy * sy); + } + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/agg_trans_single_path.h b/desmume/src/windows/agg/include/agg_trans_single_path.h new file mode 100644 index 000000000..479a1f1cf --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_single_path.h @@ -0,0 +1,106 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_SINGLE_PATH_INCLUDED +#define AGG_TRANS_SINGLE_PATH_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + // See also: agg_trans_single_path.cpp + // + //-------------------------------------------------------trans_single_path + class trans_single_path + { + enum status_e + { + initial, + making_path, + ready + }; + + public: + typedef vertex_sequence vertex_storage; + + trans_single_path(); + + //-------------------------------------------------------------------- + void base_length(double v) { m_base_length = v; } + double base_length() const { return m_base_length; } + + //-------------------------------------------------------------------- + void preserve_x_scale(bool f) { m_preserve_x_scale = f; } + bool preserve_x_scale() const { return m_preserve_x_scale; } + + //-------------------------------------------------------------------- + void reset(); + void move_to(double x, double y); + void line_to(double x, double y); + void finalize_path(); + + //-------------------------------------------------------------------- + template + void add_path(VertexSource& vs, unsigned path_id=0) + { + double x; + double y; + + unsigned cmd; + vs.rewind(path_id); + while(!is_stop(cmd = vs.vertex(&x, &y))) + { + if(is_move_to(cmd)) + { + move_to(x, y); + } + else + { + if(is_vertex(cmd)) + { + line_to(x, y); + } + } + } + finalize_path(); + } + + //-------------------------------------------------------------------- + double total_length() const; + void transform(double *x, double *y) const; + + private: + vertex_storage m_src_vertices; + double m_base_length; + double m_kindex; + status_e m_status; + bool m_preserve_x_scale; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_trans_viewport.h b/desmume/src/windows/agg/include/agg_trans_viewport.h new file mode 100644 index 000000000..a9d18b540 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_viewport.h @@ -0,0 +1,312 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// Viewport transformer - simple orthogonal conversions from world coordinates +// to screen (device) ones. +// +//---------------------------------------------------------------------------- + +#ifndef AGG_TRANS_VIEWPORT_INCLUDED +#define AGG_TRANS_VIEWPORT_INCLUDED + +#include +#include "agg_trans_affine.h" + + +namespace agg +{ + + enum aspect_ratio_e + { + aspect_ratio_stretch, + aspect_ratio_meet, + aspect_ratio_slice + }; + + + //----------------------------------------------------------trans_viewport + class trans_viewport + { + public: + //------------------------------------------------------------------- + trans_viewport() : + m_world_x1(0.0), + m_world_y1(0.0), + m_world_x2(1.0), + m_world_y2(1.0), + m_device_x1(0.0), + m_device_y1(0.0), + m_device_x2(1.0), + m_device_y2(1.0), + m_aspect(aspect_ratio_stretch), + m_is_valid(true), + m_align_x(0.5), + m_align_y(0.5), + m_wx1(0.0), + m_wy1(0.0), + m_wx2(1.0), + m_wy2(1.0), + m_dx1(0.0), + m_dy1(0.0), + m_kx(1.0), + m_ky(1.0) + {} + + //------------------------------------------------------------------- + void preserve_aspect_ratio(double alignx, + double aligny, + aspect_ratio_e aspect) + { + m_align_x = alignx; + m_align_y = aligny; + m_aspect = aspect; + update(); + } + + //------------------------------------------------------------------- + void device_viewport(double x1, double y1, double x2, double y2) + { + m_device_x1 = x1; + m_device_y1 = y1; + m_device_x2 = x2; + m_device_y2 = y2; + update(); + } + + //------------------------------------------------------------------- + void world_viewport(double x1, double y1, double x2, double y2) + { + m_world_x1 = x1; + m_world_y1 = y1; + m_world_x2 = x2; + m_world_y2 = y2; + update(); + } + + //------------------------------------------------------------------- + void device_viewport(double* x1, double* y1, double* x2, double* y2) const + { + *x1 = m_device_x1; + *y1 = m_device_y1; + *x2 = m_device_x2; + *y2 = m_device_y2; + } + + //------------------------------------------------------------------- + void world_viewport(double* x1, double* y1, double* x2, double* y2) const + { + *x1 = m_world_x1; + *y1 = m_world_y1; + *x2 = m_world_x2; + *y2 = m_world_y2; + } + + //------------------------------------------------------------------- + void world_viewport_actual(double* x1, double* y1, + double* x2, double* y2) const + { + *x1 = m_wx1; + *y1 = m_wy1; + *x2 = m_wx2; + *y2 = m_wy2; + } + + //------------------------------------------------------------------- + bool is_valid() const { return m_is_valid; } + double align_x() const { return m_align_x; } + double align_y() const { return m_align_y; } + aspect_ratio_e aspect_ratio() const { return m_aspect; } + + //------------------------------------------------------------------- + void transform(double* x, double* y) const + { + *x = (*x - m_wx1) * m_kx + m_dx1; + *y = (*y - m_wy1) * m_ky + m_dy1; + } + + //------------------------------------------------------------------- + void transform_scale_only(double* x, double* y) const + { + *x *= m_kx; + *y *= m_ky; + } + + //------------------------------------------------------------------- + void inverse_transform(double* x, double* y) const + { + *x = (*x - m_dx1) / m_kx + m_wx1; + *y = (*y - m_dy1) / m_ky + m_wy1; + } + + //------------------------------------------------------------------- + void inverse_transform_scale_only(double* x, double* y) const + { + *x /= m_kx; + *y /= m_ky; + } + + //------------------------------------------------------------------- + double device_dx() const { return m_dx1 - m_wx1 * m_kx; } + double device_dy() const { return m_dy1 - m_wy1 * m_ky; } + + //------------------------------------------------------------------- + double scale_x() const + { + return m_kx; + } + + //------------------------------------------------------------------- + double scale_y() const + { + return m_ky; + } + + //------------------------------------------------------------------- + double scale() const + { + return (m_kx + m_ky) * 0.5; + } + + //------------------------------------------------------------------- + trans_affine to_affine() const + { + trans_affine mtx = trans_affine_translation(-m_wx1, -m_wy1); + mtx *= trans_affine_scaling(m_kx, m_ky); + mtx *= trans_affine_translation(m_dx1, m_dy1); + return mtx; + } + + //------------------------------------------------------------------- + trans_affine to_affine_scale_only() const + { + return trans_affine_scaling(m_kx, m_ky); + } + + //------------------------------------------------------------------- + unsigned byte_size() const + { + return sizeof(*this); + } + + void serialize(int8u* ptr) const + { + memcpy(ptr, this, sizeof(*this)); + } + + void deserialize(const int8u* ptr) + { + memcpy(this, ptr, sizeof(*this)); + } + + private: + void update(); + + double m_world_x1; + double m_world_y1; + double m_world_x2; + double m_world_y2; + double m_device_x1; + double m_device_y1; + double m_device_x2; + double m_device_y2; + aspect_ratio_e m_aspect; + bool m_is_valid; + double m_align_x; + double m_align_y; + double m_wx1; + double m_wy1; + double m_wx2; + double m_wy2; + double m_dx1; + double m_dy1; + double m_kx; + double m_ky; + }; + + + + //----------------------------------------------------------------------- + inline void trans_viewport::update() + { + const double epsilon = 1e-30; + if(fabs(m_world_x1 - m_world_x2) < epsilon || + fabs(m_world_y1 - m_world_y2) < epsilon || + fabs(m_device_x1 - m_device_x2) < epsilon || + fabs(m_device_y1 - m_device_y2) < epsilon) + { + m_wx1 = m_world_x1; + m_wy1 = m_world_y1; + m_wx2 = m_world_x1 + 1.0; + m_wy2 = m_world_y2 + 1.0; + m_dx1 = m_device_x1; + m_dy1 = m_device_y1; + m_kx = 1.0; + m_ky = 1.0; + m_is_valid = false; + return; + } + + double world_x1 = m_world_x1; + double world_y1 = m_world_y1; + double world_x2 = m_world_x2; + double world_y2 = m_world_y2; + double device_x1 = m_device_x1; + double device_y1 = m_device_y1; + double device_x2 = m_device_x2; + double device_y2 = m_device_y2; + if(m_aspect != aspect_ratio_stretch) + { + double d; + m_kx = (device_x2 - device_x1) / (world_x2 - world_x1); + m_ky = (device_y2 - device_y1) / (world_y2 - world_y1); + + if((m_aspect == aspect_ratio_meet) == (m_kx < m_ky)) + { + d = (world_y2 - world_y1) * m_ky / m_kx; + world_y1 += (world_y2 - world_y1 - d) * m_align_y; + world_y2 = world_y1 + d; + } + else + { + d = (world_x2 - world_x1) * m_kx / m_ky; + world_x1 += (world_x2 - world_x1 - d) * m_align_x; + world_x2 = world_x1 + d; + } + } + m_wx1 = world_x1; + m_wy1 = world_y1; + m_wx2 = world_x2; + m_wy2 = world_y2; + m_dx1 = device_x1; + m_dy1 = device_y1; + m_kx = (device_x2 - device_x1) / (world_x2 - world_x1); + m_ky = (device_y2 - device_y1) / (world_y2 - world_y1); + m_is_valid = true; + } + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_trans_warp_magnifier.h b/desmume/src/windows/agg/include/agg_trans_warp_magnifier.h new file mode 100644 index 000000000..d80f71152 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_trans_warp_magnifier.h @@ -0,0 +1,65 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_WARP_MAGNIFIER_INCLUDED +#define AGG_WARP_MAGNIFIER_INCLUDED + + +namespace agg +{ + + //----------------------------------------------------trans_warp_magnifier + // + // See Inmplementation agg_trans_warp_magnifier.cpp + // + class trans_warp_magnifier + { + public: + trans_warp_magnifier() : m_xc(0.0), m_yc(0.0), m_magn(1.0), m_radius(1.0) {} + + void center(double x, double y) { m_xc = x; m_yc = y; } + void magnification(double m) { m_magn = m; } + void radius(double r) { m_radius = r; } + + double xc() const { return m_xc; } + double yc() const { return m_yc; } + double magnification() const { return m_magn; } + double radius() const { return m_radius; } + + void transform(double* x, double* y) const; + void inverse_transform(double* x, double* y) const; + + private: + double m_xc; + double m_yc; + double m_magn; + double m_radius; + }; + + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_vcgen_bspline.h b/desmume/src/windows/agg/include/agg_vcgen_bspline.h new file mode 100644 index 000000000..d55dd0657 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_bspline.h @@ -0,0 +1,83 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_BSPLINE_INCLUDED +#define AGG_VCGEN_BSPLINE_INCLUDED + +#include "agg_basics.h" +#include "agg_array.h" +#include "agg_bspline.h" + + +namespace agg +{ + + //==========================================================vcgen_bspline + class vcgen_bspline + { + enum status_e + { + initial, + ready, + polygon, + end_poly, + stop + }; + + public: + typedef pod_bvector vertex_storage; + + vcgen_bspline(); + + void interpolation_step(double v) { m_interpolation_step = v; } + double interpolation_step() const { return m_interpolation_step; } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_bspline(const vcgen_bspline&); + const vcgen_bspline& operator = (const vcgen_bspline&); + + vertex_storage m_src_vertices; + bspline m_spline_x; + bspline m_spline_y; + double m_interpolation_step; + unsigned m_closed; + status_e m_status; + unsigned m_src_vertex; + double m_cur_abscissa; + double m_max_abscissa; + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_vcgen_contour.h b/desmume/src/windows/agg/include/agg_vcgen_contour.h new file mode 100644 index 000000000..85ca00e3c --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_contour.h @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_CONTOUR_INCLUDED +#define AGG_VCGEN_CONTOUR_INCLUDED + +#include "agg_math_stroke.h" + +namespace agg +{ + + //----------------------------------------------------------vcgen_contour + // + // See Implementation agg_vcgen_contour.cpp + // + class vcgen_contour + { + enum status_e + { + initial, + ready, + outline, + out_vertices, + end_poly, + stop + }; + + public: + typedef vertex_sequence vertex_storage; + typedef pod_bvector coord_storage; + + vcgen_contour(); + + void line_cap(line_cap_e lc) { m_stroker.line_cap(lc); } + void line_join(line_join_e lj) { m_stroker.line_join(lj); } + void inner_join(inner_join_e ij) { m_stroker.inner_join(ij); } + + line_cap_e line_cap() const { return m_stroker.line_cap(); } + line_join_e line_join() const { return m_stroker.line_join(); } + inner_join_e inner_join() const { return m_stroker.inner_join(); } + + void width(double w) { m_stroker.width(m_width = w); } + void miter_limit(double ml) { m_stroker.miter_limit(ml); } + void miter_limit_theta(double t) { m_stroker.miter_limit_theta(t); } + void inner_miter_limit(double ml) { m_stroker.inner_miter_limit(ml); } + void approximation_scale(double as) { m_stroker.approximation_scale(as); } + + double width() const { return m_width; } + double miter_limit() const { return m_stroker.miter_limit(); } + double inner_miter_limit() const { return m_stroker.inner_miter_limit(); } + double approximation_scale() const { return m_stroker.approximation_scale(); } + + void auto_detect_orientation(bool v) { m_auto_detect = v; } + bool auto_detect_orientation() const { return m_auto_detect; } + + // Generator interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_contour(const vcgen_contour&); + const vcgen_contour& operator = (const vcgen_contour&); + + math_stroke m_stroker; + double m_width; + vertex_storage m_src_vertices; + coord_storage m_out_vertices; + status_e m_status; + unsigned m_src_vertex; + unsigned m_out_vertex; + unsigned m_closed; + unsigned m_orientation; + bool m_auto_detect; + }; + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vcgen_dash.h b/desmume/src/windows/agg/include/agg_vcgen_dash.h new file mode 100644 index 000000000..8f195a230 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_dash.h @@ -0,0 +1,99 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_DASH_INCLUDED +#define AGG_VCGEN_DASH_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + //---------------------------------------------------------------vcgen_dash + // + // See Implementation agg_vcgen_dash.cpp + // + class vcgen_dash + { + enum max_dashes_e + { + max_dashes = 32 + }; + + enum status_e + { + initial, + ready, + polyline, + stop + }; + + public: + typedef vertex_sequence vertex_storage; + + vcgen_dash(); + + void remove_all_dashes(); + void add_dash(double dash_len, double gap_len); + void dash_start(double ds); + + void shorten(double s) { m_shorten = s; } + double shorten() const { return m_shorten; } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_dash(const vcgen_dash&); + const vcgen_dash& operator = (const vcgen_dash&); + + void calc_dash_start(double ds); + + double m_dashes[max_dashes]; + double m_total_dash_len; + unsigned m_num_dashes; + double m_dash_start; + double m_shorten; + double m_curr_dash_start; + unsigned m_curr_dash; + double m_curr_rest; + const vertex_dist* m_v1; + const vertex_dist* m_v2; + + vertex_storage m_src_vertices; + unsigned m_closed; + status_e m_status; + unsigned m_src_vertex; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vcgen_markers_term.h b/desmume/src/windows/agg/include/agg_vcgen_markers_term.h new file mode 100644 index 000000000..b4abc22ca --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_markers_term.h @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_MARKERS_TERM_INCLUDED +#define AGG_VCGEN_MARKERS_TERM_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + +namespace agg +{ + + //======================================================vcgen_markers_term + // + // See Implemantation agg_vcgen_markers_term.cpp + // Terminal markers generator (arrowhead/arrowtail) + // + //------------------------------------------------------------------------ + class vcgen_markers_term + { + public: + vcgen_markers_term() : m_curr_id(0), m_curr_idx(0) {} + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_markers_term(const vcgen_markers_term&); + const vcgen_markers_term& operator = (const vcgen_markers_term&); + + struct coord_type + { + double x, y; + + coord_type() {} + coord_type(double x_, double y_) : x(x_), y(y_) {} + }; + + typedef pod_bvector coord_storage; + + coord_storage m_markers; + unsigned m_curr_id; + unsigned m_curr_idx; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vcgen_smooth_poly1.h b/desmume/src/windows/agg/include/agg_vcgen_smooth_poly1.h new file mode 100644 index 000000000..8e10823a5 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_smooth_poly1.h @@ -0,0 +1,96 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_SMOOTH_POLY1_INCLUDED +#define AGG_VCGEN_SMOOTH_POLY1_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" + + +namespace agg +{ + + //======================================================vcgen_smooth_poly1 + // + // See Implementation agg_vcgen_smooth_poly1.cpp + // Smooth polygon generator + // + //------------------------------------------------------------------------ + class vcgen_smooth_poly1 + { + enum status_e + { + initial, + ready, + polygon, + ctrl_b, + ctrl_e, + ctrl1, + ctrl2, + end_poly, + stop + }; + + public: + typedef vertex_sequence vertex_storage; + + vcgen_smooth_poly1(); + + void smooth_value(double v) { m_smooth_value = v * 0.5; } + double smooth_value() const { return m_smooth_value * 2.0; } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_smooth_poly1(const vcgen_smooth_poly1&); + const vcgen_smooth_poly1& operator = (const vcgen_smooth_poly1&); + + void calculate(const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + const vertex_dist& v3); + + vertex_storage m_src_vertices; + double m_smooth_value; + unsigned m_closed; + status_e m_status; + unsigned m_src_vertex; + double m_ctrl1_x; + double m_ctrl1_y; + double m_ctrl2_x; + double m_ctrl2_y; + }; + +} + + +#endif + diff --git a/desmume/src/windows/agg/include/agg_vcgen_stroke.h b/desmume/src/windows/agg/include/agg_vcgen_stroke.h new file mode 100644 index 000000000..e39afb8d5 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_stroke.h @@ -0,0 +1,111 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_STROKE_INCLUDED +#define AGG_VCGEN_STROKE_INCLUDED + +#include "agg_math_stroke.h" + + +namespace agg +{ + + //============================================================vcgen_stroke + // + // See Implementation agg_vcgen_stroke.cpp + // Stroke generator + // + //------------------------------------------------------------------------ + class vcgen_stroke + { + enum status_e + { + initial, + ready, + cap1, + cap2, + outline1, + close_first, + outline2, + out_vertices, + end_poly1, + end_poly2, + stop + }; + + public: + typedef vertex_sequence vertex_storage; + typedef pod_bvector coord_storage; + + vcgen_stroke(); + + void line_cap(line_cap_e lc) { m_stroker.line_cap(lc); } + void line_join(line_join_e lj) { m_stroker.line_join(lj); } + void inner_join(inner_join_e ij) { m_stroker.inner_join(ij); } + + line_cap_e line_cap() const { return m_stroker.line_cap(); } + line_join_e line_join() const { return m_stroker.line_join(); } + inner_join_e inner_join() const { return m_stroker.inner_join(); } + + void width(double w) { m_stroker.width(w); } + void miter_limit(double ml) { m_stroker.miter_limit(ml); } + void miter_limit_theta(double t) { m_stroker.miter_limit_theta(t); } + void inner_miter_limit(double ml) { m_stroker.inner_miter_limit(ml); } + void approximation_scale(double as) { m_stroker.approximation_scale(as); } + + double width() const { return m_stroker.width(); } + double miter_limit() const { return m_stroker.miter_limit(); } + double inner_miter_limit() const { return m_stroker.inner_miter_limit(); } + double approximation_scale() const { return m_stroker.approximation_scale(); } + + void shorten(double s) { m_shorten = s; } + double shorten() const { return m_shorten; } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + vcgen_stroke(const vcgen_stroke&); + const vcgen_stroke& operator = (const vcgen_stroke&); + + math_stroke m_stroker; + vertex_storage m_src_vertices; + coord_storage m_out_vertices; + double m_shorten; + unsigned m_closed; + status_e m_status; + status_e m_prev_status; + unsigned m_src_vertex; + unsigned m_out_vertex; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vcgen_vertex_sequence.h b/desmume/src/windows/agg/include/agg_vcgen_vertex_sequence.h new file mode 100644 index 000000000..af94f046b --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vcgen_vertex_sequence.h @@ -0,0 +1,144 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VCGEN_VERTEX_SEQUENCE_INCLUDED +#define AGG_VCGEN_VERTEX_SEQUENCE_INCLUDED + +#include "agg_basics.h" +#include "agg_vertex_sequence.h" +#include "agg_shorten_path.h" + +namespace agg +{ + + //===================================================vcgen_vertex_sequence + class vcgen_vertex_sequence + { + public: + typedef vertex_dist_cmd vertex_type; + typedef vertex_sequence vertex_storage; + + vcgen_vertex_sequence() : + m_flags(0), + m_cur_vertex(0), + m_shorten(0.0), + m_ready(false) + { + } + + // Vertex Generator Interface + void remove_all(); + void add_vertex(double x, double y, unsigned cmd); + + // Vertex Source Interface + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + void shorten(double s) { m_shorten = s; } + double shorten() const { return m_shorten; } + + private: + vcgen_vertex_sequence(const vcgen_vertex_sequence&); + const vcgen_vertex_sequence& operator = (const vcgen_vertex_sequence&); + + vertex_storage m_src_vertices; + unsigned m_flags; + unsigned m_cur_vertex; + double m_shorten; + bool m_ready; + }; + + + //------------------------------------------------------------------------ + inline void vcgen_vertex_sequence::remove_all() + { + m_ready = false; + m_src_vertices.remove_all(); + m_cur_vertex = 0; + m_flags = 0; + } + + //------------------------------------------------------------------------ + inline void vcgen_vertex_sequence::add_vertex(double x, double y, unsigned cmd) + { + m_ready = false; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(vertex_dist_cmd(x, y, cmd)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(vertex_dist_cmd(x, y, cmd)); + } + else + { + m_flags = cmd & path_flags_mask; + } + } + } + + + //------------------------------------------------------------------------ + inline void vcgen_vertex_sequence::rewind(unsigned) + { + if(!m_ready) + { + m_src_vertices.close(is_closed(m_flags)); + shorten_path(m_src_vertices, m_shorten, get_close_flag(m_flags)); + } + m_ready = true; + m_cur_vertex = 0; + } + + //------------------------------------------------------------------------ + inline unsigned vcgen_vertex_sequence::vertex(double* x, double* y) + { + if(!m_ready) + { + rewind(0); + } + + if(m_cur_vertex == m_src_vertices.size()) + { + ++m_cur_vertex; + return path_cmd_end_poly | m_flags; + } + + if(m_cur_vertex > m_src_vertices.size()) + { + return path_cmd_stop; + } + + vertex_type& v = m_src_vertices[m_cur_vertex++]; + *x = v.x; + *y = v.y; + return v.cmd; + } + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vertex_sequence.h b/desmume/src/windows/agg/include/agg_vertex_sequence.h new file mode 100644 index 000000000..9125577de --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vertex_sequence.h @@ -0,0 +1,178 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VERTEX_SEQUENCE_INCLUDED +#define AGG_VERTEX_SEQUENCE_INCLUDED + +#include "agg_basics.h" +#include "agg_array.h" +#include "agg_math.h" + +namespace agg +{ + + //----------------------------------------------------------vertex_sequence + // Modified agg::pod_bvector. The data is interpreted as a sequence + // of vertices. It means that the type T must expose: + // + // bool T::operator() (const T& val) + // + // that is called every time new vertex is being added. The main purpose + // of this operator is the possibility to calculate some values during + // adding and to return true if the vertex fits some criteria or false if + // it doesn't. In the last case the new vertex is not added. + // + // The simple example is filtering coinciding vertices with calculation + // of the distance between the current and previous ones: + // + // struct vertex_dist + // { + // double x; + // double y; + // double dist; + // + // vertex_dist() {} + // vertex_dist(double x_, double y_) : + // x(x_), + // y(y_), + // dist(0.0) + // { + // } + // + // bool operator () (const vertex_dist& val) + // { + // return (dist = calc_distance(x, y, val.x, val.y)) > EPSILON; + // } + // }; + // + // Function close() calls this operator and removes the last vertex if + // necessary. + //------------------------------------------------------------------------ + template + class vertex_sequence : public pod_bvector + { + public: + typedef pod_bvector base_type; + + void add(const T& val); + void modify_last(const T& val); + void close(bool remove_flag); + }; + + + + //------------------------------------------------------------------------ + template + void vertex_sequence::add(const T& val) + { + if(base_type::size() > 1) + { + if(!(*this)[base_type::size() - 2]((*this)[base_type::size() - 1])) + { + base_type::remove_last(); + } + } + base_type::add(val); + } + + + //------------------------------------------------------------------------ + template + void vertex_sequence::modify_last(const T& val) + { + base_type::remove_last(); + add(val); + } + + + + //------------------------------------------------------------------------ + template + void vertex_sequence::close(bool closed) + { + while(base_type::size() > 1) + { + if((*this)[base_type::size() - 2]((*this)[base_type::size() - 1])) break; + T t = (*this)[base_type::size() - 1]; + base_type::remove_last(); + modify_last(t); + } + + if(closed) + { + while(base_type::size() > 1) + { + if((*this)[base_type::size() - 1]((*this)[0])) break; + base_type::remove_last(); + } + } + } + + + //-------------------------------------------------------------vertex_dist + // Vertex (x, y) with the distance to the next one. The last vertex has + // distance between the last and the first points if the polygon is closed + // and 0.0 if it's a polyline. + struct vertex_dist + { + double x; + double y; + double dist; + + vertex_dist() {} + vertex_dist(double x_, double y_) : + x(x_), + y(y_), + dist(0.0) + { + } + + bool operator () (const vertex_dist& val) + { + bool ret = (dist = calc_distance(x, y, val.x, val.y)) > vertex_dist_epsilon; + if(!ret) dist = 1.0 / vertex_dist_epsilon; + return ret; + } + }; + + + + //--------------------------------------------------------vertex_dist_cmd + // Save as the above but with additional "command" value + struct vertex_dist_cmd : public vertex_dist + { + unsigned cmd; + + vertex_dist_cmd() {} + vertex_dist_cmd(double x_, double y_, unsigned cmd_) : + vertex_dist(x_, y_), + cmd(cmd_) + { + } + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/agg_vpgen_clip_polygon.h b/desmume/src/windows/agg/include/agg_vpgen_clip_polygon.h new file mode 100644 index 000000000..f5e791ebc --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vpgen_clip_polygon.h @@ -0,0 +1,92 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VPGEN_CLIP_POLYGON_INCLUDED +#define AGG_VPGEN_CLIP_POLYGON_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //======================================================vpgen_clip_polygon + // + // See Implementation agg_vpgen_clip_polygon.cpp + // + class vpgen_clip_polygon + { + public: + vpgen_clip_polygon() : + m_clip_box(0, 0, 1, 1), + m_x1(0), + m_y1(0), + m_clip_flags(0), + m_num_vertices(0), + m_vertex(0), + m_cmd(path_cmd_move_to) + { + } + + void clip_box(double x1, double y1, double x2, double y2) + { + m_clip_box.x1 = x1; + m_clip_box.y1 = y1; + m_clip_box.x2 = x2; + m_clip_box.y2 = y2; + m_clip_box.normalize(); + } + + + double x1() const { return m_clip_box.x1; } + double y1() const { return m_clip_box.y1; } + double x2() const { return m_clip_box.x2; } + double y2() const { return m_clip_box.y2; } + + static bool auto_close() { return true; } + static bool auto_unclose() { return false; } + + void reset(); + void move_to(double x, double y); + void line_to(double x, double y); + unsigned vertex(double* x, double* y); + + private: + unsigned clipping_flags(double x, double y); + + private: + rect_d m_clip_box; + double m_x1; + double m_y1; + unsigned m_clip_flags; + double m_x[4]; + double m_y[4]; + unsigned m_num_vertices; + unsigned m_vertex; + unsigned m_cmd; + }; + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_vpgen_clip_polyline.h b/desmume/src/windows/agg/include/agg_vpgen_clip_polyline.h new file mode 100644 index 000000000..31ab16e10 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vpgen_clip_polyline.h @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VPGEN_CLIP_POLYLINE_INCLUDED +#define AGG_VPGEN_CLIP_POLYLINE_INCLUDED + +#include "agg_basics.h" + +namespace agg +{ + + //======================================================vpgen_clip_polyline + // + // See Implementation agg_vpgen_clip_polyline.cpp + // + class vpgen_clip_polyline + { + public: + vpgen_clip_polyline() : + m_clip_box(0, 0, 1, 1), + m_x1(0), + m_y1(0), + m_num_vertices(0), + m_vertex(0), + m_move_to(false) + { + } + + void clip_box(double x1, double y1, double x2, double y2) + { + m_clip_box.x1 = x1; + m_clip_box.y1 = y1; + m_clip_box.x2 = x2; + m_clip_box.y2 = y2; + m_clip_box.normalize(); + } + + double x1() const { return m_clip_box.x1; } + double y1() const { return m_clip_box.y1; } + double x2() const { return m_clip_box.x2; } + double y2() const { return m_clip_box.y2; } + + static bool auto_close() { return false; } + static bool auto_unclose() { return true; } + + void reset(); + void move_to(double x, double y); + void line_to(double x, double y); + unsigned vertex(double* x, double* y); + + private: + rect_d m_clip_box; + double m_x1; + double m_y1; + double m_x[2]; + double m_y[2]; + unsigned m_cmd[2]; + unsigned m_num_vertices; + unsigned m_vertex; + bool m_move_to; + }; + +} + + +#endif diff --git a/desmume/src/windows/agg/include/agg_vpgen_segmentator.h b/desmume/src/windows/agg/include/agg_vpgen_segmentator.h new file mode 100644 index 000000000..eba6052a6 --- /dev/null +++ b/desmume/src/windows/agg/include/agg_vpgen_segmentator.h @@ -0,0 +1,70 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_VPGEN_SEGMENTATOR_INCLUDED +#define AGG_VPGEN_SEGMENTATOR_INCLUDED + +#include +#include "agg_basics.h" + +namespace agg +{ + + //=======================================================vpgen_segmentator + // + // See Implementation agg_vpgen_segmentator.cpp + // + class vpgen_segmentator + { + public: + vpgen_segmentator() : m_approximation_scale(1.0) {} + + void approximation_scale(double s) { m_approximation_scale = s; } + double approximation_scale() const { return m_approximation_scale; } + + static bool auto_close() { return false; } + static bool auto_unclose() { return false; } + + void reset() { m_cmd = path_cmd_stop; } + void move_to(double x, double y); + void line_to(double x, double y); + unsigned vertex(double* x, double* y); + + private: + double m_approximation_scale; + double m_x1; + double m_y1; + double m_dx; + double m_dy; + double m_dl; + double m_ddl; + unsigned m_cmd; + }; + + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_bezier_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_bezier_ctrl.h new file mode 100644 index 000000000..f4598e571 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_bezier_ctrl.h @@ -0,0 +1,201 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_BEZIER_CTRL_INCLUDED +#define AGG_BEZIER_CTRL_INCLUDED + +#include "agg_math.h" +#include "agg_ellipse.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_conv_stroke.h" +#include "agg_conv_curve.h" +#include "agg_polygon_ctrl.h" + + +namespace agg +{ + + //--------------------------------------------------------bezier_ctrl_impl + class bezier_ctrl_impl : public ctrl + { + public: + bezier_ctrl_impl(); + + void curve(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4); + curve4& curve(); + + double x1() const { return m_poly.xn(0); } + double y1() const { return m_poly.yn(0); } + double x2() const { return m_poly.xn(1); } + double y2() const { return m_poly.yn(1); } + double x3() const { return m_poly.xn(2); } + double y3() const { return m_poly.yn(2); } + double x4() const { return m_poly.xn(3); } + double y4() const { return m_poly.yn(3); } + + void x1(double x) { m_poly.xn(0) = x; } + void y1(double y) { m_poly.yn(0) = y; } + void x2(double x) { m_poly.xn(1) = x; } + void y2(double y) { m_poly.yn(1) = y; } + void x3(double x) { m_poly.xn(2) = x; } + void y3(double y) { m_poly.yn(2) = y; } + void x4(double x) { m_poly.xn(3) = x; } + void y4(double y) { m_poly.yn(3) = y; } + + void line_width(double w) { m_stroke.width(w); } + double line_width() const { return m_stroke.width(); } + + void point_radius(double r) { m_poly.point_radius(r); } + double point_radius() const { return m_poly.point_radius(); } + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex source interface + unsigned num_paths() { return 7; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + + private: + curve4 m_curve; + ellipse m_ellipse; + conv_stroke m_stroke; + polygon_ctrl_impl m_poly; + unsigned m_idx; + }; + + + + //----------------------------------------------------------bezier_ctrl + template class bezier_ctrl : public bezier_ctrl_impl + { + public: + bezier_ctrl() : + m_color(rgba(0.0, 0.0, 0.0)) + { + } + + void line_color(const ColorT& c) { m_color = c; } + const ColorT& color(unsigned i) const { return m_color; } + + private: + bezier_ctrl(const bezier_ctrl&); + const bezier_ctrl& operator = (const bezier_ctrl&); + + ColorT m_color; + }; + + + + + + //--------------------------------------------------------curve3_ctrl_impl + class curve3_ctrl_impl : public ctrl + { + public: + curve3_ctrl_impl(); + + void curve(double x1, double y1, + double x2, double y2, + double x3, double y3); + curve3& curve(); + + double x1() const { return m_poly.xn(0); } + double y1() const { return m_poly.yn(0); } + double x2() const { return m_poly.xn(1); } + double y2() const { return m_poly.yn(1); } + double x3() const { return m_poly.xn(2); } + double y3() const { return m_poly.yn(2); } + + void x1(double x) { m_poly.xn(0) = x; } + void y1(double y) { m_poly.yn(0) = y; } + void x2(double x) { m_poly.xn(1) = x; } + void y2(double y) { m_poly.yn(1) = y; } + void x3(double x) { m_poly.xn(2) = x; } + void y3(double y) { m_poly.yn(2) = y; } + + void line_width(double w) { m_stroke.width(w); } + double line_width() const { return m_stroke.width(); } + + void point_radius(double r) { m_poly.point_radius(r); } + double point_radius() const { return m_poly.point_radius(); } + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex source interface + unsigned num_paths() { return 6; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + + private: + curve3 m_curve; + ellipse m_ellipse; + conv_stroke m_stroke; + polygon_ctrl_impl m_poly; + unsigned m_idx; + }; + + + + //----------------------------------------------------------curve3_ctrl + template class curve3_ctrl : public curve3_ctrl_impl + { + public: + curve3_ctrl() : + m_color(rgba(0.0, 0.0, 0.0)) + { + } + + void line_color(const ColorT& c) { m_color = c; } + const ColorT& color(unsigned i) const { return m_color; } + + private: + curve3_ctrl(const curve3_ctrl&); + const curve3_ctrl& operator = (const curve3_ctrl&); + + ColorT m_color; + }; + + + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_cbox_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_cbox_ctrl.h new file mode 100644 index 000000000..67b1228dd --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_cbox_ctrl.h @@ -0,0 +1,117 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CBOX_CTRL_INCLUDED +#define AGG_CBOX_CTRL_INCLUDED + +#include "agg_basics.h" +#include "agg_conv_stroke.h" +#include "agg_gsv_text.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + + + +namespace agg +{ + + //----------------------------------------------------------cbox_ctrl_impl + class cbox_ctrl_impl : public ctrl + { + public: + cbox_ctrl_impl(double x, double y, const char* label, bool flip_y=false); + + void text_thickness(double t) { m_text_thickness = t; } + void text_size(double h, double w=0.0); + + const char* label() { return m_label; } + void label(const char* l); + + bool status() const { return m_status; } + void status(bool st) { m_status = st; } + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex soutce interface + unsigned num_paths() { return 3; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + double m_text_thickness; + double m_text_height; + double m_text_width; + char m_label[128]; + bool m_status; + double m_vx[32]; + double m_vy[32]; + + gsv_text m_text; + conv_stroke m_text_poly; + + unsigned m_idx; + unsigned m_vertex; + }; + + + //----------------------------------------------------------cbox_ctrl_impl + template class cbox_ctrl : public cbox_ctrl_impl + { + public: + cbox_ctrl(double x, double y, const char* label, bool flip_y=false) : + cbox_ctrl_impl(x, y, label, flip_y), + m_text_color(rgba(0.0, 0.0, 0.0)), + m_inactive_color(rgba(0.0, 0.0, 0.0)), + m_active_color(rgba(0.4, 0.0, 0.0)) + { + m_colors[0] = &m_inactive_color; + m_colors[1] = &m_text_color; + m_colors[2] = &m_active_color; + } + + void text_color(const ColorT& c) { m_text_color = c; } + void inactive_color(const ColorT& c) { m_inactive_color = c; } + void active_color(const ColorT& c) { m_active_color = c; } + + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + cbox_ctrl(const cbox_ctrl&); + const cbox_ctrl& operator = (const cbox_ctrl&); + + ColorT m_text_color; + ColorT m_inactive_color; + ColorT m_active_color; + ColorT* m_colors[3]; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/ctrl/agg_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_ctrl.h new file mode 100644 index 000000000..fff2cb021 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_ctrl.h @@ -0,0 +1,123 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_CTRL_INCLUDED +#define AGG_CTRL_INCLUDED + +#include "agg_trans_affine.h" +#include "agg_renderer_scanline.h" + +namespace agg +{ + + //--------------------------------------------------------------------ctrl + class ctrl + { + public: + //-------------------------------------------------------------------- + virtual ~ctrl() {} + ctrl(double x1, double y1, double x2, double y2, bool flip_y) : + m_x1(x1), m_y1(y1), m_x2(x2), m_y2(y2), + m_flip_y(flip_y), + m_mtx(0) + { + } + + //-------------------------------------------------------------------- + virtual bool in_rect(double x, double y) const = 0; + virtual bool on_mouse_button_down(double x, double y) = 0; + virtual bool on_mouse_button_up(double x, double y) = 0; + virtual bool on_mouse_move(double x, double y, bool button_flag) = 0; + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up) = 0; + + //-------------------------------------------------------------------- + void transform(const trans_affine& mtx) { m_mtx = &mtx; } + void no_transform() { m_mtx = 0; } + + //-------------------------------------------------------------------- + void transform_xy(double* x, double* y) const + { + if(m_flip_y) *y = m_y1 + m_y2 - *y; + if(m_mtx) m_mtx->transform(x, y); + } + + //-------------------------------------------------------------------- + void inverse_transform_xy(double* x, double* y) const + { + if(m_mtx) m_mtx->inverse_transform(x, y); + if(m_flip_y) *y = m_y1 + m_y2 - *y; + } + + //-------------------------------------------------------------------- + double scale() const { return m_mtx ? m_mtx->scale() : 1.0; } + + private: + ctrl(const ctrl&); + const ctrl& operator = (const ctrl&); + + protected: + double m_x1; + double m_y1; + double m_x2; + double m_y2; + + private: + bool m_flip_y; + const trans_affine* m_mtx; + }; + + + //-------------------------------------------------------------------- + template + void render_ctrl(Rasterizer& ras, Scanline& sl, Renderer& r, Ctrl& c) + { + unsigned i; + for(i = 0; i < c.num_paths(); i++) + { + ras.reset(); + ras.add_path(c, i); + render_scanlines_aa_solid(ras, sl, r, c.color(i)); + } + } + + + //-------------------------------------------------------------------- + template + void render_ctrl_rs(Rasterizer& ras, Scanline& sl, Renderer& r, Ctrl& c) + { + unsigned i; + for(i = 0; i < c.num_paths(); i++) + { + ras.reset(); + ras.add_path(c, i); + r.color(c.color(i)); + render_scanlines(ras, sl, r); + } + } + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/ctrl/agg_gamma_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_gamma_ctrl.h new file mode 100644 index 000000000..58b2130ac --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_gamma_ctrl.h @@ -0,0 +1,175 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_CTRL_INCLUDED +#define AGG_GAMMA_CTRL_INCLUDED + +#include "agg_basics.h" +#include "agg_gamma_spline.h" +#include "agg_ellipse.h" +#include "agg_conv_stroke.h" +#include "agg_gsv_text.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + +namespace agg +{ + //------------------------------------------------------------------------ + // Class that can be used to create an interactive control to set up + // gamma arrays. + //------------------------------------------------------------------------ + class gamma_ctrl_impl : public ctrl + { + public: + gamma_ctrl_impl(double x1, double y1, double x2, double y2, bool flip_y=false); + + // Set other parameters + void border_width(double t, double extra=0.0); + void curve_width(double t) { m_curve_width = t; } + void grid_width(double t) { m_grid_width = t; } + void text_thickness(double t) { m_text_thickness = t; } + void text_size(double h, double w=0.0); + void point_size(double s) { m_point_size = s; } + + // Event handlers. Just call them if the respective events + // in your system occure. The functions return true if redrawing + // is required. + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + void change_active_point(); + + // A copy of agg::gamma_spline interface + void values(double kx1, double ky1, double kx2, double ky2); + void values(double* kx1, double* ky1, double* kx2, double* ky2) const; + const unsigned char* gamma() const { return m_gamma_spline.gamma(); } + double y(double x) const { return m_gamma_spline.y(x); } + double operator() (double x) const { return m_gamma_spline.y(x); } + const gamma_spline& get_gamma_spline() const { return m_gamma_spline; } + + // Vertex soutce interface + unsigned num_paths() { return 7; } + void rewind(unsigned idx); + unsigned vertex(double* x, double* y); + + private: + void calc_spline_box(); + void calc_points(); + void calc_values(); + + gamma_spline m_gamma_spline; + double m_border_width; + double m_border_extra; + double m_curve_width; + double m_grid_width; + double m_text_thickness; + double m_point_size; + double m_text_height; + double m_text_width; + double m_xc1; + double m_yc1; + double m_xc2; + double m_yc2; + double m_xs1; + double m_ys1; + double m_xs2; + double m_ys2; + double m_xt1; + double m_yt1; + double m_xt2; + double m_yt2; + conv_stroke m_curve_poly; + ellipse m_ellipse; + gsv_text m_text; + conv_stroke m_text_poly; + unsigned m_idx; + unsigned m_vertex; + double m_vx[32]; + double m_vy[32]; + double m_xp1; + double m_yp1; + double m_xp2; + double m_yp2; + bool m_p1_active; + unsigned m_mouse_point; + double m_pdx; + double m_pdy; + }; + + + + template class gamma_ctrl : public gamma_ctrl_impl + { + public: + gamma_ctrl(double x1, double y1, double x2, double y2, bool flip_y=false) : + gamma_ctrl_impl(x1, y1, x2, y2, flip_y), + m_background_color(rgba(1.0, 1.0, 0.9)), + m_border_color(rgba(0.0, 0.0, 0.0)), + m_curve_color(rgba(0.0, 0.0, 0.0)), + m_grid_color(rgba(0.2, 0.2, 0.0)), + m_inactive_pnt_color(rgba(0.0, 0.0, 0.0)), + m_active_pnt_color(rgba(1.0, 0.0, 0.0)), + m_text_color(rgba(0.0, 0.0, 0.0)) + { + m_colors[0] = &m_background_color; + m_colors[1] = &m_border_color; + m_colors[2] = &m_curve_color; + m_colors[3] = &m_grid_color; + m_colors[4] = &m_inactive_pnt_color; + m_colors[5] = &m_active_pnt_color; + m_colors[6] = &m_text_color; + } + + // Set colors + void background_color(const ColorT& c) { m_background_color = c; } + void border_color(const ColorT& c) { m_border_color = c; } + void curve_color(const ColorT& c) { m_curve_color = c; } + void grid_color(const ColorT& c) { m_grid_color = c; } + void inactive_pnt_color(const ColorT& c) { m_inactive_pnt_color = c; } + void active_pnt_color(const ColorT& c) { m_active_pnt_color = c; } + void text_color(const ColorT& c) { m_text_color = c; } + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + gamma_ctrl(const gamma_ctrl&); + const gamma_ctrl& operator = (const gamma_ctrl&); + + ColorT m_background_color; + ColorT m_border_color; + ColorT m_curve_color; + ColorT m_grid_color; + ColorT m_inactive_pnt_color; + ColorT m_active_pnt_color; + ColorT m_text_color; + ColorT* m_colors[7]; + }; + + +} + +#endif diff --git a/desmume/src/windows/agg/include/ctrl/agg_gamma_spline.h b/desmume/src/windows/agg/include/ctrl/agg_gamma_spline.h new file mode 100644 index 000000000..db5d8ee02 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_gamma_spline.h @@ -0,0 +1,100 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_GAMMA_SPLINE_INCLUDED +#define AGG_GAMMA_SPLINE_INCLUDED + +#include "agg_basics.h" +#include "agg_bspline.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + // Class-helper for calculation gamma-correction arrays. A gamma-correction + // array is an array of 256 unsigned chars that determine the actual values + // of Anti-Aliasing for each pixel coverage value from 0 to 255. If all the + // values in the array are equal to its index, i.e. 0,1,2,3,... there's + // no gamma-correction. Class agg::polyfill allows you to use custom + // gamma-correction arrays. You can calculate it using any approach, and + // class gamma_spline allows you to calculate almost any reasonable shape + // of the gamma-curve with using only 4 values - kx1, ky1, kx2, ky2. + // + // kx2 + // +----------------------------------+ + // | | | . | + // | | | . | ky2 + // | | . ------| + // | | . | + // | | . | + // |----------------.|----------------| + // | . | | + // | . | | + // |-------. | | + // ky1 | . | | | + // | . | | | + // +----------------------------------+ + // kx1 + // + // Each value can be in range [0...2]. Value 1.0 means one quarter of the + // bounding rectangle. Function values() calculates the curve by these + // 4 values. After calling it one can get the gamma-array with call gamma(). + // Class also supports the vertex source interface, i.e rewind() and + // vertex(). It's made for convinience and used in class gamma_ctrl. + // Before calling rewind/vertex one must set the bounding box + // box() using pixel coordinates. + //------------------------------------------------------------------------ + + class gamma_spline + { + public: + gamma_spline(); + + void values(double kx1, double ky1, double kx2, double ky2); + const unsigned char* gamma() const { return m_gamma; } + double y(double x) const; + void values(double* kx1, double* ky1, double* kx2, double* ky2) const; + void box(double x1, double y1, double x2, double y2); + + void rewind(unsigned); + unsigned vertex(double* x, double* y); + + private: + unsigned char m_gamma[256]; + double m_x[4]; + double m_y[4]; + bspline m_spline; + double m_x1; + double m_y1; + double m_x2; + double m_y2; + double m_cur_x; + }; + + + + +} + +#endif diff --git a/desmume/src/windows/agg/include/ctrl/agg_polygon_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_polygon_ctrl.h new file mode 100644 index 000000000..f23e09452 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_polygon_ctrl.h @@ -0,0 +1,171 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef POLYGON_CTRL_INCLUDED +#define POLYGON_CTRL_INCLUDED + +#include "agg_array.h" +#include "agg_conv_stroke.h" +#include "agg_ellipse.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + +namespace agg +{ + class simple_polygon_vertex_source + { + public: + simple_polygon_vertex_source(const double* polygon, unsigned np, + bool roundoff = false, + bool close = true) : + m_polygon(polygon), + m_num_points(np), + m_vertex(0), + m_roundoff(roundoff), + m_close(close) + { + } + + void close(bool f) { m_close = f; } + bool close() const { return m_close; } + + void rewind(unsigned) + { + m_vertex = 0; + } + + unsigned vertex(double* x, double* y) + { + if(m_vertex > m_num_points) return path_cmd_stop; + if(m_vertex == m_num_points) + { + ++m_vertex; + return path_cmd_end_poly | (m_close ? path_flags_close : 0); + } + *x = m_polygon[m_vertex * 2]; + *y = m_polygon[m_vertex * 2 + 1]; + if(m_roundoff) + { + *x = floor(*x) + 0.5; + *y = floor(*y) + 0.5; + } + ++m_vertex; + return (m_vertex == 1) ? path_cmd_move_to : path_cmd_line_to; + } + + private: + const double* m_polygon; + unsigned m_num_points; + unsigned m_vertex; + bool m_roundoff; + bool m_close; + }; + + + + + class polygon_ctrl_impl : public ctrl + { + public: + polygon_ctrl_impl(unsigned np, double point_radius=5); + + unsigned num_points() const { return m_num_points; } + double xn(unsigned n) const { return m_polygon[n * 2]; } + double yn(unsigned n) const { return m_polygon[n * 2 + 1]; } + double& xn(unsigned n) { return m_polygon[n * 2]; } + double& yn(unsigned n) { return m_polygon[n * 2 + 1]; } + + const double* polygon() const { return &m_polygon[0]; } + + void line_width(double w) { m_stroke.width(w); } + double line_width() const { return m_stroke.width(); } + + void point_radius(double r) { m_point_radius = r; } + double point_radius() const { return m_point_radius; } + + void in_polygon_check(bool f) { m_in_polygon_check = f; } + bool in_polygon_check() const { return m_in_polygon_check; } + + void close(bool f) { m_vs.close(f); } + bool close() const { return m_vs.close(); } + + // Vertex source interface + unsigned num_paths() { return 1; } + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + + private: + bool check_edge(unsigned i, double x, double y) const; + bool point_in_polygon(double x, double y) const; + + pod_array m_polygon; + unsigned m_num_points; + int m_node; + int m_edge; + simple_polygon_vertex_source m_vs; + conv_stroke m_stroke; + ellipse m_ellipse; + double m_point_radius; + unsigned m_status; + double m_dx; + double m_dy; + bool m_in_polygon_check; + }; + + + + //----------------------------------------------------------polygon_ctrl + template class polygon_ctrl : public polygon_ctrl_impl + { + public: + polygon_ctrl(unsigned np, double point_radius=5) : + polygon_ctrl_impl(np, point_radius), + m_color(rgba(0.0, 0.0, 0.0)) + { + } + + void line_color(const ColorT& c) { m_color = c; } + const ColorT& color(unsigned i) const { return m_color; } + + private: + polygon_ctrl(const polygon_ctrl&); + const polygon_ctrl& operator = (const polygon_ctrl&); + + ColorT m_color; + }; + + + + +} + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_rbox_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_rbox_ctrl.h new file mode 100644 index 000000000..c1c406613 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_rbox_ctrl.h @@ -0,0 +1,146 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_RBOX_CTRL_INCLUDED +#define AGG_RBOX_CTRL_INCLUDED + +#include "agg_array.h" +#include "agg_ellipse.h" +#include "agg_conv_stroke.h" +#include "agg_gsv_text.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + + + +namespace agg +{ + + //------------------------------------------------------------------------ + class rbox_ctrl_impl : public ctrl + { + public: + rbox_ctrl_impl(double x1, double y1, double x2, double y2, bool flip_y=false); + + void border_width(double t, double extra=0.0); + void text_thickness(double t) { m_text_thickness = t; } + void text_size(double h, double w=0.0); + + void add_item(const char* text); + int cur_item() const { return m_cur_item; } + void cur_item(int i) { m_cur_item = i; } + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex soutce interface + unsigned num_paths() { return 5; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + void calc_rbox(); + + double m_border_width; + double m_border_extra; + double m_text_thickness; + double m_text_height; + double m_text_width; + pod_array m_items[32]; + unsigned m_num_items; + int m_cur_item; + + double m_xs1; + double m_ys1; + double m_xs2; + double m_ys2; + + double m_vx[32]; + double m_vy[32]; + unsigned m_draw_item; + double m_dy; + + ellipse m_ellipse; + conv_stroke m_ellipse_poly; + gsv_text m_text; + conv_stroke m_text_poly; + + unsigned m_idx; + unsigned m_vertex; + }; + + + + //------------------------------------------------------------------------ + template class rbox_ctrl : public rbox_ctrl_impl + { + public: + rbox_ctrl(double x1, double y1, double x2, double y2, bool flip_y=false) : + rbox_ctrl_impl(x1, y1, x2, y2, flip_y), + m_background_color(rgba(1.0, 1.0, 0.9)), + m_border_color(rgba(0.0, 0.0, 0.0)), + m_text_color(rgba(0.0, 0.0, 0.0)), + m_inactive_color(rgba(0.0, 0.0, 0.0)), + m_active_color(rgba(0.4, 0.0, 0.0)) + { + m_colors[0] = &m_background_color; + m_colors[1] = &m_border_color; + m_colors[2] = &m_text_color; + m_colors[3] = &m_inactive_color; + m_colors[4] = &m_active_color; + } + + + void background_color(const ColorT& c) { m_background_color = c; } + void border_color(const ColorT& c) { m_border_color = c; } + void text_color(const ColorT& c) { m_text_color = c; } + void inactive_color(const ColorT& c) { m_inactive_color = c; } + void active_color(const ColorT& c) { m_active_color = c; } + + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + rbox_ctrl(const rbox_ctrl&); + const rbox_ctrl& operator = (const rbox_ctrl&); + + ColorT m_background_color; + ColorT m_border_color; + ColorT m_text_color; + ColorT m_inactive_color; + ColorT m_active_color; + ColorT* m_colors[5]; + }; + + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_scale_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_scale_ctrl.h new file mode 100644 index 000000000..40f95f998 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_scale_ctrl.h @@ -0,0 +1,151 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SCALE_CTRL_INCLUDED +#define AGG_SCALE_CTRL_INCLUDED + +#include "agg_basics.h" +#include "agg_math.h" +#include "agg_ellipse.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + + +namespace agg +{ + + //------------------------------------------------------------------------ + class scale_ctrl_impl : public ctrl + { + enum move_e + { + move_nothing, + move_value1, + move_value2, + move_slider + }; + + public: + scale_ctrl_impl(double x1, double y1, double x2, double y2, bool flip_y=false); + + void border_thickness(double t, double extra=0.0); + void resize(double x1, double y1, double x2, double y2); + + double min_delta() const { return m_min_d; } + void min_delta(double d) { m_min_d = d; } + + double value1() const { return m_value1; } + void value1(double value); + + double value2() const { return m_value2; } + void value2(double value); + + void move(double d); + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex soutce interface + unsigned num_paths() { return 5; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + void calc_box(); + + double m_border_thickness; + double m_border_extra; + double m_value1; + double m_value2; + double m_min_d; + double m_xs1; + double m_ys1; + double m_xs2; + double m_ys2; + double m_pdx; + double m_pdy; + move_e m_move_what; + double m_vx[32]; + double m_vy[32]; + + ellipse m_ellipse; + + unsigned m_idx; + unsigned m_vertex; + + }; + + + + //------------------------------------------------------------------------ + template class scale_ctrl : public scale_ctrl_impl + { + public: + scale_ctrl(double x1, double y1, double x2, double y2, bool flip_y=false) : + scale_ctrl_impl(x1, y1, x2, y2, flip_y), + m_background_color(rgba(1.0, 0.9, 0.8)), + m_border_color(rgba(0.0, 0.0, 0.0)), + m_pointers_color(rgba(0.8, 0.0, 0.0, 0.8)), + m_slider_color(rgba(0.2, 0.1, 0.0, 0.6)) + { + m_colors[0] = &m_background_color; + m_colors[1] = &m_border_color; + m_colors[2] = &m_pointers_color; + m_colors[3] = &m_pointers_color; + m_colors[4] = &m_slider_color; + } + + + void background_color(const ColorT& c) { m_background_color = c; } + void border_color(const ColorT& c) { m_border_color = c; } + void pointers_color(const ColorT& c) { m_pointers_color = c; } + void slider_color(const ColorT& c) { m_slider_color = c; } + + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + scale_ctrl(const scale_ctrl&); + const scale_ctrl& operator = (const scale_ctrl&); + + ColorT m_background_color; + ColorT m_border_color; + ColorT m_pointers_color; + ColorT m_slider_color; + ColorT* m_colors[5]; + }; + + + + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_slider_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_slider_ctrl.h new file mode 100644 index 000000000..54f45caa2 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_slider_ctrl.h @@ -0,0 +1,155 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SLIDER_CTRL_INCLUDED +#define AGG_SLIDER_CTRL_INCLUDED + +#include "agg_basics.h" +#include "agg_math.h" +#include "agg_ellipse.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_gsv_text.h" +#include "agg_conv_stroke.h" +#include "agg_path_storage.h" +#include "agg_ctrl.h" + + +namespace agg +{ + + //--------------------------------------------------------slider_ctrl_impl + class slider_ctrl_impl : public ctrl + { + public: + slider_ctrl_impl(double x1, double y1, double x2, double y2, bool flip_y=false); + + void border_width(double t, double extra=0.0); + + void range(double min, double max) { m_min = min; m_max = max; } + void num_steps(unsigned num) { m_num_steps = num; } + void label(const char* fmt); + void text_thickness(double t) { m_text_thickness = t; } + + bool descending() const { return m_descending; } + void descending(bool v) { m_descending = v; } + + double value() const { return m_value * (m_max - m_min) + m_min; } + void value(double value); + + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + // Vertex source interface + unsigned num_paths() { return 6; }; + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + void calc_box(); + bool normalize_value(bool preview_value_flag); + + double m_border_width; + double m_border_extra; + double m_text_thickness; + double m_value; + double m_preview_value; + double m_min; + double m_max; + unsigned m_num_steps; + bool m_descending; + char m_label[64]; + double m_xs1; + double m_ys1; + double m_xs2; + double m_ys2; + double m_pdx; + bool m_mouse_move; + double m_vx[32]; + double m_vy[32]; + + ellipse m_ellipse; + + unsigned m_idx; + unsigned m_vertex; + + gsv_text m_text; + conv_stroke m_text_poly; + path_storage m_storage; + + }; + + + + //----------------------------------------------------------slider_ctrl + template class slider_ctrl : public slider_ctrl_impl + { + public: + slider_ctrl(double x1, double y1, double x2, double y2, bool flip_y=false) : + slider_ctrl_impl(x1, y1, x2, y2, flip_y), + m_background_color(rgba(1.0, 0.9, 0.8)), + m_triangle_color(rgba(0.7, 0.6, 0.6)), + m_text_color(rgba(0.0, 0.0, 0.0)), + m_pointer_preview_color(rgba(0.6, 0.4, 0.4, 0.4)), + m_pointer_color(rgba(0.8, 0.0, 0.0, 0.6)) + { + m_colors[0] = &m_background_color; + m_colors[1] = &m_triangle_color; + m_colors[2] = &m_text_color; + m_colors[3] = &m_pointer_preview_color; + m_colors[4] = &m_pointer_color; + m_colors[5] = &m_text_color; + } + + + void background_color(const ColorT& c) { m_background_color = c; } + void pointer_color(const ColorT& c) { m_pointer_color = c; } + + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + slider_ctrl(const slider_ctrl&); + const slider_ctrl& operator = (const slider_ctrl&); + + ColorT m_background_color; + ColorT m_triangle_color; + ColorT m_text_color; + ColorT m_pointer_preview_color; + ColorT m_pointer_color; + ColorT* m_colors[6]; + }; + + + + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/ctrl/agg_spline_ctrl.h b/desmume/src/windows/agg/include/ctrl/agg_spline_ctrl.h new file mode 100644 index 000000000..31591fd29 --- /dev/null +++ b/desmume/src/windows/agg/include/ctrl/agg_spline_ctrl.h @@ -0,0 +1,164 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_SPLINE_CTRL_INCLUDED +#define AGG_SPLINE_CTRL_INCLUDED + +#include "agg_basics.h" +#include "agg_ellipse.h" +#include "agg_bspline.h" +#include "agg_conv_stroke.h" +#include "agg_path_storage.h" +#include "agg_trans_affine.h" +#include "agg_color_rgba.h" +#include "agg_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + // Class that can be used to create an interactive control to set up + // gamma arrays. + //------------------------------------------------------------------------ + class spline_ctrl_impl : public ctrl + { + public: + spline_ctrl_impl(double x1, double y1, double x2, double y2, + unsigned num_pnt, bool flip_y=false); + + // Set other parameters + void border_width(double t, double extra=0.0); + void curve_width(double t) { m_curve_width = t; } + void point_size(double s) { m_point_size = s; } + + // Event handlers. Just call them if the respective events + // in your system occure. The functions return true if redrawing + // is required. + virtual bool in_rect(double x, double y) const; + virtual bool on_mouse_button_down(double x, double y); + virtual bool on_mouse_button_up(double x, double y); + virtual bool on_mouse_move(double x, double y, bool button_flag); + virtual bool on_arrow_keys(bool left, bool right, bool down, bool up); + + void active_point(int i); + + const double* spline() const { return m_spline_values; } + const int8u* spline8() const { return m_spline_values8; } + double value(double x) const; + void value(unsigned idx, double y); + void point(unsigned idx, double x, double y); + void x(unsigned idx, double x) { m_xp[idx] = x; } + void y(unsigned idx, double y) { m_yp[idx] = y; } + double x(unsigned idx) const { return m_xp[idx]; } + double y(unsigned idx) const { return m_yp[idx]; } + void update_spline(); + + // Vertex soutce interface + unsigned num_paths() { return 5; } + void rewind(unsigned path_id); + unsigned vertex(double* x, double* y); + + private: + void calc_spline_box(); + void calc_curve(); + double calc_xp(unsigned idx); + double calc_yp(unsigned idx); + void set_xp(unsigned idx, double val); + void set_yp(unsigned idx, double val); + + unsigned m_num_pnt; + double m_xp[32]; + double m_yp[32]; + bspline m_spline; + double m_spline_values[256]; + int8u m_spline_values8[256]; + double m_border_width; + double m_border_extra; + double m_curve_width; + double m_point_size; + double m_xs1; + double m_ys1; + double m_xs2; + double m_ys2; + path_storage m_curve_pnt; + conv_stroke m_curve_poly; + ellipse m_ellipse; + unsigned m_idx; + unsigned m_vertex; + double m_vx[32]; + double m_vy[32]; + int m_active_pnt; + int m_move_pnt; + double m_pdx; + double m_pdy; + const trans_affine* m_mtx; + }; + + + template class spline_ctrl : public spline_ctrl_impl + { + public: + spline_ctrl(double x1, double y1, double x2, double y2, + unsigned num_pnt, bool flip_y=false) : + spline_ctrl_impl(x1, y1, x2, y2, num_pnt, flip_y), + m_background_color(rgba(1.0, 1.0, 0.9)), + m_border_color(rgba(0.0, 0.0, 0.0)), + m_curve_color(rgba(0.0, 0.0, 0.0)), + m_inactive_pnt_color(rgba(0.0, 0.0, 0.0)), + m_active_pnt_color(rgba(1.0, 0.0, 0.0)) + { + m_colors[0] = &m_background_color; + m_colors[1] = &m_border_color; + m_colors[2] = &m_curve_color; + m_colors[3] = &m_inactive_pnt_color; + m_colors[4] = &m_active_pnt_color; + } + + // Set colors + void background_color(const ColorT& c) { m_background_color = c; } + void border_color(const ColorT& c) { m_border_color = c; } + void curve_color(const ColorT& c) { m_curve_color = c; } + void inactive_pnt_color(const ColorT& c) { m_inactive_pnt_color = c; } + void active_pnt_color(const ColorT& c) { m_active_pnt_color = c; } + const ColorT& color(unsigned i) const { return *m_colors[i]; } + + private: + spline_ctrl(const spline_ctrl&); + const spline_ctrl& operator = (const spline_ctrl&); + + ColorT m_background_color; + ColorT m_border_color; + ColorT m_curve_color; + ColorT m_inactive_pnt_color; + ColorT m_active_pnt_color; + ColorT* m_colors[5]; + }; + + + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/platform/agg_platform_support.h b/desmume/src/windows/agg/include/platform/agg_platform_support.h new file mode 100644 index 000000000..a2b18e84a --- /dev/null +++ b/desmume/src/windows/agg/include/platform/agg_platform_support.h @@ -0,0 +1,680 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// class platform_support +// +// It's not a part of the AGG library, it's just a helper class to create +// interactive demo examples. Since the examples should not be too complex +// this class is provided to support some very basic interactive graphical +// funtionality, such as putting the rendered image to the window, simple +// keyboard and mouse input, window resizing, setting the window title, +// and catching the "idle" events. +// +// The idea is to have a single header file that does not depend on any +// platform (I hate these endless #ifdef/#elif/#elif.../#endif) and a number +// of different implementations depending on the concrete platform. +// The most popular platforms are: +// +// Windows-32 API +// X-Window API +// SDL library (see http://www.libsdl.org/) +// MacOS C/C++ API +// +// This file does not include any system dependent .h files such as +// windows.h or X11.h, so, your demo applications do not depend on the +// platform. The only file that can #include system dependend headers +// is the implementation file agg_platform_support.cpp. Different +// implementations are placed in different directories, such as +// ~/agg/src/platform/win32 +// ~/agg/src/platform/sdl +// ~/agg/src/platform/X11 +// and so on. +// +// All the system dependent stuff sits in the platform_specific +// class which is forward-declared here but not defined. +// The platform_support class has just a pointer to it and it's +// the responsibility of the implementation to create/delete it. +// This class being defined in the implementation file can have +// any platform dependent stuff such as HWND, X11 Window and so on. +// +//---------------------------------------------------------------------------- + + +#ifndef AGG_PLATFORM_SUPPORT_INCLUDED +#define AGG_PLATFORM_SUPPORT_INCLUDED + + +#include "agg_basics.h" +#include "agg_rendering_buffer.h" +#include "agg_trans_viewport.h" +#include "ctrl/agg_ctrl.h" + +namespace agg +{ + + //----------------------------------------------------------window_flag_e + // These are flags used in method init(). Not all of them are + // applicable on different platforms, for example the win32_api + // cannot use a hardware buffer (window_hw_buffer). + // The implementation should simply ignore unsupported flags. + enum window_flag_e + { + window_resize = 1, + window_hw_buffer = 2, + window_keep_aspect_ratio = 4, + window_process_all_keys = 8 + }; + + //-----------------------------------------------------------pix_format_e + // Possible formats of the rendering buffer. Initially I thought that it's + // reasonable to create the buffer and the rendering functions in + // accordance with the native pixel format of the system because it + // would have no overhead for pixel format conersion. + // But eventually I came to a conclusion that having a possibility to + // convert pixel formats on demand is a good idea. First, it was X11 where + // there lots of different formats and visuals and it would be great to + // render everything in, say, RGB-24 and display it automatically without + // any additional efforts. The second reason is to have a possibility to + // debug renderers for different pixel formats and colorspaces having only + // one computer and one system. + // + // This stuff is not included into the basic AGG functionality because the + // number of supported pixel formats (and/or colorspaces) can be great and + // if one needs to add new format it would be good only to add new + // rendering files without having to modify any existing ones (a general + // principle of incapsulation and isolation). + // + // Using a particular pixel format doesn't obligatory mean the necessity + // of software conversion. For example, win32 API can natively display + // gray8, 15-bit RGB, 24-bit BGR, and 32-bit BGRA formats. + // This list can be (and will be!) extended in future. + enum pix_format_e + { + pix_format_undefined = 0, // By default. No conversions are applied + pix_format_bw, // 1 bit per color B/W + pix_format_gray8, // Simple 256 level grayscale + pix_format_gray16, // Simple 65535 level grayscale + pix_format_rgb555, // 15 bit rgb. Depends on the byte ordering! + pix_format_rgb565, // 16 bit rgb. Depends on the byte ordering! + pix_format_rgbAAA, // 30 bit rgb. Depends on the byte ordering! + pix_format_rgbBBA, // 32 bit rgb. Depends on the byte ordering! + pix_format_bgrAAA, // 30 bit bgr. Depends on the byte ordering! + pix_format_bgrABB, // 32 bit bgr. Depends on the byte ordering! + pix_format_rgb24, // R-G-B, one byte per color component + pix_format_bgr24, // B-G-R, native win32 BMP format. + pix_format_rgba32, // R-G-B-A, one byte per color component + pix_format_argb32, // A-R-G-B, native MAC format + pix_format_abgr32, // A-B-G-R, one byte per color component + pix_format_bgra32, // B-G-R-A, native win32 BMP format + pix_format_rgb48, // R-G-B, 16 bits per color component + pix_format_bgr48, // B-G-R, native win32 BMP format. + pix_format_rgba64, // R-G-B-A, 16 bits byte per color component + pix_format_argb64, // A-R-G-B, native MAC format + pix_format_abgr64, // A-B-G-R, one byte per color component + pix_format_bgra64, // B-G-R-A, native win32 BMP format + + end_of_pix_formats + }; + + //-------------------------------------------------------------input_flag_e + // Mouse and keyboard flags. They can be different on different platforms + // and the ways they are obtained are also different. But in any case + // the system dependent flags should be mapped into these ones. The meaning + // of that is as follows. For example, if kbd_ctrl is set it means that the + // ctrl key is pressed and being held at the moment. They are also used in + // the overridden methods such as on_mouse_move(), on_mouse_button_down(), + // on_mouse_button_dbl_click(), on_mouse_button_up(), on_key(). + // In the method on_mouse_button_up() the mouse flags have different + // meaning. They mean that the respective button is being released, but + // the meaning of the keyboard flags remains the same. + // There's absolut minimal set of flags is used because they'll be most + // probably supported on different platforms. Even the mouse_right flag + // is restricted because Mac's mice have only one button, but AFAIK + // it can be simulated with holding a special key on the keydoard. + enum input_flag_e + { + mouse_left = 1, + mouse_right = 2, + kbd_shift = 4, + kbd_ctrl = 8 + }; + + //--------------------------------------------------------------key_code_e + // Keyboard codes. There's also a restricted set of codes that are most + // probably supported on different platforms. Any platform dependent codes + // should be converted into these ones. There're only those codes are + // defined that cannot be represented as printable ASCII-characters. + // All printable ASCII-set can be used in a regular C/C++ manner: + // ' ', 'A', '0' '+' and so on. + // Since the class is used for creating very simple demo-applications + // we don't need very rich possibilities here, just basic ones. + // Actually the numeric key codes are taken from the SDL library, so, + // the implementation of the SDL support does not require any mapping. + enum key_code_e + { + // ASCII set. Should be supported everywhere + key_backspace = 8, + key_tab = 9, + key_clear = 12, + key_return = 13, + key_pause = 19, + key_escape = 27, + + // Keypad + key_delete = 127, + key_kp0 = 256, + key_kp1 = 257, + key_kp2 = 258, + key_kp3 = 259, + key_kp4 = 260, + key_kp5 = 261, + key_kp6 = 262, + key_kp7 = 263, + key_kp8 = 264, + key_kp9 = 265, + key_kp_period = 266, + key_kp_divide = 267, + key_kp_multiply = 268, + key_kp_minus = 269, + key_kp_plus = 270, + key_kp_enter = 271, + key_kp_equals = 272, + + // Arrow-keys and stuff + key_up = 273, + key_down = 274, + key_right = 275, + key_left = 276, + key_insert = 277, + key_home = 278, + key_end = 279, + key_page_up = 280, + key_page_down = 281, + + // Functional keys. You'd better avoid using + // f11...f15 in your applications if you want + // the applications to be portable + key_f1 = 282, + key_f2 = 283, + key_f3 = 284, + key_f4 = 285, + key_f5 = 286, + key_f6 = 287, + key_f7 = 288, + key_f8 = 289, + key_f9 = 290, + key_f10 = 291, + key_f11 = 292, + key_f12 = 293, + key_f13 = 294, + key_f14 = 295, + key_f15 = 296, + + // The possibility of using these keys is + // very restricted. Actually it's guaranteed + // only in win32_api and win32_sdl implementations + key_numlock = 300, + key_capslock = 301, + key_scrollock = 302, + + // Phew! + end_of_key_codes + }; + + + //------------------------------------------------------------------------ + // A predeclaration of the platform dependent class. Since we do not + // know anything here the only we can have is just a pointer to this + // class as a data member. It should be created and destroyed explicitly + // in the constructor/destructor of the platform_support class. + // Although the pointer to platform_specific is public the application + // cannot have access to its members or methods since it does not know + // anything about them and it's a perfect incapsulation :-) + class platform_specific; + + + //----------------------------------------------------------ctrl_container + // A helper class that contains pointers to a number of controls. + // This class is used to ease the event handling with controls. + // The implementation should simply call the appropriate methods + // of this class when appropriate events occur. + class ctrl_container + { + enum max_ctrl_e { max_ctrl = 64 }; + + public: + //-------------------------------------------------------------------- + ctrl_container() : m_num_ctrl(0), m_cur_ctrl(-1) {} + + //-------------------------------------------------------------------- + void add(ctrl& c) + { + if(m_num_ctrl < max_ctrl) + { + m_ctrl[m_num_ctrl++] = &c; + } + } + + //-------------------------------------------------------------------- + bool in_rect(double x, double y) + { + unsigned i; + for(i = 0; i < m_num_ctrl; i++) + { + if(m_ctrl[i]->in_rect(x, y)) return true; + } + return false; + } + + //-------------------------------------------------------------------- + bool on_mouse_button_down(double x, double y) + { + unsigned i; + for(i = 0; i < m_num_ctrl; i++) + { + if(m_ctrl[i]->on_mouse_button_down(x, y)) return true; + } + return false; + } + + //-------------------------------------------------------------------- + bool on_mouse_button_up(double x, double y) + { + unsigned i; + bool flag = false; + for(i = 0; i < m_num_ctrl; i++) + { + if(m_ctrl[i]->on_mouse_button_up(x, y)) flag = true; + } + return flag; + } + + //-------------------------------------------------------------------- + bool on_mouse_move(double x, double y, bool button_flag) + { + unsigned i; + for(i = 0; i < m_num_ctrl; i++) + { + if(m_ctrl[i]->on_mouse_move(x, y, button_flag)) return true; + } + return false; + } + + //-------------------------------------------------------------------- + bool on_arrow_keys(bool left, bool right, bool down, bool up) + { + if(m_cur_ctrl >= 0) + { + return m_ctrl[m_cur_ctrl]->on_arrow_keys(left, right, down, up); + } + return false; + } + + //-------------------------------------------------------------------- + bool set_cur(double x, double y) + { + unsigned i; + for(i = 0; i < m_num_ctrl; i++) + { + if(m_ctrl[i]->in_rect(x, y)) + { + if(m_cur_ctrl != int(i)) + { + m_cur_ctrl = i; + return true; + } + return false; + } + } + if(m_cur_ctrl != -1) + { + m_cur_ctrl = -1; + return true; + } + return false; + } + + private: + ctrl* m_ctrl[max_ctrl]; + unsigned m_num_ctrl; + int m_cur_ctrl; + }; + + + + //---------------------------------------------------------platform_support + // This class is a base one to the apllication classes. It can be used + // as follows: + // + // class the_application : public agg::platform_support + // { + // public: + // the_application(unsigned bpp, bool flip_y) : + // platform_support(bpp, flip_y) + // . . . + // + // //override stuff . . . + // virtual void on_init() + // { + // . . . + // } + // + // virtual void on_draw() + // { + // . . . + // } + // + // virtual void on_resize(int sx, int sy) + // { + // . . . + // } + // // . . . and so on, see virtual functions + // + // + // //any your own stuff . . . + // }; + // + // + // int agg_main(int argc, char* argv[]) + // { + // the_application app(pix_format_rgb24, true); + // app.caption("AGG Example. Lion"); + // + // if(app.init(500, 400, agg::window_resize)) + // { + // return app.run(); + // } + // return 1; + // } + // + // The reason to have agg_main() instead of just main() is that SDL + // for Windows requires including SDL.h if you define main(). Since + // the demo applications cannot rely on any platform/library specific + // stuff it's impossible to include SDL.h into the application files. + // The demo applications are simple and their use is restricted, so, + // this approach is quite reasonable. + // + class platform_support + { + public: + enum max_images_e { max_images = 16 }; + + // format - see enum pix_format_e {}; + // flip_y - true if you want to have the Y-axis flipped vertically. + platform_support(pix_format_e format, bool flip_y); + virtual ~platform_support(); + + // Setting the windows caption (title). Should be able + // to be called at least before calling init(). + // It's perfect if they can be called anytime. + void caption(const char* cap); + const char* caption() const { return m_caption; } + + //-------------------------------------------------------------------- + // These 3 methods handle working with images. The image + // formats are the simplest ones, such as .BMP in Windows or + // .ppm in Linux. In the applications the names of the files + // should not have any file extensions. Method load_img() can + // be called before init(), so, the application could be able + // to determine the initial size of the window depending on + // the size of the loaded image. + // The argument "idx" is the number of the image 0...max_images-1 + bool load_img(unsigned idx, const char* file); + bool save_img(unsigned idx, const char* file); + bool create_img(unsigned idx, unsigned width=0, unsigned height=0); + + //-------------------------------------------------------------------- + // init() and run(). See description before the class for details. + // The necessity of calling init() after creation is that it's + // impossible to call the overridden virtual function (on_init()) + // from the constructor. On the other hand it's very useful to have + // some on_init() event handler when the window is created but + // not yet displayed. The rbuf_window() method (see below) is + // accessible from on_init(). + bool init(unsigned width, unsigned height, unsigned flags); + int run(); + + //-------------------------------------------------------------------- + // The very same parameters that were used in the constructor + pix_format_e format() const { return m_format; } + bool flip_y() const { return m_flip_y; } + unsigned bpp() const { return m_bpp; } + + //-------------------------------------------------------------------- + // The following provides a very simple mechanism of doing someting + // in background. It's not multithreading. When wait_mode is true + // the class waits for the events and it does not ever call on_idle(). + // When it's false it calls on_idle() when the event queue is empty. + // The mode can be changed anytime. This mechanism is satisfactory + // to create very simple animations. + bool wait_mode() const { return m_wait_mode; } + void wait_mode(bool wait_mode) { m_wait_mode = wait_mode; } + + //-------------------------------------------------------------------- + // These two functions control updating of the window. + // force_redraw() is an analog of the Win32 InvalidateRect() function. + // Being called it sets a flag (or sends a message) which results + // in calling on_draw() and updating the content of the window + // when the next event cycle comes. + // update_window() results in just putting immediately the content + // of the currently rendered buffer to the window without calling + // on_draw(). + void force_redraw(); + void update_window(); + + //-------------------------------------------------------------------- + // So, finally, how to draw anythig with AGG? Very simple. + // rbuf_window() returns a reference to the main rendering + // buffer which can be attached to any rendering class. + // rbuf_img() returns a reference to the previously created + // or loaded image buffer (see load_img()). The image buffers + // are not displayed directly, they should be copied to or + // combined somehow with the rbuf_window(). rbuf_window() is + // the only buffer that can be actually displayed. + rendering_buffer& rbuf_window() { return m_rbuf_window; } + rendering_buffer& rbuf_img(unsigned idx) { return m_rbuf_img[idx]; } + + + //-------------------------------------------------------------------- + // Returns file extension used in the implementation for the particular + // system. + const char* img_ext() const; + + //-------------------------------------------------------------------- + void copy_img_to_window(unsigned idx) + { + if(idx < max_images && rbuf_img(idx).buf()) + { + rbuf_window().copy_from(rbuf_img(idx)); + } + } + + //-------------------------------------------------------------------- + void copy_window_to_img(unsigned idx) + { + if(idx < max_images) + { + create_img(idx, rbuf_window().width(), rbuf_window().height()); + rbuf_img(idx).copy_from(rbuf_window()); + } + } + + //-------------------------------------------------------------------- + void copy_img_to_img(unsigned idx_to, unsigned idx_from) + { + if(idx_from < max_images && + idx_to < max_images && + rbuf_img(idx_from).buf()) + { + create_img(idx_to, + rbuf_img(idx_from).width(), + rbuf_img(idx_from).height()); + rbuf_img(idx_to).copy_from(rbuf_img(idx_from)); + } + } + + //-------------------------------------------------------------------- + // Event handlers. They are not pure functions, so you don't have + // to override them all. + // In my demo applications these functions are defined inside + // the the_application class (implicit inlining) which is in general + // very bad practice, I mean vitual inline methods. At least it does + // not make sense. + // But in this case it's quite appropriate bacause we have the only + // instance of the the_application class and it is in the same file + // where this class is defined. + virtual void on_init(); + virtual void on_resize(int sx, int sy); + virtual void on_idle(); + virtual void on_mouse_move(int x, int y, unsigned flags); + virtual void on_mouse_button_down(int x, int y, unsigned flags); + virtual void on_mouse_button_up(int x, int y, unsigned flags); + virtual void on_key(int x, int y, unsigned key, unsigned flags); + virtual void on_ctrl_change(); + virtual void on_draw(); + virtual void on_post_draw(void* raw_handler); + + //-------------------------------------------------------------------- + // Adding control elements. A control element once added will be + // working and reacting to the mouse and keyboard events. Still, you + // will have to render them in the on_draw() using function + // render_ctrl() because platform_support doesn't know anything about + // renderers you use. The controls will be also scaled automatically + // if they provide a proper scaling mechanism (all the controls + // included into the basic AGG package do). + // If you don't need a particular control to be scaled automatically + // call ctrl::no_transform() after adding. + void add_ctrl(ctrl& c) { m_ctrls.add(c); c.transform(m_resize_mtx); } + + //-------------------------------------------------------------------- + // Auxiliary functions. trans_affine_resizing() modifier sets up the resizing + // matrix on the basis of the given width and height and the initial + // width and height of the window. The implementation should simply + // call this function every time when it catches the resizing event + // passing in the new values of width and height of the window. + // Nothing prevents you from "cheating" the scaling matrix if you + // call this function from somewhere with wrong arguments. + // trans_affine_resizing() accessor simply returns current resizing matrix + // which can be used to apply additional scaling of any of your + // stuff when the window is being resized. + // width(), height(), initial_width(), and initial_height() must be + // clear to understand with no comments :-) + void trans_affine_resizing(int width, int height) + { + if(m_window_flags & window_keep_aspect_ratio) + { + //double sx = double(width) / double(m_initial_width); + //double sy = double(height) / double(m_initial_height); + //if(sy < sx) sx = sy; + //m_resize_mtx = trans_affine_scaling(sx, sx); + trans_viewport vp; + vp.preserve_aspect_ratio(0.5, 0.5, aspect_ratio_meet); + vp.device_viewport(0, 0, width, height); + vp.world_viewport(0, 0, m_initial_width, m_initial_height); + m_resize_mtx = vp.to_affine(); + } + else + { + m_resize_mtx = trans_affine_scaling( + double(width) / double(m_initial_width), + double(height) / double(m_initial_height)); + } + } + const trans_affine& trans_affine_resizing() const { return m_resize_mtx; } + double width() const { return m_rbuf_window.width(); } + double height() const { return m_rbuf_window.height(); } + double initial_width() const { return m_initial_width; } + double initial_height() const { return m_initial_height; } + unsigned window_flags() const { return m_window_flags; } + + //-------------------------------------------------------------------- + // Get raw display handler depending on the system. + // For win32 its an HDC, for other systems it can be a pointer to some + // structure. See the implementation files for detals. + // It's provided "as is", so, first you should check if it's not null. + // If it's null the raw_display_handler is not supported. Also, there's + // no guarantee that this function is implemented, so, in some + // implementations you may have simply an unresolved symbol when linking. + void* raw_display_handler(); + + //-------------------------------------------------------------------- + // display message box or print the message to the console + // (depending on implementation) + void message(const char* msg); + + //-------------------------------------------------------------------- + // Stopwatch functions. Function elapsed_time() returns time elapsed + // since the latest start_timer() invocation in millisecods. + // The resolutoin depends on the implementation. + // In Win32 it uses QueryPerformanceFrequency() / QueryPerformanceCounter(). + void start_timer(); + double elapsed_time() const; + + //-------------------------------------------------------------------- + // Get the full file name. In most cases it simply returns + // file_name. As it's appropriate in many systems if you open + // a file by its name without specifying the path, it tries to + // open it in the current directory. The demos usually expect + // all the supplementary files to be placed in the current + // directory, that is usually coincides with the directory where + // the the executable is. However, in some systems (BeOS) it's not so. + // For those kinds of systems full_file_name() can help access files + // preserving commonly used policy. + // So, it's a good idea to use in the demos the following: + // FILE* fd = fopen(full_file_name("some.file"), "r"); + // instead of + // FILE* fd = fopen("some.file", "r"); + const char* full_file_name(const char* file_name); + + public: + platform_specific* m_specific; + ctrl_container m_ctrls; + + // Sorry, I'm too tired to describe the private + // data membders. See the implementations for different + // platforms for details. + private: + platform_support(const platform_support&); + const platform_support& operator = (const platform_support&); + + pix_format_e m_format; + unsigned m_bpp; + rendering_buffer m_rbuf_window; + rendering_buffer m_rbuf_img[max_images]; + unsigned m_window_flags; + bool m_wait_mode; + bool m_flip_y; + char m_caption[256]; + int m_initial_width; + int m_initial_height; + trans_affine m_resize_mtx; + }; + + +} + + + +#endif + diff --git a/desmume/src/windows/agg/include/platform/mac/agg_mac_pmap.h b/desmume/src/windows/agg/include/platform/mac/agg_mac_pmap.h new file mode 100644 index 000000000..e7633b800 --- /dev/null +++ b/desmume/src/windows/agg/include/platform/mac/agg_mac_pmap.h @@ -0,0 +1,92 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Copyright (C) 2002 Hansruedi Baer (MacOS support) +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_MAC_PMAP_INCLUDED +#define AGG_MAC_PMAP_INCLUDED + + +#include +#include + + +namespace agg +{ + enum org_e + { + org_mono8 = 8, + org_color16 = 16, + org_color24 = 24, + org_color32 = 32 + }; + + class pixel_map + { + public: + ~pixel_map(); + pixel_map(); + + public: + void destroy(); + void create(unsigned width, + unsigned height, + org_e org, + unsigned clear_val=255); + + void clear(unsigned clear_val=255); + bool load_from_qt(const char* filename); + bool save_as_qt(const char* filename) const; + + void draw(WindowRef window, + const Rect* device_rect=0, + const Rect* bmp_rect=0) const; + void draw(WindowRef window, int x, int y, double scale=1.0) const; + void blend(WindowRef window, + const Rect* device_rect=0, + const Rect* bmp_rect=0) const; + void blend(WindowRef window, int x, int y, double scale=1.0) const; + + unsigned char* buf(); + unsigned width() const; + unsigned height() const; + int row_bytes() const; + unsigned bpp() const { return m_bpp; } + + //Auxiliary static functions + static unsigned calc_row_len(unsigned width, unsigned bits_per_pixel); + private: + pixel_map(const pixel_map&); + const pixel_map& operator = (const pixel_map&); + + private: + GWorldPtr m_pmap; + unsigned char* m_buf; + unsigned m_bpp; + unsigned m_img_size; + }; + +} + + +#endif diff --git a/desmume/src/windows/agg/include/platform/win32/agg_win32_bmp.h b/desmume/src/windows/agg/include/platform/win32/agg_win32_bmp.h new file mode 100644 index 000000000..e8b3dad1e --- /dev/null +++ b/desmume/src/windows/agg/include/platform/win32/agg_win32_bmp.h @@ -0,0 +1,123 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_WIN32_BMP_INCLUDED +#define AGG_WIN32_BMP_INCLUDED + + +#include +#include + + +namespace agg +{ + enum org_e + { + org_mono8 = 8, + org_color16 = 16, + org_color24 = 24, + org_color32 = 32, + org_color48 = 48, + org_color64 = 64 + }; + + class pixel_map + { + public: + ~pixel_map(); + pixel_map(); + + public: + void destroy(); + void create(unsigned width, + unsigned height, + org_e org, + unsigned clear_val=256); + HBITMAP create_dib_section(HDC h_dc, + unsigned width, + unsigned height, + org_e org, + unsigned clear_val=256); + + void clear(unsigned clear_val=256); + void attach_to_bmp(BITMAPINFO* bmp); + BITMAPINFO* bitmap_info() { return m_bmp; } + bool load_from_bmp(FILE* fd); + bool save_as_bmp(FILE* fd) const; + bool load_from_bmp(const char* filename); + bool save_as_bmp(const char* filename) const; + + void draw(HDC h_dc, + const RECT* device_rect=0, + const RECT* bmp_rect=0) const; + void draw(HDC h_dc, int x, int y, double scale=1.0) const; + + void blend(HDC h_dc, + const RECT* device_rect=0, + const RECT* bmp_rect=0) const; + void blend(HDC h_dc, int x, int y, double scale=1.0) const; + + + unsigned char* buf(); + unsigned width() const; + unsigned height() const; + int stride() const; + unsigned bpp() const { return m_bpp; } + + //Auxiliary static functions + static unsigned calc_full_size(BITMAPINFO *bmp); + static unsigned calc_header_size(BITMAPINFO *bmp); + static unsigned calc_palette_size(unsigned clr_used, + unsigned bits_per_pixel); + static unsigned calc_palette_size(BITMAPINFO *bmp); + static unsigned char* calc_img_ptr(BITMAPINFO *bmp); + static BITMAPINFO* create_bitmap_info(unsigned width, + unsigned height, + unsigned bits_per_pixel); + static void create_gray_scale_palette(BITMAPINFO *bmp); + static unsigned calc_row_len(unsigned width, unsigned bits_per_pixel); + + private: + pixel_map(const pixel_map&); + const pixel_map& operator = (const pixel_map&); + void create_from_bmp(BITMAPINFO *bmp); + + HBITMAP create_dib_section_from_args(HDC h_dc, + unsigned width, + unsigned height, + unsigned bits_per_pixel); + + private: + BITMAPINFO* m_bmp; + unsigned char* m_buf; + unsigned m_bpp; + bool m_is_internal; + unsigned m_img_size; + unsigned m_full_size; + }; + +} + + +#endif diff --git a/desmume/src/windows/agg/include/util/agg_color_conv.h b/desmume/src/windows/agg/include/util/agg_color_conv.h new file mode 100644 index 000000000..940d7d155 --- /dev/null +++ b/desmume/src/windows/agg/include/util/agg_color_conv.h @@ -0,0 +1,89 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_CONV_INCLUDED +#define AGG_COLOR_CONV_INCLUDED + +#include +#include "agg_basics.h" +#include "agg_rendering_buffer.h" + + + + +namespace agg +{ + + //--------------------------------------------------------------color_conv + template + void color_conv(RenBuf* dst, const RenBuf* src, CopyRow copy_row_functor) + { + unsigned width = src->width(); + unsigned height = src->height(); + + if(dst->width() < width) width = dst->width(); + if(dst->height() < height) height = dst->height(); + + if(width) + { + unsigned y; + for(y = 0; y < height; y++) + { + copy_row_functor(dst->row_ptr(0, y, width), + src->row_ptr(y), + width); + } + } + } + + + //---------------------------------------------------------color_conv_row + template + void color_conv_row(int8u* dst, + const int8u* src, + unsigned width, + CopyRow copy_row_functor) + { + copy_row_functor(dst, src, width); + } + + + //---------------------------------------------------------color_conv_same + template class color_conv_same + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + memmove(dst, src, width*BPP); + } + }; + + +} + + + +#endif diff --git a/desmume/src/windows/agg/include/util/agg_color_conv_rgb16.h b/desmume/src/windows/agg/include/util/agg_color_conv_rgb16.h new file mode 100644 index 000000000..4b780a382 --- /dev/null +++ b/desmume/src/windows/agg/include/util/agg_color_conv_rgb16.h @@ -0,0 +1,294 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// This part of the library has been sponsored by +// Liberty Technology Systems, Inc., visit http://lib-sys.com +// +// Liberty Technology Systems, Inc. is the provider of +// PostScript and PDF technology for software developers. +// +//---------------------------------------------------------------------------- +// +// A set of functors used with color_conv(). See file agg_color_conv.h +// These functors can convert images with up to 8 bits per component. +// Use convertors in the following way: +// +// agg::color_conv(dst, src, agg::color_conv_XXXX_to_YYYY()); +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_CONV_RGB16_INCLUDED +#define AGG_COLOR_CONV_RGB16_INCLUDED + +#include "agg_basics.h" +#include "agg_color_conv.h" + +namespace agg +{ + + //-------------------------------------------------color_conv_gray16_to_gray8 + class color_conv_gray16_to_gray8 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + int16u* s = (int16u*)src; + do + { + *dst++ = *s++ >> 8; + } + while(--width); + } + }; + + + //-----------------------------------------------------color_conv_rgb24_rgb48 + template class color_conv_rgb24_rgb48 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + int16u* d = (int16u*)dst; + do + { + *d++ = (src[I1] << 8) | src[I1]; + *d++ = (src[1] << 8) | src[1] ; + *d++ = (src[I3] << 8) | src[I3]; + src += 3; + } + while(--width); + } + }; + + typedef color_conv_rgb24_rgb48<0,2> color_conv_rgb24_to_rgb48; + typedef color_conv_rgb24_rgb48<0,2> color_conv_bgr24_to_bgr48; + typedef color_conv_rgb24_rgb48<2,0> color_conv_rgb24_to_bgr48; + typedef color_conv_rgb24_rgb48<2,0> color_conv_bgr24_to_rgb48; + + + //-----------------------------------------------------color_conv_rgb24_rgb48 + template class color_conv_rgb48_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + const int16u* s = (const int16u*)src; + do + { + *dst++ = s[I1] >> 8; + *dst++ = s[1] >> 8; + *dst++ = s[I3] >> 8; + s += 3; + } + while(--width); + } + }; + + typedef color_conv_rgb48_rgb24<0,2> color_conv_rgb48_to_rgb24; + typedef color_conv_rgb48_rgb24<0,2> color_conv_bgr48_to_bgr24; + typedef color_conv_rgb48_rgb24<2,0> color_conv_rgb48_to_bgr24; + typedef color_conv_rgb48_rgb24<2,0> color_conv_bgr48_to_rgb24; + + + //----------------------------------------------color_conv_rgbAAA_rgb24 + template class color_conv_rgbAAA_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + int32u rgb = *(int32u*)src; + dst[R] = int8u(rgb >> 22); + dst[1] = int8u(rgb >> 12); + dst[B] = int8u(rgb >> 2); + src += 4; + dst += 3; + } + while(--width); + } + }; + + typedef color_conv_rgbAAA_rgb24<0,2> color_conv_rgbAAA_to_rgb24; + typedef color_conv_rgbAAA_rgb24<2,0> color_conv_rgbAAA_to_bgr24; + typedef color_conv_rgbAAA_rgb24<2,0> color_conv_bgrAAA_to_rgb24; + typedef color_conv_rgbAAA_rgb24<0,2> color_conv_bgrAAA_to_bgr24; + + + //----------------------------------------------color_conv_rgbBBA_rgb24 + template class color_conv_rgbBBA_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + int32u rgb = *(int32u*)src; + dst[R] = int8u(rgb >> 24); + dst[1] = int8u(rgb >> 13); + dst[B] = int8u(rgb >> 2); + src += 4; + dst += 3; + } + while(--width); + } + }; + + typedef color_conv_rgbBBA_rgb24<0,2> color_conv_rgbBBA_to_rgb24; + typedef color_conv_rgbBBA_rgb24<2,0> color_conv_rgbBBA_to_bgr24; + + + //----------------------------------------------color_conv_bgrABB_rgb24 + template class color_conv_bgrABB_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + int32u bgr = *(int32u*)src; + dst[R] = int8u(bgr >> 3); + dst[1] = int8u(bgr >> 14); + dst[B] = int8u(bgr >> 24); + src += 4; + dst += 3; + } + while(--width); + } + }; + + typedef color_conv_bgrABB_rgb24<2,0> color_conv_bgrABB_to_rgb24; + typedef color_conv_bgrABB_rgb24<0,2> color_conv_bgrABB_to_bgr24; + + + //-------------------------------------------------color_conv_rgba64_rgba32 + template class color_conv_rgba64_rgba32 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *dst++ = int8u(((int16u*)src)[I1] >> 8); + *dst++ = int8u(((int16u*)src)[I2] >> 8); + *dst++ = int8u(((int16u*)src)[I3] >> 8); + *dst++ = int8u(((int16u*)src)[I4] >> 8); + src += 8; + } + while(--width); + } + }; + + //------------------------------------------------------------------------ + typedef color_conv_rgba64_rgba32<0,1,2,3> color_conv_rgba64_to_rgba32; //----color_conv_rgba64_to_rgba32 + typedef color_conv_rgba64_rgba32<0,1,2,3> color_conv_argb64_to_argb32; //----color_conv_argb64_to_argb32 + typedef color_conv_rgba64_rgba32<0,1,2,3> color_conv_bgra64_to_bgra32; //----color_conv_bgra64_to_bgra32 + typedef color_conv_rgba64_rgba32<0,1,2,3> color_conv_abgr64_to_abgr32; //----color_conv_abgr64_to_abgr32 + typedef color_conv_rgba64_rgba32<0,3,2,1> color_conv_argb64_to_abgr32; //----color_conv_argb64_to_abgr32 + typedef color_conv_rgba64_rgba32<3,2,1,0> color_conv_argb64_to_bgra32; //----color_conv_argb64_to_bgra32 + typedef color_conv_rgba64_rgba32<1,2,3,0> color_conv_argb64_to_rgba32; //----color_conv_argb64_to_rgba32 + typedef color_conv_rgba64_rgba32<3,0,1,2> color_conv_bgra64_to_abgr32; //----color_conv_bgra64_to_abgr32 + typedef color_conv_rgba64_rgba32<3,2,1,0> color_conv_bgra64_to_argb32; //----color_conv_bgra64_to_argb32 + typedef color_conv_rgba64_rgba32<2,1,0,3> color_conv_bgra64_to_rgba32; //----color_conv_bgra64_to_rgba32 + typedef color_conv_rgba64_rgba32<3,2,1,0> color_conv_rgba64_to_abgr32; //----color_conv_rgba64_to_abgr32 + typedef color_conv_rgba64_rgba32<3,0,1,2> color_conv_rgba64_to_argb32; //----color_conv_rgba64_to_argb32 + typedef color_conv_rgba64_rgba32<2,1,0,3> color_conv_rgba64_to_bgra32; //----color_conv_rgba64_to_bgra32 + typedef color_conv_rgba64_rgba32<0,3,2,1> color_conv_abgr64_to_argb32; //----color_conv_abgr64_to_argb32 + typedef color_conv_rgba64_rgba32<1,2,3,0> color_conv_abgr64_to_bgra32; //----color_conv_abgr64_to_bgra32 + typedef color_conv_rgba64_rgba32<3,2,1,0> color_conv_abgr64_to_rgba32; //----color_conv_abgr64_to_rgba32 + + + + //--------------------------------------------color_conv_rgb24_rgba64 + template class color_conv_rgb24_rgba64 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + int16u* d = (int16u*)dst; + do + { + d[I1] = (src[0] << 8) | src[0]; + d[I2] = (src[1] << 8) | src[1]; + d[I3] = (src[2] << 8) | src[2]; + d[A] = 65535; + d += 4; + src += 3; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb24_rgba64<1,2,3,0> color_conv_rgb24_to_argb64; //----color_conv_rgb24_to_argb64 + typedef color_conv_rgb24_rgba64<3,2,1,0> color_conv_rgb24_to_abgr64; //----color_conv_rgb24_to_abgr64 + typedef color_conv_rgb24_rgba64<2,1,0,3> color_conv_rgb24_to_bgra64; //----color_conv_rgb24_to_bgra64 + typedef color_conv_rgb24_rgba64<0,1,2,3> color_conv_rgb24_to_rgba64; //----color_conv_rgb24_to_rgba64 + typedef color_conv_rgb24_rgba64<3,2,1,0> color_conv_bgr24_to_argb64; //----color_conv_bgr24_to_argb64 + typedef color_conv_rgb24_rgba64<1,2,3,0> color_conv_bgr24_to_abgr64; //----color_conv_bgr24_to_abgr64 + typedef color_conv_rgb24_rgba64<0,1,2,3> color_conv_bgr24_to_bgra64; //----color_conv_bgr24_to_bgra64 + typedef color_conv_rgb24_rgba64<2,1,0,3> color_conv_bgr24_to_rgba64; //----color_conv_bgr24_to_rgba64 + + + template class color_conv_rgb24_gray16 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + int16u* d = (int16u*)dst; + do + { + *d++ = src[R]*77 + src[1]*150 + src[B]*29; + src += 3; + } + while(--width); + } + }; + + typedef color_conv_rgb24_gray16<0,2> color_conv_rgb24_to_gray16; + typedef color_conv_rgb24_gray16<2,0> color_conv_bgr24_to_gray16; + + +} + + +#endif diff --git a/desmume/src/windows/agg/include/util/agg_color_conv_rgb8.h b/desmume/src/windows/agg/include/util/agg_color_conv_rgb8.h new file mode 100644 index 000000000..6a7525cf5 --- /dev/null +++ b/desmume/src/windows/agg/include/util/agg_color_conv_rgb8.h @@ -0,0 +1,478 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +// +// A set of functors used with color_conv(). See file agg_color_conv.h +// These functors can convert images with up to 8 bits per component. +// Use convertors in the following way: +// +// agg::color_conv(dst, src, agg::color_conv_XXXX_to_YYYY()); +// whare XXXX and YYYY can be any of: +// rgb24 +// bgr24 +// rgba32 +// abgr32 +// argb32 +// bgra32 +// rgb555 +// rgb565 +//---------------------------------------------------------------------------- + +#ifndef AGG_COLOR_CONV_RGB8_INCLUDED +#define AGG_COLOR_CONV_RGB8_INCLUDED + +#include "agg_basics.h" +#include "agg_color_conv.h" + +namespace agg +{ + + //-----------------------------------------------------color_conv_rgb24 + class color_conv_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *dst++ = src[2]; + *dst++ = src[1]; + *dst++ = src[0]; + src += 3; + } + while(--width); + } + }; + + typedef color_conv_rgb24 color_conv_rgb24_to_bgr24; + typedef color_conv_rgb24 color_conv_bgr24_to_rgb24; + + typedef color_conv_same<3> color_conv_bgr24_to_bgr24; + typedef color_conv_same<3> color_conv_rgb24_to_rgb24; + + + + //------------------------------------------------------color_conv_rgba32 + template class color_conv_rgba32 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *dst++ = src[I1]; + *dst++ = src[I2]; + *dst++ = src[I3]; + *dst++ = src[I4]; + src += 4; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgba32<0,3,2,1> color_conv_argb32_to_abgr32; //----color_conv_argb32_to_abgr32 + typedef color_conv_rgba32<3,2,1,0> color_conv_argb32_to_bgra32; //----color_conv_argb32_to_bgra32 + typedef color_conv_rgba32<1,2,3,0> color_conv_argb32_to_rgba32; //----color_conv_argb32_to_rgba32 + typedef color_conv_rgba32<3,0,1,2> color_conv_bgra32_to_abgr32; //----color_conv_bgra32_to_abgr32 + typedef color_conv_rgba32<3,2,1,0> color_conv_bgra32_to_argb32; //----color_conv_bgra32_to_argb32 + typedef color_conv_rgba32<2,1,0,3> color_conv_bgra32_to_rgba32; //----color_conv_bgra32_to_rgba32 + typedef color_conv_rgba32<3,2,1,0> color_conv_rgba32_to_abgr32; //----color_conv_rgba32_to_abgr32 + typedef color_conv_rgba32<3,0,1,2> color_conv_rgba32_to_argb32; //----color_conv_rgba32_to_argb32 + typedef color_conv_rgba32<2,1,0,3> color_conv_rgba32_to_bgra32; //----color_conv_rgba32_to_bgra32 + typedef color_conv_rgba32<0,3,2,1> color_conv_abgr32_to_argb32; //----color_conv_abgr32_to_argb32 + typedef color_conv_rgba32<1,2,3,0> color_conv_abgr32_to_bgra32; //----color_conv_abgr32_to_bgra32 + typedef color_conv_rgba32<3,2,1,0> color_conv_abgr32_to_rgba32; //----color_conv_abgr32_to_rgba32 + + //------------------------------------------------------------------------ + typedef color_conv_same<4> color_conv_rgba32_to_rgba32; //----color_conv_rgba32_to_rgba32 + typedef color_conv_same<4> color_conv_argb32_to_argb32; //----color_conv_argb32_to_argb32 + typedef color_conv_same<4> color_conv_bgra32_to_bgra32; //----color_conv_bgra32_to_bgra32 + typedef color_conv_same<4> color_conv_abgr32_to_abgr32; //----color_conv_abgr32_to_abgr32 + + + //--------------------------------------------color_conv_rgb24_rgba32 + template class color_conv_rgb24_rgba32 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + dst[I1] = *src++; + dst[I2] = *src++; + dst[I3] = *src++; + dst[A] = 255; + dst += 4; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb24_rgba32<1,2,3,0> color_conv_rgb24_to_argb32; //----color_conv_rgb24_to_argb32 + typedef color_conv_rgb24_rgba32<3,2,1,0> color_conv_rgb24_to_abgr32; //----color_conv_rgb24_to_abgr32 + typedef color_conv_rgb24_rgba32<2,1,0,3> color_conv_rgb24_to_bgra32; //----color_conv_rgb24_to_bgra32 + typedef color_conv_rgb24_rgba32<0,1,2,3> color_conv_rgb24_to_rgba32; //----color_conv_rgb24_to_rgba32 + typedef color_conv_rgb24_rgba32<3,2,1,0> color_conv_bgr24_to_argb32; //----color_conv_bgr24_to_argb32 + typedef color_conv_rgb24_rgba32<1,2,3,0> color_conv_bgr24_to_abgr32; //----color_conv_bgr24_to_abgr32 + typedef color_conv_rgb24_rgba32<0,1,2,3> color_conv_bgr24_to_bgra32; //----color_conv_bgr24_to_bgra32 + typedef color_conv_rgb24_rgba32<2,1,0,3> color_conv_bgr24_to_rgba32; //----color_conv_bgr24_to_rgba32 + + + + //-------------------------------------------------color_conv_rgba32_rgb24 + template class color_conv_rgba32_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *dst++ = src[I1]; + *dst++ = src[I2]; + *dst++ = src[I3]; + src += 4; + } + while(--width); + } + }; + + + + //------------------------------------------------------------------------ + typedef color_conv_rgba32_rgb24<1,2,3> color_conv_argb32_to_rgb24; //----color_conv_argb32_to_rgb24 + typedef color_conv_rgba32_rgb24<3,2,1> color_conv_abgr32_to_rgb24; //----color_conv_abgr32_to_rgb24 + typedef color_conv_rgba32_rgb24<2,1,0> color_conv_bgra32_to_rgb24; //----color_conv_bgra32_to_rgb24 + typedef color_conv_rgba32_rgb24<0,1,2> color_conv_rgba32_to_rgb24; //----color_conv_rgba32_to_rgb24 + typedef color_conv_rgba32_rgb24<3,2,1> color_conv_argb32_to_bgr24; //----color_conv_argb32_to_bgr24 + typedef color_conv_rgba32_rgb24<1,2,3> color_conv_abgr32_to_bgr24; //----color_conv_abgr32_to_bgr24 + typedef color_conv_rgba32_rgb24<0,1,2> color_conv_bgra32_to_bgr24; //----color_conv_bgra32_to_bgr24 + typedef color_conv_rgba32_rgb24<2,1,0> color_conv_rgba32_to_bgr24; //----color_conv_rgba32_to_bgr24 + + + //------------------------------------------------color_conv_rgb555_rgb24 + template class color_conv_rgb555_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + unsigned rgb = *(int16u*)src; + dst[R] = (int8u)((rgb >> 7) & 0xF8); + dst[1] = (int8u)((rgb >> 2) & 0xF8); + dst[B] = (int8u)((rgb << 3) & 0xF8); + src += 2; + dst += 3; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb555_rgb24<2,0> color_conv_rgb555_to_bgr24; //----color_conv_rgb555_to_bgr24 + typedef color_conv_rgb555_rgb24<0,2> color_conv_rgb555_to_rgb24; //----color_conv_rgb555_to_rgb24 + + + //-------------------------------------------------color_conv_rgb24_rgb555 + template class color_conv_rgb24_rgb555 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *(int16u*)dst = (int16u)(((unsigned(src[R]) << 7) & 0x7C00) | + ((unsigned(src[1]) << 2) & 0x3E0) | + ((unsigned(src[B]) >> 3))); + src += 3; + dst += 2; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb24_rgb555<2,0> color_conv_bgr24_to_rgb555; //----color_conv_bgr24_to_rgb555 + typedef color_conv_rgb24_rgb555<0,2> color_conv_rgb24_to_rgb555; //----color_conv_rgb24_to_rgb555 + + + //-------------------------------------------------color_conv_rgb565_rgb24 + template class color_conv_rgb565_rgb24 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + unsigned rgb = *(int16u*)src; + dst[R] = (rgb >> 8) & 0xF8; + dst[1] = (rgb >> 3) & 0xFC; + dst[B] = (rgb << 3) & 0xF8; + src += 2; + dst += 3; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb565_rgb24<2,0> color_conv_rgb565_to_bgr24; //----color_conv_rgb565_to_bgr24 + typedef color_conv_rgb565_rgb24<0,2> color_conv_rgb565_to_rgb24; //----color_conv_rgb565_to_rgb24 + + + //-------------------------------------------------color_conv_rgb24_rgb565 + template class color_conv_rgb24_rgb565 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *(int16u*)dst = (int16u)(((unsigned(src[R]) << 8) & 0xF800) | + ((unsigned(src[1]) << 3) & 0x7E0) | + ((unsigned(src[B]) >> 3))); + src += 3; + dst += 2; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb24_rgb565<2,0> color_conv_bgr24_to_rgb565; //----color_conv_bgr24_to_rgb565 + typedef color_conv_rgb24_rgb565<0,2> color_conv_rgb24_to_rgb565; //----color_conv_rgb24_to_rgb565 + + + + //-------------------------------------------------color_conv_rgb555_rgba32 + template class color_conv_rgb555_rgba32 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + int rgb = *(int16*)src; + dst[R] = (int8u)((rgb >> 7) & 0xF8); + dst[G] = (int8u)((rgb >> 2) & 0xF8); + dst[B] = (int8u)((rgb << 3) & 0xF8); + dst[A] = (int8u)(rgb >> 15); + src += 2; + dst += 4; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb555_rgba32<1,2,3,0> color_conv_rgb555_to_argb32; //----color_conv_rgb555_to_argb32 + typedef color_conv_rgb555_rgba32<3,2,1,0> color_conv_rgb555_to_abgr32; //----color_conv_rgb555_to_abgr32 + typedef color_conv_rgb555_rgba32<2,1,0,3> color_conv_rgb555_to_bgra32; //----color_conv_rgb555_to_bgra32 + typedef color_conv_rgb555_rgba32<0,1,2,3> color_conv_rgb555_to_rgba32; //----color_conv_rgb555_to_rgba32 + + + //------------------------------------------------color_conv_rgba32_rgb555 + template class color_conv_rgba32_rgb555 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *(int16u*)dst = (int16u)(((unsigned(src[R]) << 7) & 0x7C00) | + ((unsigned(src[G]) << 2) & 0x3E0) | + ((unsigned(src[B]) >> 3)) | + ((unsigned(src[A]) << 8) & 0x8000)); + src += 4; + dst += 2; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgba32_rgb555<1,2,3,0> color_conv_argb32_to_rgb555; //----color_conv_argb32_to_rgb555 + typedef color_conv_rgba32_rgb555<3,2,1,0> color_conv_abgr32_to_rgb555; //----color_conv_abgr32_to_rgb555 + typedef color_conv_rgba32_rgb555<2,1,0,3> color_conv_bgra32_to_rgb555; //----color_conv_bgra32_to_rgb555 + typedef color_conv_rgba32_rgb555<0,1,2,3> color_conv_rgba32_to_rgb555; //----color_conv_rgba32_to_rgb555 + + + + //------------------------------------------------color_conv_rgb565_rgba32 + template class color_conv_rgb565_rgba32 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + int rgb = *(int16*)src; + dst[R] = (rgb >> 8) & 0xF8; + dst[G] = (rgb >> 3) & 0xFC; + dst[B] = (rgb << 3) & 0xF8; + dst[A] = 255; + src += 2; + dst += 4; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgb565_rgba32<1,2,3,0> color_conv_rgb565_to_argb32; //----color_conv_rgb565_to_argb32 + typedef color_conv_rgb565_rgba32<3,2,1,0> color_conv_rgb565_to_abgr32; //----color_conv_rgb565_to_abgr32 + typedef color_conv_rgb565_rgba32<2,1,0,3> color_conv_rgb565_to_bgra32; //----color_conv_rgb565_to_bgra32 + typedef color_conv_rgb565_rgba32<0,1,2,3> color_conv_rgb565_to_rgba32; //----color_conv_rgb565_to_rgba32 + + + //------------------------------------------------color_conv_rgba32_rgb565 + template class color_conv_rgba32_rgb565 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *(int16u*)dst = (int16u)(((unsigned(src[R]) << 8) & 0xF800) | + ((unsigned(src[G]) << 3) & 0x7E0) | + ((unsigned(src[B]) >> 3))); + src += 4; + dst += 2; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_rgba32_rgb565<1,2,3> color_conv_argb32_to_rgb565; //----color_conv_argb32_to_rgb565 + typedef color_conv_rgba32_rgb565<3,2,1> color_conv_abgr32_to_rgb565; //----color_conv_abgr32_to_rgb565 + typedef color_conv_rgba32_rgb565<2,1,0> color_conv_bgra32_to_rgb565; //----color_conv_bgra32_to_rgb565 + typedef color_conv_rgba32_rgb565<0,1,2> color_conv_rgba32_to_rgb565; //----color_conv_rgba32_to_rgb565 + + + //---------------------------------------------color_conv_rgb555_to_rgb565 + class color_conv_rgb555_to_rgb565 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + unsigned rgb = *(int16u*)src; + *(int16u*)dst = (int16u)(((rgb << 1) & 0xFFC0) | (rgb & 0x1F)); + src += 2; + dst += 2; + } + while(--width); + } + }; + + + //----------------------------------------------color_conv_rgb565_to_rgb555 + class color_conv_rgb565_to_rgb555 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + unsigned rgb = *(int16u*)src; + *(int16u*)dst = (int16u)(((rgb >> 1) & 0x7FE0) | (rgb & 0x1F)); + src += 2; + dst += 2; + } + while(--width); + } + }; + + + //------------------------------------------------------------------------ + typedef color_conv_same<2> color_conv_rgb555_to_rgb555; //----color_conv_rgb555_to_rgb555 + typedef color_conv_same<2> color_conv_rgb565_to_rgb565; //----color_conv_rgb565_to_rgb565 + + + template class color_conv_rgb24_gray8 + { + public: + void operator () (int8u* dst, + const int8u* src, + unsigned width) const + { + do + { + *dst++ = (src[R]*77 + src[1]*150 + src[B]*29) >> 8; + src += 3; + } + while(--width); + } + }; + + typedef color_conv_rgb24_gray8<0,2> color_conv_rgb24_to_gray8; //----color_conv_rgb24_to_gray8 + typedef color_conv_rgb24_gray8<2,0> color_conv_bgr24_to_gray8; //----color_conv_bgr24_to_gray8 + + +} + + + +#endif diff --git a/desmume/src/windows/agg/src/ChangeLog b/desmume/src/windows/agg/src/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/desmume/src/windows/agg/src/Makefile b/desmume/src/windows/agg/src/Makefile new file mode 100644 index 000000000..429f66ab8 --- /dev/null +++ b/desmume/src/windows/agg/src/Makefile @@ -0,0 +1,59 @@ +include ../Makefile.in.$(shell uname) + +CXXFLAGS= $(AGGCXXFLAGS) -I../include -L./ + +SRC_CXX=\ +agg_arc.cpp \ +agg_arrowhead.cpp \ +agg_bezier_arc.cpp \ +agg_bspline.cpp \ +agg_curves.cpp \ +agg_vcgen_contour.cpp \ +agg_vcgen_dash.cpp \ +agg_vcgen_markers_term.cpp \ +agg_vcgen_smooth_poly1.cpp \ +agg_vcgen_stroke.cpp \ +agg_vcgen_bspline.cpp \ +agg_gsv_text.cpp \ +agg_image_filters.cpp \ +agg_line_aa_basics.cpp \ +agg_line_profile_aa.cpp \ +agg_rounded_rect.cpp \ +agg_sqrt_tables.cpp \ +agg_embedded_raster_fonts.cpp \ +agg_trans_affine.cpp \ +agg_trans_warp_magnifier.cpp \ +agg_trans_single_path.cpp \ +agg_trans_double_path.cpp \ +agg_vpgen_clip_polygon.cpp \ +agg_vpgen_clip_polyline.cpp \ +agg_vpgen_segmentator.cpp \ +ctrl/agg_cbox_ctrl.cpp \ +ctrl/agg_gamma_ctrl.cpp \ +ctrl/agg_gamma_spline.cpp \ +ctrl/agg_rbox_ctrl.cpp \ +ctrl/agg_slider_ctrl.cpp \ +ctrl/agg_spline_ctrl.cpp \ +ctrl/agg_scale_ctrl.cpp \ +ctrl/agg_polygon_ctrl.cpp \ +ctrl/agg_bezier_ctrl.cpp + +SRC_C=\ +../gpc/gpc.c + + +OBJ=$(SRC_CXX:.cpp=.o) $(SRC_C:.c=.o) + +all: $(OBJ) + $(LIB) libagg.a $(OBJ) + +clean: + rm -f *.o *.a ctrl/*.o ../gpc/*.o + rm -rf SunWS_cache + rm -rf ctrl/SunWS_cache + +%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $*.cpp -o $@ + +%.o: %.c + $(C) -c $(CXXFLAGS) $*.c -o $@ diff --git a/desmume/src/windows/agg/src/agg_arc.cpp b/desmume/src/windows/agg/src/agg_arc.cpp new file mode 100644 index 000000000..f97717f58 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_arc.cpp @@ -0,0 +1,111 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_arc.h" + + +namespace agg +{ + //------------------------------------------------------------------------ + arc::arc(double x, double y, + double rx, double ry, + double a1, double a2, + bool ccw) : + m_x(x), m_y(y), m_rx(rx), m_ry(ry), m_scale(1.0) + { + normalize(a1, a2, ccw); + } + + //------------------------------------------------------------------------ + void arc::init(double x, double y, + double rx, double ry, + double a1, double a2, + bool ccw) + { + m_x = x; m_y = y; + m_rx = rx; m_ry = ry; + normalize(a1, a2, ccw); + } + + //------------------------------------------------------------------------ + void arc::approximation_scale(double s) + { + m_scale = s; + if(m_initialized) + { + normalize(m_start, m_end, m_ccw); + } + } + + //------------------------------------------------------------------------ + void arc::rewind(unsigned) + { + m_path_cmd = path_cmd_move_to; + m_angle = m_start; + } + + //------------------------------------------------------------------------ + unsigned arc::vertex(double* x, double* y) + { + if(is_stop(m_path_cmd)) return path_cmd_stop; + if((m_angle < m_end - m_da/4) != m_ccw) + { + *x = m_x + cos(m_end) * m_rx; + *y = m_y + sin(m_end) * m_ry; + m_path_cmd = path_cmd_stop; + return path_cmd_line_to; + } + + *x = m_x + cos(m_angle) * m_rx; + *y = m_y + sin(m_angle) * m_ry; + + m_angle += m_da; + + unsigned pf = m_path_cmd; + m_path_cmd = path_cmd_line_to; + return pf; + } + + //------------------------------------------------------------------------ + void arc::normalize(double a1, double a2, bool ccw) + { + double ra = (fabs(m_rx) + fabs(m_ry)) / 2; + m_da = acos(ra / (ra + 0.125 / m_scale)) * 2; + if(ccw) + { + while(a2 < a1) a2 += pi * 2.0; + } + else + { + while(a1 < a2) a1 += pi * 2.0; + m_da = -m_da; + } + m_ccw = ccw; + m_start = a1; + m_end = a2; + m_initialized = true; + } + +} diff --git a/desmume/src/windows/agg/src/agg_arrowhead.cpp b/desmume/src/windows/agg/src/agg_arrowhead.cpp new file mode 100644 index 000000000..47a6572a8 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_arrowhead.cpp @@ -0,0 +1,115 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_arrowhead.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + arrowhead::arrowhead() : + m_head_d1(1.0), + m_head_d2(1.0), + m_head_d3(1.0), + m_head_d4(0.0), + m_tail_d1(1.0), + m_tail_d2(1.0), + m_tail_d3(1.0), + m_tail_d4(0.0), + m_head_flag(false), + m_tail_flag(false), + m_curr_id(0), + m_curr_coord(0) + { + } + + + + //------------------------------------------------------------------------ + void arrowhead::rewind(unsigned path_id) + { + m_curr_id = path_id; + m_curr_coord = 0; + if(path_id == 0) + { + if(!m_tail_flag) + { + m_cmd[0] = path_cmd_stop; + return; + } + m_coord[0] = m_tail_d1; m_coord[1] = 0.0; + m_coord[2] = m_tail_d1 - m_tail_d4; m_coord[3] = m_tail_d3; + m_coord[4] = -m_tail_d2 - m_tail_d4; m_coord[5] = m_tail_d3; + m_coord[6] = -m_tail_d2; m_coord[7] = 0.0; + m_coord[8] = -m_tail_d2 - m_tail_d4; m_coord[9] = -m_tail_d3; + m_coord[10] = m_tail_d1 - m_tail_d4; m_coord[11] = -m_tail_d3; + + m_cmd[0] = path_cmd_move_to; + m_cmd[1] = path_cmd_line_to; + m_cmd[2] = path_cmd_line_to; + m_cmd[3] = path_cmd_line_to; + m_cmd[4] = path_cmd_line_to; + m_cmd[5] = path_cmd_line_to; + m_cmd[7] = path_cmd_end_poly | path_flags_close | path_flags_ccw; + m_cmd[6] = path_cmd_stop; + return; + } + + if(path_id == 1) + { + if(!m_head_flag) + { + m_cmd[0] = path_cmd_stop; + return; + } + m_coord[0] = -m_head_d1; m_coord[1] = 0.0; + m_coord[2] = m_head_d2 + m_head_d4; m_coord[3] = -m_head_d3; + m_coord[4] = m_head_d2; m_coord[5] = 0.0; + m_coord[6] = m_head_d2 + m_head_d4; m_coord[7] = m_head_d3; + + m_cmd[0] = path_cmd_move_to; + m_cmd[1] = path_cmd_line_to; + m_cmd[2] = path_cmd_line_to; + m_cmd[3] = path_cmd_line_to; + m_cmd[4] = path_cmd_end_poly | path_flags_close | path_flags_ccw; + m_cmd[5] = path_cmd_stop; + return; + } + } + + + //------------------------------------------------------------------------ + unsigned arrowhead::vertex(double* x, double* y) + { + if(m_curr_id < 2) + { + unsigned curr_idx = m_curr_coord * 2; + *x = m_coord[curr_idx]; + *y = m_coord[curr_idx + 1]; + return m_cmd[m_curr_coord++]; + } + return path_cmd_stop; + } + +} diff --git a/desmume/src/windows/agg/src/agg_bezier_arc.cpp b/desmume/src/windows/agg/src/agg_bezier_arc.cpp new file mode 100644 index 000000000..2140783ac --- /dev/null +++ b/desmume/src/windows/agg/src/agg_bezier_arc.cpp @@ -0,0 +1,261 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_bezier_arc.h" + + +namespace agg +{ + + // This epsilon is used to prevent us from adding degenerate curves + // (converging to a single point). + // The value isn't very critical. Function arc_to_bezier() has a limit + // of the sweep_angle. If fabs(sweep_angle) exceeds pi/2 the curve + // becomes inaccurate. But slight exceeding is quite appropriate. + //-------------------------------------------------bezier_arc_angle_epsilon + const double bezier_arc_angle_epsilon = 0.01; + + //------------------------------------------------------------arc_to_bezier + void arc_to_bezier(double cx, double cy, double rx, double ry, + double start_angle, double sweep_angle, + double* curve) + { + double x0 = cos(sweep_angle / 2.0); + double y0 = sin(sweep_angle / 2.0); + double tx = (1.0 - x0) * 4.0 / 3.0; + double ty = y0 - tx * x0 / y0; + double px[4]; + double py[4]; + px[0] = x0; + py[0] = -y0; + px[1] = x0 + tx; + py[1] = -ty; + px[2] = x0 + tx; + py[2] = ty; + px[3] = x0; + py[3] = y0; + + double sn = sin(start_angle + sweep_angle / 2.0); + double cs = cos(start_angle + sweep_angle / 2.0); + + unsigned i; + for(i = 0; i < 4; i++) + { + curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn); + curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs); + } + } + + + + //------------------------------------------------------------------------ + void bezier_arc::init(double x, double y, + double rx, double ry, + double start_angle, + double sweep_angle) + { + start_angle = fmod(start_angle, 2.0 * pi); + if(sweep_angle >= 2.0 * pi) sweep_angle = 2.0 * pi; + if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi; + + if(fabs(sweep_angle) < 1e-10) + { + m_num_vertices = 4; + m_cmd = path_cmd_line_to; + m_vertices[0] = x + rx * cos(start_angle); + m_vertices[1] = y + ry * sin(start_angle); + m_vertices[2] = x + rx * cos(start_angle + sweep_angle); + m_vertices[3] = y + ry * sin(start_angle + sweep_angle); + return; + } + + double total_sweep = 0.0; + double local_sweep = 0.0; + double prev_sweep; + m_num_vertices = 2; + m_cmd = path_cmd_curve4; + bool done = false; + do + { + if(sweep_angle < 0.0) + { + prev_sweep = total_sweep; + local_sweep = -pi * 0.5; + total_sweep -= pi * 0.5; + if(total_sweep <= sweep_angle + bezier_arc_angle_epsilon) + { + local_sweep = sweep_angle - prev_sweep; + done = true; + } + } + else + { + prev_sweep = total_sweep; + local_sweep = pi * 0.5; + total_sweep += pi * 0.5; + if(total_sweep >= sweep_angle - bezier_arc_angle_epsilon) + { + local_sweep = sweep_angle - prev_sweep; + done = true; + } + } + + arc_to_bezier(x, y, rx, ry, + start_angle, + local_sweep, + m_vertices + m_num_vertices - 2); + + m_num_vertices += 6; + start_angle += local_sweep; + } + while(!done && m_num_vertices < 26); + } + + + + + //-------------------------------------------------------------------- + void bezier_arc_svg::init(double x0, double y0, + double rx, double ry, + double angle, + bool large_arc_flag, + bool sweep_flag, + double x2, double y2) + { + m_radii_ok = true; + + if(rx < 0.0) rx = -rx; + if(ry < 0.0) ry = -rx; + + // Calculate the middle point between + // the current and the final points + //------------------------ + double dx2 = (x0 - x2) / 2.0; + double dy2 = (y0 - y2) / 2.0; + + double cos_a = cos(angle); + double sin_a = sin(angle); + + // Calculate (x1, y1) + //------------------------ + double x1 = cos_a * dx2 + sin_a * dy2; + double y1 = -sin_a * dx2 + cos_a * dy2; + + // Ensure radii are large enough + //------------------------ + double prx = rx * rx; + double pry = ry * ry; + double px1 = x1 * x1; + double py1 = y1 * y1; + + // Check that radii are large enough + //------------------------ + double radii_check = px1/prx + py1/pry; + if(radii_check > 1.0) + { + rx = sqrt(radii_check) * rx; + ry = sqrt(radii_check) * ry; + prx = rx * rx; + pry = ry * ry; + if(radii_check > 10.0) m_radii_ok = false; + } + + // Calculate (cx1, cy1) + //------------------------ + double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0; + double sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1); + double coef = sign * sqrt((sq < 0) ? 0 : sq); + double cx1 = coef * ((rx * y1) / ry); + double cy1 = coef * -((ry * x1) / rx); + + // + // Calculate (cx, cy) from (cx1, cy1) + //------------------------ + double sx2 = (x0 + x2) / 2.0; + double sy2 = (y0 + y2) / 2.0; + double cx = sx2 + (cos_a * cx1 - sin_a * cy1); + double cy = sy2 + (sin_a * cx1 + cos_a * cy1); + + // Calculate the start_angle (angle1) and the sweep_angle (dangle) + //------------------------ + double ux = (x1 - cx1) / rx; + double uy = (y1 - cy1) / ry; + double vx = (-x1 - cx1) / rx; + double vy = (-y1 - cy1) / ry; + double p, n; + + // Calculate the angle start + //------------------------ + n = sqrt(ux*ux + uy*uy); + p = ux; // (1 * ux) + (0 * uy) + sign = (uy < 0) ? -1.0 : 1.0; + double v = p / n; + if(v < -1.0) v = -1.0; + if(v > 1.0) v = 1.0; + double start_angle = sign * acos(v); + + // Calculate the sweep angle + //------------------------ + n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)); + p = ux * vx + uy * vy; + sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0; + v = p / n; + if(v < -1.0) v = -1.0; + if(v > 1.0) v = 1.0; + double sweep_angle = sign * acos(v); + if(!sweep_flag && sweep_angle > 0) + { + sweep_angle -= pi * 2.0; + } + else + if (sweep_flag && sweep_angle < 0) + { + sweep_angle += pi * 2.0; + } + + // We can now build and transform the resulting arc + //------------------------ + m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle); + trans_affine mtx = trans_affine_rotation(angle); + mtx *= trans_affine_translation(cx, cy); + + for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2) + { + mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1); + } + + // We must make sure that the starting and ending points + // exactly coincide with the initial (x0,y0) and (x2,y2) + m_arc.vertices()[0] = x0; + m_arc.vertices()[1] = y0; + if(m_arc.num_vertices() > 2) + { + m_arc.vertices()[m_arc.num_vertices() - 2] = x2; + m_arc.vertices()[m_arc.num_vertices() - 1] = y2; + } + } + + +} diff --git a/desmume/src/windows/agg/src/agg_bspline.cpp b/desmume/src/windows/agg/src/agg_bspline.cpp new file mode 100644 index 000000000..85bdfa969 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_bspline.cpp @@ -0,0 +1,289 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_bspline.h" + +namespace agg +{ + //------------------------------------------------------------------------ + bspline::bspline() : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + } + + //------------------------------------------------------------------------ + bspline::bspline(int num) : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + init(num); + } + + //------------------------------------------------------------------------ + bspline::bspline(int num, const double* x, const double* y) : + m_max(0), + m_num(0), + m_x(0), + m_y(0), + m_last_idx(-1) + { + init(num, x, y); + } + + + //------------------------------------------------------------------------ + void bspline::init(int max) + { + if(max > 2 && max > m_max) + { + m_am.resize(max * 3); + m_max = max; + m_x = &m_am[m_max]; + m_y = &m_am[m_max * 2]; + } + m_num = 0; + m_last_idx = -1; + } + + + //------------------------------------------------------------------------ + void bspline::add_point(double x, double y) + { + if(m_num < m_max) + { + m_x[m_num] = x; + m_y[m_num] = y; + ++m_num; + } + } + + + //------------------------------------------------------------------------ + void bspline::prepare() + { + if(m_num > 2) + { + int i, k, n1; + double* temp; + double* r; + double* s; + double h, p, d, f, e; + + for(k = 0; k < m_num; k++) + { + m_am[k] = 0.0; + } + + n1 = 3 * m_num; + + pod_array al(n1); + temp = &al[0]; + + for(k = 0; k < n1; k++) + { + temp[k] = 0.0; + } + + r = temp + m_num; + s = temp + m_num * 2; + + n1 = m_num - 1; + d = m_x[1] - m_x[0]; + e = (m_y[1] - m_y[0]) / d; + + for(k = 1; k < n1; k++) + { + h = d; + d = m_x[k + 1] - m_x[k]; + f = e; + e = (m_y[k + 1] - m_y[k]) / d; + al[k] = d / (d + h); + r[k] = 1.0 - al[k]; + s[k] = 6.0 * (e - f) / (h + d); + } + + for(k = 1; k < n1; k++) + { + p = 1.0 / (r[k] * al[k - 1] + 2.0); + al[k] *= -p; + s[k] = (s[k] - r[k] * s[k - 1]) * p; + } + + m_am[n1] = 0.0; + al[n1 - 1] = s[n1 - 1]; + m_am[n1 - 1] = al[n1 - 1]; + + for(k = n1 - 2, i = 0; i < m_num - 2; i++, k--) + { + al[k] = al[k] * al[k + 1] + s[k]; + m_am[k] = al[k]; + } + } + m_last_idx = -1; + } + + + + //------------------------------------------------------------------------ + void bspline::init(int num, const double* x, const double* y) + { + if(num > 2) + { + init(num); + int i; + for(i = 0; i < num; i++) + { + add_point(*x++, *y++); + } + prepare(); + } + m_last_idx = -1; + } + + + //------------------------------------------------------------------------ + void bspline::bsearch(int n, const double *x, double x0, int *i) + { + int j = n - 1; + int k; + + for(*i = 0; (j - *i) > 1; ) + { + if(x0 < x[k = (*i + j) >> 1]) j = k; + else *i = k; + } + } + + + + //------------------------------------------------------------------------ + double bspline::interpolation(double x, int i) const + { + int j = i + 1; + double d = m_x[i] - m_x[j]; + double h = x - m_x[j]; + double r = m_x[i] - x; + double p = d * d / 6.0; + return (m_am[j] * r * r * r + m_am[i] * h * h * h) / 6.0 / d + + ((m_y[j] - m_am[j] * p) * r + (m_y[i] - m_am[i] * p) * h) / d; + } + + + //------------------------------------------------------------------------ + double bspline::extrapolation_left(double x) const + { + double d = m_x[1] - m_x[0]; + return (-d * m_am[1] / 6 + (m_y[1] - m_y[0]) / d) * + (x - m_x[0]) + + m_y[0]; + } + + //------------------------------------------------------------------------ + double bspline::extrapolation_right(double x) const + { + double d = m_x[m_num - 1] - m_x[m_num - 2]; + return (d * m_am[m_num - 2] / 6 + (m_y[m_num - 1] - m_y[m_num - 2]) / d) * + (x - m_x[m_num - 1]) + + m_y[m_num - 1]; + } + + //------------------------------------------------------------------------ + double bspline::get(double x) const + { + if(m_num > 2) + { + int i; + + // Extrapolation on the left + if(x < m_x[0]) return extrapolation_left(x); + + // Extrapolation on the right + if(x >= m_x[m_num - 1]) return extrapolation_right(x); + + // Interpolation + bsearch(m_num, m_x, x, &i); + return interpolation(x, i); + } + return 0.0; + } + + + //------------------------------------------------------------------------ + double bspline::get_stateful(double x) const + { + if(m_num > 2) + { + // Extrapolation on the left + if(x < m_x[0]) return extrapolation_left(x); + + // Extrapolation on the right + if(x >= m_x[m_num - 1]) return extrapolation_right(x); + + if(m_last_idx >= 0) + { + // Check if x is not in current range + if(x < m_x[m_last_idx] || x > m_x[m_last_idx + 1]) + { + // Check if x between next points (most probably) + if(m_last_idx < m_num - 2 && + x >= m_x[m_last_idx + 1] && + x <= m_x[m_last_idx + 2]) + { + ++m_last_idx; + } + else + if(m_last_idx > 0 && + x >= m_x[m_last_idx - 1] && + x <= m_x[m_last_idx]) + { + // x is between pevious points + --m_last_idx; + } + else + { + // Else perform full search + bsearch(m_num, m_x, x, &m_last_idx); + } + } + return interpolation(x, m_last_idx); + } + else + { + // Interpolation + bsearch(m_num, m_x, x, &m_last_idx); + return interpolation(x, m_last_idx); + } + } + return 0.0; + } + +} + diff --git a/desmume/src/windows/agg/src/agg_curves.cpp b/desmume/src/windows/agg/src/agg_curves.cpp new file mode 100644 index 000000000..eec67d21f --- /dev/null +++ b/desmume/src/windows/agg/src/agg_curves.cpp @@ -0,0 +1,620 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_curves.h" +#include "agg_math.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + const double curve_distance_epsilon = 1e-30; + const double curve_collinearity_epsilon = 1e-30; + const double curve_angle_tolerance_epsilon = 0.01; + enum curve_recursion_limit_e { curve_recursion_limit = 32 }; + + + + //------------------------------------------------------------------------ + void curve3_inc::approximation_scale(double s) + { + m_scale = s; + } + + //------------------------------------------------------------------------ + double curve3_inc::approximation_scale() const + { + return m_scale; + } + + //------------------------------------------------------------------------ + void curve3_inc::init(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + m_start_x = x1; + m_start_y = y1; + m_end_x = x3; + m_end_y = y3; + + double dx1 = x2 - x1; + double dy1 = y2 - y1; + double dx2 = x3 - x2; + double dy2 = y3 - y2; + + double len = sqrt(dx1 * dx1 + dy1 * dy1) + sqrt(dx2 * dx2 + dy2 * dy2); + + m_num_steps = uround(len * 0.25 * m_scale); + + if(m_num_steps < 4) + { + m_num_steps = 4; + } + + double subdivide_step = 1.0 / m_num_steps; + double subdivide_step2 = subdivide_step * subdivide_step; + + double tmpx = (x1 - x2 * 2.0 + x3) * subdivide_step2; + double tmpy = (y1 - y2 * 2.0 + y3) * subdivide_step2; + + m_saved_fx = m_fx = x1; + m_saved_fy = m_fy = y1; + + m_saved_dfx = m_dfx = tmpx + (x2 - x1) * (2.0 * subdivide_step); + m_saved_dfy = m_dfy = tmpy + (y2 - y1) * (2.0 * subdivide_step); + + m_ddfx = tmpx * 2.0; + m_ddfy = tmpy * 2.0; + + m_step = m_num_steps; + } + + //------------------------------------------------------------------------ + void curve3_inc::rewind(unsigned) + { + if(m_num_steps == 0) + { + m_step = -1; + return; + } + m_step = m_num_steps; + m_fx = m_saved_fx; + m_fy = m_saved_fy; + m_dfx = m_saved_dfx; + m_dfy = m_saved_dfy; + } + + //------------------------------------------------------------------------ + unsigned curve3_inc::vertex(double* x, double* y) + { + if(m_step < 0) return path_cmd_stop; + if(m_step == m_num_steps) + { + *x = m_start_x; + *y = m_start_y; + --m_step; + return path_cmd_move_to; + } + if(m_step == 0) + { + *x = m_end_x; + *y = m_end_y; + --m_step; + return path_cmd_line_to; + } + m_fx += m_dfx; + m_fy += m_dfy; + m_dfx += m_ddfx; + m_dfy += m_ddfy; + *x = m_fx; + *y = m_fy; + --m_step; + return path_cmd_line_to; + } + + //------------------------------------------------------------------------ + void curve3_div::init(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + m_points.remove_all(); + m_distance_tolerance_square = 0.5 / m_approximation_scale; + m_distance_tolerance_square *= m_distance_tolerance_square; + bezier(x1, y1, x2, y2, x3, y3); + m_count = 0; + } + + //------------------------------------------------------------------------ + void curve3_div::recursive_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + unsigned level) + { + if(level > curve_recursion_limit) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + double x12 = (x1 + x2) / 2; + double y12 = (y1 + y2) / 2; + double x23 = (x2 + x3) / 2; + double y23 = (y2 + y3) / 2; + double x123 = (x12 + x23) / 2; + double y123 = (y12 + y23) / 2; + + double dx = x3-x1; + double dy = y3-y1; + double d = fabs(((x2 - x3) * dy - (y2 - y3) * dx)); + double da; + + if(d > curve_collinearity_epsilon) + { + // Regular case + //----------------- + if(d * d <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.add(point_d(x123, y123)); + return; + } + + // Angle & Cusp Condition + //---------------------- + da = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); + if(da >= pi) da = 2*pi - da; + + if(da < m_angle_tolerance) + { + // Finally we can stop the recursion + //---------------------- + m_points.add(point_d(x123, y123)); + return; + } + } + } + else + { + // Collinear case + //------------------ + da = dx*dx + dy*dy; + if(da == 0) + { + d = calc_sq_distance(x1, y1, x2, y2); + } + else + { + d = ((x2 - x1)*dx + (y2 - y1)*dy) / da; + if(d > 0 && d < 1) + { + // Simple collinear case, 1---2---3 + // We can leave just two endpoints + return; + } + if(d <= 0) d = calc_sq_distance(x2, y2, x1, y1); + else if(d >= 1) d = calc_sq_distance(x2, y2, x3, y3); + else d = calc_sq_distance(x2, y2, x1 + d*dx, y1 + d*dy); + } + if(d < m_distance_tolerance_square) + { + m_points.add(point_d(x2, y2)); + return; + } + } + + // Continue subdivision + //---------------------- + recursive_bezier(x1, y1, x12, y12, x123, y123, level + 1); + recursive_bezier(x123, y123, x23, y23, x3, y3, level + 1); + } + + //------------------------------------------------------------------------ + void curve3_div::bezier(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + m_points.add(point_d(x1, y1)); + recursive_bezier(x1, y1, x2, y2, x3, y3, 0); + m_points.add(point_d(x3, y3)); + } + + + + + + //------------------------------------------------------------------------ + void curve4_inc::approximation_scale(double s) + { + m_scale = s; + } + + //------------------------------------------------------------------------ + double curve4_inc::approximation_scale() const + { + return m_scale; + } + + //------------------------------------------------------------------------ + static double MSC60_fix_ICE(double v) { return v; } + + //------------------------------------------------------------------------ + void curve4_inc::init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + m_start_x = x1; + m_start_y = y1; + m_end_x = x4; + m_end_y = y4; + + double dx1 = x2 - x1; + double dy1 = y2 - y1; + double dx2 = x3 - x2; + double dy2 = y3 - y2; + double dx3 = x4 - x3; + double dy3 = y4 - y3; + + double len = (sqrt(dx1 * dx1 + dy1 * dy1) + + sqrt(dx2 * dx2 + dy2 * dy2) + + sqrt(dx3 * dx3 + dy3 * dy3)) * 0.25 * m_scale; + +#if defined(_MSC_VER) && _MSC_VER <= 1200 + m_num_steps = uround(MSC60_fix_ICE(len)); +#else + m_num_steps = uround(len); +#endif + + if(m_num_steps < 4) + { + m_num_steps = 4; + } + + double subdivide_step = 1.0 / m_num_steps; + double subdivide_step2 = subdivide_step * subdivide_step; + double subdivide_step3 = subdivide_step * subdivide_step * subdivide_step; + + double pre1 = 3.0 * subdivide_step; + double pre2 = 3.0 * subdivide_step2; + double pre4 = 6.0 * subdivide_step2; + double pre5 = 6.0 * subdivide_step3; + + double tmp1x = x1 - x2 * 2.0 + x3; + double tmp1y = y1 - y2 * 2.0 + y3; + + double tmp2x = (x2 - x3) * 3.0 - x1 + x4; + double tmp2y = (y2 - y3) * 3.0 - y1 + y4; + + m_saved_fx = m_fx = x1; + m_saved_fy = m_fy = y1; + + m_saved_dfx = m_dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdivide_step3; + m_saved_dfy = m_dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdivide_step3; + + m_saved_ddfx = m_ddfx = tmp1x * pre4 + tmp2x * pre5; + m_saved_ddfy = m_ddfy = tmp1y * pre4 + tmp2y * pre5; + + m_dddfx = tmp2x * pre5; + m_dddfy = tmp2y * pre5; + + m_step = m_num_steps; + } + + //------------------------------------------------------------------------ + void curve4_inc::rewind(unsigned) + { + if(m_num_steps == 0) + { + m_step = -1; + return; + } + m_step = m_num_steps; + m_fx = m_saved_fx; + m_fy = m_saved_fy; + m_dfx = m_saved_dfx; + m_dfy = m_saved_dfy; + m_ddfx = m_saved_ddfx; + m_ddfy = m_saved_ddfy; + } + + //------------------------------------------------------------------------ + unsigned curve4_inc::vertex(double* x, double* y) + { + if(m_step < 0) return path_cmd_stop; + if(m_step == m_num_steps) + { + *x = m_start_x; + *y = m_start_y; + --m_step; + return path_cmd_move_to; + } + + if(m_step == 0) + { + *x = m_end_x; + *y = m_end_y; + --m_step; + return path_cmd_line_to; + } + + m_fx += m_dfx; + m_fy += m_dfy; + m_dfx += m_ddfx; + m_dfy += m_ddfy; + m_ddfx += m_dddfx; + m_ddfy += m_dddfy; + + *x = m_fx; + *y = m_fy; + --m_step; + return path_cmd_line_to; + } + + + + + //------------------------------------------------------------------------ + void curve4_div::init(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + m_points.remove_all(); + m_distance_tolerance_square = 0.5 / m_approximation_scale; + m_distance_tolerance_square *= m_distance_tolerance_square; + bezier(x1, y1, x2, y2, x3, y3, x4, y4); + m_count = 0; + } + + //------------------------------------------------------------------------ + void curve4_div::recursive_bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4, + unsigned level) + { + if(level > curve_recursion_limit) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + double x12 = (x1 + x2) / 2; + double y12 = (y1 + y2) / 2; + double x23 = (x2 + x3) / 2; + double y23 = (y2 + y3) / 2; + double x34 = (x3 + x4) / 2; + double y34 = (y3 + y4) / 2; + double x123 = (x12 + x23) / 2; + double y123 = (y12 + y23) / 2; + double x234 = (x23 + x34) / 2; + double y234 = (y23 + y34) / 2; + double x1234 = (x123 + x234) / 2; + double y1234 = (y123 + y234) / 2; + + + // Try to approximate the full cubic curve by a single straight line + //------------------ + double dx = x4-x1; + double dy = y4-y1; + + double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx)); + double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx)); + double da1, da2, k; + + switch((int(d2 > curve_collinearity_epsilon) << 1) + + int(d3 > curve_collinearity_epsilon)) + { + case 0: + // All collinear OR p1==p4 + //---------------------- + k = dx*dx + dy*dy; + if(k == 0) + { + d2 = calc_sq_distance(x1, y1, x2, y2); + d3 = calc_sq_distance(x4, y4, x3, y3); + } + else + { + k = 1 / k; + da1 = x2 - x1; + da2 = y2 - y1; + d2 = k * (da1*dx + da2*dy); + da1 = x3 - x1; + da2 = y3 - y1; + d3 = k * (da1*dx + da2*dy); + if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) + { + // Simple collinear case, 1---2---3---4 + // We can leave just two endpoints + return; + } + if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1); + else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4); + else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy); + + if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1); + else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4); + else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy); + } + if(d2 > d3) + { + if(d2 < m_distance_tolerance_square) + { + m_points.add(point_d(x2, y2)); + return; + } + } + else + { + if(d3 < m_distance_tolerance_square) + { + m_points.add(point_d(x3, y3)); + return; + } + } + break; + + case 1: + // p1,p2,p4 are collinear, p3 is significant + //---------------------- + if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.add(point_d(x23, y23)); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2)); + if(da1 >= pi) da1 = 2*pi - da1; + + if(da1 < m_angle_tolerance) + { + m_points.add(point_d(x2, y2)); + m_points.add(point_d(x3, y3)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.add(point_d(x3, y3)); + return; + } + } + } + break; + + case 2: + // p1,p3,p4 are collinear, p2 is significant + //---------------------- + if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.add(point_d(x23, y23)); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); + if(da1 >= pi) da1 = 2*pi - da1; + + if(da1 < m_angle_tolerance) + { + m_points.add(point_d(x2, y2)); + m_points.add(point_d(x3, y3)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.add(point_d(x2, y2)); + return; + } + } + } + break; + + case 3: + // Regular case + //----------------- + if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.add(point_d(x23, y23)); + return; + } + + // Angle & Cusp Condition + //---------------------- + k = atan2(y3 - y2, x3 - x2); + da1 = fabs(k - atan2(y2 - y1, x2 - x1)); + da2 = fabs(atan2(y4 - y3, x4 - x3) - k); + if(da1 >= pi) da1 = 2*pi - da1; + if(da2 >= pi) da2 = 2*pi - da2; + + if(da1 + da2 < m_angle_tolerance) + { + // Finally we can stop the recursion + //---------------------- + m_points.add(point_d(x23, y23)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.add(point_d(x2, y2)); + return; + } + + if(da2 > m_cusp_limit) + { + m_points.add(point_d(x3, y3)); + return; + } + } + } + break; + } + + // Continue subdivision + //---------------------- + recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1); + recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1); + } + + //------------------------------------------------------------------------ + void curve4_div::bezier(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + m_points.add(point_d(x1, y1)); + recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0); + m_points.add(point_d(x4, y4)); + } + +} + diff --git a/desmume/src/windows/agg/src/agg_embedded_raster_fonts.cpp b/desmume/src/windows/agg/src/agg_embedded_raster_fonts.cpp new file mode 100644 index 000000000..9320fd86a --- /dev/null +++ b/desmume/src/windows/agg/src/agg_embedded_raster_fonts.cpp @@ -0,0 +1,10435 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_embedded_raster_fonts.h" + +namespace agg +{ + + const int8u gse4x6[] = + { + 6, 0, 32, 128-32, + + 0x00,0x00,0x07,0x00,0x0e,0x00,0x15,0x00,0x1c,0x00,0x23,0x00,0x2a,0x00,0x31,0x00,0x38,0x00, + 0x3f,0x00,0x46,0x00,0x4d,0x00,0x54,0x00,0x5b,0x00,0x62,0x00,0x69,0x00,0x70,0x00,0x77,0x00, + 0x7e,0x00,0x85,0x00,0x8c,0x00,0x93,0x00,0x9a,0x00,0xa1,0x00,0xa8,0x00,0xaf,0x00,0xb6,0x00, + 0xbd,0x00,0xc4,0x00,0xcb,0x00,0xd2,0x00,0xd9,0x00,0xe0,0x00,0xe7,0x00,0xee,0x00,0xf5,0x00, + 0xfc,0x00,0x03,0x01,0x0a,0x01,0x11,0x01,0x18,0x01,0x1f,0x01,0x26,0x01,0x2d,0x01,0x34,0x01, + 0x3b,0x01,0x42,0x01,0x49,0x01,0x50,0x01,0x57,0x01,0x5e,0x01,0x65,0x01,0x6c,0x01,0x73,0x01, + 0x7a,0x01,0x81,0x01,0x88,0x01,0x8f,0x01,0x96,0x01,0x9d,0x01,0xa4,0x01,0xab,0x01,0xb2,0x01, + 0xb9,0x01,0xc0,0x01,0xc7,0x01,0xce,0x01,0xd5,0x01,0xdc,0x01,0xe3,0x01,0xea,0x01,0xf1,0x01, + 0xf8,0x01,0xff,0x01,0x06,0x02,0x0d,0x02,0x14,0x02,0x1b,0x02,0x22,0x02,0x29,0x02,0x30,0x02, + 0x37,0x02,0x3e,0x02,0x45,0x02,0x4c,0x02,0x53,0x02,0x5a,0x02,0x61,0x02,0x68,0x02,0x6f,0x02, + 0x76,0x02,0x7d,0x02,0x84,0x02,0x8b,0x02,0x92,0x02,0x99,0x02, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x40,0x40,0x40,0x00,0x40,0x00, + + 4, // 0x22 '"' + 0xa0,0xa0,0x00,0x00,0x00,0x00, + + 4, // 0x23 '#' + 0x60,0xf0,0x60,0xf0,0x60,0x00, + + 4, // 0x24 '$' + 0x40,0x60,0xc0,0x60,0xc0,0x40, + + 4, // 0x25 '%' + 0xa0,0x20,0x40,0x80,0xa0,0x00, + + 4, // 0x26 '&' + 0xe0,0xa0,0x50,0xa0,0xd0,0x00, + + 4, // 0x27 ''' + 0x40,0x40,0x00,0x00,0x00,0x00, + + 4, // 0x28 '(' + 0x20,0x40,0x40,0x40,0x20,0x00, + + 4, // 0x29 ')' + 0x40,0x20,0x20,0x20,0x40,0x00, + + 4, // 0x2a '*' + 0xa0,0x40,0xe0,0x40,0xa0,0x00, + + 4, // 0x2b '+' + 0x40,0x40,0xe0,0x40,0x40,0x00, + + 4, // 0x2c ',' + 0x00,0x00,0x00,0x40,0x40,0x80, + + 4, // 0x2d '-' + 0x00,0x00,0xe0,0x00,0x00,0x00, + + 4, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x40,0x00, + + 4, // 0x2f '/' + 0x10,0x20,0x20,0x40,0x40,0x80, + + 4, // 0x30 '0' + 0xe0,0xa0,0xa0,0xa0,0xe0,0x00, + + 4, // 0x31 '1' + 0x40,0xc0,0x40,0x40,0xe0,0x00, + + 4, // 0x32 '2' + 0xe0,0xa0,0x20,0x40,0xe0,0x00, + + 4, // 0x33 '3' + 0xe0,0x20,0x40,0x20,0xe0,0x00, + + 4, // 0x34 '4' + 0xa0,0xa0,0xe0,0x20,0x20,0x00, + + 4, // 0x35 '5' + 0xe0,0x80,0xc0,0x20,0xc0,0x00, + + 4, // 0x36 '6' + 0x40,0x80,0xe0,0xa0,0xe0,0x00, + + 4, // 0x37 '7' + 0xe0,0xa0,0x20,0x40,0x40,0x00, + + 4, // 0x38 '8' + 0xe0,0xa0,0x40,0xa0,0xe0,0x00, + + 4, // 0x39 '9' + 0xe0,0xa0,0xe0,0x20,0xc0,0x00, + + 4, // 0x3a ':' + 0x00,0x40,0x00,0x40,0x00,0x00, + + 4, // 0x3b ';' + 0x00,0x40,0x00,0x40,0x40,0x80, + + 4, // 0x3c '<' + 0x20,0x40,0x80,0x40,0x20,0x00, + + 4, // 0x3d '=' + 0x00,0xe0,0x00,0xe0,0x00,0x00, + + 4, // 0x3e '>' + 0x80,0x40,0x20,0x40,0x80,0x00, + + 4, // 0x3f '?' + 0xc0,0x20,0x40,0x00,0x40,0x00, + + 4, // 0x40 '@' + 0x40,0xa0,0xe0,0xe0,0x80,0x60, + + 4, // 0x41 'A' + 0x40,0xa0,0xe0,0xa0,0xa0,0x00, + + 4, // 0x42 'B' + 0xc0,0xa0,0xc0,0xa0,0xc0,0x00, + + 4, // 0x43 'C' + 0x60,0x80,0x80,0x80,0x60,0x00, + + 4, // 0x44 'D' + 0xc0,0xa0,0xa0,0xa0,0xc0,0x00, + + 4, // 0x45 'E' + 0xe0,0x80,0xc0,0x80,0xe0,0x00, + + 4, // 0x46 'F' + 0xe0,0x80,0xc0,0x80,0x80,0x00, + + 4, // 0x47 'G' + 0x60,0x80,0xa0,0xa0,0x40,0x00, + + 4, // 0x48 'H' + 0xa0,0xa0,0xe0,0xa0,0xa0,0x00, + + 4, // 0x49 'I' + 0xe0,0x40,0x40,0x40,0xe0,0x00, + + 4, // 0x4a 'J' + 0x20,0x20,0x20,0x20,0xa0,0x40, + + 4, // 0x4b 'K' + 0xa0,0xa0,0xc0,0xc0,0xa0,0x00, + + 4, // 0x4c 'L' + 0x80,0x80,0x80,0x80,0xe0,0x00, + + 4, // 0x4d 'M' + 0xa0,0xe0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x4e 'N' + 0x90,0xd0,0xb0,0x90,0x90,0x00, + + 4, // 0x4f 'O' + 0x40,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x50 'P' + 0xc0,0xa0,0xa0,0xc0,0x80,0x00, + + 4, // 0x51 'Q' + 0x40,0xa0,0xa0,0xa0,0x60,0x00, + + 4, // 0x52 'R' + 0xc0,0xa0,0xa0,0xc0,0xa0,0x00, + + 4, // 0x53 'S' + 0x60,0x80,0x40,0x20,0xc0,0x00, + + 4, // 0x54 'T' + 0xe0,0x40,0x40,0x40,0x40,0x00, + + 4, // 0x55 'U' + 0xa0,0xa0,0xa0,0xa0,0xe0,0x00, + + 4, // 0x56 'V' + 0xa0,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x57 'W' + 0xa0,0xa0,0xa0,0xe0,0xa0,0x00, + + 4, // 0x58 'X' + 0xa0,0xa0,0x40,0xa0,0xa0,0x00, + + 4, // 0x59 'Y' + 0xa0,0xa0,0x40,0x40,0x40,0x00, + + 4, // 0x5a 'Z' + 0xe0,0x20,0x40,0x80,0xe0,0x00, + + 4, // 0x5b '[' + 0xc0,0x80,0x80,0x80,0xc0,0x00, + + 4, // 0x5c '\' + 0x80,0x40,0x40,0x20,0x20,0x10, + + 4, // 0x5d ']' + 0xc0,0x40,0x40,0x40,0xc0,0x00, + + 4, // 0x5e '^' + 0x40,0xa0,0x00,0x00,0x00,0x00, + + 4, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0xf0, + + 4, // 0x60 '`' + 0x40,0x20,0x00,0x00,0x00,0x00, + + 4, // 0x61 'a' + 0x00,0x60,0xa0,0xa0,0x70,0x00, + + 4, // 0x62 'b' + 0x80,0x80,0xc0,0xa0,0xc0,0x00, + + 4, // 0x63 'c' + 0x00,0x60,0x80,0x80,0x60,0x00, + + 4, // 0x64 'd' + 0x20,0x20,0x60,0xa0,0x60,0x00, + + 4, // 0x65 'e' + 0x00,0x40,0xe0,0x80,0x60,0x00, + + 4, // 0x66 'f' + 0x20,0x40,0xe0,0x40,0x40,0x00, + + 4, // 0x67 'g' + 0x00,0x60,0xa0,0x60,0x20,0xc0, + + 4, // 0x68 'h' + 0x80,0x80,0xc0,0xa0,0xa0,0x00, + + 4, // 0x69 'i' + 0x40,0x00,0xc0,0x40,0xe0,0x00, + + 4, // 0x6a 'j' + 0x40,0x00,0xc0,0x40,0x40,0x80, + + 4, // 0x6b 'k' + 0x80,0x80,0xa0,0xc0,0xa0,0x00, + + 4, // 0x6c 'l' + 0xc0,0x40,0x40,0x40,0xe0,0x00, + + 4, // 0x6d 'm' + 0x00,0xa0,0xf0,0xf0,0x90,0x00, + + 4, // 0x6e 'n' + 0x00,0xc0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x6f 'o' + 0x00,0x40,0xa0,0xa0,0x40,0x00, + + 4, // 0x70 'p' + 0x00,0xc0,0xa0,0xc0,0x80,0x80, + + 4, // 0x71 'q' + 0x00,0x60,0xa0,0x60,0x20,0x20, + + 4, // 0x72 'r' + 0x00,0xa0,0x50,0x40,0x40,0x00, + + 4, // 0x73 's' + 0x00,0x60,0xc0,0x20,0xc0,0x00, + + 4, // 0x74 't' + 0x40,0x40,0xe0,0x40,0x60,0x00, + + 4, // 0x75 'u' + 0x00,0xa0,0xa0,0xa0,0x60,0x00, + + 4, // 0x76 'v' + 0x00,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x77 'w' + 0x00,0xa0,0xa0,0xe0,0xa0,0x00, + + 4, // 0x78 'x' + 0x00,0xa0,0x40,0xa0,0xa0,0x00, + + 4, // 0x79 'y' + 0x00,0xa0,0xa0,0x60,0x20,0xc0, + + 4, // 0x7a 'z' + 0x00,0xe0,0x40,0x80,0xe0,0x00, + + 4, // 0x7b '{' + 0x30,0x20,0xc0,0x20,0x30,0x00, + + 4, // 0x7c '|' + 0x40,0x40,0x00,0x40,0x40,0x40, + + 4, // 0x7d '}' + 0xc0,0x40,0x30,0x40,0xc0,0x00, + + 4, // 0x7e '~' + 0x50,0xa0,0x00,0x00,0x00,0x00, + + 4, // 0x7f '' + 0x00,0x60,0x90,0xf0,0x00,0x00, + 0 + }; + + const int8u gse4x8[] = + { + 8, 0, 32, 128-32, + + 0x00,0x00,0x09,0x00,0x12,0x00,0x1b,0x00,0x24,0x00,0x2d,0x00,0x36,0x00,0x3f,0x00,0x48,0x00, + 0x51,0x00,0x5a,0x00,0x63,0x00,0x6c,0x00,0x75,0x00,0x7e,0x00,0x87,0x00,0x90,0x00,0x99,0x00, + 0xa2,0x00,0xab,0x00,0xb4,0x00,0xbd,0x00,0xc6,0x00,0xcf,0x00,0xd8,0x00,0xe1,0x00,0xea,0x00, + 0xf3,0x00,0xfc,0x00,0x05,0x01,0x0e,0x01,0x17,0x01,0x20,0x01,0x29,0x01,0x32,0x01,0x3b,0x01, + 0x44,0x01,0x4d,0x01,0x56,0x01,0x5f,0x01,0x68,0x01,0x71,0x01,0x7a,0x01,0x83,0x01,0x8c,0x01, + 0x95,0x01,0x9e,0x01,0xa7,0x01,0xb0,0x01,0xb9,0x01,0xc2,0x01,0xcb,0x01,0xd4,0x01,0xdd,0x01, + 0xe6,0x01,0xef,0x01,0xf8,0x01,0x01,0x02,0x0a,0x02,0x13,0x02,0x1c,0x02,0x25,0x02,0x2e,0x02, + 0x37,0x02,0x40,0x02,0x49,0x02,0x52,0x02,0x5b,0x02,0x64,0x02,0x6d,0x02,0x76,0x02,0x7f,0x02, + 0x88,0x02,0x91,0x02,0x9a,0x02,0xa3,0x02,0xac,0x02,0xb5,0x02,0xbe,0x02,0xc7,0x02,0xd0,0x02, + 0xd9,0x02,0xe2,0x02,0xeb,0x02,0xf4,0x02,0xfd,0x02,0x06,0x03,0x0f,0x03,0x18,0x03,0x21,0x03, + 0x2a,0x03,0x33,0x03,0x3c,0x03,0x45,0x03,0x4e,0x03,0x57,0x03, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x40,0x40,0x40,0x40,0x00,0x40,0x00, + + 4, // 0x22 '"' + 0x00,0xa0,0xa0,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x23 '#' + 0x60,0x60,0xf0,0x60,0x60,0xf0,0x60,0x60, + + 4, // 0x24 '$' + 0x40,0x60,0xc0,0xc0,0x60,0x60,0xc0,0x40, + + 4, // 0x25 '%' + 0x00,0xa0,0x20,0x40,0x40,0x80,0xa0,0x00, + + 4, // 0x26 '&' + 0x00,0x40,0xa0,0xa0,0x40,0xb0,0xa0,0x70, + + 4, // 0x27 ''' + 0x00,0x40,0x40,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x28 '(' + 0x20,0x40,0x80,0x80,0x80,0x80,0x40,0x20, + + 4, // 0x29 ')' + 0x80,0x40,0x20,0x20,0x20,0x20,0x40,0x80, + + 4, // 0x2a '*' + 0x00,0xa0,0x40,0xe0,0x40,0xa0,0x00,0x00, + + 4, // 0x2b '+' + 0x00,0x40,0x40,0xe0,0x40,0x40,0x00,0x00, + + 4, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80, + + 4, // 0x2d '-' + 0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00, + + 4, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, + + 4, // 0x2f '/' + 0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80, + + 4, // 0x30 '0' + 0x00,0xe0,0xa0,0xa0,0xa0,0xa0,0xe0,0x00, + + 4, // 0x31 '1' + 0x00,0x40,0xc0,0x40,0x40,0x40,0xe0,0x00, + + 4, // 0x32 '2' + 0x00,0xe0,0xa0,0x20,0x40,0x80,0xe0,0x00, + + 4, // 0x33 '3' + 0x00,0xe0,0x20,0x40,0x20,0x20,0xe0,0x00, + + 4, // 0x34 '4' + 0x00,0x60,0xa0,0xa0,0xf0,0x20,0x20,0x00, + + 4, // 0x35 '5' + 0x00,0xe0,0x80,0xc0,0x20,0x20,0xc0,0x00, + + 4, // 0x36 '6' + 0x00,0x40,0x80,0xe0,0xa0,0xa0,0xe0,0x00, + + 4, // 0x37 '7' + 0x00,0xe0,0xa0,0x20,0x40,0x40,0x40,0x00, + + 4, // 0x38 '8' + 0x00,0xe0,0xa0,0x40,0xa0,0xa0,0xe0,0x00, + + 4, // 0x39 '9' + 0x00,0xe0,0xa0,0xe0,0x20,0x20,0x40,0x00, + + 4, // 0x3a ':' + 0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00, + + 4, // 0x3b ';' + 0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x80, + + 4, // 0x3c '<' + 0x00,0x20,0x40,0x80,0x40,0x20,0x00,0x00, + + 4, // 0x3d '=' + 0x00,0x00,0xe0,0x00,0xe0,0x00,0x00,0x00, + + 4, // 0x3e '>' + 0x00,0x80,0x40,0x20,0x40,0x80,0x00,0x00, + + 4, // 0x3f '?' + 0x00,0x40,0xa0,0x20,0x40,0x00,0x40,0x00, + + 4, // 0x40 '@' + 0x00,0x40,0xa0,0xe0,0xe0,0x80,0x60,0x00, + + 4, // 0x41 'A' + 0x00,0x40,0xa0,0xa0,0xe0,0xa0,0xa0,0x00, + + 4, // 0x42 'B' + 0x00,0xc0,0xa0,0xc0,0xa0,0xa0,0xc0,0x00, + + 4, // 0x43 'C' + 0x00,0x40,0xa0,0x80,0x80,0xa0,0x40,0x00, + + 4, // 0x44 'D' + 0x00,0xc0,0xa0,0xa0,0xa0,0xa0,0xc0,0x00, + + 4, // 0x45 'E' + 0x00,0xe0,0x80,0xc0,0x80,0x80,0xe0,0x00, + + 4, // 0x46 'F' + 0x00,0xe0,0x80,0xc0,0x80,0x80,0x80,0x00, + + 4, // 0x47 'G' + 0x00,0x60,0x80,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x48 'H' + 0x00,0xa0,0xa0,0xe0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x49 'I' + 0x00,0xe0,0x40,0x40,0x40,0x40,0xe0,0x00, + + 4, // 0x4a 'J' + 0x00,0x20,0x20,0x20,0x20,0xa0,0x40,0x00, + + 4, // 0x4b 'K' + 0x00,0xa0,0xa0,0xc0,0xc0,0xa0,0xa0,0x00, + + 4, // 0x4c 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0xe0,0x00, + + 4, // 0x4d 'M' + 0x00,0xa0,0xe0,0xa0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x4e 'N' + 0x00,0x90,0x90,0xd0,0xb0,0x90,0x90,0x00, + + 4, // 0x4f 'O' + 0x00,0x40,0xa0,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x50 'P' + 0x00,0xc0,0xa0,0xa0,0xc0,0x80,0x80,0x00, + + 4, // 0x51 'Q' + 0x00,0x40,0xa0,0xa0,0xa0,0xa0,0x60,0x00, + + 4, // 0x52 'R' + 0x00,0xc0,0xa0,0xa0,0xc0,0xc0,0xa0,0x00, + + 4, // 0x53 'S' + 0x00,0x60,0x80,0x40,0x20,0x20,0xc0,0x00, + + 4, // 0x54 'T' + 0x00,0xe0,0x40,0x40,0x40,0x40,0x40,0x00, + + 4, // 0x55 'U' + 0x00,0xa0,0xa0,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x56 'V' + 0x00,0xa0,0xa0,0xa0,0xa0,0x40,0x40,0x00, + + 4, // 0x57 'W' + 0x00,0xa0,0xa0,0xa0,0xa0,0xe0,0xa0,0x00, + + 4, // 0x58 'X' + 0x00,0xa0,0xa0,0x40,0xa0,0xa0,0xa0,0x00, + + 4, // 0x59 'Y' + 0x00,0xa0,0xa0,0x40,0x40,0x40,0x40,0x00, + + 4, // 0x5a 'Z' + 0x00,0xe0,0x20,0x40,0x40,0x80,0xe0,0x00, + + 4, // 0x5b '[' + 0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0xc0, + + 4, // 0x5c '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10, + + 4, // 0x5d ']' + 0xc0,0x40,0x40,0x40,0x40,0x40,0x40,0xc0, + + 4, // 0x5e '^' + 0x00,0x40,0xa0,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0, + + 4, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x61 'a' + 0x00,0x00,0x60,0xa0,0xa0,0xa0,0x70,0x00, + + 4, // 0x62 'b' + 0x00,0x80,0x80,0xc0,0xa0,0xa0,0xc0,0x00, + + 4, // 0x63 'c' + 0x00,0x00,0x40,0xa0,0x80,0xa0,0x40,0x00, + + 4, // 0x64 'd' + 0x00,0x20,0x20,0x60,0xa0,0xa0,0x60,0x00, + + 4, // 0x65 'e' + 0x00,0x00,0x40,0xa0,0xe0,0x80,0x60,0x00, + + 4, // 0x66 'f' + 0x00,0x20,0x40,0x40,0xe0,0x40,0x40,0x00, + + 4, // 0x67 'g' + 0x00,0x00,0x60,0xa0,0xa0,0x60,0x20,0xc0, + + 4, // 0x68 'h' + 0x00,0x80,0x80,0xc0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x69 'i' + 0x00,0x40,0x00,0xc0,0x40,0x40,0xe0,0x00, + + 4, // 0x6a 'j' + 0x00,0x40,0x00,0xc0,0x40,0x40,0x40,0x80, + + 4, // 0x6b 'k' + 0x00,0x80,0x80,0xa0,0xc0,0xc0,0xa0,0x00, + + 4, // 0x6c 'l' + 0x00,0xc0,0x40,0x40,0x40,0x40,0xe0,0x00, + + 4, // 0x6d 'm' + 0x00,0x00,0xa0,0xf0,0xf0,0xf0,0x90,0x00, + + 4, // 0x6e 'n' + 0x00,0x00,0xc0,0xa0,0xa0,0xa0,0xa0,0x00, + + 4, // 0x6f 'o' + 0x00,0x00,0x40,0xa0,0xa0,0xa0,0x40,0x00, + + 4, // 0x70 'p' + 0x00,0x00,0xc0,0xa0,0xa0,0xc0,0x80,0x80, + + 4, // 0x71 'q' + 0x00,0x00,0x60,0xa0,0xa0,0x60,0x20,0x20, + + 4, // 0x72 'r' + 0x00,0x00,0xa0,0x50,0x40,0x40,0x40,0x00, + + 4, // 0x73 's' + 0x00,0x00,0x60,0x80,0x40,0x20,0xc0,0x00, + + 4, // 0x74 't' + 0x00,0x40,0x40,0xe0,0x40,0x40,0x20,0x00, + + 4, // 0x75 'u' + 0x00,0x00,0xa0,0xa0,0xa0,0xa0,0x60,0x00, + + 4, // 0x76 'v' + 0x00,0x00,0xa0,0xa0,0xa0,0x40,0x40,0x00, + + 4, // 0x77 'w' + 0x00,0x00,0xa0,0xa0,0xa0,0xe0,0xa0,0x00, + + 4, // 0x78 'x' + 0x00,0x00,0xa0,0xa0,0x40,0xa0,0xa0,0x00, + + 4, // 0x79 'y' + 0x00,0x00,0xa0,0xa0,0xa0,0x60,0x20,0xc0, + + 4, // 0x7a 'z' + 0x00,0x00,0xe0,0x20,0x40,0x80,0xe0,0x00, + + 4, // 0x7b '{' + 0x10,0x20,0x20,0xc0,0x20,0x20,0x10,0x00, + + 4, // 0x7c '|' + 0x00,0x40,0x40,0x40,0x00,0x40,0x40,0x40, + + 4, // 0x7d '}' + 0x80,0x40,0x40,0x30,0x40,0x40,0x80,0x00, + + 4, // 0x7e '~' + 0x00,0x50,0xa0,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x7f '' + 0x00,0x00,0x00,0x60,0x90,0xf0,0x00,0x00, + 0 + }; + + const int8u gse5x7[] = + { + 7, 0, 32, 128-32, + + 0x00,0x00,0x08,0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x38,0x00,0x40,0x00, + 0x48,0x00,0x50,0x00,0x58,0x00,0x60,0x00,0x68,0x00,0x70,0x00,0x78,0x00,0x80,0x00,0x88,0x00, + 0x90,0x00,0x98,0x00,0xa0,0x00,0xa8,0x00,0xb0,0x00,0xb8,0x00,0xc0,0x00,0xc8,0x00,0xd0,0x00, + 0xd8,0x00,0xe0,0x00,0xe8,0x00,0xf0,0x00,0xf8,0x00,0x00,0x01,0x08,0x01,0x10,0x01,0x18,0x01, + 0x20,0x01,0x28,0x01,0x30,0x01,0x38,0x01,0x40,0x01,0x48,0x01,0x50,0x01,0x58,0x01,0x60,0x01, + 0x68,0x01,0x70,0x01,0x78,0x01,0x80,0x01,0x88,0x01,0x90,0x01,0x98,0x01,0xa0,0x01,0xa8,0x01, + 0xb0,0x01,0xb8,0x01,0xc0,0x01,0xc8,0x01,0xd0,0x01,0xd8,0x01,0xe0,0x01,0xe8,0x01,0xf0,0x01, + 0xf8,0x01,0x00,0x02,0x08,0x02,0x10,0x02,0x18,0x02,0x20,0x02,0x28,0x02,0x30,0x02,0x38,0x02, + 0x40,0x02,0x48,0x02,0x50,0x02,0x58,0x02,0x60,0x02,0x68,0x02,0x70,0x02,0x78,0x02,0x80,0x02, + 0x88,0x02,0x90,0x02,0x98,0x02,0xa0,0x02,0xa8,0x02,0xb0,0x02,0xb8,0x02,0xc0,0x02,0xc8,0x02, + 0xd0,0x02,0xd8,0x02,0xe0,0x02,0xe8,0x02,0xf0,0x02,0xf8,0x02, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x00,0x20,0x00, + + 5, // 0x22 '"' + 0x00,0x50,0x50,0x00,0x00,0x00,0x00, + + 5, // 0x23 '#' + 0x00,0x50,0xf8,0x50,0xf8,0x50,0x00, + + 5, // 0x24 '$' + 0x20,0x78,0xa0,0x70,0x28,0xf0,0x20, + + 5, // 0x25 '%' + 0x00,0x88,0x10,0x20,0x40,0x88,0x00, + + 5, // 0x26 '&' + 0x00,0x40,0xa0,0x68,0x90,0x68,0x00, + + 5, // 0x27 ''' + 0x00,0x20,0x20,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x40,0x40,0x40,0x20,0x10, + + 5, // 0x29 ')' + 0x80,0x40,0x20,0x20,0x20,0x40,0x80, + + 5, // 0x2a '*' + 0x00,0x20,0xa8,0x70,0xa8,0x20,0x00, + + 5, // 0x2b '+' + 0x00,0x20,0x20,0xf8,0x20,0x20,0x00, + + 5, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x20,0x20,0x40, + + 5, // 0x2d '-' + 0x00,0x00,0x00,0xf0,0x00,0x00,0x00, + + 5, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x40,0x00, + + 5, // 0x2f '/' + 0x00,0x08,0x10,0x20,0x40,0x80,0x00, + + 5, // 0x30 '0' + 0x00,0x60,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x70,0x00, + + 5, // 0x32 '2' + 0x00,0x60,0x90,0x20,0x40,0xf0,0x00, + + 5, // 0x33 '3' + 0x00,0xf0,0x20,0x60,0x10,0xe0,0x00, + + 5, // 0x34 '4' + 0x00,0x30,0x50,0x90,0xf0,0x10,0x00, + + 5, // 0x35 '5' + 0x00,0xf0,0x80,0xe0,0x10,0xe0,0x00, + + 5, // 0x36 '6' + 0x00,0x60,0x80,0xe0,0x90,0x60,0x00, + + 5, // 0x37 '7' + 0x00,0xf0,0x90,0x20,0x40,0x40,0x00, + + 5, // 0x38 '8' + 0x00,0x60,0x90,0x60,0x90,0x60,0x00, + + 5, // 0x39 '9' + 0x00,0x60,0x90,0x70,0x10,0x60,0x00, + + 5, // 0x3a ':' + 0x00,0x00,0x20,0x00,0x20,0x00,0x00, + + 5, // 0x3b ';' + 0x00,0x00,0x20,0x00,0x20,0x20,0x40, + + 5, // 0x3c '<' + 0x00,0x10,0x20,0x40,0x20,0x10,0x00, + + 5, // 0x3d '=' + 0x00,0x00,0xf0,0x00,0xf0,0x00,0x00, + + 5, // 0x3e '>' + 0x00,0x80,0x40,0x20,0x40,0x80,0x00, + + 5, // 0x3f '?' + 0x00,0x60,0x90,0x20,0x00,0x20,0x00, + + 5, // 0x40 '@' + 0x00,0x60,0x90,0xb0,0x80,0x70,0x00, + + 5, // 0x41 'A' + 0x00,0x60,0x90,0xf0,0x90,0x90,0x00, + + 5, // 0x42 'B' + 0x00,0xe0,0x90,0xe0,0x90,0xe0,0x00, + + 5, // 0x43 'C' + 0x00,0x60,0x90,0x80,0x90,0x60,0x00, + + 5, // 0x44 'D' + 0x00,0xe0,0x90,0x90,0x90,0xe0,0x00, + + 5, // 0x45 'E' + 0x00,0xf0,0x80,0xe0,0x80,0xf0,0x00, + + 5, // 0x46 'F' + 0x00,0xf0,0x80,0xe0,0x80,0x80,0x00, + + 5, // 0x47 'G' + 0x00,0x70,0x80,0xb0,0x90,0x60,0x00, + + 5, // 0x48 'H' + 0x00,0x90,0x90,0xf0,0x90,0x90,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x4a 'J' + 0x00,0x70,0x20,0x20,0xa0,0x40,0x00, + + 5, // 0x4b 'K' + 0x00,0x90,0xa0,0xc0,0xa0,0x90,0x00, + + 5, // 0x4c 'L' + 0x00,0x80,0x80,0x80,0x80,0xf0,0x00, + + 5, // 0x4d 'M' + 0x00,0x90,0xf0,0x90,0x90,0x90,0x00, + + 5, // 0x4e 'N' + 0x00,0x90,0xd0,0xb0,0x90,0x90,0x00, + + 5, // 0x4f 'O' + 0x00,0x60,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x50 'P' + 0x00,0xe0,0x90,0xe0,0x80,0x80,0x00, + + 5, // 0x51 'Q' + 0x00,0x60,0x90,0x90,0xa0,0x50,0x00, + + 5, // 0x52 'R' + 0x00,0xe0,0x90,0xe0,0xa0,0x90,0x00, + + 5, // 0x53 'S' + 0x00,0x70,0x80,0x60,0x10,0xe0,0x00, + + 5, // 0x54 'T' + 0x00,0x70,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x55 'U' + 0x00,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x56 'V' + 0x00,0x50,0x50,0x50,0x20,0x20,0x00, + + 5, // 0x57 'W' + 0x00,0x90,0x90,0x90,0xf0,0x90,0x00, + + 5, // 0x58 'X' + 0x00,0x90,0x90,0x60,0x90,0x90,0x00, + + 5, // 0x59 'Y' + 0x00,0x50,0x50,0x20,0x20,0x20,0x00, + + 5, // 0x5a 'Z' + 0x00,0xf0,0x10,0x20,0x40,0xf0,0x00, + + 5, // 0x5b '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x70, + + 5, // 0x5c '\' + 0x00,0x80,0x40,0x20,0x10,0x08,0x00, + + 5, // 0x5d ']' + 0xe0,0x20,0x20,0x20,0x20,0x20,0xe0, + + 5, // 0x5e '^' + 0x00,0x20,0x50,0x00,0x00,0x00,0x00, + + 5, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0xf8,0x00, + + 5, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00, + + 5, // 0x61 'a' + 0x00,0x00,0x60,0xa0,0xa0,0x50,0x00, + + 5, // 0x62 'b' + 0x00,0x80,0x80,0xe0,0x90,0xe0,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x70,0x80,0x80,0x70,0x00, + + 5, // 0x64 'd' + 0x00,0x10,0x10,0x70,0x90,0x70,0x00, + + 5, // 0x65 'e' + 0x00,0x00,0x60,0xf0,0x80,0x70,0x00, + + 5, // 0x66 'f' + 0x00,0x30,0x40,0xe0,0x40,0x40,0x00, + + 5, // 0x67 'g' + 0x00,0x00,0x70,0x90,0x70,0x10,0x60, + + 5, // 0x68 'h' + 0x00,0x80,0x80,0xe0,0x90,0x90,0x00, + + 5, // 0x69 'i' + 0x20,0x00,0x60,0x20,0x20,0x70,0x00, + + 5, // 0x6a 'j' + 0x20,0x00,0x60,0x20,0x20,0xa0,0x40, + + 5, // 0x6b 'k' + 0x80,0x80,0x90,0xa0,0xe0,0x90,0x00, + + 5, // 0x6c 'l' + 0x00,0x60,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x6d 'm' + 0x00,0x00,0xa0,0xf0,0xf0,0x90,0x00, + + 5, // 0x6e 'n' + 0x00,0x00,0xa0,0xd0,0x90,0x90,0x00, + + 5, // 0x6f 'o' + 0x00,0x00,0x60,0x90,0x90,0x60,0x00, + + 5, // 0x70 'p' + 0x00,0x00,0xe0,0x90,0xe0,0x80,0x80, + + 5, // 0x71 'q' + 0x00,0x00,0x70,0x90,0x70,0x10,0x10, + + 5, // 0x72 'r' + 0x00,0x00,0xe0,0x90,0x80,0x80,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x70,0xe0,0x10,0xe0,0x00, + + 5, // 0x74 't' + 0x40,0x40,0xe0,0x40,0x40,0x70,0x00, + + 5, // 0x75 'u' + 0x00,0x00,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x76 'v' + 0x00,0x00,0x50,0x50,0x50,0x20,0x00, + + 5, // 0x77 'w' + 0x00,0x00,0x90,0x90,0xf0,0x90,0x00, + + 5, // 0x78 'x' + 0x00,0x00,0x90,0x60,0x60,0x90,0x00, + + 5, // 0x79 'y' + 0x00,0x00,0x90,0x90,0x70,0x10,0x60, + + 5, // 0x7a 'z' + 0x00,0x00,0xf0,0x20,0x40,0xf0,0x00, + + 5, // 0x7b '{' + 0x10,0x20,0x20,0xc0,0x20,0x20,0x10, + + 5, // 0x7c '|' + 0x20,0x20,0x20,0x00,0x20,0x20,0x20, + + 5, // 0x7d '}' + 0x40,0x20,0x20,0x18,0x20,0x20,0x40, + + 5, // 0x7e '~' + 0x00,0x40,0xa8,0x10,0x00,0x00,0x00, + + 5, // 0x7f '' + 0x00,0x00,0x20,0x50,0x88,0xf8,0x00, + 0 + }; + + const int8u gse5x9[] = + { + 9, 0, 32, 128-32, + + 0x00,0x00,0x0a,0x00,0x14,0x00,0x1e,0x00,0x28,0x00,0x32,0x00,0x3c,0x00,0x46,0x00,0x50,0x00, + 0x5a,0x00,0x64,0x00,0x6e,0x00,0x78,0x00,0x82,0x00,0x8c,0x00,0x96,0x00,0xa0,0x00,0xaa,0x00, + 0xb4,0x00,0xbe,0x00,0xc8,0x00,0xd2,0x00,0xdc,0x00,0xe6,0x00,0xf0,0x00,0xfa,0x00,0x04,0x01, + 0x0e,0x01,0x18,0x01,0x22,0x01,0x2c,0x01,0x36,0x01,0x40,0x01,0x4a,0x01,0x54,0x01,0x5e,0x01, + 0x68,0x01,0x72,0x01,0x7c,0x01,0x86,0x01,0x90,0x01,0x9a,0x01,0xa4,0x01,0xae,0x01,0xb8,0x01, + 0xc2,0x01,0xcc,0x01,0xd6,0x01,0xe0,0x01,0xea,0x01,0xf4,0x01,0xfe,0x01,0x08,0x02,0x12,0x02, + 0x1c,0x02,0x26,0x02,0x30,0x02,0x3a,0x02,0x44,0x02,0x4e,0x02,0x58,0x02,0x62,0x02,0x6c,0x02, + 0x76,0x02,0x80,0x02,0x8a,0x02,0x94,0x02,0x9e,0x02,0xa8,0x02,0xb2,0x02,0xbc,0x02,0xc6,0x02, + 0xd0,0x02,0xda,0x02,0xe4,0x02,0xee,0x02,0xf8,0x02,0x02,0x03,0x0c,0x03,0x16,0x03,0x20,0x03, + 0x2a,0x03,0x34,0x03,0x3e,0x03,0x48,0x03,0x52,0x03,0x5c,0x03,0x66,0x03,0x70,0x03,0x7a,0x03, + 0x84,0x03,0x8e,0x03,0x98,0x03,0xa2,0x03,0xac,0x03,0xb6,0x03, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + + 5, // 0x22 '"' + 0x00,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x23 '#' + 0x00,0x50,0x50,0xf8,0x50,0xf8,0x50,0x50,0x00, + + 5, // 0x24 '$' + 0x00,0x20,0x78,0xa0,0x70,0x28,0xf0,0x20,0x00, + + 5, // 0x25 '%' + 0x00,0xc8,0xc8,0x10,0x20,0x40,0x98,0x98,0x00, + + 5, // 0x26 '&' + 0x00,0x40,0xa0,0xa0,0x40,0xa8,0x90,0x68,0x00, + + 5, // 0x27 ''' + 0x00,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x10, + + 5, // 0x29 ')' + 0x80,0x40,0x20,0x20,0x20,0x20,0x20,0x40,0x80, + + 5, // 0x2a '*' + 0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00, + + 5, // 0x2b '+' + 0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00, + + 5, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x40, + + 5, // 0x2d '-' + 0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00, + + 5, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, + + 5, // 0x2f '/' + 0x00,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80, + + 5, // 0x30 '0' + 0x00,0x60,0x90,0xb0,0xd0,0x90,0x90,0x60,0x00, + + 5, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x32 '2' + 0x00,0x60,0x90,0x10,0x20,0x40,0x80,0xf0,0x00, + + 5, // 0x33 '3' + 0x00,0xf0,0x10,0x20,0x60,0x10,0x90,0x60,0x00, + + 5, // 0x34 '4' + 0x00,0x30,0x50,0x90,0x90,0xf8,0x10,0x10,0x00, + + 5, // 0x35 '5' + 0x00,0xf0,0x80,0xe0,0x10,0x10,0x10,0xe0,0x00, + + 5, // 0x36 '6' + 0x00,0x60,0x80,0xe0,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x37 '7' + 0x00,0xf0,0x90,0x10,0x20,0x40,0x40,0x40,0x00, + + 5, // 0x38 '8' + 0x00,0x60,0x90,0x90,0x60,0x90,0x90,0x60,0x00, + + 5, // 0x39 '9' + 0x00,0x60,0x90,0x90,0x70,0x10,0x90,0x60,0x00, + + 5, // 0x3a ':' + 0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00, + + 5, // 0x3b ';' + 0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x20,0x40, + + 5, // 0x3c '<' + 0x00,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00, + + 5, // 0x3d '=' + 0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x00,0x00, + + 5, // 0x3e '>' + 0x00,0x80,0x40,0x20,0x10,0x20,0x40,0x80,0x00, + + 5, // 0x3f '?' + 0x00,0x60,0x90,0x10,0x20,0x20,0x00,0x20,0x00, + + 5, // 0x40 '@' + 0x00,0x60,0x90,0xb0,0xb0,0xb0,0x80,0x70,0x00, + + 5, // 0x41 'A' + 0x00,0x60,0x90,0x90,0xf0,0x90,0x90,0x90,0x00, + + 5, // 0x42 'B' + 0x00,0xe0,0x90,0x90,0xe0,0x90,0x90,0xe0,0x00, + + 5, // 0x43 'C' + 0x00,0x60,0x90,0x80,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x44 'D' + 0x00,0xe0,0x90,0x90,0x90,0x90,0x90,0xe0,0x00, + + 5, // 0x45 'E' + 0x00,0xf0,0x80,0x80,0xe0,0x80,0x80,0xf0,0x00, + + 5, // 0x46 'F' + 0x00,0xf0,0x80,0x80,0xe0,0x80,0x80,0x80,0x00, + + 5, // 0x47 'G' + 0x00,0x60,0x90,0x80,0xb0,0x90,0x90,0x60,0x00, + + 5, // 0x48 'H' + 0x00,0x90,0x90,0x90,0xf0,0x90,0x90,0x90,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x4a 'J' + 0x00,0x70,0x20,0x20,0x20,0x20,0xa0,0x40,0x00, + + 5, // 0x4b 'K' + 0x00,0x90,0x90,0xa0,0xc0,0xa0,0x90,0x90,0x00, + + 5, // 0x4c 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xf0,0x00, + + 5, // 0x4d 'M' + 0x00,0x90,0xf0,0x90,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x4e 'N' + 0x00,0x90,0x90,0xd0,0xb0,0x90,0x90,0x90,0x00, + + 5, // 0x4f 'O' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x50 'P' + 0x00,0xe0,0x90,0x90,0xe0,0x80,0x80,0x80,0x00, + + 5, // 0x51 'Q' + 0x00,0x60,0x90,0x90,0x90,0x90,0xa0,0x50,0x00, + + 5, // 0x52 'R' + 0x00,0xe0,0x90,0x90,0xe0,0xa0,0x90,0x90,0x00, + + 5, // 0x53 'S' + 0x00,0x60,0x90,0x80,0x60,0x10,0x90,0x60,0x00, + + 5, // 0x54 'T' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x55 'U' + 0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x56 'V' + 0x00,0x50,0x50,0x50,0x50,0x50,0x20,0x20,0x00, + + 5, // 0x57 'W' + 0x00,0x90,0x90,0x90,0x90,0x90,0xf0,0x90,0x00, + + 5, // 0x58 'X' + 0x00,0x90,0x90,0x60,0x60,0x90,0x90,0x90,0x00, + + 5, // 0x59 'Y' + 0x00,0x50,0x50,0x50,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x5a 'Z' + 0x00,0xf0,0x10,0x10,0x20,0x40,0x80,0xf0,0x00, + + 5, // 0x5b '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00, + + 5, // 0x5c '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x00, + + 5, // 0x5d ']' + 0xe0,0x20,0x20,0x20,0x20,0x20,0x20,0xe0,0x00, + + 5, // 0x5e '^' + 0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00, + + 5, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x61 'a' + 0x00,0x00,0x60,0x10,0x70,0x90,0x90,0x70,0x00, + + 5, // 0x62 'b' + 0x00,0x80,0x80,0xe0,0x90,0x90,0x90,0xe0,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x60,0x90,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x64 'd' + 0x00,0x10,0x10,0x70,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x65 'e' + 0x00,0x00,0x60,0x90,0xf0,0x80,0x80,0x70,0x00, + + 5, // 0x66 'f' + 0x00,0x30,0x40,0x40,0xe0,0x40,0x40,0x40,0x00, + + 5, // 0x67 'g' + 0x00,0x00,0x70,0x90,0x90,0x70,0x10,0x90,0x60, + + 5, // 0x68 'h' + 0x00,0x80,0x80,0xe0,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x69 'i' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x6a 'j' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0xa0,0x40, + + 5, // 0x6b 'k' + 0x00,0x80,0x80,0x90,0xa0,0xc0,0xa0,0x90,0x00, + + 5, // 0x6c 'l' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x6d 'm' + 0x00,0x00,0xa0,0xf0,0xf0,0xf0,0x90,0x90,0x00, + + 5, // 0x6e 'n' + 0x00,0x00,0xa0,0xd0,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x6f 'o' + 0x00,0x00,0x60,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x70 'p' + 0x00,0x00,0xe0,0x90,0x90,0x90,0xe0,0x80,0x80, + + 5, // 0x71 'q' + 0x00,0x00,0x70,0x90,0x90,0x90,0x70,0x10,0x10, + + 5, // 0x72 'r' + 0x00,0x00,0xe0,0x90,0x80,0x80,0x80,0x80,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x60,0x90,0x40,0x20,0x90,0x60,0x00, + + 5, // 0x74 't' + 0x00,0x40,0x40,0xe0,0x40,0x40,0x50,0x20,0x00, + + 5, // 0x75 'u' + 0x00,0x00,0x90,0x90,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x76 'v' + 0x00,0x00,0x50,0x50,0x50,0x50,0x20,0x20,0x00, + + 5, // 0x77 'w' + 0x00,0x00,0x90,0x90,0x90,0x90,0xf0,0x90,0x00, + + 5, // 0x78 'x' + 0x00,0x00,0x90,0x90,0x60,0x60,0x90,0x90,0x00, + + 5, // 0x79 'y' + 0x00,0x00,0x90,0x90,0x90,0x90,0x70,0x10,0xe0, + + 5, // 0x7a 'z' + 0x00,0x00,0xf0,0x10,0x20,0x40,0x80,0xf0,0x00, + + 5, // 0x7b '{' + 0x10,0x20,0x20,0x20,0xc0,0x20,0x20,0x20,0x10, + + 5, // 0x7c '|' + 0x00,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00, + + 5, // 0x7d '}' + 0x80,0x40,0x40,0x40,0x30,0x40,0x40,0x40,0x80, + + 5, // 0x7e '~' + 0x00,0x40,0xa8,0x10,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x7f '' + 0x00,0x00,0x00,0x20,0x50,0x88,0xf8,0x00,0x00, + 0 + }; + + const int8u gse6x12[] = + { + 12, 0, 32, 128-32, + + 0x00,0x00,0x0d,0x00,0x1a,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x4e,0x00,0x5b,0x00,0x68,0x00, + 0x75,0x00,0x82,0x00,0x8f,0x00,0x9c,0x00,0xa9,0x00,0xb6,0x00,0xc3,0x00,0xd0,0x00,0xdd,0x00, + 0xea,0x00,0xf7,0x00,0x04,0x01,0x11,0x01,0x1e,0x01,0x2b,0x01,0x38,0x01,0x45,0x01,0x52,0x01, + 0x5f,0x01,0x6c,0x01,0x79,0x01,0x86,0x01,0x93,0x01,0xa0,0x01,0xad,0x01,0xba,0x01,0xc7,0x01, + 0xd4,0x01,0xe1,0x01,0xee,0x01,0xfb,0x01,0x08,0x02,0x15,0x02,0x22,0x02,0x2f,0x02,0x3c,0x02, + 0x49,0x02,0x56,0x02,0x63,0x02,0x70,0x02,0x7d,0x02,0x8a,0x02,0x97,0x02,0xa4,0x02,0xb1,0x02, + 0xbe,0x02,0xcb,0x02,0xd8,0x02,0xe5,0x02,0xf2,0x02,0xff,0x02,0x0c,0x03,0x19,0x03,0x26,0x03, + 0x33,0x03,0x40,0x03,0x4d,0x03,0x5a,0x03,0x67,0x03,0x74,0x03,0x81,0x03,0x8e,0x03,0x9b,0x03, + 0xa8,0x03,0xb5,0x03,0xc2,0x03,0xcf,0x03,0xdc,0x03,0xe9,0x03,0xf6,0x03,0x03,0x04,0x10,0x04, + 0x1d,0x04,0x2a,0x04,0x37,0x04,0x44,0x04,0x51,0x04,0x5e,0x04,0x6b,0x04,0x78,0x04,0x85,0x04, + 0x92,0x04,0x9f,0x04,0xac,0x04,0xb9,0x04,0xc6,0x04,0xd3,0x04, + + 6, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 6, // 0x22 '"' + 0x00,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x50,0x50,0xf8,0x50,0x50,0x50,0xf8,0x50,0x50,0x00,0x00, + + 6, // 0x24 '$' + 0x00,0x20,0x70,0xa8,0xa0,0x70,0x28,0xa8,0x70,0x20,0x00,0x00, + + 6, // 0x25 '%' + 0x00,0xc8,0xd8,0x10,0x30,0x20,0x60,0x40,0xd8,0x98,0x00,0x00, + + 6, // 0x26 '&' + 0x00,0x60,0x90,0x90,0x90,0x60,0xa8,0x90,0x90,0x68,0x00,0x00, + + 6, // 0x27 ''' + 0x00,0x20,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x00,0x10,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x10,0x00,0x00, + + 6, // 0x29 ')' + 0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x40,0x00,0x00, + + 6, // 0x2a '*' + 0x00,0x00,0x00,0x50,0x20,0xf8,0x20,0x50,0x00,0x00,0x00,0x00, + + 6, // 0x2b '+' + 0x00,0x00,0x20,0x20,0x20,0xf8,0x20,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40, + + 6, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00, + + 6, // 0x2f '/' + 0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00,0x00, + + 6, // 0x30 '0' + 0x00,0x70,0x88,0x88,0x98,0xa8,0xc8,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x31 '1' + 0x00,0x20,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x32 '2' + 0x00,0x70,0x88,0x88,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x00, + + 6, // 0x33 '3' + 0x00,0xf8,0x10,0x20,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x10,0x20,0x40,0x90,0x90,0xf8,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x35 '5' + 0x00,0xf8,0x80,0x80,0xf0,0x08,0x08,0x08,0x88,0x70,0x00,0x00, + + 6, // 0x36 '6' + 0x00,0x70,0x88,0x80,0x80,0xf0,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0xf8,0x88,0x08,0x08,0x10,0x20,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x38 '8' + 0x00,0x70,0x88,0x88,0x88,0x70,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x39 '9' + 0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x08,0x88,0x70,0x00,0x00, + + 6, // 0x3a ':' + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + + 6, // 0x3b ';' + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x20,0x20,0x40, + + 6, // 0x3c '<' + 0x00,0x08,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x08,0x00,0x00, + + 6, // 0x3d '=' + 0x00,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3e '>' + 0x00,0x80,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x80,0x00,0x00, + + 6, // 0x3f '?' + 0x00,0x70,0x88,0x88,0x08,0x10,0x20,0x20,0x00,0x20,0x00,0x00, + + 6, // 0x40 '@' + 0x00,0x70,0x88,0x88,0xb8,0xb8,0xb0,0x80,0x88,0x70,0x00,0x00, + + 6, // 0x41 'A' + 0x00,0x20,0x50,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x42 'B' + 0x00,0xf0,0x88,0x88,0x88,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x70,0x88,0x88,0x80,0x80,0x80,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x44 'D' + 0x00,0xe0,0x90,0x88,0x88,0x88,0x88,0x88,0x90,0xe0,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0xf8,0x80,0x80,0x80,0xf0,0x80,0x80,0x80,0xf8,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0xf8,0x80,0x80,0x80,0xf0,0x80,0x80,0x80,0x80,0x00,0x00, + + 6, // 0x47 'G' + 0x00,0x70,0x88,0x80,0x80,0xb8,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x48 'H' + 0x00,0x88,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x4a 'J' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0x60,0x00,0x00, + + 6, // 0x4b 'K' + 0x00,0x88,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x88,0x00,0x00, + + 6, // 0x4c 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x00, + + 6, // 0x4d 'M' + 0x00,0x88,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x4e 'N' + 0x00,0x88,0x88,0xc8,0xa8,0x98,0x88,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x4f 'O' + 0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x50 'P' + 0x00,0xf0,0x88,0x88,0x88,0xf0,0x80,0x80,0x80,0x80,0x00,0x00, + + 6, // 0x51 'Q' + 0x00,0x70,0x88,0x88,0x88,0x88,0x88,0xa8,0x90,0x68,0x00,0x00, + + 6, // 0x52 'R' + 0x00,0xf0,0x88,0x88,0x88,0x88,0xf0,0xa0,0x90,0x88,0x00,0x00, + + 6, // 0x53 'S' + 0x00,0x70,0x88,0x80,0x80,0x70,0x08,0x08,0x88,0x70,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x55 'U' + 0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x56 'V' + 0x00,0x88,0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x20,0x00,0x00, + + 6, // 0x57 'W' + 0x00,0x88,0x88,0x88,0x88,0x88,0xa8,0xa8,0xd8,0x88,0x00,0x00, + + 6, // 0x58 'X' + 0x00,0x88,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x59 'Y' + 0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x5a 'Z' + 0x00,0xf8,0x08,0x08,0x10,0x20,0x40,0x80,0x80,0xf8,0x00,0x00, + + 6, // 0x5b '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00, + + 6, // 0x5c '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00,0x00, + + 6, // 0x5d ']' + 0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, + + 6, // 0x5e '^' + 0x00,0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00, + + 6, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x70,0x88,0x08,0x78,0x88,0x88,0x78,0x00,0x00, + + 6, // 0x62 'b' + 0x00,0x80,0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0xf0,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,0x00, + + 6, // 0x64 'd' + 0x00,0x08,0x08,0x08,0x78,0x88,0x88,0x88,0x88,0x78,0x00,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x70,0x88,0x88,0xf8,0x80,0x80,0x78,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x18,0x20,0x20,0xf8,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x78,0x88,0x88,0x88,0x88,0x78,0x08,0x08,0xf0, + + 6, // 0x68 'h' + 0x00,0x80,0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x69 'i' + 0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x6a 'j' + 0x00,0x10,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x90,0x60, + + 6, // 0x6b 'k' + 0x00,0x80,0x80,0x80,0x88,0x90,0xa0,0xd0,0x88,0x88,0x00,0x00, + + 6, // 0x6c 'l' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x6d 'm' + 0x00,0x00,0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x00,0x00, + + 6, // 0x6e 'n' + 0x00,0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x88,0x88,0x00,0x00, + + 6, // 0x6f 'o' + 0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0xf0,0x88,0x88,0x88,0x88,0xf0,0x80,0x80,0x80, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x78,0x88,0x88,0x88,0x88,0x78,0x08,0x08,0x08, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0xb0,0xc8,0x88,0x80,0x80,0x80,0x80,0x00,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x40,0x40,0x40,0xe0,0x40,0x40,0x40,0x48,0x30,0x00,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x78,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x50,0x20,0x20,0x00,0x00, + + 6, // 0x77 'w' + 0x00,0x00,0x00,0x88,0x88,0x88,0xa8,0xa8,0xd8,0x88,0x00,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x88,0x78,0x08,0x10,0xe0, + + 6, // 0x7a 'z' + 0x00,0x00,0x00,0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x00, + + 6, // 0x7b '{' + 0x18,0x20,0x20,0x20,0x20,0xc0,0x20,0x20,0x20,0x20,0x18,0x00, + + 6, // 0x7c '|' + 0x00,0x20,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x7d '}' + 0xc0,0x20,0x20,0x20,0x20,0x18,0x20,0x20,0x20,0x20,0xc0,0x00, + + 6, // 0x7e '~' + 0x00,0x00,0x40,0xa8,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x7f '' + 0x00,0x00,0x00,0x00,0x20,0x50,0x88,0xf8,0x00,0x00,0x00,0x00, + 0 + }; + + const int8u gse6x9[] = + { + 9, 0, 32, 128-32, + + 0x00,0x00,0x0a,0x00,0x14,0x00,0x1e,0x00,0x28,0x00,0x32,0x00,0x3c,0x00,0x46,0x00,0x50,0x00, + 0x5a,0x00,0x64,0x00,0x6e,0x00,0x78,0x00,0x82,0x00,0x8c,0x00,0x96,0x00,0xa0,0x00,0xaa,0x00, + 0xb4,0x00,0xbe,0x00,0xc8,0x00,0xd2,0x00,0xdc,0x00,0xe6,0x00,0xf0,0x00,0xfa,0x00,0x04,0x01, + 0x0e,0x01,0x18,0x01,0x22,0x01,0x2c,0x01,0x36,0x01,0x40,0x01,0x4a,0x01,0x54,0x01,0x5e,0x01, + 0x68,0x01,0x72,0x01,0x7c,0x01,0x86,0x01,0x90,0x01,0x9a,0x01,0xa4,0x01,0xae,0x01,0xb8,0x01, + 0xc2,0x01,0xcc,0x01,0xd6,0x01,0xe0,0x01,0xea,0x01,0xf4,0x01,0xfe,0x01,0x08,0x02,0x12,0x02, + 0x1c,0x02,0x26,0x02,0x30,0x02,0x3a,0x02,0x44,0x02,0x4e,0x02,0x58,0x02,0x62,0x02,0x6c,0x02, + 0x76,0x02,0x80,0x02,0x8a,0x02,0x94,0x02,0x9e,0x02,0xa8,0x02,0xb2,0x02,0xbc,0x02,0xc6,0x02, + 0xd0,0x02,0xda,0x02,0xe4,0x02,0xee,0x02,0xf8,0x02,0x02,0x03,0x0c,0x03,0x16,0x03,0x20,0x03, + 0x2a,0x03,0x34,0x03,0x3e,0x03,0x48,0x03,0x52,0x03,0x5c,0x03,0x66,0x03,0x70,0x03,0x7a,0x03, + 0x84,0x03,0x8e,0x03,0x98,0x03,0xa2,0x03,0xac,0x03,0xb6,0x03, + + 6, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + + 6, // 0x22 '"' + 0x00,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x50,0x50,0xf8,0x50,0xf8,0x50,0x50,0x00, + + 6, // 0x24 '$' + 0x00,0x70,0xa8,0xa0,0x70,0x28,0xa8,0x70,0x00, + + 6, // 0x25 '%' + 0x00,0xc8,0xc8,0x10,0x20,0x40,0x98,0x98,0x00, + + 6, // 0x26 '&' + 0x00,0x60,0x90,0x90,0x60,0xa8,0x90,0x68,0x00, + + 6, // 0x27 ''' + 0x00,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x10,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x10, + + 6, // 0x29 ')' + 0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x40, + + 6, // 0x2a '*' + 0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00, + + 6, // 0x2b '+' + 0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00, + + 6, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x40, + + 6, // 0x2d '-' + 0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00, + + 6, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, + + 6, // 0x2f '/' + 0x00,0x08,0x08,0x10,0x20,0x40,0x80,0x80,0x00, + + 6, // 0x30 '0' + 0x00,0x70,0x88,0x98,0xa8,0xc8,0x88,0x70,0x00, + + 6, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00, + + 6, // 0x32 '2' + 0x00,0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00, + + 6, // 0x33 '3' + 0x00,0xf8,0x10,0x20,0x70,0x08,0x88,0x70,0x00, + + 6, // 0x34 '4' + 0x00,0x10,0x20,0x40,0x90,0xf8,0x10,0x10,0x00, + + 6, // 0x35 '5' + 0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00, + + 6, // 0x36 '6' + 0x00,0x70,0x88,0x80,0xf0,0x88,0x88,0x70,0x00, + + 6, // 0x37 '7' + 0x00,0xf8,0x08,0x08,0x10,0x20,0x40,0x40,0x00, + + 6, // 0x38 '8' + 0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00, + + 6, // 0x39 '9' + 0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00, + + 6, // 0x3a ':' + 0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00, + + 6, // 0x3b ';' + 0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x20,0x40, + + 6, // 0x3c '<' + 0x00,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00, + + 6, // 0x3d '=' + 0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00, + + 6, // 0x3e '>' + 0x00,0x80,0x40,0x20,0x10,0x20,0x40,0x80,0x00, + + 6, // 0x3f '?' + 0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00, + + 6, // 0x40 '@' + 0x00,0x70,0x88,0x88,0xb8,0xb8,0x80,0x70,0x00, + + 6, // 0x41 'A' + 0x00,0x20,0x50,0x88,0x88,0xf8,0x88,0x88,0x00, + + 6, // 0x42 'B' + 0x00,0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00, + + 6, // 0x43 'C' + 0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, + + 6, // 0x44 'D' + 0x00,0xe0,0x90,0x88,0x88,0x88,0x90,0xe0,0x00, + + 6, // 0x45 'E' + 0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00, + + 6, // 0x46 'F' + 0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00, + + 6, // 0x47 'G' + 0x00,0x70,0x88,0x80,0xb8,0x88,0x88,0x70,0x00, + + 6, // 0x48 'H' + 0x00,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00, + + 6, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 6, // 0x4a 'J' + 0x00,0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00, + + 6, // 0x4b 'K' + 0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00, + + 6, // 0x4c 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00, + + 6, // 0x4d 'M' + 0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00, + + 6, // 0x4e 'N' + 0x00,0x88,0x88,0xc8,0xa8,0x98,0x88,0x88,0x00, + + 6, // 0x4f 'O' + 0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + + 6, // 0x50 'P' + 0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00, + + 6, // 0x51 'Q' + 0x00,0x70,0x88,0x88,0x88,0xa8,0x90,0x68,0x00, + + 6, // 0x52 'R' + 0x00,0xf0,0x88,0x88,0x88,0xf0,0x90,0x88,0x00, + + 6, // 0x53 'S' + 0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00, + + 6, // 0x54 'T' + 0x00,0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 6, // 0x55 'U' + 0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + + 6, // 0x56 'V' + 0x00,0x88,0x88,0x88,0x50,0x50,0x20,0x20,0x00, + + 6, // 0x57 'W' + 0x00,0x88,0x88,0x88,0xa8,0xa8,0xd8,0x88,0x00, + + 6, // 0x58 'X' + 0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00, + + 6, // 0x59 'Y' + 0x00,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00, + + 6, // 0x5a 'Z' + 0x00,0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00, + + 6, // 0x5b '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70, + + 6, // 0x5c '\' + 0x00,0x80,0x80,0x40,0x20,0x10,0x08,0x08,0x00, + + 6, // 0x5d ']' + 0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70, + + 6, // 0x5e '^' + 0x00,0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00, + + 6, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00, + + 6, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00, + + 6, // 0x62 'b' + 0x00,0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00, + + 6, // 0x64 'd' + 0x00,0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x78,0x00, + + 6, // 0x66 'f' + 0x00,0x18,0x20,0x20,0xf8,0x20,0x20,0x20,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70, + + 6, // 0x68 'h' + 0x00,0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00, + + 6, // 0x69 'i' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00, + + 6, // 0x6a 'j' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x90,0x60, + + 6, // 0x6b 'k' + 0x00,0x00,0x80,0x88,0x90,0xa0,0xd0,0x88,0x00, + + 6, // 0x6c 'l' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 6, // 0x6d 'm' + 0x00,0x00,0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0x00, + + 6, // 0x6e 'n' + 0x00,0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00, + + 6, // 0x6f 'o' + 0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0xb8,0xc0,0x80,0x80,0x80,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00, + + 6, // 0x74 't' + 0x00,0x40,0x40,0xe0,0x40,0x40,0x48,0x30,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x78,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00, + + 6, // 0x77 'w' + 0x00,0x00,0x00,0x88,0x88,0xa8,0xd8,0x88,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x88,0x88,0x88,0x78,0x08,0x70, + + 6, // 0x7a 'z' + 0x00,0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00, + + 6, // 0x7b '{' + 0x18,0x20,0x20,0x20,0xc0,0x20,0x20,0x20,0x18, + + 6, // 0x7c '|' + 0x00,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00, + + 6, // 0x7d '}' + 0xc0,0x20,0x20,0x20,0x18,0x20,0x20,0x20,0xc0, + + 6, // 0x7e '~' + 0x00,0x40,0xa8,0x10,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x7f '' + 0x00,0x00,0x00,0x20,0x50,0x88,0xf8,0x00,0x00, + 0 + }; + + const int8u gse7x11[] = + { + 11, 0, 32, 128-32, + + 0x00,0x00,0x0c,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3c,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6c,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9c,0x00,0xa8,0x00,0xb4,0x00,0xc0,0x00,0xcc,0x00, + 0xd8,0x00,0xe4,0x00,0xf0,0x00,0xfc,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2c,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5c,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8c,0x01,0x98,0x01,0xa4,0x01, + 0xb0,0x01,0xbc,0x01,0xc8,0x01,0xd4,0x01,0xe0,0x01,0xec,0x01,0xf8,0x01,0x04,0x02,0x10,0x02, + 0x1c,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4c,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7c,0x02, + 0x88,0x02,0x94,0x02,0xa0,0x02,0xac,0x02,0xb8,0x02,0xc4,0x02,0xd0,0x02,0xdc,0x02,0xe8,0x02, + 0xf4,0x02,0x00,0x03,0x0c,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3c,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6c,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9c,0x03,0xa8,0x03,0xb4,0x03,0xc0,0x03, + 0xcc,0x03,0xd8,0x03,0xe4,0x03,0xf0,0x03,0xfc,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2c,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5c,0x04,0x68,0x04,0x74,0x04, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x00,0x10,0x38,0x38,0x38,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x48,0x48,0xfc,0x48,0x48,0xfc,0x48,0x48,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x00, + + 7, // 0x25 '%' + 0x00,0x00,0x42,0xa4,0x48,0x10,0x24,0x4a,0x84,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x30,0x48,0x48,0x30,0x60,0x94,0x98,0x6c,0x00,0x00, + + 7, // 0x27 ''' + 0x00,0x20,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x08,0x04,0x00,0x00, + + 7, // 0x29 ')' + 0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x20,0x40,0x00,0x00, + + 7, // 0x2a '*' + 0x00,0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00,0x00, + + 7, // 0x2b '+' + 0x00,0x00,0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60, + + 7, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 7, // 0x2f '/' + 0x00,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x38,0x44,0x4c,0x54,0x64,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x38,0x44,0x04,0x08,0x10,0x20,0x44,0x7c,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x7c,0x48,0x10,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x08,0x10,0x20,0x48,0x48,0x7c,0x08,0x1c,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x7c,0x40,0x40,0x78,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x18,0x20,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x7c,0x44,0x04,0x08,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3c,0x04,0x08,0x30,0x00,0x00, + + 7, // 0x3a ':' + 0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + + 7, // 0x3b ';' + 0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x30,0x60,0x00, + + 7, // 0x3c '<' + 0x00,0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x00,0x00, + + 7, // 0x3d '=' + 0x00,0x00,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00, + + 7, // 0x3e '>' + 0x00,0x00,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00, + + 7, // 0x3f '?' + 0x00,0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x30,0x48,0x04,0x34,0x54,0x54,0x54,0x28,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x10,0x28,0x44,0x44,0x7c,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x7c,0x40,0x40,0x70,0x40,0x40,0x40,0x7c,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x7c,0x40,0x40,0x70,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5c,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x4a 'J' + 0x00,0x1c,0x08,0x08,0x08,0x08,0x08,0x48,0x30,0x00,0x00, + + 7, // 0x4b 'K' + 0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x44,0x00,0x00, + + 7, // 0x4c 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7c,0x00,0x00, + + 7, // 0x4d 'M' + 0x00,0x44,0x6c,0x54,0x54,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x4e 'N' + 0x00,0x44,0x44,0x64,0x54,0x4c,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x4f 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x78,0x50,0x48,0x44,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x7c,0x54,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0x44,0x44,0x44,0x44,0x54,0x54,0x6c,0x44,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x5a 'Z' + 0x00,0x7c,0x04,0x08,0x10,0x20,0x40,0x44,0x7c,0x00,0x00, + + 7, // 0x5b '[' + 0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00,0x00, + + 7, // 0x5c '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00,0x00, + + 7, // 0x5d ']' + 0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00,0x00, + + 7, // 0x5e '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x20,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3c,0x44,0x44,0x3c,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x04,0x04,0x3c,0x44,0x44,0x44,0x44,0x3c,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x7c,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x18,0x24,0x20,0x70,0x20,0x20,0x20,0x70,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x3c,0x44,0x44,0x44,0x3c,0x04,0x44,0x38, + + 7, // 0x68 'h' + 0x00,0x40,0x40,0x40,0x58,0x64,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x6a 'j' + 0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x48,0x30,0x00, + + 7, // 0x6b 'k' + 0x00,0x40,0x40,0x44,0x48,0x50,0x68,0x44,0x44,0x00,0x00, + + 7, // 0x6c 'l' + 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x6d 'm' + 0x00,0x00,0x00,0xa8,0x54,0x54,0x54,0x54,0x54,0x00,0x00, + + 7, // 0x6e 'n' + 0x00,0x00,0x00,0xb8,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x6f 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x3c,0x44,0x44,0x44,0x44,0x3c,0x04,0x04, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x64,0x44,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x3c,0x40,0x38,0x04,0x04,0x78,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x24,0x18,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x3a,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x10,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x44,0x44,0x54,0x54,0x6c,0x44,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x3c,0x04,0x08,0x30,0x00, + + 7, // 0x7a 'z' + 0x00,0x00,0x00,0x7c,0x08,0x10,0x20,0x44,0x7c,0x00,0x00, + + 7, // 0x7b '{' + 0x00,0x0c,0x10,0x10,0x10,0x60,0x10,0x10,0x0c,0x00,0x00, + + 7, // 0x7c '|' + 0x00,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x20,0x00,0x00, + + 7, // 0x7d '}' + 0x00,0x60,0x10,0x10,0x10,0x0c,0x10,0x10,0x60,0x00,0x00, + + 7, // 0x7e '~' + 0x00,0x00,0x64,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7f '' + 0x00,0x00,0x00,0x10,0x28,0x44,0x44,0x7c,0x00,0x00,0x00, + 0 + }; + + const int8u gse7x11_bold[] = + { + 11, 0, 32, 128-32, + + 0x00,0x00,0x0c,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3c,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6c,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9c,0x00,0xa8,0x00,0xb4,0x00,0xc0,0x00,0xcc,0x00, + 0xd8,0x00,0xe4,0x00,0xf0,0x00,0xfc,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2c,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5c,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8c,0x01,0x98,0x01,0xa4,0x01, + 0xb0,0x01,0xbc,0x01,0xc8,0x01,0xd4,0x01,0xe0,0x01,0xec,0x01,0xf8,0x01,0x04,0x02,0x10,0x02, + 0x1c,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4c,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7c,0x02, + 0x88,0x02,0x94,0x02,0xa0,0x02,0xac,0x02,0xb8,0x02,0xc4,0x02,0xd0,0x02,0xdc,0x02,0xe8,0x02, + 0xf4,0x02,0x00,0x03,0x0c,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3c,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6c,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9c,0x03,0xa8,0x03,0xb4,0x03,0xc0,0x03, + 0xcc,0x03,0xd8,0x03,0xe4,0x03,0xf0,0x03,0xfc,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2c,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5c,0x04,0x68,0x04,0x74,0x04, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x00,0x30,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x6c,0x6c,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x48,0x48,0xfc,0x48,0x48,0xfc,0x48,0x48,0x00,0x00, + + 7, // 0x24 '$' + 0x30,0x30,0x78,0xcc,0xc0,0x78,0x0c,0xcc,0x78,0x30,0x30, + + 7, // 0x25 '%' + 0x00,0x00,0xc4,0x0c,0x18,0x30,0x60,0xc0,0x8c,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x30,0x58,0x58,0x30,0x74,0xdc,0xd8,0x6c,0x00,0x00, + + 7, // 0x27 ''' + 0x00,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00, + + 7, // 0x29 ')' + 0x00,0xc0,0x60,0x30,0x30,0x30,0x30,0x60,0xc0,0x00,0x00, + + 7, // 0x2a '*' + 0x00,0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00,0x00, + + 7, // 0x2b '+' + 0x00,0x00,0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00, + + 7, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 7, // 0x2f '/' + 0x00,0x0c,0x0c,0x18,0x18,0x30,0x30,0x60,0x60,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x78,0xcc,0xcc,0xdc,0xec,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x30,0x70,0xf0,0x30,0x30,0x30,0x30,0xfc,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x78,0xcc,0xcc,0x18,0x30,0x60,0xcc,0xfc,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0xfc,0x98,0x30,0x78,0x0c,0x0c,0xcc,0x78,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x18,0x30,0x68,0xd8,0xd8,0xfc,0x18,0x3c,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0xfc,0xc0,0xc0,0xf8,0x0c,0x0c,0xcc,0x78,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x38,0x60,0xc0,0xf8,0xcc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0xfc,0x8c,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x78,0xcc,0xcc,0xcc,0x7c,0x0c,0x18,0x70,0x00,0x00, + + 7, // 0x3a ':' + 0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x3b ';' + 0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x30,0x60,0x00, + + 7, // 0x3c '<' + 0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x00, + + 7, // 0x3d '=' + 0x00,0x00,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00, + + 7, // 0x3e '>' + 0x00,0x00,0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x00, + + 7, // 0x3f '?' + 0x00,0x78,0xcc,0xcc,0x18,0x30,0x30,0x00,0x30,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x70,0x88,0x04,0x74,0xb4,0xb4,0xb4,0x68,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x30,0x78,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0xf8,0xcc,0xcc,0xf8,0xcc,0xcc,0xcc,0xf8,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x78,0xcc,0xc0,0xc0,0xc0,0xc0,0xcc,0x78,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0xf0,0xd8,0xcc,0xcc,0xcc,0xcc,0xd8,0xf0,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0xfc,0xc4,0xd0,0xf0,0xd0,0xc0,0xc4,0xfc,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0xfc,0xc4,0xd0,0xf0,0xd0,0xc0,0xc0,0xc0,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x78,0xcc,0xc0,0xc0,0xdc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x4a 'J' + 0x00,0x3c,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,0x00,0x00, + + 7, // 0x4b 'K' + 0x00,0xcc,0xcc,0xd8,0xf0,0xd8,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x4c 'L' + 0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc4,0xfc,0x00,0x00, + + 7, // 0x4d 'M' + 0x00,0x84,0xcc,0xfc,0xb4,0xcc,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x4e 'N' + 0x00,0xcc,0xcc,0xec,0xfc,0xdc,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x4f 'O' + 0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0xf8,0xcc,0xcc,0xcc,0xf8,0xc0,0xc0,0xc0,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xdc,0x78,0x18,0x0c,0x00, + + 7, // 0x52 'R' + 0x00,0xf8,0xcc,0xcc,0xcc,0xf8,0xd8,0xcc,0xcc,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x78,0xcc,0xe0,0x70,0x38,0x1c,0xcc,0x78,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0xfc,0xb4,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0xcc,0xcc,0xcc,0xcc,0xb4,0xfc,0xcc,0x84,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0xcc,0xcc,0x78,0x30,0x78,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0xcc,0xcc,0xcc,0x78,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x5a 'Z' + 0x00,0xfc,0x8c,0x18,0x30,0x60,0xc0,0xc4,0xfc,0x00,0x00, + + 7, // 0x5b '[' + 0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x78,0x00,0x00, + + 7, // 0x5c '\' + 0x00,0x60,0x60,0x30,0x30,0x18,0x18,0x0c,0x0c,0x00,0x00, + + 7, // 0x5d ']' + 0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x78,0x00,0x00, + + 7, // 0x5e '^' + 0x00,0x10,0x38,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x70,0x18,0x78,0xd8,0xd8,0x6c,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x60,0x60,0x60,0x78,0x6c,0x6c,0x6c,0x78,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x78,0xcc,0xc0,0xc0,0xcc,0x78,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x18,0x18,0x18,0x78,0xd8,0xd8,0xd8,0x6c,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x78,0xcc,0xfc,0xc0,0xcc,0x78,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x18,0x34,0x30,0x78,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x6c,0xd8,0xd8,0xd8,0x78,0x18,0xd8,0x70, + + 7, // 0x68 'h' + 0x00,0xc0,0xc0,0xd8,0xec,0xcc,0xcc,0xcc,0xcc,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x30,0x00,0x70,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x6a 'j' + 0x00,0x0c,0x00,0x1c,0x0c,0x0c,0x0c,0x0c,0x6c,0x6c,0x38, + + 7, // 0x6b 'k' + 0x00,0xc0,0xc0,0xcc,0xcc,0xd8,0xf0,0xd8,0xcc,0x00,0x00, + + 7, // 0x6c 'l' + 0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x6d 'm' + 0x00,0x00,0x00,0xe8,0xfc,0xd4,0xd4,0xc4,0xc4,0x00,0x00, + + 7, // 0x6e 'n' + 0x00,0x00,0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00, + + 7, // 0x6f 'o' + 0x00,0x00,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xf8,0xc0,0xc0,0xc0, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x7c,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x0c, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0xd8,0xec,0xcc,0xc0,0xc0,0xc0,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x78,0xcc,0x60,0x18,0xcc,0x78,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x20,0x60,0x60,0xf0,0x60,0x60,0x68,0x30,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0x6c,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0xcc,0xcc,0xb4,0xfc,0xcc,0x84,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0xcc,0x78,0x30,0x78,0xcc,0xcc,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x18,0xf0, + + 7, // 0x7a 'z' + 0x00,0x00,0x00,0xfc,0x98,0x30,0x60,0xc4,0xfc,0x00,0x00, + + 7, // 0x7b '{' + 0x1c,0x30,0x30,0x30,0xe0,0x30,0x30,0x30,0x1c,0x00,0x00, + + 7, // 0x7c '|' + 0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x30,0x30,0x00,0x00, + + 7, // 0x7d '}' + 0xe0,0x30,0x30,0x30,0x1c,0x30,0x30,0x30,0xe0,0x00,0x00, + + 7, // 0x7e '~' + 0x00,0x34,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7f '' + 0x00,0x00,0x00,0x30,0x78,0xcc,0xcc,0xfc,0x00,0x00,0x00, + 0 + }; + + const int8u gse7x15[] = + { + 15, 0, 32, 128-32, + + 0x00,0x00,0x10,0x00,0x20,0x00,0x30,0x00,0x40,0x00,0x50,0x00,0x60,0x00,0x70,0x00,0x80,0x00, + 0x90,0x00,0xa0,0x00,0xb0,0x00,0xc0,0x00,0xd0,0x00,0xe0,0x00,0xf0,0x00,0x00,0x01,0x10,0x01, + 0x20,0x01,0x30,0x01,0x40,0x01,0x50,0x01,0x60,0x01,0x70,0x01,0x80,0x01,0x90,0x01,0xa0,0x01, + 0xb0,0x01,0xc0,0x01,0xd0,0x01,0xe0,0x01,0xf0,0x01,0x00,0x02,0x10,0x02,0x20,0x02,0x30,0x02, + 0x40,0x02,0x50,0x02,0x60,0x02,0x70,0x02,0x80,0x02,0x90,0x02,0xa0,0x02,0xb0,0x02,0xc0,0x02, + 0xd0,0x02,0xe0,0x02,0xf0,0x02,0x00,0x03,0x10,0x03,0x20,0x03,0x30,0x03,0x40,0x03,0x50,0x03, + 0x60,0x03,0x70,0x03,0x80,0x03,0x90,0x03,0xa0,0x03,0xb0,0x03,0xc0,0x03,0xd0,0x03,0xe0,0x03, + 0xf0,0x03,0x00,0x04,0x10,0x04,0x20,0x04,0x30,0x04,0x40,0x04,0x50,0x04,0x60,0x04,0x70,0x04, + 0x80,0x04,0x90,0x04,0xa0,0x04,0xb0,0x04,0xc0,0x04,0xd0,0x04,0xe0,0x04,0xf0,0x04,0x00,0x05, + 0x10,0x05,0x20,0x05,0x30,0x05,0x40,0x05,0x50,0x05,0x60,0x05,0x70,0x05,0x80,0x05,0x90,0x05, + 0xa0,0x05,0xb0,0x05,0xc0,0x05,0xd0,0x05,0xe0,0x05,0xf0,0x05, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x00,0x00,0x10,0x38,0x38,0x38,0x38,0x10,0x10,0x00,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x24,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x00,0x48,0x48,0x48,0xfc,0x48,0x48,0xfc,0x48,0x48,0x48,0x00,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x00,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x54,0x38,0x10,0x00,0x00,0x00, + + 7, // 0x25 '%' + 0x00,0x00,0x44,0x44,0x08,0x08,0x10,0x10,0x20,0x20,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x00,0x00,0x30,0x48,0x48,0x30,0x60,0x94,0x98,0x90,0x6c,0x00,0x00,0x00, + + 7, // 0x27 ''' + 0x00,0x00,0x20,0x20,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x04,0x08,0x10,0x20,0x20,0x20,0x20,0x20,0x10,0x08,0x04,0x00,0x00,0x00, + + 7, // 0x29 ')' + 0x00,0x40,0x20,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x20,0x40,0x00,0x00,0x00, + + 7, // 0x2a '*' + 0x00,0x00,0x00,0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2b '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x00,0x00,0x00,0x00, + + 7, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60,0x00, + + 7, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x2f '/' + 0x00,0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x00,0x38,0x44,0x44,0x4c,0x54,0x64,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x00,0x10,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x00,0x38,0x44,0x44,0x04,0x08,0x10,0x20,0x40,0x44,0x7c,0x00,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x00,0x7c,0x44,0x08,0x10,0x38,0x04,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x00,0x08,0x10,0x20,0x40,0x48,0x48,0x7c,0x08,0x08,0x1c,0x00,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x00,0x7c,0x40,0x40,0x40,0x78,0x04,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x00,0x18,0x20,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x00,0x7c,0x44,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x3c,0x04,0x04,0x08,0x30,0x00,0x00,0x00, + + 7, // 0x3a ':' + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, + + 7, // 0x3b ';' + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00, + + 7, // 0x3c '<' + 0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00, + + 7, // 0x3d '=' + 0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3e '>' + 0x00,0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00, + + 7, // 0x3f '?' + 0x00,0x00,0x78,0x84,0x84,0x84,0x08,0x10,0x20,0x20,0x00,0x20,0x00,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x00,0x00,0x30,0x48,0x04,0x34,0x54,0x54,0x54,0x54,0x28,0x00,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x00,0x10,0x28,0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x00,0x38,0x44,0x44,0x40,0x40,0x40,0x40,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x00,0x7c,0x40,0x40,0x40,0x70,0x40,0x40,0x40,0x40,0x7c,0x00,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x00,0x7c,0x40,0x40,0x40,0x70,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x5c,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x00,0x44,0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x4a 'J' + 0x00,0x00,0x1c,0x08,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00,0x00, + + 7, // 0x4b 'K' + 0x00,0x00,0x44,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x4c 'L' + 0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7c,0x00,0x00,0x00, + + 7, // 0x4d 'M' + 0x00,0x00,0x44,0x6c,0x54,0x54,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x4e 'N' + 0x00,0x00,0x44,0x44,0x44,0x64,0x54,0x4c,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x4f 'O' + 0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x50,0x48,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x00,0x38,0x44,0x44,0x40,0x38,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x00,0x7c,0x54,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x54,0x54,0x6c,0x44,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x00,0x44,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x5a 'Z' + 0x00,0x00,0x7c,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x40,0x7c,0x00,0x00,0x00, + + 7, // 0x5b '[' + 0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00,0x00, + + 7, // 0x5c '\' + 0x00,0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00,0x00,0x00, + + 7, // 0x5d ']' + 0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00,0x00, + + 7, // 0x5e '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x20,0x20,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x38,0x44,0x04,0x3c,0x44,0x44,0x44,0x3a,0x00,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x00,0x04,0x04,0x04,0x3c,0x44,0x44,0x44,0x44,0x44,0x3a,0x00,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x38,0x44,0x44,0x7c,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x00,0x18,0x24,0x20,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x3a,0x44,0x44,0x44,0x44,0x44,0x3c,0x04,0x44,0x38,0x00, + + 7, // 0x68 'h' + 0x00,0x00,0x40,0x40,0x40,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x00,0x10,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x6a 'j' + 0x00,0x00,0x08,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00, + + 7, // 0x6b 'k' + 0x00,0x00,0x40,0x40,0x44,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00,0x00, + + 7, // 0x6c 'l' + 0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x6d 'm' + 0x00,0x00,0x00,0x00,0xa8,0x54,0x54,0x54,0x54,0x54,0x54,0x54,0x00,0x00,0x00, + + 7, // 0x6e 'n' + 0x00,0x00,0x00,0x00,0xb8,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x6f 'o' + 0x00,0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x3c,0x44,0x44,0x44,0x44,0x44,0x3c,0x04,0x04,0x04,0x00, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x58,0x64,0x44,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x00,0x20,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x24,0x18,0x00,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x3a,0x00,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x54,0x54,0x6c,0x44,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x3c,0x04,0x08,0x70,0x00, + + 7, // 0x7a 'z' + 0x00,0x00,0x00,0x00,0x7c,0x04,0x08,0x10,0x20,0x40,0x40,0x7c,0x00,0x00,0x00, + + 7, // 0x7b '{' + 0x00,0x0c,0x10,0x10,0x10,0x10,0x10,0x60,0x10,0x10,0x10,0x10,0x0c,0x00,0x00, + + 7, // 0x7c '|' + 0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 7, // 0x7d '}' + 0x00,0x60,0x10,0x10,0x10,0x10,0x10,0x0c,0x10,0x10,0x10,0x10,0x60,0x00,0x00, + + 7, // 0x7e '~' + 0x00,0x00,0x64,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7f '' + 0x00,0x00,0x00,0x00,0x00,0x10,0x28,0x44,0x44,0x7c,0x00,0x00,0x00,0x00,0x00, + 0 + }; + + const int8u gse7x15_bold[] = + { + 15, 0, 32, 128-32, + + 0x00,0x00,0x10,0x00,0x20,0x00,0x30,0x00,0x40,0x00,0x50,0x00,0x60,0x00,0x70,0x00,0x80,0x00, + 0x90,0x00,0xa0,0x00,0xb0,0x00,0xc0,0x00,0xd0,0x00,0xe0,0x00,0xf0,0x00,0x00,0x01,0x10,0x01, + 0x20,0x01,0x30,0x01,0x40,0x01,0x50,0x01,0x60,0x01,0x70,0x01,0x80,0x01,0x90,0x01,0xa0,0x01, + 0xb0,0x01,0xc0,0x01,0xd0,0x01,0xe0,0x01,0xf0,0x01,0x00,0x02,0x10,0x02,0x20,0x02,0x30,0x02, + 0x40,0x02,0x50,0x02,0x60,0x02,0x70,0x02,0x80,0x02,0x90,0x02,0xa0,0x02,0xb0,0x02,0xc0,0x02, + 0xd0,0x02,0xe0,0x02,0xf0,0x02,0x00,0x03,0x10,0x03,0x20,0x03,0x30,0x03,0x40,0x03,0x50,0x03, + 0x60,0x03,0x70,0x03,0x80,0x03,0x90,0x03,0xa0,0x03,0xb0,0x03,0xc0,0x03,0xd0,0x03,0xe0,0x03, + 0xf0,0x03,0x00,0x04,0x10,0x04,0x20,0x04,0x30,0x04,0x40,0x04,0x50,0x04,0x60,0x04,0x70,0x04, + 0x80,0x04,0x90,0x04,0xa0,0x04,0xb0,0x04,0xc0,0x04,0xd0,0x04,0xe0,0x04,0xf0,0x04,0x00,0x05, + 0x10,0x05,0x20,0x05,0x30,0x05,0x40,0x05,0x50,0x05,0x60,0x05,0x70,0x05,0x80,0x05,0x90,0x05, + 0xa0,0x05,0xb0,0x05,0xc0,0x05,0xd0,0x05,0xe0,0x05,0xf0,0x05, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x00,0x00,0x00,0x30,0x78,0x78,0x78,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x6c,0x6c,0x6c,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x00,0x48,0x48,0x48,0xfc,0x48,0x48,0xfc,0x48,0x48,0x48,0x00,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x30,0x30,0x78,0xcc,0xe0,0x70,0x38,0x1c,0xcc,0x78,0x30,0x30,0x00,0x00, + + 7, // 0x25 '%' + 0x00,0x00,0x00,0x64,0x6c,0x08,0x18,0x10,0x30,0x20,0x6c,0x4c,0x00,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x00,0x00,0x30,0x58,0x58,0x30,0x74,0xdc,0xd8,0xd8,0x6c,0x00,0x00,0x00, + + 7, // 0x27 ''' + 0x00,0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x0c,0x18,0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x18,0x0c,0x00,0x00,0x00, + + 7, // 0x29 ')' + 0x00,0xc0,0x60,0x30,0x18,0x18,0x18,0x18,0x18,0x30,0x60,0xc0,0x00,0x00,0x00, + + 7, // 0x2a '*' + 0x00,0x00,0x00,0x00,0x00,0x20,0xa8,0x70,0xa8,0x20,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2b '+' + 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60,0x00, + + 7, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x2f '/' + 0x00,0x00,0x0c,0x0c,0x18,0x18,0x30,0x30,0x60,0x60,0xc0,0xc0,0x00,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xdc,0xec,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x00,0x30,0x30,0x70,0xf0,0x30,0x30,0x30,0x30,0x30,0xfc,0x00,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x00,0x78,0xcc,0xcc,0x0c,0x18,0x30,0x60,0xc0,0xcc,0xfc,0x00,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x00,0xfc,0x8c,0x18,0x30,0x78,0x0c,0x0c,0x0c,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x00,0x18,0x30,0x60,0xc8,0xd8,0xd8,0xfc,0x18,0x18,0x3c,0x00,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x00,0xfc,0xc0,0xc0,0xc0,0xf8,0x0c,0x0c,0x0c,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x00,0x38,0x60,0xc0,0xc0,0xf8,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x00,0xfc,0x8c,0x0c,0x0c,0x18,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x00,0x78,0xcc,0xcc,0xcc,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x18,0x70,0x00,0x00,0x00, + + 7, // 0x3a ':' + 0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00, + + 7, // 0x3b ';' + 0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60,0x00, + + 7, // 0x3c '<' + 0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x0c,0x00,0x00,0x00, + + 7, // 0x3d '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3e '>' + 0x00,0x00,0x00,0xc0,0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0xc0,0x00,0x00,0x00, + + 7, // 0x3f '?' + 0x00,0x00,0x78,0xcc,0xcc,0x18,0x30,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x00,0x00,0x70,0x88,0x04,0x74,0xb4,0xb4,0xb4,0xb4,0x68,0x00,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x00,0x30,0x78,0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xf8,0xcc,0xcc,0xcc,0xcc,0xf8,0x00,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x00,0x78,0xcc,0xc4,0xc0,0xc0,0xc0,0xc0,0xc4,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x00,0xf0,0xd8,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xd8,0xf0,0x00,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x00,0xfc,0xc4,0xc0,0xd0,0xf0,0xd0,0xc0,0xc0,0xc4,0xfc,0x00,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x00,0xfc,0xc4,0xc0,0xd0,0xf0,0xd0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x00,0x78,0xcc,0xc0,0xc0,0xc0,0xdc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00, + + 7, // 0x4a 'J' + 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,0x00,0x00,0x00, + + 7, // 0x4b 'K' + 0x00,0x00,0xcc,0xcc,0xd8,0xd8,0xf0,0xd8,0xd8,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x4c 'L' + 0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc4,0xfc,0x00,0x00,0x00, + + 7, // 0x4d 'M' + 0x00,0x00,0x84,0xcc,0xfc,0xb4,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x4e 'N' + 0x00,0x00,0xcc,0xcc,0xcc,0xec,0xfc,0xdc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x4f 'O' + 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xcc,0xf8,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xdc,0x78,0x18,0x0c,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xcc,0xf8,0xd8,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x00,0x78,0xcc,0xcc,0xe0,0x70,0x38,0x1c,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x00,0xfc,0xb4,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xb4,0xfc,0xcc,0x84,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x78,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00, + + 7, // 0x5a 'Z' + 0x00,0x00,0xfc,0x8c,0x0c,0x18,0x30,0x60,0xc0,0xc0,0xc4,0xfc,0x00,0x00,0x00, + + 7, // 0x5b '[' + 0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78,0x00,0x00, + + 7, // 0x5c '\' + 0x00,0x00,0xc0,0xc0,0x60,0x60,0x30,0x30,0x18,0x18,0x0c,0x0c,0x00,0x00,0x00, + + 7, // 0x5d ']' + 0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x78,0x00,0x00, + + 7, // 0x5e '^' + 0x00,0x10,0x38,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x00,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x30,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x70,0xd8,0x18,0x78,0xd8,0xd8,0xd8,0x6c,0x00,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x00,0x60,0x60,0x60,0x78,0x6c,0x6c,0x6c,0x6c,0x6c,0x78,0x00,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x78,0xcc,0xc0,0xc0,0xc0,0xc0,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x00,0x18,0x18,0x18,0x78,0xd8,0xd8,0xd8,0xd8,0xd8,0x6c,0x00,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x78,0xcc,0xcc,0xfc,0xc0,0xc0,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x00,0x30,0x68,0x60,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x6c,0xd8,0xd8,0xd8,0xd8,0xd8,0x78,0x18,0xd8,0x70,0x00, + + 7, // 0x68 'h' + 0x00,0x00,0xc0,0xc0,0xc0,0xd8,0xec,0xcc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x00,0x30,0x30,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00, + + 7, // 0x6a 'j' + 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,0x00, + + 7, // 0x6b 'k' + 0x00,0x00,0xc0,0xc0,0xcc,0xcc,0xcc,0xd8,0xf0,0xd8,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x6c 'l' + 0x00,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00, + + 7, // 0x6d 'm' + 0x00,0x00,0x00,0x00,0xe8,0xfc,0xd4,0xd4,0xd4,0xc4,0xc4,0xc4,0x00,0x00,0x00, + + 7, // 0x6e 'n' + 0x00,0x00,0x00,0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00, + + 7, // 0x6f 'o' + 0x00,0x00,0x00,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xcc,0xcc,0xf8,0xc0,0xc0,0xc0,0x00, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x7c,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x0c,0x00, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x00,0xd8,0xec,0xcc,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x78,0xcc,0xe0,0x70,0x38,0x1c,0xcc,0x78,0x00,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x00,0x20,0x60,0x60,0xf0,0x60,0x60,0x60,0x60,0x6c,0x38,0x00,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x00,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0x6c,0x00,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xb4,0xfc,0xcc,0x84,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x00,0xcc,0xcc,0x78,0x30,0x78,0xcc,0xcc,0xcc,0x00,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x18,0xf0,0x00, + + 7, // 0x7a 'z' + 0x00,0x00,0x00,0x00,0xfc,0x8c,0x18,0x30,0x60,0xc0,0xc4,0xfc,0x00,0x00,0x00, + + 7, // 0x7b '{' + 0x00,0x1c,0x30,0x30,0x30,0x30,0x30,0xe0,0x30,0x30,0x30,0x30,0x1c,0x00,0x00, + + 7, // 0x7c '|' + 0x00,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x00,0x00, + + 7, // 0x7d '}' + 0x00,0xe0,0x30,0x30,0x30,0x30,0x30,0x1c,0x30,0x30,0x30,0x30,0xe0,0x00,0x00, + + 7, // 0x7e '~' + 0x00,0x00,0x34,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7f '' + 0x00,0x00,0x00,0x00,0x00,0x30,0x78,0xcc,0xcc,0xfc,0x00,0x00,0x00,0x00,0x00, + 0 + }; + + const int8u gse8x16[] = + { + 16, 0, 32, 128-32, + + 0x00,0x00,0x11,0x00,0x22,0x00,0x33,0x00,0x44,0x00,0x55,0x00,0x66,0x00,0x77,0x00,0x88,0x00, + 0x99,0x00,0xaa,0x00,0xbb,0x00,0xcc,0x00,0xdd,0x00,0xee,0x00,0xff,0x00,0x10,0x01,0x21,0x01, + 0x32,0x01,0x43,0x01,0x54,0x01,0x65,0x01,0x76,0x01,0x87,0x01,0x98,0x01,0xa9,0x01,0xba,0x01, + 0xcb,0x01,0xdc,0x01,0xed,0x01,0xfe,0x01,0x0f,0x02,0x20,0x02,0x31,0x02,0x42,0x02,0x53,0x02, + 0x64,0x02,0x75,0x02,0x86,0x02,0x97,0x02,0xa8,0x02,0xb9,0x02,0xca,0x02,0xdb,0x02,0xec,0x02, + 0xfd,0x02,0x0e,0x03,0x1f,0x03,0x30,0x03,0x41,0x03,0x52,0x03,0x63,0x03,0x74,0x03,0x85,0x03, + 0x96,0x03,0xa7,0x03,0xb8,0x03,0xc9,0x03,0xda,0x03,0xeb,0x03,0xfc,0x03,0x0d,0x04,0x1e,0x04, + 0x2f,0x04,0x40,0x04,0x51,0x04,0x62,0x04,0x73,0x04,0x84,0x04,0x95,0x04,0xa6,0x04,0xb7,0x04, + 0xc8,0x04,0xd9,0x04,0xea,0x04,0xfb,0x04,0x0c,0x05,0x1d,0x05,0x2e,0x05,0x3f,0x05,0x50,0x05, + 0x61,0x05,0x72,0x05,0x83,0x05,0x94,0x05,0xa5,0x05,0xb6,0x05,0xc7,0x05,0xd8,0x05,0xe9,0x05, + 0xfa,0x05,0x0b,0x06,0x1c,0x06,0x2d,0x06,0x3e,0x06,0x4f,0x06, + + 8, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x21 '!' + 0x00,0x00,0x10,0x38,0x38,0x38,0x38,0x10,0x10,0x00,0x10,0x10,0x00,0x00,0x00,0x00, + + 8, // 0x22 '"' + 0x00,0x24,0x24,0x24,0x24,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x23 '#' + 0x00,0x00,0x24,0x24,0x24,0x7e,0x24,0x24,0x7e,0x24,0x24,0x24,0x00,0x00,0x00,0x00, + + 8, // 0x24 '$' + 0x00,0x14,0x14,0x3e,0x55,0x54,0x54,0x3e,0x15,0x15,0x55,0x3e,0x14,0x14,0x00,0x00, + + 8, // 0x25 '%' + 0x00,0x00,0x32,0x56,0x6c,0x04,0x08,0x08,0x10,0x13,0x25,0x26,0x00,0x00,0x00,0x00, + + 8, // 0x26 '&' + 0x00,0x00,0x18,0x24,0x24,0x24,0x18,0x28,0x45,0x46,0x44,0x3b,0x00,0x00,0x00,0x00, + + 8, // 0x27 ''' + 0x00,0x00,0x08,0x08,0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x28 '(' + 0x00,0x04,0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x10,0x10,0x08,0x04,0x00,0x00,0x00, + + 8, // 0x29 ')' + 0x00,0x10,0x08,0x04,0x04,0x02,0x02,0x02,0x02,0x04,0x04,0x08,0x10,0x00,0x00,0x00, + + 8, // 0x2a '*' + 0x00,0x00,0x00,0x00,0x66,0x24,0x18,0xff,0x18,0x24,0x66,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2b '+' + 0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x7f,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x20,0x00, + + 8, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + + 8, // 0x2f '/' + 0x00,0x02,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00,0x00, + + 8, // 0x30 '0' + 0x00,0x00,0x3c,0x42,0x42,0x46,0x4a,0x52,0x62,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x31 '1' + 0x00,0x00,0x08,0x08,0x18,0x38,0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x32 '2' + 0x00,0x00,0x3c,0x42,0x42,0x02,0x04,0x08,0x10,0x20,0x42,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x33 '3' + 0x00,0x00,0x7e,0x42,0x04,0x08,0x1c,0x02,0x02,0x02,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x34 '4' + 0x00,0x00,0x04,0x08,0x10,0x24,0x44,0x44,0x7e,0x04,0x04,0x0e,0x00,0x00,0x00,0x00, + + 8, // 0x35 '5' + 0x00,0x00,0x7e,0x42,0x40,0x40,0x7c,0x02,0x02,0x02,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x36 '6' + 0x00,0x00,0x1c,0x20,0x40,0x40,0x7c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x37 '7' + 0x00,0x00,0x7e,0x42,0x42,0x02,0x04,0x08,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00, + + 8, // 0x38 '8' + 0x00,0x00,0x3c,0x42,0x42,0x42,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x39 '9' + 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3e,0x02,0x02,0x04,0x38,0x00,0x00,0x00,0x00, + + 8, // 0x3a ':' + 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3b ';' + 0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60,0x40,0x00, + + 8, // 0x3c '<' + 0x00,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00, + + 8, // 0x3d '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3e '>' + 0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x04,0x08,0x10,0x20,0x00,0x00,0x00,0x00, + + 8, // 0x3f '?' + 0x00,0x00,0x3c,0x42,0x42,0x42,0x04,0x08,0x08,0x00,0x08,0x08,0x00,0x00,0x00,0x00, + + 8, // 0x40 '@' + 0x00,0x00,0x3c,0x42,0x01,0x39,0x49,0x49,0x49,0x49,0x49,0x36,0x00,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x00,0x00,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x7c,0x22,0x22,0x22,0x3c,0x22,0x22,0x22,0x22,0x7c,0x00,0x00,0x00,0x00, + + 8, // 0x43 'C' + 0x00,0x00,0x3c,0x42,0x42,0x40,0x40,0x40,0x40,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x44 'D' + 0x00,0x00,0x7c,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x7c,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x7e,0x22,0x20,0x28,0x38,0x28,0x20,0x20,0x22,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x7e,0x22,0x20,0x28,0x38,0x28,0x20,0x20,0x20,0x70,0x00,0x00,0x00,0x00, + + 8, // 0x47 'G' + 0x00,0x00,0x3c,0x42,0x42,0x40,0x40,0x4e,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x48 'H' + 0x00,0x00,0x42,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00,0x00, + + 8, // 0x49 'I' + 0x00,0x00,0x1c,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x4a 'J' + 0x00,0x00,0x0e,0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00, + + 8, // 0x4b 'K' + 0x00,0x00,0x62,0x22,0x24,0x28,0x30,0x28,0x24,0x22,0x22,0x62,0x00,0x00,0x00,0x00, + + 8, // 0x4c 'L' + 0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x4d 'M' + 0x00,0x00,0x41,0x63,0x55,0x49,0x41,0x41,0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00, + + 8, // 0x4e 'N' + 0x00,0x00,0x42,0x42,0x62,0x52,0x4a,0x46,0x42,0x42,0x42,0x42,0x00,0x00,0x00,0x00, + + 8, // 0x4f 'O' + 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x7c,0x22,0x22,0x22,0x22,0x3c,0x20,0x20,0x20,0x70,0x00,0x00,0x00,0x00, + + 8, // 0x51 'Q' + 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x4a,0x44,0x3a,0x02,0x00,0x00,0x00, + + 8, // 0x52 'R' + 0x00,0x00,0x7c,0x22,0x22,0x22,0x22,0x3c,0x28,0x24,0x22,0x62,0x00,0x00,0x00,0x00, + + 8, // 0x53 'S' + 0x00,0x00,0x3c,0x42,0x42,0x40,0x30,0x0c,0x02,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x54 'T' + 0x00,0x00,0x7f,0x49,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x55 'U' + 0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x41,0x41,0x41,0x41,0x22,0x22,0x14,0x14,0x08,0x08,0x00,0x00,0x00,0x00, + + 8, // 0x57 'W' + 0x00,0x00,0x41,0x41,0x41,0x41,0x41,0x49,0x49,0x55,0x63,0x41,0x00,0x00,0x00,0x00, + + 8, // 0x58 'X' + 0x00,0x00,0x42,0x42,0x42,0x24,0x18,0x18,0x24,0x42,0x42,0x42,0x00,0x00,0x00,0x00, + + 8, // 0x59 'Y' + 0x00,0x00,0x22,0x22,0x22,0x22,0x14,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x5a 'Z' + 0x00,0x00,0x7e,0x42,0x02,0x04,0x08,0x10,0x20,0x40,0x42,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x5b '[' + 0x00,0x1e,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1e,0x00,0x00,0x00, + + 8, // 0x5c '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x02,0x02,0x00,0x00,0x00, + + 8, // 0x5d ']' + 0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00, + + 8, // 0x5e '^' + 0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + + 8, // 0x60 '`' + 0x00,0x00,0x08,0x08,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x04,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x60,0x20,0x20,0x38,0x24,0x22,0x22,0x22,0x22,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x40,0x40,0x40,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x0c,0x04,0x04,0x1c,0x24,0x44,0x44,0x44,0x44,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x7e,0x40,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x66 'f' + 0x00,0x00,0x0c,0x12,0x10,0x10,0x38,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x44,0x44,0x44,0x44,0x44,0x3c,0x04,0x44,0x38,0x00, + + 8, // 0x68 'h' + 0x00,0x00,0x60,0x20,0x20,0x2c,0x32,0x22,0x22,0x22,0x22,0x62,0x00,0x00,0x00,0x00, + + 8, // 0x69 'i' + 0x00,0x00,0x08,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x6a 'j' + 0x00,0x00,0x04,0x04,0x00,0x0c,0x04,0x04,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00, + + 8, // 0x6b 'k' + 0x00,0x00,0x60,0x20,0x20,0x22,0x24,0x28,0x38,0x24,0x22,0x62,0x00,0x00,0x00,0x00, + + 8, // 0x6c 'l' + 0x00,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x6d 'm' + 0x00,0x00,0x00,0x00,0x00,0x76,0x49,0x49,0x49,0x49,0x41,0x41,0x00,0x00,0x00,0x00, + + 8, // 0x6e 'n' + 0x00,0x00,0x00,0x00,0x00,0x5c,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00,0x00, + + 8, // 0x6f 'o' + 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x7c,0x22,0x22,0x22,0x22,0x22,0x3c,0x20,0x20,0x70,0x00, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x44,0x44,0x44,0x44,0x44,0x3c,0x04,0x04,0x0e,0x00, + + 8, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x7c,0x22,0x22,0x20,0x20,0x20,0x70,0x00,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x40,0x3c,0x02,0x42,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x74 't' + 0x00,0x00,0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,0x12,0x0c,0x00,0x00,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x41,0x22,0x14,0x08,0x00,0x00,0x00,0x00, + + 8, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x49,0x49,0x55,0x22,0x00,0x00,0x00,0x00, + + 8, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x00,0x00,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x3e,0x02,0x04,0x78,0x00, + + 8, // 0x7a 'z' + 0x00,0x00,0x00,0x00,0x00,0x7e,0x44,0x08,0x10,0x20,0x42,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x7b '{' + 0x00,0x06,0x08,0x08,0x08,0x08,0x08,0x30,0x08,0x08,0x08,0x08,0x08,0x06,0x00,0x00, + + 8, // 0x7c '|' + 0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00, + + 8, // 0x7d '}' + 0x00,0x30,0x08,0x08,0x08,0x08,0x08,0x06,0x08,0x08,0x08,0x08,0x08,0x30,0x00,0x00, + + 8, // 0x7e '~' + 0x00,0x00,0x39,0x4e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x7f '' + 0x00,0x00,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x41,0x7f,0x00,0x00,0x00,0x00,0x00, + 0 + }; + + const int8u gse8x16_bold[] = + { + 16, 0, 32, 128-32, + + 0x00,0x00,0x11,0x00,0x22,0x00,0x33,0x00,0x44,0x00,0x55,0x00,0x66,0x00,0x77,0x00,0x88,0x00, + 0x99,0x00,0xaa,0x00,0xbb,0x00,0xcc,0x00,0xdd,0x00,0xee,0x00,0xff,0x00,0x10,0x01,0x21,0x01, + 0x32,0x01,0x43,0x01,0x54,0x01,0x65,0x01,0x76,0x01,0x87,0x01,0x98,0x01,0xa9,0x01,0xba,0x01, + 0xcb,0x01,0xdc,0x01,0xed,0x01,0xfe,0x01,0x0f,0x02,0x20,0x02,0x31,0x02,0x42,0x02,0x53,0x02, + 0x64,0x02,0x75,0x02,0x86,0x02,0x97,0x02,0xa8,0x02,0xb9,0x02,0xca,0x02,0xdb,0x02,0xec,0x02, + 0xfd,0x02,0x0e,0x03,0x1f,0x03,0x30,0x03,0x41,0x03,0x52,0x03,0x63,0x03,0x74,0x03,0x85,0x03, + 0x96,0x03,0xa7,0x03,0xb8,0x03,0xc9,0x03,0xda,0x03,0xeb,0x03,0xfc,0x03,0x0d,0x04,0x1e,0x04, + 0x2f,0x04,0x40,0x04,0x51,0x04,0x62,0x04,0x73,0x04,0x84,0x04,0x95,0x04,0xa6,0x04,0xb7,0x04, + 0xc8,0x04,0xd9,0x04,0xea,0x04,0xfb,0x04,0x0c,0x05,0x1d,0x05,0x2e,0x05,0x3f,0x05,0x50,0x05, + 0x61,0x05,0x72,0x05,0x83,0x05,0x94,0x05,0xa5,0x05,0xb6,0x05,0xc7,0x05,0xd8,0x05,0xe9,0x05, + 0xfa,0x05,0x0b,0x06,0x1c,0x06,0x2d,0x06,0x3e,0x06,0x4f,0x06, + + 8, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x21 '!' + 0x00,0x00,0x18,0x3c,0x3c,0x3c,0x3c,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + + 8, // 0x22 '"' + 0x00,0x66,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x23 '#' + 0x00,0x00,0x66,0x66,0x66,0xff,0x66,0x66,0xff,0x66,0x66,0x66,0x00,0x00,0x00,0x00, + + 8, // 0x24 '$' + 0x00,0x08,0x08,0x3e,0x6b,0x6b,0x68,0x3e,0x0b,0x6b,0x6b,0x3e,0x08,0x08,0x00,0x00, + + 8, // 0x25 '%' + 0x00,0x00,0x66,0xbe,0xcc,0x0c,0x18,0x18,0x30,0x33,0x65,0x66,0x00,0x00,0x00,0x00, + + 8, // 0x26 '&' + 0x00,0x00,0x1c,0x36,0x36,0x36,0x1c,0x3b,0x6e,0x66,0x66,0x3b,0x00,0x00,0x00,0x00, + + 8, // 0x27 ''' + 0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x28 '(' + 0x00,0x06,0x0c,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0c,0x06,0x00,0x00,0x00, + + 8, // 0x29 ')' + 0x00,0x30,0x18,0x0c,0x0c,0x06,0x06,0x06,0x06,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00, + + 8, // 0x2a '*' + 0x00,0x00,0x00,0x00,0x66,0x24,0x18,0xff,0x18,0x24,0x66,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2b '+' + 0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2c ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x20,0x00, + + 8, // 0x2d '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2e '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, + + 8, // 0x2f '/' + 0x00,0x03,0x03,0x06,0x06,0x0c,0x0c,0x18,0x18,0x30,0x30,0x60,0x60,0x00,0x00,0x00, + + 8, // 0x30 '0' + 0x00,0x00,0x3e,0x63,0x63,0x67,0x6b,0x73,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x31 '1' + 0x00,0x00,0x0c,0x0c,0x1c,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x00,0x00,0x00, + + 8, // 0x32 '2' + 0x00,0x00,0x3e,0x63,0x63,0x03,0x06,0x0c,0x18,0x30,0x61,0x7f,0x00,0x00,0x00,0x00, + + 8, // 0x33 '3' + 0x00,0x00,0x7f,0x43,0x06,0x0c,0x1e,0x03,0x03,0x03,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x34 '4' + 0x00,0x00,0x06,0x0c,0x18,0x32,0x66,0x66,0x7f,0x06,0x06,0x0f,0x00,0x00,0x00,0x00, + + 8, // 0x35 '5' + 0x00,0x00,0x7f,0x61,0x60,0x60,0x7e,0x03,0x03,0x03,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x36 '6' + 0x00,0x00,0x1e,0x30,0x60,0x60,0x7e,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x37 '7' + 0x00,0x00,0x7f,0x63,0x63,0x03,0x06,0x0c,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, + + 8, // 0x38 '8' + 0x00,0x00,0x3e,0x63,0x63,0x63,0x3e,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x39 '9' + 0x00,0x00,0x3e,0x63,0x63,0x63,0x63,0x3f,0x03,0x03,0x06,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x3a ':' + 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3b ';' + 0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x60,0x40,0x00, + + 8, // 0x3c '<' + 0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00, + + 8, // 0x3d '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3e '>' + 0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x30,0x00,0x00,0x00,0x00, + + 8, // 0x3f '?' + 0x00,0x00,0x3e,0x63,0x63,0x63,0x06,0x0c,0x0c,0x00,0x0c,0x0c,0x00,0x00,0x00,0x00, + + 8, // 0x40 '@' + 0x00,0x00,0x7c,0x86,0x03,0x73,0xdb,0xdb,0xdb,0xdb,0xdb,0x6e,0x00,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x63,0x7f,0x63,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x7e,0x33,0x33,0x33,0x3e,0x33,0x33,0x33,0x33,0x7e,0x00,0x00,0x00,0x00, + + 8, // 0x43 'C' + 0x00,0x00,0x1e,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x44 'D' + 0x00,0x00,0x7c,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7c,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x7f,0x33,0x31,0x34,0x3c,0x34,0x30,0x31,0x33,0x7f,0x00,0x00,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x7f,0x33,0x31,0x34,0x3c,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, + + 8, // 0x47 'G' + 0x00,0x00,0x1f,0x33,0x61,0x60,0x60,0x6f,0x63,0x63,0x33,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x48 'H' + 0x00,0x00,0x63,0x63,0x63,0x63,0x7f,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x49 'I' + 0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x4a 'J' + 0x00,0x00,0x0f,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x4b 'K' + 0x00,0x00,0x73,0x33,0x36,0x36,0x3c,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00, + + 8, // 0x4c 'L' + 0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7f,0x00,0x00,0x00,0x00, + + 8, // 0x4d 'M' + 0x00,0x00,0x63,0x63,0x77,0x77,0x7f,0x6b,0x6b,0x63,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x4e 'N' + 0x00,0x00,0x63,0x63,0x73,0x7b,0x6f,0x67,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x4f 'O' + 0x00,0x00,0x1c,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1c,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x7e,0x33,0x33,0x33,0x33,0x3e,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, + + 8, // 0x51 'Q' + 0x00,0x00,0x1c,0x36,0x63,0x63,0x63,0x63,0x63,0x6f,0x36,0x1e,0x03,0x00,0x00,0x00, + + 8, // 0x52 'R' + 0x00,0x00,0x7e,0x33,0x33,0x33,0x33,0x3e,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00, + + 8, // 0x53 'S' + 0x00,0x00,0x3e,0x63,0x63,0x30,0x18,0x0c,0x06,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x54 'T' + 0x00,0x00,0x3f,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x55 'U' + 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x00, + + 8, // 0x57 'W' + 0x00,0x00,0x63,0x63,0x63,0x6b,0x6b,0x7f,0x77,0x77,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x58 'X' + 0x00,0x00,0x63,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x59 'Y' + 0x00,0x00,0x33,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x5a 'Z' + 0x00,0x00,0x7f,0x63,0x43,0x06,0x0c,0x18,0x30,0x61,0x63,0x7f,0x00,0x00,0x00,0x00, + + 8, // 0x5b '[' + 0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00, + + 8, // 0x5c '\' + 0x00,0x60,0x60,0x30,0x30,0x18,0x18,0x0c,0x0c,0x06,0x06,0x03,0x03,0x00,0x00,0x00, + + 8, // 0x5d ']' + 0x00,0x7c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x7c,0x00,0x00,0x00, + + 8, // 0x5e '^' + 0x00,0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5f '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, + + 8, // 0x60 '`' + 0x00,0x00,0x18,0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x06,0x3e,0x66,0x66,0x3b,0x00,0x00,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x70,0x30,0x30,0x3c,0x36,0x33,0x33,0x33,0x33,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x63,0x60,0x60,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x0e,0x06,0x06,0x1e,0x36,0x66,0x66,0x66,0x66,0x3b,0x00,0x00,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x63,0x7f,0x60,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x66 'f' + 0x00,0x00,0x0e,0x1b,0x1b,0x18,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3b,0x66,0x66,0x66,0x66,0x66,0x3e,0x06,0x66,0x3c,0x00, + + 8, // 0x68 'h' + 0x00,0x00,0x70,0x30,0x30,0x36,0x3b,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00, + + 8, // 0x69 'i' + 0x00,0x00,0x0c,0x0c,0x00,0x1c,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x6a 'j' + 0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00,0x00, + + 8, // 0x6b 'k' + 0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3c,0x36,0x33,0x73,0x00,0x00,0x00,0x00, + + 8, // 0x6c 'l' + 0x00,0x00,0x1c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, + + 8, // 0x6d 'm' + 0x00,0x00,0x00,0x00,0x00,0x76,0x7f,0x6b,0x6b,0x6b,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x6e 'n' + 0x00,0x00,0x00,0x00,0x00,0x6e,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00, + + 8, // 0x6f 'o' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x6e,0x33,0x33,0x33,0x33,0x33,0x3e,0x30,0x30,0x78,0x00, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3b,0x66,0x66,0x66,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00, + + 8, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x60,0x3e,0x03,0x63,0x3e,0x00,0x00,0x00,0x00, + + 8, // 0x74 't' + 0x00,0x00,0x08,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x1b,0x0e,0x00,0x00,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3b,0x00,0x00,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x00, + + 8, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x6b,0x6b,0x7f,0x36,0x36,0x00,0x00,0x00,0x00, + + 8, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x1c,0x36,0x63,0x63,0x00,0x00,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x3f,0x03,0x06,0x7c,0x00, + + 8, // 0x7a 'z' + 0x00,0x00,0x00,0x00,0x00,0x7f,0x63,0x06,0x0c,0x18,0x31,0x7f,0x00,0x00,0x00,0x00, + + 8, // 0x7b '{' + 0x00,0x03,0x04,0x0c,0x0c,0x0c,0x08,0x30,0x08,0x0c,0x0c,0x0c,0x04,0x03,0x00,0x00, + + 8, // 0x7c '|' + 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x00,0x00,0x00, + + 8, // 0x7d '}' + 0x00,0x60,0x10,0x18,0x18,0x18,0x08,0x06,0x08,0x18,0x18,0x18,0x10,0x60,0x00,0x00, + + 8, // 0x7e '~' + 0x00,0x00,0x3b,0x6e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x7f '' + 0x00,0x00,0x00,0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00,0x00,0x00,0x00,0x00, + 0 + }; + + const int8u mcs11_prop[] = + { + 11, 2, 32, 128-32, + 0x00,0x00,0x0C,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3C,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6C,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9C,0x00,0xA8,0x00,0xB4,0x00,0xC0,0x00,0xCC,0x00, + 0xD8,0x00,0xE4,0x00,0xF0,0x00,0xFC,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2C,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5C,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8C,0x01,0x98,0x01,0xA4,0x01, + 0xB0,0x01,0xBC,0x01,0xC8,0x01,0xD4,0x01,0xE0,0x01,0xEC,0x01,0xF8,0x01,0x04,0x02,0x10,0x02, + 0x1C,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4C,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7C,0x02, + 0x88,0x02,0x94,0x02,0xA0,0x02,0xAC,0x02,0xB8,0x02,0xC4,0x02,0xD0,0x02,0xDC,0x02,0xE8,0x02, + 0xF4,0x02,0x00,0x03,0x0C,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3C,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6C,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9C,0x03,0xA8,0x03,0xB4,0x03,0xC0,0x03, + 0xCC,0x03,0xD8,0x03,0xE4,0x03,0xF0,0x03,0xFC,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2C,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5C,0x04,0x68,0x04,0x74,0x04, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + + 4, // 0x22 '"' + 0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x28,0x28,0x7C,0x28,0x28,0x28,0x7C,0x28,0x28,0x00, + + 6, // 0x24 '$' + 0x10,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x10, + + 6, // 0x25 '%' + 0x00,0x00,0x68,0xA8,0xD0,0x10,0x20,0x2C,0x54,0x58,0x00, + + 6, // 0x26 '&' + 0x00,0x20,0x50,0x50,0x50,0x20,0x54,0x54,0x48,0x34,0x00, + + 3, // 0x27 ''' + 0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x28 '(' + 0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 5, // 0x29 ')' + 0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40, + + 6, // 0x2A '*' + 0x00,0x00,0x28,0x7C,0x38,0x7C,0x28,0x00,0x00,0x00,0x00, + + 6, // 0x2B '+' + 0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00, + + 7, // 0x2F '/' + 0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40, + + 6, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x54,0x44,0x44,0x38,0x00, + + 4, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 6, // 0x32 '2' + 0x00,0x38,0x44,0x44,0x04,0x08,0x10,0x20,0x40,0x7C,0x00, + + 6, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x04,0x38,0x04,0x04,0x44,0x38,0x00, + + 6, // 0x34 '4' + 0x00,0x08,0x18,0x18,0x28,0x28,0x48,0x7C,0x08,0x08,0x00, + + 6, // 0x35 '5' + 0x00,0x7C,0x40,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00, + + 6, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x40,0x78,0x44,0x44,0x44,0x38,0x00, + + 6, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x00, + + 6, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00, + + 6, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x04,0x44,0x38,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0xC0, + + 6, // 0x3C '<' + 0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00, + + 6, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00, + + 6, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00, + + 6, // 0x40 '@' + 0x00,0x38,0x44,0x44,0x5C,0x54,0x54,0x4C,0x40,0x38,0x00, + + 6, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00, + + 6, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00, + + 6, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x40,0x44,0x38,0x00, + + 6, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00, + + 6, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00, + + 6, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00, + + 6, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5C,0x44,0x44,0x4C,0x34,0x00, + + 6, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00, + + 4, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 6, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x08,0x08,0x48,0x30,0x00, + + 6, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x60,0x50,0x48,0x44,0x44,0x00, + + 6, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00, + + 8, // 0x4D 'M' + 0x00,0x41,0x63,0x55,0x49,0x49,0x41,0x41,0x41,0x41,0x00, + + 7, // 0x4E 'N' + 0x00,0x42,0x42,0x62,0x52,0x4A,0x46,0x42,0x42,0x42,0x00, + + 6, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00, + + 6, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x40,0x00, + + 6, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00, + + 6, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x44,0x00, + + 6, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x40,0x38,0x04,0x04,0x44,0x38,0x00, + + 6, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00, + + 6, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00, + + 6, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00, + + 8, // 0x57 'W' + 0x00,0x41,0x41,0x41,0x41,0x49,0x49,0x49,0x55,0x22,0x00, + + 6, // 0x58 'X' + 0x00,0x44,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00, + + 6, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x10,0x00, + + 6, // 0x5A 'Z' + 0x00,0x7C,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00, + + 5, // 0x5B '[' + 0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30, + + 7, // 0x5C '\' + 0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00, + + 4, // 0x5D ']' + 0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60, + + 6, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00, + + 4, // 0x60 '`' + 0x00,0x40,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x44,0x3C,0x00, + + 6, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00, + + 6, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x44,0x7C,0x40,0x44,0x38,0x00, + + 4, // 0x66 'f' + 0x00,0x10,0x20,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x3C,0x04,0x44,0x38, + + 6, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x00, + + 2, // 0x69 'i' + 0x00,0x40,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, + + 3, // 0x6A 'j' + 0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0xA0,0x40, + + 5, // 0x6B 'k' + 0x00,0x40,0x40,0x48,0x50,0x60,0x60,0x50,0x48,0x48,0x00, + + 2, // 0x6C 'l' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, + + 8, // 0x6D 'm' + 0x00,0x00,0x00,0x76,0x49,0x49,0x49,0x49,0x41,0x41,0x00, + + 6, // 0x6E 'n' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x00, + + 6, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x04, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x20,0x20,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00, + + 5, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x28,0x10,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x4C,0x34,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00, + + 8, // 0x77 'w' + 0x00,0x00,0x00,0x41,0x41,0x41,0x41,0x49,0x49,0x36,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x3C,0x04,0x08,0x70, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00, + + 5, // 0x7B '{' + 0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x20,0x18, + + 3, // 0x7C '|' + 0x00,0x40,0x40,0x40,0x40,0x00,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x20,0x18,0x20,0x20,0x20,0x20,0xC0, + + 6, // 0x7E '~' + 0x00,0x24,0x54,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs11_prop_condensed[] = + { + 11, 2, 32, 128-32, + 0x00,0x00,0x0C,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3C,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6C,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9C,0x00,0xA8,0x00,0xB4,0x00,0xC0,0x00,0xCC,0x00, + 0xD8,0x00,0xE4,0x00,0xF0,0x00,0xFC,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2C,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5C,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8C,0x01,0x98,0x01,0xA4,0x01, + 0xB0,0x01,0xBC,0x01,0xC8,0x01,0xD4,0x01,0xE0,0x01,0xEC,0x01,0xF8,0x01,0x04,0x02,0x10,0x02, + 0x1C,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4C,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7C,0x02, + 0x88,0x02,0x94,0x02,0xA0,0x02,0xAC,0x02,0xB8,0x02,0xC4,0x02,0xD0,0x02,0xDC,0x02,0xE8,0x02, + 0xF4,0x02,0x00,0x03,0x0C,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3C,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6C,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9C,0x03,0xA8,0x03,0xB4,0x03,0xC0,0x03, + 0xCC,0x03,0xD8,0x03,0xE4,0x03,0xF0,0x03,0xFC,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2C,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5C,0x04,0x68,0x04,0x74,0x04, + + 3, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x21 '!' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x40,0x00, + + 4, // 0x22 '"' + 0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x23 '#' + 0x00,0x50,0x50,0xF8,0x50,0x50,0x50,0xF8,0x50,0x50,0x00, + + 5, // 0x24 '$' + 0x00,0x40,0x60,0x90,0x80,0x60,0x10,0x90,0x60,0x20,0x00, + + 5, // 0x25 '%' + 0x00,0x00,0x90,0x90,0x20,0x20,0x40,0x40,0x90,0x90,0x00, + + 5, // 0x26 '&' + 0x00,0x40,0xA0,0xA0,0xA0,0x40,0xA8,0x90,0x90,0x68,0x00, + + 5, // 0x27 ''' + 0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 4, // 0x29 ')' + 0x80,0x40,0x40,0x20,0x20,0x20,0x20,0x20,0x40,0x40,0x80, + + 5, // 0x2A '*' + 0x00,0x00,0x90,0x60,0xF0,0x60,0x90,0x00,0x00,0x00,0x00, + + 5, // 0x2B '+' + 0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x00, + + 6, // 0x2F '/' + 0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00, + + 5, // 0x30 '0' + 0x00,0x70,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00, + + 3, // 0x31 '1' + 0x00,0x40,0xC0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x32 '2' + 0x00,0x60,0x90,0x90,0x10,0x10,0x20,0x40,0x80,0xF0,0x00, + + 5, // 0x33 '3' + 0x00,0x60,0x90,0x10,0x10,0x60,0x10,0x10,0x90,0x60,0x00, + + 5, // 0x34 '4' + 0x00,0x10,0x30,0x30,0x50,0x50,0x90,0xF0,0x10,0x10,0x00, + + 5, // 0x35 '5' + 0x00,0xF0,0x80,0x80,0xE0,0x90,0x10,0x10,0x90,0x60,0x00, + + 5, // 0x36 '6' + 0x00,0x60,0x90,0x80,0x80,0xE0,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x37 '7' + 0x00,0xF0,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x00, + + 5, // 0x38 '8' + 0x00,0x60,0x90,0x90,0x90,0x60,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x39 '9' + 0x00,0x60,0x90,0x90,0x90,0x70,0x10,0x10,0x90,0x60,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0xC0, + + 6, // 0x3C '<' + 0x00,0x08,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x08,0x00, + + 5, // 0x3D '=' + 0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0xF0,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x00,0x80,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x80,0x00, + + 5, // 0x3F '?' + 0x00,0x60,0x90,0x10,0x10,0x20,0x40,0x00,0x40,0x00,0x00, + + 5, // 0x40 '@' + 0x00,0x60,0x90,0x90,0xB0,0xB0,0xB0,0x80,0x80,0x70,0x00, + + 5, // 0x41 'A' + 0x00,0x60,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x42 'B' + 0x00,0xE0,0x90,0x90,0x90,0xE0,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x43 'C' + 0x00,0x60,0x90,0x80,0x80,0x80,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x44 'D' + 0x00,0xE0,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x45 'E' + 0x00,0xF0,0x80,0x80,0x80,0xF0,0x80,0x80,0x80,0xF0,0x00, + + 5, // 0x46 'F' + 0x00,0xF0,0x80,0x80,0x80,0xF0,0x80,0x80,0x80,0x80,0x00, + + 5, // 0x47 'G' + 0x00,0x70,0x80,0x80,0x80,0xB0,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x48 'H' + 0x00,0x90,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x90,0x00, + + 4, // 0x49 'I' + 0x00,0xE0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xE0,0x00, + + 5, // 0x4A 'J' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0xA0,0xA0,0x40,0x00, + + 5, // 0x4B 'K' + 0x00,0x90,0x90,0xA0,0xA0,0xC0,0xA0,0xA0,0x90,0x90,0x00, + + 5, // 0x4C 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xF0,0x00, + + 6, // 0x4D 'M' + 0x00,0x88,0xD8,0xA8,0xA8,0xA8,0x88,0x88,0x88,0x88,0x00, + + 5, // 0x4E 'N' + 0x00,0x90,0x90,0xD0,0xD0,0xB0,0xB0,0x90,0x90,0x90,0x00, + + 5, // 0x4F 'O' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x50 'P' + 0x00,0xE0,0x90,0x90,0x90,0x90,0xE0,0x80,0x80,0x80,0x00, + + 5, // 0x51 'Q' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x30, + + 5, // 0x52 'R' + 0x00,0xE0,0x90,0x90,0x90,0x90,0xE0,0xA0,0x90,0x90,0x00, + + 5, // 0x53 'S' + 0x00,0x60,0x90,0x80,0x80,0x60,0x10,0x10,0x90,0x60,0x00, + + 6, // 0x54 'T' + 0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x55 'U' + 0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 6, // 0x56 'V' + 0x00,0x88,0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x20,0x00, + + 6, // 0x57 'W' + 0x00,0x88,0x88,0x88,0xA8,0xA8,0xA8,0xA8,0xA8,0x50,0x00, + + 5, // 0x58 'X' + 0x00,0x90,0x90,0x90,0x60,0x60,0x90,0x90,0x90,0x90,0x00, + + 6, // 0x59 'Y' + 0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x5A 'Z' + 0x00,0xF0,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0xF0,0x00, + + 4, // 0x5B '[' + 0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x60,0x00, + + 6, // 0x5C '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00, + + 4, // 0x5D ']' + 0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60,0x00, + + 5, // 0x5E '^' + 0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00, + + 5, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x61 'a' + 0x00,0x00,0x00,0x60,0x90,0x10,0x70,0x90,0x90,0x70,0x00, + + 5, // 0x62 'b' + 0x00,0x80,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x00,0x60,0x90,0x80,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x64 'd' + 0x00,0x10,0x10,0x10,0x70,0x90,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x65 'e' + 0x00,0x00,0x00,0x60,0x90,0x90,0xF0,0x80,0x90,0x60,0x00, + + 4, // 0x66 'f' + 0x00,0x20,0x40,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x67 'g' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x70,0x10,0x90,0x60, + + 5, // 0x68 'h' + 0x00,0x80,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0x90,0x00, + + 2, // 0x69 'i' + 0x00,0x80,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00, + + 4, // 0x6A 'j' + 0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0xA0,0x40, + + 5, // 0x6B 'k' + 0x00,0x80,0x80,0x90,0x90,0xA0,0xC0,0xA0,0x90,0x90,0x00, + + 2, // 0x6C 'l' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00, + + 6, // 0x6D 'm' + 0x00,0x00,0x00,0xD0,0xA8,0xA8,0xA8,0x88,0x88,0x88,0x00, + + 5, // 0x6E 'n' + 0x00,0x00,0x00,0xA0,0xD0,0x90,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x6F 'o' + 0x00,0x00,0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x70 'p' + 0x00,0x00,0x00,0xE0,0x90,0x90,0x90,0x90,0xE0,0x80,0x80, + + 5, // 0x71 'q' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x90,0x70,0x10,0x10, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0xB8,0x48,0x40,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x00,0x60,0x90,0x40,0x20,0x10,0x90,0x60,0x00, + + 4, // 0x74 't' + 0x00,0x40,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x20,0x00, + + 5, // 0x75 'u' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x70,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x50,0x20,0x20,0x00, + + 6, // 0x77 'w' + 0x00,0x00,0x00,0x88,0x88,0x88,0xA8,0xA8,0xA8,0x50,0x00, + + 5, // 0x78 'x' + 0x00,0x00,0x00,0x90,0x90,0x60,0x60,0x90,0x90,0x90,0x00, + + 5, // 0x79 'y' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x70,0x10,0x20,0xC0, + + 5, // 0x7A 'z' + 0x00,0x00,0x00,0xF0,0x10,0x20,0x40,0x80,0x80,0xF0,0x00, + + 5, // 0x7B '{' + 0x30,0x40,0x40,0x40,0x40,0x80,0x40,0x40,0x40,0x40,0x30, + + 3, // 0x7C '|' + 0x00,0x40,0x40,0x40,0x40,0x00,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0xC0, + + 5, // 0x7E '~' + 0x00,0x40,0xA8,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x7F '' + 0x00,0x20,0x70,0xD8,0x88,0x88,0xF8,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs12_prop[] = + { + 12, 3, 32, 128-32, + 0x00,0x00,0x0D,0x00,0x1A,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x4E,0x00,0x5B,0x00,0x68,0x00, + 0x75,0x00,0x82,0x00,0x8F,0x00,0x9C,0x00,0xA9,0x00,0xB6,0x00,0xC3,0x00,0xD0,0x00,0xDD,0x00, + 0xEA,0x00,0xF7,0x00,0x04,0x01,0x11,0x01,0x1E,0x01,0x2B,0x01,0x38,0x01,0x45,0x01,0x52,0x01, + 0x5F,0x01,0x6C,0x01,0x79,0x01,0x86,0x01,0x93,0x01,0xA0,0x01,0xAD,0x01,0xBA,0x01,0xC7,0x01, + 0xD4,0x01,0xE1,0x01,0xEE,0x01,0xFB,0x01,0x08,0x02,0x15,0x02,0x22,0x02,0x2F,0x02,0x3C,0x02, + 0x49,0x02,0x62,0x02,0x6F,0x02,0x7C,0x02,0x89,0x02,0x96,0x02,0xA3,0x02,0xB0,0x02,0xBD,0x02, + 0xCA,0x02,0xD7,0x02,0xF0,0x02,0xFD,0x02,0x0A,0x03,0x17,0x03,0x24,0x03,0x31,0x03,0x3E,0x03, + 0x4B,0x03,0x58,0x03,0x65,0x03,0x72,0x03,0x7F,0x03,0x8C,0x03,0x99,0x03,0xA6,0x03,0xB3,0x03, + 0xC0,0x03,0xCD,0x03,0xDA,0x03,0xE7,0x03,0xF4,0x03,0x01,0x04,0x1A,0x04,0x27,0x04,0x34,0x04, + 0x41,0x04,0x4E,0x04,0x5B,0x04,0x68,0x04,0x75,0x04,0x82,0x04,0x8F,0x04,0xA8,0x04,0xB5,0x04, + 0xC2,0x04,0xCF,0x04,0xDC,0x04,0xE9,0x04,0xF6,0x04,0x03,0x05, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 4, // 0x22 '"' + 0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x28,0x28,0x28,0x7C,0x28,0x28,0x28,0x7C,0x28,0x28,0x28,0x00, + + 6, // 0x24 '$' + 0x10,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x10,0x00, + + 7, // 0x25 '%' + 0x32,0x54,0x64,0x08,0x08,0x10,0x10,0x26,0x2A,0x4C,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x30,0x48,0x48,0x48,0x30,0x4A,0x4A,0x44,0x3A,0x00,0x00, + + 3, // 0x27 ''' + 0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x00, + + 5, // 0x29 ')' + 0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40,0x00, + + 6, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x00, + + 6, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x40,0x80, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 7, // 0x2F '/' + 0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00, + + 7, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x54,0x44,0x44,0x38,0x00,0x00, + + 4, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x04,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x28,0x48,0x48,0x7C,0x08,0x08,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x7C,0x40,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x78,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x04,0x44,0x38,0x00,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x40,0x80, + + 6, // 0x3C '<' + 0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00, + + 6, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00, + + 6, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x38,0x44,0x44,0x5C,0x54,0x54,0x4C,0x40,0x38,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5C,0x44,0x44,0x4C,0x34,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00, + + 6, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x60,0x50,0x48,0x44,0x44,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 9, // 0x4D 'M' + 0x00,0x00,0x41,0x00,0x63,0x00,0x55,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x4E 'N' + 0x00,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x44,0x00,0x00, + + 7, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x40,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00, + + 9, // 0x57 'W' + 0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x49,0x00,0x49,0x00,0x55,0x00,0x22,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x44,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x5A 'Z' + 0x00,0x7C,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00, + + 4, // 0x5B '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00, + + 7, // 0x5C '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00, + + 4, // 0x5D ']' + 0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xE0,0x00, + + 6, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00, + + 4, // 0x60 '`' + 0x00,0x40,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x44,0x7C,0x40,0x44,0x38,0x00,0x00, + + 4, // 0x66 'f' + 0x00,0x30,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x78, + + 7, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x40,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0x60, + + 6, // 0x6B 'k' + 0x00,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 9, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x6E 'n' + 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x04, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x20,0x18,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, + + 9, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x36,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x3C,0x08,0x70, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 5, // 0x7B '{' + 0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x20,0x18,0x00, + + 3, // 0x7C '|' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x20,0x18,0x20,0x20,0x20,0x20,0xC0,0x00, + + 7, // 0x7E '~' + 0x00,0x60,0x92,0x92,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs13_prop[] = + { + 13, 4, 32, 128-32, + 0x00,0x00,0x0E,0x00,0x1C,0x00,0x2A,0x00,0x38,0x00,0x46,0x00,0x54,0x00,0x62,0x00,0x70,0x00, + 0x7E,0x00,0x8C,0x00,0x9A,0x00,0xA8,0x00,0xB6,0x00,0xC4,0x00,0xD2,0x00,0xE0,0x00,0xEE,0x00, + 0xFC,0x00,0x0A,0x01,0x18,0x01,0x26,0x01,0x34,0x01,0x42,0x01,0x50,0x01,0x5E,0x01,0x6C,0x01, + 0x7A,0x01,0x88,0x01,0x96,0x01,0xA4,0x01,0xB2,0x01,0xC0,0x01,0xCE,0x01,0xDC,0x01,0xEA,0x01, + 0xF8,0x01,0x06,0x02,0x14,0x02,0x22,0x02,0x30,0x02,0x3E,0x02,0x4C,0x02,0x5A,0x02,0x68,0x02, + 0x76,0x02,0x91,0x02,0x9F,0x02,0xAD,0x02,0xBB,0x02,0xC9,0x02,0xD7,0x02,0xE5,0x02,0xF3,0x02, + 0x01,0x03,0x0F,0x03,0x2A,0x03,0x38,0x03,0x46,0x03,0x54,0x03,0x62,0x03,0x70,0x03,0x7E,0x03, + 0x8C,0x03,0x9A,0x03,0xA8,0x03,0xB6,0x03,0xC4,0x03,0xD2,0x03,0xE0,0x03,0xEE,0x03,0xFC,0x03, + 0x0A,0x04,0x18,0x04,0x26,0x04,0x34,0x04,0x42,0x04,0x50,0x04,0x6B,0x04,0x79,0x04,0x87,0x04, + 0x95,0x04,0xA3,0x04,0xB1,0x04,0xBF,0x04,0xCD,0x04,0xDB,0x04,0xE9,0x04,0x04,0x05,0x12,0x05, + 0x20,0x05,0x2E,0x05,0x3C,0x05,0x4A,0x05,0x58,0x05,0x66,0x05, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 4, // 0x22 '"' + 0x00,0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x28,0x28,0x28,0x7C,0x28,0x28,0x28,0x7C,0x28,0x28,0x28,0x00, + + 6, // 0x24 '$' + 0x00,0x10,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x10,0x00, + + 7, // 0x25 '%' + 0x00,0x32,0x54,0x64,0x08,0x08,0x10,0x10,0x26,0x2A,0x4C,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x30,0x48,0x48,0x48,0x30,0x4A,0x4A,0x44,0x3A,0x00,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x00,0x00, + + 5, // 0x29 ')' + 0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40,0x00,0x00, + + 6, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x00,0x00, + + 6, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x20,0x40,0x80, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00, + + 7, // 0x2F '/' + 0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x54,0x44,0x44,0x38,0x00,0x00,0x00, + + 4, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x04,0x38,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x28,0x48,0x48,0x7C,0x08,0x08,0x00,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x7C,0x40,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x78,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x00,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x20,0x40,0x80, + + 6, // 0x3C '<' + 0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, + + 6, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, + + 6, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x38,0x44,0x44,0x5C,0x54,0x54,0x4C,0x40,0x38,0x00,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5C,0x44,0x44,0x4C,0x34,0x00,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00,0x00, + + 6, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x60,0x50,0x48,0x44,0x44,0x00,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00,0x00, + + 9, // 0x4D 'M' + 0x00,0x00,0x41,0x00,0x63,0x00,0x55,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x4E 'N' + 0x00,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x40,0x38,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 6, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 9, // 0x57 'W' + 0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x49,0x00,0x49,0x00,0x55,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x44,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 6, // 0x5A 'Z' + 0x00,0x7C,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00,0x00, + + 4, // 0x5B '[' + 0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70,0x00,0x00, + + 7, // 0x5C '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00,0x00, + + 4, // 0x5D ']' + 0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xE0,0x00,0x00, + + 6, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00, + + 4, // 0x60 '`' + 0x00,0x40,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x44,0x3C,0x00,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x00,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x44,0x7C,0x40,0x44,0x38,0x00,0x00,0x00, + + 4, // 0x66 'f' + 0x00,0x30,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x44,0x38, + + 7, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x40,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0x60,0x00, + + 6, // 0x6B 'k' + 0x00,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 9, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x6E 'n' + 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x04,0x04, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x20,0x18,0x00,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 9, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x08,0x70, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,0x00, + + 5, // 0x7B '{' + 0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x20,0x18,0x00,0x00, + + 3, // 0x7C '|' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x20,0x18,0x20,0x20,0x20,0x20,0xC0,0x00,0x00, + + 7, // 0x7E '~' + 0x00,0x60,0x92,0x92,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs5x10_mono[] = + { + 10, 2, 32, 128-32, + 0x00,0x00,0x0B,0x00,0x16,0x00,0x21,0x00,0x2C,0x00,0x37,0x00,0x42,0x00,0x4D,0x00,0x58,0x00, + 0x63,0x00,0x6E,0x00,0x79,0x00,0x84,0x00,0x8F,0x00,0x9A,0x00,0xA5,0x00,0xB0,0x00,0xBB,0x00, + 0xC6,0x00,0xD1,0x00,0xDC,0x00,0xE7,0x00,0xF2,0x00,0xFD,0x00,0x08,0x01,0x13,0x01,0x1E,0x01, + 0x29,0x01,0x34,0x01,0x3F,0x01,0x4A,0x01,0x55,0x01,0x60,0x01,0x6B,0x01,0x76,0x01,0x81,0x01, + 0x8C,0x01,0x97,0x01,0xA2,0x01,0xAD,0x01,0xB8,0x01,0xC3,0x01,0xCE,0x01,0xD9,0x01,0xE4,0x01, + 0xEF,0x01,0xFA,0x01,0x05,0x02,0x10,0x02,0x1B,0x02,0x26,0x02,0x31,0x02,0x3C,0x02,0x47,0x02, + 0x52,0x02,0x5D,0x02,0x68,0x02,0x73,0x02,0x7E,0x02,0x89,0x02,0x94,0x02,0x9F,0x02,0xAA,0x02, + 0xB5,0x02,0xC0,0x02,0xCB,0x02,0xD6,0x02,0xE1,0x02,0xEC,0x02,0xF7,0x02,0x02,0x03,0x0D,0x03, + 0x18,0x03,0x23,0x03,0x2E,0x03,0x39,0x03,0x44,0x03,0x4F,0x03,0x5A,0x03,0x65,0x03,0x70,0x03, + 0x7B,0x03,0x86,0x03,0x91,0x03,0x9C,0x03,0xA7,0x03,0xB2,0x03,0xBD,0x03,0xC8,0x03,0xD3,0x03, + 0xDE,0x03,0xE9,0x03,0xF4,0x03,0xFF,0x03,0x0A,0x04,0x15,0x04, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + + 5, // 0x22 '"' + 0x00,0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x23 '#' + 0x00,0x50,0x50,0xF8,0x50,0x50,0x50,0xF8,0x50,0x50, + + 5, // 0x24 '$' + 0x00,0x40,0x60,0x90,0x80,0x60,0x10,0x90,0x60,0x20, + + 5, // 0x25 '%' + 0x00,0x00,0x90,0x90,0x20,0x20,0x40,0x40,0x90,0x90, + + 5, // 0x26 '&' + 0x00,0x40,0xA0,0xA0,0xA0,0x40,0xA8,0x90,0x90,0x68, + + 5, // 0x27 ''' + 0x00,0x20,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 5, // 0x29 ')' + 0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x20,0x20,0x40, + + 5, // 0x2A '*' + 0x00,0x00,0x90,0x60,0xF0,0x60,0x90,0x00,0x00,0x00, + + 5, // 0x2B '+' + 0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00, + + 5, // 0x2F '/' + 0x00,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x00, + + 5, // 0x30 '0' + 0x00,0x70,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x32 '2' + 0x00,0x60,0x90,0x90,0x10,0x20,0x40,0x80,0xF0,0x00, + + 5, // 0x33 '3' + 0x00,0x60,0x90,0x10,0x60,0x10,0x10,0x90,0x60,0x00, + + 5, // 0x34 '4' + 0x00,0x10,0x30,0x50,0x50,0x90,0xF0,0x10,0x10,0x00, + + 5, // 0x35 '5' + 0x00,0xF0,0x80,0x80,0xE0,0x10,0x10,0x90,0x60,0x00, + + 5, // 0x36 '6' + 0x00,0x60,0x80,0x80,0xE0,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x37 '7' + 0x00,0xF0,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x00, + + 5, // 0x38 '8' + 0x00,0x60,0x90,0x90,0x60,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x39 '9' + 0x00,0x60,0x90,0x90,0x90,0x70,0x10,0x10,0x60,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0xC0, + + 5, // 0x3C '<' + 0x00,0x08,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x08, + + 5, // 0x3D '=' + 0x00,0x00,0x00,0x00,0xF0,0x00,0xF0,0x00,0x00,0x00, + + 5, // 0x3E '>' + 0x00,0x80,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x80, + + 5, // 0x3F '?' + 0x00,0x60,0x90,0x10,0x10,0x20,0x40,0x00,0x40,0x00, + + 5, // 0x40 '@' + 0x00,0x60,0x90,0x90,0xB0,0xB0,0x80,0x80,0x70,0x00, + + 5, // 0x41 'A' + 0x00,0x60,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x00, + + 5, // 0x42 'B' + 0x00,0xE0,0x90,0x90,0xE0,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x43 'C' + 0x00,0x60,0x90,0x80,0x80,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x44 'D' + 0x00,0xE0,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x45 'E' + 0x00,0xF0,0x80,0x80,0xF0,0x80,0x80,0x80,0xF0,0x00, + + 5, // 0x46 'F' + 0x00,0xF0,0x80,0x80,0xF0,0x80,0x80,0x80,0x80,0x00, + + 5, // 0x47 'G' + 0x00,0x60,0x90,0x80,0x80,0xB0,0x90,0x90,0x60,0x00, + + 5, // 0x48 'H' + 0x00,0x90,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x4A 'J' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0xA0,0x40,0x00, + + 5, // 0x4B 'K' + 0x00,0x90,0xA0,0xA0,0xC0,0xC0,0xA0,0xA0,0x90,0x00, + + 5, // 0x4C 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xF0,0x00, + + 5, // 0x4D 'M' + 0x00,0x90,0x90,0xF0,0xF0,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x4E 'N' + 0x00,0x90,0x90,0xD0,0xD0,0xB0,0xB0,0x90,0x90,0x00, + + 5, // 0x4F 'O' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x50 'P' + 0x00,0xE0,0x90,0x90,0x90,0xE0,0x80,0x80,0x80,0x00, + + 5, // 0x51 'Q' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x30, + + 5, // 0x52 'R' + 0x00,0xE0,0x90,0x90,0x90,0xE0,0xA0,0x90,0x90,0x00, + + 5, // 0x53 'S' + 0x00,0x60,0x90,0x80,0x60,0x10,0x90,0x90,0x60,0x00, + + 5, // 0x54 'T' + 0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x55 'U' + 0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x56 'V' + 0x00,0x90,0x90,0x90,0x50,0x50,0x50,0x20,0x20,0x00, + + 5, // 0x57 'W' + 0x00,0x90,0x90,0x90,0x90,0x90,0xF0,0xF0,0x90,0x00, + + 5, // 0x58 'X' + 0x00,0x90,0x90,0x90,0x60,0x60,0x90,0x90,0x90,0x00, + + 5, // 0x59 'Y' + 0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x5A 'Z' + 0x00,0xF0,0x10,0x20,0x20,0x40,0x40,0x80,0xF0,0x00, + + 5, // 0x5B '[' + 0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x60, + + 5, // 0x5C '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08, + + 5, // 0x5D ']' + 0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60, + + 5, // 0x5E '^' + 0x00,0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00, + + 5, // 0x60 '`' + 0x00,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x61 'a' + 0x00,0x00,0x00,0x60,0x10,0x70,0x90,0x90,0x70,0x00, + + 5, // 0x62 'b' + 0x00,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0xE0,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x00,0x60,0x90,0x80,0x80,0x90,0x60,0x00, + + 5, // 0x64 'd' + 0x00,0x10,0x10,0x70,0x90,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x65 'e' + 0x00,0x00,0x00,0x60,0x90,0x90,0xF0,0x80,0x70,0x00, + + 5, // 0x66 'f' + 0x00,0x30,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x00, + + 5, // 0x67 'g' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x70,0x10,0xE0, + + 5, // 0x68 'h' + 0x00,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x69 'i' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x6A 'j' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0xC0, + + 5, // 0x6B 'k' + 0x00,0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x90,0x00, + + 5, // 0x6C 'l' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00, + + 5, // 0x6D 'm' + 0x00,0x00,0x00,0x90,0xF0,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x6E 'n' + 0x00,0x00,0x00,0xE0,0x90,0x90,0x90,0x90,0x90,0x00, + + 5, // 0x6F 'o' + 0x00,0x00,0x00,0x60,0x90,0x90,0x90,0x90,0x60,0x00, + + 5, // 0x70 'p' + 0x00,0x00,0x00,0xE0,0x90,0x90,0x90,0xE0,0x80,0x80, + + 5, // 0x71 'q' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x70,0x10,0x10, + + 5, // 0x72 'r' + 0x00,0x00,0x00,0xB0,0x50,0x40,0x40,0x40,0xE0,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x00,0x60,0x90,0x40,0x20,0x90,0x60,0x00, + + 5, // 0x74 't' + 0x00,0x40,0x40,0xE0,0x40,0x40,0x40,0x50,0x20,0x00, + + 5, // 0x75 'u' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x90,0x70,0x00, + + 5, // 0x76 'v' + 0x00,0x00,0x00,0x90,0x90,0x50,0x50,0x20,0x20,0x00, + + 5, // 0x77 'w' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0xF0,0x90,0x00, + + 5, // 0x78 'x' + 0x00,0x00,0x00,0x90,0x90,0x60,0x60,0x90,0x90,0x00, + + 5, // 0x79 'y' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x70,0x10,0xE0, + + 5, // 0x7A 'z' + 0x00,0x00,0x00,0xF0,0x10,0x20,0x40,0x80,0xF0,0x00, + + 5, // 0x7B '{' + 0x30,0x40,0x40,0x40,0x80,0x40,0x40,0x40,0x40,0x30, + + 5, // 0x7C '|' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0xC0, + + 5, // 0x7E '~' + 0x00,0x40,0xA8,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x7F '' + 0x00,0x20,0x70,0xD8,0x88,0x88,0xF8,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs5x11_mono[] = + { + 11, 3, 32, 128-32, + 0x00,0x00,0x0C,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3C,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6C,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9C,0x00,0xA8,0x00,0xB4,0x00,0xC0,0x00,0xCC,0x00, + 0xD8,0x00,0xE4,0x00,0xF0,0x00,0xFC,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2C,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5C,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8C,0x01,0x98,0x01,0xA4,0x01, + 0xB0,0x01,0xBC,0x01,0xC8,0x01,0xD4,0x01,0xE0,0x01,0xEC,0x01,0xF8,0x01,0x04,0x02,0x10,0x02, + 0x1C,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4C,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7C,0x02, + 0x88,0x02,0x94,0x02,0xA0,0x02,0xAC,0x02,0xB8,0x02,0xC4,0x02,0xD0,0x02,0xDC,0x02,0xE8,0x02, + 0xF4,0x02,0x00,0x03,0x0C,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3C,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6C,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9C,0x03,0xA8,0x03,0xB4,0x03,0xC0,0x03, + 0xCC,0x03,0xD8,0x03,0xE4,0x03,0xF0,0x03,0xFC,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2C,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5C,0x04,0x68,0x04,0x74,0x04, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 5, // 0x22 '"' + 0x00,0x50,0x50,0xA0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x23 '#' + 0x00,0x50,0x50,0xF8,0x50,0x50,0x50,0xF8,0x50,0x50,0x00, + + 5, // 0x24 '$' + 0x00,0x40,0x60,0x90,0x80,0x60,0x10,0x90,0x60,0x20,0x00, + + 5, // 0x25 '%' + 0x00,0x00,0x90,0x90,0x20,0x20,0x40,0x40,0x90,0x90,0x00, + + 5, // 0x26 '&' + 0x00,0x40,0xA0,0xA0,0x40,0xA8,0x90,0x90,0x68,0x00,0x00, + + 5, // 0x27 ''' + 0x00,0x20,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x00,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 5, // 0x29 ')' + 0x00,0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x20,0x20,0x40, + + 5, // 0x2A '*' + 0x00,0x00,0x90,0x60,0xF0,0x60,0x90,0x00,0x00,0x00,0x00, + + 5, // 0x2B '+' + 0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x40,0x80, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 5, // 0x2F '/' + 0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00, + + 5, // 0x30 '0' + 0x00,0x70,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00,0x00, + + 5, // 0x31 '1' + 0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x32 '2' + 0x00,0x60,0x90,0x90,0x10,0x20,0x40,0x80,0xF0,0x00,0x00, + + 5, // 0x33 '3' + 0x00,0x60,0x90,0x10,0x60,0x10,0x10,0x90,0x60,0x00,0x00, + + 5, // 0x34 '4' + 0x00,0x10,0x30,0x50,0x50,0x90,0xF8,0x10,0x10,0x00,0x00, + + 5, // 0x35 '5' + 0x00,0xF0,0x80,0xE0,0x90,0x10,0x10,0x90,0x60,0x00,0x00, + + 5, // 0x36 '6' + 0x00,0x60,0x90,0x80,0xE0,0x90,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x37 '7' + 0x00,0xF0,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x00,0x00, + + 5, // 0x38 '8' + 0x00,0x60,0x90,0x90,0x60,0x90,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x39 '9' + 0x00,0x60,0x90,0x90,0x90,0x70,0x10,0x90,0x60,0x00,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x40,0x80, + + 5, // 0x3C '<' + 0x00,0x08,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x08,0x00, + + 5, // 0x3D '=' + 0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0xF0,0x00,0x00,0x00, + + 5, // 0x3E '>' + 0x00,0x80,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x80,0x00, + + 5, // 0x3F '?' + 0x00,0x60,0x90,0x10,0x10,0x20,0x40,0x00,0x40,0x00,0x00, + + 5, // 0x40 '@' + 0x00,0x60,0x90,0x90,0xB0,0xB0,0x80,0x80,0x70,0x00,0x00, + + 5, // 0x41 'A' + 0x00,0x60,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x42 'B' + 0x00,0xE0,0x90,0x90,0xE0,0x90,0x90,0x90,0xE0,0x00,0x00, + + 5, // 0x43 'C' + 0x00,0x60,0x90,0x80,0x80,0x80,0x80,0x90,0x60,0x00,0x00, + + 5, // 0x44 'D' + 0x00,0xE0,0x90,0x90,0x90,0x90,0x90,0x90,0xE0,0x00,0x00, + + 5, // 0x45 'E' + 0x00,0xF0,0x80,0x80,0xE0,0x80,0x80,0x80,0xF0,0x00,0x00, + + 5, // 0x46 'F' + 0x00,0xF0,0x80,0x80,0xE0,0x80,0x80,0x80,0x80,0x00,0x00, + + 5, // 0x47 'G' + 0x00,0x60,0x90,0x80,0x80,0xB0,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x48 'H' + 0x00,0x90,0x90,0x90,0xF0,0x90,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x4A 'J' + 0x00,0x70,0x20,0x20,0x20,0x20,0xA0,0xA0,0x40,0x00,0x00, + + 5, // 0x4B 'K' + 0x00,0x90,0xA0,0xA0,0xC0,0xA0,0xA0,0x90,0x90,0x00,0x00, + + 5, // 0x4C 'L' + 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xF0,0x00,0x00, + + 5, // 0x4D 'M' + 0x00,0x90,0xF0,0xF0,0x90,0x90,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x4E 'N' + 0x00,0x90,0x90,0xD0,0xD0,0xB0,0xB0,0x90,0x90,0x00,0x00, + + 5, // 0x4F 'O' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x50 'P' + 0x00,0xE0,0x90,0x90,0x90,0xE0,0x80,0x80,0x80,0x00,0x00, + + 5, // 0x51 'Q' + 0x00,0x60,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x30,0x00, + + 5, // 0x52 'R' + 0x00,0xE0,0x90,0x90,0x90,0xE0,0xA0,0x90,0x90,0x00,0x00, + + 5, // 0x53 'S' + 0x00,0x60,0x90,0x80,0x60,0x10,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x54 'T' + 0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00, + + 5, // 0x55 'U' + 0x00,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x56 'V' + 0x00,0x90,0x90,0x90,0x50,0x50,0x50,0x20,0x20,0x00,0x00, + + 5, // 0x57 'W' + 0x00,0x90,0x90,0x90,0x90,0x90,0xF0,0xF0,0x90,0x00,0x00, + + 5, // 0x58 'X' + 0x00,0x90,0x90,0x90,0x60,0x60,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x59 'Y' + 0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00,0x00, + + 5, // 0x5A 'Z' + 0x00,0xF0,0x10,0x20,0x20,0x40,0x40,0x80,0xF0,0x00,0x00, + + 5, // 0x5B '[' + 0x00,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x60, + + 5, // 0x5C '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00, + + 5, // 0x5D ']' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60, + + 5, // 0x5E '^' + 0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00, + + 5, // 0x60 '`' + 0x00,0x40,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x61 'a' + 0x00,0x00,0x00,0x60,0x10,0x70,0x90,0x90,0x70,0x00,0x00, + + 5, // 0x62 'b' + 0x00,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0xE0,0x00,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x00,0x60,0x90,0x80,0x80,0x90,0x60,0x00,0x00, + + 5, // 0x64 'd' + 0x00,0x10,0x10,0x70,0x90,0x90,0x90,0x90,0x70,0x00,0x00, + + 5, // 0x65 'e' + 0x00,0x00,0x00,0x60,0x90,0x90,0xF0,0x80,0x70,0x00,0x00, + + 5, // 0x66 'f' + 0x00,0x30,0x40,0xE0,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 5, // 0x67 'g' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x90,0x70,0x10,0xE0, + + 5, // 0x68 'h' + 0x00,0x80,0x80,0xE0,0x90,0x90,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x69 'i' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0xA0,0x40, + + 5, // 0x6B 'k' + 0x00,0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x90,0x00,0x00, + + 5, // 0x6C 'l' + 0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x6D 'm' + 0x00,0x00,0x00,0x90,0xF0,0x90,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x6E 'n' + 0x00,0x00,0x00,0xE0,0x90,0x90,0x90,0x90,0x90,0x00,0x00, + + 5, // 0x6F 'o' + 0x00,0x00,0x00,0x60,0x90,0x90,0x90,0x90,0x60,0x00,0x00, + + 5, // 0x70 'p' + 0x00,0x00,0x00,0xE0,0x90,0x90,0x90,0x90,0xE0,0x80,0x80, + + 5, // 0x71 'q' + 0x00,0x00,0x00,0x70,0x90,0x90,0x90,0x90,0x70,0x10,0x10, + + 5, // 0x72 'r' + 0x00,0x00,0x00,0xA0,0x50,0x40,0x40,0x40,0xE0,0x00,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x00,0x60,0x90,0x40,0x20,0x90,0x60,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x40,0x40,0xE0,0x40,0x40,0x40,0x40,0x30,0x00,0x00, + + 5, // 0x75 'u' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x90,0x70,0x00,0x00, + + 5, // 0x76 'v' + 0x00,0x00,0x00,0x90,0x90,0x50,0x50,0x20,0x20,0x00,0x00, + + 5, // 0x77 'w' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0xF0,0x90,0x00,0x00, + + 5, // 0x78 'x' + 0x00,0x00,0x00,0x90,0x90,0x60,0x60,0x90,0x90,0x00,0x00, + + 5, // 0x79 'y' + 0x00,0x00,0x00,0x90,0x90,0x90,0x90,0x90,0x70,0x10,0xE0, + + 5, // 0x7A 'z' + 0x00,0x00,0x00,0xF0,0x10,0x20,0x40,0x80,0xF0,0x00,0x00, + + 5, // 0x7B '{' + 0x30,0x40,0x40,0x40,0x40,0x80,0x40,0x40,0x40,0x40,0x30, + + 5, // 0x7C '|' + 0x00,0x20,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x20,0x00, + + 5, // 0x7D '}' + 0xC0,0x20,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0xC0, + + 5, // 0x7E '~' + 0x00,0x40,0xA8,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x7F '' + 0x00,0x20,0x70,0xD8,0x88,0x88,0xF8,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs6x10_mono[] = + { + 10, 3, 32, 128-32, + 0x00,0x00,0x0B,0x00,0x16,0x00,0x21,0x00,0x2C,0x00,0x37,0x00,0x42,0x00,0x4D,0x00,0x58,0x00, + 0x63,0x00,0x6E,0x00,0x79,0x00,0x84,0x00,0x8F,0x00,0x9A,0x00,0xA5,0x00,0xB0,0x00,0xBB,0x00, + 0xC6,0x00,0xD1,0x00,0xDC,0x00,0xE7,0x00,0xF2,0x00,0xFD,0x00,0x08,0x01,0x13,0x01,0x1E,0x01, + 0x29,0x01,0x34,0x01,0x3F,0x01,0x4A,0x01,0x55,0x01,0x60,0x01,0x6B,0x01,0x76,0x01,0x81,0x01, + 0x8C,0x01,0x97,0x01,0xA2,0x01,0xAD,0x01,0xB8,0x01,0xC3,0x01,0xCE,0x01,0xD9,0x01,0xE4,0x01, + 0xEF,0x01,0xFA,0x01,0x05,0x02,0x10,0x02,0x1B,0x02,0x26,0x02,0x31,0x02,0x3C,0x02,0x47,0x02, + 0x52,0x02,0x5D,0x02,0x68,0x02,0x73,0x02,0x7E,0x02,0x89,0x02,0x94,0x02,0x9F,0x02,0xAA,0x02, + 0xB5,0x02,0xC0,0x02,0xCB,0x02,0xD6,0x02,0xE1,0x02,0xEC,0x02,0xF7,0x02,0x02,0x03,0x0D,0x03, + 0x18,0x03,0x23,0x03,0x2E,0x03,0x39,0x03,0x44,0x03,0x4F,0x03,0x5A,0x03,0x65,0x03,0x70,0x03, + 0x7B,0x03,0x86,0x03,0x91,0x03,0x9C,0x03,0xA7,0x03,0xB2,0x03,0xBD,0x03,0xC8,0x03,0xD3,0x03, + 0xDE,0x03,0xE9,0x03,0xF4,0x03,0xFF,0x03,0x0A,0x04,0x15,0x04, + + 6, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00, + + 6, // 0x22 '"' + 0x00,0x28,0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x28,0x28,0x7C,0x28,0x28,0x7C,0x28,0x28,0x00, + + 6, // 0x24 '$' + 0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x00, + + 6, // 0x25 '%' + 0x00,0x08,0xC8,0xD0,0x10,0x20,0x2C,0x4C,0x40,0x00, + + 6, // 0x26 '&' + 0x00,0x20,0x50,0x50,0x24,0x54,0x48,0x34,0x00,0x00, + + 6, // 0x27 ''' + 0x00,0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x08,0x10,0x10,0x20,0x20,0x20,0x10,0x10,0x08,0x00, + + 6, // 0x29 ')' + 0x20,0x10,0x10,0x08,0x08,0x08,0x10,0x10,0x20,0x00, + + 6, // 0x2A '*' + 0x00,0x00,0x28,0x7C,0x38,0x7C,0x28,0x00,0x00,0x00, + + 6, // 0x2B '+' + 0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 6, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 6, // 0x2F '/' + 0x00,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00, + + 6, // 0x30 '0' + 0x00,0x38,0x44,0x4C,0x54,0x64,0x44,0x38,0x00,0x00, + + 6, // 0x31 '1' + 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x32 '2' + 0x00,0x38,0x44,0x04,0x18,0x20,0x40,0x7C,0x00,0x00, + + 6, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x38,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x48,0x7C,0x08,0x08,0x00,0x00, + + 6, // 0x35 '5' + 0x00,0x7C,0x40,0x40,0x78,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x36 '6' + 0x00,0x38,0x40,0x40,0x78,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x10,0x20,0x20,0x20,0x00,0x00, + + 6, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x3C,0x04,0x04,0x38,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x20,0x40, + + 6, // 0x3C '<' + 0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00, + + 6, // 0x3D '=' + 0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00, + + 6, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x18,0x10,0x00,0x10,0x00,0x00, + + 6, // 0x40 '@' + 0x00,0x38,0x44,0x5C,0x54,0x5C,0x40,0x38,0x00,0x00, + + 6, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x00,0x00, + + 6, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 6, // 0x44 'D' + 0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x4C,0x44,0x44,0x3C,0x00,0x00, + + 6, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00, + + 6, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x4D 'M' + 0x00,0x44,0x6C,0x54,0x54,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x4E 'N' + 0x00,0x44,0x44,0x64,0x54,0x4C,0x44,0x44,0x00,0x00, + + 6, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00, + + 6, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00, + + 6, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x10,0x00,0x00, + + 6, // 0x57 'W' + 0x00,0x44,0x44,0x54,0x54,0x54,0x54,0x28,0x00,0x00, + + 6, // 0x58 'X' + 0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 6, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x5A 'Z' + 0x00,0x78,0x08,0x10,0x20,0x40,0x40,0x78,0x00,0x00, + + 6, // 0x5B '[' + 0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, + + 6, // 0x5C '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00, + + 6, // 0x5D ']' + 0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00, + + 6, // 0x5E '^' + 0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00, + + 6, // 0x60 '`' + 0x00,0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x3C,0x00,0x00, + + 6, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x3C,0x00,0x00, + + 6, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x3C,0x00,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x78,0x40,0x3C,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x0C,0x10,0x10,0x38,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x3C,0x04,0x38, + + 6, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x69 'i' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x6A 'j' + 0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x48,0x30, + + 6, // 0x6B 'k' + 0x00,0x40,0x40,0x48,0x50,0x60,0x50,0x48,0x00,0x00, + + 6, // 0x6C 'l' + 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x6D 'm' + 0x00,0x00,0x00,0x68,0x54,0x54,0x44,0x44,0x00,0x00, + + 6, // 0x6E 'n' + 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x3C,0x04,0x04, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x38,0x40,0x38,0x04,0x78,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x10,0x10,0x38,0x10,0x10,0x14,0x08,0x00,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x4C,0x34,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x10,0x00,0x00, + + 6, // 0x77 'w' + 0x00,0x00,0x00,0x44,0x44,0x54,0x7C,0x28,0x00,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x48,0x48,0x30,0x48,0x48,0x00,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x3C,0x04,0x38, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x78,0x08,0x30,0x40,0x78,0x00,0x00, + + 6, // 0x7B '{' + 0x18,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x18,0x00, + + 6, // 0x7C '|' + 0x10,0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x10,0x00, + + 6, // 0x7D '}' + 0x60,0x10,0x10,0x10,0x0C,0x10,0x10,0x10,0x60,0x00, + + 6, // 0x7E '~' + 0x00,0x48,0xA8,0x90,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs6x11_mono[] = + { + 11, 3, 32, 128-32, + 0x00,0x00,0x0C,0x00,0x18,0x00,0x24,0x00,0x30,0x00,0x3C,0x00,0x48,0x00,0x54,0x00,0x60,0x00, + 0x6C,0x00,0x78,0x00,0x84,0x00,0x90,0x00,0x9C,0x00,0xA8,0x00,0xB4,0x00,0xC0,0x00,0xCC,0x00, + 0xD8,0x00,0xE4,0x00,0xF0,0x00,0xFC,0x00,0x08,0x01,0x14,0x01,0x20,0x01,0x2C,0x01,0x38,0x01, + 0x44,0x01,0x50,0x01,0x5C,0x01,0x68,0x01,0x74,0x01,0x80,0x01,0x8C,0x01,0x98,0x01,0xA4,0x01, + 0xB0,0x01,0xBC,0x01,0xC8,0x01,0xD4,0x01,0xE0,0x01,0xEC,0x01,0xF8,0x01,0x04,0x02,0x10,0x02, + 0x1C,0x02,0x28,0x02,0x34,0x02,0x40,0x02,0x4C,0x02,0x58,0x02,0x64,0x02,0x70,0x02,0x7C,0x02, + 0x88,0x02,0x94,0x02,0xA0,0x02,0xAC,0x02,0xB8,0x02,0xC4,0x02,0xD0,0x02,0xDC,0x02,0xE8,0x02, + 0xF4,0x02,0x00,0x03,0x0C,0x03,0x18,0x03,0x24,0x03,0x30,0x03,0x3C,0x03,0x48,0x03,0x54,0x03, + 0x60,0x03,0x6C,0x03,0x78,0x03,0x84,0x03,0x90,0x03,0x9C,0x03,0xA8,0x03,0xB4,0x03,0xC0,0x03, + 0xCC,0x03,0xD8,0x03,0xE4,0x03,0xF0,0x03,0xFC,0x03,0x08,0x04,0x14,0x04,0x20,0x04,0x2C,0x04, + 0x38,0x04,0x44,0x04,0x50,0x04,0x5C,0x04,0x68,0x04,0x74,0x04, + + 6, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 6, // 0x22 '"' + 0x00,0x28,0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x23 '#' + 0x00,0x28,0x28,0x7C,0x28,0x28,0x7C,0x28,0x28,0x00,0x00, + + 6, // 0x24 '$' + 0x00,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x00, + + 6, // 0x25 '%' + 0x00,0x68,0xA8,0xD0,0x10,0x20,0x2C,0x54,0x58,0x00,0x00, + + 6, // 0x26 '&' + 0x00,0x20,0x50,0x50,0x20,0x54,0x54,0x48,0x34,0x00,0x00, + + 6, // 0x27 ''' + 0x00,0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x10,0x10,0x08,0x00, + + 6, // 0x29 ')' + 0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x10,0x10,0x20,0x00, + + 6, // 0x2A '*' + 0x00,0x00,0x28,0x7C,0x38,0x7C,0x28,0x00,0x00,0x00,0x00, + + 6, // 0x2B '+' + 0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 6, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 6, // 0x2F '/' + 0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00, + + 6, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x31 '1' + 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x32 '2' + 0x00,0x38,0x44,0x44,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 6, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x28,0x48,0x7C,0x08,0x08,0x00,0x00, + + 6, // 0x35 '5' + 0x00,0x7C,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x00,0x00, + + 6, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x3C,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x20,0x40, + + 6, // 0x3C '<' + 0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00, + + 6, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00, + + 6, // 0x3E '>' + 0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00, + + 6, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00, + + 6, // 0x40 '@' + 0x00,0x38,0x44,0x5C,0x54,0x54,0x4C,0x40,0x38,0x00,0x00, + + 6, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 6, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5C,0x44,0x4C,0x34,0x00,0x00, + + 6, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00, + + 6, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x44,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x4D 'M' + 0x00,0x44,0x6C,0x54,0x54,0x54,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x4E 'N' + 0x00,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x00,0x00, + + 6, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00, + + 6, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00, + + 6, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00, + + 6, // 0x57 'W' + 0x00,0x44,0x44,0x54,0x54,0x54,0x54,0x54,0x28,0x00,0x00, + + 6, // 0x58 'X' + 0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x5A 'Z' + 0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x5B '[' + 0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, + + 6, // 0x5C '\' + 0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00, + + 6, // 0x5D ']' + 0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00, + + 6, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00, + + 6, // 0x60 '`' + 0x00,0x20,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x3C,0x00,0x00, + + 6, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x44,0x38,0x00,0x00, + + 6, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x3C,0x00,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x7C,0x40,0x44,0x38,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x0C,0x10,0x38,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x78, + + 6, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x69 'i' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x6A 'j' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x50,0x20, + + 6, // 0x6B 'k' + 0x00,0x40,0x40,0x4C,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 6, // 0x6C 'l' + 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 6, // 0x6D 'm' + 0x00,0x00,0x00,0x68,0x54,0x54,0x54,0x44,0x44,0x00,0x00, + + 6, // 0x6E 'n' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 6, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x04, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x70,0x00,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x30,0x08,0x44,0x38,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x18,0x00,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, + + 6, // 0x77 'w' + 0x00,0x00,0x00,0x44,0x44,0x44,0x54,0x7C,0x28,0x00,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x3C,0x08,0x70, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 6, // 0x7B '{' + 0x18,0x20,0x20,0x20,0xC0,0xC0,0x20,0x20,0x20,0x18,0x00, + + 6, // 0x7C '|' + 0x00,0x10,0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x10,0x00, + + 6, // 0x7D '}' + 0x60,0x10,0x10,0x10,0x0C,0x0C,0x10,0x10,0x10,0x60,0x00, + + 6, // 0x7E '~' + 0x00,0x24,0x54,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs7x12_mono_high[] = + { + 12, 3, 32, 128-32, + 0x00,0x00,0x0D,0x00,0x1A,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x4E,0x00,0x5B,0x00,0x68,0x00, + 0x75,0x00,0x82,0x00,0x8F,0x00,0x9C,0x00,0xA9,0x00,0xB6,0x00,0xC3,0x00,0xD0,0x00,0xDD,0x00, + 0xEA,0x00,0xF7,0x00,0x04,0x01,0x11,0x01,0x1E,0x01,0x2B,0x01,0x38,0x01,0x45,0x01,0x52,0x01, + 0x5F,0x01,0x6C,0x01,0x79,0x01,0x86,0x01,0x93,0x01,0xA0,0x01,0xAD,0x01,0xBA,0x01,0xC7,0x01, + 0xD4,0x01,0xE1,0x01,0xEE,0x01,0xFB,0x01,0x08,0x02,0x15,0x02,0x22,0x02,0x2F,0x02,0x3C,0x02, + 0x49,0x02,0x56,0x02,0x63,0x02,0x70,0x02,0x7D,0x02,0x8A,0x02,0x97,0x02,0xA4,0x02,0xB1,0x02, + 0xBE,0x02,0xCB,0x02,0xD8,0x02,0xE5,0x02,0xF2,0x02,0xFF,0x02,0x0C,0x03,0x19,0x03,0x26,0x03, + 0x33,0x03,0x40,0x03,0x4D,0x03,0x5A,0x03,0x67,0x03,0x74,0x03,0x81,0x03,0x8E,0x03,0x9B,0x03, + 0xA8,0x03,0xB5,0x03,0xC2,0x03,0xCF,0x03,0xDC,0x03,0xE9,0x03,0xF6,0x03,0x03,0x04,0x10,0x04, + 0x1D,0x04,0x2A,0x04,0x37,0x04,0x44,0x04,0x51,0x04,0x5E,0x04,0x6B,0x04,0x78,0x04,0x85,0x04, + 0x92,0x04,0x9F,0x04,0xAC,0x04,0xB9,0x04,0xC6,0x04,0xD3,0x04, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x22 '"' + 0x28,0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x24,0x24,0x24,0x7E,0x24,0x24,0x24,0x7E,0x24,0x24,0x24,0x00, + + 7, // 0x24 '$' + 0x10,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x10,0x00, + + 7, // 0x25 '%' + 0x32,0x54,0x64,0x08,0x08,0x10,0x10,0x26,0x2A,0x4C,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x20,0x50,0x50,0x50,0x20,0x54,0x54,0x48,0x34,0x00,0x00, + + 7, // 0x27 ''' + 0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x20,0x10,0x10,0x08,0x00, + + 7, // 0x29 ')' + 0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x10,0x20,0x00, + + 7, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x00, + + 7, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 7, // 0x2F '/' + 0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x54,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x04,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x28,0x48,0x48,0x7C,0x08,0x08,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x7C,0x40,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x78,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x3A ':' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x00, + + 7, // 0x3B ';' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x20,0x40, + + 7, // 0x3C '<' + 0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, + + 7, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,0x00, + + 7, // 0x3E '>' + 0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x38,0x44,0x44,0x5C,0x54,0x54,0x4C,0x40,0x38,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x5C,0x44,0x44,0x4C,0x34,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00, + + 7, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x60,0x50,0x48,0x44,0x44,0x00,0x00, + + 7, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 7, // 0x4D 'M' + 0x00,0x44,0x6C,0x6C,0x54,0x54,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x4E 'N' + 0x00,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x44,0x00,0x00, + + 7, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x40,0x38,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x54,0x54,0x28,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x44,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x5A 'Z' + 0x00,0x7C,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00, + + 7, // 0x5B '[' + 0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, + + 7, // 0x5C '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00, + + 7, // 0x5D ']' + 0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00, + + 7, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x20,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x44,0x7C,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x0C,0x10,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x78, + + 7, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x6A 'j' + 0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x48,0x30, + + 7, // 0x6B 'k' + 0x00,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 7, // 0x6C 'l' + 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x6D 'm' + 0x00,0x00,0x00,0x68,0x54,0x54,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x6E 'n' + 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x04, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x20,0x24,0x18,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x54,0x54,0x28,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x3C,0x08,0x70, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 7, // 0x7B '{' + 0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x20,0x18,0x00, + + 7, // 0x7C '|' + 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00, + + 7, // 0x7D '}' + 0x60,0x10,0x10,0x10,0x10,0x0C,0x10,0x10,0x10,0x10,0x60,0x00, + + 7, // 0x7E '~' + 0x00,0x60,0x92,0x92,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u mcs7x12_mono_low[] = + { + 12, 4, 32, 128-32, + 0x00,0x00,0x0D,0x00,0x1A,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x4E,0x00,0x5B,0x00,0x68,0x00, + 0x75,0x00,0x82,0x00,0x8F,0x00,0x9C,0x00,0xA9,0x00,0xB6,0x00,0xC3,0x00,0xD0,0x00,0xDD,0x00, + 0xEA,0x00,0xF7,0x00,0x04,0x01,0x11,0x01,0x1E,0x01,0x2B,0x01,0x38,0x01,0x45,0x01,0x52,0x01, + 0x5F,0x01,0x6C,0x01,0x79,0x01,0x86,0x01,0x93,0x01,0xA0,0x01,0xAD,0x01,0xBA,0x01,0xC7,0x01, + 0xD4,0x01,0xE1,0x01,0xEE,0x01,0xFB,0x01,0x08,0x02,0x15,0x02,0x22,0x02,0x2F,0x02,0x3C,0x02, + 0x49,0x02,0x56,0x02,0x63,0x02,0x70,0x02,0x7D,0x02,0x8A,0x02,0x97,0x02,0xA4,0x02,0xB1,0x02, + 0xBE,0x02,0xCB,0x02,0xD8,0x02,0xE5,0x02,0xF2,0x02,0xFF,0x02,0x0C,0x03,0x19,0x03,0x26,0x03, + 0x33,0x03,0x40,0x03,0x4D,0x03,0x5A,0x03,0x67,0x03,0x74,0x03,0x81,0x03,0x8E,0x03,0x9B,0x03, + 0xA8,0x03,0xB5,0x03,0xC2,0x03,0xCF,0x03,0xDC,0x03,0xE9,0x03,0xF6,0x03,0x03,0x04,0x10,0x04, + 0x1D,0x04,0x2A,0x04,0x37,0x04,0x44,0x04,0x51,0x04,0x5E,0x04,0x6B,0x04,0x78,0x04,0x85,0x04, + 0x92,0x04,0x9F,0x04,0xAC,0x04,0xB9,0x04,0xC6,0x04,0xD3,0x04, + + 7, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x21 '!' + 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x22 '"' + 0x28,0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x28,0x28,0x7C,0x28,0x28,0x28,0x7C,0x28,0x28,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x10,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x10,0x00,0x00, + + 7, // 0x25 '%' + 0x34,0x54,0x68,0x08,0x10,0x10,0x20,0x2C,0x54,0x58,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x20,0x50,0x50,0x20,0x54,0x54,0x48,0x34,0x00,0x00,0x00, + + 7, // 0x27 ''' + 0x00,0x10,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x20,0x10,0x10,0x08,0x00, + + 7, // 0x29 ')' + 0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x10,0x20,0x00, + + 7, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x00, + + 7, // 0x2B '+' + 0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00,0x00, + + 7, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x20,0x40,0x00, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x2F '/' + 0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,0x00, + + 7, // 0x30 '0' + 0x00,0x38,0x44,0x44,0x54,0x54,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x38,0x44,0x44,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x38,0x44,0x04,0x38,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x08,0x18,0x28,0x28,0x48,0x7C,0x08,0x08,0x00,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x7C,0x40,0x78,0x44,0x04,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x38,0x44,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x00,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x3A ':' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x3B ';' + 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x20,0x40,0x00, + + 7, // 0x3C '<' + 0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, + + 7, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,0x00, + + 7, // 0x3E '>' + 0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00, + + 7, // 0x40 '@' + 0x00,0x38,0x44,0x44,0x5C,0x54,0x4C,0x40,0x38,0x00,0x00,0x00, + + 7, // 0x41 'A' + 0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 7, // 0x43 'C' + 0x00,0x38,0x44,0x40,0x40,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x38,0x44,0x40,0x40,0x4C,0x44,0x4C,0x34,0x00,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x49 'I' + 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x4A 'J' + 0x00,0x1C,0x08,0x08,0x08,0x08,0x48,0x48,0x30,0x00,0x00,0x00, + + 7, // 0x4B 'K' + 0x00,0x44,0x48,0x50,0x60,0x60,0x50,0x48,0x44,0x00,0x00,0x00, + + 7, // 0x4C 'L' + 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x4D 'M' + 0x00,0x44,0x6C,0x54,0x54,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x4E 'N' + 0x00,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x00,0x00,0x00, + + 7, // 0x4F 'O' + 0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x51 'Q' + 0x00,0x38,0x44,0x44,0x44,0x44,0x54,0x48,0x34,0x00,0x00,0x00, + + 7, // 0x52 'R' + 0x00,0x78,0x44,0x44,0x44,0x78,0x48,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x57 'W' + 0x00,0x44,0x44,0x44,0x44,0x44,0x54,0x54,0x28,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x44,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x5A 'Z' + 0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x5B '[' + 0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, + + 7, // 0x5C '\' + 0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00, + + 7, // 0x5D ']' + 0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,0x00, + + 7, // 0x5E '^' + 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00, + + 7, // 0x60 '`' + 0x00,0x20,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x3C,0x00,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x78,0x00,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x3C,0x00,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x38,0x44,0x7C,0x40,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x66 'f' + 0x00,0x0C,0x10,0x38,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x44,0x38, + + 7, // 0x68 'h' + 0x00,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x69 'i' + 0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x6A 'j' + 0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x48,0x48,0x30, + + 7, // 0x6B 'k' + 0x00,0x40,0x40,0x4C,0x50,0x60,0x50,0x48,0x44,0x00,0x00,0x00, + + 7, // 0x6C 'l' + 0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00, + + 7, // 0x6D 'm' + 0x00,0x00,0x00,0x68,0x54,0x54,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x6E 'n' + 0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x6F 'o' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x04,0x04, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x58,0x24,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x38,0x44,0x30,0x08,0x44,0x38,0x00,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x24,0x18,0x00,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x44,0x44,0x44,0x54,0x54,0x28,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x08,0x70, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x7C,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,0x00, + + 7, // 0x7B '{' + 0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x20,0x18,0x00, + + 7, // 0x7C '|' + 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00, + + 7, // 0x7D '}' + 0x60,0x10,0x10,0x10,0x10,0x0C,0x10,0x10,0x10,0x10,0x60,0x00, + + 7, // 0x7E '~' + 0x00,0x24,0x54,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x7F '' + 0x00,0x10,0x38,0x6C,0x44,0x44,0x7C,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana12[] = + { + 12, 3, 32, 128-32, + 0x00,0x00,0x0D,0x00,0x1A,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x5A,0x00,0x67,0x00,0x74,0x00, + 0x81,0x00,0x8E,0x00,0x9B,0x00,0xA8,0x00,0xB5,0x00,0xC2,0x00,0xCF,0x00,0xDC,0x00,0xE9,0x00, + 0xF6,0x00,0x03,0x01,0x10,0x01,0x1D,0x01,0x2A,0x01,0x37,0x01,0x44,0x01,0x51,0x01,0x5E,0x01, + 0x6B,0x01,0x78,0x01,0x85,0x01,0x92,0x01,0x9F,0x01,0xAC,0x01,0xC5,0x01,0xD2,0x01,0xDF,0x01, + 0xEC,0x01,0xF9,0x01,0x06,0x02,0x13,0x02,0x20,0x02,0x2D,0x02,0x3A,0x02,0x47,0x02,0x54,0x02, + 0x61,0x02,0x7A,0x02,0x87,0x02,0xA0,0x02,0xAD,0x02,0xC6,0x02,0xD3,0x02,0xE0,0x02,0xED,0x02, + 0xFA,0x02,0x07,0x03,0x20,0x03,0x2D,0x03,0x3A,0x03,0x47,0x03,0x54,0x03,0x61,0x03,0x6E,0x03, + 0x7B,0x03,0x88,0x03,0x95,0x03,0xA2,0x03,0xAF,0x03,0xBC,0x03,0xC9,0x03,0xD6,0x03,0xE3,0x03, + 0xF0,0x03,0xFD,0x03,0x0A,0x04,0x17,0x04,0x24,0x04,0x31,0x04,0x4A,0x04,0x57,0x04,0x64,0x04, + 0x71,0x04,0x7E,0x04,0x8B,0x04,0x98,0x04,0xA5,0x04,0xB2,0x04,0xBF,0x04,0xCC,0x04,0xD9,0x04, + 0xE6,0x04,0xF3,0x04,0x00,0x05,0x0D,0x05,0x1A,0x05,0x27,0x05, + + 3, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 5, // 0x22 '"' + 0x00,0x00,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x00,0x10,0x10,0x3C,0x50,0x30,0x18,0x14,0x78,0x10,0x10, + + 11, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x4A,0x00,0x4A,0x00,0x35,0x80,0x0A,0x40,0x0A,0x40,0x11,0x80,0x00,0x00,0x00,0x00, + + 7, // 0x26 '&' + 0x00,0x00,0x00,0x30,0x48,0x48,0x32,0x4A,0x44,0x3A,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x00,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x28 '(' + 0x00,0x00,0x10,0x20,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x10, + + 4, // 0x29 ')' + 0x00,0x00,0x80,0x40,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x80, + + 7, // 0x2A '*' + 0x00,0x10,0x54,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 3, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80,0x00, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x00,0x00, + + 4, // 0x2F '/' + 0x00,0x00,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x80,0x00, + + 7, // 0x30 '0' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x00,0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x38,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x00,0x00,0x38,0x44,0x04,0x08,0x10,0x20,0x7C,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x00,0x00,0x38,0x44,0x04,0x18,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x00,0x00,0x08,0x18,0x28,0x48,0x7C,0x08,0x08,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x00,0x00,0x7C,0x40,0x78,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x00,0x00,0x18,0x20,0x40,0x78,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x00,0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x00,0x00,0x38,0x44,0x44,0x3C,0x04,0x08,0x30,0x00,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x00,0x40,0x40,0x80,0x00, + + 7, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x04,0x18,0x60,0x18,0x04,0x00,0x00,0x00, + + 7, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x7C,0x00,0x00,0x00,0x00, + + 7, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x40,0x30,0x0C,0x30,0x40,0x00,0x00,0x00, + + 6, // 0x3F '?' + 0x00,0x00,0x00,0x70,0x08,0x08,0x10,0x20,0x00,0x20,0x00,0x00, + + 10, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x20,0x80,0x4E,0x80,0x52,0x80,0x52,0x80,0x4D,0x00,0x20,0x00,0x1F,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x00,0x18,0x18,0x24,0x24,0x7E,0x42,0x42,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x00,0x00,0x70,0x48,0x48,0x78,0x44,0x44,0x78,0x00,0x00, + + 8, // 0x43 'C' + 0x00,0x00,0x00,0x1C,0x22,0x40,0x40,0x40,0x22,0x1C,0x00,0x00, + + 8, // 0x44 'D' + 0x00,0x00,0x00,0x78,0x44,0x42,0x42,0x42,0x44,0x78,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x00,0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x00,0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x00,0x00, + + 8, // 0x47 'G' + 0x00,0x00,0x00,0x1C,0x22,0x40,0x4E,0x42,0x22,0x1C,0x00,0x00, + + 8, // 0x48 'H' + 0x00,0x00,0x00,0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x4A 'J' + 0x00,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0xE0,0x00,0x00, + + 7, // 0x4B 'K' + 0x00,0x00,0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 9, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x55,0x00,0x55,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4E 'N' + 0x00,0x00,0x00,0x42,0x62,0x52,0x4A,0x46,0x42,0x42,0x00,0x00, + + 9, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x00,0x00, + + 9, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x04,0x00,0x03,0x00, + + 7, // 0x52 'R' + 0x00,0x00,0x00,0x78,0x44,0x44,0x78,0x50,0x48,0x44,0x00,0x00, + + 7, // 0x53 'S' + 0x00,0x00,0x00,0x38,0x44,0x40,0x38,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x00,0x00,0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 8, // 0x55 'U' + 0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x00,0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00, + + 9, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x49,0x00,0x49,0x00,0x55,0x00,0x55,0x00,0x22,0x00,0x22,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x10,0x10,0x00,0x00, + + 7, // 0x5A 'Z' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 4, // 0x5B '[' + 0x00,0x00,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x60, + + 4, // 0x5C '\' + 0x00,0x00,0x80,0x80,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x00, + + 4, // 0x5D ']' + 0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x60, + + 7, // 0x5E '^' + 0x00,0x00,0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, + + 6, // 0x60 '`' + 0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x30,0x08,0x38,0x48,0x38,0x00,0x00, + + 6, // 0x62 'b' + 0x00,0x00,0x40,0x40,0x40,0x70,0x48,0x48,0x48,0x70,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x40,0x40,0x38,0x00,0x00, + + 6, // 0x64 'd' + 0x00,0x00,0x08,0x08,0x08,0x38,0x48,0x48,0x48,0x38,0x00,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x78,0x40,0x38,0x00,0x00, + + 4, // 0x66 'f' + 0x00,0x00,0x30,0x40,0x40,0xE0,0x40,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x30, + + 6, // 0x68 'h' + 0x00,0x00,0x40,0x40,0x40,0x70,0x48,0x48,0x48,0x48,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 3, // 0x6A 'j' + 0x00,0x00,0x00,0x40,0x00,0xC0,0x40,0x40,0x40,0x40,0x40,0x80, + + 6, // 0x6B 'k' + 0x00,0x00,0x40,0x40,0x40,0x48,0x50,0x60,0x50,0x48,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 9, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x49,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x70,0x48,0x48,0x48,0x48,0x00,0x00, + + 6, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x48,0x48,0x30,0x00,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x70,0x48,0x48,0x48,0x70,0x40,0x40, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x08, + + 4, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x50,0x60,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x30,0x08,0x70,0x00,0x00, + + 4, // 0x74 't' + 0x00,0x00,0x00,0x00,0x40,0xF0,0x40,0x40,0x40,0x30,0x00,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x48,0x48,0x48,0x48,0x38,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x48,0x48,0x48,0x30,0x30,0x00,0x00, + + 7, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x44,0x54,0x54,0x28,0x28,0x00,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x48,0x48,0x30,0x48,0x48,0x00,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x48,0x48,0x48,0x30,0x10,0x20,0x20, + + 5, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x70,0x10,0x20,0x40,0x70,0x00,0x00, + + 6, // 0x7B '{' + 0x00,0x00,0x18,0x20,0x20,0x20,0x20,0xC0,0x20,0x20,0x20,0x18, + + 5, // 0x7C '|' + 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + + 6, // 0x7D '}' + 0x00,0x00,0x60,0x10,0x10,0x10,0x10,0x0C,0x10,0x10,0x10,0x60, + + 7, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x58,0x00,0x00,0x00,0x00, + + 9, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7F,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana12_bold[] = + { + 12, 3, 32, 128-32, + 0x00,0x00,0x0D,0x00,0x1A,0x00,0x27,0x00,0x34,0x00,0x41,0x00,0x5A,0x00,0x67,0x00,0x74,0x00, + 0x81,0x00,0x8E,0x00,0x9B,0x00,0xA8,0x00,0xB5,0x00,0xC2,0x00,0xCF,0x00,0xDC,0x00,0xE9,0x00, + 0xF6,0x00,0x03,0x01,0x10,0x01,0x1D,0x01,0x2A,0x01,0x37,0x01,0x44,0x01,0x51,0x01,0x5E,0x01, + 0x6B,0x01,0x78,0x01,0x85,0x01,0x92,0x01,0x9F,0x01,0xAC,0x01,0xC5,0x01,0xD2,0x01,0xDF,0x01, + 0xEC,0x01,0xF9,0x01,0x06,0x02,0x13,0x02,0x20,0x02,0x2D,0x02,0x3A,0x02,0x47,0x02,0x54,0x02, + 0x61,0x02,0x6E,0x02,0x7B,0x02,0x88,0x02,0x95,0x02,0xA2,0x02,0xAF,0x02,0xBC,0x02,0xC9,0x02, + 0xD6,0x02,0xE3,0x02,0xFC,0x02,0x09,0x03,0x16,0x03,0x23,0x03,0x30,0x03,0x3D,0x03,0x4A,0x03, + 0x57,0x03,0x64,0x03,0x71,0x03,0x7E,0x03,0x8B,0x03,0x98,0x03,0xA5,0x03,0xB2,0x03,0xBF,0x03, + 0xCC,0x03,0xD9,0x03,0xE6,0x03,0xF3,0x03,0x00,0x04,0x0D,0x04,0x26,0x04,0x33,0x04,0x40,0x04, + 0x4D,0x04,0x5A,0x04,0x67,0x04,0x74,0x04,0x81,0x04,0x8E,0x04,0x9B,0x04,0xB4,0x04,0xC1,0x04, + 0xCE,0x04,0xDB,0x04,0xE8,0x04,0xF5,0x04,0x02,0x05,0x0F,0x05, + + 3, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x00,0x60,0x00,0x00, + + 5, // 0x22 '"' + 0x00,0x00,0xD8,0xD8,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x23 '#' + 0x00,0x00,0x00,0x14,0x14,0x7E,0x28,0xFC,0x50,0x50,0x00,0x00, + + 6, // 0x24 '$' + 0x00,0x00,0x20,0x20,0x70,0xE8,0xE0,0x38,0xB8,0x70,0x20,0x20, + + 11, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x62,0x00,0x94,0x00,0x94,0x00,0x69,0x80,0x0A,0x40,0x0A,0x40,0x11,0x80,0x00,0x00,0x00,0x00, + + 8, // 0x26 '&' + 0x00,0x00,0x00,0x70,0xD8,0xD8,0x76,0xDC,0xCC,0x76,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x00,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x00,0x00,0x30,0x60,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x60,0x30, + + 5, // 0x29 ')' + 0x00,0x00,0xC0,0x60,0x30,0x30,0x30,0x30,0x30,0x30,0x60,0xC0, + + 6, // 0x2A '*' + 0x00,0x00,0x20,0xA8,0x70,0xA8,0x20,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00, + + 3, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x80,0x00, + + 4, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x00,0x00, + + 6, // 0x2F '/' + 0x00,0x00,0x08,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x80,0x00, + + 6, // 0x30 '0' + 0x00,0x00,0x00,0x70,0xD8,0xD8,0xD8,0xD8,0xD8,0x70,0x00,0x00, + + 6, // 0x31 '1' + 0x00,0x00,0x00,0x30,0x70,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 6, // 0x32 '2' + 0x00,0x00,0x00,0x70,0x98,0x18,0x30,0x60,0xC0,0xF8,0x00,0x00, + + 6, // 0x33 '3' + 0x00,0x00,0x00,0x70,0x98,0x18,0x70,0x18,0x98,0x70,0x00,0x00, + + 6, // 0x34 '4' + 0x00,0x00,0x00,0x18,0x38,0x58,0x98,0xFC,0x18,0x18,0x00,0x00, + + 6, // 0x35 '5' + 0x00,0x00,0x00,0xF8,0xC0,0xF0,0x18,0x18,0x98,0x70,0x00,0x00, + + 6, // 0x36 '6' + 0x00,0x00,0x00,0x70,0xC0,0xF0,0xD8,0xD8,0xD8,0x70,0x00,0x00, + + 6, // 0x37 '7' + 0x00,0x00,0x00,0xF8,0x18,0x30,0x30,0x60,0x60,0xC0,0x00,0x00, + + 6, // 0x38 '8' + 0x00,0x00,0x00,0x70,0xD8,0xD8,0x70,0xD8,0xD8,0x70,0x00,0x00, + + 6, // 0x39 '9' + 0x00,0x00,0x00,0x70,0xD8,0xD8,0xD8,0x78,0x18,0x70,0x00,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x00,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x40,0x00, + + 8, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x04,0x18,0x60,0x60,0x18,0x04,0x00,0x00, + + 8, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x7C,0x00,0x00,0x00,0x00, + + 8, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x40,0x30,0x0C,0x0C,0x30,0x40,0x00,0x00, + + 6, // 0x3F '?' + 0x00,0x00,0x00,0xF0,0x18,0x18,0x30,0x60,0x00,0x60,0x00,0x00, + + 9, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x42,0x00,0x9D,0x00,0xA5,0x00,0xA5,0x00,0x9E,0x00,0x40,0x00,0x3C,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x00,0x38,0x38,0x6C,0x6C,0x7C,0xC6,0xC6,0x00,0x00, + + 7, // 0x42 'B' + 0x00,0x00,0x00,0xF8,0xCC,0xCC,0xF8,0xCC,0xCC,0xF8,0x00,0x00, + + 6, // 0x43 'C' + 0x00,0x00,0x00,0x70,0xC8,0xC0,0xC0,0xC0,0xC8,0x70,0x00,0x00, + + 7, // 0x44 'D' + 0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0xCC,0xF8,0x00,0x00, + + 6, // 0x45 'E' + 0x00,0x00,0x00,0xF8,0xC0,0xC0,0xF8,0xC0,0xC0,0xF8,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x00,0x00,0xF8,0xC0,0xC0,0xF8,0xC0,0xC0,0xC0,0x00,0x00, + + 7, // 0x47 'G' + 0x00,0x00,0x00,0x78,0xC4,0xC0,0xC0,0xDC,0xCC,0x7C,0x00,0x00, + + 7, // 0x48 'H' + 0x00,0x00,0x00,0xCC,0xCC,0xCC,0xFC,0xCC,0xCC,0xCC,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0xF0,0x00,0x00, + + 5, // 0x4A 'J' + 0x00,0x00,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0xE0,0x00,0x00, + + 7, // 0x4B 'K' + 0x00,0x00,0x00,0xCC,0xD8,0xF0,0xE0,0xF0,0xD8,0xCC,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xF8,0x00,0x00, + + 8, // 0x4D 'M' + 0x00,0x00,0x00,0x82,0xC6,0xEE,0xB6,0xB6,0x86,0x86,0x00,0x00, + + 7, // 0x4E 'N' + 0x00,0x00,0x00,0x84,0xC4,0xE4,0xB4,0x9C,0x8C,0x84,0x00,0x00, + + 8, // 0x4F 'O' + 0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xF8,0xC0,0xC0,0x00,0x00, + + 8, // 0x51 'Q' + 0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x18,0x0E, + + 7, // 0x52 'R' + 0x00,0x00,0x00,0xF8,0xCC,0xCC,0xF8,0xD8,0xCC,0xC6,0x00,0x00, + + 6, // 0x53 'S' + 0x00,0x00,0x00,0x70,0xC8,0xC0,0x70,0x18,0x98,0x70,0x00,0x00, + + 6, // 0x54 'T' + 0x00,0x00,0x00,0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00, + + 7, // 0x55 'U' + 0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x00,0x00, + + 7, // 0x56 'V' + 0x00,0x00,0x00,0xCC,0xCC,0x78,0x78,0x78,0x30,0x30,0x00,0x00, + + 11, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0xCC,0xC0,0xCC,0xC0,0x6D,0x80,0x6D,0x80,0x73,0x80,0x33,0x00,0x33,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x58 'X' + 0x00,0x00,0x00,0xCC,0xCC,0x78,0x30,0x78,0xCC,0xCC,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0x00,0xCC,0xCC,0x78,0x30,0x30,0x30,0x30,0x00,0x00, + + 6, // 0x5A 'Z' + 0x00,0x00,0x00,0xF8,0x18,0x30,0x60,0xC0,0xC0,0xF8,0x00,0x00, + + 5, // 0x5B '[' + 0x00,0x00,0x70,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x70, + + 6, // 0x5C '\' + 0x00,0x00,0x80,0x80,0x40,0x40,0x20,0x10,0x10,0x08,0x08,0x00, + + 5, // 0x5D ']' + 0x00,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x70, + + 8, // 0x5E '^' + 0x00,0x00,0x00,0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, + + 6, // 0x60 '`' + 0x00,0x00,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x70,0x18,0x78,0xD8,0x78,0x00,0x00, + + 6, // 0x62 'b' + 0x00,0x00,0xC0,0xC0,0xC0,0xF0,0xD8,0xD8,0xD8,0xF0,0x00,0x00, + + 5, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0xC0,0xC0,0x70,0x00,0x00, + + 6, // 0x64 'd' + 0x00,0x00,0x18,0x18,0x18,0x78,0xD8,0xD8,0xD8,0x78,0x00,0x00, + + 6, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x70,0xD8,0xF8,0xC0,0x78,0x00,0x00, + + 5, // 0x66 'f' + 0x00,0x00,0x38,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x00,0x00, + + 6, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x78,0xD8,0xD8,0xD8,0x78,0x18,0x70, + + 6, // 0x68 'h' + 0x00,0x00,0xC0,0xC0,0xC0,0xF0,0xD8,0xD8,0xD8,0xD8,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x00,0xC0,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00, + + 4, // 0x6A 'j' + 0x00,0x00,0x00,0x60,0x00,0xE0,0x60,0x60,0x60,0x60,0x60,0xC0, + + 6, // 0x6B 'k' + 0x00,0x00,0xC0,0xC0,0xC0,0xD8,0xD8,0xF0,0xD8,0xD8,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00, + + 9, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF6,0x00,0xDB,0x00,0xDB,0x00,0xDB,0x00,0xDB,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0xF0,0xD8,0xD8,0xD8,0xD8,0x00,0x00, + + 6, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x70,0xD8,0xD8,0xD8,0x70,0x00,0x00, + + 6, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0xF0,0xD8,0xD8,0xD8,0xF0,0xC0,0xC0, + + 6, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x78,0xD8,0xD8,0xD8,0x78,0x18,0x18, + + 4, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0xD0,0xE0,0xC0,0xC0,0xC0,0x00,0x00, + + 5, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0xF0,0x30,0xE0,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x00,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x38,0x00,0x00, + + 6, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0xD8,0xD8,0xD8,0xD8,0x78,0x00,0x00, + + 6, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0xD8,0xD8,0xD8,0x70,0x70,0x00,0x00, + + 9, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDB,0x00,0xDB,0x00,0xDB,0x00,0x66,0x00,0x66,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0xD8,0xD8,0x70,0xD8,0xD8,0x00,0x00, + + 6, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0xD8,0xD8,0xD8,0x70,0x70,0x30,0x60, + + 5, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0xF0,0x30,0x60,0xC0,0xF0,0x00,0x00, + + 6, // 0x7B '{' + 0x00,0x00,0x18,0x30,0x30,0x30,0xE0,0x30,0x30,0x30,0x30,0x18, + + 5, // 0x7C '|' + 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + + 6, // 0x7D '}' + 0x00,0x00,0xC0,0x60,0x60,0x60,0x38,0x60,0x60,0x60,0x60,0xC0, + + 8, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x62,0x92,0x8C,0x00,0x00,0x00, + + 9, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7F,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana13[] = + { + 13, 3, 32, 128-32, + 0x00,0x00,0x0E,0x00,0x1C,0x00,0x2A,0x00,0x45,0x00,0x53,0x00,0x6E,0x00,0x7C,0x00,0x8A,0x00, + 0x98,0x00,0xA6,0x00,0xB4,0x00,0xCF,0x00,0xDD,0x00,0xEB,0x00,0xF9,0x00,0x07,0x01,0x15,0x01, + 0x23,0x01,0x31,0x01,0x3F,0x01,0x4D,0x01,0x5B,0x01,0x69,0x01,0x77,0x01,0x85,0x01,0x93,0x01, + 0xA1,0x01,0xAF,0x01,0xCA,0x01,0xE5,0x01,0x00,0x02,0x0E,0x02,0x29,0x02,0x37,0x02,0x45,0x02, + 0x60,0x02,0x7B,0x02,0x89,0x02,0x97,0x02,0xB2,0x02,0xC0,0x02,0xCE,0x02,0xDC,0x02,0xEA,0x02, + 0xF8,0x02,0x13,0x03,0x21,0x03,0x3C,0x03,0x4A,0x03,0x65,0x03,0x73,0x03,0x81,0x03,0x8F,0x03, + 0x9D,0x03,0xAB,0x03,0xC6,0x03,0xD4,0x03,0xE2,0x03,0xF0,0x03,0xFE,0x03,0x0C,0x04,0x1A,0x04, + 0x35,0x04,0x43,0x04,0x51,0x04,0x5F,0x04,0x6D,0x04,0x7B,0x04,0x89,0x04,0x97,0x04,0xA5,0x04, + 0xB3,0x04,0xC1,0x04,0xCF,0x04,0xDD,0x04,0xEB,0x04,0xF9,0x04,0x14,0x05,0x22,0x05,0x30,0x05, + 0x3E,0x05,0x4C,0x05,0x5A,0x05,0x68,0x05,0x76,0x05,0x84,0x05,0x92,0x05,0xAD,0x05,0xBB,0x05, + 0xC9,0x05,0xD7,0x05,0xE5,0x05,0xF3,0x05,0x01,0x06,0x1C,0x06, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 5, // 0x22 '"' + 0x00,0x00,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x0A,0x00,0x3F,0x00,0x14,0x00,0x14,0x00,0x7E,0x00,0x28,0x00,0x28,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x24 '$' + 0x00,0x00,0x10,0x10,0x3C,0x50,0x50,0x38,0x14,0x14,0x78,0x10,0x10, + + 12, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x49,0x00,0x4A,0x00,0x32,0x00,0x04,0xC0,0x05,0x20,0x09,0x20,0x08,0xC0,0x00,0x00,0x00,0x00, + + 8, // 0x26 '&' + 0x00,0x00,0x00,0x30,0x48,0x48,0x32,0x4A,0x44,0x46,0x39,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x00,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x00,0x00,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 5, // 0x29 ')' + 0x00,0x00,0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40, + + 7, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00, + + 5, // 0x2F '/' + 0x00,0x00,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00, + + 7, // 0x30 '0' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x31 '1' + 0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00, + + 7, // 0x32 '2' + 0x00,0x00,0x00,0x38,0x44,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 7, // 0x33 '3' + 0x00,0x00,0x00,0x38,0x44,0x04,0x18,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x34 '4' + 0x00,0x00,0x00,0x08,0x18,0x28,0x48,0x88,0xFC,0x08,0x08,0x00,0x00, + + 7, // 0x35 '5' + 0x00,0x00,0x00,0x7C,0x40,0x40,0x78,0x04,0x04,0x44,0x38,0x00,0x00, + + 7, // 0x36 '6' + 0x00,0x00,0x00,0x18,0x20,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x37 '7' + 0x00,0x00,0x00,0x7C,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x00,0x00, + + 7, // 0x38 '8' + 0x00,0x00,0x00,0x38,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x39 '9' + 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x3C,0x04,0x08,0x30,0x00,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x40, + + 9, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3F '?' + 0x00,0x00,0x00,0x70,0x08,0x08,0x10,0x20,0x20,0x00,0x20,0x00,0x00, + + 10, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x20,0x80,0x4E,0x80,0x52,0x80,0x52,0x80,0x52,0x80,0x4D,0x00,0x20,0x00,0x1E,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x00,0x18,0x18,0x24,0x24,0x24,0x7E,0x42,0x42,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x00,0x78,0x44,0x44,0x7C,0x42,0x42,0x42,0x7C,0x00,0x00, + + 9, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x45 'E' + 0x00,0x00,0x00,0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x7C,0x00,0x00, + + 6, // 0x46 'F' + 0x00,0x00,0x00,0x7C,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00, + + 9, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x47,0x00,0x41,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x48 'H' + 0x00,0x00,0x00,0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x42,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x4A 'J' + 0x00,0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0xE0,0x00,0x00, + + 8, // 0x4B 'K' + 0x00,0x00,0x00,0x42,0x44,0x48,0x50,0x70,0x48,0x44,0x42,0x00,0x00, + + 6, // 0x4C 'L' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00, + + 9, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x55,0x00,0x55,0x00,0x49,0x00,0x49,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4E 'N' + 0x00,0x00,0x00,0x62,0x62,0x52,0x52,0x4A,0x4A,0x46,0x46,0x00,0x00, + + 9, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x50 'P' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x00,0x00, + + 9, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x04,0x00,0x03,0x00, + + 8, // 0x52 'R' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x78,0x48,0x44,0x42,0x00,0x00, + + 8, // 0x53 'S' + 0x00,0x00,0x00,0x3C,0x42,0x40,0x30,0x0C,0x02,0x42,0x3C,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x00,0x00,0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 8, // 0x55 'U' + 0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x00,0x42,0x42,0x42,0x24,0x24,0x24,0x18,0x18,0x00,0x00, + + 11, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x40,0x44,0x40,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x11,0x00,0x11,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x58 'X' + 0x00,0x00,0x00,0x42,0x42,0x24,0x18,0x18,0x24,0x42,0x42,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0x00,0x82,0x44,0x28,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 8, // 0x5A 'Z' + 0x00,0x00,0x00,0x7E,0x02,0x04,0x08,0x10,0x20,0x40,0x7E,0x00,0x00, + + 5, // 0x5B '[' + 0x00,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70, + + 5, // 0x5C '\' + 0x00,0x00,0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x00, + + 5, // 0x5D ']' + 0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70, + + 9, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x14,0x00,0x22,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE, + + 7, // 0x60 '`' + 0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x38,0x04,0x3C,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x62 'b' + 0x00,0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x78,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x44,0x38,0x00,0x00, + + 7, // 0x64 'd' + 0x00,0x00,0x04,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x7C,0x40,0x44,0x38,0x00,0x00, + + 4, // 0x66 'f' + 0x00,0x00,0x30,0x40,0x40,0xF0,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x38, + + 7, // 0x68 'h' + 0x00,0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 4, // 0x6A 'j' + 0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0xC0, + + 7, // 0x6B 'k' + 0x00,0x00,0x40,0x40,0x40,0x44,0x48,0x50,0x70,0x48,0x44,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 11, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7B,0x80,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x00,0x00,0x00,0x00, + + 7, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x00,0x00, + + 7, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x38,0x00,0x00, + + 7, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x78,0x40,0x40, + + 7, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x04, + + 5, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x58,0x60,0x40,0x40,0x40,0x40,0x00,0x00, + + 6, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x60,0x18,0x08,0x70,0x00,0x00, + + 4, // 0x74 't' + 0x00,0x00,0x00,0x40,0x40,0xF0,0x40,0x40,0x40,0x40,0x30,0x00,0x00, + + 7, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x3C,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, + + 9, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x49,0x00,0x55,0x00,0x55,0x00,0x22,0x00,0x22,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x44,0x28,0x10,0x10,0x28,0x44,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x44,0x28,0x28,0x28,0x10,0x10,0x10,0x20, + + 6, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x78,0x08,0x10,0x20,0x40,0x78,0x00,0x00, + + 7, // 0x7B '{' + 0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x60,0x10,0x10,0x10,0x10,0x0C, + + 5, // 0x7C '|' + 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + + 7, // 0x7D '}' + 0x00,0x00,0x60,0x10,0x10,0x10,0x10,0x0C,0x10,0x10,0x10,0x10,0x60, + + 9, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x49,0x00,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x7F,0x80,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana13_bold[] = + { + 13, 3, 32, 128-32, + 0x00,0x00,0x0E,0x00,0x1C,0x00,0x2A,0x00,0x45,0x00,0x53,0x00,0x6E,0x00,0x89,0x00,0x97,0x00, + 0xA5,0x00,0xB3,0x00,0xC1,0x00,0xDC,0x00,0xEA,0x00,0xF8,0x00,0x06,0x01,0x14,0x01,0x22,0x01, + 0x30,0x01,0x3E,0x01,0x4C,0x01,0x5A,0x01,0x68,0x01,0x76,0x01,0x84,0x01,0x92,0x01,0xA0,0x01, + 0xAE,0x01,0xBC,0x01,0xD7,0x01,0xF2,0x01,0x0D,0x02,0x1B,0x02,0x36,0x02,0x51,0x02,0x5F,0x02, + 0x6D,0x02,0x88,0x02,0x96,0x02,0xA4,0x02,0xBF,0x02,0xDA,0x02,0xE8,0x02,0xF6,0x02,0x04,0x03, + 0x12,0x03,0x2D,0x03,0x48,0x03,0x63,0x03,0x71,0x03,0x8C,0x03,0x9A,0x03,0xA8,0x03,0xB6,0x03, + 0xD1,0x03,0xDF,0x03,0xFA,0x03,0x08,0x04,0x16,0x04,0x24,0x04,0x32,0x04,0x40,0x04,0x4E,0x04, + 0x69,0x04,0x77,0x04,0x85,0x04,0x93,0x04,0xA1,0x04,0xAF,0x04,0xBD,0x04,0xCB,0x04,0xD9,0x04, + 0xE7,0x04,0xF5,0x04,0x03,0x05,0x11,0x05,0x1F,0x05,0x2D,0x05,0x48,0x05,0x56,0x05,0x64,0x05, + 0x72,0x05,0x80,0x05,0x8E,0x05,0x9C,0x05,0xAA,0x05,0xB8,0x05,0xC6,0x05,0xE1,0x05,0xEF,0x05, + 0xFD,0x05,0x0B,0x06,0x19,0x06,0x27,0x06,0x35,0x06,0x50,0x06, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x21 '!' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x60,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x0A,0x00,0x3F,0x00,0x14,0x00,0x14,0x00,0x7E,0x00,0x28,0x00,0x28,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x24 '$' + 0x00,0x00,0x08,0x08,0x3C,0x6A,0x68,0x3C,0x16,0x56,0x3C,0x10,0x10, + + 14, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x80,0x6C,0x80,0x6D,0x00,0x6D,0x70,0x3A,0xD8,0x02,0xD8,0x04,0xD8,0x04,0x70,0x00,0x00,0x00,0x00, + + 10, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x6C,0x00,0x6C,0x00,0x39,0x80,0x6D,0x00,0x66,0x00,0x63,0x00,0x3D,0x80,0x00,0x00,0x00,0x00, + + 4, // 0x27 ''' + 0x00,0x00,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x00,0x00,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x30,0x18, + + 6, // 0x29 ')' + 0x00,0x00,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x30,0x30,0x60, + + 8, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x40, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 8, // 0x2F '/' + 0x00,0x00,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x30,0x30,0x60,0x60, + + 8, // 0x30 '0' + 0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00, + + 8, // 0x31 '1' + 0x00,0x00,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00, + + 8, // 0x32 '2' + 0x00,0x00,0x00,0x3C,0x66,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00,0x00, + + 8, // 0x33 '3' + 0x00,0x00,0x00,0x3C,0x66,0x06,0x1C,0x06,0x06,0x66,0x3C,0x00,0x00, + + 8, // 0x34 '4' + 0x00,0x00,0x00,0x04,0x0C,0x1C,0x2C,0x4C,0x7E,0x0C,0x0C,0x00,0x00, + + 8, // 0x35 '5' + 0x00,0x00,0x00,0x3E,0x30,0x30,0x3C,0x06,0x06,0x66,0x3C,0x00,0x00, + + 8, // 0x36 '6' + 0x00,0x00,0x00,0x1C,0x30,0x60,0x7C,0x66,0x66,0x66,0x3C,0x00,0x00, + + 8, // 0x37 '7' + 0x00,0x00,0x00,0x7E,0x06,0x0C,0x0C,0x18,0x18,0x30,0x30,0x00,0x00, + + 8, // 0x38 '8' + 0x00,0x00,0x00,0x3C,0x66,0x66,0x3C,0x66,0x66,0x66,0x3C,0x00,0x00, + + 8, // 0x39 '9' + 0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00,0x00, + + 4, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00, + + 4, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x60,0x40, + + 9, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x00,0x06,0x00,0x18,0x00,0x60,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x00,0x00,0x38,0x4C,0x0C,0x18,0x30,0x30,0x00,0x30,0x00,0x00, + + 11, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x4F,0x40,0x5B,0x40,0x5B,0x40,0x5B,0x40,0x4F,0x80,0x20,0x00,0x1F,0x00,0x00,0x00, + + 9, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x1C,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x7F,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x00,0x7C,0x66,0x66,0x7C,0x66,0x66,0x66,0x7C,0x00,0x00, + + 8, // 0x43 'C' + 0x00,0x00,0x00,0x3C,0x62,0x60,0x60,0x60,0x60,0x62,0x3C,0x00,0x00, + + 9, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x66,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x66,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x00,0x7E,0x60,0x60,0x7E,0x60,0x60,0x60,0x7E,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x00,0x7E,0x60,0x60,0x7E,0x60,0x60,0x60,0x60,0x00,0x00, + + 9, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x61,0x00,0x60,0x00,0x60,0x00,0x67,0x00,0x63,0x00,0x63,0x00,0x3F,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x00,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0xF0,0x00,0x00, + + 8, // 0x4B 'K' + 0x00,0x00,0x00,0x66,0x6C,0x78,0x70,0x70,0x78,0x6C,0x66,0x00,0x00, + + 7, // 0x4C 'L' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0x00, + + 10, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x80,0x71,0x80,0x7B,0x80,0x5D,0x80,0x49,0x80,0x41,0x80,0x41,0x80,0x41,0x80,0x00,0x00,0x00,0x00, + + 9, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x61,0x00,0x71,0x00,0x59,0x00,0x4D,0x00,0x47,0x00,0x43,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x00,0x00, + + 9, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x0C,0x00,0x07,0x00, + + 8, // 0x52 'R' + 0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x7C,0x6C,0x66,0x63,0x00,0x00, + + 8, // 0x53 'S' + 0x00,0x00,0x00,0x3C,0x62,0x60,0x7C,0x3E,0x06,0x46,0x3C,0x00,0x00, + + 8, // 0x54 'T' + 0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00, + + 9, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x00,0x66,0x66,0x66,0x3C,0x3C,0x3C,0x18,0x18,0x00,0x00, + + 12, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x60,0x66,0x60,0x66,0x60,0x36,0xC0,0x3F,0xC0,0x39,0xC0,0x19,0x80,0x19,0x80,0x00,0x00,0x00,0x00, + + 8, // 0x58 'X' + 0x00,0x00,0x00,0x66,0x66,0x3C,0x18,0x18,0x3C,0x66,0x66,0x00,0x00, + + 8, // 0x59 'Y' + 0x00,0x00,0x00,0x66,0x66,0x3C,0x3C,0x18,0x18,0x18,0x18,0x00,0x00, + + 8, // 0x5A 'Z' + 0x00,0x00,0x00,0x7E,0x06,0x0E,0x1C,0x38,0x70,0x60,0x7E,0x00,0x00, + + 6, // 0x5B '[' + 0x00,0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78, + + 8, // 0x5C '\' + 0x00,0x00,0x60,0x60,0x30,0x30,0x18,0x18,0x18,0x0C,0x0C,0x06,0x06, + + 6, // 0x5D ']' + 0x00,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x78, + + 10, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, + + 8, // 0x60 '`' + 0x00,0x00,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x60,0x60,0x60,0x60,0x3C,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x06,0x06,0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x7E,0x60,0x62,0x3C,0x00,0x00, + + 5, // 0x66 'f' + 0x00,0x00,0x38,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C, + + 8, // 0x68 'h' + 0x00,0x00,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x66,0x00,0x00, + + 4, // 0x69 'i' + 0x00,0x00,0x00,0x60,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x00,0x00,0x30,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0xE0, + + 8, // 0x6B 'k' + 0x00,0x00,0x60,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0x00,0x00, + + 4, // 0x6C 'l' + 0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 12, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7D,0xC0,0x66,0x60,0x66,0x60,0x66,0x60,0x66,0x60,0x66,0x60,0x00,0x00,0x00,0x00, + + 8, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x66,0x00,0x00, + + 8, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x7C,0x60,0x60, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x66,0x66,0x66,0x66,0x3E,0x06,0x06, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x6C,0x7C,0x60,0x60,0x60,0x60,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x60,0x78,0x3C,0x0C,0x78,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x00,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x38,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x3C,0x3C,0x18,0x00,0x00, + + 10, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6D,0x80,0x6D,0x80,0x6D,0x80,0x6D,0x80,0x33,0x00,0x33,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x3C,0x3C,0x66,0x66,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x3C,0x3C,0x18,0x18,0x30,0x30, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x0C,0x18,0x30,0x60,0x7C,0x00,0x00, + + 8, // 0x7B '{' + 0x00,0x00,0x0E,0x18,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E, + + 6, // 0x7C '|' + 0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + + 8, // 0x7D '}' + 0x00,0x00,0x70,0x18,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70, + + 9, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x49,0x00,0x49,0x00,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x7F,0x80,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana14[] = + { + 14, 3, 32, 128-32, + 0x00,0x00,0x0F,0x00,0x1E,0x00,0x2D,0x00,0x4A,0x00,0x59,0x00,0x76,0x00,0x93,0x00,0xA2,0x00, + 0xB1,0x00,0xC0,0x00,0xCF,0x00,0xEC,0x00,0xFB,0x00,0x0A,0x01,0x19,0x01,0x28,0x01,0x37,0x01, + 0x46,0x01,0x55,0x01,0x64,0x01,0x73,0x01,0x82,0x01,0x91,0x01,0xA0,0x01,0xAF,0x01,0xBE,0x01, + 0xCD,0x01,0xDC,0x01,0xF9,0x01,0x16,0x02,0x33,0x02,0x42,0x02,0x5F,0x02,0x6E,0x02,0x7D,0x02, + 0x9A,0x02,0xB7,0x02,0xC6,0x02,0xD5,0x02,0xF2,0x02,0x0F,0x03,0x1E,0x03,0x2D,0x03,0x3C,0x03, + 0x4B,0x03,0x68,0x03,0x85,0x03,0xA2,0x03,0xB1,0x03,0xCE,0x03,0xDD,0x03,0xEC,0x03,0xFB,0x03, + 0x18,0x04,0x27,0x04,0x44,0x04,0x53,0x04,0x62,0x04,0x71,0x04,0x80,0x04,0x8F,0x04,0x9E,0x04, + 0xBB,0x04,0xCA,0x04,0xD9,0x04,0xE8,0x04,0xF7,0x04,0x06,0x05,0x15,0x05,0x24,0x05,0x33,0x05, + 0x42,0x05,0x51,0x05,0x60,0x05,0x6F,0x05,0x7E,0x05,0x8D,0x05,0xAA,0x05,0xB9,0x05,0xC8,0x05, + 0xD7,0x05,0xE6,0x05,0xF5,0x05,0x04,0x06,0x13,0x06,0x22,0x06,0x31,0x06,0x4E,0x06,0x5D,0x06, + 0x6C,0x06,0x7B,0x06,0x8A,0x06,0x99,0x06,0xA8,0x06,0xC5,0x06, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, + + 6, // 0x22 '"' + 0x00,0x00,0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x09,0x00,0x12,0x00,0x3F,0x80,0x12,0x00,0x12,0x00,0x7F,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x24 '$' + 0x00,0x00,0x10,0x10,0x3E,0x50,0x50,0x30,0x1C,0x12,0x12,0x7C,0x10,0x10, + + 13, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x80,0x49,0x00,0x49,0x00,0x4A,0x00,0x32,0x60,0x02,0x90,0x04,0x90,0x04,0x90,0x08,0x60,0x00,0x00,0x00,0x00, + + 10, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x44,0x00,0x44,0x00,0x44,0x00,0x39,0x00,0x45,0x00,0x42,0x00,0x43,0x00,0x3C,0x80,0x00,0x00,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x00,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x28 '(' + 0x00,0x00,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10, + + 5, // 0x29 ')' + 0x00,0x00,0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40, + + 8, // 0x2A '*' + 0x00,0x00,0x10,0x54,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40, + + 5, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00, + + 5, // 0x2F '/' + 0x00,0x00,0x08,0x08,0x10,0x10,0x10,0x20,0x20,0x20,0x40,0x40,0x40,0x80, + + 8, // 0x30 '0' + 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x31 '1' + 0x00,0x00,0x00,0x08,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x3E,0x00,0x00, + + 8, // 0x32 '2' + 0x00,0x00,0x00,0x3C,0x42,0x42,0x02,0x04,0x18,0x20,0x40,0x7E,0x00,0x00, + + 8, // 0x33 '3' + 0x00,0x00,0x00,0x3C,0x42,0x02,0x02,0x1C,0x02,0x02,0x42,0x3C,0x00,0x00, + + 8, // 0x34 '4' + 0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x44,0x7F,0x04,0x04,0x04,0x00,0x00, + + 8, // 0x35 '5' + 0x00,0x00,0x00,0x7E,0x40,0x40,0x7C,0x02,0x02,0x02,0x42,0x3C,0x00,0x00, + + 8, // 0x36 '6' + 0x00,0x00,0x00,0x1C,0x20,0x40,0x7C,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x37 '7' + 0x00,0x00,0x00,0x7E,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x00,0x00, + + 8, // 0x38 '8' + 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x3C,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x39 '9' + 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x3E,0x02,0x04,0x38,0x00,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x20,0x20,0x00,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x20,0x20,0x20,0x40, + + 9, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x00,0x06,0x00,0x18,0x00,0x60,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x00,0x00,0x38,0x44,0x04,0x04,0x08,0x10,0x10,0x00,0x10,0x00,0x00, + + 12, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x27,0x40,0x49,0x20,0x49,0x20,0x49,0x20,0x49,0x20,0x27,0xC0,0x30,0x00,0x0F,0x00,0x00,0x00, + + 8, // 0x41 'A' + 0x00,0x00,0x00,0x18,0x18,0x24,0x24,0x42,0x42,0x7E,0x81,0x81,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x7C,0x42,0x42,0x42,0x7C,0x00,0x00, + + 9, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x7E,0x40,0x40,0x40,0x7E,0x00,0x00, + + 7, // 0x46 'F' + 0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x7C,0x40,0x40,0x40,0x40,0x00,0x00, + + 9, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x47,0x00,0x41,0x00,0x41,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7F,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, + + 5, // 0x4A 'J' + 0x00,0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xE0,0x00,0x00, + + 8, // 0x4B 'K' + 0x00,0x00,0x00,0x42,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x42,0x00,0x00, + + 7, // 0x4C 'L' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7E,0x00,0x00, + + 10, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x52,0x80,0x52,0x80,0x52,0x80,0x4C,0x80,0x4C,0x80,0x40,0x80,0x40,0x80,0x00,0x00,0x00,0x00, + + 9, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x00,0x61,0x00,0x51,0x00,0x51,0x00,0x49,0x00,0x45,0x00,0x45,0x00,0x43,0x00,0x43,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x00,0x7C,0x42,0x42,0x42,0x42,0x7C,0x40,0x40,0x40,0x00,0x00, + + 10, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x02,0x00,0x01,0x80, + + 8, // 0x52 'R' + 0x00,0x00,0x00,0x7C,0x42,0x42,0x42,0x7C,0x48,0x44,0x42,0x41,0x00,0x00, + + 8, // 0x53 'S' + 0x00,0x00,0x00,0x3C,0x42,0x40,0x40,0x3C,0x02,0x02,0x42,0x3C,0x00,0x00, + + 7, // 0x54 'T' + 0x00,0x00,0x00,0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 9, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x56 'V' + 0x00,0x00,0x00,0x81,0x81,0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00, + + 13, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x10,0x42,0x10,0x45,0x10,0x45,0x10,0x25,0x20,0x28,0xA0,0x28,0xA0,0x10,0x40,0x10,0x40,0x00,0x00,0x00,0x00, + + 8, // 0x58 'X' + 0x00,0x00,0x00,0x42,0x42,0x24,0x18,0x18,0x18,0x24,0x42,0x42,0x00,0x00, + + 7, // 0x59 'Y' + 0x00,0x00,0x00,0x82,0x44,0x44,0x28,0x10,0x10,0x10,0x10,0x10,0x00,0x00, + + 8, // 0x5A 'Z' + 0x00,0x00,0x00,0x7E,0x02,0x04,0x08,0x10,0x10,0x20,0x40,0x7E,0x00,0x00, + + 5, // 0x5B '[' + 0x00,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x70, + + 5, // 0x5C '\' + 0x00,0x00,0x80,0x80,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x08, + + 5, // 0x5D ']' + 0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70, + + 10, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x12,0x00,0x21,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF, + + 8, // 0x60 '`' + 0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x02,0x02,0x3E,0x42,0x42,0x3E,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x40,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42,0x7C,0x00,0x00, + + 6, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x40,0x40,0x40,0x44,0x38,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x02,0x02,0x02,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x7E,0x40,0x42,0x3C,0x00,0x00, + + 4, // 0x66 'f' + 0x00,0x00,0x30,0x40,0x40,0xF0,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x02,0x3C, + + 8, // 0x68 'h' + 0x00,0x00,0x40,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 4, // 0x6A 'j' + 0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xC0, + + 7, // 0x6B 'k' + 0x00,0x00,0x40,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 11, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7B,0x80,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x00,0x00,0x00,0x00, + + 8, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x5C,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00, + + 8, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x5C,0x62,0x42,0x42,0x42,0x42,0x7C,0x40,0x40, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x02,0x02, + + 5, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x58,0x60,0x40,0x40,0x40,0x40,0x40,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x40,0x40,0x38,0x04,0x04,0x78,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x00,0x00,0x40,0x40,0xF8,0x40,0x40,0x40,0x40,0x40,0x38,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x46,0x3A,0x00,0x00, + + 7, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00, + + 11, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x40,0x44,0x40,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x11,0x00,0x11,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00, + + 7, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x10,0x20, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00, + + 8, // 0x7B '{' + 0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x60,0x10,0x10,0x10,0x10,0x10,0x0C, + + 5, // 0x7C '|' + 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + + 8, // 0x7D '}' + 0x00,0x00,0x30,0x08,0x08,0x08,0x08,0x06,0x08,0x08,0x08,0x08,0x08,0x30, + + 10, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x80,0x4C,0x80,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3F,0xE0,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana14_bold[] = + { + 14, 3, 32, 128-32, + 0x00,0x00,0x0F,0x00,0x1E,0x00,0x2D,0x00,0x4A,0x00,0x67,0x00,0x84,0x00,0xA1,0x00,0xB0,0x00, + 0xBF,0x00,0xCE,0x00,0xEB,0x00,0x08,0x01,0x17,0x01,0x26,0x01,0x35,0x01,0x44,0x01,0x61,0x01, + 0x7E,0x01,0x9B,0x01,0xB8,0x01,0xD5,0x01,0xF2,0x01,0x0F,0x02,0x2C,0x02,0x49,0x02,0x66,0x02, + 0x75,0x02,0x84,0x02,0xA1,0x02,0xBE,0x02,0xDB,0x02,0xEA,0x02,0x07,0x03,0x24,0x03,0x41,0x03, + 0x5E,0x03,0x7B,0x03,0x8A,0x03,0x99,0x03,0xB6,0x03,0xD3,0x03,0xE2,0x03,0xF1,0x03,0x0E,0x04, + 0x1D,0x04,0x3A,0x04,0x57,0x04,0x74,0x04,0x91,0x04,0xAE,0x04,0xCB,0x04,0xE8,0x04,0xF7,0x04, + 0x14,0x05,0x31,0x05,0x4E,0x05,0x6B,0x05,0x88,0x05,0x97,0x05,0xA6,0x05,0xB5,0x05,0xC4,0x05, + 0xE1,0x05,0xFE,0x05,0x1B,0x06,0x2A,0x06,0x39,0x06,0x48,0x06,0x57,0x06,0x66,0x06,0x75,0x06, + 0x84,0x06,0x93,0x06,0xA2,0x06,0xB1,0x06,0xC0,0x06,0xCF,0x06,0xEC,0x06,0xFB,0x06,0x0A,0x07, + 0x19,0x07,0x28,0x07,0x37,0x07,0x46,0x07,0x55,0x07,0x64,0x07,0x73,0x07,0x90,0x07,0x9F,0x07, + 0xAE,0x07,0xBD,0x07,0xDA,0x07,0xE9,0x07,0x06,0x08,0x23,0x08, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x60,0x60,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x09,0x00,0x3F,0x80,0x3F,0x80,0x12,0x00,0x7F,0x00,0x7F,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x3E,0x00,0x69,0x00,0x68,0x00,0x7E,0x00,0x3F,0x00,0x0B,0x00,0x4B,0x00,0x3E,0x00,0x08,0x00,0x08,0x00, + + 15, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x6C,0x40,0x6C,0x80,0x6C,0xB8,0x6D,0x6C,0x3A,0x6C,0x02,0x6C,0x04,0x6C,0x04,0x38,0x00,0x00,0x00,0x00, + + 10, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x6C,0x00,0x6C,0x00,0x6C,0x00,0x39,0x80,0x6D,0x00,0x66,0x00,0x63,0x00,0x3D,0x80,0x00,0x00,0x00,0x00, + + 4, // 0x27 ''' + 0x00,0x00,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x00,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0x30,0x18, + + 7, // 0x29 ')' + 0x00,0x00,0x30,0x18,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x18,0x30, + + 9, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x08,0x00,0x2A,0x00,0x1C,0x00,0x1C,0x00,0x2A,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x40, + + 6, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 8, // 0x2F '/' + 0x00,0x00,0x06,0x06,0x0C,0x0C,0x0C,0x18,0x18,0x30,0x30,0x30,0x60,0x60, + + 9, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x3C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x3F,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x7F,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x03,0x00,0x03,0x00,0x1E,0x00,0x03,0x00,0x03,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0E,0x00,0x16,0x00,0x16,0x00,0x26,0x00,0x46,0x00,0x7F,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x30,0x00,0x30,0x00,0x3E,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x30,0x00,0x60,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3F,0x00,0x03,0x00,0x06,0x00,0x3C,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x00,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x60,0x60,0x40, + + 10, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x06,0x00,0x18,0x00,0x60,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x00,0x00,0x38,0x4C,0x0C,0x18,0x30,0x30,0x00,0x30,0x30,0x00,0x00, + + 12, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x2F,0x40,0x5B,0x20,0x5B,0x20,0x5B,0x20,0x5B,0x20,0x2F,0xC0,0x30,0x00,0x0F,0x00,0x00,0x00, + + 9, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x1C,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x7F,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x66,0x00,0x66,0x00,0x66,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x31,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x31,0x00,0x1E,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x00,0x7E,0x60,0x60,0x60,0x7E,0x60,0x60,0x60,0x7E,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x00,0x7E,0x60,0x60,0x60,0x7E,0x60,0x60,0x60,0x60,0x00,0x00, + + 10, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x30,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x67,0x80,0x61,0x80,0x31,0x80,0x1F,0x80,0x00,0x00,0x00,0x00, + + 10, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x7F,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00, + + 7, // 0x4A 'J' + 0x00,0x00,0x00,0x7C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xF8,0x00,0x00, + + 9, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x66,0x00,0x6C,0x00,0x78,0x00,0x70,0x00,0x78,0x00,0x6C,0x00,0x66,0x00,0x63,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4C 'L' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7F,0x00,0x00, + + 11, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x71,0xC0,0x71,0xC0,0x5A,0xC0,0x5A,0xC0,0x4C,0xC0,0x4C,0xC0,0x40,0xC0,0x40,0xC0,0x00,0x00,0x00,0x00, + + 10, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x80,0x70,0x80,0x58,0x80,0x58,0x80,0x4C,0x80,0x46,0x80,0x46,0x80,0x43,0x80,0x41,0x80,0x00,0x00,0x00,0x00, + + 11, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x06,0x00,0x03,0xC0, + + 9, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x6C,0x00,0x66,0x00,0x63,0x00,0x61,0x80,0x00,0x00,0x00,0x00, + + 9, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x61,0x00,0x60,0x00,0x70,0x00,0x3E,0x00,0x07,0x00,0x03,0x00,0x43,0x00,0x3E,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x54 'T' + 0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00, + + 10, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x18,0x63,0x18,0x63,0x18,0x33,0x30,0x37,0xB0,0x34,0xB0,0x1C,0xE0,0x18,0x60,0x18,0x60,0x00,0x00,0x00,0x00, + + 9, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x36,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5A 'Z' + 0x00,0x00,0x00,0x7E,0x0C,0x0C,0x18,0x18,0x30,0x30,0x60,0x7E,0x00,0x00, + + 6, // 0x5B '[' + 0x00,0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78, + + 8, // 0x5C '\' + 0x00,0x00,0x60,0x60,0x30,0x30,0x30,0x18,0x18,0x0C,0x0C,0x0C,0x06,0x06, + + 6, // 0x5D ']' + 0x00,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x78, + + 10, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x80, + + 9, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x30,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x66,0x7C,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x62,0x60,0x60,0x60,0x62,0x3C,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x06,0x06,0x06,0x3E,0x66,0x66,0x66,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x62,0x3C,0x00,0x00, + + 5, // 0x66 'f' + 0x00,0x00,0x38,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x66,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C, + + 8, // 0x68 'h' + 0x00,0x00,0x60,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00, + + 4, // 0x69 'i' + 0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x00,0x30,0x30,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xE0, + + 8, // 0x6B 'k' + 0x00,0x00,0x60,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0x63,0x00,0x00, + + 4, // 0x6C 'l' + 0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 12, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0xC0,0x77,0x60,0x66,0x60,0x66,0x60,0x66,0x60,0x66,0x60,0x66,0x60,0x00,0x00,0x00,0x00, + + 8, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00, + + 8, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x3E,0x66,0x66,0x66,0x66,0x66,0x3E,0x06,0x06, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x6C,0x7C,0x60,0x60,0x60,0x60,0x60,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x3C,0x60,0x60,0x38,0x0C,0x0C,0x78,0x00,0x00, + + 5, // 0x74 't' + 0x00,0x00,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x60,0x38,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3E,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x3C,0x3C,0x3C,0x18,0x00,0x00, + + 12, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x60,0x66,0x60,0x66,0x60,0x69,0x60,0x39,0xC0,0x30,0xC0,0x30,0xC0,0x00,0x00,0x00,0x00, + + 8, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x3C,0x3C,0x3C,0x18,0x18,0x30, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x7C,0x0C,0x18,0x38,0x30,0x60,0x7C,0x00,0x00, + + 9, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x0E,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x70,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0E,0x00, + + 6, // 0x7C '|' + 0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, + + 9, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x07,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x38,0x00, + + 10, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x80,0x48,0x80,0x44,0x80,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3F,0xE0,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana16[] = + { + 16, 4, 32, 128-32, + 0x00,0x00,0x11,0x00,0x22,0x00,0x33,0x00,0x54,0x00,0x65,0x00,0x86,0x00,0xA7,0x00,0xB8,0x00, + 0xC9,0x00,0xDA,0x00,0xFB,0x00,0x1C,0x01,0x2D,0x01,0x3E,0x01,0x4F,0x01,0x60,0x01,0x71,0x01, + 0x82,0x01,0x93,0x01,0xA4,0x01,0xB5,0x01,0xC6,0x01,0xD7,0x01,0xE8,0x01,0xF9,0x01,0x0A,0x02, + 0x1B,0x02,0x2C,0x02,0x4D,0x02,0x6E,0x02,0x8F,0x02,0xA0,0x02,0xC1,0x02,0xE2,0x02,0xF3,0x02, + 0x14,0x03,0x35,0x03,0x46,0x03,0x57,0x03,0x78,0x03,0x99,0x03,0xAA,0x03,0xBB,0x03,0xCC,0x03, + 0xDD,0x03,0xFE,0x03,0x1F,0x04,0x40,0x04,0x51,0x04,0x72,0x04,0x93,0x04,0xB4,0x04,0xD5,0x04, + 0xF6,0x04,0x17,0x05,0x38,0x05,0x59,0x05,0x7A,0x05,0x9B,0x05,0xAC,0x05,0xBD,0x05,0xCE,0x05, + 0xEF,0x05,0x00,0x06,0x11,0x06,0x22,0x06,0x33,0x06,0x44,0x06,0x55,0x06,0x66,0x06,0x77,0x06, + 0x88,0x06,0x99,0x06,0xAA,0x06,0xBB,0x06,0xCC,0x06,0xDD,0x06,0xFE,0x06,0x0F,0x07,0x20,0x07, + 0x31,0x07,0x42,0x07,0x53,0x07,0x64,0x07,0x75,0x07,0x86,0x07,0x97,0x07,0xB8,0x07,0xC9,0x07, + 0xDA,0x07,0xEB,0x07,0xFC,0x07,0x0D,0x08,0x1E,0x08,0x3F,0x08, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x20,0x00,0x00,0x00, + + 5, // 0x22 '"' + 0x00,0x00,0x00,0x50,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x09,0x00,0x3F,0x80,0x12,0x00,0x12,0x00,0x7F,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x24 '$' + 0x00,0x00,0x00,0x10,0x10,0x3E,0x50,0x50,0x30,0x1C,0x12,0x12,0x7C,0x10,0x10,0x00, + + 13, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x44,0x80,0x45,0x00,0x45,0x00,0x3A,0xE0,0x05,0x10,0x05,0x10,0x09,0x10,0x10,0xE0,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x44,0x00,0x44,0x00,0x44,0x00,0x38,0x80,0x45,0x00,0x42,0x00,0x46,0x00,0x39,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x27 ''' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x00,0x00,0x00,0x08,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x08, + + 6, // 0x29 ')' + 0x00,0x00,0x00,0x40,0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x10,0x20,0x40, + + 9, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x2A,0x00,0x1C,0x00,0x2A,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40,0x00, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x2F '/' + 0x00,0x00,0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00, + + 8, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x08,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x3E,0x00,0x00,0x00, + + 8, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x02,0x04,0x18,0x20,0x40,0x7E,0x00,0x00,0x00, + + 8, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x02,0x02,0x1C,0x02,0x02,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x44,0x7F,0x04,0x04,0x04,0x00,0x00,0x00, + + 8, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x3E,0x20,0x20,0x20,0x3C,0x02,0x02,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x1C,0x20,0x40,0x7C,0x42,0x42,0x42,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x7E,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x00,0x00,0x00, + + 8, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x3C,0x42,0x42,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x3E,0x02,0x04,0x38,0x00,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x20,0x20,0x20,0x40,0x00, + + 9, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x00,0x06,0x00,0x18,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x38,0x44,0x04,0x08,0x10,0x10,0x00,0x10,0x10,0x00,0x00,0x00, + + 13, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x10,0x40,0x27,0xA0,0x48,0x90,0x48,0x90,0x48,0x90,0x48,0x90,0x48,0x90,0x27,0xE0,0x10,0x00,0x0F,0x80,0x00,0x00, + + 9, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x14,0x00,0x14,0x00,0x22,0x00,0x22,0x00,0x3E,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x7C,0x42,0x42,0x42,0x7C,0x00,0x00,0x00, + + 9, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x7E,0x40,0x40,0x40,0x7E,0x00,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x7C,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 9, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x47,0x00,0x41,0x00,0x21,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7F,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x00, + + 8, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x42,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x42,0x00,0x00,0x00, + + 7, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7E,0x00,0x00,0x00, + + 11, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x51,0x40,0x51,0x40,0x4A,0x40,0x4A,0x40,0x44,0x40,0x44,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x00,0x61,0x00,0x51,0x00,0x51,0x00,0x49,0x00,0x45,0x00,0x45,0x00,0x43,0x00,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x7C,0x42,0x42,0x42,0x42,0x7C,0x40,0x40,0x40,0x00,0x00,0x00, + + 10, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x02,0x00,0x01,0x80,0x00,0x00, + + 9, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x42,0x00,0x42,0x00,0x44,0x00,0x78,0x00,0x44,0x00,0x42,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x41,0x00,0x40,0x00,0x40,0x00,0x3E,0x00,0x01,0x00,0x01,0x00,0x41,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x54 'T' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x10,0x42,0x10,0x45,0x10,0x45,0x10,0x25,0x20,0x28,0xA0,0x28,0xA0,0x10,0x40,0x10,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00,0x14,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x01,0x00,0x02,0x00,0x04,0x00,0x08,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5B '[' + 0x00,0x00,0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, + + 6, // 0x5C '\' + 0x00,0x00,0x00,0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00, + + 6, // 0x5D ']' + 0x00,0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, + + 11, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00, + + 8, // 0x60 '`' + 0x00,0x00,0x00,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x02,0x02,0x3E,0x42,0x42,0x3E,0x00,0x00,0x00, + + 8, // 0x62 'b' + 0x00,0x00,0x00,0x40,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42,0x7C,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x40,0x40,0x40,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x64 'd' + 0x00,0x00,0x00,0x02,0x02,0x02,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x00,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x7E,0x40,0x42,0x3C,0x00,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x00,0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, + + 8, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x02,0x02,0x3C, + + 8, // 0x68 'h' + 0x00,0x00,0x00,0x40,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 4, // 0x6A 'j' + 0x00,0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xC0, + + 7, // 0x6B 'k' + 0x00,0x00,0x00,0x40,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 11, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x80,0x66,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x44,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, + + 8, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00,0x00, + + 8, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x62,0x42,0x42,0x42,0x42,0x7C,0x40,0x40,0x40, + + 8, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x42,0x42,0x42,0x42,0x46,0x3A,0x02,0x02,0x02, + + 5, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x60,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 7, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x40,0x40,0x38,0x04,0x04,0x78,0x00,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x00,0x00,0x00,0x20,0x20,0x78,0x20,0x20,0x20,0x20,0x20,0x18,0x00,0x00,0x00, + + 8, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x46,0x3A,0x00,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x24,0x24,0x24,0x18,0x18,0x00,0x00,0x00, + + 11, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x40,0x44,0x40,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x11,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x24,0x24,0x24,0x18,0x18,0x10,0x10,0x20, + + 7, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,0x00, + + 8, // 0x7B '{' + 0x00,0x00,0x00,0x0C,0x10,0x10,0x10,0x10,0x60,0x10,0x10,0x10,0x10,0x10,0x0C,0x00, + + 7, // 0x7C '|' + 0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00, + + 8, // 0x7D '}' + 0x00,0x00,0x00,0x30,0x08,0x08,0x08,0x08,0x06,0x08,0x08,0x08,0x08,0x08,0x30,0x00, + + 11, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x80,0x4C,0x80,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF0,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x3F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana16_bold[] = + { + 16, 4, 32, 128-32, + 0x00,0x00,0x11,0x00,0x22,0x00,0x33,0x00,0x54,0x00,0x75,0x00,0xA6,0x00,0xC7,0x00,0xD8,0x00, + 0xE9,0x00,0xFA,0x00,0x1B,0x01,0x3C,0x01,0x4D,0x01,0x5E,0x01,0x6F,0x01,0x90,0x01,0xB1,0x01, + 0xD2,0x01,0xF3,0x01,0x14,0x02,0x35,0x02,0x56,0x02,0x77,0x02,0x98,0x02,0xB9,0x02,0xDA,0x02, + 0xEB,0x02,0xFC,0x02,0x1D,0x03,0x3E,0x03,0x5F,0x03,0x70,0x03,0x91,0x03,0xB2,0x03,0xD3,0x03, + 0xF4,0x03,0x15,0x04,0x36,0x04,0x57,0x04,0x78,0x04,0x99,0x04,0xAA,0x04,0xBB,0x04,0xDC,0x04, + 0xED,0x04,0x0E,0x05,0x2F,0x05,0x50,0x05,0x71,0x05,0x92,0x05,0xB3,0x05,0xD4,0x05,0xE5,0x05, + 0x06,0x06,0x27,0x06,0x48,0x06,0x69,0x06,0x8A,0x06,0xAB,0x06,0xBC,0x06,0xDD,0x06,0xEE,0x06, + 0x0F,0x07,0x30,0x07,0x51,0x07,0x72,0x07,0x93,0x07,0xA4,0x07,0xC5,0x07,0xE6,0x07,0xF7,0x07, + 0x18,0x08,0x39,0x08,0x4A,0x08,0x5B,0x08,0x6C,0x08,0x7D,0x08,0x9E,0x08,0xBF,0x08,0xE0,0x08, + 0x01,0x09,0x22,0x09,0x33,0x09,0x44,0x09,0x55,0x09,0x76,0x09,0x97,0x09,0xB8,0x09,0xD9,0x09, + 0xFA,0x09,0x0B,0x0A,0x2C,0x0A,0x3D,0x0A,0x5E,0x0A,0x7F,0x0A, + + 4, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x00,0x6C,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x09,0x00,0x3F,0x80,0x3F,0x80,0x12,0x00,0x7F,0x00,0x7F,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x3E,0x00,0x69,0x00,0x68,0x00,0x78,0x00,0x3E,0x00,0x0F,0x00,0x0B,0x00,0x4B,0x00,0x3E,0x00,0x08,0x00,0x08,0x00,0x00,0x00, + + 17, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x20,0x00,0x66,0x20,0x00,0x66,0x40,0x00,0x66,0x5E,0x00,0x66,0xB3,0x00,0x3D,0x33,0x00,0x01,0x33,0x00,0x02,0x33,0x00,0x02,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x66,0x00,0x66,0x00,0x66,0xC0,0x3C,0xC0,0x66,0x80,0x63,0x00,0x63,0x80,0x3C,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x27 ''' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x00,0x00,0x0C,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x30,0x18,0x0C, + + 7, // 0x29 ')' + 0x00,0x00,0x00,0x60,0x30,0x18,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x18,0x30,0x60, + + 9, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x00,0x2A,0x00,0x1C,0x00,0x2A,0x00,0x49,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x60,0x60,0xC0,0xC0, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 9, // 0x2F '/' + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x60,0x00,0x00,0x00, + + 9, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x3C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x03,0x00,0x0E,0x00,0x03,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0E,0x00,0x16,0x00,0x26,0x00,0x46,0x00,0x7F,0x80,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x30,0x00,0x30,0x00,0x3E,0x00,0x03,0x00,0x03,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x30,0x00,0x60,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3F,0x00,0x03,0x00,0x06,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, + + 5, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x38,0x30,0x30,0x60,0x60, + + 11, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x18,0x00,0x06,0x00,0x01,0x80,0x00,0x40,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x3C,0x66,0x06,0x0C,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00, + + 13, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x30,0x60,0x27,0xA0,0x4D,0x90,0x4D,0x90,0x4D,0x90,0x4D,0x90,0x27,0xE0,0x30,0x00,0x0F,0x80,0x00,0x00,0x00,0x00, + + 10, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x1E,0x00,0x1E,0x00,0x33,0x00,0x33,0x00,0x7F,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7F,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x61,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x61,0x80,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x61,0x80,0x60,0x00,0x60,0x00,0x63,0x80,0x61,0x80,0x31,0x80,0x1F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x7F,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00, + + 7, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x7C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xF8,0x00,0x00,0x00, + + 9, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x66,0x00,0x6C,0x00,0x78,0x00,0x78,0x00,0x6C,0x00,0x66,0x00,0x63,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7F,0x00,0x00,0x00, + + 12, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xE0,0x70,0xE0,0x59,0x60,0x59,0x60,0x4E,0x60,0x4E,0x60,0x44,0x60,0x44,0x60,0x40,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x80,0x70,0x80,0x58,0x80,0x58,0x80,0x4C,0x80,0x46,0x80,0x46,0x80,0x43,0x80,0x43,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x03,0x00,0x01,0xC0,0x00,0x00, + + 9, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x6C,0x00,0x66,0x00,0x63,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x70,0x00,0x3E,0x00,0x07,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x54 'T' + 0x00,0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00, + + 10, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x00,0x33,0x00,0x1E,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x18,0x63,0x18,0x63,0x18,0x33,0x30,0x37,0xB0,0x34,0xB0,0x1C,0xE0,0x18,0x60,0x18,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x33,0x00,0x33,0x00,0x1E,0x00,0x0C,0x00,0x1E,0x00,0x33,0x00,0x33,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5B '[' + 0x00,0x00,0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x78,0x00, + + 9, // 0x5C '\' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00, + + 6, // 0x5D ']' + 0x00,0x00,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x78,0x00, + + 10, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x00,0x00, + + 9, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x3F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x62 'b' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6E,0x00,0x73,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00, + + 9, // 0x64 'd' + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x3F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x67,0x00,0x3B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x7F,0x00,0x60,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x66 'f' + 0x00,0x00,0x00,0x38,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 9, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x67,0x00,0x3B,0x00,0x03,0x00,0x03,0x00,0x3E,0x00, + + 9, // 0x68 'h' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6E,0x00,0x73,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x69 'i' + 0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x00,0x00,0x30,0x30,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xE0, + + 8, // 0x6B 'k' + 0x00,0x00,0x00,0x60,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0x63,0x00,0x00,0x00, + + 4, // 0x6C 'l' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 14, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x70,0x73,0x98,0x63,0x18,0x63,0x18,0x63,0x18,0x63,0x18,0x63,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x00,0x73,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x00,0x73,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00, + + 9, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x67,0x00,0x3B,0x00,0x03,0x00,0x03,0x00,0x03,0x00, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0x7C,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x60,0x70,0x3C,0x0E,0x06,0x7C,0x00,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x00,0x00,0x00,0x60,0x60,0xF8,0x60,0x60,0x60,0x60,0x60,0x38,0x00,0x00,0x00, + + 9, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x67,0x00,0x3B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x60,0x66,0x60,0x66,0x60,0x69,0x60,0x39,0xC0,0x30,0xC0,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x1C,0x00,0x36,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00, + + 8, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00,0x00,0x00, + + 9, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x70,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x07,0x00,0x00,0x00, + + 8, // 0x7C '|' + 0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00, + + 9, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x07,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x70,0x00,0x00,0x00, + + 11, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x44,0x40,0x44,0x40,0x43,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF0,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x3F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana17[] = + { + 17, 4, 32, 128-32, + 0x00,0x00,0x12,0x00,0x24,0x00,0x36,0x00,0x59,0x00,0x7C,0x00,0x9F,0x00,0xC2,0x00,0xD4,0x00, + 0xE6,0x00,0xF8,0x00,0x1B,0x01,0x3E,0x01,0x50,0x01,0x62,0x01,0x74,0x01,0x86,0x01,0xA9,0x01, + 0xCC,0x01,0xEF,0x01,0x12,0x02,0x35,0x02,0x58,0x02,0x7B,0x02,0x9E,0x02,0xC1,0x02,0xE4,0x02, + 0xF6,0x02,0x08,0x03,0x2B,0x03,0x4E,0x03,0x71,0x03,0x83,0x03,0xA6,0x03,0xC9,0x03,0xEC,0x03, + 0x0F,0x04,0x32,0x04,0x55,0x04,0x67,0x04,0x8A,0x04,0xAD,0x04,0xBF,0x04,0xD1,0x04,0xF4,0x04, + 0x06,0x05,0x29,0x05,0x4C,0x05,0x6F,0x05,0x81,0x05,0xA4,0x05,0xC7,0x05,0xEA,0x05,0x0D,0x06, + 0x30,0x06,0x53,0x06,0x76,0x06,0x99,0x06,0xBC,0x06,0xDF,0x06,0xF1,0x06,0x03,0x07,0x15,0x07, + 0x38,0x07,0x5B,0x07,0x7E,0x07,0x90,0x07,0xB3,0x07,0xC5,0x07,0xE8,0x07,0xFA,0x07,0x0C,0x08, + 0x2F,0x08,0x52,0x08,0x64,0x08,0x76,0x08,0x88,0x08,0x9A,0x08,0xBD,0x08,0xE0,0x08,0x03,0x09, + 0x26,0x09,0x49,0x09,0x5B,0x09,0x6D,0x09,0x7F,0x09,0xA2,0x09,0xB4,0x09,0xD7,0x09,0xFA,0x09, + 0x0C,0x0A,0x1E,0x0A,0x41,0x0A,0x53,0x0A,0x76,0x0A,0x99,0x0A, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x22 '"' + 0x00,0x00,0x00,0x48,0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x80,0x04,0x80,0x09,0x00,0x3F,0xC0,0x09,0x00,0x12,0x00,0x7F,0x80,0x12,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x3E,0x00,0x49,0x00,0x48,0x00,0x48,0x00,0x3E,0x00,0x09,0x00,0x09,0x00,0x49,0x00,0x3E,0x00,0x08,0x00,0x08,0x00,0x00,0x00, + + 15, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x44,0x40,0x44,0x80,0x44,0x80,0x45,0x38,0x39,0x44,0x02,0x44,0x04,0x44,0x04,0x44,0x08,0x38,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x42,0x00,0x42,0x00,0x44,0x00,0x38,0x80,0x44,0x80,0x42,0x80,0x41,0x00,0x22,0x80,0x1C,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x27 ''' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x28 '(' + 0x00,0x00,0x00,0x08,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x08, + + 6, // 0x29 ')' + 0x00,0x00,0x00,0x40,0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x10,0x20,0x40, + + 9, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x00,0x2A,0x00,0x1C,0x00,0x2A,0x00,0x49,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x7F,0xC0,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40,0x00, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x2F '/' + 0x00,0x00,0x00,0x04,0x08,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x40,0x40,0x80,0x80,0x00, + + 9, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x38,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x41,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x0C,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x41,0x00,0x01,0x00,0x02,0x00,0x1C,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x42,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x00,0x0A,0x00,0x12,0x00,0x22,0x00,0x42,0x00,0x7F,0x80,0x02,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7C,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x42,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x30,0x00,0x20,0x00,0x40,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x04,0x00,0x04,0x00,0x08,0x00,0x10,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x3E,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x21,0x00,0x1F,0x00,0x01,0x00,0x02,0x00,0x06,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40,0x00, + + 11, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x06,0x00,0x18,0x00,0x60,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xC0,0x00,0x00,0x00,0x00,0x3F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x02,0x02,0x0C,0x10,0x10,0x00,0x10,0x10,0x00,0x00,0x00, + + 14, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x18,0x20,0x20,0x10,0x27,0xC8,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x27,0xF0,0x20,0x00,0x18,0x00,0x07,0xC0,0x00,0x00, + + 10, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x12,0x00,0x12,0x00,0x21,0x00,0x21,0x00,0x21,0x00,0x7F,0x80,0x40,0x80,0x80,0x40,0x80,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7E,0x00,0x41,0x00,0x40,0x80,0x40,0x80,0x41,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0x80,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x30,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x41,0x80,0x40,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x41,0x80,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x7E,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 11, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x20,0x40,0x40,0x00,0x40,0x00,0x43,0xC0,0x40,0x40,0x20,0x40,0x30,0x40,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x7F,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 6, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x00, + + 10, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x42,0x00,0x44,0x00,0x48,0x00,0x50,0x00,0x68,0x00,0x44,0x00,0x42,0x00,0x41,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x00,0x00,0x00, + + 11, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x51,0x40,0x51,0x40,0x4A,0x40,0x4A,0x40,0x44,0x40,0x44,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x80,0x60,0x80,0x50,0x80,0x48,0x80,0x48,0x80,0x44,0x80,0x44,0x80,0x42,0x80,0x41,0x80,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x31,0x80,0x20,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x80,0x31,0x80,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x7C,0x42,0x41,0x41,0x42,0x7C,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 11, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x31,0x80,0x20,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x80,0x31,0x80,0x0E,0x00,0x02,0x00,0x02,0x00,0x01,0xC0, + + 10, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x42,0x00,0x42,0x00,0x44,0x00,0x78,0x00,0x44,0x00,0x42,0x00,0x41,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x20,0x80,0x40,0x00,0x40,0x00,0x38,0x00,0x07,0x00,0x00,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x54 'T' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x80,0x40,0x40,0x80,0x40,0x80,0x21,0x00,0x21,0x00,0x21,0x00,0x12,0x00,0x12,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 15, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x04,0x41,0x04,0x22,0x88,0x22,0x88,0x22,0x88,0x14,0x50,0x14,0x50,0x14,0x50,0x08,0x20,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x21,0x00,0x12,0x00,0x12,0x00,0x0C,0x00,0x0C,0x00,0x12,0x00,0x12,0x00,0x21,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x41,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x00,0x80,0x01,0x00,0x02,0x00,0x04,0x00,0x08,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x5B '[' + 0x00,0x00,0x00,0x3C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3C, + + 6, // 0x5C '\' + 0x00,0x00,0x00,0x80,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x08,0x08,0x04,0x00, + + 6, // 0x5D ']' + 0x00,0x00,0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x78, + + 11, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x00,0x00, + + 9, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x22,0x02,0x3E,0x42,0x42,0x46,0x3A,0x00,0x00,0x00, + + 9, // 0x62 'b' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x5C,0x00,0x62,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x22,0x40,0x40,0x40,0x40,0x22,0x1C,0x00,0x00,0x00, + + 9, // 0x64 'd' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x24,0x42,0x7E,0x40,0x40,0x22,0x1C,0x00,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x00,0x00,0x1C,0x20,0x20,0x7C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, + + 9, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x01,0x00,0x22,0x00,0x1C,0x00, + + 9, // 0x68 'h' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x5E,0x00,0x61,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x00,0x00,0x00,0x10,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xE0, + + 8, // 0x6B 'k' + 0x00,0x00,0x00,0x40,0x40,0x40,0x42,0x44,0x48,0x50,0x70,0x48,0x44,0x42,0x00,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 13, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0xE0,0x63,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0x42,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5E,0x00,0x61,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x00,0x62,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x40,0x00,0x40,0x00,0x40,0x00, + + 9, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x01,0x00,0x01,0x00,0x01,0x00, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x40,0x30,0x0C,0x02,0x42,0x3C,0x00,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x00,0x00,0x00,0x20,0x20,0x7C,0x20,0x20,0x20,0x20,0x20,0x20,0x1C,0x00,0x00,0x00, + + 9, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x43,0x00,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x24,0x24,0x24,0x18,0x18,0x18,0x00,0x00,0x00, + + 11, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x40,0x44,0x40,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x2A,0x80,0x11,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x14,0x00,0x22,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x24,0x24,0x24,0x18,0x18,0x18,0x10,0x10,0x20, + + 8, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x02,0x04,0x08,0x10,0x20,0x40,0x7E,0x00,0x00,0x00, + + 9, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x60,0x00,0x10,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x07,0x00, + + 6, // 0x7C '|' + 0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + + 9, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x04,0x00,0x03,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x70,0x00, + + 11, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x44,0x40,0x44,0x40,0x43,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana17_bold[] = + { + 17, 4, 32, 128-32, + 0x00,0x00,0x12,0x00,0x24,0x00,0x36,0x00,0x59,0x00,0x7C,0x00,0xB0,0x00,0xD3,0x00,0xE5,0x00, + 0xF7,0x00,0x09,0x01,0x2C,0x01,0x4F,0x01,0x61,0x01,0x73,0x01,0x85,0x01,0xA8,0x01,0xCB,0x01, + 0xEE,0x01,0x11,0x02,0x34,0x02,0x57,0x02,0x7A,0x02,0x9D,0x02,0xC0,0x02,0xE3,0x02,0x06,0x03, + 0x18,0x03,0x2A,0x03,0x4D,0x03,0x70,0x03,0x93,0x03,0xB6,0x03,0xD9,0x03,0xFC,0x03,0x1F,0x04, + 0x42,0x04,0x65,0x04,0x88,0x04,0xAB,0x04,0xCE,0x04,0xF1,0x04,0x03,0x05,0x15,0x05,0x38,0x05, + 0x5B,0x05,0x7E,0x05,0xA1,0x05,0xC4,0x05,0xE7,0x05,0x0A,0x06,0x2D,0x06,0x50,0x06,0x73,0x06, + 0x96,0x06,0xB9,0x06,0xDC,0x06,0xFF,0x06,0x22,0x07,0x45,0x07,0x57,0x07,0x7A,0x07,0x8C,0x07, + 0xAF,0x07,0xD2,0x07,0xF5,0x07,0x18,0x08,0x3B,0x08,0x4D,0x08,0x70,0x08,0x93,0x08,0xA5,0x08, + 0xC8,0x08,0xEB,0x08,0xFD,0x08,0x0F,0x09,0x32,0x09,0x44,0x09,0x67,0x09,0x8A,0x09,0xAD,0x09, + 0xD0,0x09,0xF3,0x09,0x05,0x0A,0x17,0x0A,0x29,0x0A,0x4C,0x0A,0x6F,0x0A,0x92,0x0A,0xB5,0x0A, + 0xD8,0x0A,0xEA,0x0A,0x0D,0x0B,0x1F,0x0B,0x42,0x0B,0x65,0x0B, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00, + + 8, // 0x22 '"' + 0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x40,0x04,0x40,0x3F,0xE0,0x3F,0xE0,0x08,0x80,0x11,0x00,0x7F,0xC0,0x7F,0xC0,0x22,0x00,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x1F,0x00,0x34,0x80,0x64,0x00,0x74,0x00,0x3C,0x00,0x0F,0x00,0x0B,0x80,0x09,0x80,0x4B,0x00,0x3E,0x00,0x08,0x00,0x08,0x00,0x00,0x00, + + 18, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x08,0x00,0x66,0x10,0x00,0x66,0x20,0x00,0x66,0x2F,0x00,0x66,0x59,0x80,0x66,0x99,0x80,0x3D,0x19,0x80,0x01,0x19,0x80,0x02,0x19,0x80,0x04,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x33,0x00,0x36,0x00,0x1C,0x60,0x36,0x60,0x63,0x60,0x61,0xC0,0x31,0xC0,0x1F,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x27 ''' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x28 '(' + 0x00,0x00,0x00,0x0C,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0x30,0x18,0x0C, + + 8, // 0x29 ')' + 0x00,0x00,0x00,0x30,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x30, + + 10, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x00,0x2A,0x00,0x1C,0x00,0x2A,0x00,0x49,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x7F,0xC0,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x60,0x60,0xC0,0xC0,0x00, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x00, + + 10, // 0x2F '/' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x60,0x00,0x00,0x00, + + 10, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x3C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x61,0x80,0x61,0x80,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x61,0x80,0x61,0x80,0x01,0x80,0x0F,0x00,0x03,0x00,0x01,0x80,0x61,0x80,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x07,0x00,0x0B,0x00,0x13,0x00,0x23,0x00,0x43,0x00,0x7F,0xC0,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x30,0x00,0x30,0x00,0x3E,0x00,0x03,0x00,0x01,0x80,0x01,0x80,0x61,0x80,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x38,0x00,0x30,0x00,0x6E,0x00,0x73,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x3F,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x03,0x00,0x07,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x38,0x30,0x30,0x60,0x60,0x00, + + 12, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x18,0x00,0x06,0x00,0x01,0x80,0x00,0x40,0x01,0x80,0x06,0x00,0x18,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x18,0x20,0x20,0x10,0x27,0xC8,0x4C,0xC8,0x4C,0xC8,0x4C,0xC8,0x4C,0xC8,0x27,0xF0,0x20,0x00,0x18,0x00,0x07,0xC0,0x00,0x00, + + 11, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x1B,0x00,0x1B,0x00,0x31,0x80,0x3F,0x80,0x31,0x80,0x60,0xC0,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x30,0xC0,0x30,0xC0,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x30,0xC0,0x30,0xC0,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x61,0x80,0x61,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x61,0x80,0x61,0x80,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x30,0xC0,0x30,0xC0,0x60,0x00,0x60,0x00,0x63,0xC0,0x60,0xC0,0x30,0xC0,0x30,0xC0,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x7F,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00, + + 8, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x3E,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x0C,0xF8,0x00,0x00,0x00, + + 11, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x61,0x80,0x63,0x00,0x66,0x00,0x6C,0x00,0x7C,0x00,0x76,0x00,0x63,0x00,0x61,0x80,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x70,0x70,0x70,0x70,0xF0,0x58,0xB0,0x59,0xB0,0x4D,0x30,0x4F,0x30,0x46,0x30,0x46,0x30,0x40,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x40,0x70,0x40,0x58,0x40,0x4C,0x40,0x4C,0x40,0x46,0x40,0x43,0x40,0x43,0x40,0x41,0xC0,0x40,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x30,0xC0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0xC0,0x30,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x63,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x30,0xC0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0xC0,0x30,0xC0,0x0F,0x80,0x03,0x00,0x03,0x00,0x01,0xE0, + + 11, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x63,0x00,0x61,0x80,0x60,0xC0,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x61,0x80,0x60,0x00,0x3E,0x00,0x1F,0x00,0x01,0x80,0x61,0x80,0x63,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x54 'T' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x31,0x80,0x31,0x80,0x1B,0x00,0x1B,0x00,0x0E,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 16, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x86,0x61,0x86,0x63,0xC6,0x32,0x4C,0x36,0x6C,0x36,0x6C,0x34,0x2C,0x1C,0x38,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x31,0x80,0x31,0x80,0x1B,0x00,0x0E,0x00,0x0E,0x00,0x1B,0x00,0x31,0x80,0x31,0x80,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5B '[' + 0x00,0x00,0x00,0x3E,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3E, + + 10, // 0x5C '\' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x01,0x80,0x01,0x80,0x00,0x00, + + 8, // 0x5D ']' + 0x00,0x00,0x00,0x7C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C, + + 12, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x0E,0x00,0x1B,0x00,0x31,0x80,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xC0,0x00,0x00, + + 10, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x03,0x00,0x3F,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x62 'b' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6E,0x00,0x73,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x33,0x60,0x60,0x60,0x60,0x33,0x1E,0x00,0x00,0x00, + + 10, // 0x64 'd' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x63,0x00,0x7F,0x00,0x60,0x00,0x60,0x00,0x33,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x00,0x00,0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00, + + 10, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x01,0x80,0x03,0x00,0x3E,0x00, + + 10, // 0x68 'h' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6F,0x00,0x71,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x69 'i' + 0x00,0x00,0x00,0x60,0x60,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 6, // 0x6A 'j' + 0x00,0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF0, + + 9, // 0x6B 'k' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x63,0x00,0x66,0x00,0x6C,0x00,0x78,0x00,0x7C,0x00,0x66,0x00,0x63,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x6C 'l' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 14, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x70,0x73,0x98,0x63,0x18,0x63,0x18,0x63,0x18,0x63,0x18,0x63,0x18,0x63,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6F,0x00,0x71,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x00,0x73,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00, + + 10, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x01,0x80,0x01,0x80,0x01,0x80, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x7E,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x62,0x60,0x7C,0x3E,0x06,0x46,0x3C,0x00,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x00,0x00,0x00,0x60,0x60,0xFC,0x60,0x60,0x60,0x60,0x60,0x60,0x3C,0x00,0x00,0x00, + + 10, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x80,0x3D,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x18,0x63,0x18,0x33,0x30,0x37,0xB0,0x34,0xB0,0x1C,0xE0,0x1C,0xE0,0x0C,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x36,0x00,0x63,0x00,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x63,0x00,0x63,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x1C,0x00,0x1C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00, + + 8, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x06,0x0C,0x18,0x18,0x30,0x60,0x7E,0x00,0x00,0x00, + + 10, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x70,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x07,0x80, + + 8, // 0x7C '|' + 0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + + 10, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x06,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x78,0x00, + + 12, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x20,0x24,0x20,0x46,0x20,0x42,0x40,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana18[] = + { + 18, 4, 32, 128-32, + 0x00,0x00,0x13,0x00,0x26,0x00,0x39,0x00,0x5E,0x00,0x83,0x00,0xA8,0x00,0xCD,0x00,0xE0,0x00, + 0xF3,0x00,0x06,0x01,0x2B,0x01,0x50,0x01,0x63,0x01,0x76,0x01,0x89,0x01,0x9C,0x01,0xC1,0x01, + 0xE6,0x01,0x0B,0x02,0x30,0x02,0x55,0x02,0x7A,0x02,0x9F,0x02,0xC4,0x02,0xE9,0x02,0x0E,0x03, + 0x21,0x03,0x34,0x03,0x59,0x03,0x7E,0x03,0xA3,0x03,0xB6,0x03,0xDB,0x03,0x00,0x04,0x25,0x04, + 0x4A,0x04,0x6F,0x04,0x94,0x04,0xB9,0x04,0xDE,0x04,0x03,0x05,0x16,0x05,0x29,0x05,0x4E,0x05, + 0x61,0x05,0x86,0x05,0xAB,0x05,0xD0,0x05,0xF5,0x05,0x1A,0x06,0x3F,0x06,0x64,0x06,0x89,0x06, + 0xAE,0x06,0xD3,0x06,0xF8,0x06,0x1D,0x07,0x42,0x07,0x67,0x07,0x7A,0x07,0x8D,0x07,0xA0,0x07, + 0xC5,0x07,0xEA,0x07,0x0F,0x08,0x34,0x08,0x59,0x08,0x6C,0x08,0x91,0x08,0xB6,0x08,0xC9,0x08, + 0xEE,0x08,0x13,0x09,0x26,0x09,0x39,0x09,0x5E,0x09,0x71,0x09,0x96,0x09,0xBB,0x09,0xE0,0x09, + 0x05,0x0A,0x2A,0x0A,0x3D,0x0A,0x50,0x0A,0x63,0x0A,0x88,0x0A,0xAD,0x0A,0xD2,0x0A,0xF7,0x0A, + 0x1C,0x0B,0x41,0x0B,0x66,0x0B,0x79,0x0B,0x9E,0x0B,0xC3,0x0B, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x20,0x00,0x00,0x00, + + 7, // 0x22 '"' + 0x00,0x00,0x00,0x48,0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x80,0x04,0x80,0x09,0x00,0x3F,0xC0,0x09,0x00,0x11,0x00,0x12,0x00,0x7F,0x80,0x12,0x00,0x24,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x3E,0x00,0x49,0x00,0x48,0x00,0x48,0x00,0x38,0x00,0x0E,0x00,0x09,0x00,0x09,0x00,0x49,0x00,0x3E,0x00,0x08,0x00,0x08,0x00,0x08,0x00, + + 16, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x44,0x40,0x44,0x40,0x44,0x80,0x44,0x80,0x38,0x9C,0x01,0x22,0x01,0x22,0x02,0x22,0x02,0x22,0x04,0x1C,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x21,0x00,0x21,0x00,0x1E,0x40,0x24,0x40,0x42,0x40,0x41,0x40,0x40,0x80,0x21,0x40,0x1E,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x27 ''' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x28 '(' + 0x00,0x00,0x00,0x08,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x08, + + 7, // 0x29 ')' + 0x00,0x00,0x00,0x20,0x10,0x08,0x08,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x08,0x10,0x20, + + 10, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x00,0x2A,0x00,0x1C,0x00,0x2A,0x00,0x49,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x3F,0xE0,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x40,0x40, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x00,0x00,0x00, + + 7, // 0x2F '/' + 0x00,0x00,0x00,0x02,0x04,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x00, + + 10, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x1C,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x41,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x01,0x00,0x02,0x00,0x0C,0x00,0x30,0x00,0x40,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x40,0x80,0x00,0x80,0x01,0x00,0x0E,0x00,0x01,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x41,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x05,0x00,0x09,0x00,0x11,0x00,0x21,0x00,0x41,0x00,0x7F,0xC0,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x20,0x00,0x20,0x00,0x20,0x00,0x3E,0x00,0x01,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x41,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x5E,0x00,0x61,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x04,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x20,0x80,0x1F,0x80,0x00,0x80,0x01,0x00,0x02,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00, + + 7, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x20,0x20, + + 12, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x3C,0x42,0x02,0x02,0x04,0x08,0x10,0x10,0x00,0x10,0x10,0x00,0x00,0x00, + + 15, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x18,0x60,0x20,0x10,0x23,0xD0,0x44,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x44,0x48,0x23,0xF0,0x20,0x00,0x18,0x00,0x07,0xC0,0x00,0x00, + + 10, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x21,0x00,0x21,0x00,0x40,0x80,0x7F,0x80,0x40,0x80,0x80,0x40,0x80,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x7E,0x00,0x41,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x41,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x20,0x40,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x40,0x30,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x41,0x80,0x40,0x80,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x80,0x41,0x80,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x40,0x00,0x40,0x00,0x40,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x30,0x60,0x20,0x20,0x40,0x00,0x40,0x00,0x41,0xE0,0x40,0x20,0x40,0x20,0x20,0x20,0x30,0x20,0x0F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0xC0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00, + + 7, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x3C,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x08,0xF0,0x00,0x00,0x00, + + 10, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x41,0x00,0x42,0x00,0x44,0x00,0x48,0x00,0x50,0x00,0x68,0x00,0x44,0x00,0x42,0x00,0x41,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x00,0x00,0x00, + + 13, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x50,0x50,0x50,0x50,0x48,0x90,0x48,0x90,0x45,0x10,0x45,0x10,0x42,0x10,0x42,0x10,0x40,0x10,0x40,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x60,0x40,0x50,0x40,0x48,0x40,0x48,0x40,0x44,0x40,0x42,0x40,0x42,0x40,0x41,0x40,0x40,0xC0,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x20,0x40,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x20,0x40,0x30,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x41,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x41,0x00,0x7E,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x30,0xC0,0x20,0x40,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x20,0x40,0x30,0xC0,0x0F,0x00,0x01,0x00,0x01,0x00,0x00,0xE0, + + 10, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x42,0x00,0x41,0x00,0x40,0x80,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x20,0x80,0x40,0x00,0x40,0x00,0x20,0x00,0x1E,0x00,0x01,0x00,0x00,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x54 'T' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x20,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x80,0x40,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x21,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 15, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x04,0x41,0x04,0x22,0x88,0x22,0x88,0x22,0x88,0x12,0x90,0x14,0x50,0x14,0x50,0x14,0x50,0x08,0x20,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x21,0x00,0x21,0x00,0x12,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x12,0x00,0x21,0x00,0x21,0x00,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x41,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x00,0x80,0x01,0x00,0x02,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x5B '[' + 0x00,0x00,0x00,0x3C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3C, + + 7, // 0x5C '\' + 0x00,0x00,0x00,0x80,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x04,0x02,0x00, + + 7, // 0x5D ']' + 0x00,0x00,0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x78, + + 12, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x09,0x00,0x10,0x80,0x20,0x40,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xC0,0x00,0x00, + + 10, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x01,0x00,0x3F,0x00,0x41,0x00,0x41,0x00,0x43,0x00,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x62 'b' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x5C,0x00,0x62,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x21,0x40,0x40,0x40,0x40,0x21,0x1E,0x00,0x00,0x00, + + 9, // 0x64 'd' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x22,0x00,0x41,0x00,0x7F,0x00,0x40,0x00,0x40,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x00,0x00,0x1C,0x20,0x20,0x20,0x7C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00, + + 9, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x01,0x00,0x22,0x00,0x1C,0x00, + + 9, // 0x68 'h' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x5E,0x00,0x61,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x69 'i' + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 5, // 0x6A 'j' + 0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xE0, + + 9, // 0x6B 'k' + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x42,0x00,0x44,0x00,0x48,0x00,0x50,0x00,0x68,0x00,0x44,0x00,0x42,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 3, // 0x6C 'l' + 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 15, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2E,0x70,0x31,0x88,0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5E,0x00,0x61,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x21,0x00,0x40,0x80,0x40,0x80,0x40,0x80,0x40,0x80,0x21,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x00,0x62,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x42,0x00,0x7C,0x00,0x40,0x00,0x40,0x00,0x40,0x00, + + 9, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x21,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x23,0x00,0x1D,0x00,0x01,0x00,0x01,0x00,0x01,0x00, + + 6, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5C,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00, + + 8, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x40,0x30,0x0C,0x02,0x42,0x3C,0x00,0x00,0x00, + + 6, // 0x74 't' + 0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x7C,0x20,0x20,0x20,0x20,0x20,0x20,0x1C,0x00,0x00,0x00, + + 9, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x41,0x00,0x43,0x00,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x14,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x10,0x42,0x10,0x25,0x20,0x25,0x20,0x28,0xA0,0x28,0xA0,0x10,0x40,0x10,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x14,0x00,0x22,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x41,0x00,0x22,0x00,0x22,0x00,0x22,0x00,0x14,0x00,0x14,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x10,0x00, + + 9, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x02,0x00,0x04,0x00,0x08,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x60,0x00,0x10,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x07,0x00, + + 7, // 0x7C '|' + 0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + + 10, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x02,0x00,0x01,0x80,0x02,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x38,0x00, + + 12, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x20,0x24,0x20,0x42,0x40,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 15, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + + const int8u verdana18_bold[] = + { + 18, 4, 32, 128-32, + 0x00,0x00,0x13,0x00,0x26,0x00,0x4B,0x00,0x70,0x00,0x95,0x00,0xCC,0x00,0xF1,0x00,0x04,0x01, + 0x17,0x01,0x2A,0x01,0x4F,0x01,0x74,0x01,0x87,0x01,0x9A,0x01,0xAD,0x01,0xD2,0x01,0xF7,0x01, + 0x1C,0x02,0x41,0x02,0x66,0x02,0x8B,0x02,0xB0,0x02,0xD5,0x02,0xFA,0x02,0x1F,0x03,0x44,0x03, + 0x57,0x03,0x6A,0x03,0x8F,0x03,0xB4,0x03,0xD9,0x03,0xFE,0x03,0x23,0x04,0x48,0x04,0x6D,0x04, + 0x92,0x04,0xB7,0x04,0xDC,0x04,0x01,0x05,0x26,0x05,0x4B,0x05,0x5E,0x05,0x71,0x05,0x96,0x05, + 0xBB,0x05,0xE0,0x05,0x05,0x06,0x2A,0x06,0x4F,0x06,0x74,0x06,0x99,0x06,0xBE,0x06,0xE3,0x06, + 0x08,0x07,0x2D,0x07,0x52,0x07,0x77,0x07,0x9C,0x07,0xC1,0x07,0xD4,0x07,0xF9,0x07,0x0C,0x08, + 0x31,0x08,0x56,0x08,0x7B,0x08,0xA0,0x08,0xC5,0x08,0xD8,0x08,0xFD,0x08,0x22,0x09,0x35,0x09, + 0x5A,0x09,0x7F,0x09,0x92,0x09,0xA5,0x09,0xCA,0x09,0xDD,0x09,0x02,0x0A,0x27,0x0A,0x4C,0x0A, + 0x71,0x0A,0x96,0x0A,0xA9,0x0A,0xCE,0x0A,0xE1,0x0A,0x06,0x0B,0x2B,0x0B,0x50,0x0B,0x75,0x0B, + 0x9A,0x0B,0xBF,0x0B,0xE4,0x0B,0xF7,0x0B,0x1C,0x0C,0x41,0x0C, + + 5, // 0x20 ' ' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x21 '!' + 0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00, + + 9, // 0x22 '"' + 0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x23 '#' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x20,0x04,0x20,0x08,0x40,0x3F,0xF0,0x3F,0xF0,0x08,0x40,0x10,0x80,0x7F,0xE0,0x7F,0xE0,0x21,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x24 '$' + 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x1F,0x80,0x34,0xC0,0x64,0xC0,0x64,0x00,0x3C,0x00,0x07,0x80,0x04,0xC0,0x64,0xC0,0x65,0x80,0x3F,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + + 19, // 0x25 '%' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x08,0x00,0x63,0x10,0x00,0x63,0x10,0x00,0x63,0x20,0x00,0x63,0x2F,0x80,0x63,0x58,0xC0,0x3E,0x98,0xC0,0x00,0x98,0xC0,0x01,0x18,0xC0,0x01,0x18,0xC0,0x02,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x26 '&' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x33,0x00,0x33,0x00,0x1E,0x60,0x36,0x60,0x63,0x60,0x61,0xC0,0x60,0xC0,0x30,0xE0,0x1F,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x27 ''' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x28 '(' + 0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x30,0x18,0x0C,0x06, + + 8, // 0x29 ')' + 0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x30,0x60, + + 11, // 0x2A '*' + 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x24,0x80,0x15,0x00,0x0E,0x00,0x15,0x00,0x24,0x80,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x2B '+' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x3F,0xE0,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2C ',' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x60,0x60,0x60,0xC0,0xC0, + + 7, // 0x2D '-' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 5, // 0x2E '.' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x00, + + 10, // 0x2F '/' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x60,0x00,0x00,0x00, + + 11, // 0x30 '0' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x31 '1' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x1E,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x1F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x32 '2' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x61,0x80,0x60,0xC0,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x33 '3' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x61,0x80,0x60,0xC0,0x00,0xC0,0x01,0x80,0x0F,0x00,0x01,0x80,0x00,0xC0,0x60,0xC0,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x34 '4' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x03,0x80,0x05,0x80,0x09,0x80,0x11,0x80,0x21,0x80,0x41,0x80,0x7F,0xE0,0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x35 '5' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xC0,0x30,0x00,0x30,0x00,0x30,0x00,0x3F,0x00,0x01,0x80,0x00,0xC0,0x00,0xC0,0x60,0xC0,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x36 '6' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x18,0x00,0x30,0x00,0x60,0x00,0x6F,0x00,0x71,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x37 '7' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0xC0,0x01,0x80,0x01,0x80,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x38 '8' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x39 '9' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x31,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0xC0,0x1E,0xC0,0x00,0xC0,0x01,0x80,0x03,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x3A ':' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x00, + + 6, // 0x3B ';' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x38,0x30,0x30,0x30,0x60,0x60, + + 13, // 0x3C '<' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x30,0x00,0x0C,0x00,0x03,0x00,0x00,0xC0,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x3D '=' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x3E '>' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x18,0x00,0x06,0x00,0x01,0x80,0x00,0x60,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 9, // 0x3F '?' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x63,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x40 '@' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x18,0x60,0x20,0x10,0x27,0xD0,0x4C,0xC8,0x4C,0xC8,0x4C,0xC8,0x4C,0xC8,0x4C,0xC8,0x27,0xF0,0x20,0x00,0x18,0x00,0x07,0xC0,0x00,0x00, + + 12, // 0x41 'A' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0F,0x00,0x0F,0x00,0x19,0x80,0x19,0x80,0x30,0xC0,0x3F,0xC0,0x30,0xC0,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x42 'B' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x43 'C' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x38,0xC0,0x30,0xC0,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x30,0xC0,0x38,0xC0,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x44 'D' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0xC0,0x60,0xC0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x61,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x45 'E' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x46 'F' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x47 'G' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x38,0x60,0x30,0x60,0x60,0x00,0x60,0x00,0x63,0xE0,0x60,0x60,0x60,0x60,0x30,0x60,0x38,0x60,0x0F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x48 'H' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7F,0xE0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x49 'I' + 0x00,0x00,0x00,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00, + + 8, // 0x4A 'J' + 0x00,0x00,0x00,0x00,0x3E,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x0C,0xF8,0x00,0x00,0x00, + + 12, // 0x4B 'K' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0xC0,0x61,0x80,0x63,0x00,0x66,0x00,0x6C,0x00,0x7E,0x00,0x73,0x00,0x61,0x80,0x60,0xC0,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x4C 'L' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x4D 'M' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x38,0x70,0x38,0x70,0x78,0x58,0x58,0x58,0xD8,0x4C,0x98,0x4D,0x98,0x47,0x18,0x47,0x18,0x42,0x18,0x40,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x4E 'N' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x20,0x70,0x20,0x58,0x20,0x4C,0x20,0x4C,0x20,0x46,0x20,0x43,0x20,0x43,0x20,0x41,0xA0,0x40,0xE0,0x40,0xE0,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x4F 'O' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x38,0xE0,0x30,0x60,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x30,0x60,0x38,0xE0,0x0F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x50 'P' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 13, // 0x51 'Q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x38,0xE0,0x30,0x60,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x30,0x60,0x38,0xE0,0x0F,0x80,0x03,0x00,0x03,0x80,0x01,0xF0, + + 12, // 0x52 'R' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x61,0x80,0x60,0xC0,0x60,0x60,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x53 'S' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x30,0xC0,0x60,0xC0,0x60,0x00,0x7C,0x00,0x3F,0x80,0x03,0xC0,0x00,0xC0,0x60,0xC0,0x61,0x80,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x54 'T' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 12, // 0x55 'U' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0xC0,0x1F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x56 'V' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x60,0xC0,0x60,0xC0,0x31,0x80,0x31,0x80,0x31,0x80,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x0E,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 16, // 0x57 'W' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x86,0x61,0x86,0x63,0xC6,0x33,0xCC,0x32,0x4C,0x32,0x4C,0x1E,0x78,0x1C,0x38,0x1C,0x38,0x0C,0x30,0x0C,0x30,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x58 'X' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xC0,0x31,0x80,0x31,0x80,0x1B,0x00,0x0E,0x00,0x0E,0x00,0x0E,0x00,0x1B,0x00,0x31,0x80,0x31,0x80,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x59 'Y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x5A 'Z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x80,0x01,0x80,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0x7F,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x5B '[' + 0x00,0x00,0x00,0x3E,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3E, + + 10, // 0x5C '\' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x01,0x80,0x01,0x80,0x00,0x00, + + 8, // 0x5D ']' + 0x00,0x00,0x00,0x7C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C, + + 13, // 0x5E '^' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0F,0x00,0x19,0x80,0x30,0xC0,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x5F '_' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xE0,0x00,0x00, + + 11, // 0x60 '`' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x61 'a' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x01,0x80,0x01,0x80,0x3F,0x80,0x61,0x80,0x61,0x80,0x63,0x80,0x3D,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x62 'b' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6E,0x00,0x73,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 8, // 0x63 'c' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x33,0x60,0x60,0x60,0x60,0x33,0x1E,0x00,0x00,0x00, + + 10, // 0x64 'd' + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x65 'e' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x7F,0x80,0x60,0x00,0x60,0x00,0x31,0x80,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 6, // 0x66 'f' + 0x00,0x00,0x00,0x1C,0x30,0x30,0x30,0x7C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00, + + 10, // 0x67 'g' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x01,0x80,0x03,0x00,0x3E,0x00, + + 10, // 0x68 'h' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x6F,0x00,0x71,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x69 'i' + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 6, // 0x6A 'j' + 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF0, + + 10, // 0x6B 'k' + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x61,0x80,0x63,0x00,0x66,0x00,0x6C,0x00,0x7E,0x00,0x73,0x00,0x61,0x80,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, + + 4, // 0x6C 'l' + 0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 16, // 0x6D 'm' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6F,0x3C,0x71,0xC6,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x6E 'n' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6F,0x00,0x71,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x6F 'o' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x33,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x70 'p' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x00,0x73,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x00,0x7E,0x00,0x60,0x00,0x60,0x00,0x60,0x00, + + 10, // 0x71 'q' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x33,0x80,0x1D,0x80,0x01,0x80,0x01,0x80,0x01,0x80, + + 7, // 0x72 'r' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E,0x7E,0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x00, + + 9, // 0x73 's' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x61,0x00,0x60,0x00,0x7E,0x00,0x3F,0x00,0x03,0x00,0x43,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 7, // 0x74 't' + 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x7E,0x30,0x30,0x30,0x30,0x30,0x30,0x1E,0x00,0x00,0x00, + + 10, // 0x75 'u' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x61,0x80,0x63,0x80,0x3D,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x76 'v' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x33,0x00,0x33,0x00,0x33,0x00,0x1E,0x00,0x1E,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 14, // 0x77 'w' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x18,0x63,0x18,0x63,0x18,0x37,0xB0,0x34,0xB0,0x3C,0xF0,0x18,0x60,0x18,0x60,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x78 'x' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x33,0x00,0x33,0x00,0x1E,0x00,0x1E,0x00,0x33,0x00,0x33,0x00,0x61,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + + 10, // 0x79 'y' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x80,0x61,0x80,0x33,0x00,0x33,0x00,0x33,0x00,0x1E,0x00,0x1E,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00, + + 9, // 0x7A 'z' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 11, // 0x7B '{' + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x80,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x70,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x07,0x80, + + 8, // 0x7C '|' + 0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + + 11, // 0x7D '}' + 0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x01,0xC0,0x03,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3C,0x00, + + 13, // 0x7E '~' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x10,0x24,0x10,0x42,0x10,0x41,0x20,0x40,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 15, // 0x7F '' + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, + + 0 + }; + +} + diff --git a/desmume/src/windows/agg/src/agg_gsv_text.cpp b/desmume/src/windows/agg/src/agg_gsv_text.cpp new file mode 100644 index 000000000..bc54ef7cb --- /dev/null +++ b/desmume/src/windows/agg/src/agg_gsv_text.cpp @@ -0,0 +1,681 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include +#include "agg_gsv_text.h" +#include "agg_bounding_rect.h" + + + +namespace agg +{ + int8u gsv_default_font[] = + { + 0x40,0x00,0x6c,0x0f,0x15,0x00,0x0e,0x00,0xf9,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0d,0x0a,0x0d,0x0a,0x46,0x6f,0x6e,0x74,0x20,0x28, + 0x63,0x29,0x20,0x4d,0x69,0x63,0x72,0x6f,0x50,0x72, + 0x6f,0x66,0x20,0x32,0x37,0x20,0x53,0x65,0x70,0x74, + 0x65,0x6d,0x62,0x2e,0x31,0x39,0x38,0x39,0x00,0x0d, + 0x0a,0x0d,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x00,0x12,0x00,0x34,0x00,0x46,0x00,0x94,0x00, + 0xd0,0x00,0x2e,0x01,0x3e,0x01,0x64,0x01,0x8a,0x01, + 0x98,0x01,0xa2,0x01,0xb4,0x01,0xba,0x01,0xc6,0x01, + 0xcc,0x01,0xf0,0x01,0xfa,0x01,0x18,0x02,0x38,0x02, + 0x44,0x02,0x68,0x02,0x98,0x02,0xa2,0x02,0xde,0x02, + 0x0e,0x03,0x24,0x03,0x40,0x03,0x48,0x03,0x52,0x03, + 0x5a,0x03,0x82,0x03,0xec,0x03,0xfa,0x03,0x26,0x04, + 0x4c,0x04,0x6a,0x04,0x7c,0x04,0x8a,0x04,0xb6,0x04, + 0xc4,0x04,0xca,0x04,0xe0,0x04,0xee,0x04,0xf8,0x04, + 0x0a,0x05,0x18,0x05,0x44,0x05,0x5e,0x05,0x8e,0x05, + 0xac,0x05,0xd6,0x05,0xe0,0x05,0xf6,0x05,0x00,0x06, + 0x12,0x06,0x1c,0x06,0x28,0x06,0x36,0x06,0x48,0x06, + 0x4e,0x06,0x60,0x06,0x6e,0x06,0x74,0x06,0x84,0x06, + 0xa6,0x06,0xc8,0x06,0xe6,0x06,0x08,0x07,0x2c,0x07, + 0x3c,0x07,0x68,0x07,0x7c,0x07,0x8c,0x07,0xa2,0x07, + 0xb0,0x07,0xb6,0x07,0xd8,0x07,0xec,0x07,0x10,0x08, + 0x32,0x08,0x54,0x08,0x64,0x08,0x88,0x08,0x98,0x08, + 0xac,0x08,0xb6,0x08,0xc8,0x08,0xd2,0x08,0xe4,0x08, + 0xf2,0x08,0x3e,0x09,0x48,0x09,0x94,0x09,0xc2,0x09, + 0xc4,0x09,0xd0,0x09,0xe2,0x09,0x04,0x0a,0x0e,0x0a, + 0x26,0x0a,0x34,0x0a,0x4a,0x0a,0x66,0x0a,0x70,0x0a, + 0x7e,0x0a,0x8e,0x0a,0x9a,0x0a,0xa6,0x0a,0xb4,0x0a, + 0xd8,0x0a,0xe2,0x0a,0xf6,0x0a,0x18,0x0b,0x22,0x0b, + 0x32,0x0b,0x56,0x0b,0x60,0x0b,0x6e,0x0b,0x7c,0x0b, + 0x8a,0x0b,0x9c,0x0b,0x9e,0x0b,0xb2,0x0b,0xc2,0x0b, + 0xd8,0x0b,0xf4,0x0b,0x08,0x0c,0x30,0x0c,0x56,0x0c, + 0x72,0x0c,0x90,0x0c,0xb2,0x0c,0xce,0x0c,0xe2,0x0c, + 0xfe,0x0c,0x10,0x0d,0x26,0x0d,0x36,0x0d,0x42,0x0d, + 0x4e,0x0d,0x5c,0x0d,0x78,0x0d,0x8c,0x0d,0x8e,0x0d, + 0x90,0x0d,0x92,0x0d,0x94,0x0d,0x96,0x0d,0x98,0x0d, + 0x9a,0x0d,0x9c,0x0d,0x9e,0x0d,0xa0,0x0d,0xa2,0x0d, + 0xa4,0x0d,0xa6,0x0d,0xa8,0x0d,0xaa,0x0d,0xac,0x0d, + 0xae,0x0d,0xb0,0x0d,0xb2,0x0d,0xb4,0x0d,0xb6,0x0d, + 0xb8,0x0d,0xba,0x0d,0xbc,0x0d,0xbe,0x0d,0xc0,0x0d, + 0xc2,0x0d,0xc4,0x0d,0xc6,0x0d,0xc8,0x0d,0xca,0x0d, + 0xcc,0x0d,0xce,0x0d,0xd0,0x0d,0xd2,0x0d,0xd4,0x0d, + 0xd6,0x0d,0xd8,0x0d,0xda,0x0d,0xdc,0x0d,0xde,0x0d, + 0xe0,0x0d,0xe2,0x0d,0xe4,0x0d,0xe6,0x0d,0xe8,0x0d, + 0xea,0x0d,0xec,0x0d,0x0c,0x0e,0x26,0x0e,0x48,0x0e, + 0x64,0x0e,0x88,0x0e,0x92,0x0e,0xa6,0x0e,0xb4,0x0e, + 0xd0,0x0e,0xee,0x0e,0x02,0x0f,0x16,0x0f,0x26,0x0f, + 0x3c,0x0f,0x58,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f, + 0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f, + 0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f, + 0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x6c,0x0f,0x10,0x80, + 0x05,0x95,0x00,0x72,0x00,0xfb,0xff,0x7f,0x01,0x7f, + 0x01,0x01,0xff,0x01,0x05,0xfe,0x05,0x95,0xff,0x7f, + 0x00,0x7a,0x01,0x86,0xff,0x7a,0x01,0x87,0x01,0x7f, + 0xfe,0x7a,0x0a,0x87,0xff,0x7f,0x00,0x7a,0x01,0x86, + 0xff,0x7a,0x01,0x87,0x01,0x7f,0xfe,0x7a,0x05,0xf2, + 0x0b,0x95,0xf9,0x64,0x0d,0x9c,0xf9,0x64,0xfa,0x91, + 0x0e,0x00,0xf1,0xfa,0x0e,0x00,0x04,0xfc,0x08,0x99, + 0x00,0x63,0x04,0x9d,0x00,0x63,0x04,0x96,0xff,0x7f, + 0x01,0x7f,0x01,0x01,0x00,0x01,0xfe,0x02,0xfd,0x01, + 0xfc,0x00,0xfd,0x7f,0xfe,0x7e,0x00,0x7e,0x01,0x7e, + 0x01,0x7f,0x02,0x7f,0x06,0x7e,0x02,0x7f,0x02,0x7e, + 0xf2,0x89,0x02,0x7e,0x02,0x7f,0x06,0x7e,0x02,0x7f, + 0x01,0x7f,0x01,0x7e,0x00,0x7c,0xfe,0x7e,0xfd,0x7f, + 0xfc,0x00,0xfd,0x01,0xfe,0x02,0x00,0x01,0x01,0x01, + 0x01,0x7f,0xff,0x7f,0x10,0xfd,0x15,0x95,0xee,0x6b, + 0x05,0x95,0x02,0x7e,0x00,0x7e,0xff,0x7e,0xfe,0x7f, + 0xfe,0x00,0xfe,0x02,0x00,0x02,0x01,0x02,0x02,0x01, + 0x02,0x00,0x02,0x7f,0x03,0x7f,0x03,0x00,0x03,0x01, + 0x02,0x01,0xfc,0xf2,0xfe,0x7f,0xff,0x7e,0x00,0x7e, + 0x02,0x7e,0x02,0x00,0x02,0x01,0x01,0x02,0x00,0x02, + 0xfe,0x02,0xfe,0x00,0x07,0xf9,0x15,0x8d,0xff,0x7f, + 0x01,0x7f,0x01,0x01,0x00,0x01,0xff,0x01,0xff,0x00, + 0xff,0x7f,0xff,0x7e,0xfe,0x7b,0xfe,0x7d,0xfe,0x7e, + 0xfe,0x7f,0xfd,0x00,0xfd,0x01,0xff,0x02,0x00,0x03, + 0x01,0x02,0x06,0x04,0x02,0x02,0x01,0x02,0x00,0x02, + 0xff,0x02,0xfe,0x01,0xfe,0x7f,0xff,0x7e,0x00,0x7e, + 0x01,0x7d,0x02,0x7d,0x05,0x79,0x02,0x7e,0x03,0x7f, + 0x01,0x00,0x01,0x01,0x00,0x01,0xf1,0xfe,0xfe,0x01, + 0xff,0x02,0x00,0x03,0x01,0x02,0x02,0x02,0x00,0x86, + 0x01,0x7e,0x08,0x75,0x02,0x7e,0x02,0x7f,0x05,0x80, + 0x05,0x93,0xff,0x01,0x01,0x01,0x01,0x7f,0x00,0x7e, + 0xff,0x7e,0xff,0x7f,0x06,0xf1,0x0b,0x99,0xfe,0x7e, + 0xfe,0x7d,0xfe,0x7c,0xff,0x7b,0x00,0x7c,0x01,0x7b, + 0x02,0x7c,0x02,0x7d,0x02,0x7e,0xfe,0x9e,0xfe,0x7c, + 0xff,0x7d,0xff,0x7b,0x00,0x7c,0x01,0x7b,0x01,0x7d, + 0x02,0x7c,0x05,0x85,0x03,0x99,0x02,0x7e,0x02,0x7d, + 0x02,0x7c,0x01,0x7b,0x00,0x7c,0xff,0x7b,0xfe,0x7c, + 0xfe,0x7d,0xfe,0x7e,0x02,0x9e,0x02,0x7c,0x01,0x7d, + 0x01,0x7b,0x00,0x7c,0xff,0x7b,0xff,0x7d,0xfe,0x7c, + 0x09,0x85,0x08,0x95,0x00,0x74,0xfb,0x89,0x0a,0x7a, + 0x00,0x86,0xf6,0x7a,0x0d,0xf4,0x0d,0x92,0x00,0x6e, + 0xf7,0x89,0x12,0x00,0x04,0xf7,0x06,0x81,0xff,0x7f, + 0xff,0x01,0x01,0x01,0x01,0x7f,0x00,0x7e,0xff,0x7e, + 0xff,0x7f,0x06,0x84,0x04,0x89,0x12,0x00,0x04,0xf7, + 0x05,0x82,0xff,0x7f,0x01,0x7f,0x01,0x01,0xff,0x01, + 0x05,0xfe,0x00,0xfd,0x0e,0x18,0x00,0xeb,0x09,0x95, + 0xfd,0x7f,0xfe,0x7d,0xff,0x7b,0x00,0x7d,0x01,0x7b, + 0x02,0x7d,0x03,0x7f,0x02,0x00,0x03,0x01,0x02,0x03, + 0x01,0x05,0x00,0x03,0xff,0x05,0xfe,0x03,0xfd,0x01, + 0xfe,0x00,0x0b,0xeb,0x06,0x91,0x02,0x01,0x03,0x03, + 0x00,0x6b,0x09,0x80,0x04,0x90,0x00,0x01,0x01,0x02, + 0x01,0x01,0x02,0x01,0x04,0x00,0x02,0x7f,0x01,0x7f, + 0x01,0x7e,0x00,0x7e,0xff,0x7e,0xfe,0x7d,0xf6,0x76, + 0x0e,0x00,0x03,0x80,0x05,0x95,0x0b,0x00,0xfa,0x78, + 0x03,0x00,0x02,0x7f,0x01,0x7f,0x01,0x7d,0x00,0x7e, + 0xff,0x7d,0xfe,0x7e,0xfd,0x7f,0xfd,0x00,0xfd,0x01, + 0xff,0x01,0xff,0x02,0x11,0xfc,0x0d,0x95,0xf6,0x72, + 0x0f,0x00,0xfb,0x8e,0x00,0x6b,0x07,0x80,0x0f,0x95, + 0xf6,0x00,0xff,0x77,0x01,0x01,0x03,0x01,0x03,0x00, + 0x03,0x7f,0x02,0x7e,0x01,0x7d,0x00,0x7e,0xff,0x7d, + 0xfe,0x7e,0xfd,0x7f,0xfd,0x00,0xfd,0x01,0xff,0x01, + 0xff,0x02,0x11,0xfc,0x10,0x92,0xff,0x02,0xfd,0x01, + 0xfe,0x00,0xfd,0x7f,0xfe,0x7d,0xff,0x7b,0x00,0x7b, + 0x01,0x7c,0x02,0x7e,0x03,0x7f,0x01,0x00,0x03,0x01, + 0x02,0x02,0x01,0x03,0x00,0x01,0xff,0x03,0xfe,0x02, + 0xfd,0x01,0xff,0x00,0xfd,0x7f,0xfe,0x7e,0xff,0x7d, + 0x10,0xf9,0x11,0x95,0xf6,0x6b,0xfc,0x95,0x0e,0x00, + 0x03,0xeb,0x08,0x95,0xfd,0x7f,0xff,0x7e,0x00,0x7e, + 0x01,0x7e,0x02,0x7f,0x04,0x7f,0x03,0x7f,0x02,0x7e, + 0x01,0x7e,0x00,0x7d,0xff,0x7e,0xff,0x7f,0xfd,0x7f, + 0xfc,0x00,0xfd,0x01,0xff,0x01,0xff,0x02,0x00,0x03, + 0x01,0x02,0x02,0x02,0x03,0x01,0x04,0x01,0x02,0x01, + 0x01,0x02,0x00,0x02,0xff,0x02,0xfd,0x01,0xfc,0x00, + 0x0c,0xeb,0x10,0x8e,0xff,0x7d,0xfe,0x7e,0xfd,0x7f, + 0xff,0x00,0xfd,0x01,0xfe,0x02,0xff,0x03,0x00,0x01, + 0x01,0x03,0x02,0x02,0x03,0x01,0x01,0x00,0x03,0x7f, + 0x02,0x7e,0x01,0x7c,0x00,0x7b,0xff,0x7b,0xfe,0x7d, + 0xfd,0x7f,0xfe,0x00,0xfd,0x01,0xff,0x02,0x10,0xfd, + 0x05,0x8e,0xff,0x7f,0x01,0x7f,0x01,0x01,0xff,0x01, + 0x00,0xf4,0xff,0x7f,0x01,0x7f,0x01,0x01,0xff,0x01, + 0x05,0xfe,0x05,0x8e,0xff,0x7f,0x01,0x7f,0x01,0x01, + 0xff,0x01,0x01,0xf3,0xff,0x7f,0xff,0x01,0x01,0x01, + 0x01,0x7f,0x00,0x7e,0xff,0x7e,0xff,0x7f,0x06,0x84, + 0x14,0x92,0xf0,0x77,0x10,0x77,0x04,0x80,0x04,0x8c, + 0x12,0x00,0xee,0xfa,0x12,0x00,0x04,0xfa,0x04,0x92, + 0x10,0x77,0xf0,0x77,0x14,0x80,0x03,0x90,0x00,0x01, + 0x01,0x02,0x01,0x01,0x02,0x01,0x04,0x00,0x02,0x7f, + 0x01,0x7f,0x01,0x7e,0x00,0x7e,0xff,0x7e,0xff,0x7f, + 0xfc,0x7e,0x00,0x7d,0x00,0xfb,0xff,0x7f,0x01,0x7f, + 0x01,0x01,0xff,0x01,0x09,0xfe,0x12,0x8d,0xff,0x02, + 0xfe,0x01,0xfd,0x00,0xfe,0x7f,0xff,0x7f,0xff,0x7d, + 0x00,0x7d,0x01,0x7e,0x02,0x7f,0x03,0x00,0x02,0x01, + 0x01,0x02,0xfb,0x88,0xfe,0x7e,0xff,0x7d,0x00,0x7d, + 0x01,0x7e,0x01,0x7f,0x07,0x8b,0xff,0x78,0x00,0x7e, + 0x02,0x7f,0x02,0x00,0x02,0x02,0x01,0x03,0x00,0x02, + 0xff,0x03,0xff,0x02,0xfe,0x02,0xfe,0x01,0xfd,0x01, + 0xfd,0x00,0xfd,0x7f,0xfe,0x7f,0xfe,0x7e,0xff,0x7e, + 0xff,0x7d,0x00,0x7d,0x01,0x7d,0x01,0x7e,0x02,0x7e, + 0x02,0x7f,0x03,0x7f,0x03,0x00,0x03,0x01,0x02,0x01, + 0x01,0x01,0xfe,0x8d,0xff,0x78,0x00,0x7e,0x01,0x7f, + 0x08,0xfb,0x09,0x95,0xf8,0x6b,0x08,0x95,0x08,0x6b, + 0xf3,0x87,0x0a,0x00,0x04,0xf9,0x04,0x95,0x00,0x6b, + 0x00,0x95,0x09,0x00,0x03,0x7f,0x01,0x7f,0x01,0x7e, + 0x00,0x7e,0xff,0x7e,0xff,0x7f,0xfd,0x7f,0xf7,0x80, + 0x09,0x00,0x03,0x7f,0x01,0x7f,0x01,0x7e,0x00,0x7d, + 0xff,0x7e,0xff,0x7f,0xfd,0x7f,0xf7,0x00,0x11,0x80, + 0x12,0x90,0xff,0x02,0xfe,0x02,0xfe,0x01,0xfc,0x00, + 0xfe,0x7f,0xfe,0x7e,0xff,0x7e,0xff,0x7d,0x00,0x7b, + 0x01,0x7d,0x01,0x7e,0x02,0x7e,0x02,0x7f,0x04,0x00, + 0x02,0x01,0x02,0x02,0x01,0x02,0x03,0xfb,0x04,0x95, + 0x00,0x6b,0x00,0x95,0x07,0x00,0x03,0x7f,0x02,0x7e, + 0x01,0x7e,0x01,0x7d,0x00,0x7b,0xff,0x7d,0xff,0x7e, + 0xfe,0x7e,0xfd,0x7f,0xf9,0x00,0x11,0x80,0x04,0x95, + 0x00,0x6b,0x00,0x95,0x0d,0x00,0xf3,0xf6,0x08,0x00, + 0xf8,0xf5,0x0d,0x00,0x02,0x80,0x04,0x95,0x00,0x6b, + 0x00,0x95,0x0d,0x00,0xf3,0xf6,0x08,0x00,0x06,0xf5, + 0x12,0x90,0xff,0x02,0xfe,0x02,0xfe,0x01,0xfc,0x00, + 0xfe,0x7f,0xfe,0x7e,0xff,0x7e,0xff,0x7d,0x00,0x7b, + 0x01,0x7d,0x01,0x7e,0x02,0x7e,0x02,0x7f,0x04,0x00, + 0x02,0x01,0x02,0x02,0x01,0x02,0x00,0x03,0xfb,0x80, + 0x05,0x00,0x03,0xf8,0x04,0x95,0x00,0x6b,0x0e,0x95, + 0x00,0x6b,0xf2,0x8b,0x0e,0x00,0x04,0xf5,0x04,0x95, + 0x00,0x6b,0x04,0x80,0x0c,0x95,0x00,0x70,0xff,0x7d, + 0xff,0x7f,0xfe,0x7f,0xfe,0x00,0xfe,0x01,0xff,0x01, + 0xff,0x03,0x00,0x02,0x0e,0xf9,0x04,0x95,0x00,0x6b, + 0x0e,0x95,0xf2,0x72,0x05,0x85,0x09,0x74,0x03,0x80, + 0x04,0x95,0x00,0x6b,0x00,0x80,0x0c,0x00,0x01,0x80, + 0x04,0x95,0x00,0x6b,0x00,0x95,0x08,0x6b,0x08,0x95, + 0xf8,0x6b,0x08,0x95,0x00,0x6b,0x04,0x80,0x04,0x95, + 0x00,0x6b,0x00,0x95,0x0e,0x6b,0x00,0x95,0x00,0x6b, + 0x04,0x80,0x09,0x95,0xfe,0x7f,0xfe,0x7e,0xff,0x7e, + 0xff,0x7d,0x00,0x7b,0x01,0x7d,0x01,0x7e,0x02,0x7e, + 0x02,0x7f,0x04,0x00,0x02,0x01,0x02,0x02,0x01,0x02, + 0x01,0x03,0x00,0x05,0xff,0x03,0xff,0x02,0xfe,0x02, + 0xfe,0x01,0xfc,0x00,0x0d,0xeb,0x04,0x95,0x00,0x6b, + 0x00,0x95,0x09,0x00,0x03,0x7f,0x01,0x7f,0x01,0x7e, + 0x00,0x7d,0xff,0x7e,0xff,0x7f,0xfd,0x7f,0xf7,0x00, + 0x11,0xf6,0x09,0x95,0xfe,0x7f,0xfe,0x7e,0xff,0x7e, + 0xff,0x7d,0x00,0x7b,0x01,0x7d,0x01,0x7e,0x02,0x7e, + 0x02,0x7f,0x04,0x00,0x02,0x01,0x02,0x02,0x01,0x02, + 0x01,0x03,0x00,0x05,0xff,0x03,0xff,0x02,0xfe,0x02, + 0xfe,0x01,0xfc,0x00,0x03,0xef,0x06,0x7a,0x04,0x82, + 0x04,0x95,0x00,0x6b,0x00,0x95,0x09,0x00,0x03,0x7f, + 0x01,0x7f,0x01,0x7e,0x00,0x7e,0xff,0x7e,0xff,0x7f, + 0xfd,0x7f,0xf7,0x00,0x07,0x80,0x07,0x75,0x03,0x80, + 0x11,0x92,0xfe,0x02,0xfd,0x01,0xfc,0x00,0xfd,0x7f, + 0xfe,0x7e,0x00,0x7e,0x01,0x7e,0x01,0x7f,0x02,0x7f, + 0x06,0x7e,0x02,0x7f,0x01,0x7f,0x01,0x7e,0x00,0x7d, + 0xfe,0x7e,0xfd,0x7f,0xfc,0x00,0xfd,0x01,0xfe,0x02, + 0x11,0xfd,0x08,0x95,0x00,0x6b,0xf9,0x95,0x0e,0x00, + 0x01,0xeb,0x04,0x95,0x00,0x71,0x01,0x7d,0x02,0x7e, + 0x03,0x7f,0x02,0x00,0x03,0x01,0x02,0x02,0x01,0x03, + 0x00,0x0f,0x04,0xeb,0x01,0x95,0x08,0x6b,0x08,0x95, + 0xf8,0x6b,0x09,0x80,0x02,0x95,0x05,0x6b,0x05,0x95, + 0xfb,0x6b,0x05,0x95,0x05,0x6b,0x05,0x95,0xfb,0x6b, + 0x07,0x80,0x03,0x95,0x0e,0x6b,0x00,0x95,0xf2,0x6b, + 0x11,0x80,0x01,0x95,0x08,0x76,0x00,0x75,0x08,0x95, + 0xf8,0x76,0x09,0xf5,0x11,0x95,0xf2,0x6b,0x00,0x95, + 0x0e,0x00,0xf2,0xeb,0x0e,0x00,0x03,0x80,0x03,0x93, + 0x00,0x6c,0x01,0x94,0x00,0x6c,0xff,0x94,0x05,0x00, + 0xfb,0xec,0x05,0x00,0x02,0x81,0x00,0x95,0x0e,0x68, + 0x00,0x83,0x06,0x93,0x00,0x6c,0x01,0x94,0x00,0x6c, + 0xfb,0x94,0x05,0x00,0xfb,0xec,0x05,0x00,0x03,0x81, + 0x03,0x87,0x08,0x05,0x08,0x7b,0xf0,0x80,0x08,0x04, + 0x08,0x7c,0x03,0xf9,0x01,0x80,0x10,0x00,0x01,0x80, + 0x06,0x95,0xff,0x7f,0xff,0x7e,0x00,0x7e,0x01,0x7f, + 0x01,0x01,0xff,0x01,0x05,0xef,0x0f,0x8e,0x00,0x72, + 0x00,0x8b,0xfe,0x02,0xfe,0x01,0xfd,0x00,0xfe,0x7f, + 0xfe,0x7e,0xff,0x7d,0x00,0x7e,0x01,0x7d,0x02,0x7e, + 0x02,0x7f,0x03,0x00,0x02,0x01,0x02,0x02,0x04,0xfd, + 0x04,0x95,0x00,0x6b,0x00,0x8b,0x02,0x02,0x02,0x01, + 0x03,0x00,0x02,0x7f,0x02,0x7e,0x01,0x7d,0x00,0x7e, + 0xff,0x7d,0xfe,0x7e,0xfe,0x7f,0xfd,0x00,0xfe,0x01, + 0xfe,0x02,0x0f,0xfd,0x0f,0x8b,0xfe,0x02,0xfe,0x01, + 0xfd,0x00,0xfe,0x7f,0xfe,0x7e,0xff,0x7d,0x00,0x7e, + 0x01,0x7d,0x02,0x7e,0x02,0x7f,0x03,0x00,0x02,0x01, + 0x02,0x02,0x03,0xfd,0x0f,0x95,0x00,0x6b,0x00,0x8b, + 0xfe,0x02,0xfe,0x01,0xfd,0x00,0xfe,0x7f,0xfe,0x7e, + 0xff,0x7d,0x00,0x7e,0x01,0x7d,0x02,0x7e,0x02,0x7f, + 0x03,0x00,0x02,0x01,0x02,0x02,0x04,0xfd,0x03,0x88, + 0x0c,0x00,0x00,0x02,0xff,0x02,0xff,0x01,0xfe,0x01, + 0xfd,0x00,0xfe,0x7f,0xfe,0x7e,0xff,0x7d,0x00,0x7e, + 0x01,0x7d,0x02,0x7e,0x02,0x7f,0x03,0x00,0x02,0x01, + 0x02,0x02,0x03,0xfd,0x0a,0x95,0xfe,0x00,0xfe,0x7f, + 0xff,0x7d,0x00,0x6f,0xfd,0x8e,0x07,0x00,0x03,0xf2, + 0x0f,0x8e,0x00,0x70,0xff,0x7d,0xff,0x7f,0xfe,0x7f, + 0xfd,0x00,0xfe,0x01,0x09,0x91,0xfe,0x02,0xfe,0x01, + 0xfd,0x00,0xfe,0x7f,0xfe,0x7e,0xff,0x7d,0x00,0x7e, + 0x01,0x7d,0x02,0x7e,0x02,0x7f,0x03,0x00,0x02,0x01, + 0x02,0x02,0x04,0xfd,0x04,0x95,0x00,0x6b,0x00,0x8a, + 0x03,0x03,0x02,0x01,0x03,0x00,0x02,0x7f,0x01,0x7d, + 0x00,0x76,0x04,0x80,0x03,0x95,0x01,0x7f,0x01,0x01, + 0xff,0x01,0xff,0x7f,0x01,0xf9,0x00,0x72,0x04,0x80, + 0x05,0x95,0x01,0x7f,0x01,0x01,0xff,0x01,0xff,0x7f, + 0x01,0xf9,0x00,0x6f,0xff,0x7d,0xfe,0x7f,0xfe,0x00, + 0x09,0x87,0x04,0x95,0x00,0x6b,0x0a,0x8e,0xf6,0x76, + 0x04,0x84,0x07,0x78,0x02,0x80,0x04,0x95,0x00,0x6b, + 0x04,0x80,0x04,0x8e,0x00,0x72,0x00,0x8a,0x03,0x03, + 0x02,0x01,0x03,0x00,0x02,0x7f,0x01,0x7d,0x00,0x76, + 0x00,0x8a,0x03,0x03,0x02,0x01,0x03,0x00,0x02,0x7f, + 0x01,0x7d,0x00,0x76,0x04,0x80,0x04,0x8e,0x00,0x72, + 0x00,0x8a,0x03,0x03,0x02,0x01,0x03,0x00,0x02,0x7f, + 0x01,0x7d,0x00,0x76,0x04,0x80,0x08,0x8e,0xfe,0x7f, + 0xfe,0x7e,0xff,0x7d,0x00,0x7e,0x01,0x7d,0x02,0x7e, + 0x02,0x7f,0x03,0x00,0x02,0x01,0x02,0x02,0x01,0x03, + 0x00,0x02,0xff,0x03,0xfe,0x02,0xfe,0x01,0xfd,0x00, + 0x0b,0xf2,0x04,0x8e,0x00,0x6b,0x00,0x92,0x02,0x02, + 0x02,0x01,0x03,0x00,0x02,0x7f,0x02,0x7e,0x01,0x7d, + 0x00,0x7e,0xff,0x7d,0xfe,0x7e,0xfe,0x7f,0xfd,0x00, + 0xfe,0x01,0xfe,0x02,0x0f,0xfd,0x0f,0x8e,0x00,0x6b, + 0x00,0x92,0xfe,0x02,0xfe,0x01,0xfd,0x00,0xfe,0x7f, + 0xfe,0x7e,0xff,0x7d,0x00,0x7e,0x01,0x7d,0x02,0x7e, + 0x02,0x7f,0x03,0x00,0x02,0x01,0x02,0x02,0x04,0xfd, + 0x04,0x8e,0x00,0x72,0x00,0x88,0x01,0x03,0x02,0x02, + 0x02,0x01,0x03,0x00,0x01,0xf2,0x0e,0x8b,0xff,0x02, + 0xfd,0x01,0xfd,0x00,0xfd,0x7f,0xff,0x7e,0x01,0x7e, + 0x02,0x7f,0x05,0x7f,0x02,0x7f,0x01,0x7e,0x00,0x7f, + 0xff,0x7e,0xfd,0x7f,0xfd,0x00,0xfd,0x01,0xff,0x02, + 0x0e,0xfd,0x05,0x95,0x00,0x6f,0x01,0x7d,0x02,0x7f, + 0x02,0x00,0xf8,0x8e,0x07,0x00,0x03,0xf2,0x04,0x8e, + 0x00,0x76,0x01,0x7d,0x02,0x7f,0x03,0x00,0x02,0x01, + 0x03,0x03,0x00,0x8a,0x00,0x72,0x04,0x80,0x02,0x8e, + 0x06,0x72,0x06,0x8e,0xfa,0x72,0x08,0x80,0x03,0x8e, + 0x04,0x72,0x04,0x8e,0xfc,0x72,0x04,0x8e,0x04,0x72, + 0x04,0x8e,0xfc,0x72,0x07,0x80,0x03,0x8e,0x0b,0x72, + 0x00,0x8e,0xf5,0x72,0x0e,0x80,0x02,0x8e,0x06,0x72, + 0x06,0x8e,0xfa,0x72,0xfe,0x7c,0xfe,0x7e,0xfe,0x7f, + 0xff,0x00,0x0f,0x87,0x0e,0x8e,0xf5,0x72,0x00,0x8e, + 0x0b,0x00,0xf5,0xf2,0x0b,0x00,0x03,0x80,0x09,0x99, + 0xfe,0x7f,0xff,0x7f,0xff,0x7e,0x00,0x7e,0x01,0x7e, + 0x01,0x7f,0x01,0x7e,0x00,0x7e,0xfe,0x7e,0x01,0x8e, + 0xff,0x7e,0x00,0x7e,0x01,0x7e,0x01,0x7f,0x01,0x7e, + 0x00,0x7e,0xff,0x7e,0xfc,0x7e,0x04,0x7e,0x01,0x7e, + 0x00,0x7e,0xff,0x7e,0xff,0x7f,0xff,0x7e,0x00,0x7e, + 0x01,0x7e,0xff,0x8e,0x02,0x7e,0x00,0x7e,0xff,0x7e, + 0xff,0x7f,0xff,0x7e,0x00,0x7e,0x01,0x7e,0x01,0x7f, + 0x02,0x7f,0x05,0x87,0x04,0x95,0x00,0x77,0x00,0xfd, + 0x00,0x77,0x04,0x80,0x05,0x99,0x02,0x7f,0x01,0x7f, + 0x01,0x7e,0x00,0x7e,0xff,0x7e,0xff,0x7f,0xff,0x7e, + 0x00,0x7e,0x02,0x7e,0xff,0x8e,0x01,0x7e,0x00,0x7e, + 0xff,0x7e,0xff,0x7f,0xff,0x7e,0x00,0x7e,0x01,0x7e, + 0x04,0x7e,0xfc,0x7e,0xff,0x7e,0x00,0x7e,0x01,0x7e, + 0x01,0x7f,0x01,0x7e,0x00,0x7e,0xff,0x7e,0x01,0x8e, + 0xfe,0x7e,0x00,0x7e,0x01,0x7e,0x01,0x7f,0x01,0x7e, + 0x00,0x7e,0xff,0x7e,0xff,0x7f,0xfe,0x7f,0x09,0x87, + 0x03,0x86,0x00,0x02,0x01,0x03,0x02,0x01,0x02,0x00, + 0x02,0x7f,0x04,0x7d,0x02,0x7f,0x02,0x00,0x02,0x01, + 0x01,0x02,0xee,0xfe,0x01,0x02,0x02,0x01,0x02,0x00, + 0x02,0x7f,0x04,0x7d,0x02,0x7f,0x02,0x00,0x02,0x01, + 0x01,0x03,0x00,0x02,0x03,0xf4,0x10,0x80,0x03,0x80, + 0x07,0x15,0x08,0x6b,0xfe,0x85,0xf5,0x00,0x10,0xfb, + 0x0d,0x95,0xf6,0x00,0x00,0x6b,0x0a,0x00,0x02,0x02, + 0x00,0x08,0xfe,0x02,0xf6,0x00,0x0e,0xf4,0x03,0x80, + 0x00,0x15,0x0a,0x00,0x02,0x7e,0x00,0x7e,0x00,0x7d, + 0x00,0x7e,0xfe,0x7f,0xf6,0x00,0x0a,0x80,0x02,0x7e, + 0x01,0x7e,0x00,0x7d,0xff,0x7d,0xfe,0x7f,0xf6,0x00, + 0x10,0x80,0x03,0x80,0x00,0x15,0x0c,0x00,0xff,0x7e, + 0x03,0xed,0x03,0xfd,0x00,0x03,0x02,0x00,0x00,0x12, + 0x02,0x03,0x0a,0x00,0x00,0x6b,0x02,0x00,0x00,0x7d, + 0xfe,0x83,0xf4,0x00,0x11,0x80,0x0f,0x80,0xf4,0x00, + 0x00,0x15,0x0c,0x00,0xff,0xf6,0xf5,0x00,0x0f,0xf5, + 0x04,0x95,0x07,0x76,0x00,0x0a,0x07,0x80,0xf9,0x76, + 0x00,0x75,0xf8,0x80,0x07,0x0c,0x09,0xf4,0xf9,0x0c, + 0x09,0xf4,0x03,0x92,0x02,0x03,0x07,0x00,0x03,0x7d, + 0x00,0x7b,0xfc,0x7e,0x04,0x7d,0x00,0x7a,0xfd,0x7e, + 0xf9,0x00,0xfe,0x02,0x06,0x89,0x02,0x00,0x06,0xf5, + 0x03,0x95,0x00,0x6b,0x0c,0x15,0x00,0x6b,0x02,0x80, + 0x03,0x95,0x00,0x6b,0x0c,0x15,0x00,0x6b,0xf8,0x96, + 0x03,0x00,0x07,0xea,0x03,0x80,0x00,0x15,0x0c,0x80, + 0xf7,0x76,0xfd,0x00,0x03,0x80,0x0a,0x75,0x03,0x80, + 0x03,0x80,0x07,0x13,0x02,0x02,0x03,0x00,0x00,0x6b, + 0x02,0x80,0x03,0x80,0x00,0x15,0x09,0x6b,0x09,0x15, + 0x00,0x6b,0x03,0x80,0x03,0x80,0x00,0x15,0x00,0xf6, + 0x0d,0x00,0x00,0x8a,0x00,0x6b,0x03,0x80,0x07,0x80, + 0xfd,0x00,0xff,0x03,0x00,0x04,0x00,0x07,0x00,0x04, + 0x01,0x02,0x03,0x01,0x06,0x00,0x03,0x7f,0x01,0x7e, + 0x01,0x7c,0x00,0x79,0xff,0x7c,0xff,0x7d,0xfd,0x00, + 0xfa,0x00,0x0e,0x80,0x03,0x80,0x00,0x15,0x0c,0x00, + 0x00,0x6b,0x02,0x80,0x03,0x80,0x00,0x15,0x0a,0x00, + 0x02,0x7f,0x01,0x7d,0x00,0x7b,0xff,0x7e,0xfe,0x7f, + 0xf6,0x00,0x10,0xf7,0x11,0x8f,0xff,0x03,0xff,0x02, + 0xfe,0x01,0xfa,0x00,0xfd,0x7f,0xff,0x7e,0x00,0x7c, + 0x00,0x79,0x00,0x7b,0x01,0x7e,0x03,0x00,0x06,0x00, + 0x02,0x00,0x01,0x03,0x01,0x02,0x03,0xfb,0x03,0x95, + 0x0c,0x00,0xfa,0x80,0x00,0x6b,0x09,0x80,0x03,0x95, + 0x00,0x77,0x06,0x7a,0x06,0x06,0x00,0x09,0xfa,0xf1, + 0xfa,0x7a,0x0e,0x80,0x03,0x87,0x00,0x0b,0x02,0x02, + 0x03,0x00,0x02,0x7e,0x01,0x02,0x04,0x00,0x02,0x7e, + 0x00,0x75,0xfe,0x7e,0xfc,0x00,0xff,0x01,0xfe,0x7f, + 0xfd,0x00,0xfe,0x02,0x07,0x8e,0x00,0x6b,0x09,0x80, + 0x03,0x80,0x0e,0x15,0xf2,0x80,0x0e,0x6b,0x03,0x80, + 0x03,0x95,0x00,0x6b,0x0e,0x00,0x00,0x7d,0xfe,0x98, + 0x00,0x6b,0x05,0x80,0x03,0x95,0x00,0x75,0x02,0x7d, + 0x0a,0x00,0x00,0x8e,0x00,0x6b,0x02,0x80,0x03,0x95, + 0x00,0x6b,0x10,0x00,0x00,0x15,0xf8,0x80,0x00,0x6b, + 0x0a,0x80,0x03,0x95,0x00,0x6b,0x10,0x00,0x00,0x15, + 0xf8,0x80,0x00,0x6b,0x0a,0x00,0x00,0x7d,0x02,0x83, + 0x10,0x80,0x03,0x95,0x00,0x6b,0x09,0x00,0x03,0x02, + 0x00,0x08,0xfd,0x02,0xf7,0x00,0x0e,0x89,0x00,0x6b, + 0x03,0x80,0x03,0x95,0x00,0x6b,0x09,0x00,0x03,0x02, + 0x00,0x08,0xfd,0x02,0xf7,0x00,0x0e,0xf4,0x03,0x92, + 0x02,0x03,0x07,0x00,0x03,0x7d,0x00,0x70,0xfd,0x7e, + 0xf9,0x00,0xfe,0x02,0x03,0x89,0x09,0x00,0x02,0xf5, + 0x03,0x80,0x00,0x15,0x00,0xf5,0x07,0x00,0x00,0x08, + 0x02,0x03,0x06,0x00,0x02,0x7d,0x00,0x70,0xfe,0x7e, + 0xfa,0x00,0xfe,0x02,0x00,0x08,0x0c,0xf6,0x0f,0x80, + 0x00,0x15,0xf6,0x00,0xfe,0x7d,0x00,0x79,0x02,0x7e, + 0x0a,0x00,0xf4,0xf7,0x07,0x09,0x07,0xf7,0x03,0x8c, + 0x01,0x02,0x01,0x01,0x05,0x00,0x02,0x7f,0x01,0x7e, + 0x00,0x74,0x00,0x86,0xff,0x01,0xfe,0x01,0xfb,0x00, + 0xff,0x7f,0xff,0x7f,0x00,0x7c,0x01,0x7e,0x01,0x00, + 0x05,0x00,0x02,0x00,0x01,0x02,0x03,0xfe,0x04,0x8e, + 0x02,0x01,0x04,0x00,0x02,0x7f,0x01,0x7e,0x00,0x77, + 0xff,0x7e,0xfe,0x7f,0xfc,0x00,0xfe,0x01,0xff,0x02, + 0x00,0x09,0x01,0x02,0x02,0x02,0x03,0x01,0x02,0x01, + 0x01,0x01,0x01,0x02,0x02,0xeb,0x03,0x80,0x00,0x15, + 0x03,0x00,0x02,0x7e,0x00,0x7b,0xfe,0x7e,0xfd,0x00, + 0x03,0x80,0x04,0x00,0x03,0x7e,0x00,0x78,0xfd,0x7e, + 0xf9,0x00,0x0c,0x80,0x03,0x8c,0x02,0x02,0x02,0x01, + 0x03,0x00,0x02,0x7f,0x01,0x7d,0xfe,0x7e,0xf9,0x7d, + 0xff,0x7e,0x00,0x7d,0x03,0x7f,0x02,0x00,0x03,0x01, + 0x02,0x01,0x02,0xfe,0x0d,0x8c,0xff,0x02,0xfe,0x01, + 0xfc,0x00,0xfe,0x7f,0xff,0x7e,0x00,0x77,0x01,0x7e, + 0x02,0x7f,0x04,0x00,0x02,0x01,0x01,0x02,0x00,0x0f, + 0xff,0x02,0xfe,0x01,0xf9,0x00,0x0c,0xeb,0x03,0x88, + 0x0a,0x00,0x00,0x02,0x00,0x03,0xfe,0x02,0xfa,0x00, + 0xff,0x7e,0xff,0x7d,0x00,0x7b,0x01,0x7c,0x01,0x7f, + 0x06,0x00,0x02,0x02,0x03,0xfe,0x03,0x8f,0x06,0x77, + 0x06,0x09,0xfa,0x80,0x00,0x71,0xff,0x87,0xfb,0x79, + 0x07,0x87,0x05,0x79,0x02,0x80,0x03,0x8d,0x02,0x02, + 0x06,0x00,0x02,0x7e,0x00,0x7d,0xfc,0x7d,0x04,0x7e, + 0x00,0x7d,0xfe,0x7e,0xfa,0x00,0xfe,0x02,0x04,0x85, + 0x02,0x00,0x06,0xf9,0x03,0x8f,0x00,0x73,0x01,0x7e, + 0x07,0x00,0x02,0x02,0x00,0x0d,0x00,0xf3,0x01,0x7e, + 0x03,0x80,0x03,0x8f,0x00,0x73,0x01,0x7e,0x07,0x00, + 0x02,0x02,0x00,0x0d,0x00,0xf3,0x01,0x7e,0xf8,0x90, + 0x03,0x00,0x08,0xf0,0x03,0x80,0x00,0x15,0x00,0xf3, + 0x02,0x00,0x06,0x07,0xfa,0xf9,0x07,0x78,0x03,0x80, + 0x03,0x80,0x04,0x0c,0x02,0x03,0x04,0x00,0x00,0x71, + 0x02,0x80,0x03,0x80,0x00,0x0f,0x06,0x77,0x06,0x09, + 0x00,0x71,0x02,0x80,0x03,0x80,0x00,0x0f,0x0a,0xf1, + 0x00,0x0f,0xf6,0xf8,0x0a,0x00,0x02,0xf9,0x05,0x80, + 0xff,0x01,0xff,0x04,0x00,0x05,0x01,0x03,0x01,0x02, + 0x06,0x00,0x02,0x7e,0x00,0x7d,0x00,0x7b,0x00,0x7c, + 0xfe,0x7f,0xfa,0x00,0x0b,0x80,0x03,0x80,0x00,0x0f, + 0x00,0xfb,0x01,0x03,0x01,0x02,0x05,0x00,0x02,0x7e, + 0x01,0x7d,0x00,0x76,0x03,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80, + 0x10,0x80,0x0a,0x8f,0x02,0x7f,0x01,0x7e,0x00,0x76, + 0xff,0x7f,0xfe,0x7f,0xfb,0x00,0xff,0x01,0xff,0x01, + 0x00,0x0a,0x01,0x02,0x01,0x01,0x05,0x00,0xf9,0x80, + 0x00,0x6b,0x0c,0x86,0x0d,0x8a,0xff,0x03,0xfe,0x02, + 0xfb,0x00,0xff,0x7e,0xff,0x7d,0x00,0x7b,0x01,0x7c, + 0x01,0x7f,0x05,0x00,0x02,0x01,0x01,0x03,0x03,0xfc, + 0x03,0x80,0x00,0x0f,0x00,0xfb,0x01,0x03,0x01,0x02, + 0x04,0x00,0x01,0x7e,0x01,0x7d,0x00,0x76,0x00,0x8a, + 0x01,0x03,0x02,0x02,0x03,0x00,0x02,0x7e,0x01,0x7d, + 0x00,0x76,0x03,0x80,0x03,0x8f,0x00,0x74,0x01,0x7e, + 0x02,0x7f,0x04,0x00,0x02,0x01,0x01,0x01,0x00,0x8d, + 0x00,0x6e,0xff,0x7e,0xfe,0x7f,0xfb,0x00,0xfe,0x01, + 0x0c,0x85,0x03,0x8d,0x01,0x02,0x03,0x00,0x02,0x7e, + 0x01,0x02,0x03,0x00,0x02,0x7e,0x00,0x74,0xfe,0x7f, + 0xfd,0x00,0xff,0x01,0xfe,0x7f,0xfd,0x00,0xff,0x01, + 0x00,0x0c,0x06,0x82,0x00,0x6b,0x08,0x86,0x03,0x80, + 0x0a,0x0f,0xf6,0x80,0x0a,0x71,0x03,0x80,0x03,0x8f, + 0x00,0x73,0x01,0x7e,0x07,0x00,0x02,0x02,0x00,0x0d, + 0x00,0xf3,0x01,0x7e,0x00,0x7e,0x03,0x82,0x03,0x8f, + 0x00,0x79,0x02,0x7e,0x08,0x00,0x00,0x89,0x00,0x71, + 0x02,0x80,0x03,0x8f,0x00,0x73,0x01,0x7e,0x03,0x00, + 0x02,0x02,0x00,0x0d,0x00,0xf3,0x01,0x7e,0x03,0x00, + 0x02,0x02,0x00,0x0d,0x00,0xf3,0x01,0x7e,0x03,0x80, + 0x03,0x8f,0x00,0x73,0x01,0x7e,0x03,0x00,0x02,0x02, + 0x00,0x0d,0x00,0xf3,0x01,0x7e,0x03,0x00,0x02,0x02, + 0x00,0x0d,0x00,0xf3,0x01,0x7e,0x00,0x7e,0x03,0x82, + 0x03,0x8d,0x00,0x02,0x02,0x00,0x00,0x71,0x08,0x00, + 0x02,0x02,0x00,0x06,0xfe,0x02,0xf8,0x00,0x0c,0xf6, + 0x03,0x8f,0x00,0x71,0x07,0x00,0x02,0x02,0x00,0x06, + 0xfe,0x02,0xf9,0x00,0x0c,0x85,0x00,0x71,0x02,0x80, + 0x03,0x8f,0x00,0x71,0x07,0x00,0x03,0x02,0x00,0x06, + 0xfd,0x02,0xf9,0x00,0x0c,0xf6,0x03,0x8d,0x02,0x02, + 0x06,0x00,0x02,0x7e,0x00,0x75,0xfe,0x7e,0xfa,0x00, + 0xfe,0x02,0x04,0x85,0x06,0x00,0x02,0xf9,0x03,0x80, + 0x00,0x0f,0x00,0xf8,0x04,0x00,0x00,0x06,0x02,0x02, + 0x04,0x00,0x02,0x7e,0x00,0x75,0xfe,0x7e,0xfc,0x00, + 0xfe,0x02,0x00,0x05,0x0a,0xf9,0x0d,0x80,0x00,0x0f, + 0xf7,0x00,0xff,0x7e,0x00,0x7b,0x01,0x7e,0x09,0x00, + 0xf6,0xfa,0x04,0x06,0x08,0xfa + }; + + //------------------------------------------------------------------------- + gsv_text::gsv_text() : + m_x(0.0), + m_y(0.0), + m_start_x(0.0), + m_width(10.0), + m_height(0.0), + m_space(0.0), + m_line_space(0.0), + m_text(m_chr), + m_text_buf(), + m_cur_chr(m_chr), + m_font(gsv_default_font), + m_loaded_font(), + m_status(initial), + m_big_endian(false), + m_flip(false) + { + m_chr[0] = m_chr[1] = 0; + + int t = 1; + if(*(char*)&t == 0) m_big_endian = true; + } + + //------------------------------------------------------------------------- + void gsv_text::font(const void* font) + { + m_font = font; + if(m_font == 0) m_font = &m_loaded_font[0]; + } + + //------------------------------------------------------------------------- + void gsv_text::size(double height, double width) + { + m_height = height; + m_width = width; + } + + //------------------------------------------------------------------------- + void gsv_text::space(double space) + { + m_space = space; + } + + //------------------------------------------------------------------------- + void gsv_text::line_space(double line_space) + { + m_line_space = line_space; + } + + //------------------------------------------------------------------------- + void gsv_text::start_point(double x, double y) + { + m_x = m_start_x = x; + m_y = y; + //if(m_flip) m_y += m_height; + } + + //------------------------------------------------------------------------- + void gsv_text::load_font(const char* file) + { + m_loaded_font.resize(0); + FILE* fd = fopen(file, "rb"); + if(fd) + { + unsigned len; + + fseek(fd, 0l, SEEK_END); + len = ftell(fd); + fseek(fd, 0l, SEEK_SET); + if(len > 0) + { + m_loaded_font.resize(len); + fread(&m_loaded_font[0], 1, len, fd); + m_font = &m_loaded_font[0]; + } + fclose(fd); + } + } + + //------------------------------------------------------------------------- + void gsv_text::text(const char* text) + { + if(text == 0) + { + m_chr[0] = 0; + m_text = m_chr; + return; + } + unsigned new_size = strlen(text) + 1; + if(new_size > m_text_buf.size()) + { + m_text_buf.resize(new_size); + } + memcpy(&m_text_buf[0], text, new_size); + m_text = &m_text_buf[0]; + } + + //------------------------------------------------------------------------- + void gsv_text::rewind(unsigned) + { + m_status = initial; + if(m_font == 0) return; + + m_indices = (int8u*)m_font; + double base_height = value(m_indices + 4); + m_indices += value(m_indices); + m_glyphs = (int8*)(m_indices + 257*2); + m_h = m_height / base_height; + m_w = (m_width == 0.0) ? m_h : m_width / base_height; + if(m_flip) m_h = -m_h; + m_cur_chr = m_text; + } + + //------------------------------------------------------------------------- + unsigned gsv_text::vertex(double* x, double* y) + { + unsigned idx; + int8 yc, yf; + int dx, dy; + bool quit = false; + + while(!quit) + { + switch(m_status) + { + case initial: + if(m_font == 0) + { + quit = true; + break; + } + m_status = next_char; + + case next_char: + if(*m_cur_chr == 0) + { + quit = true; + break; + } + idx = (*m_cur_chr++) & 0xFF; + if(idx == '\n') + { + m_x = m_start_x; + m_y -= m_flip ? -m_height - m_line_space : m_height + m_line_space; + break; + } + idx <<= 1; + m_bglyph = m_glyphs + value(m_indices + idx); + m_eglyph = m_glyphs + value(m_indices + idx + 2); + m_status = start_glyph; + + case start_glyph: + *x = m_x; + *y = m_y; + m_status = glyph; + return path_cmd_move_to; + + case glyph: + if(m_bglyph >= m_eglyph) + { + m_status = next_char; + m_x += m_space; + break; + } + dx = int(*m_bglyph++); + yf = (yc = *m_bglyph++) & 0x80; + yc <<= 1; + yc >>= 1; + dy = int(yc); + m_x += double(dx) * m_w; + m_y += double(dy) * m_h; + *x = m_x; + *y = m_y; + return yf ? path_cmd_move_to : path_cmd_line_to; + } + + } + return path_cmd_stop; + } + + //------------------------------------------------------------------------- + double gsv_text::text_width() + { + double x1, y1, x2, y2; + bounding_rect_single(*this, 0, &x1, &y1, &x2, &y2); + return x2 - x1; + } + + +} diff --git a/desmume/src/windows/agg/src/agg_image_filters.cpp b/desmume/src/windows/agg/src/agg_image_filters.cpp new file mode 100644 index 000000000..fb508f61d --- /dev/null +++ b/desmume/src/windows/agg/src/agg_image_filters.cpp @@ -0,0 +1,107 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_image_filters.h" + + +namespace agg +{ + //-------------------------------------------------------------------- + void image_filter_lut::realloc_lut(double radius) + { + m_radius = radius; + m_diameter = uceil(radius) * 2; + m_start = -int(m_diameter / 2 - 1); + unsigned size = m_diameter << image_subpixel_shift; + if(size > m_weight_array.size()) + { + m_weight_array.resize(size); + } + } + + + + //-------------------------------------------------------------------- + // This function normalizes integer values and corrects the rounding + // errors. It doesn't do anything with the source floating point values + // (m_weight_array_dbl), it corrects only integers according to the rule + // of 1.0 which means that any sum of pixel weights must be equal to 1.0. + // So, the filter function must produce a graph of the proper shape. + //-------------------------------------------------------------------- + void image_filter_lut::normalize() + { + unsigned i; + int flip = 1; + + for(i = 0; i < image_subpixel_scale; i++) + { + for(;;) + { + int sum = 0; + unsigned j; + for(j = 0; j < m_diameter; j++) + { + sum += m_weight_array[j * image_subpixel_scale + i]; + } + + if(sum == image_filter_scale) break; + + double k = double(image_filter_scale) / double(sum); + sum = 0; + for(j = 0; j < m_diameter; j++) + { + sum += m_weight_array[j * image_subpixel_scale + i] = + iround(m_weight_array[j * image_subpixel_scale + i] * k); + } + + sum -= image_filter_scale; + int inc = (sum > 0) ? -1 : 1; + + for(j = 0; j < m_diameter && sum; j++) + { + flip ^= 1; + unsigned idx = flip ? m_diameter/2 + j/2 : m_diameter/2 - j/2; + int v = m_weight_array[idx * image_subpixel_scale + i]; + if(v < image_filter_scale) + { + m_weight_array[idx * image_subpixel_scale + i] += inc; + sum += inc; + } + } + } + } + + unsigned pivot = m_diameter << (image_subpixel_shift - 1); + + for(i = 0; i < pivot; i++) + { + m_weight_array[pivot + i] = m_weight_array[pivot - i]; + } + unsigned end = (diameter() << image_subpixel_shift) - 1; + m_weight_array[0] = m_weight_array[end]; + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_line_aa_basics.cpp b/desmume/src/windows/agg/src/agg_line_aa_basics.cpp new file mode 100644 index 000000000..8570e5c71 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_line_aa_basics.cpp @@ -0,0 +1,91 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_line_aa_basics.h" + +namespace agg +{ + //------------------------------------------------------------------------- + // The number of the octant is determined as a 3-bit value as follows: + // bit 0 = vertical flag + // bit 1 = sx < 0 + // bit 2 = sy < 0 + // + // [N] shows the number of the orthogonal quadrant + // shows the number of the diagonal quadrant + // <1> + // [1] | [0] + // . (3)011 | 001(1) . + // . | . + // . | . + // . | . + // (2)010 .|. 000(0) + // <2> ----------.+.----------- <0> + // (6)110 . | . 100(4) + // . | . + // . | . + // . | . + // (7)111 | 101(5) + // [2] | [3] + // <3> + // 0,1,2,3,4,5,6,7 + const int8u line_parameters::s_orthogonal_quadrant[8] = { 0,0,1,1,3,3,2,2 }; + const int8u line_parameters::s_diagonal_quadrant[8] = { 0,1,2,1,0,3,2,3 }; + + + + //------------------------------------------------------------------------- + void bisectrix(const line_parameters& l1, + const line_parameters& l2, + int* x, int* y) + { + double k = double(l2.len) / double(l1.len); + double tx = l2.x2 - (l2.x1 - l1.x1) * k; + double ty = l2.y2 - (l2.y1 - l1.y1) * k; + + //All bisectrices must be on the right of the line + //If the next point is on the left (l1 => l2.2) + //then the bisectix should be rotated by 180 degrees. + if(double(l2.x2 - l2.x1) * double(l2.y1 - l1.y1) < + double(l2.y2 - l2.y1) * double(l2.x1 - l1.x1) + 100.0) + { + tx -= (tx - l2.x1) * 2.0; + ty -= (ty - l2.y1) * 2.0; + } + + // Check if the bisectrix is too short + double dx = tx - l2.x1; + double dy = ty - l2.y1; + if((int)sqrt(dx * dx + dy * dy) < line_subpixel_scale) + { + *x = (l2.x1 + l2.x1 + (l2.y1 - l1.y1) + (l2.y2 - l2.y1)) >> 1; + *y = (l2.y1 + l2.y1 - (l2.x1 - l1.x1) - (l2.x2 - l2.x1)) >> 1; + return; + } + *x = iround(tx); + *y = iround(ty); + } + +} diff --git a/desmume/src/windows/agg/src/agg_line_profile_aa.cpp b/desmume/src/windows/agg/src/agg_line_profile_aa.cpp new file mode 100644 index 000000000..1df9e2cb8 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_line_profile_aa.cpp @@ -0,0 +1,125 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_renderer_outline_aa.h" + +namespace agg +{ + + //--------------------------------------------------------------------- + void line_profile_aa::width(double w) + { + if(w < 0.0) w = 0.0; + + if(w < m_smoother_width) w += w; + else w += m_smoother_width; + + w *= 0.5; + + w -= m_smoother_width; + double s = m_smoother_width; + if(w < 0.0) + { + s += w; + w = 0.0; + } + set(w, s); + } + + + //--------------------------------------------------------------------- + line_profile_aa::value_type* line_profile_aa::profile(double w) + { + m_subpixel_width = uround(w * subpixel_scale); + unsigned size = m_subpixel_width + subpixel_scale * 6; + if(size > m_profile.size()) + { + m_profile.resize(size); + } + return &m_profile[0]; + } + + + //--------------------------------------------------------------------- + void line_profile_aa::set(double center_width, double smoother_width) + { + double base_val = 1.0; + if(center_width == 0.0) center_width = 1.0 / subpixel_scale; + if(smoother_width == 0.0) smoother_width = 1.0 / subpixel_scale; + + double width = center_width + smoother_width; + if(width < m_min_width) + { + double k = width / m_min_width; + base_val *= k; + center_width /= k; + smoother_width /= k; + } + + value_type* ch = profile(center_width + smoother_width); + + unsigned subpixel_center_width = unsigned(center_width * subpixel_scale); + unsigned subpixel_smoother_width = unsigned(smoother_width * subpixel_scale); + + value_type* ch_center = ch + subpixel_scale*2; + value_type* ch_smoother = ch_center + subpixel_center_width; + + unsigned i; + + unsigned val = m_gamma[unsigned(base_val * aa_mask)]; + ch = ch_center; + for(i = 0; i < subpixel_center_width; i++) + { + *ch++ = (value_type)val; + } + + for(i = 0; i < subpixel_smoother_width; i++) + { + *ch_smoother++ = + m_gamma[unsigned((base_val - + base_val * + (double(i) / subpixel_smoother_width)) * aa_mask)]; + } + + unsigned n_smoother = profile_size() - + subpixel_smoother_width - + subpixel_center_width - + subpixel_scale*2; + + val = m_gamma[0]; + for(i = 0; i < n_smoother; i++) + { + *ch_smoother++ = (value_type)val; + } + + ch = ch_center; + for(i = 0; i < subpixel_scale*2; i++) + { + *--ch = *ch_center++; + } + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_rounded_rect.cpp b/desmume/src/windows/agg/src/agg_rounded_rect.cpp new file mode 100644 index 000000000..a480043a3 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_rounded_rect.cpp @@ -0,0 +1,169 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_rounded_rect.h" + + +namespace agg +{ + //------------------------------------------------------------------------ + rounded_rect::rounded_rect(double x1, double y1, double x2, double y2, double r) : + m_x1(x1), m_y1(y1), m_x2(x2), m_y2(y2), + m_rx1(r), m_ry1(r), m_rx2(r), m_ry2(r), + m_rx3(r), m_ry3(r), m_rx4(r), m_ry4(r) + { + if(x1 > x2) { m_x1 = x2; m_x2 = x1; } + if(y1 > y2) { m_y1 = y2; m_y2 = y1; } + } + + //-------------------------------------------------------------------- + void rounded_rect::rect(double x1, double y1, double x2, double y2) + { + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + if(x1 > x2) { m_x1 = x2; m_x2 = x1; } + if(y1 > y2) { m_y1 = y2; m_y2 = y1; } + } + + //-------------------------------------------------------------------- + void rounded_rect::radius(double r) + { + m_rx1 = m_ry1 = m_rx2 = m_ry2 = m_rx3 = m_ry3 = m_rx4 = m_ry4 = r; + } + + //-------------------------------------------------------------------- + void rounded_rect::radius(double rx, double ry) + { + m_rx1 = m_rx2 = m_rx3 = m_rx4 = rx; + m_ry1 = m_ry2 = m_ry3 = m_ry4 = ry; + } + + //-------------------------------------------------------------------- + void rounded_rect::radius(double rx_bottom, double ry_bottom, + double rx_top, double ry_top) + { + m_rx1 = m_rx2 = rx_bottom; + m_rx3 = m_rx4 = rx_top; + m_ry1 = m_ry2 = ry_bottom; + m_ry3 = m_ry4 = ry_top; + } + + //-------------------------------------------------------------------- + void rounded_rect::radius(double rx1, double ry1, double rx2, double ry2, + double rx3, double ry3, double rx4, double ry4) + { + m_rx1 = rx1; m_ry1 = ry1; m_rx2 = rx2; m_ry2 = ry2; + m_rx3 = rx3; m_ry3 = ry3; m_rx4 = rx4; m_ry4 = ry4; + } + + //-------------------------------------------------------------------- + void rounded_rect::normalize_radius() + { + double dx = fabs(m_x2 - m_x1); + double dy = fabs(m_y2 - m_y1); + + double k = 1.0; + double t; + t = dx / (m_rx1 + m_rx2); if(t < k) k = t; + t = dx / (m_rx3 + m_rx4); if(t < k) k = t; + t = dy / (m_ry1 + m_ry2); if(t < k) k = t; + t = dy / (m_ry3 + m_ry4); if(t < k) k = t; + + if(k < 1.0) + { + m_rx1 *= k; m_ry1 *= k; m_rx2 *= k; m_ry2 *= k; + m_rx3 *= k; m_ry3 *= k; m_rx4 *= k; m_ry4 *= k; + } + } + + //-------------------------------------------------------------------- + void rounded_rect::rewind(unsigned) + { + m_status = 0; + } + + //-------------------------------------------------------------------- + unsigned rounded_rect::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + switch(m_status) + { + case 0: + m_arc.init(m_x1 + m_rx1, m_y1 + m_ry1, m_rx1, m_ry1, + pi, pi+pi*0.5); + m_arc.rewind(0); + m_status++; + + case 1: + cmd = m_arc.vertex(x, y); + if(is_stop(cmd)) m_status++; + else return cmd; + + case 2: + m_arc.init(m_x2 - m_rx2, m_y1 + m_ry2, m_rx2, m_ry2, + pi+pi*0.5, 0.0); + m_arc.rewind(0); + m_status++; + + case 3: + cmd = m_arc.vertex(x, y); + if(is_stop(cmd)) m_status++; + else return path_cmd_line_to; + + case 4: + m_arc.init(m_x2 - m_rx3, m_y2 - m_ry3, m_rx3, m_ry3, + 0.0, pi*0.5); + m_arc.rewind(0); + m_status++; + + case 5: + cmd = m_arc.vertex(x, y); + if(is_stop(cmd)) m_status++; + else return path_cmd_line_to; + + case 6: + m_arc.init(m_x1 + m_rx4, m_y2 - m_ry4, m_rx4, m_ry4, + pi*0.5, pi); + m_arc.rewind(0); + m_status++; + + case 7: + cmd = m_arc.vertex(x, y); + if(is_stop(cmd)) m_status++; + else return path_cmd_line_to; + + case 8: + cmd = path_cmd_end_poly | path_flags_close | path_flags_ccw; + m_status++; + break; + } + return cmd; + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_sqrt_tables.cpp b/desmume/src/windows/agg/src/agg_sqrt_tables.cpp new file mode 100644 index 000000000..52c311c5f --- /dev/null +++ b/desmume/src/windows/agg/src/agg_sqrt_tables.cpp @@ -0,0 +1,120 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_basics.h" + +namespace agg +{ + int16u g_sqrt_table[1024] = //----------g_sqrt_table + { + 0, + 2048,2896,3547,4096,4579,5017,5418,5793,6144,6476,6792,7094,7384,7663,7932,8192,8444, + 8689,8927,9159,9385,9606,9822,10033,10240,10443,10642,10837,11029,11217,11403,11585, + 11765,11942,12116,12288,12457,12625,12790,12953,13114,13273,13430,13585,13738,13890, + 14040,14189,14336,14482,14626,14768,14910,15050,15188,15326,15462,15597,15731,15864, + 15995,16126,16255,16384,16512,16638,16764,16888,17012,17135,17257,17378,17498,17618, + 17736,17854,17971,18087,18203,18318,18432,18545,18658,18770,18882,18992,19102,19212, + 19321,19429,19537,19644,19750,19856,19961,20066,20170,20274,20377,20480,20582,20684, + 20785,20886,20986,21085,21185,21283,21382,21480,21577,21674,21771,21867,21962,22058, + 22153,22247,22341,22435,22528,22621,22713,22806,22897,22989,23080,23170,23261,23351, + 23440,23530,23619,23707,23796,23884,23971,24059,24146,24232,24319,24405,24491,24576, + 24661,24746,24831,24915,24999,25083,25166,25249,25332,25415,25497,25580,25661,25743, + 25824,25905,25986,26067,26147,26227,26307,26387,26466,26545,26624,26703,26781,26859, + 26937,27015,27092,27170,27247,27324,27400,27477,27553,27629,27705,27780,27856,27931, + 28006,28081,28155,28230,28304,28378,28452,28525,28599,28672,28745,28818,28891,28963, + 29035,29108,29180,29251,29323,29394,29466,29537,29608,29678,29749,29819,29890,29960, + 30030,30099,30169,30238,30308,30377,30446,30515,30583,30652,30720,30788,30856,30924, + 30992,31059,31127,31194,31261,31328,31395,31462,31529,31595,31661,31727,31794,31859, + 31925,31991,32056,32122,32187,32252,32317,32382,32446,32511,32575,32640,32704,32768, + 32832,32896,32959,33023,33086,33150,33213,33276,33339,33402,33465,33527,33590,33652, + 33714,33776,33839,33900,33962,34024,34086,34147,34208,34270,34331,34392,34453,34514, + 34574,34635,34695,34756,34816,34876,34936,34996,35056,35116,35176,35235,35295,35354, + 35413,35472,35531,35590,35649,35708,35767,35825,35884,35942,36001,36059,36117,36175, + 36233,36291,36348,36406,36464,36521,36578,36636,36693,36750,36807,36864,36921,36978, + 37034,37091,37147,37204,37260,37316,37372,37429,37485,37540,37596,37652,37708,37763, + 37819,37874,37929,37985,38040,38095,38150,38205,38260,38315,38369,38424,38478,38533, + 38587,38642,38696,38750,38804,38858,38912,38966,39020,39073,39127,39181,39234,39287, + 39341,39394,39447,39500,39553,39606,39659,39712,39765,39818,39870,39923,39975,40028, + 40080,40132,40185,40237,40289,40341,40393,40445,40497,40548,40600,40652,40703,40755, + 40806,40857,40909,40960,41011,41062,41113,41164,41215,41266,41317,41368,41418,41469, + 41519,41570,41620,41671,41721,41771,41821,41871,41922,41972,42021,42071,42121,42171, + 42221,42270,42320,42369,42419,42468,42518,42567,42616,42665,42714,42763,42813,42861, + 42910,42959,43008,43057,43105,43154,43203,43251,43300,43348,43396,43445,43493,43541, + 43589,43637,43685,43733,43781,43829,43877,43925,43972,44020,44068,44115,44163,44210, + 44258,44305,44352,44400,44447,44494,44541,44588,44635,44682,44729,44776,44823,44869, + 44916,44963,45009,45056,45103,45149,45195,45242,45288,45334,45381,45427,45473,45519, + 45565,45611,45657,45703,45749,45795,45840,45886,45932,45977,46023,46069,46114,46160, + 46205,46250,46296,46341,46386,46431,46477,46522,46567,46612,46657,46702,46746,46791, + 46836,46881,46926,46970,47015,47059,47104,47149,47193,47237,47282,47326,47370,47415, + 47459,47503,47547,47591,47635,47679,47723,47767,47811,47855,47899,47942,47986,48030, + 48074,48117,48161,48204,48248,48291,48335,48378,48421,48465,48508,48551,48594,48637, + 48680,48723,48766,48809,48852,48895,48938,48981,49024,49067,49109,49152,49195,49237, + 49280,49322,49365,49407,49450,49492,49535,49577,49619,49661,49704,49746,49788,49830, + 49872,49914,49956,49998,50040,50082,50124,50166,50207,50249,50291,50332,50374,50416, + 50457,50499,50540,50582,50623,50665,50706,50747,50789,50830,50871,50912,50954,50995, + 51036,51077,51118,51159,51200,51241,51282,51323,51364,51404,51445,51486,51527,51567, + 51608,51649,51689,51730,51770,51811,51851,51892,51932,51972,52013,52053,52093,52134, + 52174,52214,52254,52294,52334,52374,52414,52454,52494,52534,52574,52614,52654,52694, + 52734,52773,52813,52853,52892,52932,52972,53011,53051,53090,53130,53169,53209,53248, + 53287,53327,53366,53405,53445,53484,53523,53562,53601,53640,53679,53719,53758,53797, + 53836,53874,53913,53952,53991,54030,54069,54108,54146,54185,54224,54262,54301,54340, + 54378,54417,54455,54494,54532,54571,54609,54647,54686,54724,54762,54801,54839,54877, + 54915,54954,54992,55030,55068,55106,55144,55182,55220,55258,55296,55334,55372,55410, + 55447,55485,55523,55561,55599,55636,55674,55712,55749,55787,55824,55862,55900,55937, + 55975,56012,56049,56087,56124,56162,56199,56236,56273,56311,56348,56385,56422,56459, + 56497,56534,56571,56608,56645,56682,56719,56756,56793,56830,56867,56903,56940,56977, + 57014,57051,57087,57124,57161,57198,57234,57271,57307,57344,57381,57417,57454,57490, + 57527,57563,57599,57636,57672,57709,57745,57781,57817,57854,57890,57926,57962,57999, + 58035,58071,58107,58143,58179,58215,58251,58287,58323,58359,58395,58431,58467,58503, + 58538,58574,58610,58646,58682,58717,58753,58789,58824,58860,58896,58931,58967,59002, + 59038,59073,59109,59144,59180,59215,59251,59286,59321,59357,59392,59427,59463,59498, + 59533,59568,59603,59639,59674,59709,59744,59779,59814,59849,59884,59919,59954,59989, + 60024,60059,60094,60129,60164,60199,60233,60268,60303,60338,60373,60407,60442,60477, + 60511,60546,60581,60615,60650,60684,60719,60753,60788,60822,60857,60891,60926,60960, + 60995,61029,61063,61098,61132,61166,61201,61235,61269,61303,61338,61372,61406,61440, + 61474,61508,61542,61576,61610,61644,61678,61712,61746,61780,61814,61848,61882,61916, + 61950,61984,62018,62051,62085,62119,62153,62186,62220,62254,62287,62321,62355,62388, + 62422,62456,62489,62523,62556,62590,62623,62657,62690,62724,62757,62790,62824,62857, + 62891,62924,62957,62991,63024,63057,63090,63124,63157,63190,63223,63256,63289,63323, + 63356,63389,63422,63455,63488,63521,63554,63587,63620,63653,63686,63719,63752,63785, + 63817,63850,63883,63916,63949,63982,64014,64047,64080,64113,64145,64178,64211,64243, + 64276,64309,64341,64374,64406,64439,64471,64504,64536,64569,64601,64634,64666,64699, + 64731,64763,64796,64828,64861,64893,64925,64957,64990,65022,65054,65086,65119,65151, + 65183,65215,65247,65279,65312,65344,65376,65408,65440,65472,65504 + }; + + + int8 g_elder_bit_table[256] = //---------g_elder_bit_table + { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + }; + +} diff --git a/desmume/src/windows/agg/src/agg_trans_affine.cpp b/desmume/src/windows/agg/src/agg_trans_affine.cpp new file mode 100644 index 000000000..489c056cc --- /dev/null +++ b/desmume/src/windows/agg/src/agg_trans_affine.cpp @@ -0,0 +1,200 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_trans_affine.h" + + + +namespace agg +{ + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::parl_to_parl(const double* src, + const double* dst) + { + sx = src[2] - src[0]; + shy = src[3] - src[1]; + shx = src[4] - src[0]; + sy = src[5] - src[1]; + tx = src[0]; + ty = src[1]; + invert(); + multiply(trans_affine(dst[2] - dst[0], dst[3] - dst[1], + dst[4] - dst[0], dst[5] - dst[1], + dst[0], dst[1])); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::rect_to_parl(double x1, double y1, + double x2, double y2, + const double* parl) + { + double src[6]; + src[0] = x1; src[1] = y1; + src[2] = x2; src[3] = y1; + src[4] = x2; src[5] = y2; + parl_to_parl(src, parl); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::parl_to_rect(const double* parl, + double x1, double y1, + double x2, double y2) + { + double dst[6]; + dst[0] = x1; dst[1] = y1; + dst[2] = x2; dst[3] = y1; + dst[4] = x2; dst[5] = y2; + parl_to_parl(parl, dst); + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::multiply(const trans_affine& m) + { + double t0 = sx * m.sx + shy * m.shx; + double t2 = shx * m.sx + sy * m.shx; + double t4 = tx * m.sx + ty * m.shx + m.tx; + shy = sx * m.shy + shy * m.sy; + sy = shx * m.shy + sy * m.sy; + ty = tx * m.shy + ty * m.sy + m.ty; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::invert() + { + double d = determinant_reciprocal(); + + double t0 = sy * d; + sy = sx * d; + shy = -shy * d; + shx = -shx * d; + + double t4 = -tx * t0 - ty * shx; + ty = -tx * shy - ty * sy; + + sx = t0; + tx = t4; + return *this; + } + + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::flip_x() + { + sx = -sx; + shy = -shy; + tx = -tx; + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::flip_y() + { + shx = -shx; + sy = -sy; + ty = -ty; + return *this; + } + + //------------------------------------------------------------------------ + const trans_affine& trans_affine::reset() + { + sx = sy = 1.0; + shy = shx = tx = ty = 0.0; + return *this; + } + + //------------------------------------------------------------------------ + bool trans_affine::is_identity(double epsilon) const + { + return is_equal_eps(sx, 1.0, epsilon) && + is_equal_eps(shy, 0.0, epsilon) && + is_equal_eps(shx, 0.0, epsilon) && + is_equal_eps(sy, 1.0, epsilon) && + is_equal_eps(tx, 0.0, epsilon) && + is_equal_eps(ty, 0.0, epsilon); + } + + //------------------------------------------------------------------------ + bool trans_affine::is_valid(double epsilon) const + { + return fabs(sx) > epsilon && fabs(sy) > epsilon; + } + + //------------------------------------------------------------------------ + bool trans_affine::is_equal(const trans_affine& m, double epsilon) const + { + return is_equal_eps(sx, m.sx, epsilon) && + is_equal_eps(shy, m.shy, epsilon) && + is_equal_eps(shx, m.shx, epsilon) && + is_equal_eps(sy, m.sy, epsilon) && + is_equal_eps(tx, m.tx, epsilon) && + is_equal_eps(ty, m.ty, epsilon); + } + + //------------------------------------------------------------------------ + double trans_affine::rotation() const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 0.0; + transform(&x1, &y1); + transform(&x2, &y2); + return atan2(y2-y1, x2-x1); + } + + //------------------------------------------------------------------------ + void trans_affine::translation(double* dx, double* dy) const + { + *dx = tx; + *dy = ty; + } + + //------------------------------------------------------------------------ + void trans_affine::scaling(double* x, double* y) const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 1.0; + trans_affine t(*this); + t *= trans_affine_rotation(-rotation()); + t.transform(&x1, &y1); + t.transform(&x2, &y2); + *x = x2 - x1; + *y = y2 - y1; + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_trans_double_path.cpp b/desmume/src/windows/agg/src/agg_trans_double_path.cpp new file mode 100644 index 000000000..fc3d184e5 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_trans_double_path.cpp @@ -0,0 +1,282 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_math.h" +#include "agg_trans_double_path.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + trans_double_path::trans_double_path() : + m_kindex1(0.0), + m_kindex2(0.0), + m_base_length(0.0), + m_base_height(1.0), + m_status1(initial), + m_status2(initial), + m_preserve_x_scale(true) + { + } + + + //------------------------------------------------------------------------ + void trans_double_path::reset() + { + m_src_vertices1.remove_all(); + m_src_vertices2.remove_all(); + m_kindex1 = 0.0; + m_kindex1 = 0.0; + m_status1 = initial; + m_status2 = initial; + } + + + //------------------------------------------------------------------------ + void trans_double_path::move_to1(double x, double y) + { + if(m_status1 == initial) + { + m_src_vertices1.modify_last(vertex_dist(x, y)); + m_status1 = making_path; + } + else + { + line_to1(x, y); + } + } + + + //------------------------------------------------------------------------ + void trans_double_path::line_to1(double x, double y) + { + if(m_status1 == making_path) + { + m_src_vertices1.add(vertex_dist(x, y)); + } + } + + + //------------------------------------------------------------------------ + void trans_double_path::move_to2(double x, double y) + { + if(m_status2 == initial) + { + m_src_vertices2.modify_last(vertex_dist(x, y)); + m_status2 = making_path; + } + else + { + line_to2(x, y); + } + } + + + //------------------------------------------------------------------------ + void trans_double_path::line_to2(double x, double y) + { + if(m_status2 == making_path) + { + m_src_vertices2.add(vertex_dist(x, y)); + } + } + + + //------------------------------------------------------------------------ + double trans_double_path::finalize_path(vertex_storage& vertices) + { + unsigned i; + double dist; + double d; + + vertices.close(false); + if(vertices.size() > 2) + { + if(vertices[vertices.size() - 2].dist * 10.0 < + vertices[vertices.size() - 3].dist) + { + d = vertices[vertices.size() - 3].dist + + vertices[vertices.size() - 2].dist; + + vertices[vertices.size() - 2] = + vertices[vertices.size() - 1]; + + vertices.remove_last(); + vertices[vertices.size() - 2].dist = d; + } + } + + dist = 0; + for(i = 0; i < vertices.size(); i++) + { + vertex_dist& v = vertices[i]; + d = v.dist; + v.dist = dist; + dist += d; + } + + return (vertices.size() - 1) / dist; + } + + + //------------------------------------------------------------------------ + void trans_double_path::finalize_paths() + { + if(m_status1 == making_path && m_src_vertices1.size() > 1 && + m_status2 == making_path && m_src_vertices2.size() > 1) + { + m_kindex1 = finalize_path(m_src_vertices1); + m_kindex2 = finalize_path(m_src_vertices2); + m_status1 = ready; + m_status2 = ready; + } + } + + + //------------------------------------------------------------------------ + double trans_double_path::total_length1() const + { + if(m_base_length >= 1e-10) return m_base_length; + return (m_status1 == ready) ? + m_src_vertices1[m_src_vertices1.size() - 1].dist : + 0.0; + } + + + //------------------------------------------------------------------------ + double trans_double_path::total_length2() const + { + if(m_base_length >= 1e-10) return m_base_length; + return (m_status2 == ready) ? + m_src_vertices2[m_src_vertices2.size() - 1].dist : + 0.0; + } + + + //------------------------------------------------------------------------ + void trans_double_path::transform1(const vertex_storage& vertices, + double kindex, double kx, + double *x, double* y) const + { + double x1 = 0.0; + double y1 = 0.0; + double dx = 1.0; + double dy = 1.0; + double d = 0.0; + double dd = 1.0; + *x *= kx; + if(*x < 0.0) + { + // Extrapolation on the left + //-------------------------- + x1 = vertices[0].x; + y1 = vertices[0].y; + dx = vertices[1].x - x1; + dy = vertices[1].y - y1; + dd = vertices[1].dist - vertices[0].dist; + d = *x; + } + else + if(*x > vertices[vertices.size() - 1].dist) + { + // Extrapolation on the right + //-------------------------- + unsigned i = vertices.size() - 2; + unsigned j = vertices.size() - 1; + x1 = vertices[j].x; + y1 = vertices[j].y; + dx = x1 - vertices[i].x; + dy = y1 - vertices[i].y; + dd = vertices[j].dist - vertices[i].dist; + d = *x - vertices[j].dist; + } + else + { + // Interpolation + //-------------------------- + unsigned i = 0; + unsigned j = vertices.size() - 1; + if(m_preserve_x_scale) + { + unsigned k; + for(i = 0; (j - i) > 1; ) + { + if(*x < vertices[k = (i + j) >> 1].dist) + { + j = k; + } + else + { + i = k; + } + } + d = vertices[i].dist; + dd = vertices[j].dist - d; + d = *x - d; + } + else + { + i = unsigned(*x * kindex); + j = i + 1; + dd = vertices[j].dist - vertices[i].dist; + d = ((*x * kindex) - i) * dd; + } + x1 = vertices[i].x; + y1 = vertices[i].y; + dx = vertices[j].x - x1; + dy = vertices[j].y - y1; + } + *x = x1 + dx * d / dd; + *y = y1 + dy * d / dd; + } + + + //------------------------------------------------------------------------ + void trans_double_path::transform(double *x, double *y) const + { + if(m_status1 == ready && m_status2 == ready) + { + if(m_base_length > 1e-10) + { + *x *= m_src_vertices1[m_src_vertices1.size() - 1].dist / + m_base_length; + } + + double x1 = *x; + double y1 = *y; + double x2 = *x; + double y2 = *y; + double dd = m_src_vertices2[m_src_vertices2.size() - 1].dist / + m_src_vertices1[m_src_vertices1.size() - 1].dist; + + transform1(m_src_vertices1, m_kindex1, 1.0, &x1, &y1); + transform1(m_src_vertices2, m_kindex2, dd, &x2, &y2); + + *x = x1 + *y * (x2 - x1) / m_base_height; + *y = y1 + *y * (y2 - y1) / m_base_height; + } + } + +} + diff --git a/desmume/src/windows/agg/src/agg_trans_single_path.cpp b/desmume/src/windows/agg/src/agg_trans_single_path.cpp new file mode 100644 index 000000000..368ab883a --- /dev/null +++ b/desmume/src/windows/agg/src/agg_trans_single_path.cpp @@ -0,0 +1,211 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_math.h" +#include "agg_vertex_sequence.h" +#include "agg_trans_single_path.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + trans_single_path::trans_single_path() : + m_base_length(0.0), + m_kindex(0.0), + m_status(initial), + m_preserve_x_scale(true) + { + } + + //------------------------------------------------------------------------ + void trans_single_path::reset() + { + m_src_vertices.remove_all(); + m_kindex = 0.0; + m_status = initial; + } + + //------------------------------------------------------------------------ + void trans_single_path::move_to(double x, double y) + { + if(m_status == initial) + { + m_src_vertices.modify_last(vertex_dist(x, y)); + m_status = making_path; + } + else + { + line_to(x, y); + } + } + + //------------------------------------------------------------------------ + void trans_single_path::line_to(double x, double y) + { + if(m_status == making_path) + { + m_src_vertices.add(vertex_dist(x, y)); + } + } + + + //------------------------------------------------------------------------ + void trans_single_path::finalize_path() + { + if(m_status == making_path && m_src_vertices.size() > 1) + { + unsigned i; + double dist; + double d; + + m_src_vertices.close(false); + if(m_src_vertices.size() > 2) + { + if(m_src_vertices[m_src_vertices.size() - 2].dist * 10.0 < + m_src_vertices[m_src_vertices.size() - 3].dist) + { + d = m_src_vertices[m_src_vertices.size() - 3].dist + + m_src_vertices[m_src_vertices.size() - 2].dist; + + m_src_vertices[m_src_vertices.size() - 2] = + m_src_vertices[m_src_vertices.size() - 1]; + + m_src_vertices.remove_last(); + m_src_vertices[m_src_vertices.size() - 2].dist = d; + } + } + + dist = 0.0; + for(i = 0; i < m_src_vertices.size(); i++) + { + vertex_dist& v = m_src_vertices[i]; + double d = v.dist; + v.dist = dist; + dist += d; + } + m_kindex = (m_src_vertices.size() - 1) / dist; + m_status = ready; + } + } + + + + //------------------------------------------------------------------------ + double trans_single_path::total_length() const + { + if(m_base_length >= 1e-10) return m_base_length; + return (m_status == ready) ? + m_src_vertices[m_src_vertices.size() - 1].dist : + 0.0; + } + + + //------------------------------------------------------------------------ + void trans_single_path::transform(double *x, double *y) const + { + if(m_status == ready) + { + if(m_base_length > 1e-10) + { + *x *= m_src_vertices[m_src_vertices.size() - 1].dist / + m_base_length; + } + + double x1 = 0.0; + double y1 = 0.0; + double dx = 1.0; + double dy = 1.0; + double d = 0.0; + double dd = 1.0; + if(*x < 0.0) + { + // Extrapolation on the left + //-------------------------- + x1 = m_src_vertices[0].x; + y1 = m_src_vertices[0].y; + dx = m_src_vertices[1].x - x1; + dy = m_src_vertices[1].y - y1; + dd = m_src_vertices[1].dist - m_src_vertices[0].dist; + d = *x; + } + else + if(*x > m_src_vertices[m_src_vertices.size() - 1].dist) + { + // Extrapolation on the right + //-------------------------- + unsigned i = m_src_vertices.size() - 2; + unsigned j = m_src_vertices.size() - 1; + x1 = m_src_vertices[j].x; + y1 = m_src_vertices[j].y; + dx = x1 - m_src_vertices[i].x; + dy = y1 - m_src_vertices[i].y; + dd = m_src_vertices[j].dist - m_src_vertices[i].dist; + d = *x - m_src_vertices[j].dist; + } + else + { + // Interpolation + //-------------------------- + unsigned i = 0; + unsigned j = m_src_vertices.size() - 1; + if(m_preserve_x_scale) + { + unsigned k; + for(i = 0; (j - i) > 1; ) + { + if(*x < m_src_vertices[k = (i + j) >> 1].dist) + { + j = k; + } + else + { + i = k; + } + } + d = m_src_vertices[i].dist; + dd = m_src_vertices[j].dist - d; + d = *x - d; + } + else + { + i = unsigned(*x * m_kindex); + j = i + 1; + dd = m_src_vertices[j].dist - m_src_vertices[i].dist; + d = ((*x * m_kindex) - i) * dd; + } + x1 = m_src_vertices[i].x; + y1 = m_src_vertices[i].y; + dx = m_src_vertices[j].x - x1; + dy = m_src_vertices[j].y - y1; + } + double x2 = x1 + dx * d / dd; + double y2 = y1 + dy * d / dd; + *x = x2 - *y * dy / dd; + *y = y2 + *y * dx / dd; + } + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_trans_warp_magnifier.cpp b/desmume/src/windows/agg/src/agg_trans_warp_magnifier.cpp new file mode 100644 index 000000000..0854d06ce --- /dev/null +++ b/desmume/src/windows/agg/src/agg_trans_warp_magnifier.cpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_trans_warp_magnifier.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + void trans_warp_magnifier::transform(double* x, double* y) const + { + double dx = *x - m_xc; + double dy = *y - m_yc; + double r = sqrt(dx * dx + dy * dy); + if(r < m_radius) + { + *x = m_xc + dx * m_magn; + *y = m_yc + dy * m_magn; + return; + } + + double m = (r + m_radius * (m_magn - 1.0)) / r; + *x = m_xc + dx * m; + *y = m_yc + dy * m; + } + + //------------------------------------------------------------------------ + void trans_warp_magnifier::inverse_transform(double* x, double* y) const + { + // New version by Andrew Skalkin + //----------------- + double dx = *x - m_xc; + double dy = *y - m_yc; + double r = sqrt(dx * dx + dy * dy); + + if(r < m_radius * m_magn) + { + *x = m_xc + dx / m_magn; + *y = m_yc + dy / m_magn; + } + else + { + double rnew = r - m_radius * (m_magn - 1.0); + *x = m_xc + rnew * dx / r; + *y = m_yc + rnew * dy / r; + } + + // Old version + //----------------- + //trans_warp_magnifier t(*this); + //t.magnification(1.0 / m_magn); + //t.radius(m_radius * m_magn); + //t.transform(x, y); + } + + +} diff --git a/desmume/src/windows/agg/src/agg_vcgen_bspline.cpp b/desmume/src/windows/agg/src/agg_vcgen_bspline.cpp new file mode 100644 index 000000000..4483f612e --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_bspline.cpp @@ -0,0 +1,203 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vcgen_bspline.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_bspline::vcgen_bspline() : + m_src_vertices(), + m_spline_x(), + m_spline_y(), + m_interpolation_step(1.0/50.0), + m_closed(0), + m_status(initial), + m_src_vertex(0) + { + } + + + //------------------------------------------------------------------------ + void vcgen_bspline::remove_all() + { + m_src_vertices.remove_all(); + m_closed = 0; + m_status = initial; + m_src_vertex = 0; + } + + + //------------------------------------------------------------------------ + void vcgen_bspline::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(point_d(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(point_d(x, y)); + } + else + { + m_closed = get_close_flag(cmd); + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_bspline::rewind(unsigned) + { + m_cur_abscissa = 0.0; + m_max_abscissa = 0.0; + m_src_vertex = 0; + if(m_status == initial && m_src_vertices.size() > 2) + { + if(m_closed) + { + m_spline_x.init(m_src_vertices.size() + 8); + m_spline_y.init(m_src_vertices.size() + 8); + m_spline_x.add_point(0.0, m_src_vertices.prev(m_src_vertices.size() - 3).x); + m_spline_y.add_point(0.0, m_src_vertices.prev(m_src_vertices.size() - 3).y); + m_spline_x.add_point(1.0, m_src_vertices[m_src_vertices.size() - 3].x); + m_spline_y.add_point(1.0, m_src_vertices[m_src_vertices.size() - 3].y); + m_spline_x.add_point(2.0, m_src_vertices[m_src_vertices.size() - 2].x); + m_spline_y.add_point(2.0, m_src_vertices[m_src_vertices.size() - 2].y); + m_spline_x.add_point(3.0, m_src_vertices[m_src_vertices.size() - 1].x); + m_spline_y.add_point(3.0, m_src_vertices[m_src_vertices.size() - 1].y); + } + else + { + m_spline_x.init(m_src_vertices.size()); + m_spline_y.init(m_src_vertices.size()); + } + unsigned i; + for(i = 0; i < m_src_vertices.size(); i++) + { + double x = m_closed ? i + 4 : i; + m_spline_x.add_point(x, m_src_vertices[i].x); + m_spline_y.add_point(x, m_src_vertices[i].y); + } + m_cur_abscissa = 0.0; + m_max_abscissa = m_src_vertices.size() - 1; + if(m_closed) + { + m_cur_abscissa = 4.0; + m_max_abscissa += 5.0; + m_spline_x.add_point(m_src_vertices.size() + 4, m_src_vertices[0].x); + m_spline_y.add_point(m_src_vertices.size() + 4, m_src_vertices[0].y); + m_spline_x.add_point(m_src_vertices.size() + 5, m_src_vertices[1].x); + m_spline_y.add_point(m_src_vertices.size() + 5, m_src_vertices[1].y); + m_spline_x.add_point(m_src_vertices.size() + 6, m_src_vertices[2].x); + m_spline_y.add_point(m_src_vertices.size() + 6, m_src_vertices[2].y); + m_spline_x.add_point(m_src_vertices.size() + 7, m_src_vertices.next(2).x); + m_spline_y.add_point(m_src_vertices.size() + 7, m_src_vertices.next(2).y); + } + m_spline_x.prepare(); + m_spline_y.prepare(); + } + m_status = ready; + } + + + + + + + //------------------------------------------------------------------------ + unsigned vcgen_bspline::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_src_vertices.size() < 2) + { + cmd = path_cmd_stop; + break; + } + + if(m_src_vertices.size() == 2) + { + *x = m_src_vertices[m_src_vertex].x; + *y = m_src_vertices[m_src_vertex].y; + m_src_vertex++; + if(m_src_vertex == 1) return path_cmd_move_to; + if(m_src_vertex == 2) return path_cmd_line_to; + cmd = path_cmd_stop; + break; + } + + cmd = path_cmd_move_to; + m_status = polygon; + m_src_vertex = 0; + + case polygon: + if(m_cur_abscissa >= m_max_abscissa) + { + if(m_closed) + { + m_status = end_poly; + break; + } + else + { + *x = m_src_vertices[m_src_vertices.size() - 1].x; + *y = m_src_vertices[m_src_vertices.size() - 1].y; + m_status = end_poly; + return path_cmd_line_to; + } + } + + *x = m_spline_x.get_stateful(m_cur_abscissa); + *y = m_spline_y.get_stateful(m_cur_abscissa); + m_src_vertex++; + m_cur_abscissa += m_interpolation_step; + return (m_src_vertex == 1) ? path_cmd_move_to : path_cmd_line_to; + + case end_poly: + m_status = stop; + return path_cmd_end_poly | m_closed; + + case stop: + return path_cmd_stop; + } + } + return cmd; + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_vcgen_contour.cpp b/desmume/src/windows/agg/src/agg_vcgen_contour.cpp new file mode 100644 index 000000000..02c8b73ff --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_contour.cpp @@ -0,0 +1,170 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_vcgen_contour.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_contour::vcgen_contour() : + m_stroker(), + m_width(1), + m_src_vertices(), + m_out_vertices(), + m_status(initial), + m_src_vertex(0), + m_closed(0), + m_orientation(0), + m_auto_detect(false) + { + } + + //------------------------------------------------------------------------ + void vcgen_contour::remove_all() + { + m_src_vertices.remove_all(); + m_closed = 0; + m_orientation = 0; + m_status = initial; + } + + //------------------------------------------------------------------------ + void vcgen_contour::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(vertex_dist(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(vertex_dist(x, y)); + } + else + { + if(is_end_poly(cmd)) + { + m_closed = get_close_flag(cmd); + if(m_orientation == path_flags_none) + { + m_orientation = get_orientation(cmd); + } + } + } + } + } + + //------------------------------------------------------------------------ + void vcgen_contour::rewind(unsigned) + { + if(m_status == initial) + { + m_src_vertices.close(true); + if(m_auto_detect) + { + if(!is_oriented(m_orientation)) + { + m_orientation = (calc_polygon_area(m_src_vertices) > 0.0) ? + path_flags_ccw : + path_flags_cw; + } + } + if(is_oriented(m_orientation)) + { + m_stroker.width(is_ccw(m_orientation) ? m_width : -m_width); + } + } + m_status = ready; + m_src_vertex = 0; + } + + //------------------------------------------------------------------------ + unsigned vcgen_contour::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_src_vertices.size() < 2 + unsigned(m_closed != 0)) + { + cmd = path_cmd_stop; + break; + } + m_status = outline; + cmd = path_cmd_move_to; + m_src_vertex = 0; + m_out_vertex = 0; + + case outline: + if(m_src_vertex >= m_src_vertices.size()) + { + m_status = end_poly; + break; + } + m_stroker.calc_join(m_out_vertices, + m_src_vertices.prev(m_src_vertex), + m_src_vertices.curr(m_src_vertex), + m_src_vertices.next(m_src_vertex), + m_src_vertices.prev(m_src_vertex).dist, + m_src_vertices.curr(m_src_vertex).dist); + ++m_src_vertex; + m_status = out_vertices; + m_out_vertex = 0; + + case out_vertices: + if(m_out_vertex >= m_out_vertices.size()) + { + m_status = outline; + } + else + { + const point_d& c = m_out_vertices[m_out_vertex++]; + *x = c.x; + *y = c.y; + return cmd; + } + break; + + case end_poly: + if(!m_closed) return path_cmd_stop; + m_status = stop; + return path_cmd_end_poly | path_flags_close | path_flags_ccw; + + case stop: + return path_cmd_stop; + } + } + return cmd; + } + +} diff --git a/desmume/src/windows/agg/src/agg_vcgen_dash.cpp b/desmume/src/windows/agg/src/agg_vcgen_dash.cpp new file mode 100644 index 000000000..60d2f9ac9 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_dash.cpp @@ -0,0 +1,240 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_vcgen_dash.h" +#include "agg_shorten_path.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_dash::vcgen_dash() : + m_total_dash_len(0.0), + m_num_dashes(0), + m_dash_start(0.0), + m_shorten(0.0), + m_curr_dash_start(0.0), + m_curr_dash(0), + m_src_vertices(), + m_closed(0), + m_status(initial), + m_src_vertex(0) + { + } + + + + //------------------------------------------------------------------------ + void vcgen_dash::remove_all_dashes() + { + m_total_dash_len = 0.0; + m_num_dashes = 0; + m_curr_dash_start = 0.0; + m_curr_dash = 0; + } + + + //------------------------------------------------------------------------ + void vcgen_dash::add_dash(double dash_len, double gap_len) + { + if(m_num_dashes < max_dashes) + { + m_total_dash_len += dash_len + gap_len; + m_dashes[m_num_dashes++] = dash_len; + m_dashes[m_num_dashes++] = gap_len; + } + } + + + //------------------------------------------------------------------------ + void vcgen_dash::dash_start(double ds) + { + m_dash_start = ds; + calc_dash_start(fabs(ds)); + } + + + //------------------------------------------------------------------------ + void vcgen_dash::calc_dash_start(double ds) + { + m_curr_dash = 0; + m_curr_dash_start = 0.0; + while(ds > 0.0) + { + if(ds > m_dashes[m_curr_dash]) + { + ds -= m_dashes[m_curr_dash]; + ++m_curr_dash; + m_curr_dash_start = 0.0; + if(m_curr_dash >= m_num_dashes) m_curr_dash = 0; + } + else + { + m_curr_dash_start = ds; + ds = 0.0; + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_dash::remove_all() + { + m_status = initial; + m_src_vertices.remove_all(); + m_closed = 0; + } + + + //------------------------------------------------------------------------ + void vcgen_dash::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(vertex_dist(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(vertex_dist(x, y)); + } + else + { + m_closed = get_close_flag(cmd); + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_dash::rewind(unsigned) + { + if(m_status == initial) + { + m_src_vertices.close(m_closed != 0); + shorten_path(m_src_vertices, m_shorten, m_closed); + } + m_status = ready; + m_src_vertex = 0; + } + + + //------------------------------------------------------------------------ + unsigned vcgen_dash::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_move_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_num_dashes < 2 || m_src_vertices.size() < 2) + { + cmd = path_cmd_stop; + break; + } + m_status = polyline; + m_src_vertex = 1; + m_v1 = &m_src_vertices[0]; + m_v2 = &m_src_vertices[1]; + m_curr_rest = m_v1->dist; + *x = m_v1->x; + *y = m_v1->y; + if(m_dash_start >= 0.0) calc_dash_start(m_dash_start); + return path_cmd_move_to; + + case polyline: + { + double dash_rest = m_dashes[m_curr_dash] - m_curr_dash_start; + + unsigned cmd = (m_curr_dash & 1) ? + path_cmd_move_to : + path_cmd_line_to; + + if(m_curr_rest > dash_rest) + { + m_curr_rest -= dash_rest; + ++m_curr_dash; + if(m_curr_dash >= m_num_dashes) m_curr_dash = 0; + m_curr_dash_start = 0.0; + *x = m_v2->x - (m_v2->x - m_v1->x) * m_curr_rest / m_v1->dist; + *y = m_v2->y - (m_v2->y - m_v1->y) * m_curr_rest / m_v1->dist; + } + else + { + m_curr_dash_start += m_curr_rest; + *x = m_v2->x; + *y = m_v2->y; + ++m_src_vertex; + m_v1 = m_v2; + m_curr_rest = m_v1->dist; + if(m_closed) + { + if(m_src_vertex > m_src_vertices.size()) + { + m_status = stop; + } + else + { + m_v2 = &m_src_vertices + [ + (m_src_vertex >= m_src_vertices.size()) ? 0 : + m_src_vertex + ]; + } + } + else + { + if(m_src_vertex >= m_src_vertices.size()) + { + m_status = stop; + } + else + { + m_v2 = &m_src_vertices[m_src_vertex]; + } + } + } + return cmd; + } + break; + + case stop: + cmd = path_cmd_stop; + break; + } + + } + return path_cmd_stop; + } + + +} + diff --git a/desmume/src/windows/agg/src/agg_vcgen_markers_term.cpp b/desmume/src/windows/agg/src/agg_vcgen_markers_term.cpp new file mode 100644 index 000000000..5863691f6 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_markers_term.cpp @@ -0,0 +1,108 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vcgen_markers_term.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + void vcgen_markers_term::remove_all() + { + m_markers.remove_all(); + } + + + //------------------------------------------------------------------------ + void vcgen_markers_term::add_vertex(double x, double y, unsigned cmd) + { + if(is_move_to(cmd)) + { + if(m_markers.size() & 1) + { + // Initial state, the first coordinate was added. + // If two of more calls of start_vertex() occures + // we just modify the last one. + m_markers.modify_last(coord_type(x, y)); + } + else + { + m_markers.add(coord_type(x, y)); + } + } + else + { + if(is_vertex(cmd)) + { + if(m_markers.size() & 1) + { + // Initial state, the first coordinate was added. + // Add three more points, 0,1,1,0 + m_markers.add(coord_type(x, y)); + m_markers.add(m_markers[m_markers.size() - 1]); + m_markers.add(m_markers[m_markers.size() - 3]); + } + else + { + if(m_markers.size()) + { + // Replace two last points: 0,1,1,0 -> 0,1,2,1 + m_markers[m_markers.size() - 1] = m_markers[m_markers.size() - 2]; + m_markers[m_markers.size() - 2] = coord_type(x, y); + } + } + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_markers_term::rewind(unsigned path_id) + { + m_curr_id = path_id * 2; + m_curr_idx = m_curr_id; + } + + + //------------------------------------------------------------------------ + unsigned vcgen_markers_term::vertex(double* x, double* y) + { + if(m_curr_id > 2 || m_curr_idx >= m_markers.size()) + { + return path_cmd_stop; + } + const coord_type& c = m_markers[m_curr_idx]; + *x = c.x; + *y = c.y; + if(m_curr_idx & 1) + { + m_curr_idx += 3; + return path_cmd_line_to; + } + ++m_curr_idx; + return path_cmd_move_to; + } + + +} diff --git a/desmume/src/windows/agg/src/agg_vcgen_smooth_poly1.cpp b/desmume/src/windows/agg/src/agg_vcgen_smooth_poly1.cpp new file mode 100644 index 000000000..359768aa7 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_smooth_poly1.cpp @@ -0,0 +1,230 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vcgen_smooth_poly1.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_smooth_poly1::vcgen_smooth_poly1() : + m_src_vertices(), + m_smooth_value(0.5), + m_closed(0), + m_status(initial), + m_src_vertex(0) + { + } + + + //------------------------------------------------------------------------ + void vcgen_smooth_poly1::remove_all() + { + m_src_vertices.remove_all(); + m_closed = 0; + m_status = initial; + } + + + //------------------------------------------------------------------------ + void vcgen_smooth_poly1::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(vertex_dist(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(vertex_dist(x, y)); + } + else + { + m_closed = get_close_flag(cmd); + } + } + } + + + //------------------------------------------------------------------------ + void vcgen_smooth_poly1::rewind(unsigned) + { + if(m_status == initial) + { + m_src_vertices.close(m_closed != 0); + } + m_status = ready; + m_src_vertex = 0; + } + + + //------------------------------------------------------------------------ + void vcgen_smooth_poly1::calculate(const vertex_dist& v0, + const vertex_dist& v1, + const vertex_dist& v2, + const vertex_dist& v3) + { + + double k1 = v0.dist / (v0.dist + v1.dist); + double k2 = v1.dist / (v1.dist + v2.dist); + + double xm1 = v0.x + (v2.x - v0.x) * k1; + double ym1 = v0.y + (v2.y - v0.y) * k1; + double xm2 = v1.x + (v3.x - v1.x) * k2; + double ym2 = v1.y + (v3.y - v1.y) * k2; + + m_ctrl1_x = v1.x + m_smooth_value * (v2.x - xm1); + m_ctrl1_y = v1.y + m_smooth_value * (v2.y - ym1); + m_ctrl2_x = v2.x + m_smooth_value * (v1.x - xm2); + m_ctrl2_y = v2.y + m_smooth_value * (v1.y - ym2); + } + + + //------------------------------------------------------------------------ + unsigned vcgen_smooth_poly1::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_src_vertices.size() < 2) + { + cmd = path_cmd_stop; + break; + } + + if(m_src_vertices.size() == 2) + { + *x = m_src_vertices[m_src_vertex].x; + *y = m_src_vertices[m_src_vertex].y; + m_src_vertex++; + if(m_src_vertex == 1) return path_cmd_move_to; + if(m_src_vertex == 2) return path_cmd_line_to; + cmd = path_cmd_stop; + break; + } + + cmd = path_cmd_move_to; + m_status = polygon; + m_src_vertex = 0; + + case polygon: + if(m_closed) + { + if(m_src_vertex >= m_src_vertices.size()) + { + *x = m_src_vertices[0].x; + *y = m_src_vertices[0].y; + m_status = end_poly; + return path_cmd_curve4; + } + } + else + { + if(m_src_vertex >= m_src_vertices.size() - 1) + { + *x = m_src_vertices[m_src_vertices.size() - 1].x; + *y = m_src_vertices[m_src_vertices.size() - 1].y; + m_status = end_poly; + return path_cmd_curve3; + } + } + + calculate(m_src_vertices.prev(m_src_vertex), + m_src_vertices.curr(m_src_vertex), + m_src_vertices.next(m_src_vertex), + m_src_vertices.next(m_src_vertex + 1)); + + *x = m_src_vertices[m_src_vertex].x; + *y = m_src_vertices[m_src_vertex].y; + m_src_vertex++; + + if(m_closed) + { + m_status = ctrl1; + return ((m_src_vertex == 1) ? + path_cmd_move_to : + path_cmd_curve4); + } + else + { + if(m_src_vertex == 1) + { + m_status = ctrl_b; + return path_cmd_move_to; + } + if(m_src_vertex >= m_src_vertices.size() - 1) + { + m_status = ctrl_e; + return path_cmd_curve3; + } + m_status = ctrl1; + return path_cmd_curve4; + } + break; + + case ctrl_b: + *x = m_ctrl2_x; + *y = m_ctrl2_y; + m_status = polygon; + return path_cmd_curve3; + + case ctrl_e: + *x = m_ctrl1_x; + *y = m_ctrl1_y; + m_status = polygon; + return path_cmd_curve3; + + case ctrl1: + *x = m_ctrl1_x; + *y = m_ctrl1_y; + m_status = ctrl2; + return path_cmd_curve4; + + case ctrl2: + *x = m_ctrl2_x; + *y = m_ctrl2_y; + m_status = polygon; + return path_cmd_curve4; + + case end_poly: + m_status = stop; + return path_cmd_end_poly | m_closed; + + case stop: + return path_cmd_stop; + } + } + return cmd; + } + +} + diff --git a/desmume/src/windows/agg/src/agg_vcgen_stroke.cpp b/desmume/src/windows/agg/src/agg_vcgen_stroke.cpp new file mode 100644 index 000000000..b36927bf3 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vcgen_stroke.cpp @@ -0,0 +1,219 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_vcgen_stroke.h" +#include "agg_shorten_path.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + vcgen_stroke::vcgen_stroke() : + m_stroker(), + m_src_vertices(), + m_out_vertices(), + m_shorten(0.0), + m_closed(0), + m_status(initial), + m_src_vertex(0), + m_out_vertex(0) + { + } + + //------------------------------------------------------------------------ + void vcgen_stroke::remove_all() + { + m_src_vertices.remove_all(); + m_closed = 0; + m_status = initial; + } + + + //------------------------------------------------------------------------ + void vcgen_stroke::add_vertex(double x, double y, unsigned cmd) + { + m_status = initial; + if(is_move_to(cmd)) + { + m_src_vertices.modify_last(vertex_dist(x, y)); + } + else + { + if(is_vertex(cmd)) + { + m_src_vertices.add(vertex_dist(x, y)); + } + else + { + m_closed = get_close_flag(cmd); + } + } + } + + //------------------------------------------------------------------------ + void vcgen_stroke::rewind(unsigned) + { + if(m_status == initial) + { + m_src_vertices.close(m_closed != 0); + shorten_path(m_src_vertices, m_shorten, m_closed); + if(m_src_vertices.size() < 3) m_closed = 0; + } + m_status = ready; + m_src_vertex = 0; + m_out_vertex = 0; + } + + + //------------------------------------------------------------------------ + unsigned vcgen_stroke::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + while(!is_stop(cmd)) + { + switch(m_status) + { + case initial: + rewind(0); + + case ready: + if(m_src_vertices.size() < 2 + unsigned(m_closed != 0)) + { + cmd = path_cmd_stop; + break; + } + m_status = m_closed ? outline1 : cap1; + cmd = path_cmd_move_to; + m_src_vertex = 0; + m_out_vertex = 0; + break; + + case cap1: + m_stroker.calc_cap(m_out_vertices, + m_src_vertices[0], + m_src_vertices[1], + m_src_vertices[0].dist); + m_src_vertex = 1; + m_prev_status = outline1; + m_status = out_vertices; + m_out_vertex = 0; + break; + + case cap2: + m_stroker.calc_cap(m_out_vertices, + m_src_vertices[m_src_vertices.size() - 1], + m_src_vertices[m_src_vertices.size() - 2], + m_src_vertices[m_src_vertices.size() - 2].dist); + m_prev_status = outline2; + m_status = out_vertices; + m_out_vertex = 0; + break; + + case outline1: + if(m_closed) + { + if(m_src_vertex >= m_src_vertices.size()) + { + m_prev_status = close_first; + m_status = end_poly1; + break; + } + } + else + { + if(m_src_vertex >= m_src_vertices.size() - 1) + { + m_status = cap2; + break; + } + } + m_stroker.calc_join(m_out_vertices, + m_src_vertices.prev(m_src_vertex), + m_src_vertices.curr(m_src_vertex), + m_src_vertices.next(m_src_vertex), + m_src_vertices.prev(m_src_vertex).dist, + m_src_vertices.curr(m_src_vertex).dist); + ++m_src_vertex; + m_prev_status = m_status; + m_status = out_vertices; + m_out_vertex = 0; + break; + + case close_first: + m_status = outline2; + cmd = path_cmd_move_to; + + case outline2: + if(m_src_vertex <= unsigned(m_closed == 0)) + { + m_status = end_poly2; + m_prev_status = stop; + break; + } + + --m_src_vertex; + m_stroker.calc_join(m_out_vertices, + m_src_vertices.next(m_src_vertex), + m_src_vertices.curr(m_src_vertex), + m_src_vertices.prev(m_src_vertex), + m_src_vertices.curr(m_src_vertex).dist, + m_src_vertices.prev(m_src_vertex).dist); + + m_prev_status = m_status; + m_status = out_vertices; + m_out_vertex = 0; + break; + + case out_vertices: + if(m_out_vertex >= m_out_vertices.size()) + { + m_status = m_prev_status; + } + else + { + const point_d& c = m_out_vertices[m_out_vertex++]; + *x = c.x; + *y = c.y; + return cmd; + } + break; + + case end_poly1: + m_status = m_prev_status; + return path_cmd_end_poly | path_flags_close | path_flags_ccw; + + case end_poly2: + m_status = m_prev_status; + return path_cmd_end_poly | path_flags_close | path_flags_cw; + + case stop: + cmd = path_cmd_stop; + break; + } + } + return cmd; + } + +} diff --git a/desmume/src/windows/agg/src/agg_vpgen_clip_polygon.cpp b/desmume/src/windows/agg/src/agg_vpgen_clip_polygon.cpp new file mode 100644 index 000000000..2e9502ce0 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vpgen_clip_polygon.cpp @@ -0,0 +1,142 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vpgen_clip_polygon.h" +#include "agg_clip_liang_barsky.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + // Determine the clipping code of the vertex according to the + // Cyrus-Beck line clipping algorithm + // + // | | + // 0110 | 0010 | 0011 + // | | + // -------+--------+-------- clip_box.y2 + // | | + // 0100 | 0000 | 0001 + // | | + // -------+--------+-------- clip_box.y1 + // | | + // 1100 | 1000 | 1001 + // | | + // clip_box.x1 clip_box.x2 + // + // + unsigned vpgen_clip_polygon::clipping_flags(double x, double y) + { + if(x < m_clip_box.x1) + { + if(y > m_clip_box.y2) return 6; + if(y < m_clip_box.y1) return 12; + return 4; + } + + if(x > m_clip_box.x2) + { + if(y > m_clip_box.y2) return 3; + if(y < m_clip_box.y1) return 9; + return 1; + } + + if(y > m_clip_box.y2) return 2; + if(y < m_clip_box.y1) return 8; + + return 0; + } + + //---------------------------------------------------------------------------- + void vpgen_clip_polygon::reset() + { + m_vertex = 0; + m_num_vertices = 0; + } + + //---------------------------------------------------------------------------- + void vpgen_clip_polygon::move_to(double x, double y) + { + m_vertex = 0; + m_num_vertices = 0; + m_clip_flags = clipping_flags(x, y); + if(m_clip_flags == 0) + { + m_x[0] = x; + m_y[0] = y; + m_num_vertices = 1; + } + m_x1 = x; + m_y1 = y; + m_cmd = path_cmd_move_to; + } + + + //---------------------------------------------------------------------------- + void vpgen_clip_polygon::line_to(double x, double y) + { + m_vertex = 0; + m_num_vertices = 0; + unsigned flags = clipping_flags(x, y); + + if(m_clip_flags == flags) + { + if(flags == 0) + { + m_x[0] = x; + m_y[0] = y; + m_num_vertices = 1; + } + } + else + { + m_num_vertices = clip_liang_barsky(m_x1, m_y1, + x, y, + m_clip_box, + m_x, m_y); + } + + m_clip_flags = flags; + m_x1 = x; + m_y1 = y; + } + + + //---------------------------------------------------------------------------- + unsigned vpgen_clip_polygon::vertex(double* x, double* y) + { + if(m_vertex < m_num_vertices) + { + *x = m_x[m_vertex]; + *y = m_y[m_vertex]; + ++m_vertex; + unsigned cmd = m_cmd; + m_cmd = path_cmd_line_to; + return cmd; + } + return path_cmd_stop; + } + + +} diff --git a/desmume/src/windows/agg/src/agg_vpgen_clip_polyline.cpp b/desmume/src/windows/agg/src/agg_vpgen_clip_polyline.cpp new file mode 100644 index 000000000..9b2f72680 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vpgen_clip_polyline.cpp @@ -0,0 +1,86 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "agg_vpgen_clip_polyline.h" +#include "agg_clip_liang_barsky.h" + +namespace agg +{ + //---------------------------------------------------------------------------- + void vpgen_clip_polyline::reset() + { + m_vertex = 0; + m_num_vertices = 0; + m_move_to = false; + } + + //---------------------------------------------------------------------------- + void vpgen_clip_polyline::move_to(double x, double y) + { + m_vertex = 0; + m_num_vertices = 0; + m_x1 = x; + m_y1 = y; + m_move_to = true; + } + + //---------------------------------------------------------------------------- + void vpgen_clip_polyline::line_to(double x, double y) + { + double x2 = x; + double y2 = y; + unsigned flags = clip_line_segment(&m_x1, &m_y1, &x2, &y2, m_clip_box); + + m_vertex = 0; + m_num_vertices = 0; + if((flags & 4) == 0) + { + if((flags & 1) != 0 || m_move_to) + { + m_x[0] = m_x1; + m_y[0] = m_y1; + m_cmd[0] = path_cmd_move_to; + m_num_vertices = 1; + } + m_x[m_num_vertices] = x2; + m_y[m_num_vertices] = y2; + m_cmd[m_num_vertices++] = path_cmd_line_to; + m_move_to = (flags & 2) != 0; + } + m_x1 = x; + m_y1 = y; + } + + //---------------------------------------------------------------------------- + unsigned vpgen_clip_polyline::vertex(double* x, double* y) + { + if(m_vertex < m_num_vertices) + { + *x = m_x[m_vertex]; + *y = m_y[m_vertex]; + return m_cmd[m_vertex++]; + } + return path_cmd_stop; + } +} diff --git a/desmume/src/windows/agg/src/agg_vpgen_segmentator.cpp b/desmume/src/windows/agg/src/agg_vpgen_segmentator.cpp new file mode 100644 index 000000000..eb5df3f34 --- /dev/null +++ b/desmume/src/windows/agg/src/agg_vpgen_segmentator.cpp @@ -0,0 +1,76 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_vpgen_segmentator.h" + +namespace agg +{ + + void vpgen_segmentator::move_to(double x, double y) + { + m_x1 = x; + m_y1 = y; + m_dx = 0.0; + m_dy = 0.0; + m_dl = 2.0; + m_ddl = 2.0; + m_cmd = path_cmd_move_to; + } + + void vpgen_segmentator::line_to(double x, double y) + { + m_x1 += m_dx; + m_y1 += m_dy; + m_dx = x - m_x1; + m_dy = y - m_y1; + double len = sqrt(m_dx * m_dx + m_dy * m_dy) * m_approximation_scale; + if(len < 1e-30) len = 1e-30; + m_ddl = 1.0 / len; + m_dl = (m_cmd == path_cmd_move_to) ? 0.0 : m_ddl; + if(m_cmd == path_cmd_stop) m_cmd = path_cmd_line_to; + } + + unsigned vpgen_segmentator::vertex(double* x, double* y) + { + if(m_cmd == path_cmd_stop) return path_cmd_stop; + + unsigned cmd = m_cmd; + m_cmd = path_cmd_line_to; + if(m_dl >= 1.0 - m_ddl) + { + m_dl = 1.0; + m_cmd = path_cmd_stop; + *x = m_x1 + m_dx; + *y = m_y1 + m_dy; + return cmd; + } + *x = m_x1 + m_dx * m_dl; + *y = m_y1 + m_dy * m_dl; + m_dl += m_ddl; + return cmd; + } + +} + diff --git a/desmume/src/windows/agg/src/authors b/desmume/src/windows/agg/src/authors new file mode 100644 index 000000000..e69de29bb diff --git a/desmume/src/windows/agg/src/autogen.sh b/desmume/src/windows/agg/src/autogen.sh new file mode 100644 index 000000000..d37d8e2b9 --- /dev/null +++ b/desmume/src/windows/agg/src/autogen.sh @@ -0,0 +1,20 @@ +# autogen.sh +# +# invoke the auto* tools to create the configureation system + +# build aclocal.m4 +aclocal + +# build the configure script +autoconf + +# set up libtool +libtoolize --force + +# invoke automake +automake --foreign --add-missing + +# and finally invoke our new configure +./configure $* + +# end diff --git a/desmume/src/windows/agg/src/configure.in b/desmume/src/windows/agg/src/configure.in new file mode 100644 index 000000000..fe58b4290 --- /dev/null +++ b/desmume/src/windows/agg/src/configure.in @@ -0,0 +1,15 @@ +AC_INIT(src/agg_arc.cpp) # give me a source file, any source file... +AM_INIT_AUTOMAKE(agg, 2.0.0) + +AC_PROG_LN_S +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_LIBTOOL + +AC_OUTPUT( + Makefile + gpc/Makefile + src/Makefile + src/ctrl/Makefile +) diff --git a/desmume/src/windows/agg/src/copying b/desmume/src/windows/agg/src/copying new file mode 100644 index 000000000..a88fd34d7 --- /dev/null +++ b/desmume/src/windows/agg/src/copying @@ -0,0 +1,21 @@ +Anti-Grain Geometry (AGG) - Version 2.5 +A high quality rendering engine for C++ +Copyright (C) 2002-2006 Maxim Shemanarev +Contact: mcseem@antigrain.com + mcseemagg@yahoo.com + http://antigrain.com + +AGG is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +AGG is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with AGG; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. diff --git a/desmume/src/windows/agg/src/ctrl/agg_bezier_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_bezier_ctrl.cpp new file mode 100644 index 000000000..ff278bd49 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_bezier_ctrl.cpp @@ -0,0 +1,375 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include +#include "ctrl/agg_bezier_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + bezier_ctrl_impl::bezier_ctrl_impl() : + ctrl(0,0,1,1,false), + m_stroke(m_curve), + m_poly(4, 5.0), + m_idx(0) + { + m_poly.in_polygon_check(false); + m_poly.xn(0) = 100.0; + m_poly.yn(0) = 0.0; + m_poly.xn(1) = 100.0; + m_poly.yn(1) = 50.0; + m_poly.xn(2) = 50.0; + m_poly.yn(2) = 100.0; + m_poly.xn(3) = 0.0; + m_poly.yn(3) = 100.0; + } + + + //------------------------------------------------------------------------ + void bezier_ctrl_impl::curve(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + m_poly.xn(0) = x1; + m_poly.yn(0) = y1; + m_poly.xn(1) = x2; + m_poly.yn(1) = y2; + m_poly.xn(2) = x3; + m_poly.yn(2) = y3; + m_poly.xn(3) = x4; + m_poly.yn(3) = y4; + curve(); + } + + //------------------------------------------------------------------------ + curve4& bezier_ctrl_impl::curve() + { + m_curve.init(m_poly.xn(0), m_poly.yn(0), + m_poly.xn(1), m_poly.yn(1), + m_poly.xn(2), m_poly.yn(2), + m_poly.xn(3), m_poly.yn(3)); + return m_curve; + } + + //------------------------------------------------------------------------ + void bezier_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + + m_curve.approximation_scale(scale()); + switch(idx) + { + default: + case 0: // Control line 1 + m_curve.init(m_poly.xn(0), m_poly.yn(0), + (m_poly.xn(0) + m_poly.xn(1)) * 0.5, + (m_poly.yn(0) + m_poly.yn(1)) * 0.5, + (m_poly.xn(0) + m_poly.xn(1)) * 0.5, + (m_poly.yn(0) + m_poly.yn(1)) * 0.5, + m_poly.xn(1), m_poly.yn(1)); + m_stroke.rewind(0); + break; + + case 1: // Control line 2 + m_curve.init(m_poly.xn(2), m_poly.yn(2), + (m_poly.xn(2) + m_poly.xn(3)) * 0.5, + (m_poly.yn(2) + m_poly.yn(3)) * 0.5, + (m_poly.xn(2) + m_poly.xn(3)) * 0.5, + (m_poly.yn(2) + m_poly.yn(3)) * 0.5, + m_poly.xn(3), m_poly.yn(3)); + m_stroke.rewind(0); + break; + + case 2: // Curve itself + m_curve.init(m_poly.xn(0), m_poly.yn(0), + m_poly.xn(1), m_poly.yn(1), + m_poly.xn(2), m_poly.yn(2), + m_poly.xn(3), m_poly.yn(3)); + m_stroke.rewind(0); + break; + + case 3: // Point 1 + m_ellipse.init(m_poly.xn(0), m_poly.yn(0), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + + case 4: // Point 2 + m_ellipse.init(m_poly.xn(1), m_poly.yn(1), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + + case 5: // Point 3 + m_ellipse.init(m_poly.xn(2), m_poly.yn(2), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + + case 6: // Point 4 + m_ellipse.init(m_poly.xn(3), m_poly.yn(3), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + } + } + + + //------------------------------------------------------------------------ + unsigned bezier_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + switch(m_idx) + { + case 0: + case 1: + case 2: + cmd = m_stroke.vertex(x, y); + break; + + case 3: + case 4: + case 5: + case 6: + case 7: + cmd = m_ellipse.vertex(x, y); + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + return cmd; + } + + + + //------------------------------------------------------------------------ + bool bezier_ctrl_impl::in_rect(double x, double y) const + { + return false; + } + + + //------------------------------------------------------------------------ + bool bezier_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + return m_poly.on_mouse_button_down(x, y); + } + + + //------------------------------------------------------------------------ + bool bezier_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + return m_poly.on_mouse_move(x, y, button_flag); + } + + + //------------------------------------------------------------------------ + bool bezier_ctrl_impl::on_mouse_button_up(double x, double y) + { + return m_poly.on_mouse_button_up(x, y); + } + + + //------------------------------------------------------------------------ + bool bezier_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + return m_poly.on_arrow_keys(left, right, down, up); + } + + + + + + + //------------------------------------------------------------------------ + curve3_ctrl_impl::curve3_ctrl_impl() : + ctrl(0,0,1,1,false), + m_stroke(m_curve), + m_poly(3, 5.0), + m_idx(0) + { + m_poly.in_polygon_check(false); + m_poly.xn(0) = 100.0; + m_poly.yn(0) = 0.0; + m_poly.xn(1) = 100.0; + m_poly.yn(1) = 50.0; + m_poly.xn(2) = 50.0; + m_poly.yn(2) = 100.0; + } + + + //------------------------------------------------------------------------ + void curve3_ctrl_impl::curve(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + m_poly.xn(0) = x1; + m_poly.yn(0) = y1; + m_poly.xn(1) = x2; + m_poly.yn(1) = y2; + m_poly.xn(2) = x3; + m_poly.yn(2) = y3; + curve(); + } + + //------------------------------------------------------------------------ + curve3& curve3_ctrl_impl::curve() + { + m_curve.init(m_poly.xn(0), m_poly.yn(0), + m_poly.xn(1), m_poly.yn(1), + m_poly.xn(2), m_poly.yn(2)); + return m_curve; + } + + //------------------------------------------------------------------------ + void curve3_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + + switch(idx) + { + default: + case 0: // Control line + m_curve.init(m_poly.xn(0), m_poly.yn(0), + (m_poly.xn(0) + m_poly.xn(1)) * 0.5, + (m_poly.yn(0) + m_poly.yn(1)) * 0.5, + m_poly.xn(1), m_poly.yn(1)); + m_stroke.rewind(0); + break; + + case 1: // Control line 2 + m_curve.init(m_poly.xn(1), m_poly.yn(1), + (m_poly.xn(1) + m_poly.xn(2)) * 0.5, + (m_poly.yn(1) + m_poly.yn(2)) * 0.5, + m_poly.xn(2), m_poly.yn(2)); + m_stroke.rewind(0); + break; + + case 2: // Curve itself + m_curve.init(m_poly.xn(0), m_poly.yn(0), + m_poly.xn(1), m_poly.yn(1), + m_poly.xn(2), m_poly.yn(2)); + m_stroke.rewind(0); + break; + + case 3: // Point 1 + m_ellipse.init(m_poly.xn(0), m_poly.yn(0), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + + case 4: // Point 2 + m_ellipse.init(m_poly.xn(1), m_poly.yn(1), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + + case 5: // Point 3 + m_ellipse.init(m_poly.xn(2), m_poly.yn(2), point_radius(), point_radius(), 20); + m_ellipse.rewind(0); + break; + } + } + + + //------------------------------------------------------------------------ + unsigned curve3_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + switch(m_idx) + { + case 0: + case 1: + case 2: + cmd = m_stroke.vertex(x, y); + break; + + case 3: + case 4: + case 5: + case 6: + cmd = m_ellipse.vertex(x, y); + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + return cmd; + } + + + + //------------------------------------------------------------------------ + bool curve3_ctrl_impl::in_rect(double x, double y) const + { + return false; + } + + + //------------------------------------------------------------------------ + bool curve3_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + return m_poly.on_mouse_button_down(x, y); + } + + + //------------------------------------------------------------------------ + bool curve3_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + return m_poly.on_mouse_move(x, y, button_flag); + } + + + //------------------------------------------------------------------------ + bool curve3_ctrl_impl::on_mouse_button_up(double x, double y) + { + return m_poly.on_mouse_button_up(x, y); + } + + + //------------------------------------------------------------------------ + bool curve3_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + return m_poly.on_arrow_keys(left, right, down, up); + } + + + + + + + + + + + + +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_cbox_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_cbox_ctrl.cpp new file mode 100644 index 000000000..3753475ca --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_cbox_ctrl.cpp @@ -0,0 +1,219 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "ctrl/agg_cbox_ctrl.h" + + +namespace agg +{ + + //------------------------------------------------------------------------ + cbox_ctrl_impl::cbox_ctrl_impl(double x, double y, + const char* l, + bool flip_y) : + ctrl(x, y, x + 9.0 * 1.5, y + 9.0 * 1.5, flip_y), + m_text_thickness(1.5), + m_text_height(9.0), + m_text_width(0.0), + m_status(false), + m_text_poly(m_text) + { + label(l); + } + + + //------------------------------------------------------------------------ + void cbox_ctrl_impl::text_size(double h, double w) + { + m_text_width = w; + m_text_height = h; + } + + //------------------------------------------------------------------------ + void cbox_ctrl_impl::label(const char* l) + { + unsigned len = strlen(l); + if(len > 127) len = 127; + memcpy(m_label, l, len); + m_label[len] = 0; + } + + + //------------------------------------------------------------------------ + bool cbox_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + if(x >= m_x1 && y >= m_y1 && x <= m_x2 && y <= m_y2) + { + m_status = !m_status; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool cbox_ctrl_impl::on_mouse_move(double, double, bool) + { + return false; + } + + //------------------------------------------------------------------------ + bool cbox_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && y >= m_y1 && x <= m_x2 && y <= m_y2; + } + + //------------------------------------------------------------------------ + bool cbox_ctrl_impl::on_mouse_button_up(double, double) + { + return false; + } + + //------------------------------------------------------------------------ + bool cbox_ctrl_impl::on_arrow_keys(bool, bool, bool, bool) + { + return false; + } + + + //------------------------------------------------------------------------ + void cbox_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + + double d2; + double t; + + switch(idx) + { + default: + case 0: // Border + m_vertex = 0; + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y2; + m_vx[4] = m_x1 + m_text_thickness; + m_vy[4] = m_y1 + m_text_thickness; + m_vx[5] = m_x1 + m_text_thickness; + m_vy[5] = m_y2 - m_text_thickness; + m_vx[6] = m_x2 - m_text_thickness; + m_vy[6] = m_y2 - m_text_thickness; + m_vx[7] = m_x2 - m_text_thickness; + m_vy[7] = m_y1 + m_text_thickness; + break; + + case 1: // Text + m_text.text(m_label); + m_text.start_point(m_x1 + m_text_height * 2.0, m_y1 + m_text_height / 5.0); + m_text.size(m_text_height, m_text_width); + m_text_poly.width(m_text_thickness); + m_text_poly.line_join(round_join); + m_text_poly.line_cap(round_cap); + m_text_poly.rewind(0); + break; + + case 2: // Active item + m_vertex = 0; + d2 = (m_y2 - m_y1) / 2.0; + t = m_text_thickness * 1.5; + m_vx[0] = m_x1 + m_text_thickness; + m_vy[0] = m_y1 + m_text_thickness; + m_vx[1] = m_x1 + d2; + m_vy[1] = m_y1 + d2 - t; + m_vx[2] = m_x2 - m_text_thickness; + m_vy[2] = m_y1 + m_text_thickness; + m_vx[3] = m_x1 + d2 + t; + m_vy[3] = m_y1 + d2; + m_vx[4] = m_x2 - m_text_thickness; + m_vy[4] = m_y2 - m_text_thickness; + m_vx[5] = m_x1 + d2; + m_vy[5] = m_y1 + d2 + t; + m_vx[6] = m_x1 + m_text_thickness; + m_vy[6] = m_y2 - m_text_thickness; + m_vx[7] = m_x1 + d2 - t; + m_vy[7] = m_y1 + d2; + break; + + } + } + + + + + //------------------------------------------------------------------------ + unsigned cbox_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + if(m_vertex == 0 || m_vertex == 4) cmd = path_cmd_move_to; + if(m_vertex >= 8) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + cmd = m_text_poly.vertex(x, y); + break; + + case 2: + if(m_status) + { + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 8) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + } + else + { + cmd = path_cmd_stop; + } + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + return cmd; + } +} + + + diff --git a/desmume/src/windows/agg/src/ctrl/agg_gamma_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_gamma_ctrl.cpp new file mode 100644 index 000000000..9521bab9f --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_gamma_ctrl.cpp @@ -0,0 +1,438 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "agg_math.h" +#include "ctrl/agg_gamma_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + gamma_ctrl_impl::gamma_ctrl_impl(double x1, double y1, double x2, double y2, bool flip_y) : + ctrl(x1, y1, x2, y2, flip_y), + m_border_width(2.0), + m_border_extra(0.0), + m_curve_width(2.0), + m_grid_width(0.2), + m_text_thickness(1.5), + m_point_size(5.0), + m_text_height(9.0), + m_text_width(0.0), + m_xc1(x1), + m_yc1(y1), + m_xc2(x2), + m_yc2(y2 - m_text_height * 2.0), + m_xt1(x1), + m_yt1(y2 - m_text_height * 2.0), + m_xt2(x2), + m_yt2(y2), + m_curve_poly(m_gamma_spline), + m_text_poly(m_text), + m_idx(0), + m_vertex(0), + m_p1_active(true), + m_mouse_point(0), + m_pdx(0.0), + m_pdy(0.0) + { + calc_spline_box(); + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::calc_spline_box() + { + m_xs1 = m_xc1 + m_border_width; + m_ys1 = m_yc1 + m_border_width; + m_xs2 = m_xc2 - m_border_width; + m_ys2 = m_yc2 - m_border_width * 0.5; + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::calc_points() + { + double kx1, ky1, kx2, ky2; + m_gamma_spline.values(&kx1, &ky1, &kx2, &ky2); + m_xp1 = m_xs1 + (m_xs2 - m_xs1) * kx1 * 0.25; + m_yp1 = m_ys1 + (m_ys2 - m_ys1) * ky1 * 0.25; + m_xp2 = m_xs2 - (m_xs2 - m_xs1) * kx2 * 0.25; + m_yp2 = m_ys2 - (m_ys2 - m_ys1) * ky2 * 0.25; + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::calc_values() + { + double kx1, ky1, kx2, ky2; + + kx1 = (m_xp1 - m_xs1) * 4.0 / (m_xs2 - m_xs1); + ky1 = (m_yp1 - m_ys1) * 4.0 / (m_ys2 - m_ys1); + kx2 = (m_xs2 - m_xp2) * 4.0 / (m_xs2 - m_xs1); + ky2 = (m_ys2 - m_yp2) * 4.0 / (m_ys2 - m_ys1); + m_gamma_spline.values(kx1, ky1, kx2, ky2); + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::text_size(double h, double w) + { + m_text_width = w; + m_text_height = h; + m_yc2 = m_y2 - m_text_height * 2.0; + m_yt1 = m_y2 - m_text_height * 2.0; + calc_spline_box(); + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::border_width(double t, double extra) + { + m_border_width = t; + m_border_extra = extra; + calc_spline_box(); + } + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::values(double kx1, double ky1, double kx2, double ky2) + { + m_gamma_spline.values(kx1, ky1, kx2, ky2); + } + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::values(double* kx1, double* ky1, double* kx2, double* ky2) const + { + m_gamma_spline.values(kx1, ky1, kx2, ky2); + } + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::rewind(unsigned idx) + { + double kx1, ky1, kx2, ky2; + char tbuf[32]; + + m_idx = idx; + + switch(idx) + { + default: + + case 0: // Background + m_vertex = 0; + m_vx[0] = m_x1 - m_border_extra; + m_vy[0] = m_y1 - m_border_extra; + m_vx[1] = m_x2 + m_border_extra; + m_vy[1] = m_y1 - m_border_extra; + m_vx[2] = m_x2 + m_border_extra; + m_vy[2] = m_y2 + m_border_extra; + m_vx[3] = m_x1 - m_border_extra; + m_vy[3] = m_y2 + m_border_extra; + break; + + case 1: // Border + m_vertex = 0; + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y2; + m_vx[4] = m_x1 + m_border_width; + m_vy[4] = m_y1 + m_border_width; + m_vx[5] = m_x1 + m_border_width; + m_vy[5] = m_y2 - m_border_width; + m_vx[6] = m_x2 - m_border_width; + m_vy[6] = m_y2 - m_border_width; + m_vx[7] = m_x2 - m_border_width; + m_vy[7] = m_y1 + m_border_width; + m_vx[8] = m_xc1 + m_border_width; + m_vy[8] = m_yc2 - m_border_width * 0.5; + m_vx[9] = m_xc2 - m_border_width; + m_vy[9] = m_yc2 - m_border_width * 0.5; + m_vx[10] = m_xc2 - m_border_width; + m_vy[10] = m_yc2 + m_border_width * 0.5; + m_vx[11] = m_xc1 + m_border_width; + m_vy[11] = m_yc2 + m_border_width * 0.5; + break; + + case 2: // Curve + m_gamma_spline.box(m_xs1, m_ys1, m_xs2, m_ys2); + m_curve_poly.width(m_curve_width); + m_curve_poly.rewind(0); + break; + + case 3: // Grid + m_vertex = 0; + m_vx[0] = m_xs1; + m_vy[0] = (m_ys1 + m_ys2) * 0.5 - m_grid_width * 0.5; + m_vx[1] = m_xs2; + m_vy[1] = (m_ys1 + m_ys2) * 0.5 - m_grid_width * 0.5; + m_vx[2] = m_xs2; + m_vy[2] = (m_ys1 + m_ys2) * 0.5 + m_grid_width * 0.5; + m_vx[3] = m_xs1; + m_vy[3] = (m_ys1 + m_ys2) * 0.5 + m_grid_width * 0.5; + m_vx[4] = (m_xs1 + m_xs2) * 0.5 - m_grid_width * 0.5; + m_vy[4] = m_ys1; + m_vx[5] = (m_xs1 + m_xs2) * 0.5 - m_grid_width * 0.5; + m_vy[5] = m_ys2; + m_vx[6] = (m_xs1 + m_xs2) * 0.5 + m_grid_width * 0.5; + m_vy[6] = m_ys2; + m_vx[7] = (m_xs1 + m_xs2) * 0.5 + m_grid_width * 0.5; + m_vy[7] = m_ys1; + calc_points(); + m_vx[8] = m_xs1; + m_vy[8] = m_yp1 - m_grid_width * 0.5; + m_vx[9] = m_xp1 - m_grid_width * 0.5; + m_vy[9] = m_yp1 - m_grid_width * 0.5; + m_vx[10] = m_xp1 - m_grid_width * 0.5; + m_vy[10] = m_ys1; + m_vx[11] = m_xp1 + m_grid_width * 0.5; + m_vy[11] = m_ys1; + m_vx[12] = m_xp1 + m_grid_width * 0.5; + m_vy[12] = m_yp1 + m_grid_width * 0.5; + m_vx[13] = m_xs1; + m_vy[13] = m_yp1 + m_grid_width * 0.5; + m_vx[14] = m_xs2; + m_vy[14] = m_yp2 + m_grid_width * 0.5; + m_vx[15] = m_xp2 + m_grid_width * 0.5; + m_vy[15] = m_yp2 + m_grid_width * 0.5; + m_vx[16] = m_xp2 + m_grid_width * 0.5; + m_vy[16] = m_ys2; + m_vx[17] = m_xp2 - m_grid_width * 0.5; + m_vy[17] = m_ys2; + m_vx[18] = m_xp2 - m_grid_width * 0.5; + m_vy[18] = m_yp2 - m_grid_width * 0.5; + m_vx[19] = m_xs2; + m_vy[19] = m_yp2 - m_grid_width * 0.5; + break; + + case 4: // Point1 + calc_points(); + if(m_p1_active) m_ellipse.init(m_xp2, m_yp2, m_point_size, m_point_size, 32); + else m_ellipse.init(m_xp1, m_yp1, m_point_size, m_point_size, 32); + break; + + case 5: // Point2 + calc_points(); + if(m_p1_active) m_ellipse.init(m_xp1, m_yp1, m_point_size, m_point_size, 32); + else m_ellipse.init(m_xp2, m_yp2, m_point_size, m_point_size, 32); + break; + + case 6: // Text + m_gamma_spline.values(&kx1, &ky1, &kx2, &ky2); + sprintf(tbuf, "%5.3f %5.3f %5.3f %5.3f", kx1, ky1, kx2, ky2); + m_text.text(tbuf); + m_text.size(m_text_height, m_text_width); + m_text.start_point(m_xt1 + m_border_width * 2.0, (m_yt1 + m_yt2) * 0.5 - m_text_height * 0.5); + m_text_poly.width(m_text_thickness); + m_text_poly.line_join(round_join); + m_text_poly.line_cap(round_cap); + m_text_poly.rewind(0); + break; + } + } + + + //------------------------------------------------------------------------ + unsigned gamma_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + if(m_vertex == 0 || m_vertex == 4 || m_vertex == 8) cmd = path_cmd_move_to; + if(m_vertex >= 12) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 2: + cmd = m_curve_poly.vertex(x, y); + break; + + case 3: + if(m_vertex == 0 || + m_vertex == 4 || + m_vertex == 8 || + m_vertex == 14) cmd = path_cmd_move_to; + + if(m_vertex >= 20) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 4: // Point1 + case 5: // Point2 + cmd = m_ellipse.vertex(x, y); + break; + + case 6: + cmd = m_text_poly.vertex(x, y); + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + + return cmd; + } + + + + //------------------------------------------------------------------------ + bool gamma_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + double kx1, ky1, kx2, ky2; + bool ret = false; + m_gamma_spline.values(&kx1, &ky1, &kx2, &ky2); + if(m_p1_active) + { + if(left) { kx1 -= 0.005; ret = true; } + if(right) { kx1 += 0.005; ret = true; } + if(down) { ky1 -= 0.005; ret = true; } + if(up) { ky1 += 0.005; ret = true; } + } + else + { + if(left) { kx2 += 0.005; ret = true; } + if(right) { kx2 -= 0.005; ret = true; } + if(down) { ky2 += 0.005; ret = true; } + if(up) { ky2 -= 0.005; ret = true; } + } + if(ret) + { + m_gamma_spline.values(kx1, ky1, kx2, ky2); + } + return ret; + } + + + + //------------------------------------------------------------------------ + void gamma_ctrl_impl::change_active_point() + { + m_p1_active = m_p1_active ? false : true; + } + + + + + //------------------------------------------------------------------------ + bool gamma_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2; + } + + + //------------------------------------------------------------------------ + bool gamma_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + calc_points(); + + if(calc_distance(x, y, m_xp1, m_yp1) <= m_point_size + 1) + { + m_mouse_point = 1; + m_pdx = m_xp1 - x; + m_pdy = m_yp1 - y; + m_p1_active = true; + return true; + } + + if(calc_distance(x, y, m_xp2, m_yp2) <= m_point_size + 1) + { + m_mouse_point = 2; + m_pdx = m_xp2 - x; + m_pdy = m_yp2 - y; + m_p1_active = false; + return true; + } + + return false; + } + + + //------------------------------------------------------------------------ + bool gamma_ctrl_impl::on_mouse_button_up(double, double) + { + if(m_mouse_point) + { + m_mouse_point = 0; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool gamma_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + if(!button_flag) + { + return on_mouse_button_up(x, y); + } + + if(m_mouse_point == 1) + { + m_xp1 = x + m_pdx; + m_yp1 = y + m_pdy; + calc_values(); + return true; + } + if(m_mouse_point == 2) + { + m_xp2 = x + m_pdx; + m_yp2 = y + m_pdy; + calc_values(); + return true; + } + return false; + } + + + +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_gamma_spline.cpp b/desmume/src/windows/agg/src/ctrl/agg_gamma_spline.cpp new file mode 100644 index 000000000..10de9c397 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_gamma_spline.cpp @@ -0,0 +1,135 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "ctrl/agg_gamma_spline.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + gamma_spline::gamma_spline() : + m_x1(0), m_y1(0), m_x2(10), m_y2(10), m_cur_x(0.0) + { + values(1.0, 1.0, 1.0, 1.0); + } + + + //------------------------------------------------------------------------ + double gamma_spline::y(double x) const + { + if(x < 0.0) x = 0.0; + if(x > 1.0) x = 1.0; + double val = m_spline.get(x); + if(val < 0.0) val = 0.0; + if(val > 1.0) val = 1.0; + return val; + } + + + + //------------------------------------------------------------------------ + void gamma_spline::values(double kx1, double ky1, double kx2, double ky2) + { + if(kx1 < 0.001) kx1 = 0.001; + if(kx1 > 1.999) kx1 = 1.999; + if(ky1 < 0.001) ky1 = 0.001; + if(ky1 > 1.999) ky1 = 1.999; + if(kx2 < 0.001) kx2 = 0.001; + if(kx2 > 1.999) kx2 = 1.999; + if(ky2 < 0.001) ky2 = 0.001; + if(ky2 > 1.999) ky2 = 1.999; + + m_x[0] = 0.0; + m_y[0] = 0.0; + m_x[1] = kx1 * 0.25; + m_y[1] = ky1 * 0.25; + m_x[2] = 1.0 - kx2 * 0.25; + m_y[2] = 1.0 - ky2 * 0.25; + m_x[3] = 1.0; + m_y[3] = 1.0; + + m_spline.init(4, m_x, m_y); + + int i; + for(i = 0; i < 256; i++) + { + m_gamma[i] = (unsigned char)(y(double(i) / 255.0) * 255.0); + } + } + + + //------------------------------------------------------------------------ + void gamma_spline::values(double* kx1, double* ky1, double* kx2, double* ky2) const + { + *kx1 = m_x[1] * 4.0; + *ky1 = m_y[1] * 4.0; + *kx2 = (1.0 - m_x[2]) * 4.0; + *ky2 = (1.0 - m_y[2]) * 4.0; + } + + + //------------------------------------------------------------------------ + void gamma_spline::box(double x1, double y1, double x2, double y2) + { + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + } + + + //------------------------------------------------------------------------ + void gamma_spline::rewind(unsigned) + { + m_cur_x = 0.0; + } + + + //------------------------------------------------------------------------ + unsigned gamma_spline::vertex(double* vx, double* vy) + { + if(m_cur_x == 0.0) + { + *vx = m_x1; + *vy = m_y1; + m_cur_x += 1.0 / (m_x2 - m_x1); + return path_cmd_move_to; + } + + if(m_cur_x > 1.0) + { + return path_cmd_stop; + } + + *vx = m_x1 + m_cur_x * (m_x2 - m_x1); + *vy = m_y1 + y(m_cur_x) * (m_y2 - m_y1); + + m_cur_x += 1.0 / (m_x2 - m_x1); + return path_cmd_line_to; + } + + + +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_polygon_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_polygon_ctrl.cpp new file mode 100644 index 000000000..4dde1b7f8 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_polygon_ctrl.cpp @@ -0,0 +1,337 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "ctrl/agg_polygon_ctrl.h" + +namespace agg +{ + + polygon_ctrl_impl::polygon_ctrl_impl(unsigned np, double point_radius) : + ctrl(0, 0, 1, 1, false), + m_polygon(np * 2), + m_num_points(np), + m_node(-1), + m_edge(-1), + m_vs(&m_polygon[0], m_num_points, false), + m_stroke(m_vs), + m_point_radius(point_radius), + m_status(0), + m_dx(0.0), + m_dy(0.0), + m_in_polygon_check(true) + { + m_stroke.width(1.0); + } + + + void polygon_ctrl_impl::rewind(unsigned) + { + m_status = 0; + m_stroke.rewind(0); + } + + unsigned polygon_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_stop; + double r = m_point_radius; + if(m_status == 0) + { + cmd = m_stroke.vertex(x, y); + if(!is_stop(cmd)) + { + transform_xy(x, y); + return cmd; + } + if(m_node >= 0 && m_node == int(m_status)) r *= 1.2; + m_ellipse.init(xn(m_status), yn(m_status), r, r, 32); + ++m_status; + } + cmd = m_ellipse.vertex(x, y); + if(!is_stop(cmd)) + { + transform_xy(x, y); + return cmd; + } + if(m_status >= m_num_points) return path_cmd_stop; + if(m_node >= 0 && m_node == int(m_status)) r *= 1.2; + m_ellipse.init(xn(m_status), yn(m_status), r, r, 32); + ++m_status; + cmd = m_ellipse.vertex(x, y); + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + return cmd; + } + + + bool polygon_ctrl_impl::check_edge(unsigned i, double x, double y) const + { + bool ret = false; + + unsigned n1 = i; + unsigned n2 = (i + m_num_points - 1) % m_num_points; + double x1 = xn(n1); + double y1 = yn(n1); + double x2 = xn(n2); + double y2 = yn(n2); + + double dx = x2 - x1; + double dy = y2 - y1; + + if(sqrt(dx*dx + dy*dy) > 0.0000001) + { + double x3 = x; + double y3 = y; + double x4 = x3 - dy; + double y4 = y3 + dx; + + double den = (y4-y3) * (x2-x1) - (x4-x3) * (y2-y1); + double u1 = ((x4-x3) * (y1-y3) - (y4-y3) * (x1-x3)) / den; + + double xi = x1 + u1 * (x2 - x1); + double yi = y1 + u1 * (y2 - y1); + + dx = xi - x; + dy = yi - y; + + if (u1 > 0.0 && u1 < 1.0 && sqrt(dx*dx + dy*dy) <= m_point_radius) + { + ret = true; + } + } + return ret; + } + + + + bool polygon_ctrl_impl::in_rect(double x, double y) const + { + return false; + } + + + bool polygon_ctrl_impl::on_mouse_button_down(double x, double y) + { + unsigned i; + bool ret = false; + m_node = -1; + m_edge = -1; + inverse_transform_xy(&x, &y); + for (i = 0; i < m_num_points; i++) + { + if(sqrt( (x-xn(i)) * (x-xn(i)) + (y-yn(i)) * (y-yn(i)) ) < m_point_radius) + { + m_dx = x - xn(i); + m_dy = y - yn(i); + m_node = int(i); + ret = true; + break; + } + } + + if(!ret) + { + for (i = 0; i < m_num_points; i++) + { + if(check_edge(i, x, y)) + { + m_dx = x; + m_dy = y; + m_edge = int(i); + ret = true; + break; + } + } + } + + if(!ret) + { + if(point_in_polygon(x, y)) + { + m_dx = x; + m_dy = y; + m_node = int(m_num_points); + ret = true; + } + } + return ret; + } + + + bool polygon_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + bool ret = false; + double dx; + double dy; + inverse_transform_xy(&x, &y); + if(m_node == int(m_num_points)) + { + dx = x - m_dx; + dy = y - m_dy; + unsigned i; + for(i = 0; i < m_num_points; i++) + { + xn(i) += dx; + yn(i) += dy; + } + m_dx = x; + m_dy = y; + ret = true; + } + else + { + if(m_edge >= 0) + { + unsigned n1 = m_edge; + unsigned n2 = (n1 + m_num_points - 1) % m_num_points; + dx = x - m_dx; + dy = y - m_dy; + xn(n1) += dx; + yn(n1) += dy; + xn(n2) += dx; + yn(n2) += dy; + m_dx = x; + m_dy = y; + ret = true; + } + else + { + if(m_node >= 0) + { + xn(m_node) = x - m_dx; + yn(m_node) = y - m_dy; + ret = true; + } + } + } + return ret; + } + + bool polygon_ctrl_impl::on_mouse_button_up(double x, double y) + { + bool ret = (m_node >= 0) || (m_edge >= 0); + m_node = -1; + m_edge = -1; + return ret; + } + + + bool polygon_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + return false; + } + + + //======= Crossings Multiply algorithm of InsideTest ======================== + // + // By Eric Haines, 3D/Eye Inc, erich@eye.com + // + // This version is usually somewhat faster than the original published in + // Graphics Gems IV; by turning the division for testing the X axis crossing + // into a tricky multiplication test this part of the test became faster, + // which had the additional effect of making the test for "both to left or + // both to right" a bit slower for triangles than simply computing the + // intersection each time. The main increase is in triangle testing speed, + // which was about 15% faster; all other polygon complexities were pretty much + // the same as before. On machines where division is very expensive (not the + // case on the HP 9000 series on which I tested) this test should be much + // faster overall than the old code. Your mileage may (in fact, will) vary, + // depending on the machine and the test data, but in general I believe this + // code is both shorter and faster. This test was inspired by unpublished + // Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson. + // Related work by Samosky is in: + // + // Samosky, Joseph, "SectionView: A system for interactively specifying and + // visualizing sections through three-dimensional medical image data", + // M.S. Thesis, Department of Electrical Engineering and Computer Science, + // Massachusetts Institute of Technology, 1993. + // + // Shoot a test ray along +X axis. The strategy is to compare vertex Y values + // to the testing point's Y and quickly discard edges which are entirely to one + // side of the test ray. Note that CONVEX and WINDING code can be added as + // for the CrossingsTest() code; it is left out here for clarity. + // + // Input 2D polygon _pgon_ with _numverts_ number of vertices and test point + // _point_, returns 1 if inside, 0 if outside. + bool polygon_ctrl_impl::point_in_polygon(double tx, double ty) const + { + if(m_num_points < 3) return false; + if(!m_in_polygon_check) return false; + + unsigned j; + int yflag0, yflag1, inside_flag; + double vtx0, vty0, vtx1, vty1; + + vtx0 = xn(m_num_points - 1); + vty0 = yn(m_num_points - 1); + + // get test bit for above/below X axis + yflag0 = (vty0 >= ty); + + vtx1 = xn(0); + vty1 = yn(0); + + inside_flag = 0; + for (j = 1; j <= m_num_points; ++j) + { + yflag1 = (vty1 >= ty); + // Check if endpoints straddle (are on opposite sides) of X axis + // (i.e. the Y's differ); if so, +X ray could intersect this edge. + // The old test also checked whether the endpoints are both to the + // right or to the left of the test point. However, given the faster + // intersection point computation used below, this test was found to + // be a break-even proposition for most polygons and a loser for + // triangles (where 50% or more of the edges which survive this test + // will cross quadrants and so have to have the X intersection computed + // anyway). I credit Joseph Samosky with inspiring me to try dropping + // the "both left or both right" part of my code. + if (yflag0 != yflag1) + { + // Check intersection of pgon segment with +X ray. + // Note if >= point's X; if so, the ray hits it. + // The division operation is avoided for the ">=" test by checking + // the sign of the first vertex wrto the test point; idea inspired + // by Joseph Samosky's and Mark Haigh-Hutchinson's different + // polygon inclusion tests. + if ( ((vty1-ty) * (vtx0-vtx1) >= + (vtx1-tx) * (vty0-vty1)) == yflag1 ) + { + inside_flag ^= 1; + } + } + + // Move to the next pair of vertices, retaining info as possible. + yflag0 = yflag1; + vtx0 = vtx1; + vty0 = vty1; + + unsigned k = (j >= m_num_points) ? j - m_num_points : j; + vtx1 = xn(k); + vty1 = yn(k); + } + return inside_flag != 0; + } +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_rbox_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_rbox_ctrl.cpp new file mode 100644 index 000000000..c643fec38 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_rbox_ctrl.cpp @@ -0,0 +1,330 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include "ctrl/agg_rbox_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + rbox_ctrl_impl::rbox_ctrl_impl(double x1, double y1, + double x2, double y2, bool flip_y) : + ctrl(x1, y1, x2, y2, flip_y), + m_border_width(1.0), + m_border_extra(0.0), + m_text_thickness(1.5), + m_text_height(9.0), + m_text_width(0.0), + m_num_items(0), + m_cur_item(-1), + m_ellipse_poly(m_ellipse), + m_text_poly(m_text), + m_idx(0), + m_vertex(0) + { + calc_rbox(); + } + + + //------------------------------------------------------------------------ + void rbox_ctrl_impl::calc_rbox() + { + m_xs1 = m_x1 + m_border_width; + m_ys1 = m_y1 + m_border_width; + m_xs2 = m_x2 - m_border_width; + m_ys2 = m_y2 - m_border_width; + } + + + //------------------------------------------------------------------------ + void rbox_ctrl_impl::add_item(const char* text) + { + if(m_num_items < 32) + { + m_items[m_num_items].resize(strlen(text) + 1); + strcpy(&m_items[m_num_items][0], text); + m_num_items++; + } + } + + + //------------------------------------------------------------------------ + void rbox_ctrl_impl::border_width(double t, double extra) + { + m_border_width = t; + m_border_extra = extra; + calc_rbox(); + } + + + //------------------------------------------------------------------------ + void rbox_ctrl_impl::text_size(double h, double w) + { + m_text_width = w; + m_text_height = h; + } + + + + //------------------------------------------------------------------------ + void rbox_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + m_dy = m_text_height * 2.0; + m_draw_item = 0; + + switch(idx) + { + default: + + case 0: // Background + m_vertex = 0; + m_vx[0] = m_x1 - m_border_extra; + m_vy[0] = m_y1 - m_border_extra; + m_vx[1] = m_x2 + m_border_extra; + m_vy[1] = m_y1 - m_border_extra; + m_vx[2] = m_x2 + m_border_extra; + m_vy[2] = m_y2 + m_border_extra; + m_vx[3] = m_x1 - m_border_extra; + m_vy[3] = m_y2 + m_border_extra; + break; + + case 1: // Border + m_vertex = 0; + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y2; + m_vx[4] = m_x1 + m_border_width; + m_vy[4] = m_y1 + m_border_width; + m_vx[5] = m_x1 + m_border_width; + m_vy[5] = m_y2 - m_border_width; + m_vx[6] = m_x2 - m_border_width; + m_vy[6] = m_y2 - m_border_width; + m_vx[7] = m_x2 - m_border_width; + m_vy[7] = m_y1 + m_border_width; + break; + + case 2: // Text + m_text.text(&m_items[0][0]); + m_text.start_point(m_xs1 + m_dy * 1.5, m_ys1 + m_dy / 2.0); + m_text.size(m_text_height, m_text_width); + m_text_poly.width(m_text_thickness); + m_text_poly.line_join(round_join); + m_text_poly.line_cap(round_cap); + m_text_poly.rewind(0); + break; + + case 3: // Inactive items + m_ellipse.init(m_xs1 + m_dy / 1.3, + m_ys1 + m_dy / 1.3, + m_text_height / 1.5, + m_text_height / 1.5, 32); + m_ellipse_poly.width(m_text_thickness); + m_ellipse_poly.rewind(0); + break; + + + case 4: // Active Item + if(m_cur_item >= 0) + { + m_ellipse.init(m_xs1 + m_dy / 1.3, + m_ys1 + m_dy * m_cur_item + m_dy / 1.3, + m_text_height / 2.0, + m_text_height / 2.0, 32); + m_ellipse.rewind(0); + } + break; + + } + } + + + //------------------------------------------------------------------------ + unsigned rbox_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + if(m_vertex == 0 || m_vertex == 4) cmd = path_cmd_move_to; + if(m_vertex >= 8) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 2: + cmd = m_text_poly.vertex(x, y); + if(is_stop(cmd)) + { + m_draw_item++; + if(m_draw_item >= m_num_items) + { + break; + } + else + { + m_text.text(&m_items[m_draw_item][0]); + m_text.start_point(m_xs1 + m_dy * 1.5, + m_ys1 + m_dy * (m_draw_item + 1) - m_dy / 2.0); + + m_text_poly.rewind(0); + cmd = m_text_poly.vertex(x, y); + } + } + break; + + case 3: + cmd = m_ellipse_poly.vertex(x, y); + if(is_stop(cmd)) + { + m_draw_item++; + if(m_draw_item >= m_num_items) + { + break; + } + else + { + m_ellipse.init(m_xs1 + m_dy / 1.3, + m_ys1 + m_dy * m_draw_item + m_dy / 1.3, + m_text_height / 1.5, + m_text_height / 1.5, 32); + m_ellipse_poly.rewind(0); + cmd = m_ellipse_poly.vertex(x, y); + } + } + break; + + + case 4: + if(m_cur_item >= 0) + { + cmd = m_ellipse.vertex(x, y); + } + else + { + cmd = path_cmd_stop; + } + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + + return cmd; + } + + + //------------------------------------------------------------------------ + bool rbox_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2; + } + + + + //------------------------------------------------------------------------ + bool rbox_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + unsigned i; + for(i = 0; i < m_num_items; i++) + { + double xp = m_xs1 + m_dy / 1.3; + double yp = m_ys1 + m_dy * i + m_dy / 1.3; + if(calc_distance(x, y, xp, yp) <= m_text_height / 1.5) + { + m_cur_item = int(i); + return true; + } + } + return false; + } + + + //------------------------------------------------------------------------ + bool rbox_ctrl_impl::on_mouse_move(double, double, bool) + { + return false; + } + + //------------------------------------------------------------------------ + bool rbox_ctrl_impl::on_mouse_button_up(double, double) + { + return false; + } + + //------------------------------------------------------------------------ + bool rbox_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + if(m_cur_item >= 0) + { + if(up || right) + { + m_cur_item++; + if(m_cur_item >= int(m_num_items)) + { + m_cur_item = 0; + } + return true; + } + + if(down || left) + { + m_cur_item--; + if(m_cur_item < 0) + { + m_cur_item = m_num_items - 1; + } + return true; + } + } + return false; + } + + +} + + diff --git a/desmume/src/windows/agg/src/ctrl/agg_scale_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_scale_ctrl.cpp new file mode 100644 index 000000000..974a4fc72 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_scale_ctrl.cpp @@ -0,0 +1,459 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "ctrl/agg_scale_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + scale_ctrl_impl::scale_ctrl_impl(double x1, double y1, + double x2, double y2, bool flip_y) : + ctrl(x1, y1, x2, y2, flip_y), + m_border_thickness(1.0), + m_border_extra((fabs(x2 - x1) > fabs(y2 - y1)) ? (y2 - y1) / 2 : (x2 - x1) / 2), + m_pdx(0.0), + m_pdy(0.0), + m_move_what(move_nothing), + m_value1(0.3), + m_value2(0.7), + m_min_d(0.01) + { + calc_box(); + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::calc_box() + { + m_xs1 = m_x1 + m_border_thickness; + m_ys1 = m_y1 + m_border_thickness; + m_xs2 = m_x2 - m_border_thickness; + m_ys2 = m_y2 - m_border_thickness; + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::border_thickness(double t, double extra) + { + m_border_thickness = t; + m_border_extra = extra; + calc_box(); + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::resize(double x1, double y1, double x2, double y2) + { + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + calc_box(); + m_border_extra = (fabs(x2 - x1) > fabs(y2 - y1)) ? + (y2 - y1) / 2 : + (x2 - x1) / 2; + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::value1(double value) + { + if(value < 0.0) value = 0.0; + if(value > 1.0) value = 1.0; + if(m_value2 - value < m_min_d) value = m_value2 - m_min_d; + m_value1 = value; + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::value2(double value) + { + if(value < 0.0) value = 0.0; + if(value > 1.0) value = 1.0; + if(m_value1 + value < m_min_d) value = m_value1 + m_min_d; + m_value2 = value; + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::move(double d) + { + m_value1 += d; + m_value2 += d; + if(m_value1 < 0.0) + { + m_value2 -= m_value1; + m_value1 = 0.0; + } + if(m_value2 > 1.0) + { + m_value1 -= m_value2 - 1.0; + m_value2 = 1.0; + } + } + + + //------------------------------------------------------------------------ + void scale_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + + switch(idx) + { + default: + + case 0: // Background + m_vertex = 0; + m_vx[0] = m_x1 - m_border_extra; + m_vy[0] = m_y1 - m_border_extra; + m_vx[1] = m_x2 + m_border_extra; + m_vy[1] = m_y1 - m_border_extra; + m_vx[2] = m_x2 + m_border_extra; + m_vy[2] = m_y2 + m_border_extra; + m_vx[3] = m_x1 - m_border_extra; + m_vy[3] = m_y2 + m_border_extra; + break; + + case 1: // Border + m_vertex = 0; + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y2; + m_vx[4] = m_x1 + m_border_thickness; + m_vy[4] = m_y1 + m_border_thickness; + m_vx[5] = m_x1 + m_border_thickness; + m_vy[5] = m_y2 - m_border_thickness; + m_vx[6] = m_x2 - m_border_thickness; + m_vy[6] = m_y2 - m_border_thickness; + m_vx[7] = m_x2 - m_border_thickness; + m_vy[7] = m_y1 + m_border_thickness; + break; + + case 2: // pointer1 + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_value1, + (m_ys1 + m_ys2) / 2.0, + m_y2 - m_y1, + m_y2 - m_y1, + 32); + } + else + { + m_ellipse.init((m_xs1 + m_xs2) / 2.0, + m_ys1 + (m_ys2 - m_ys1) * m_value1, + m_x2 - m_x1, + m_x2 - m_x1, + 32); + } + m_ellipse.rewind(0); + break; + + case 3: // pointer2 + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_value2, + (m_ys1 + m_ys2) / 2.0, + m_y2 - m_y1, + m_y2 - m_y1, + 32); + } + else + { + m_ellipse.init((m_xs1 + m_xs2) / 2.0, + m_ys1 + (m_ys2 - m_ys1) * m_value2, + m_x2 - m_x1, + m_x2 - m_x1, + 32); + } + m_ellipse.rewind(0); + break; + + case 4: // slider + m_vertex = 0; + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_vx[0] = m_xs1 + (m_xs2 - m_xs1) * m_value1; + m_vy[0] = m_y1 - m_border_extra / 2.0; + m_vx[1] = m_xs1 + (m_xs2 - m_xs1) * m_value2; + m_vy[1] = m_vy[0]; + m_vx[2] = m_vx[1]; + m_vy[2] = m_y2 + m_border_extra / 2.0; + m_vx[3] = m_vx[0]; + m_vy[3] = m_vy[2]; + } + else + { + m_vx[0] = m_x1 - m_border_extra / 2.0; + m_vy[0] = m_ys1 + (m_ys2 - m_ys1) * m_value1; + m_vx[1] = m_vx[0]; + m_vy[1] = m_ys1 + (m_ys2 - m_ys1) * m_value2; + m_vx[2] = m_x2 + m_border_extra / 2.0; + m_vy[2] = m_vy[1]; + m_vx[3] = m_vx[2]; + m_vy[3] = m_vy[0]; + } + break; + } + } + + + //------------------------------------------------------------------------ + unsigned scale_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + case 4: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + if(m_vertex == 0 || m_vertex == 4) cmd = path_cmd_move_to; + if(m_vertex >= 8) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 2: + case 3: + cmd = m_ellipse.vertex(x, y); + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + + return cmd; + } + + + + //------------------------------------------------------------------------ + bool scale_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2; + } + + + //------------------------------------------------------------------------ + bool scale_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + + double xp1; + double xp2; + double ys1; + double ys2; + double xp; + double yp; + + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + xp1 = m_xs1 + (m_xs2 - m_xs1) * m_value1; + xp2 = m_xs1 + (m_xs2 - m_xs1) * m_value2; + ys1 = m_y1 - m_border_extra / 2.0; + ys2 = m_y2 + m_border_extra / 2.0; + yp = (m_ys1 + m_ys2) / 2.0; + + if(x > xp1 && y > ys1 && x < xp2 && y < ys2) + { + m_pdx = xp1 - x; + m_move_what = move_slider; + return true; + } + + //if(x < xp1 && calc_distance(x, y, xp1, yp) <= m_y2 - m_y1) + if(calc_distance(x, y, xp1, yp) <= m_y2 - m_y1) + { + m_pdx = xp1 - x; + m_move_what = move_value1; + return true; + } + + //if(x > xp2 && calc_distance(x, y, xp2, yp) <= m_y2 - m_y1) + if(calc_distance(x, y, xp2, yp) <= m_y2 - m_y1) + { + m_pdx = xp2 - x; + m_move_what = move_value2; + return true; + } + } + else + { + xp1 = m_x1 - m_border_extra / 2.0; + xp2 = m_x2 + m_border_extra / 2.0; + ys1 = m_ys1 + (m_ys2 - m_ys1) * m_value1; + ys2 = m_ys1 + (m_ys2 - m_ys1) * m_value2; + xp = (m_xs1 + m_xs2) / 2.0; + + if(x > xp1 && y > ys1 && x < xp2 && y < ys2) + { + m_pdy = ys1 - y; + m_move_what = move_slider; + return true; + } + + //if(y < ys1 && calc_distance(x, y, xp, ys1) <= m_x2 - m_x1) + if(calc_distance(x, y, xp, ys1) <= m_x2 - m_x1) + { + m_pdy = ys1 - y; + m_move_what = move_value1; + return true; + } + + //if(y > ys2 && calc_distance(x, y, xp, ys2) <= m_x2 - m_x1) + if(calc_distance(x, y, xp, ys2) <= m_x2 - m_x1) + { + m_pdy = ys2 - y; + m_move_what = move_value2; + return true; + } + } + + return false; + } + + + //------------------------------------------------------------------------ + bool scale_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + if(!button_flag) + { + return on_mouse_button_up(x, y); + } + + double xp = x + m_pdx; + double yp = y + m_pdy; + double dv; + + switch(m_move_what) + { + case move_value1: + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_value1 = (xp - m_xs1) / (m_xs2 - m_xs1); + } + else + { + m_value1 = (yp - m_ys1) / (m_ys2 - m_ys1); + } + if(m_value1 < 0.0) m_value1 = 0.0; + if(m_value1 > m_value2 - m_min_d) m_value1 = m_value2 - m_min_d; + return true; + + case move_value2: + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_value2 = (xp - m_xs1) / (m_xs2 - m_xs1); + } + else + { + m_value2 = (yp - m_ys1) / (m_ys2 - m_ys1); + } + if(m_value2 > 1.0) m_value2 = 1.0; + if(m_value2 < m_value1 + m_min_d) m_value2 = m_value1 + m_min_d; + return true; + + case move_slider: + dv = m_value2 - m_value1; + if(fabs(m_x2 - m_x1) > fabs(m_y2 - m_y1)) + { + m_value1 = (xp - m_xs1) / (m_xs2 - m_xs1); + } + else + { + m_value1 = (yp - m_ys1) / (m_ys2 - m_ys1); + } + m_value2 = m_value1 + dv; + if(m_value1 < 0.0) + { + dv = m_value2 - m_value1; + m_value1 = 0.0; + m_value2 = m_value1 + dv; + } + if(m_value2 > 1.0) + { + dv = m_value2 - m_value1; + m_value2 = 1.0; + m_value1 = m_value2 - dv; + } + return true; + } + + return false; + } + + + //------------------------------------------------------------------------ + bool scale_ctrl_impl::on_mouse_button_up(double, double) + { + m_move_what = move_nothing; + return false; + } + + + //------------------------------------------------------------------------ + bool scale_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { +/* + if(right || up) + { + m_value += 0.005; + if(m_value > 1.0) m_value = 1.0; + return true; + } + + if(left || down) + { + m_value -= 0.005; + if(m_value < 0.0) m_value = 0.0; + return true; + } +*/ + return false; + } + +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_slider_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_slider_ctrl.cpp new file mode 100644 index 000000000..113b7ac08 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_slider_ctrl.cpp @@ -0,0 +1,354 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include +#include "ctrl/agg_slider_ctrl.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + slider_ctrl_impl::slider_ctrl_impl(double x1, double y1, + double x2, double y2, bool flip_y) : + ctrl(x1, y1, x2, y2, flip_y), + m_border_width(1.0), + m_border_extra((y2 - y1) / 2), + m_text_thickness(1.0), + m_pdx(0.0), + m_mouse_move(false), + m_value(0.5), + m_preview_value(0.5), + m_min(0.0), + m_max(1.0), + m_num_steps(0), + m_descending(false), + m_text_poly(m_text) + { + m_label[0] = 0; + calc_box(); + } + + + //------------------------------------------------------------------------ + void slider_ctrl_impl::calc_box() + { + m_xs1 = m_x1 + m_border_width; + m_ys1 = m_y1 + m_border_width; + m_xs2 = m_x2 - m_border_width; + m_ys2 = m_y2 - m_border_width; + } + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::normalize_value(bool preview_value_flag) + { + bool ret = true; + if(m_num_steps) + { + int step = int(m_preview_value * m_num_steps + 0.5); + ret = m_value != step / double(m_num_steps); + m_value = step / double(m_num_steps); + } + else + { + m_value = m_preview_value; + } + + if(preview_value_flag) + { + m_preview_value = m_value; + } + return ret; + } + + + //------------------------------------------------------------------------ + void slider_ctrl_impl::border_width(double t, double extra) + { + m_border_width = t; + m_border_extra = extra; + calc_box(); + } + + + //------------------------------------------------------------------------ + void slider_ctrl_impl::value(double value) + { + m_preview_value = (value - m_min) / (m_max - m_min); + if(m_preview_value > 1.0) m_preview_value = 1.0; + if(m_preview_value < 0.0) m_preview_value = 0.0; + normalize_value(true); + } + + //------------------------------------------------------------------------ + void slider_ctrl_impl::label(const char* fmt) + { + m_label[0] = 0; + if(fmt) + { + unsigned len = strlen(fmt); + if(len > 63) len = 63; + memcpy(m_label, fmt, len); + m_label[len] = 0; + } + } + + //------------------------------------------------------------------------ + void slider_ctrl_impl::rewind(unsigned idx) + { + m_idx = idx; + + switch(idx) + { + default: + + case 0: // Background + m_vertex = 0; + m_vx[0] = m_x1 - m_border_extra; + m_vy[0] = m_y1 - m_border_extra; + m_vx[1] = m_x2 + m_border_extra; + m_vy[1] = m_y1 - m_border_extra; + m_vx[2] = m_x2 + m_border_extra; + m_vy[2] = m_y2 + m_border_extra; + m_vx[3] = m_x1 - m_border_extra; + m_vy[3] = m_y2 + m_border_extra; + break; + + case 1: // Triangle + m_vertex = 0; + if(m_descending) + { + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x1; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y1; + } + else + { + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y1; + } + break; + + case 2: + m_text.text(m_label); + if(m_label[0]) + { + char buf[256]; + sprintf(buf, m_label, value()); + m_text.text(buf); + } + m_text.start_point(m_x1, m_y1); + m_text.size((m_y2 - m_y1) * 1.2, m_y2 - m_y1); + m_text_poly.width(m_text_thickness); + m_text_poly.line_join(round_join); + m_text_poly.line_cap(round_cap); + m_text_poly.rewind(0); + break; + + case 3: // pointer preview + m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_preview_value, + (m_ys1 + m_ys2) / 2.0, + m_y2 - m_y1, + m_y2 - m_y1, + 32); + break; + + + case 4: // pointer + normalize_value(false); + m_ellipse.init(m_xs1 + (m_xs2 - m_xs1) * m_value, + (m_ys1 + m_ys2) / 2.0, + m_y2 - m_y1, + m_y2 - m_y1, + 32); + m_ellipse.rewind(0); + break; + + case 5: + m_storage.remove_all(); + if(m_num_steps) + { + unsigned i; + double d = (m_xs2 - m_xs1) / m_num_steps; + if(d > 0.004) d = 0.004; + for(i = 0; i < m_num_steps + 1; i++) + { + double x = m_xs1 + (m_xs2 - m_xs1) * i / m_num_steps; + m_storage.move_to(x, m_y1); + m_storage.line_to(x - d * (m_x2 - m_x1), m_y1 - m_border_extra); + m_storage.line_to(x + d * (m_x2 - m_x1), m_y1 - m_border_extra); + } + } + } + } + + + //------------------------------------------------------------------------ + unsigned slider_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 2: + cmd = m_text_poly.vertex(x, y); + break; + + case 3: + case 4: + cmd = m_ellipse.vertex(x, y); + break; + + case 5: + cmd = m_storage.vertex(x, y); + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + + return cmd; + } + + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2; + } + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + + double xp = m_xs1 + (m_xs2 - m_xs1) * m_value; + double yp = (m_ys1 + m_ys2) / 2.0; + + if(calc_distance(x, y, xp, yp) <= m_y2 - m_y1) + { + m_pdx = xp - x; + m_mouse_move = true; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + if(!button_flag) + { + on_mouse_button_up(x, y); + return false; + } + + if(m_mouse_move) + { + double xp = x + m_pdx; + m_preview_value = (xp - m_xs1) / (m_xs2 - m_xs1); + if(m_preview_value < 0.0) m_preview_value = 0.0; + if(m_preview_value > 1.0) m_preview_value = 1.0; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::on_mouse_button_up(double, double) + { + m_mouse_move = false; + normalize_value(true); + return true; + } + + + //------------------------------------------------------------------------ + bool slider_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + double d = 0.005; + if(m_num_steps) + { + d = 1.0 / m_num_steps; + } + + if(right || up) + { + m_preview_value += d; + if(m_preview_value > 1.0) m_preview_value = 1.0; + normalize_value(true); + return true; + } + + if(left || down) + { + m_preview_value -= d; + if(m_preview_value < 0.0) m_preview_value = 0.0; + normalize_value(true); + return true; + } + return false; + } + +} + diff --git a/desmume/src/windows/agg/src/ctrl/agg_spline_ctrl.cpp b/desmume/src/windows/agg/src/ctrl/agg_spline_ctrl.cpp new file mode 100644 index 000000000..6716607c6 --- /dev/null +++ b/desmume/src/windows/agg/src/ctrl/agg_spline_ctrl.cpp @@ -0,0 +1,412 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include "ctrl/agg_spline_ctrl.h" + + +namespace agg +{ + + //------------------------------------------------------------------------ + spline_ctrl_impl::spline_ctrl_impl(double x1, double y1, double x2, double y2, + unsigned num_pnt, bool flip_y) : + ctrl(x1, y1, x2, y2, flip_y), + m_num_pnt(num_pnt), + m_border_width(1.0), + m_border_extra(0.0), + m_curve_width(1.0), + m_point_size(3.0), + m_curve_poly(m_curve_pnt), + m_idx(0), + m_vertex(0), + m_active_pnt(-1), + m_move_pnt(-1), + m_pdx(0.0), + m_pdy(0.0) + { + if(m_num_pnt < 4) m_num_pnt = 4; + if(m_num_pnt > 32) m_num_pnt = 32; + + unsigned i; + for(i = 0; i < m_num_pnt; i++) + { + m_xp[i] = double(i) / double(m_num_pnt - 1); + m_yp[i] = 0.5; + } + calc_spline_box(); + update_spline(); + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::border_width(double t, double extra) + { + m_border_width = t; + m_border_extra = extra; + calc_spline_box(); + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::calc_spline_box() + { + m_xs1 = m_x1 + m_border_width; + m_ys1 = m_y1 + m_border_width; + m_xs2 = m_x2 - m_border_width; + m_ys2 = m_y2 - m_border_width; + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::update_spline() + { + int i; + m_spline.init(m_num_pnt, m_xp, m_yp); + for(i = 0; i < 256; i++) + { + m_spline_values[i] = m_spline.get(double(i) / 255.0); + if(m_spline_values[i] < 0.0) m_spline_values[i] = 0.0; + if(m_spline_values[i] > 1.0) m_spline_values[i] = 1.0; + m_spline_values8[i] = (int8u)(m_spline_values[i] * 255.0); + } + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::calc_curve() + { + int i; + m_curve_pnt.remove_all(); + m_curve_pnt.move_to(m_xs1, m_ys1 + (m_ys2 - m_ys1) * m_spline_values[0]); + for(i = 1; i < 256; i++) + { + m_curve_pnt.line_to(m_xs1 + (m_xs2 - m_xs1) * double(i) / 255.0, + m_ys1 + (m_ys2 - m_ys1) * m_spline_values[i]); + } + } + + + //------------------------------------------------------------------------ + double spline_ctrl_impl::calc_xp(unsigned idx) + { + return m_xs1 + (m_xs2 - m_xs1) * m_xp[idx]; + } + + + //------------------------------------------------------------------------ + double spline_ctrl_impl::calc_yp(unsigned idx) + { + return m_ys1 + (m_ys2 - m_ys1) * m_yp[idx]; + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::set_xp(unsigned idx, double val) + { + if(val < 0.0) val = 0.0; + if(val > 1.0) val = 1.0; + + if(idx == 0) + { + val = 0.0; + } + else if(idx == m_num_pnt - 1) + { + val = 1.0; + } + else + { + if(val < m_xp[idx - 1] + 0.001) val = m_xp[idx - 1] + 0.001; + if(val > m_xp[idx + 1] - 0.001) val = m_xp[idx + 1] - 0.001; + } + m_xp[idx] = val; + } + + //------------------------------------------------------------------------ + void spline_ctrl_impl::set_yp(unsigned idx, double val) + { + if(val < 0.0) val = 0.0; + if(val > 1.0) val = 1.0; + m_yp[idx] = val; + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::point(unsigned idx, double x, double y) + { + if(idx < m_num_pnt) + { + set_xp(idx, x); + set_yp(idx, y); + } + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::value(unsigned idx, double y) + { + if(idx < m_num_pnt) + { + set_yp(idx, y); + } + } + + //------------------------------------------------------------------------ + double spline_ctrl_impl::value(double x) const + { + x = m_spline.get(x); + if(x < 0.0) x = 0.0; + if(x > 1.0) x = 1.0; + return x; + } + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::rewind(unsigned idx) + { + unsigned i; + + m_idx = idx; + + switch(idx) + { + default: + + case 0: // Background + m_vertex = 0; + m_vx[0] = m_x1 - m_border_extra; + m_vy[0] = m_y1 - m_border_extra; + m_vx[1] = m_x2 + m_border_extra; + m_vy[1] = m_y1 - m_border_extra; + m_vx[2] = m_x2 + m_border_extra; + m_vy[2] = m_y2 + m_border_extra; + m_vx[3] = m_x1 - m_border_extra; + m_vy[3] = m_y2 + m_border_extra; + break; + + case 1: // Border + m_vertex = 0; + m_vx[0] = m_x1; + m_vy[0] = m_y1; + m_vx[1] = m_x2; + m_vy[1] = m_y1; + m_vx[2] = m_x2; + m_vy[2] = m_y2; + m_vx[3] = m_x1; + m_vy[3] = m_y2; + m_vx[4] = m_x1 + m_border_width; + m_vy[4] = m_y1 + m_border_width; + m_vx[5] = m_x1 + m_border_width; + m_vy[5] = m_y2 - m_border_width; + m_vx[6] = m_x2 - m_border_width; + m_vy[6] = m_y2 - m_border_width; + m_vx[7] = m_x2 - m_border_width; + m_vy[7] = m_y1 + m_border_width; + break; + + case 2: // Curve + calc_curve(); + m_curve_poly.width(m_curve_width); + m_curve_poly.rewind(0); + break; + + + case 3: // Inactive points + m_curve_pnt.remove_all(); + for(i = 0; i < m_num_pnt; i++) + { + if(int(i) != m_active_pnt) + { + m_ellipse.init(calc_xp(i), calc_yp(i), + m_point_size, m_point_size, 32); + m_curve_pnt.concat_path(m_ellipse); + } + } + m_curve_poly.rewind(0); + break; + + + case 4: // Active point + m_curve_pnt.remove_all(); + if(m_active_pnt >= 0) + { + m_ellipse.init(calc_xp(m_active_pnt), calc_yp(m_active_pnt), + m_point_size, m_point_size, 32); + + m_curve_pnt.concat_path(m_ellipse); + } + m_curve_poly.rewind(0); + break; + + } + } + + + //------------------------------------------------------------------------ + unsigned spline_ctrl_impl::vertex(double* x, double* y) + { + unsigned cmd = path_cmd_line_to; + switch(m_idx) + { + case 0: + if(m_vertex == 0) cmd = path_cmd_move_to; + if(m_vertex >= 4) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 1: + if(m_vertex == 0 || m_vertex == 4) cmd = path_cmd_move_to; + if(m_vertex >= 8) cmd = path_cmd_stop; + *x = m_vx[m_vertex]; + *y = m_vy[m_vertex]; + m_vertex++; + break; + + case 2: + cmd = m_curve_poly.vertex(x, y); + break; + + case 3: + case 4: + cmd = m_curve_pnt.vertex(x, y); + break; + + default: + cmd = path_cmd_stop; + break; + } + + if(!is_stop(cmd)) + { + transform_xy(x, y); + } + + return cmd; + } + + + + //------------------------------------------------------------------------ + void spline_ctrl_impl::active_point(int i) + { + m_active_pnt = i; + } + + + //------------------------------------------------------------------------ + bool spline_ctrl_impl::in_rect(double x, double y) const + { + inverse_transform_xy(&x, &y); + return x >= m_x1 && x <= m_x2 && y >= m_y1 && y <= m_y2; + } + + + //------------------------------------------------------------------------ + bool spline_ctrl_impl::on_mouse_button_down(double x, double y) + { + inverse_transform_xy(&x, &y); + unsigned i; + for(i = 0; i < m_num_pnt; i++) + { + double xp = calc_xp(i); + double yp = calc_yp(i); + if(calc_distance(x, y, xp, yp) <= m_point_size + 1) + { + m_pdx = xp - x; + m_pdy = yp - y; + m_active_pnt = m_move_pnt = int(i); + return true; + } + } + return false; + } + + + //------------------------------------------------------------------------ + bool spline_ctrl_impl::on_mouse_button_up(double, double) + { + if(m_move_pnt >= 0) + { + m_move_pnt = -1; + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool spline_ctrl_impl::on_mouse_move(double x, double y, bool button_flag) + { + inverse_transform_xy(&x, &y); + if(!button_flag) + { + return on_mouse_button_up(x, y); + } + + if(m_move_pnt >= 0) + { + double xp = x + m_pdx; + double yp = y + m_pdy; + + set_xp(m_move_pnt, (xp - m_xs1) / (m_xs2 - m_xs1)); + set_yp(m_move_pnt, (yp - m_ys1) / (m_ys2 - m_ys1)); + + update_spline(); + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + bool spline_ctrl_impl::on_arrow_keys(bool left, bool right, bool down, bool up) + { + double kx = 0.0; + double ky = 0.0; + bool ret = false; + if(m_active_pnt >= 0) + { + kx = m_xp[m_active_pnt]; + ky = m_yp[m_active_pnt]; + if(left) { kx -= 0.001; ret = true; } + if(right) { kx += 0.001; ret = true; } + if(down) { ky -= 0.001; ret = true; } + if(up) { ky += 0.001; ret = true; } + } + if(ret) + { + set_xp(m_active_pnt, kx); + set_yp(m_active_pnt, ky); + update_spline(); + } + return ret; + } + + + + +} + diff --git a/desmume/src/windows/agg/src/install b/desmume/src/windows/agg/src/install new file mode 100644 index 000000000..e69de29bb diff --git a/desmume/src/windows/agg/src/news b/desmume/src/windows/agg/src/news new file mode 100644 index 000000000..e69de29bb diff --git a/desmume/src/windows/agg/src/platform/win32/agg_platform_support.cpp b/desmume/src/windows/agg/src/platform/win32/agg_platform_support.cpp new file mode 100644 index 000000000..ffcc6d197 --- /dev/null +++ b/desmume/src/windows/agg/src/platform/win32/agg_platform_support.cpp @@ -0,0 +1,1478 @@ +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- + +#include +#include +#include "platform/agg_platform_support.h" +#include "platform/win32/agg_win32_bmp.h" +#include "util/agg_color_conv_rgb8.h" +#include "util/agg_color_conv_rgb16.h" + + +namespace agg +{ + + //------------------------------------------------------------------------ + HINSTANCE g_windows_instance = 0; + int g_windows_cmd_show = 0; + + + //------------------------------------------------------------------------ + class platform_specific + { + public: + platform_specific(pix_format_e format, bool flip_y); + + void create_pmap(unsigned width, unsigned height, + rendering_buffer* wnd); + + void display_pmap(HDC dc, const rendering_buffer* src); + bool load_pmap(const char* fn, unsigned idx, + rendering_buffer* dst); + + bool save_pmap(const char* fn, unsigned idx, + const rendering_buffer* src); + + unsigned translate(unsigned keycode); + + pix_format_e m_format; + pix_format_e m_sys_format; + bool m_flip_y; + unsigned m_bpp; + unsigned m_sys_bpp; + HWND m_hwnd; + pixel_map m_pmap_window; + pixel_map m_pmap_img[platform_support::max_images]; + unsigned m_keymap[256]; + unsigned m_last_translated_key; + int m_cur_x; + int m_cur_y; + unsigned m_input_flags; + bool m_redraw_flag; + HDC m_current_dc; + LARGE_INTEGER m_sw_freq; + LARGE_INTEGER m_sw_start; + }; + + + //------------------------------------------------------------------------ + platform_specific::platform_specific(pix_format_e format, bool flip_y) : + m_format(format), + m_sys_format(pix_format_undefined), + m_flip_y(flip_y), + m_bpp(0), + m_sys_bpp(0), + m_hwnd(0), + m_last_translated_key(0), + m_cur_x(0), + m_cur_y(0), + m_input_flags(0), + m_redraw_flag(true), + m_current_dc(0) + { + memset(m_keymap, 0, sizeof(m_keymap)); + + m_keymap[VK_PAUSE] = key_pause; + m_keymap[VK_CLEAR] = key_clear; + + m_keymap[VK_NUMPAD0] = key_kp0; + m_keymap[VK_NUMPAD1] = key_kp1; + m_keymap[VK_NUMPAD2] = key_kp2; + m_keymap[VK_NUMPAD3] = key_kp3; + m_keymap[VK_NUMPAD4] = key_kp4; + m_keymap[VK_NUMPAD5] = key_kp5; + m_keymap[VK_NUMPAD6] = key_kp6; + m_keymap[VK_NUMPAD7] = key_kp7; + m_keymap[VK_NUMPAD8] = key_kp8; + m_keymap[VK_NUMPAD9] = key_kp9; + m_keymap[VK_DECIMAL] = key_kp_period; + m_keymap[VK_DIVIDE] = key_kp_divide; + m_keymap[VK_MULTIPLY] = key_kp_multiply; + m_keymap[VK_SUBTRACT] = key_kp_minus; + m_keymap[VK_ADD] = key_kp_plus; + + m_keymap[VK_UP] = key_up; + m_keymap[VK_DOWN] = key_down; + m_keymap[VK_RIGHT] = key_right; + m_keymap[VK_LEFT] = key_left; + m_keymap[VK_INSERT] = key_insert; + m_keymap[VK_DELETE] = key_delete; + m_keymap[VK_HOME] = key_home; + m_keymap[VK_END] = key_end; + m_keymap[VK_PRIOR] = key_page_up; + m_keymap[VK_NEXT] = key_page_down; + + m_keymap[VK_F1] = key_f1; + m_keymap[VK_F2] = key_f2; + m_keymap[VK_F3] = key_f3; + m_keymap[VK_F4] = key_f4; + m_keymap[VK_F5] = key_f5; + m_keymap[VK_F6] = key_f6; + m_keymap[VK_F7] = key_f7; + m_keymap[VK_F8] = key_f8; + m_keymap[VK_F9] = key_f9; + m_keymap[VK_F10] = key_f10; + m_keymap[VK_F11] = key_f11; + m_keymap[VK_F12] = key_f12; + m_keymap[VK_F13] = key_f13; + m_keymap[VK_F14] = key_f14; + m_keymap[VK_F15] = key_f15; + + m_keymap[VK_NUMLOCK] = key_numlock; + m_keymap[VK_CAPITAL] = key_capslock; + m_keymap[VK_SCROLL] = key_scrollock; + + + switch(m_format) + { + case pix_format_bw: + m_sys_format = pix_format_bw; + m_bpp = 1; + m_sys_bpp = 1; + break; + + case pix_format_gray8: + m_sys_format = pix_format_gray8; + m_bpp = 8; + m_sys_bpp = 8; + break; + + case pix_format_gray16: + m_sys_format = pix_format_gray8; + m_bpp = 16; + m_sys_bpp = 8; + break; + + case pix_format_rgb565: + case pix_format_rgb555: + m_sys_format = pix_format_rgb555; + m_bpp = 16; + m_sys_bpp = 16; + break; + + case pix_format_rgbAAA: + case pix_format_bgrAAA: + case pix_format_rgbBBA: + case pix_format_bgrABB: + m_sys_format = pix_format_bgr24; + m_bpp = 32; + m_sys_bpp = 24; + break; + + case pix_format_rgb24: + case pix_format_bgr24: + m_sys_format = pix_format_bgr24; + m_bpp = 24; + m_sys_bpp = 24; + break; + + case pix_format_rgb48: + case pix_format_bgr48: + m_sys_format = pix_format_bgr24; + m_bpp = 48; + m_sys_bpp = 24; + break; + + case pix_format_bgra32: + case pix_format_abgr32: + case pix_format_argb32: + case pix_format_rgba32: + m_sys_format = pix_format_bgra32; + m_bpp = 32; + m_sys_bpp = 32; + break; + + case pix_format_bgra64: + case pix_format_abgr64: + case pix_format_argb64: + case pix_format_rgba64: + m_sys_format = pix_format_bgra32; + m_bpp = 64; + m_sys_bpp = 32; + break; + } + ::QueryPerformanceFrequency(&m_sw_freq); + ::QueryPerformanceCounter(&m_sw_start); + } + + + //------------------------------------------------------------------------ + void platform_specific::create_pmap(unsigned width, + unsigned height, + rendering_buffer* wnd) + { + m_pmap_window.create(width, height, org_e(m_bpp)); + wnd->attach(m_pmap_window.buf(), + m_pmap_window.width(), + m_pmap_window.height(), + m_flip_y ? + m_pmap_window.stride() : + -m_pmap_window.stride()); + } + + + //------------------------------------------------------------------------ + static void convert_pmap(rendering_buffer* dst, + const rendering_buffer* src, + pix_format_e format) + { + switch(format) + { + case pix_format_gray8: + break; + + case pix_format_gray16: + color_conv(dst, src, color_conv_gray16_to_gray8()); + break; + + case pix_format_rgb565: + color_conv(dst, src, color_conv_rgb565_to_rgb555()); + break; + + case pix_format_rgbAAA: + color_conv(dst, src, color_conv_rgbAAA_to_bgr24()); + break; + + case pix_format_bgrAAA: + color_conv(dst, src, color_conv_bgrAAA_to_bgr24()); + break; + + case pix_format_rgbBBA: + color_conv(dst, src, color_conv_rgbBBA_to_bgr24()); + break; + + case pix_format_bgrABB: + color_conv(dst, src, color_conv_bgrABB_to_bgr24()); + break; + + case pix_format_rgb24: + color_conv(dst, src, color_conv_rgb24_to_bgr24()); + break; + + case pix_format_rgb48: + color_conv(dst, src, color_conv_rgb48_to_bgr24()); + break; + + case pix_format_bgr48: + color_conv(dst, src, color_conv_bgr48_to_bgr24()); + break; + + case pix_format_abgr32: + color_conv(dst, src, color_conv_abgr32_to_bgra32()); + break; + + case pix_format_argb32: + color_conv(dst, src, color_conv_argb32_to_bgra32()); + break; + + case pix_format_rgba32: + color_conv(dst, src, color_conv_rgba32_to_bgra32()); + break; + + case pix_format_bgra64: + color_conv(dst, src, color_conv_bgra64_to_bgra32()); + break; + + case pix_format_abgr64: + color_conv(dst, src, color_conv_abgr64_to_bgra32()); + break; + + case pix_format_argb64: + color_conv(dst, src, color_conv_argb64_to_bgra32()); + break; + + case pix_format_rgba64: + color_conv(dst, src, color_conv_rgba64_to_bgra32()); + break; + } + } + + + //------------------------------------------------------------------------ + void platform_specific::display_pmap(HDC dc, const rendering_buffer* src) + { + if(m_sys_format == m_format) + { + m_pmap_window.draw(dc); + } + else + { + pixel_map pmap_tmp; + pmap_tmp.create(m_pmap_window.width(), + m_pmap_window.height(), + org_e(m_sys_bpp)); + + rendering_buffer rbuf_tmp; + rbuf_tmp.attach(pmap_tmp.buf(), + pmap_tmp.width(), + pmap_tmp.height(), + m_flip_y ? + pmap_tmp.stride() : + -pmap_tmp.stride()); + + convert_pmap(&rbuf_tmp, src, m_format); + pmap_tmp.draw(dc); + } + } + + + + //------------------------------------------------------------------------ + bool platform_specific::save_pmap(const char* fn, unsigned idx, + const rendering_buffer* src) + { + if(m_sys_format == m_format) + { + return m_pmap_img[idx].save_as_bmp(fn); + } + + pixel_map pmap_tmp; + pmap_tmp.create(m_pmap_img[idx].width(), + m_pmap_img[idx].height(), + org_e(m_sys_bpp)); + + rendering_buffer rbuf_tmp; + rbuf_tmp.attach(pmap_tmp.buf(), + pmap_tmp.width(), + pmap_tmp.height(), + m_flip_y ? + pmap_tmp.stride() : + -pmap_tmp.stride()); + + convert_pmap(&rbuf_tmp, src, m_format); + return pmap_tmp.save_as_bmp(fn); + } + + + + //------------------------------------------------------------------------ + bool platform_specific::load_pmap(const char* fn, unsigned idx, + rendering_buffer* dst) + { + pixel_map pmap_tmp; + if(!pmap_tmp.load_from_bmp(fn)) return false; + + rendering_buffer rbuf_tmp; + rbuf_tmp.attach(pmap_tmp.buf(), + pmap_tmp.width(), + pmap_tmp.height(), + m_flip_y ? + pmap_tmp.stride() : + -pmap_tmp.stride()); + + m_pmap_img[idx].create(pmap_tmp.width(), + pmap_tmp.height(), + org_e(m_bpp), + 0); + + dst->attach(m_pmap_img[idx].buf(), + m_pmap_img[idx].width(), + m_pmap_img[idx].height(), + m_flip_y ? + m_pmap_img[idx].stride() : + -m_pmap_img[idx].stride()); + + switch(m_format) + { + case pix_format_gray8: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_gray8()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_gray8()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray8()); break; + } + break; + + case pix_format_gray16: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_gray16()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_gray16()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_gray16()); break; + } + break; + + case pix_format_rgb555: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb555()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb555()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb555()); break; + } + break; + + case pix_format_rgb565: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb565()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb565()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb565()); break; + } + break; + + case pix_format_rgb24: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb24()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb24()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb24()); break; + } + break; + + case pix_format_bgr24: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgr24()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgr24()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr24()); break; + } + break; + + case pix_format_rgb48: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgb48()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgb48()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgb48()); break; + } + break; + + case pix_format_bgr48: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgr48()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgr48()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgr48()); break; + } + break; + + case pix_format_abgr32: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_abgr32()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_abgr32()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr32()); break; + } + break; + + case pix_format_argb32: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_argb32()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_argb32()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb32()); break; + } + break; + + case pix_format_bgra32: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgra32()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgra32()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra32()); break; + } + break; + + case pix_format_rgba32: + switch(pmap_tmp.bpp()) + { + case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgba32()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgba32()); break; + case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba32()); break; + } + break; + + case pix_format_abgr64: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_abgr64()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_abgr64()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_abgr64()); break; + } + break; + + case pix_format_argb64: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_argb64()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_argb64()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_argb64()); break; + } + break; + + case pix_format_bgra64: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_bgra64()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_bgra64()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_bgra64()); break; + } + break; + + case pix_format_rgba64: + switch(pmap_tmp.bpp()) + { + //case 16: color_conv(dst, &rbuf_tmp, color_conv_rgb555_to_rgba64()); break; + case 24: color_conv(dst, &rbuf_tmp, color_conv_bgr24_to_rgba64()); break; + //case 32: color_conv(dst, &rbuf_tmp, color_conv_bgra32_to_rgba64()); break; + } + break; + + } + + return true; + } + + + + + + + + + //------------------------------------------------------------------------ + unsigned platform_specific::translate(unsigned keycode) + { + return m_last_translated_key = (keycode > 255) ? 0 : m_keymap[keycode]; + } + + + + //------------------------------------------------------------------------ + platform_support::platform_support(pix_format_e format, bool flip_y) : + m_specific(new platform_specific(format, flip_y)), + m_format(format), + m_bpp(m_specific->m_bpp), + m_window_flags(0), + m_wait_mode(true), + m_flip_y(flip_y), + m_initial_width(10), + m_initial_height(10) + { + strcpy(m_caption, "Anti-Grain Geometry Application"); + } + + + //------------------------------------------------------------------------ + platform_support::~platform_support() + { + delete m_specific; + } + + + + //------------------------------------------------------------------------ + void platform_support::caption(const char* cap) + { + strcpy(m_caption, cap); + if(m_specific->m_hwnd) + { + SetWindowText(m_specific->m_hwnd, m_caption); + } + } + + //------------------------------------------------------------------------ + void platform_support::start_timer() + { + ::QueryPerformanceCounter(&(m_specific->m_sw_start)); + } + + //------------------------------------------------------------------------ + double platform_support::elapsed_time() const + { + LARGE_INTEGER stop; + ::QueryPerformanceCounter(&stop); + return double(stop.QuadPart - + m_specific->m_sw_start.QuadPart) * 1000.0 / + double(m_specific->m_sw_freq.QuadPart); + } + + + + //------------------------------------------------------------------------ + static unsigned get_key_flags(int wflags) + { + unsigned flags = 0; + if(wflags & MK_LBUTTON) flags |= mouse_left; + if(wflags & MK_RBUTTON) flags |= mouse_right; + if(wflags & MK_SHIFT) flags |= kbd_shift; + if(wflags & MK_CONTROL) flags |= kbd_ctrl; + return flags; + } + + + void* platform_support::raw_display_handler() + { + return m_specific->m_current_dc; + } + + + //------------------------------------------------------------------------ + LRESULT CALLBACK window_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + PAINTSTRUCT ps; + HDC paintDC; + + + void* user_data = reinterpret_cast(::GetWindowLong(hWnd, GWL_USERDATA)); + platform_support* app = 0; + + if(user_data) + { + app = reinterpret_cast(user_data); + } + + if(app == 0) + { + if(msg == WM_DESTROY) + { + ::PostQuitMessage(0); + return 0; + } + return ::DefWindowProc(hWnd, msg, wParam, lParam); + } + + HDC dc = ::GetDC(app->m_specific->m_hwnd); + app->m_specific->m_current_dc = dc; + LRESULT ret = 0; + + switch(msg) + { + //-------------------------------------------------------------------- + case WM_CREATE: + break; + + //-------------------------------------------------------------------- + case WM_SIZE: + app->m_specific->create_pmap(LOWORD(lParam), + HIWORD(lParam), + &app->rbuf_window()); + + app->trans_affine_resizing(LOWORD(lParam), HIWORD(lParam)); + app->on_resize(LOWORD(lParam), HIWORD(lParam)); + app->force_redraw(); + break; + + //-------------------------------------------------------------------- + case WM_ERASEBKGND: + break; + + //-------------------------------------------------------------------- + case WM_LBUTTONDOWN: + ::SetCapture(app->m_specific->m_hwnd); + app->m_specific->m_cur_x = int16(LOWORD(lParam)); + if(app->flip_y()) + { + app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam)); + } + else + { + app->m_specific->m_cur_y = int16(HIWORD(lParam)); + } + app->m_specific->m_input_flags = mouse_left | get_key_flags(wParam); + + app->m_ctrls.set_cur(app->m_specific->m_cur_x, + app->m_specific->m_cur_y); + if(app->m_ctrls.on_mouse_button_down(app->m_specific->m_cur_x, + app->m_specific->m_cur_y)) + { + app->on_ctrl_change(); + app->force_redraw(); + } + else + { + if(app->m_ctrls.in_rect(app->m_specific->m_cur_x, + app->m_specific->m_cur_y)) + { + if(app->m_ctrls.set_cur(app->m_specific->m_cur_x, + app->m_specific->m_cur_y)) + { + app->on_ctrl_change(); + app->force_redraw(); + } + } + else + { + app->on_mouse_button_down(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_input_flags); + } + } +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + //-------------------------------------------------------------------- + case WM_LBUTTONUP: + ::ReleaseCapture(); + app->m_specific->m_cur_x = int16(LOWORD(lParam)); + if(app->flip_y()) + { + app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam)); + } + else + { + app->m_specific->m_cur_y = int16(HIWORD(lParam)); + } + app->m_specific->m_input_flags = mouse_left | get_key_flags(wParam); + + if(app->m_ctrls.on_mouse_button_up(app->m_specific->m_cur_x, + app->m_specific->m_cur_y)) + { + app->on_ctrl_change(); + app->force_redraw(); + } + app->on_mouse_button_up(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_input_flags); +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + + //-------------------------------------------------------------------- + case WM_RBUTTONDOWN: + ::SetCapture(app->m_specific->m_hwnd); + app->m_specific->m_cur_x = int16(LOWORD(lParam)); + if(app->flip_y()) + { + app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam)); + } + else + { + app->m_specific->m_cur_y = int16(HIWORD(lParam)); + } + app->m_specific->m_input_flags = mouse_right | get_key_flags(wParam); + app->on_mouse_button_down(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_input_flags); +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + //-------------------------------------------------------------------- + case WM_RBUTTONUP: + ::ReleaseCapture(); + app->m_specific->m_cur_x = int16(LOWORD(lParam)); + if(app->flip_y()) + { + app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam)); + } + else + { + app->m_specific->m_cur_y = int16(HIWORD(lParam)); + } + app->m_specific->m_input_flags = mouse_right | get_key_flags(wParam); + app->on_mouse_button_up(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_input_flags); +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + //-------------------------------------------------------------------- + case WM_MOUSEMOVE: + app->m_specific->m_cur_x = int16(LOWORD(lParam)); + if(app->flip_y()) + { + app->m_specific->m_cur_y = app->rbuf_window().height() - int16(HIWORD(lParam)); + } + else + { + app->m_specific->m_cur_y = int16(HIWORD(lParam)); + } + app->m_specific->m_input_flags = get_key_flags(wParam); + + + if(app->m_ctrls.on_mouse_move( + app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + (app->m_specific->m_input_flags & mouse_left) != 0)) + { + app->on_ctrl_change(); + app->force_redraw(); + } + else + { + if(!app->m_ctrls.in_rect(app->m_specific->m_cur_x, + app->m_specific->m_cur_y)) + { + app->on_mouse_move(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_input_flags); + } + } +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + //-------------------------------------------------------------------- + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + app->m_specific->m_last_translated_key = 0; + switch(wParam) + { + case VK_CONTROL: + app->m_specific->m_input_flags |= kbd_ctrl; + break; + + case VK_SHIFT: + app->m_specific->m_input_flags |= kbd_shift; + break; + + default: + app->m_specific->translate(wParam); + break; + } + + if(app->m_specific->m_last_translated_key) + { + bool left = false; + bool up = false; + bool right = false; + bool down = false; + + switch(app->m_specific->m_last_translated_key) + { + case key_left: + left = true; + break; + + case key_up: + up = true; + break; + + case key_right: + right = true; + break; + + case key_down: + down = true; + break; + + case key_f2: + app->copy_window_to_img(agg::platform_support::max_images - 1); + app->save_img(agg::platform_support::max_images - 1, "screenshot"); + break; + } + + if(app->window_flags() & window_process_all_keys) + { + app->on_key(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_last_translated_key, + app->m_specific->m_input_flags); + } + else + { + if(app->m_ctrls.on_arrow_keys(left, right, down, up)) + { + app->on_ctrl_change(); + app->force_redraw(); + } + else + { + app->on_key(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + app->m_specific->m_last_translated_key, + app->m_specific->m_input_flags); + } + } + } +/* + if(!app->wait_mode()) + { + app->on_idle(); + } +*/ + break; + + //-------------------------------------------------------------------- + case WM_SYSKEYUP: + case WM_KEYUP: + app->m_specific->m_last_translated_key = 0; + switch(wParam) + { + case VK_CONTROL: + app->m_specific->m_input_flags &= ~kbd_ctrl; + break; + + case VK_SHIFT: + app->m_specific->m_input_flags &= ~kbd_shift; + break; + } + break; + + //-------------------------------------------------------------------- + case WM_CHAR: + case WM_SYSCHAR: + if(app->m_specific->m_last_translated_key == 0) + { + app->on_key(app->m_specific->m_cur_x, + app->m_specific->m_cur_y, + wParam, + app->m_specific->m_input_flags); + } + break; + + //-------------------------------------------------------------------- + case WM_PAINT: + paintDC = ::BeginPaint(hWnd, &ps); + app->m_specific->m_current_dc = paintDC; + if(app->m_specific->m_redraw_flag) + { + app->on_draw(); + app->m_specific->m_redraw_flag = false; + } + app->m_specific->display_pmap(paintDC, &app->rbuf_window()); + app->on_post_draw(paintDC); + app->m_specific->m_current_dc = 0; + ::EndPaint(hWnd, &ps); + break; + + //-------------------------------------------------------------------- + case WM_COMMAND: + break; + + //-------------------------------------------------------------------- + case WM_DESTROY: + ::PostQuitMessage(0); + break; + + //-------------------------------------------------------------------- + default: + ret = ::DefWindowProc(hWnd, msg, wParam, lParam); + break; + } + app->m_specific->m_current_dc = 0; + ::ReleaseDC(app->m_specific->m_hwnd, dc); + return ret; + } + + + //------------------------------------------------------------------------ + void platform_support::message(const char* msg) + { + ::MessageBox(m_specific->m_hwnd, msg, "AGG Message", MB_OK); + } + + + //------------------------------------------------------------------------ + bool platform_support::init(unsigned width, unsigned height, unsigned flags) + { + if(m_specific->m_sys_format == pix_format_undefined) + { + return false; + } + + m_window_flags = flags; + + int wflags = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + + WNDCLASS wc; + wc.lpszClassName = "AGGAppClass"; + wc.lpfnWndProc = window_proc; + wc.style = wflags; + wc.hInstance = g_windows_instance; + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = "AGGAppMenu"; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + ::RegisterClass(&wc); + + wflags = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + + if(m_window_flags & window_resize) + { + wflags |= WS_THICKFRAME | WS_MAXIMIZEBOX; + } + + m_specific->m_hwnd = ::CreateWindow("AGGAppClass", + m_caption, + wflags, + 100, + 100, + width, + height, + 0, + 0, + g_windows_instance, + 0); + + if(m_specific->m_hwnd == 0) + { + return false; + } + + + RECT rct; + ::GetClientRect(m_specific->m_hwnd, &rct); + + ::MoveWindow(m_specific->m_hwnd, // handle to window + 100, // horizontal position + 100, // vertical position + width + (width - (rct.right - rct.left)), + height + (height - (rct.bottom - rct.top)), + FALSE); + + ::SetWindowLong(m_specific->m_hwnd, GWL_USERDATA, (LONG)this); + m_specific->create_pmap(width, height, &m_rbuf_window); + m_initial_width = width; + m_initial_height = height; + on_init(); + m_specific->m_redraw_flag = true; + ::ShowWindow(m_specific->m_hwnd, g_windows_cmd_show); + return true; + } + + + + //------------------------------------------------------------------------ + int platform_support::run() + { + MSG msg; + + for(;;) + { + if(m_wait_mode) + { + if(!::GetMessage(&msg, 0, 0, 0)) + { + break; + } + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + else + { + if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + ::TranslateMessage(&msg); + if(msg.message == WM_QUIT) + { + break; + } + ::DispatchMessage(&msg); + } + else + { + on_idle(); + } + } + } + return (int)msg.wParam; + } + + + //------------------------------------------------------------------------ + const char* platform_support::img_ext() const { return ".bmp"; } + + + //------------------------------------------------------------------------ + const char* platform_support::full_file_name(const char* file_name) + { + return file_name; + } + + //------------------------------------------------------------------------ + bool platform_support::load_img(unsigned idx, const char* file) + { + if(idx < max_images) + { + char fn[1024]; + strcpy(fn, file); + int len = strlen(fn); + if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0) + { + strcat(fn, ".bmp"); + } + return m_specific->load_pmap(fn, idx, &m_rbuf_img[idx]); + } + return true; + } + + + + //------------------------------------------------------------------------ + bool platform_support::save_img(unsigned idx, const char* file) + { + if(idx < max_images) + { + char fn[1024]; + strcpy(fn, file); + int len = strlen(fn); + if(len < 4 || stricmp(fn + len - 4, ".BMP") != 0) + { + strcat(fn, ".bmp"); + } + return m_specific->save_pmap(fn, idx, &m_rbuf_img[idx]); + } + return true; + } + + + + //------------------------------------------------------------------------ + bool platform_support::create_img(unsigned idx, unsigned width, unsigned height) + { + if(idx < max_images) + { + if(width == 0) width = m_specific->m_pmap_window.width(); + if(height == 0) height = m_specific->m_pmap_window.height(); + m_specific->m_pmap_img[idx].create(width, height, org_e(m_specific->m_bpp)); + m_rbuf_img[idx].attach(m_specific->m_pmap_img[idx].buf(), + m_specific->m_pmap_img[idx].width(), + m_specific->m_pmap_img[idx].height(), + m_flip_y ? + m_specific->m_pmap_img[idx].stride() : + -m_specific->m_pmap_img[idx].stride()); + return true; + } + return false; + } + + + //------------------------------------------------------------------------ + void platform_support::force_redraw() + { + m_specific->m_redraw_flag = true; + ::InvalidateRect(m_specific->m_hwnd, 0, FALSE); + } + + + + //------------------------------------------------------------------------ + void platform_support::update_window() + { + HDC dc = ::GetDC(m_specific->m_hwnd); + m_specific->display_pmap(dc, &m_rbuf_window); + ::ReleaseDC(m_specific->m_hwnd, dc); + } + + + //------------------------------------------------------------------------ + void platform_support::on_init() {} + void platform_support::on_resize(int sx, int sy) {} + void platform_support::on_idle() {} + void platform_support::on_mouse_move(int x, int y, unsigned flags) {} + void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {} + void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {} + void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {} + void platform_support::on_ctrl_change() {} + void platform_support::on_draw() {} + void platform_support::on_post_draw(void* raw_handler) {} +} + + + + +namespace agg +{ + // That's ridiculous. I have to parse the command line by myself + // because Windows doesn't provide a method of getting the command + // line arguments in a form of argc, argv. Of course, there's + // CommandLineToArgv() but first, it returns Unicode that I don't + // need to deal with, but most of all, it's not compatible with Win98. + //----------------------------------------------------------------------- + class tokenizer + { + public: + enum sep_flag + { + single, + multiple, + whole_str + }; + + struct token + { + const char* ptr; + unsigned len; + }; + + public: + tokenizer(const char* sep, + const char* trim=0, + const char* quote="\"", + char mask_chr='\\', + sep_flag sf=multiple); + + void set_str(const char* str); + token next_token(); + + private: + int check_chr(const char *str, char chr); + + private: + const char* m_src_string; + int m_start; + const char* m_sep; + const char* m_trim; + const char* m_quote; + char m_mask_chr; + unsigned m_sep_len; + sep_flag m_sep_flag; + }; + + + + //----------------------------------------------------------------------- + inline void tokenizer::set_str(const char* str) + { + m_src_string = str; + m_start = 0; + } + + + //----------------------------------------------------------------------- + inline int tokenizer::check_chr(const char *str, char chr) + { + return int(strchr(str, chr)); + } + + + //----------------------------------------------------------------------- + tokenizer::tokenizer(const char* sep, + const char* trim, + const char* quote, + char mask_chr, + sep_flag sf) : + m_src_string(0), + m_start(0), + m_sep(sep), + m_trim(trim), + m_quote(quote), + m_mask_chr(mask_chr), + m_sep_len(sep ? strlen(sep) : 0), + m_sep_flag(sep ? sf : single) + { + } + + + //----------------------------------------------------------------------- + tokenizer::token tokenizer::next_token() + { + unsigned count = 0; + char quote_chr = 0; + token tok; + + tok.ptr = 0; + tok.len = 0; + if(m_src_string == 0 || m_start == -1) return tok; + + register const char *pstr = m_src_string + m_start; + + if(*pstr == 0) + { + m_start = -1; + return tok; + } + + int sep_len = 1; + if(m_sep_flag == whole_str) sep_len = m_sep_len; + + if(m_sep_flag == multiple) + { + //Pass all the separator symbols at the begin of the string + while(*pstr && check_chr(m_sep, *pstr)) + { + ++pstr; + ++m_start; + } + } + + if(*pstr == 0) + { + m_start = -1; + return tok; + } + + for(count = 0;; ++count) + { + char c = *pstr; + int found = 0; + + //We are outside of qotation: find one of separator symbols + if(quote_chr == 0) + { + if(sep_len == 1) + { + found = check_chr(m_sep, c); + } + else + { + found = strncmp(m_sep, pstr, m_sep_len) == 0; + } + } + + ++pstr; + + if(c == 0 || found) + { + if(m_trim) + { + while(count && + check_chr(m_trim, m_src_string[m_start])) + { + ++m_start; + --count; + } + + while(count && + check_chr(m_trim, m_src_string[m_start + count - 1])) + { + --count; + } + } + + tok.ptr = m_src_string + m_start; + tok.len = count; + + //Next time it will be the next separator character + //But we must check, whether it is NOT the end of the string. + m_start += count; + if(c) + { + m_start += sep_len; + if(m_sep_flag == multiple) + { + //Pass all the separator symbols + //after the end of the string + while(check_chr(m_sep, m_src_string[m_start])) + { + ++m_start; + } + } + } + break; + } + + //Switch quote. If it is not a quote yet, try to check any of + //quote symbols. Otherwise quote must be finished with quote_symb + if(quote_chr == 0) + { + if(check_chr(m_quote, c)) + { + quote_chr = c; + continue; + } + } + else + { + //We are inside quote: pass all the mask symbols + if(m_mask_chr && c == m_mask_chr) + { + if(*pstr) + { + ++count; + ++pstr; + } + continue; + } + if(c == quote_chr) + { + quote_chr = 0; + continue; + } + } + } + return tok; + } + + +} + + + +//---------------------------------------------------------------------------- +int agg_main(int argc, char* argv[]); + + + +//---------------------------------------------------------------------------- +/*int PASCAL WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow) +{ + agg::g_windows_instance = hInstance; + agg::g_windows_cmd_show = nCmdShow; + + char* argv_str = new char [strlen(lpszCmdLine) + 3]; + char* argv_ptr = argv_str; + + char* argv[64]; + memset(argv, 0, sizeof(argv)); + + agg::tokenizer cmd_line(" ", "\"' ", "\"'", '\\', agg::tokenizer::multiple); + cmd_line.set_str(lpszCmdLine); + + int argc = 0; + argv[argc++] = argv_ptr; + *argv_ptr++ = 0; + + while(argc < 64) + { + agg::tokenizer::token tok = cmd_line.next_token(); + if(tok.ptr == 0) break; + if(tok.len) + { + memcpy(argv_ptr, tok.ptr, tok.len); + argv[argc++] = argv_ptr; + argv_ptr += tok.len; + *argv_ptr++ = 0; + } + } + + int ret = agg_main(argc, argv); + delete [] argv_str; + + return ret; +}*/ + + + + diff --git a/desmume/src/windows/agg/src/platform/win32/agg_win32_bmp.cpp b/desmume/src/windows/agg/src/platform/win32/agg_win32_bmp.cpp new file mode 100644 index 000000000..249eac947 --- /dev/null +++ b/desmume/src/windows/agg/src/platform/win32/agg_win32_bmp.cpp @@ -0,0 +1,625 @@ +//---------------------------------------------------------------------------- +// +//---------------------------------------------------------------------------- +// Contact: mcseemagg@yahoo.com +//---------------------------------------------------------------------------- +// +// class pixel_map +// +//---------------------------------------------------------------------------- + +#include "platform/win32/agg_win32_bmp.h" +#include "agg_basics.h" + +namespace agg +{ + + //------------------------------------------------------------------------ + pixel_map::~pixel_map() + { + destroy(); + } + + + //------------------------------------------------------------------------ + pixel_map::pixel_map() : + m_bmp(0), + m_buf(0), + m_bpp(0), + m_is_internal(false), + m_img_size(0), + m_full_size(0) + + { + } + + + //------------------------------------------------------------------------ + void pixel_map::destroy() + { + if(m_bmp && m_is_internal) delete [] (unsigned char*)m_bmp; + m_bmp = 0; + m_is_internal = false; + m_buf = 0; + } + + + //------------------------------------------------------------------------ + void pixel_map::create(unsigned width, + unsigned height, + org_e org, + unsigned clear_val) + { + destroy(); + if(width == 0) width = 1; + if(height == 0) height = 1; + m_bpp = org; + create_from_bmp(create_bitmap_info(width, height, m_bpp)); + create_gray_scale_palette(m_bmp); + m_is_internal = true; + if(clear_val <= 255) + { + memset(m_buf, clear_val, m_img_size); + } + } + + + //------------------------------------------------------------------------ + HBITMAP pixel_map::create_dib_section(HDC h_dc, + unsigned width, + unsigned height, + org_e org, + unsigned clear_val) + { + destroy(); + if(width == 0) width = 1; + if(height == 0) height = 1; + m_bpp = org; + HBITMAP h_bitmap = create_dib_section_from_args(h_dc, width, height, m_bpp); + create_gray_scale_palette(m_bmp); + m_is_internal = true; + if(clear_val <= 255) + { + memset(m_buf, clear_val, m_img_size); + } + return h_bitmap; + } + + + + //------------------------------------------------------------------------ + void pixel_map::clear(unsigned clear_val) + { + if(m_buf) memset(m_buf, clear_val, m_img_size); + } + + + //------------------------------------------------------------------------ + void pixel_map::attach_to_bmp(BITMAPINFO *bmp) + { + if(bmp) + { + destroy(); + create_from_bmp(bmp); + m_is_internal = false; + } + } + + + + //static + //------------------------------------------------------------------------ + unsigned pixel_map::calc_full_size(BITMAPINFO *bmp) + { + if(bmp == 0) return 0; + + return sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD) * calc_palette_size(bmp) + + bmp->bmiHeader.biSizeImage; + } + + //static + //------------------------------------------------------------------------ + unsigned pixel_map::calc_header_size(BITMAPINFO *bmp) + { + if(bmp == 0) return 0; + return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * calc_palette_size(bmp); + } + + + //static + //------------------------------------------------------------------------ + unsigned pixel_map::calc_palette_size(unsigned clr_used, unsigned bits_per_pixel) + { + int palette_size = 0; + + if(bits_per_pixel <= 8) + { + palette_size = clr_used; + if(palette_size == 0) + { + palette_size = 1 << bits_per_pixel; + } + } + return palette_size; + } + + //static + //------------------------------------------------------------------------ + unsigned pixel_map::calc_palette_size(BITMAPINFO *bmp) + { + if(bmp == 0) return 0; + return calc_palette_size(bmp->bmiHeader.biClrUsed, bmp->bmiHeader.biBitCount); + } + + + //static + //------------------------------------------------------------------------ + unsigned char * pixel_map::calc_img_ptr(BITMAPINFO *bmp) + { + if(bmp == 0) return 0; + return ((unsigned char*)bmp) + calc_header_size(bmp); + } + + //static + //------------------------------------------------------------------------ + BITMAPINFO* pixel_map::create_bitmap_info(unsigned width, + unsigned height, + unsigned bits_per_pixel) + { + unsigned line_len = calc_row_len(width, bits_per_pixel); + unsigned img_size = line_len * height; + unsigned rgb_size = calc_palette_size(0, bits_per_pixel) * sizeof(RGBQUAD); + unsigned full_size = sizeof(BITMAPINFOHEADER) + rgb_size + img_size; + + BITMAPINFO *bmp = (BITMAPINFO *) new unsigned char[full_size]; + + bmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmp->bmiHeader.biWidth = width; + bmp->bmiHeader.biHeight = height; + bmp->bmiHeader.biPlanes = 1; + bmp->bmiHeader.biBitCount = (unsigned short)bits_per_pixel; + bmp->bmiHeader.biCompression = 0; + bmp->bmiHeader.biSizeImage = img_size; + bmp->bmiHeader.biXPelsPerMeter = 0; + bmp->bmiHeader.biYPelsPerMeter = 0; + bmp->bmiHeader.biClrUsed = 0; + bmp->bmiHeader.biClrImportant = 0; + + return bmp; + } + + + //static + //------------------------------------------------------------------------ + void pixel_map::create_gray_scale_palette(BITMAPINFO *bmp) + { + if(bmp == 0) return; + + unsigned rgb_size = calc_palette_size(bmp); + RGBQUAD *rgb = (RGBQUAD*)(((unsigned char*)bmp) + sizeof(BITMAPINFOHEADER)); + unsigned brightness; + unsigned i; + + for(i = 0; i < rgb_size; i++) + { + brightness = (255 * i) / (rgb_size - 1); + rgb->rgbBlue = + rgb->rgbGreen = + rgb->rgbRed = (unsigned char)brightness; + rgb->rgbReserved = 0; + rgb++; + } + } + + + + //static + //------------------------------------------------------------------------ + unsigned pixel_map::calc_row_len(unsigned width, unsigned bits_per_pixel) + { + unsigned n = width; + unsigned k; + + switch(bits_per_pixel) + { + case 1: k = n; + n = n >> 3; + if(k & 7) n++; + break; + + case 4: k = n; + n = n >> 1; + if(k & 3) n++; + break; + + case 8: + break; + + case 16: n *= 2; + break; + + case 24: n *= 3; + break; + + case 32: n *= 4; + break; + + case 48: n *= 6; + break; + + case 64: n *= 8; + break; + + default: n = 0; + break; + } + return ((n + 3) >> 2) << 2; + } + + + + + + //------------------------------------------------------------------------ + void pixel_map::draw(HDC h_dc, const RECT *device_rect, const RECT *bmp_rect) const + { + if(m_bmp == 0 || m_buf == 0) return; + + unsigned bmp_x = 0; + unsigned bmp_y = 0; + unsigned bmp_width = m_bmp->bmiHeader.biWidth; + unsigned bmp_height = m_bmp->bmiHeader.biHeight; + unsigned dvc_x = 0; + unsigned dvc_y = 0; + unsigned dvc_width = m_bmp->bmiHeader.biWidth; + unsigned dvc_height = m_bmp->bmiHeader.biHeight; + + if(bmp_rect) + { + bmp_x = bmp_rect->left; + bmp_y = bmp_rect->top; + bmp_width = bmp_rect->right - bmp_rect->left; + bmp_height = bmp_rect->bottom - bmp_rect->top; + } + + dvc_x = bmp_x; + dvc_y = bmp_y; + dvc_width = bmp_width; + dvc_height = bmp_height; + + if(device_rect) + { + dvc_x = device_rect->left; + dvc_y = device_rect->top; + dvc_width = device_rect->right - device_rect->left; + dvc_height = device_rect->bottom - device_rect->top; + } + + if(dvc_width != bmp_width || dvc_height != bmp_height) + { + ::SetStretchBltMode(h_dc, COLORONCOLOR); + ::StretchDIBits( + h_dc, // handle of device context + dvc_x, // x-coordinate of upper-left corner of source rect. + dvc_y, // y-coordinate of upper-left corner of source rect. + dvc_width, // width of source rectangle + dvc_height, // height of source rectangle + bmp_x, + bmp_y, // x, y -coordinates of upper-left corner of dest. rect. + bmp_width, // width of destination rectangle + bmp_height, // height of destination rectangle + m_buf, // address of bitmap bits + m_bmp, // address of bitmap data + DIB_RGB_COLORS, // usage + SRCCOPY // raster operation code + ); + } + else + { + ::SetDIBitsToDevice( + h_dc, // handle to device context + dvc_x, // x-coordinate of upper-left corner of + dvc_y, // y-coordinate of upper-left corner of + dvc_width, // source rectangle width + dvc_height, // source rectangle height + bmp_x, // x-coordinate of lower-left corner of + bmp_y, // y-coordinate of lower-left corner of + 0, // first scan line in array + bmp_height, // number of scan lines + m_buf, // address of array with DIB bits + m_bmp, // address of structure with bitmap info. + DIB_RGB_COLORS // RGB or palette indexes + ); + } + } + + + //------------------------------------------------------------------------ + void pixel_map::draw(HDC h_dc, int x, int y, double scale) const + { + if(m_bmp == 0 || m_buf == 0) return; + + unsigned width = unsigned(m_bmp->bmiHeader.biWidth * scale); + unsigned height = unsigned(m_bmp->bmiHeader.biHeight * scale); + RECT rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + draw(h_dc, &rect); + } + + + + + //------------------------------------------------------------------------ + void pixel_map::blend(HDC h_dc, const RECT *device_rect, const RECT *bmp_rect) const + { +#if !defined(AGG_BMP_ALPHA_BLEND) + draw(h_dc, device_rect, bmp_rect); + return; +#else + if(m_bpp != 32) + { + draw(h_dc, device_rect, bmp_rect); + return; + } + + if(m_bmp == 0 || m_buf == 0) return; + + unsigned bmp_x = 0; + unsigned bmp_y = 0; + unsigned bmp_width = m_bmp->bmiHeader.biWidth; + unsigned bmp_height = m_bmp->bmiHeader.biHeight; + unsigned dvc_x = 0; + unsigned dvc_y = 0; + unsigned dvc_width = m_bmp->bmiHeader.biWidth; + unsigned dvc_height = m_bmp->bmiHeader.biHeight; + + if(bmp_rect) + { + bmp_x = bmp_rect->left; + bmp_y = bmp_rect->top; + bmp_width = bmp_rect->right - bmp_rect->left; + bmp_height = bmp_rect->bottom - bmp_rect->top; + } + + dvc_x = bmp_x; + dvc_y = bmp_y; + dvc_width = bmp_width; + dvc_height = bmp_height; + + if(device_rect) + { + dvc_x = device_rect->left; + dvc_y = device_rect->top; + dvc_width = device_rect->right - device_rect->left; + dvc_height = device_rect->bottom - device_rect->top; + } + + HDC mem_dc = ::CreateCompatibleDC(h_dc); + void* buf = 0; + HBITMAP bmp = ::CreateDIBSection( + mem_dc, + m_bmp, + DIB_RGB_COLORS, + &buf, + 0, + 0 + ); + memcpy(buf, m_buf, m_bmp->bmiHeader.biSizeImage); + + HBITMAP temp = (HBITMAP)::SelectObject(mem_dc, bmp); + + BLENDFUNCTION blend; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + +#if defined(AC_SRC_ALPHA) + blend.AlphaFormat = AC_SRC_ALPHA; +//#elif defined(AC_SRC_NO_PREMULT_ALPHA) +// blend.AlphaFormat = AC_SRC_NO_PREMULT_ALPHA; +#else +#error "No appropriate constant for alpha format. Check version of wingdi.h, There must be AC_SRC_ALPHA or AC_SRC_NO_PREMULT_ALPHA" +#endif + + blend.SourceConstantAlpha = 255; + ::AlphaBlend( + h_dc, + dvc_x, + dvc_y, + dvc_width, + dvc_height, + mem_dc, + bmp_x, + bmp_y, + bmp_width, + bmp_height, + blend + ); + + ::SelectObject(mem_dc, temp); + ::DeleteObject(bmp); + ::DeleteObject(mem_dc); +#endif //defined(AGG_BMP_ALPHA_BLEND) + } + + + //------------------------------------------------------------------------ + void pixel_map::blend(HDC h_dc, int x, int y, double scale) const + { + if(m_bmp == 0 || m_buf == 0) return; + unsigned width = unsigned(m_bmp->bmiHeader.biWidth * scale); + unsigned height = unsigned(m_bmp->bmiHeader.biHeight * scale); + RECT rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + blend(h_dc, &rect); + } + + + //------------------------------------------------------------------------ + bool pixel_map::load_from_bmp(FILE *fd) + { + BITMAPFILEHEADER bmf; + BITMAPINFO *bmi = 0; + unsigned bmp_size; + + fread(&bmf, sizeof(bmf), 1, fd); + if(bmf.bfType != 0x4D42) goto bmperr; + + bmp_size = bmf.bfSize - sizeof(BITMAPFILEHEADER); + + bmi = (BITMAPINFO*) new unsigned char [bmp_size]; + if(fread(bmi, 1, bmp_size, fd) != bmp_size) goto bmperr; + destroy(); + m_bpp = bmi->bmiHeader.biBitCount; + create_from_bmp(bmi); + m_is_internal = 1; + return true; + + bmperr: + if(bmi) delete [] (unsigned char*) bmi; + return false; + } + + + + //------------------------------------------------------------------------ + bool pixel_map::load_from_bmp(const char *filename) + { + FILE *fd = fopen(filename, "rb"); + bool ret = false; + if(fd) + { + ret = load_from_bmp(fd); + fclose(fd); + } + return ret; + } + + + + //------------------------------------------------------------------------ + bool pixel_map::save_as_bmp(FILE *fd) const + { + if(m_bmp == 0) return 0; + + BITMAPFILEHEADER bmf; + + bmf.bfType = 0x4D42; + bmf.bfOffBits = calc_header_size(m_bmp) + sizeof(bmf); + bmf.bfSize = bmf.bfOffBits + m_img_size; + bmf.bfReserved1 = 0; + bmf.bfReserved2 = 0; + + fwrite(&bmf, sizeof(bmf), 1, fd); + fwrite(m_bmp, m_full_size, 1, fd); + return true; + } + + + + //------------------------------------------------------------------------ + bool pixel_map::save_as_bmp(const char *filename) const + { + FILE *fd = fopen(filename, "wb"); + bool ret = false; + if(fd) + { + ret = save_as_bmp(fd); + fclose(fd); + } + return ret; + } + + + //------------------------------------------------------------------------ + unsigned char* pixel_map::buf() + { + return m_buf; + } + + //------------------------------------------------------------------------ + unsigned pixel_map::width() const + { + return m_bmp->bmiHeader.biWidth; + } + + //------------------------------------------------------------------------ + unsigned pixel_map::height() const + { + return m_bmp->bmiHeader.biHeight; + } + + //------------------------------------------------------------------------ + int pixel_map::stride() const + { + return calc_row_len(m_bmp->bmiHeader.biWidth, + m_bmp->bmiHeader.biBitCount); + } + + + //private + //------------------------------------------------------------------------ + void pixel_map::create_from_bmp(BITMAPINFO *bmp) + { + if(bmp) + { + m_img_size = calc_row_len(bmp->bmiHeader.biWidth, + bmp->bmiHeader.biBitCount) * + bmp->bmiHeader.biHeight; + + m_full_size = calc_full_size(bmp); + m_bmp = bmp; + m_buf = calc_img_ptr(bmp); + } + } + + + //private + //------------------------------------------------------------------------ + HBITMAP pixel_map::create_dib_section_from_args(HDC h_dc, + unsigned width, + unsigned height, + unsigned bits_per_pixel) + { + unsigned line_len = calc_row_len(width, bits_per_pixel); + unsigned img_size = line_len * height; + unsigned rgb_size = calc_palette_size(0, bits_per_pixel) * sizeof(RGBQUAD); + unsigned full_size = sizeof(BITMAPINFOHEADER) + rgb_size; + + BITMAPINFO *bmp = (BITMAPINFO *) new unsigned char[full_size]; + + bmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmp->bmiHeader.biWidth = width; + bmp->bmiHeader.biHeight = height; + bmp->bmiHeader.biPlanes = 1; + bmp->bmiHeader.biBitCount = (unsigned short)bits_per_pixel; + bmp->bmiHeader.biCompression = 0; + bmp->bmiHeader.biSizeImage = img_size; + bmp->bmiHeader.biXPelsPerMeter = 0; + bmp->bmiHeader.biYPelsPerMeter = 0; + bmp->bmiHeader.biClrUsed = 0; + bmp->bmiHeader.biClrImportant = 0; + + void* img_ptr = 0; + HBITMAP h_bitmap = ::CreateDIBSection(h_dc, bmp, DIB_RGB_COLORS, &img_ptr, NULL, 0); + + if(img_ptr) + { + m_img_size = calc_row_len(width, bits_per_pixel) * height; + m_full_size = 0; + m_bmp = bmp; + m_buf = (unsigned char *) img_ptr; + } + + return h_bitmap; + } +} + + + diff --git a/desmume/src/windows/agg/src/readme b/desmume/src/windows/agg/src/readme new file mode 100644 index 000000000..d565c28a6 --- /dev/null +++ b/desmume/src/windows/agg/src/readme @@ -0,0 +1,5 @@ +Use automake to build the library. + +If automake is not available you still can use the old make. +There is a very simple Makefile that can be used. Note that +if you use automake it will overwrite Makefile.