From a1b81e560f91e616ada152ae9a2735c9fd6fce2e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 1 Jul 2026 01:35:35 +0800 Subject: [PATCH 001/211] ext/standard: Fix `phpcredits()` QA team heading alignment (#22501) The QA team title isn't centered correctly in phpcredits(). This PR fix this. --- ext/standard/credits.c | 2 +- ext/standard/tests/general_functions/phpcredits.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/credits.c b/ext/standard/credits.c index 2b38cbf3b4e9..1fc74d72db35 100644 --- a/ext/standard/credits.c +++ b/ext/standard/credits.c @@ -99,7 +99,7 @@ PHPAPI ZEND_COLD void php_print_credits(int flag) /* {{{ */ if (flag & PHP_CREDITS_QA) { php_info_print_table_start(); - php_info_print_table_header(1, "PHP Quality Assurance Team"); + php_info_print_table_colspan_header(1, "PHP Quality Assurance Team"); php_info_print_table_row(1, "Ilia Alshanetsky, Joerg Behrens, Antony Dovgal, Stefan Esser, Moriyoshi Koizumi, Magnus Maatta, Sebastian Nohn, Derick Rethans, Melvyn Sopacua, Pierre-Alain Joye, Dmitry Stogov, Felipe Pena, David Soria Parra, Stanislav Malyshev, Julien Pauli, Stephen Zarkos, Anatol Belski, Remi Collet, Ferenc Kovacs"); php_info_print_table_end(); } diff --git a/ext/standard/tests/general_functions/phpcredits.phpt b/ext/standard/tests/general_functions/phpcredits.phpt index dfcddc61f39d..347f3018d49a 100644 --- a/ext/standard/tests/general_functions/phpcredits.phpt +++ b/ext/standard/tests/general_functions/phpcredits.phpt @@ -33,7 +33,7 @@ Language Design & Concept %wPHP Documentation%w %a -PHP Quality Assurance Team +%wPHP Quality Assurance Team%w %a %wWebsites and Infrastructure team%w From f53c8a48ca230eb3be1dbf006680dea20b414079 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 30 Jun 2026 21:10:40 -0700 Subject: [PATCH 002/211] Update NEWS after cherry-pick (cherry picked from commit fb5e86bd2a560c78c57011eac93d892445578648) --- NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 91f69440f6fd..020afbcc7197 100644 --- a/NEWS +++ b/NEWS @@ -29,9 +29,6 @@ PHP NEWS . Fixed IntlChar methods leaving stale global error state after successful calls. (Xuyang Zhang) -- Lexbor: - . Merge patch c3a6847. (ilutov, timwolla) - - PDO_ODBC: . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN carries no credentials). (iliaal) @@ -98,6 +95,9 @@ PHP NEWS . Fixed IntlTimeZone::getDisplayName() to synchronize object error state for invalid display types. (Weilin Du) +- Lexbor: + . Merge patch c3a6847. (ilutov, timwolla) + - Opcache: . Fixed bug GH-22265 (Another tailcall vm_interrupt bug). (Levi Morrison) . Fixed bug GH-20469 (Unsafe inheritance cache replay with reentrant From a1287f0ef01042be841bec1f4cc17969b87ebb20 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Wed, 1 Jul 2026 15:01:55 +0700 Subject: [PATCH 003/211] NEWS: fix markup --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 650007b5a090..6ba018dab89d 100644 --- a/NEWS +++ b/NEWS @@ -165,7 +165,7 @@ PHP NEWS . pcntl_exec() now throws a ValueError if the $args array is not a list array. (Weilin Du) -- PDO_DBLIB; +- PDO_DBLIB: . Added dblib_handle_check_liveness handler. (freddy77) - PDO_PGSQL: From 9aaf634fde8b463dd0f2630f5c3b290a5e8c1837 Mon Sep 17 00:00:00 2001 From: Sergey Panteleev Date: Wed, 1 Jul 2026 11:11:52 +0300 Subject: [PATCH 004/211] PHP-8.2 is now for PHP 8.2.33-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index b14ecd79eacb..787589fc6dfc 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.32 +?? ??? ????, PHP 8.2.33 + + +02 Jul 2026, PHP 8.2.32 - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in diff --git a/Zend/zend.h b/Zend/zend.h index b500c17a599f..029a6ecf33bd 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.32-dev" +#define ZEND_VERSION "4.2.33-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 70b0d6f604be..95e2f680dfab 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.2.32-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.33-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index e431a4d08189..f56fd7802d2b 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 2 -#define PHP_RELEASE_VERSION 32 +#define PHP_RELEASE_VERSION 33 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.32-dev" -#define PHP_VERSION_ID 80232 +#define PHP_VERSION "8.2.33-dev" +#define PHP_VERSION_ID 80233 From fc0f25f286104e367c3b8be99cb2b42fc268dcae Mon Sep 17 00:00:00 2001 From: Nora Dossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:52:14 +0100 Subject: [PATCH 005/211] Fix NULL deref when enabling TLS fails and the peer name needs to be reset The code tries to read the context on NULL when `php_stream_xport_crypto_setup` fails because by then `stream` is reset to NULL. This is also UB, so can cause miscompiles. ``` ==1217==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000090 (pc 0x55d829ed3acf bp 0x7fff045f5770 sp 0x7fff045f4df0 T0) ==1217==The signal is caused by a READ memory access. ==1217==Hint: address points to the zero page. #0 0x55d829ed3acf in php_stream_url_wrap_http_ex /work/php-src/ext/standard/http_fopen_wrapper.c:580 #1 0x55d829ed857e in php_stream_url_wrap_http /work/php-src/ext/standard/http_fopen_wrapper.c:1204 #2 0x55d82a15073d in _php_stream_open_wrapper_ex /work/php-src/main/streams/streams.c:2270 #3 0x55d829e78fa6 in zif_file_get_contents /work/php-src/ext/standard/file.c:409 #4 0x55d829bbfe39 in zif_phar_file_get_contents /work/php-src/ext/phar/func_interceptors.c:226 #5 0x55d82a0b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306 #6 0x55d82a3e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154 #7 0x55d82a540995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519 #8 0x55d82a5558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962 #9 0x55d82a6ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980 #10 0x55d82a0ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645 #11 0x55d82a0ecccb in php_execute_script /work/php-src/main/main.c:2685 #12 0x55d82a6bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951 #13 0x55d82a6c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362 #14 0x7f9e770491c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) #15 0x7f9e7704928a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) #16 0x55d829209b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c) ``` This is a backport of: * 7782b8876bc0f61b0e8bcce262e22896e423b22e * f9519ccbb9975b2afae95a418fae65ad93823dc6 * afded3dffc432a86955e6b8018146abb3d03779b Co-authored-by: Ilija Tovilo --- NEWS | 4 ++ ext/openssl/tests/gh21031.phpt | 55 ++++++++++++++++++++ ext/openssl/tests/sni_server_cs_expired.pem | 57 +++++++++++++++++++++ ext/standard/http_fopen_wrapper.c | 12 +++-- 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 ext/openssl/tests/gh21031.phpt create mode 100644 ext/openssl/tests/sni_server_cs_expired.pem diff --git a/NEWS b/NEWS index 9a9d86351752..9e0f80a15ccc 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.32 +- Streams: + . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL + and a proxy set). (CVE-2026-12184) (ndossche) + - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in openssl_encrypt with AES-WRAP-PAD). (David Carlier) diff --git a/ext/openssl/tests/gh21031.phpt b/ext/openssl/tests/gh21031.phpt new file mode 100644 index 000000000000..25bdb385a791 --- /dev/null +++ b/ext/openssl/tests/gh21031.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-21031 (Fix NULL deref when enabling TLS fails and the peer name needs to be reset) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'SNI_server_certs' => [ + "cs.php.net" => __DIR__ . "/sni_server_cs_expired.pem", + ] + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server, 10); + if ($conn) { + fclose($conn); + } + + phpt_wait(); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create([ + 'ssl' => [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + "http" => [ + "proxy" => "tcp://{{ ADDR }}" + ], + ]); + + var_dump(@file_get_contents("https://cs.php.net/", false, $clientCtx)); + + phpt_notify(); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) diff --git a/ext/openssl/tests/sni_server_cs_expired.pem b/ext/openssl/tests/sni_server_cs_expired.pem new file mode 100644 index 000000000000..9f5a201b26d1 --- /dev/null +++ b/ext/openssl/tests/sni_server_cs_expired.pem @@ -0,0 +1,57 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf +ahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD +8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D +SD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq +511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u +TFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v +I7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx +qPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f +XOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC +2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x +5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q +crYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV +ekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu +Etbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf +R1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo +AJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB +asspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb +fpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r +B70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi +4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e +PS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo +6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646 +FD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv +jNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2 +M+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy +MTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC +LDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD +dIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O +aF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ +VG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD +Kom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK +Lz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH +hZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7 +O7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv +SLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF +ZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd +2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/ +o8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4 +HqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE +6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+ +8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH +BkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm +WS7NM6fbQpO9APNr7o0ZOjbbWFzlNw== +-----END CERTIFICATE----- diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 040ee4eabf78..00f7b4dfa80e 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -546,6 +546,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1); if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) { + if (reset_ssl_peer_name) { + php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + } + php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; @@ -567,16 +571,18 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* enable SSL transport layer */ if (stream) { + php_stream_context *old_context = PHP_STREAM_CONTEXT(stream); + if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } - } - if (reset_ssl_peer_name) { - php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + if (reset_ssl_peer_name) { + php_stream_context_unset_option(old_context, "ssl", "peer_name"); + } } } From d6c3f4b5466d96cfd7fac6e9a9a6959347224546 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 1 Jul 2026 11:18:18 +0200 Subject: [PATCH 006/211] PHP-8.3 is now for PHP 8.3.33-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 9e0f80a15ccc..348d062e83a4 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.32 +?? ??? ????, PHP 8.3.33 + + +02 Jul 2026, PHP 8.3.32 - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL diff --git a/Zend/zend.h b/Zend/zend.h index aa8787d844b2..e022b97a663d 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.31-dev" +#define ZEND_VERSION "4.3.33-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index b79f91ddecdd..f85650811327 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.31-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.33-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 21bea7bb26a2..df3ccaf05352 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 31 +#define PHP_RELEASE_VERSION 33 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.31-dev" -#define PHP_VERSION_ID 80331 +#define PHP_VERSION "8.3.33-dev" +#define PHP_VERSION_ID 80333 From 1c196d0ee5670b0e9873fad49dda5464b8294707 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 17 Jun 2026 00:19:31 +0200 Subject: [PATCH 007/211] Fixed timeout for supplemental read at end of a blocking stream in SSL stream wrapper This issue is related to GH-22332: > Let's assume we have a stream with 5 queued bytes. When calling fread($s, 1), > the underlying buffered stream will request a chunk of up to 8192 bytes > immediately, but only return the requested 1 byte back to the reader. If the > reader then requests fread($s, 10), _php_stream_read() will first return > anything that was buffered but not yet read. > > If the requested length exceeds the number of buffered bytes (as is the case > above), another read call is issued. This call will return nothing, because > the stream only provides 4 more readable bytes, all of which are buffered. > php_openssl_handle_ssl_error() (called by php_openssl_sockop_io()) will then > incorrectly set last_status to WANT_READ, even though we've already read the > remaining data. > > Furthermore, stream_select() can cause the same issue via > php_openssl_sockop_cast(castas: PHP_STREAM_AS_FD_FOR_SELECT), which pre-fills > the read buffer on SSL_pending() > 0. The subsequent fread() will lead to the > same condition as above. > > There's a second issue here. If the stream is blocking, the supplement read > will block for the duration of the timeout. This will be addressed in a second > PR. This addresses the last paragraph. Avoid a blocking read when we already have buffered data, as we may have reached the end of the stream and will wait in vain. Closes GH-22346 --- NEWS | 4 + .../stream_supplemental_read_timeout.phpt | 90 +++++++++++++++++++ ext/openssl/xp_ssl.c | 12 ++- 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 ext/openssl/tests/stream_supplemental_read_timeout.phpt diff --git a/NEWS b/NEWS index d03a77b01d91..feb9221c2e0b 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ PHP NEWS . Fixed IntlChar methods leaving stale global error state after successful calls. (Xuyang Zhang) +- OpenSSL: + . Fixed timeout for supplemental read at end of a blocking stream in SSL + stream wrapper. (ilutov) + - PDO_ODBC: . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN carries no credentials). (iliaal) diff --git a/ext/openssl/tests/stream_supplemental_read_timeout.phpt b/ext/openssl/tests/stream_supplemental_read_timeout.phpt new file mode 100644 index 000000000000..0e36f146bf9e --- /dev/null +++ b/ext/openssl/tests/stream_supplemental_read_timeout.phpt @@ -0,0 +1,90 @@ +--TEST-- +Timeout for supplemental read at end of a blocking stream in SSL stream wrapper +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + ['local_cert' => '%s']]); + $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; + $server = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server, 30); + + fwrite($conn, "hello\n"); + + phpt_wait(); + fclose($conn); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'peer_name' => '%s', + ]]); + + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx); + stream_set_blocking($client, true); + stream_set_timeout($client, 5); + $start = hrtime(true); + + $buf = ''; + $read = [$client]; + $write = $except = null; + while (true) { + if (!stream_select($read, $write, $except, 5)) { + break; + } + + // Initially, read only the first char, then request more than is stored + // in the buffer, triggering a supplemental read. + $chunk = fread($client, strlen($buf) === 0 ? 1 : 10); + if ($chunk === '' || $chunk === false) { + /* A non-application record (e.g. a TLS 1.3 session ticket) may arrive first. */ + if (feof($client)) { + break; + } + } else { + $buf .= $chunk; + if (strlen($buf) >= 6) { + break; + } + } + $read = [$client]; + $write = $except = null; + } + + echo trim($buf), "\n"; + + $diff = (hrtime(true) - $start) / 1e9; + var_dump($diff < 4.0); + + phpt_notify(); + fclose($client); +CODE; +$clientCode = sprintf($clientCode, $peerName); + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +hello +bool(true) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index ffacd8a107b7..77c98f65b518 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -2069,7 +2069,11 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si /* Only do this if SSL is active. */ if (sslsock->ssl_active) { - int retry = 1; + /* We have already returned some buffered data. Don't retry and don't + * block. We're just trying to fill the buffer more, but the stream might + * be empty, so we don't want to wait in vain. */ + bool supplemental = stream->has_buffered_data; + int retry = !supplemental; struct timeval start_time; struct timeval *timeout = NULL; int began_blocked = sslsock->s.is_blocked; @@ -2082,11 +2086,11 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si } /* never use a timeout with non-blocking sockets */ - if (began_blocked) { + if (began_blocked && !supplemental) { timeout = &sslsock->s.timeout; } - if (timeout) { + if (timeout || supplemental) { php_openssl_set_blocking(sslsock, 0); } @@ -2160,7 +2164,7 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si } /* Don't loop indefinitely in non-blocking mode if no data is available */ - if (began_blocked == 0) { + if (began_blocked == 0 || supplemental) { break; } From d822e940a2273cd8fdfa9d7cf9680e3aa1d83a46 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 16 Jun 2026 14:42:12 +0200 Subject: [PATCH 008/211] Fix stream_socket_get_crypto_status() after supplemental read Let's assume we have a stream with 5 queued bytes. When calling fread($s, 1), the underlying buffered stream will request a chunk of up to 8192 bytes immediately, but only return the requested 1 byte back to the reader. If the reader then requests fread($s, 10), _php_stream_read() will first return anything that was buffered but not yet read. If the requested length exceeds the number of buffered bytes (as is the case above), another read call is issued. This call will return nothing, because the stream only provides 4 more readable bytes, all of which are buffered. php_openssl_handle_ssl_error() (called by php_openssl_sockop_io()) will then incorrectly set last_status to WANT_READ, even though we've already read the remaining data. Furthermore, stream_select() can cause the same issue via php_openssl_sockop_cast(castas: PHP_STREAM_AS_FD_FOR_SELECT), which pre-fills the read buffer on SSL_pending() > 0. The subsequent fread() will lead to the same condition as above. Closes GH-22332 --- NEWS | 2 + ...t_get_crypto_status_supplemental_read.phpt | 83 +++++++++++++++++++ ext/openssl/xp_ssl.c | 10 +++ 3 files changed, 95 insertions(+) create mode 100644 ext/openssl/tests/stream_socket_get_crypto_status_supplemental_read.phpt diff --git a/NEWS b/NEWS index 6ba018dab89d..ec9efcbe02a3 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0alpha2 +- OpenSSL: + . Fixed stream_socket_get_crypto_status() after supplemental read. (ilutov) 02 Jul 2026, PHP 8.6.0alpha1 diff --git a/ext/openssl/tests/stream_socket_get_crypto_status_supplemental_read.phpt b/ext/openssl/tests/stream_socket_get_crypto_status_supplemental_read.phpt new file mode 100644 index 000000000000..9044b1bbdbe4 --- /dev/null +++ b/ext/openssl/tests/stream_socket_get_crypto_status_supplemental_read.phpt @@ -0,0 +1,83 @@ +--TEST-- +stream_socket_get_crypto_status(): reports status NONE after supplemental read +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + ['local_cert' => '%s']]); + $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; + $server = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server, 30); + + fwrite($conn, "hello\n"); + + phpt_wait(); + fclose($conn); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'peer_name' => '%s', + ]]); + + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx); + stream_set_blocking($client, false); + + $buf = ''; + $read = [$client]; + $write = $except = null; + while (stream_select($read, $write, $except, 5)) { + // Initially, read only the first char, then request more than is stored + // in the buffer, triggering a supplemental read. + $chunk = fread($client, strlen($buf) === 0 ? 1 : 10); + if ($chunk === '' || $chunk === false) { + /* A non-application record (e.g. a TLS 1.3 session ticket) may arrive first. */ + if (feof($client)) { + break; + } + } else { + $buf .= $chunk; + if (strlen($buf) >= 6) { + break; + } + } + $read = [$client]; + $write = $except = null; + } + + echo trim($buf), "\n"; + /* A successful read clears the pending status back to NONE. */ + var_dump(stream_socket_get_crypto_status($client) === STREAM_CRYPTO_STATUS_NONE); + + phpt_notify(); + fclose($client); +CODE; +$clientCode = sprintf($clientCode, $peerName); + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +hello +bool(true) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 92a17b60c5ce..93a9971b4a78 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -3019,6 +3019,16 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0); } + /* This might be a supplemental read after consuming buffered data. If + * the read returned nothing, ignore status WANT_READ. */ + if (read && + supplemental && + nr_bytes <= 0 && + sslsock->last_status == STREAM_CRYPTO_STATUS_WANT_READ + ) { + sslsock->last_status = STREAM_CRYPTO_STATUS_NONE; + } + /* And if we were originally supposed to be blocking, let's reset the socket to that. */ if (began_blocked) { php_openssl_set_blocking(sslsock, 1); From d27da095b1cbc353887dda31f165c0ff45de5a05 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 17 Apr 2026 11:19:26 -0400 Subject: [PATCH 009/211] Fix infinite recursion in property hook getter in opcache preloaded trait preload_fix_trait_op_array rewrites the clone op_array from its original after optimization, preserving function_name, scope, fn_flags, prototype, and static_variables. For trait-cloned property hooks, it also needs to preserve prop_info: zend_do_traits_property_binding set it to the using class's property, but the reset pointed it back at the trait's property. That mismatch caused zend_is_in_hook to miss the self-access check inside the hook, recursing into the getter/setter instead of reading or writing the backing store. Fixes GH-21770 Closes GH-21788 --- NEWS | 2 ++ ext/opcache/ZendAccelerator.c | 2 ++ ext/opcache/tests/gh21770.phpt | 25 +++++++++++++++++++++++++ ext/opcache/tests/preload_gh21770.inc | 20 ++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 ext/opcache/tests/gh21770.phpt create mode 100644 ext/opcache/tests/preload_gh21770.inc diff --git a/NEWS b/NEWS index feb9221c2e0b..012a28ffe6ce 100644 --- a/NEWS +++ b/NEWS @@ -231,6 +231,8 @@ PHP NEWS . Fixed bug GH-21593 (Borked function JIT JMPNZ smart branch). (ilutov) . Fixed bug GH-21460 (COND optimization regression). (Dmitry, Arnaud) . Fixed faulty returns out of zend_try block in zend_jit_trace(). (ilutov) + . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache + preloaded trait). (iliaal) - OpenSSL: . Fix a bunch of memory leaks and crashes on edge cases. (ndossche) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index dca5f607ad39..a2d964c15070 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4290,12 +4290,14 @@ static void preload_fix_trait_op_array(zend_op_array *op_array) uint32_t fn_flags = op_array->fn_flags; zend_function *prototype = op_array->prototype; HashTable *ht = op_array->static_variables; + const zend_property_info *prop_info = op_array->prop_info; *op_array = *orig_op_array; op_array->function_name = function_name; op_array->scope = scope; op_array->fn_flags = fn_flags; op_array->prototype = prototype; op_array->static_variables = ht; + op_array->prop_info = prop_info; } static void preload_fix_trait_methods(zend_class_entry *ce) diff --git a/ext/opcache/tests/gh21770.phpt b/ext/opcache/tests/gh21770.phpt new file mode 100644 index 000000000000..d2fd52a4712b --- /dev/null +++ b/ext/opcache/tests/gh21770.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-21770 (Infinite recursion in property hook getter in opcache preloaded trait) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/preload_gh21770.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- +a, "\n"; + +$c = new C(); +$c->x = 42; +var_dump($c->x); +?> +--EXPECT-- +a +int(42) diff --git a/ext/opcache/tests/preload_gh21770.inc b/ext/opcache/tests/preload_gh21770.inc new file mode 100644 index 000000000000..7433a15eca92 --- /dev/null +++ b/ext/opcache/tests/preload_gh21770.inc @@ -0,0 +1,20 @@ + $this->a; + } +} + +trait X { + public int $x = 0 { + set(int $value) => $value; + } +} + +class B { + use A; +} + +class C { + use X; +} From 4b945820f33d63269042c9839f15d5bd89899266 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Wed, 1 Jul 2026 11:45:59 -0300 Subject: [PATCH 010/211] Remove submodule dependency from web-php in release process (#22535) The submodule was problematic and removed in php/infrastructure#1928. Update the release process documentation to reflect this, or RMs (sorry...) will accidentally re-add the submodule following the process. Change suggested by @DanielEScherzer on Slack. --- docs/release-process.md | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 570107492ac3..4215002a7b4d 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -678,31 +678,7 @@ slightly different steps. We'll call attention where the steps differ. git push upstream master ``` -14. Switch to your local clone of the `web-php` repository and update the - `web-php-distributions` submodule. - - ```shell - cd /path/to/repos/php/web-php - git pull --rebase upstream master - git submodule init - git submodule update - cd distributions - git fetch --all - git pull --rebase upstream master - cd .. - git commit distributions -m "X.Y.Z tarballs" - git push upstream master - ``` - - > 💬 **Hint** \ - > This fetches the last commit ID from `web-php-distributions` and pins the - > "distributions" submodule in `web-php` to this commit ID. - > - > When the website syncs, which should happen within an hour, the tarballs - > will be available from `https://www.php.net/distributions/php-X.Y.Z.tar.gz`, - > etc. - -15. Once the release is tagged, contact the release-managers@php.net distribution +14. Once the release is tagged, contact the release-managers@php.net distribution list so that Windows binaries can be created. Once those are made, they may be found at https://windows.php.net/qa/. From 9c4f15ee6a43450e76b2bffee1bb3c71982f8744 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 1 Jul 2026 23:59:00 +0800 Subject: [PATCH 011/211] [skip ci] UPGRADING.md: Add missing BC break entry in several patches IntlIterator::current(): PR #22495 shmop_open() and shm_attach(): PR #22423 number_format(): PR #22435 --- UPGRADING | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/UPGRADING b/UPGRADING index fa9a816f7aed..eb6d182c54a7 100644 --- a/UPGRADING +++ b/UPGRADING @@ -40,6 +40,9 @@ PHP 8.6 UPGRADE NOTES - Intl: . Passing a non-stringable object as a time zone to Intl APIs that accept time zone objects or strings now raises a TypeError instead of an Error. + . IntlIterator::current() now returns null when called before the iterator is + positioned, or after the iterator becomes invalid, instead of exposing an + undefined value. . IntlBreakIterator::getLocale() now raises a ValueError when the type is neither Locale::ACTUAL_LOCALE nor Locale::VALID_LOCALE instead of returning false. @@ -126,6 +129,11 @@ PHP 8.6 UPGRADE NOTES to 1). RFC: https://wiki.php.net/rfc/session_security_defaults +- Shmop: + . shmop_open() now raises a ValueError when the $key argument is outside the + platform's key_t range instead of passing a truncated key to the operating + system. + - SOAP: . WSDL/XML Schema parsing now rejects out-of-range integer values for occurrence constraints and integer restriction facets. Negative minOccurs @@ -174,9 +182,16 @@ PHP 8.6 UPGRADE NOTES passed. . scandir() now raises a ValueError when an invalid $sorting_order argument value is passed. + . number_format() now raises a ValueError when $decimals is outside the + integer range instead of silently clamping very large positive values. . proc_open() now raises a ValueError when the $cwd argument contains NUL bytes. +- Sysvshm: + . shm_attach() now raises a ValueError when the $key argument is outside the + platform's key_t range instead of passing a truncated key to the operating + system. + - Zip: . ZipArchive::extractTo now raises a TypeError for the files argument if one or more of the entries is not a string. From 7c9fcfd7d8cfc5b9c2bb535ca8a1c268505bd370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 1 Jul 2026 21:13:29 +0200 Subject: [PATCH 012/211] zend_types: Convert `HT_*()` macros to inline functions (#22305) --- Zend/zend_types.h | 190 ++++++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 82 deletions(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 0edc4df37484..2cdfdcb1961f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -446,20 +446,31 @@ struct _zend_array { */ #if SIZEOF_SIZE_T == 4 # define HT_MAX_SIZE 0x02000000 -# define HT_HASH_TO_BUCKET_EX(data, idx) \ - ((Bucket*)((char*)(data) + (idx))) -# define HT_IDX_TO_HASH(idx) \ - ((idx) * sizeof(Bucket)) -# define HT_HASH_TO_IDX(idx) \ - ((idx) / sizeof(Bucket)) + +static zend_always_inline Bucket *HT_HASH_TO_BUCKET_EX(Bucket *data, uint32_t idx) { + return ((Bucket*)((char*)(data) + (idx))); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline uint32_t HT_IDX_TO_HASH(uint32_t idx) { + return idx * sizeof(Bucket); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline uint32_t HT_HASH_TO_IDX(uint32_t idx) { + return idx / sizeof(Bucket); +} #elif SIZEOF_SIZE_T == 8 # define HT_MAX_SIZE 0x40000000 -# define HT_HASH_TO_BUCKET_EX(data, idx) \ - ((data) + (idx)) -# define HT_IDX_TO_HASH(idx) \ - (idx) -# define HT_HASH_TO_IDX(idx) \ - (idx) +static zend_always_inline Bucket *HT_HASH_TO_BUCKET_EX(Bucket *data, uint32_t idx) { + return data + idx; +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline uint32_t HT_IDX_TO_HASH(uint32_t idx) { + return idx; +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline uint32_t HT_HASH_TO_IDX(uint32_t idx) { + return idx; +} #else # error "Unknown SIZEOF_SIZE_T" #endif @@ -469,81 +480,96 @@ struct _zend_array { #define HT_HASH(ht, idx) \ HT_HASH_EX((ht)->arHash, idx) -#define HT_SIZE_TO_MASK(nTableSize) \ - ((uint32_t)(-((nTableSize) + (nTableSize)))) -#define HT_HASH_SIZE(nTableMask) \ - (((size_t)-(uint32_t)(nTableMask)) * sizeof(uint32_t)) -#define HT_DATA_SIZE(nTableSize) \ - ((size_t)(nTableSize) * sizeof(Bucket)) -#define HT_SIZE_EX(nTableSize, nTableMask) \ - (HT_DATA_SIZE((nTableSize)) + HT_HASH_SIZE((nTableMask))) -#define HT_SIZE(ht) \ - HT_SIZE_EX((ht)->nTableSize, (ht)->nTableMask) -#define HT_USED_SIZE(ht) \ - (HT_HASH_SIZE((ht)->nTableMask) + ((size_t)(ht)->nNumUsed * sizeof(Bucket))) -#define HT_PACKED_DATA_SIZE(nTableSize) \ - ((size_t)(nTableSize) * sizeof(zval)) -#define HT_PACKED_SIZE_EX(nTableSize, nTableMask) \ - (HT_PACKED_DATA_SIZE((nTableSize)) + HT_HASH_SIZE((nTableMask))) -#define HT_PACKED_SIZE(ht) \ - HT_PACKED_SIZE_EX((ht)->nTableSize, (ht)->nTableMask) -#define HT_PACKED_USED_SIZE(ht) \ - (HT_HASH_SIZE((ht)->nTableMask) + ((size_t)(ht)->nNumUsed * sizeof(zval))) +ZEND_ATTRIBUTE_CONST static zend_always_inline uint32_t HT_SIZE_TO_MASK(uint32_t nTableSize) { + return (uint32_t)(-(nTableSize + nTableSize)); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline size_t HT_HASH_SIZE(uint32_t nTableMask) { + return ((size_t)(-nTableMask)) * sizeof(uint32_t); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline size_t HT_DATA_SIZE(uint32_t nTableSize) { + return ((size_t)nTableSize) * sizeof(Bucket); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline size_t HT_SIZE_EX(uint32_t nTableSize, uint32_t nTableMask) { + return HT_DATA_SIZE(nTableSize) + HT_HASH_SIZE(nTableMask); +} + +static zend_always_inline size_t HT_SIZE(const HashTable *ht) { + return HT_SIZE_EX(ht->nTableSize, ht->nTableMask); +} + +static zend_always_inline size_t HT_USED_SIZE(const HashTable *ht) { + return HT_HASH_SIZE(ht->nTableMask) + ((size_t)ht->nNumUsed * sizeof(Bucket)); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline size_t HT_PACKED_DATA_SIZE(uint32_t nTableSize) { + return ((size_t)nTableSize) * sizeof(zval); +} + +ZEND_ATTRIBUTE_CONST static zend_always_inline size_t HT_PACKED_SIZE_EX(uint32_t nTableSize, uint32_t nTableMask) { + return HT_PACKED_DATA_SIZE(nTableSize) + HT_HASH_SIZE(nTableMask); +} + +static zend_always_inline size_t HT_PACKED_SIZE(const HashTable *ht) { + return HT_PACKED_SIZE_EX(ht->nTableSize, ht->nTableMask); +} + +static zend_always_inline size_t HT_PACKED_USED_SIZE(const HashTable *ht) { + return HT_HASH_SIZE(ht->nTableMask) + ((size_t)ht->nNumUsed * sizeof(zval)); +} + +static zend_always_inline void HT_HASH_RESET(HashTable *ht) { + char *p = (char*)&HT_HASH(ht, ht->nTableMask); + size_t size = HT_HASH_SIZE(ht->nTableMask); + #if defined(__AVX2__) -# define HT_HASH_RESET(ht) do { \ - char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ - size_t size = HT_HASH_SIZE((ht)->nTableMask); \ - __m256i ymm0 = _mm256_setzero_si256(); \ - ymm0 = _mm256_cmpeq_epi64(ymm0, ymm0); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ - do { \ - _mm256_storeu_si256((__m256i*)p, ymm0); \ - _mm256_storeu_si256((__m256i*)(p+32), ymm0); \ - p += 64; \ - size -= 64; \ - } while (size != 0); \ - } while (0) + __m256i ymm0 = _mm256_setzero_si256(); + ymm0 = _mm256_cmpeq_epi64(ymm0, ymm0); + ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); + do { + _mm256_storeu_si256((__m256i*)p, ymm0); + _mm256_storeu_si256((__m256i*)(p+32), ymm0); + p += 64; + size -= 64; + } while (size != 0); #elif defined(__SSE2__) -# define HT_HASH_RESET(ht) do { \ - char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ - size_t size = HT_HASH_SIZE((ht)->nTableMask); \ - __m128i xmm0 = _mm_setzero_si128(); \ - xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ - do { \ - _mm_storeu_si128((__m128i*)p, xmm0); \ - _mm_storeu_si128((__m128i*)(p+16), xmm0); \ - _mm_storeu_si128((__m128i*)(p+32), xmm0); \ - _mm_storeu_si128((__m128i*)(p+48), xmm0); \ - p += 64; \ - size -= 64; \ - } while (size != 0); \ - } while (0) + __m128i xmm0 = _mm_setzero_si128(); + xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); + ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); + do { + _mm_storeu_si128((__m128i*)p, xmm0); + _mm_storeu_si128((__m128i*)(p+16), xmm0); + _mm_storeu_si128((__m128i*)(p+32), xmm0); + _mm_storeu_si128((__m128i*)(p+48), xmm0); + p += 64; + size -= 64; + } while (size != 0); #elif defined(__aarch64__) || defined(_M_ARM64) -# define HT_HASH_RESET(ht) do { \ - char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ - size_t size = HT_HASH_SIZE((ht)->nTableMask); \ - int32x4_t t = vdupq_n_s32(-1); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ - do { \ - vst1q_s32((int32_t*)p, t); \ - vst1q_s32((int32_t*)(p+16), t); \ - vst1q_s32((int32_t*)(p+32), t); \ - vst1q_s32((int32_t*)(p+48), t); \ - p += 64; \ - size -= 64; \ - } while (size != 0); \ - } while (0) + int32x4_t t = vdupq_n_s32(-1); + ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); + do { + vst1q_s32((int32_t*)p, t); + vst1q_s32((int32_t*)(p+16), t); + vst1q_s32((int32_t*)(p+32), t); + vst1q_s32((int32_t*)(p+48), t); + p += 64; + size -= 64; + } while (size != 0); #else -# define HT_HASH_RESET(ht) \ - memset(&HT_HASH(ht, (ht)->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE((ht)->nTableMask)) + memset(p, HT_INVALID_IDX, size); #endif -#define HT_HASH_RESET_PACKED(ht) do { \ - HT_HASH(ht, -2) = HT_INVALID_IDX; \ - HT_HASH(ht, -1) = HT_INVALID_IDX; \ - } while (0) -#define HT_HASH_TO_BUCKET(ht, idx) \ - HT_HASH_TO_BUCKET_EX((ht)->arData, idx) +} + +static zend_always_inline void HT_HASH_RESET_PACKED(HashTable *ht) { + HT_HASH(ht, -2) = HT_INVALID_IDX; + HT_HASH(ht, -1) = HT_INVALID_IDX; +} + +static zend_always_inline Bucket *HT_HASH_TO_BUCKET(HashTable *ht, uint32_t idx) { + return HT_HASH_TO_BUCKET_EX(ht->arData, idx); +} #define HT_SET_DATA_ADDR(ht, ptr) do { \ (ht)->arData = (Bucket*)(((char*)(ptr)) + HT_HASH_SIZE((ht)->nTableMask)); \ From fe53521e92eb141a3365df8e98eb41003cdf0887 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Wed, 27 May 2026 20:08:36 +0200 Subject: [PATCH 013/211] ext/session: fix GH-21314 (session GC behaviour is different since 8.4) Closes GH-22164 --- ext/session/php_session.h | 1 + ext/session/session.c | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ext/session/php_session.h b/ext/session/php_session.h index dfa7632e5a44..b52cf43dfba3 100644 --- a/ext/session/php_session.h +++ b/ext/session/php_session.h @@ -202,6 +202,7 @@ typedef struct _php_ps_globals { bool lazy_write; /* omit session write when it is possible */ bool in_save_handler; /* state if session is in save handler or not */ bool set_handler; /* state if session module i setting handler or not */ + bool random_seeded; zend_string *session_vars; /* serialized original session data */ } php_ps_globals; diff --git a/ext/session/session.c b/ext/session/session.c index 70e1673d87f5..ba71d709a536 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -394,6 +394,18 @@ static zend_long php_session_gc(bool immediate) /* {{{ */ /* GC must be done before reading session data. */ if ((PS(mod_data) || PS(mod_user_implemented))) { if (!collect && PS(gc_probability) > 0) { + /* Seed lazily on first GC draw per process. */ + if (UNEXPECTED(!PS(random_seeded))) { + php_random_uint128_t seed; + if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) { + seed = php_random_uint128_constant( + php_random_generate_fallback_seed(), + php_random_generate_fallback_seed() + ); + } + php_random_pcgoneseq128xslrr64_seed128(PS(random).state, seed); + PS(random_seeded) = true; + } collect = php_random_range(PS(random), 0, PS(gc_divisor) - 1) < PS(gc_probability); } @@ -2984,14 +2996,7 @@ static PHP_GINIT_FUNCTION(ps) /* {{{ */ .algo = &php_random_algo_pcgoneseq128xslrr64, .state = &ps_globals->random_state, }; - php_random_uint128_t seed; - if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) { - seed = php_random_uint128_constant( - php_random_generate_fallback_seed(), - php_random_generate_fallback_seed() - ); - } - php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed); + ps_globals->random_seeded = false; } /* }}} */ From 6317a472f4435af7e9869c9e594fa8680412430a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Fri, 26 Jun 2026 19:09:26 +0300 Subject: [PATCH 014/211] ext/exif: Fix GH-11020: spurious "Illegal IFD size" warning in exif_read_data() When an IFD is not followed by a 4-byte next-IFD offset, the EXIF block has no further IFD. Treat that as the end of the chain and return the parsed tags, instead of warning and discarding them. The bounds check from bug #72094 still applies, so the absent offset bytes are never read. Closes GH-22486 --- NEWS | 4 ++++ ext/exif/exif.c | 10 ++++++++-- ext/exif/tests/bug72094.phpt | 2 -- ext/exif/tests/gh11020.jpg | Bin 0 -> 663 bytes ext/exif/tests/gh11020.phpt | 12 ++++++++++++ 5 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 ext/exif/tests/gh11020.jpg create mode 100644 ext/exif/tests/gh11020.phpt diff --git a/NEWS b/NEWS index 012a28ffe6ce..766daa2ee517 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 +- Exif: + . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" + warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) + - Hash: . Fixed bug GH-18173 (ext/hash relies on implementation-defined malloc alignment). (iliaal) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 58e6d2801055..1b57377ffb89 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -3642,8 +3642,14 @@ static bool exif_process_IFD_in_JPEG(image_info_type *ImageInfo, char *dir_start * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202) to the thumbnail */ if (!exif_offset_info_contains(info, dir_start+2+NumDirEntries*12, 4)) { - exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Illegal IFD size"); - return false; + /* + * A TIFF/EXIF IFD ends with a 4-byte offset to the next IFD (IFD1 here, + * which links the thumbnail), or zero when there is none. Some files end + * the EXIF segment right after the entries and omit those 4 bytes. A + * missing offset is valid and just means there is no next IFD, so stop + * here instead of reporting the size as illegal. + */ + return true; } if (tag != TAG_EXIF_IFD_POINTER && tag != TAG_GPS_IFD_POINTER) { diff --git a/ext/exif/tests/bug72094.phpt b/ext/exif/tests/bug72094.phpt index c13a85f93f04..8fb3fa97c83d 100644 --- a/ext/exif/tests/bug72094.phpt +++ b/ext/exif/tests/bug72094.phpt @@ -47,8 +47,6 @@ Warning: exif_read_data(bug72094_3.jpg): Process tag(x3030=UndefinedTag): Illega Warning: exif_read_data(bug72094_3.jpg): Process tag(x3030=UndefinedTag): Illegal format code 0x3030, suppose BYTE in %s%ebug72094.php on line %d -Warning: exif_read_data(bug72094_3.jpg): Illegal IFD size in %s%ebug72094.php on line %d - Warning: exif_read_data(bug72094_3.jpg): File structure corrupted in %s%ebug72094.php on line %d Warning: exif_read_data(bug72094_3.jpg): Invalid JPEG file in %s%ebug72094.php on line %d diff --git a/ext/exif/tests/gh11020.jpg b/ext/exif/tests/gh11020.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f978a7576136efe54818072f84dcf2bb52ab4e4 GIT binary patch literal 663 zcmex=BhhaM)G;tL zF)@)>x3sk|ve7d(F#;LF$jQmc!_6bX%PV1|D5GdZGWdUhL68IFC}u__1|~s9WI1EBlCfRTxrg_Vt+gOiIJs9>uA0}~@NGZPClD=P~NP<1U(o`FS>RY=j$ zkxe)-kzJ`!#HexNLJno8jR!@8E`CrkPAY2R|V^&07y2J$~}^+4C1KUw!=a`ODXD z-+%o41@ado12e>1KoYCJ1cClyVqsxsVF&q(k*OSrnFU!`6%E;h90S=C3x$=88aYIq zCNA7~kW<+>=!0ld(M2vX6_bamA3 +--EXPECT-- +bool(true) +int(1) From a9c7dfefbdcab6b6d2a5c5692f5dcfb5676814ae Mon Sep 17 00:00:00 2001 From: alhudz Date: Wed, 10 Jun 2026 11:15:01 +0530 Subject: [PATCH 015/211] ext/dba: fix oob read on malformed length field in dba flatfile handler Closes GH-22266 --- ext/dba/libflatfile/flatfile.c | 60 ++++++++++++++--------------- ext/dba/tests/dba_flatfile_oob.phpt | 31 +++++++++++++++ 2 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 ext/dba/tests/dba_flatfile_oob.phpt diff --git a/ext/dba/libflatfile/flatfile.c b/ext/dba/libflatfile/flatfile.c index bd76ecfdd0ae..a68fed7e38c4 100644 --- a/ext/dba/libflatfile/flatfile.c +++ b/ext/dba/libflatfile/flatfile.c @@ -37,6 +37,18 @@ #define FLATFILE_BLOCK_SIZE 1024 +/* Parse the length prefix in `buf` into `num` and grow `buf` to hold it. + * atoi() narrows a malformed (e.g. negative) length to a huge size_t whose + * `+ FLATFILE_BLOCK_SIZE` would overflow erealloc(); the macro yields true in + * that case so the caller stops reading and the read stays within `buf_size`. */ +#define FLATFILE_GROW_BUF(num, buf, buf_size) ( \ + (num) = atoi(buf), \ + (num) >= (buf_size) && ( \ + (num) > SIZE_MAX - FLATFILE_BLOCK_SIZE \ + || ((buf) = erealloc((buf), (buf_size) = (num) + FLATFILE_BLOCK_SIZE), 0) \ + ) \ +) + /* * ret = -1 means that database was opened for read-only * ret = 0 success @@ -112,10 +124,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } pos = php_stream_tell(dba->fp); @@ -135,10 +145,8 @@ int flatfile_delete(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } /* read in the value */ num = php_stream_read(dba->fp, buf, num); @@ -162,10 +170,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); @@ -178,10 +184,8 @@ int flatfile_findkey(flatfile *dba, datum key_datum) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); } @@ -202,10 +206,8 @@ datum flatfile_firstkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); @@ -218,10 +220,8 @@ datum flatfile_firstkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); } @@ -244,20 +244,16 @@ datum flatfile_nextkey(flatfile *dba) { if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); if (!php_stream_gets(dba->fp, buf, 15)) { break; } - num = atoi(buf); - if (num >= buf_size) { - buf_size = num + FLATFILE_BLOCK_SIZE; - buf = erealloc(buf, buf_size); + if (FLATFILE_GROW_BUF(num, buf, buf_size)) { + break; } num = php_stream_read(dba->fp, buf, num); diff --git a/ext/dba/tests/dba_flatfile_oob.phpt b/ext/dba/tests/dba_flatfile_oob.phpt new file mode 100644 index 000000000000..3328e1dcba90 --- /dev/null +++ b/ext/dba/tests/dba_flatfile_oob.phpt @@ -0,0 +1,31 @@ +--TEST-- +DBA FlatFile handler bounds with a malformed (negative) length field +--EXTENSIONS-- +dba +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(false) +bool(false) +bool(false) +done From a80216b291c77f4b3cb653034d199f0e793e39ea Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 11:54:12 +0100 Subject: [PATCH 016/211] Update NEWS for bugfixes --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index 766daa2ee517..6ac3e8db3939 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 +- DBA: + . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) + - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) @@ -42,6 +45,10 @@ PHP NEWS . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore dynamic properties shadowing a private parent property). (iliaal) +- Session: + . Fixed bug GH-21314 (Different session garbage collector behavior between + PHP 8.3 and PHP 8.5). (jorgsowa) + - SPL: . Fix class_parents for classes with leading slash in non-autoload mode. (jorgsowa) From ce1d7873a58b0672971e8a50462daae7859cabda Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 12:03:35 +0100 Subject: [PATCH 017/211] ext/dba: prevent reallocation for info function (#22546) There is no need to duplicate the strings as they are all `const char*` pointers. Thus, just change the handle to return `const char*` --- ext/dba/dba.c | 4 +--- ext/dba/dba_cdb.c | 6 +++--- ext/dba/dba_db1.c | 2 +- ext/dba/dba_db2.c | 2 +- ext/dba/dba_db3.c | 2 +- ext/dba/dba_db4.c | 2 +- ext/dba/dba_dbm.c | 2 +- ext/dba/dba_flatfile.c | 2 +- ext/dba/dba_gdbm.c | 2 +- ext/dba/dba_inifile.c | 2 +- ext/dba/dba_lmdb.c | 2 +- ext/dba/dba_ndbm.c | 2 +- ext/dba/dba_qdbm.c | 2 +- ext/dba/dba_tcadb.c | 2 +- ext/dba/php_dba.h | 4 ++-- 15 files changed, 18 insertions(+), 20 deletions(-) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index c0688714fe7c..a9114753a8a6 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -1282,10 +1282,8 @@ PHP_FUNCTION(dba_handlers) for (const dba_handler *hptr = handler; hptr->name; hptr++) { if (full_info) { - // TODO: avoid reallocation ??? - char *str = hptr->info(hptr, NULL); + const char *str = hptr->info(hptr, NULL); add_assoc_string(return_value, hptr->name, str); - efree(str); } else { add_next_index_string(return_value, hptr->name); } diff --git a/ext/dba/dba_cdb.c b/ext/dba/dba_cdb.c index 79b60c765c37..3578866b546c 100644 --- a/ext/dba/dba_cdb.c +++ b/ext/dba/dba_cdb.c @@ -319,12 +319,12 @@ DBA_INFO_FUNC(cdb) { #ifdef DBA_CDB_BUILTIN if (!strcmp(hnd->name, "cdb")) { - return estrdup(cdb_version()); + return cdb_version(); } else { - return estrdup(cdb_make_version()); + return cdb_make_version(); } #else - return estrdup("External"); + return "External"; #endif } diff --git a/ext/dba/dba_db1.c b/ext/dba/dba_db1.c index dfcd637c8f87..59846c90a4c8 100644 --- a/ext/dba/dba_db1.c +++ b/ext/dba/dba_db1.c @@ -177,7 +177,7 @@ DBA_SYNC_FUNC(db1) DBA_INFO_FUNC(db1) { - return estrdup(DB1_VERSION); + return DB1_VERSION; } #endif diff --git a/ext/dba/dba_db2.c b/ext/dba/dba_db2.c index dd723ea40af8..50893d8458c8 100644 --- a/ext/dba/dba_db2.c +++ b/ext/dba/dba_db2.c @@ -187,7 +187,7 @@ DBA_SYNC_FUNC(db2) DBA_INFO_FUNC(db2) { - return estrdup(DB_VERSION_STRING); + return DB_VERSION_STRING; } #endif diff --git a/ext/dba/dba_db3.c b/ext/dba/dba_db3.c index aacc65dda5a5..d4111e8c41fc 100644 --- a/ext/dba/dba_db3.c +++ b/ext/dba/dba_db3.c @@ -225,7 +225,7 @@ DBA_SYNC_FUNC(db3) DBA_INFO_FUNC(db3) { - return estrdup(DB_VERSION_STRING); + return DB_VERSION_STRING; } #endif diff --git a/ext/dba/dba_db4.c b/ext/dba/dba_db4.c index 16ac9be37a13..fbd5f8191b44 100644 --- a/ext/dba/dba_db4.c +++ b/ext/dba/dba_db4.c @@ -282,7 +282,7 @@ DBA_SYNC_FUNC(db4) DBA_INFO_FUNC(db4) { - return estrdup(DB_VERSION_STRING); + return DB_VERSION_STRING; } #endif diff --git a/ext/dba/dba_dbm.c b/ext/dba/dba_dbm.c index 9579444454ce..73f137e9891f 100644 --- a/ext/dba/dba_dbm.c +++ b/ext/dba/dba_dbm.c @@ -192,7 +192,7 @@ DBA_INFO_FUNC(dbm) return dba_info_gdbm(hnd, info); } #endif - return estrdup(DBM_VERSION); + return DBM_VERSION; } #endif diff --git a/ext/dba/dba_flatfile.c b/ext/dba/dba_flatfile.c index 4208277a920d..5e8bae223f9e 100644 --- a/ext/dba/dba_flatfile.c +++ b/ext/dba/dba_flatfile.c @@ -167,7 +167,7 @@ DBA_SYNC_FUNC(flatfile) DBA_INFO_FUNC(flatfile) { - return estrdup(flatfile_version()); + return flatfile_version(); } #endif diff --git a/ext/dba/dba_gdbm.c b/ext/dba/dba_gdbm.c index 961f539a451f..64877eb289c4 100644 --- a/ext/dba/dba_gdbm.c +++ b/ext/dba/dba_gdbm.c @@ -189,7 +189,7 @@ DBA_SYNC_FUNC(gdbm) DBA_INFO_FUNC(gdbm) { - return estrdup(gdbm_version); + return gdbm_version; } #endif diff --git a/ext/dba/dba_inifile.c b/ext/dba/dba_inifile.c index 1539bb0496dd..bf5ef322759d 100644 --- a/ext/dba/dba_inifile.c +++ b/ext/dba/dba_inifile.c @@ -186,7 +186,7 @@ DBA_SYNC_FUNC(inifile) DBA_INFO_FUNC(inifile) { - return estrdup(inifile_version()); + return inifile_version(); } #endif diff --git a/ext/dba/dba_lmdb.c b/ext/dba/dba_lmdb.c index 57e74f320239..6e6a78ad5e24 100644 --- a/ext/dba/dba_lmdb.c +++ b/ext/dba/dba_lmdb.c @@ -360,7 +360,7 @@ DBA_SYNC_FUNC(lmdb) DBA_INFO_FUNC(lmdb) { - return estrdup(MDB_VERSION_STRING); + return MDB_VERSION_STRING; } #endif diff --git a/ext/dba/dba_ndbm.c b/ext/dba/dba_ndbm.c index 2b4002591e91..c60ab7e5f428 100644 --- a/ext/dba/dba_ndbm.c +++ b/ext/dba/dba_ndbm.c @@ -147,7 +147,7 @@ DBA_SYNC_FUNC(ndbm) DBA_INFO_FUNC(ndbm) { - return estrdup("NDBM"); + return "NDBM"; } #endif diff --git a/ext/dba/dba_qdbm.c b/ext/dba/dba_qdbm.c index 8e692b7e55bc..cc97cd9757f4 100644 --- a/ext/dba/dba_qdbm.c +++ b/ext/dba/dba_qdbm.c @@ -173,7 +173,7 @@ DBA_SYNC_FUNC(qdbm) DBA_INFO_FUNC(qdbm) { - return estrdup(dpversion); + return dpversion; } #endif diff --git a/ext/dba/dba_tcadb.c b/ext/dba/dba_tcadb.c index 0539a7036602..5f4ed5eeabdc 100644 --- a/ext/dba/dba_tcadb.c +++ b/ext/dba/dba_tcadb.c @@ -193,7 +193,7 @@ DBA_SYNC_FUNC(tcadb) DBA_INFO_FUNC(tcadb) { - return estrdup(tcversion); + return tcversion; } #endif diff --git a/ext/dba/php_dba.h b/ext/dba/php_dba.h index 86c8a4f0ed65..38ad3ea2820b 100644 --- a/ext/dba/php_dba.h +++ b/ext/dba/php_dba.h @@ -89,7 +89,7 @@ typedef struct dba_handler { zend_string* (*nextkey)(dba_info *); zend_result (*optimize)(dba_info *); zend_result (*sync)(dba_info *); - char* (*info)(const struct dba_handler *hnd, dba_info *); + const char* (*info)(const struct dba_handler *hnd, dba_info *); /* dba_info==NULL: Handler info, dba_info!=NULL: Database info */ } dba_handler; @@ -116,7 +116,7 @@ typedef struct dba_handler { #define DBA_SYNC_FUNC(x) \ zend_result dba_sync_##x(dba_info *info) #define DBA_INFO_FUNC(x) \ - char *dba_info_##x(const dba_handler *hnd, dba_info *info) + const char *dba_info_##x(const dba_handler *hnd, dba_info *info) #define DBA_FUNCS(x) \ DBA_OPEN_FUNC(x); \ From 04b63f9f67d05f79c4c0d8f180efb86b11a98713 Mon Sep 17 00:00:00 2001 From: Matteo Beccati Date: Thu, 2 Jul 2026 13:16:30 +0200 Subject: [PATCH 018/211] Added missing NEWS entries for alpha2 --- NEWS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/NEWS b/NEWS index 5f3ea1ff3f61..aa6f7f81a89d 100644 --- a/NEWS +++ b/NEWS @@ -9,13 +9,27 @@ PHP NEWS . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) +- Opcache: + . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache + preloaded trait). (iliaal) + - OpenSSL: + . Fixed timeout for supplemental read at end of a blocking stream in SSL + stream wrapper. (ilutov) . Fixed stream_socket_get_crypto_status() after supplemental read. (ilutov) +- PDO_ODBC: + . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN + carries no credentials). (iliaal) + - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between PHP 8.3 and PHP 8.5). (jorgsowa) +- Streams: + . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL + and a proxy set). (CVE-2026-12184) (ndossche) + 02 Jul 2026, PHP 8.6.0alpha1 - Core: From eb9eaa2652b26e308e5678864e284352b5b03ada Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 3 Jul 2026 00:12:45 +0800 Subject: [PATCH 019/211] [ci skip] Update EXTENSIONS status and update CODEOWNERS --- .github/CODEOWNERS | 2 ++ EXTENSIONS | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2d8b0d1c16f1..c5129bdf7426 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -43,6 +43,7 @@ /ext/pdo_pgsql @devnexen @SakiTakamachi /ext/pdo_sqlite @SakiTakamachi /ext/pgsql @devnexen +/ext/phar @LamentXU123 /ext/random @TimWolla @zeriyoshi /ext/reflection @DanielEScherzer /ext/session @Girgias @@ -55,6 +56,7 @@ /ext/xmlreader @devnexen /ext/xmlwriter @devnexen /ext/xsl @devnexen +/ext/zip @LamentXU123 /main @bukka /sapi/fpm @bukka /Zend/Optimizer @dstogov diff --git a/EXTENSIONS b/EXTENSIONS index f0b3812a0ccd..40975c411a7a 100644 --- a/EXTENSIONS +++ b/EXTENSIONS @@ -402,6 +402,7 @@ PRIMARY MAINTAINER: Greg Beaver (2008 - 2009) Marcus Börger (2008 - 2008) Steph Fox (2008 - 2008) Bishop Bettini (2018 - 2019) + Weilin Du (2026 - 2026) MAINTENANCE: Maintained STATUS: Working SINCE: 5.3 @@ -499,7 +500,8 @@ STATUS: Working ------------------------------------------------------------------------------- EXTENSION: zip PRIMARY MAINTAINER: Pierre-Alain Joye (2006 - 2011) - Remi Collet (2013-2020) + Remi Collet (2013 - 2020) + Weilin Du (2026 - 2026) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- From aae5edb8e360e5d08d331cce4556ba4cf1ed3238 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 2 Jul 2026 23:40:25 +0200 Subject: [PATCH 020/211] Implement perf stat --control for cgi (GH-22537) perf stat --control allows the profiled process to enable and disable counters at runtime. This allows us to skip profiling startup and shutdown for more accurate results. The same already exists for valgrind. $ perf stat -D -1 --control fifo:/tmp/perfctl,/tmp/perfack -D -1 starts perf stat with counters disabled. --control makes perf stat connect to the /tmp/perfctl and /tmp/perfack fifo files. These need to exist when starting perf, so create them using mkfifo /tmp/perf{ctl,ack}. The ctl fifo is written to by cgi to enable/disable counters, whereas the ack fifo is written to by perf stat to acknowledge counters have been installed. Additionally, you'll need to set the set the PERF_STAT_CTL_FIFO and PERF_STAT_ACK_FIFO env variables for cgi to find the fifo files. --- Zend/zend_perf_stat.h | 135 ++++++++++++++++++++++++++++++++++++++++++ sapi/cgi/cgi_main.c | 13 +++- 2 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Zend/zend_perf_stat.h diff --git a/Zend/zend_perf_stat.h b/Zend/zend_perf_stat.h new file mode 100644 index 000000000000..447315de0133 --- /dev/null +++ b/Zend/zend_perf_stat.h @@ -0,0 +1,135 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Ilija Tovilo | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_PERF_STAT_H +#define ZEND_PERF_STAT_H + +#include "zend_portability.h" + +# if !(HAVE_FCNTL_H && HAVE_SYS_SELECT_H && HAVE_SYS_STAT_H && HAVE_SYS_TIME_H && HAVE_UNISTD_H) +static void zend_perf_stat_enable(void) {} +static void zend_perf_stat_disable(void) {} +# else + +# include +# include +# include +# include + +# include "Zend/zend.h" + +# define ZPS_CTL_FIFO_ENV "PERF_STAT_CTL_FIFO" +# define ZPS_ACK_FIFO_ENV "PERF_STAT_ACK_FIFO" + +static int ctl_fd = -2; +static int ack_fd = -2; + +static int zps_open_fifo(const char *env_name, int flags) +{ + const char *path = getenv(env_name); + + if (path == NULL || path[0] == '\0') { + return -1; + } + +# ifdef O_CLOEXEC + flags |= O_CLOEXEC; +# endif + int fd = open(path, flags | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Failed to open fifo %s\n", path); + fflush(stderr); + zend_bailout(); + } + + struct stat st; + if (fstat(fd, &st) != 0 || !S_ISFIFO(st.st_mode)) { + close(fd); + fprintf(stderr, "File %s is not a fifo\n", path); + fflush(stderr); + zend_bailout(); + } + return fd; +} + +static void zps_init(void) +{ + if (ctl_fd == -2) { + ctl_fd = zps_open_fifo(ZPS_CTL_FIFO_ENV, O_WRONLY); + } + if (ack_fd == -2) { + ack_fd = zps_open_fifo(ZPS_ACK_FIFO_ENV, O_RDONLY); + } +} + +static void zps_wait_ack(void) +{ + if (ack_fd < 0) { + return; + } + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(ack_fd, &readfds); + if (select(ack_fd + 1, &readfds, NULL, NULL, &timeout) <= 0) { + return; + } + + char ack[sizeof("ack\n") - 1]; + ssize_t bytes_read; + do { + bytes_read = read(ack_fd, ack, sizeof(ack)); + } while (bytes_read > 0); +} + +static void zps_control(const char *command, size_t command_len) +{ + zps_init(); + + if (ctl_fd < 0) { + return; + } + + if (write(ctl_fd, command, command_len) != (ssize_t) command_len) { + close(ctl_fd); + ctl_fd = -1; + return; + } + + zps_wait_ack(); +} + +static void zend_perf_stat_enable(void) +{ + static const char command[] = "enable\n"; + + zps_control(command, sizeof(command) - 1); +} + +static void zend_perf_stat_disable(void) +{ + static const char command[] = "disable\n"; + + zps_control(command, sizeof(command) - 1); +} + +# endif +#endif diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index f49507827e2d..7efd8f159d59 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -95,6 +95,8 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS; # endif #endif +#include "zend_perf_stat.h" + #ifndef PHP_WIN32 /* XXX this will need to change later when threaded fastcgi is implemented. shane */ static struct sigaction act, old_term, old_quit, old_int; @@ -1733,6 +1735,7 @@ int main(int argc, char *argv[]) int warmup_repeats = 0; int repeats = 1; int benchmark = 0; + bool perf_enabled = false; #ifdef HAVE_GETTIMEOFDAY struct timeval start, end; #else @@ -2441,14 +2444,16 @@ consult the installation file that came with this distribution, or visit \n\ } } /* end !cgi && !fastcgi */ -#ifdef HAVE_VALGRIND if (warmup_repeats == 0) { + zend_perf_stat_enable(); + perf_enabled = true; +#ifdef HAVE_VALGRIND CALLGRIND_START_INSTRUMENTATION; # ifdef HAVE_VALGRIND_CACHEGRIND_H CACHEGRIND_START_INSTRUMENTATION; # endif - } #endif + } /* request startup only after we've done all we can to * get path_translated */ @@ -2568,6 +2573,10 @@ consult the installation file that came with this distribution, or visit \n\ SG(request_info).query_string = NULL; } + if (perf_enabled) { + zend_perf_stat_disable(); + perf_enabled = false; + } #ifdef HAVE_VALGRIND /* We're not interested in measuring shutdown */ CALLGRIND_STOP_INSTRUMENTATION; From 76dad0667f7a756aa65bcc1d26cbe6cf87fcdca0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:30:46 -0700 Subject: [PATCH 021/211] [8.5] NEWS: add entry for bugfix from GH-22187 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index fceef3f9a3c1..40cbc17db654 100644 --- a/NEWS +++ b/NEWS @@ -122,6 +122,10 @@ PHP NEWS . Fixed bug GH-21972 (Corrupted variable type when a typed by-value return contains a reference wrapper). (Weilin Du) +- OpenSSL: + . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in + openssl_encrypt with AES-WRAP-PAD). (David Carlier) + - Phar: . Fixed a bypass of the magic ".phar" directory protection in Phar::addEmptyDir() for paths starting with "/.phar", while allowing From 39b66bae333eabb173476d09de1106890bc94980 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Fri, 3 Jul 2026 01:53:33 +0200 Subject: [PATCH 022/211] Use zend_hash_find_ptr_lc() for case-insensitive name lookups (#22565) A number of call sites lowercased a class/function/method name into a temporary string solely to use it as a lookup key in the class table, function table, module registry, etc., then released it. Replace those with zend_hash_find_ptr_lc() / zend_hash_str_find_ptr_lc(), which lowercase into a stack buffer (for short names), perform the lookup, and free the temporary internally. This removes the manual tolower + release boilerplate at 17 sites and avoids a heap allocation for the common short-name case. No behavior change. --- Zend/Optimizer/zend_optimizer.c | 8 ++------ Zend/zend_builtin_functions.c | 10 ++-------- Zend/zend_inheritance.c | 19 ++++--------------- ext/hash/hash.c | 4 +--- ext/opcache/ZendAccelerator.c | 4 +--- ext/pdo/pdo_dbh.c | 5 +---- ext/reflection/php_reflection.c | 5 +---- sapi/cli/php_cli.c | 4 +--- sapi/phpdbg/phpdbg_bp.c | 18 +++--------------- sapi/phpdbg/phpdbg_list.c | 5 +---- 10 files changed, 17 insertions(+), 65 deletions(-) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index d10b4d83fc3e..0173d2ef47a2 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -104,9 +104,7 @@ zend_result zend_optimizer_eval_special_func_call( zval *result, const zend_string *name, zend_string *arg) { if (zend_string_equals_literal(name, "function_exists") || zend_string_equals_literal(name, "is_callable")) { - zend_string *lc_name = zend_string_tolower(arg); - const zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name); - zend_string_release_ex(lc_name, 0); + const zend_internal_function *func = zend_hash_find_ptr_lc(EG(function_table), arg); if (func && func->type == ZEND_INTERNAL_FUNCTION && func->module->type == MODULE_PERSISTENT @@ -120,9 +118,7 @@ zend_result zend_optimizer_eval_special_func_call( return FAILURE; } if (zend_string_equals_literal(name, "extension_loaded")) { - zend_string *lc_name = zend_string_tolower(arg); - zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name); - zend_string_release_ex(lc_name, 0); + zend_module_entry *m = zend_hash_find_ptr_lc(&module_registry, arg); if (!m) { if (PG(enable_dl)) { diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 2dceac2512db..87acf073a2da 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -953,7 +953,6 @@ ZEND_FUNCTION(method_exists) { zval *klass; zend_string *method_name; - zend_string *lcname; zend_class_entry *ce; zend_function *func; @@ -974,9 +973,7 @@ ZEND_FUNCTION(method_exists) RETURN_THROWS(); } - lcname = zend_string_tolower(method_name); - func = zend_hash_find_ptr(&ce->function_table, lcname); - zend_string_release_ex(lcname, 0); + func = zend_hash_find_ptr_lc(&ce->function_table, method_name); if (func) { /* Exclude shadow properties when checking a method on a specific class. Include @@ -2219,7 +2216,6 @@ ZEND_FUNCTION(extension_loaded) ZEND_FUNCTION(get_extension_funcs) { zend_string *extension_name; - zend_string *lcname; bool array; zend_module_entry *module; zend_function *zif; @@ -2228,9 +2224,7 @@ ZEND_FUNCTION(get_extension_funcs) RETURN_THROWS(); } if (strncasecmp(ZSTR_VAL(extension_name), "zend", sizeof("zend"))) { - lcname = zend_string_tolower(extension_name); - module = zend_hash_find_ptr(&module_registry, lcname); - zend_string_release_ex(lcname, 0); + module = zend_hash_find_ptr_lc(&module_registry, extension_name); } else { module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core") - 1); } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 8df6a5599d30..848b9d209b2d 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -264,11 +264,7 @@ static zend_class_entry *lookup_class_ex( bool in_preload = CG(compiler_options) & ZEND_COMPILE_PRELOAD; if (UNEXPECTED(!EG(active) && !in_preload)) { - zend_string *lc_name = zend_string_tolower(name); - - ce = zend_hash_find_ptr(CG(class_table), lc_name); - - zend_string_release(lc_name); + ce = zend_hash_find_ptr_lc(CG(class_table), name); if (register_unresolved && !ce) { zend_error_noreturn( @@ -2530,7 +2526,6 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e size_t i, j = 0; zend_trait_precedence *cur_precedence; zend_trait_method_reference *cur_method_ref; - zend_string *lc_trait_name; zend_string *lcname; HashTable **exclude_tables = NULL; zend_class_entry **aliases = NULL; @@ -2545,9 +2540,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e while ((cur_precedence = precedences[i])) { /** Resolve classes for all precedence operations. */ cur_method_ref = &cur_precedence->trait_method; - lc_trait_name = zend_string_tolower(cur_method_ref->class_name); - trait = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name); if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name)); } @@ -2574,9 +2567,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e zend_class_entry *exclude_ce; uint32_t trait_num; - lc_trait_name = zend_string_tolower(class_name); - exclude_ce = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + exclude_ce = zend_hash_find_ptr_lc(EG(class_table), class_name); if (!exclude_ce || !(exclude_ce->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(class_name)); } @@ -2619,9 +2610,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e lcname = zend_string_tolower(cur_method_ref->method_name); if (cur_method_ref->class_name) { /* For all aliases with an explicit class name, resolve the class now. */ - lc_trait_name = zend_string_tolower(cur_method_ref->class_name); - trait = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name); if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name)); } diff --git a/ext/hash/hash.c b/ext/hash/hash.c index e2af98c81ddf..74abf02acd81 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -102,9 +102,7 @@ static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = { PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo) /* {{{ */ { - zend_string *lower = zend_string_tolower(algo); - const php_hash_ops *ops = zend_hash_find_ptr(&php_hash_hashtable, lower); - zend_string_release(lower); + const php_hash_ops *ops = zend_hash_find_ptr_lc(&php_hash_hashtable, algo); return ops; } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 83e933b01181..6f606d9d37d5 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3847,9 +3847,7 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e memset(error, 0, sizeof(preload_error)); if (ce->parent_name) { - zend_string *key = zend_string_tolower(ce->parent_name); - const zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key); - zend_string_release(key); + const zend_class_entry *parent = zend_hash_find_ptr_lc(EG(class_table), ce->parent_name); if (!parent) { error->kind = "Unknown parent "; error->name = ZSTR_VAL(ce->parent_name); diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 6a47ec30c862..dcd9f7b126df 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1484,7 +1484,6 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n { zend_function *fbc = NULL; pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object); - zend_string *lc_method_name; if ((fbc = zend_std_get_method(object, method_name, key)) == NULL) { /* not a pre-defined method, nor a user-defined method; check @@ -1497,9 +1496,7 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n } } - lc_method_name = zend_string_tolower(method_name); - fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name); - zend_string_release_ex(lc_method_name, 0); + fbc = zend_hash_find_ptr_lc(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], method_name); } out: diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a9f6b579977e..37c45cb02168 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -859,7 +859,6 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze { smart_str param_indent = {0}; zend_function *overwrites; - zend_string *lc_name; /* TBD: Repair indenting of doc comment (or is this to be done in the parser?) * What's "wrong" is that any whitespace before the doc comment start is @@ -885,13 +884,11 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze if (fptr->common.scope != scope) { smart_str_append_printf(str, ", inherits %s", ZSTR_VAL(fptr->common.scope->name)); } else if (fptr->common.scope->parent) { - lc_name = zend_string_tolower(fptr->common.function_name); - if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) { + if ((overwrites = zend_hash_find_ptr_lc(&fptr->common.scope->parent->function_table, fptr->common.function_name)) != NULL) { if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) { smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name)); } } - zend_string_release_ex(lc_name, 0); } } if (fptr->common.prototype && fptr->common.prototype->common.scope) { diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 9a05f8e68547..e5e573d1533c 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1093,10 +1093,9 @@ static int do_cli(int argc, char **argv) /* {{{ */ case PHP_CLI_MODE_REFLECTION_EXT_INFO: { size_t len = strlen(reflection_what); - char *lcname = zend_str_tolower_dup(reflection_what, len); zend_module_entry *module; - if ((module = zend_hash_str_find_ptr(&module_registry, lcname, len)) == NULL) { + if ((module = zend_hash_str_find_ptr_lc(&module_registry, reflection_what, len)) == NULL) { if (!strcmp(reflection_what, "main")) { display_ini_entries(NULL); } else { @@ -1107,7 +1106,6 @@ static int do_cli(int argc, char **argv) /* {{{ */ php_info_print_module(module); } - efree(lcname); break; } diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index 4dfa89d4b0fe..b9c3436280cf 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -966,13 +966,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *f } if (ops->function_name) { - phpdbg_breakbase_t *brake; - zend_string *fname = zend_string_tolower(ops->function_name); - - brake = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname); - - zend_string_release(fname); - return brake; + return zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ops->function_name); } else { return zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ZEND_STRL("main")); } @@ -982,17 +976,11 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *o { HashTable *class_table; phpdbg_breakbase_t *brake = NULL; - zend_string *class_lcname = zend_string_tolower(ops->scope->name); - - if ((class_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname))) { - zend_string *lcname = zend_string_tolower(ops->function_name); - - brake = zend_hash_find_ptr(class_table, lcname); - zend_string_release(lcname); + if ((class_table = zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], ops->scope->name))) { + brake = zend_hash_find_ptr_lc(class_table, ops->function_name); } - zend_string_release(class_lcname); return brake; } /* }}} */ diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c index 505b04e81f91..3931fc9c1525 100644 --- a/sapi/phpdbg/phpdbg_list.c +++ b/sapi/phpdbg/phpdbg_list.c @@ -88,15 +88,12 @@ PHPDBG_LIST(method) /* {{{ */ if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) { zend_function *function; - char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); - if ((function = zend_hash_str_find_ptr(&ce->function_table, lcname, strlen(lcname)))) { + if ((function = zend_hash_str_find_ptr_lc(&ce->function_table, param->method.name, strlen(param->method.name)))) { phpdbg_list_function(function); } else { phpdbg_error("Could not find %s::%s", param->method.class, param->method.name); } - - efree(lcname); } else { phpdbg_error("Could not find the class %s", param->method.class); } From b6938a25ee7dcce21f5386668e4a8ba58f2330a6 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Thu, 2 Jul 2026 20:11:28 +0200 Subject: [PATCH 023/211] phpdbg: fix leaked lowercased lookup keys in phpdbg_resolve_opline_break phpdbg_resolve_opline_break() passed the result of zend_str_tolower_dup() directly as the lookup key to zend_hash_str_find_ptr() for both the class and the function/method lookups. That emalloc'd buffer was never freed, leaking on every method/function opline breakpoint resolution. Use zend_hash_str_find_ptr_lc(), which lowercases into a (stack-allocated for short names) temporary and frees it internally, instead of duplicating the key by hand and leaking it. Add a regression test that exercises all three paths through the two lookups (class + method found, method not found, class not found). Closes GH-22563 --- NEWS | 2 ++ sapi/phpdbg/phpdbg_bp.c | 4 +-- .../phpdbg_resolve_opline_break_leak.phpt | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt diff --git a/NEWS b/NEWS index 6ac3e8db3939..1edc25b4899d 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,8 @@ PHP NEWS - PHPDBG: . Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal) + . Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break. + (jorgsowa) - Reflection: . Fixed bug GH-22324 (Ignore leading namespace separator in diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index 90f8ed7bb129..361e4635f1f9 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -604,13 +604,13 @@ PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break) /* { if (new_break->class_name != NULL) { zend_class_entry *ce; - if (!(ce = zend_hash_str_find_ptr(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len))) { + if (!(ce = zend_hash_str_find_ptr_lc(EG(class_table), new_break->class_name, new_break->class_len))) { return FAILURE; } func_table = &ce->function_table; } - if (!(func = zend_hash_str_find_ptr(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len))) { + if (!(func = zend_hash_str_find_ptr_lc(func_table, new_break->func_name, new_break->func_len))) { if (new_break->class_name != NULL && new_break->func_name != NULL) { phpdbg_error("Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name); return 2; diff --git a/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt new file mode 100644 index 000000000000..411697cee021 --- /dev/null +++ b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt @@ -0,0 +1,26 @@ +--TEST-- +Resolving method/function opline breakpoints must not leak the lookup keys +--PHPDBG-- +b Foo::bar#0 +b baz#0 +b Foo::nope#0 +b Nope::bar#0 +q +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at Foo::bar#0] +prompt> [Breakpoint #1 added at baz#0] +prompt> [Method nope doesn't exist in class Foo] +prompt> [Pending breakpoint #3 at Nope::bar#0] +prompt> +--FILE-- + Date: Fri, 3 Jul 2026 14:14:25 +0800 Subject: [PATCH 024/211] Fix GH-21705: ZipArchive::getFromIndex() ignores FL_UNCHANGED (#21706) ZipArchive::getFromIndex() already passed flags to zip_fopen_index(), but it used an unflagged stat call to determine the entry size. Use the same flags for the stat lookup so deleted entries can still be read with ZipArchive::FL_UNCHANGED, matching ZipArchive::getFromName(). Closes #21706 --- NEWS | 4 ++++ ext/zip/php_zip.c | 2 +- ext/zip/tests/bug_gh21705.phpt | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 ext/zip/tests/bug_gh21705.phpt diff --git a/NEWS b/NEWS index 1edc25b4899d..6d4d1865d790 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,10 @@ PHP NEWS . Fixed bug GH-22395 (base_convert() outputs at most 64 characters). (Weilin Du) +- Zip: + . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores + ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du) + 02 Jul 2026, PHP 8.4.23 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index b29ba2c370b8..16df403dcda5 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2939,7 +2939,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ ZIP_FROM_OBJECT(intern, self); - PHP_ZIP_STAT_INDEX(intern, index, 0, sb); + PHP_ZIP_STAT_INDEX(intern, index, flags, sb); } if (sb.size < 1) { diff --git a/ext/zip/tests/bug_gh21705.phpt b/ext/zip/tests/bug_gh21705.phpt new file mode 100644 index 000000000000..41dabb224510 --- /dev/null +++ b/ext/zip/tests/bug_gh21705.phpt @@ -0,0 +1,33 @@ +--TEST-- +ZipArchive::getFromIndex() honors FL_UNCHANGED for deleted entries +--EXTENSIONS-- +zip +--FILE-- +open($name, ZipArchive::CREATE); +$zip->addFromString('foo.txt', 'foo'); +$zip->addFromString('bar.txt', 'bar'); +$zip->close(); + +$zip = new ZipArchive; +$zip->open($name); + +$index = $zip->locateName('bar.txt'); +$zip->deleteName('bar.txt'); + +var_dump($zip->getFromName('bar.txt', 0, ZipArchive::FL_UNCHANGED)); +var_dump($zip->getFromIndex($index, 0, ZipArchive::FL_UNCHANGED)); + +$zip->close(); +?> +--CLEAN-- + +--EXPECT-- +string(3) "bar" +string(3) "bar" From 14a7eadfb25508af19ea831e51e66dc32a7b871e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 3 Jul 2026 18:12:50 +0800 Subject: [PATCH 025/211] ext/intl: Fix NumberFormatter parse offset overflow --- ext/intl/formatter/formatter_parse.cpp | 16 +++++-- .../formatter_parse_offset_overflow.phpt | 46 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 ext/intl/tests/formatter_parse_offset_overflow.phpt diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index a475960809b8..7aa92488704a 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -50,7 +50,12 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) } if (zposition) { - position = (int32_t) zval_get_long(zposition); + zend_long long_position = zval_get_long(zposition); + if (ZEND_LONG_EXCEEDS_INT(long_position)) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } /* Fetch the object. */ @@ -155,8 +160,13 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); - if(zposition) { - position = (int32_t) zval_get_long(zposition); + if (zposition) { + zend_long long_position = zval_get_long(zposition); + if (ZEND_LONG_EXCEEDS_INT(long_position)) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } icu::ParsePosition pp(position); diff --git a/ext/intl/tests/formatter_parse_offset_overflow.phpt b/ext/intl/tests/formatter_parse_offset_overflow.phpt new file mode 100644 index 000000000000..9421c8897511 --- /dev/null +++ b/ext/intl/tests/formatter_parse_offset_overflow.phpt @@ -0,0 +1,46 @@ +--TEST-- +NumberFormatter parse offset overflow +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; + } +} + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + $fmt->parse('123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + numfmt_parse($fmt, '123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + $currencyFmt->parseCurrency('$123.00', $currency, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + numfmt_parse_currency($currencyFmt, '$123.00', $currency, $offset); +}); +?> +--EXPECT-- +ValueError: NumberFormatter::parse(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse(): Argument #4 ($offset) must be between -2147483648 and 2147483647 +ValueError: NumberFormatter::parseCurrency(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse_currency(): Argument #4 ($offset) must be between -2147483648 and 2147483647 From cd7222dbccc0a604467ff877fe5c58c9db002312 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 3 Jul 2026 18:16:32 +0800 Subject: [PATCH 026/211] Revert "ext/intl: Fix NumberFormatter parse offset overflow" This reverts commit 14a7eadfb25508af19ea831e51e66dc32a7b871e. --- ext/intl/formatter/formatter_parse.cpp | 16 ++----- .../formatter_parse_offset_overflow.phpt | 46 ------------------- 2 files changed, 3 insertions(+), 59 deletions(-) delete mode 100644 ext/intl/tests/formatter_parse_offset_overflow.phpt diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index 7aa92488704a..a475960809b8 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -50,12 +50,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) } if (zposition) { - zend_long long_position = zval_get_long(zposition); - if (ZEND_LONG_EXCEEDS_INT(long_position)) { - zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); - RETURN_THROWS(); - } - position = (int32_t) long_position; + position = (int32_t) zval_get_long(zposition); } /* Fetch the object. */ @@ -160,13 +155,8 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); - if (zposition) { - zend_long long_position = zval_get_long(zposition); - if (ZEND_LONG_EXCEEDS_INT(long_position)) { - zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); - RETURN_THROWS(); - } - position = (int32_t) long_position; + if(zposition) { + position = (int32_t) zval_get_long(zposition); } icu::ParsePosition pp(position); diff --git a/ext/intl/tests/formatter_parse_offset_overflow.phpt b/ext/intl/tests/formatter_parse_offset_overflow.phpt deleted file mode 100644 index 9421c8897511..000000000000 --- a/ext/intl/tests/formatter_parse_offset_overflow.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -NumberFormatter parse offset overflow ---EXTENSIONS-- -intl ---SKIPIF-- - ---FILE-- -getMessage(), PHP_EOL; - } -} - -$offset = PHP_INT_MAX; -print_error(function () use ($fmt, &$offset) { - $fmt->parse('123', NumberFormatter::TYPE_DOUBLE, $offset); -}); - -$offset = PHP_INT_MAX; -print_error(function () use ($fmt, &$offset) { - numfmt_parse($fmt, '123', NumberFormatter::TYPE_DOUBLE, $offset); -}); - -$currency = ''; -$offset = PHP_INT_MAX; -print_error(function () use ($currencyFmt, &$currency, &$offset) { - $currencyFmt->parseCurrency('$123.00', $currency, $offset); -}); - -$currency = ''; -$offset = PHP_INT_MAX; -print_error(function () use ($currencyFmt, &$currency, &$offset) { - numfmt_parse_currency($currencyFmt, '$123.00', $currency, $offset); -}); -?> ---EXPECT-- -ValueError: NumberFormatter::parse(): Argument #3 ($offset) must be between -2147483648 and 2147483647 -ValueError: numfmt_parse(): Argument #4 ($offset) must be between -2147483648 and 2147483647 -ValueError: NumberFormatter::parseCurrency(): Argument #3 ($offset) must be between -2147483648 and 2147483647 -ValueError: numfmt_parse_currency(): Argument #4 ($offset) must be between -2147483648 and 2147483647 From 53d0b0ea655599b4cb51f07144f7bd7ce0f1b57c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:15:30 +0100 Subject: [PATCH 027/211] ext/soap: remove parameters that are always NULL --- ext/soap/soap.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 83584283740d..1ee877c8fb37 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -49,9 +49,9 @@ static void type_to_string(sdlTypePtr type, smart_str *buf, int level); static void clear_soap_fault(zval *obj); static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang); -static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); +static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string); static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang); -static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); +static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string); static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string *name, zend_string *lang); static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr); static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string *name); @@ -1285,10 +1285,10 @@ static void _soap_server_exception(soapServicePtr service, sdlFunctionPtr functi if (service->send_errors) { zval rv; zend_string *msg = zval_get_string(zend_read_property_ex(zend_ce_error, Z_OBJ(exception_object), ZSTR_KNOWN(ZEND_STR_MESSAGE), /* silent */ false, &rv)); - add_soap_fault_ex_en(&exception_object, this_ptr, "Server", ZSTR_VAL(msg), NULL, NULL); + add_soap_fault_ex_en(&exception_object, this_ptr, "Server", ZSTR_VAL(msg)); zend_string_release_ex(msg, 0); } else { - add_soap_fault_ex_en(&exception_object, this_ptr, "Server", "Internal Error", NULL, NULL); + add_soap_fault_ex_en(&exception_object, this_ptr, "Server", "Internal Error"); } soap_server_fault_ex(function, &exception_object, NULL); } @@ -1915,7 +1915,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z code = "Client"; } - add_soap_fault_ex_en(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message), NULL, NULL); + add_soap_fault_ex_en(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message)); Z_ADDREF(fault); zend_throw_exception_object(&fault); zend_bailout(); @@ -2267,7 +2267,7 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co xmlDocDumpMemory(request, (xmlChar**)&buf, &buf_size); if (!buf) { - add_soap_fault_en(this_ptr, "HTTP", "Error build soap request", NULL, NULL); + add_soap_fault_en(this_ptr, "HTTP", "Error build soap request"); return false; } @@ -2297,7 +2297,7 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co if (EG(exception) && instanceof_function(EG(exception)->ce, zend_ce_error)) { /* Programmer error in __doRequest() implementation, let it bubble up. */ } else if (Z_TYPE_P(Z_CLIENT_SOAP_FAULT_P(this_ptr)) != IS_OBJECT) { - add_soap_fault_en(this_ptr, "Client", "SoapClient::__doRequest() returned non string value", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "SoapClient::__doRequest() returned non string value"); } ret = false; } else if (Z_TYPE_P(trace) == IS_TRUE) { @@ -2464,15 +2464,15 @@ static void do_soap_call(zend_execute_data *execute_data, smart_str_append(&error,function); smart_str_appends(&error,"\") is not a valid method for this service"); smart_str_0(&error); - add_soap_fault_en(this_ptr, "Client", ZSTR_VAL(error.s), NULL, NULL); + add_soap_fault_en(this_ptr, "Client", ZSTR_VAL(error.s)); smart_str_free(&error); } } else { zval *uri = Z_CLIENT_URI_P(this_ptr); if (Z_TYPE_P(uri) != IS_STRING) { - add_soap_fault_en(this_ptr, "Client", "Error finding \"uri\" property", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "Error finding \"uri\" property"); } else if (location == NULL) { - add_soap_fault_en(this_ptr, "Client", "Error could not find \"location\" property", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "Error could not find \"location\" property"); } else { if (call_uri == NULL) { call_uri = Z_STR_P(uri); @@ -2519,7 +2519,7 @@ static void do_soap_call(zend_execute_data *execute_data, if (Z_TYPE_P(fault) == IS_OBJECT) { ZVAL_COPY(return_value, fault); } else { - add_soap_fault_ex_en(return_value, this_ptr, "Client", "Unknown Error", NULL, NULL); + add_soap_fault_ex_en(return_value, this_ptr, "Client", "Unknown Error"); Z_ADDREF_P(return_value); } } else { @@ -2970,9 +2970,9 @@ static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fa } /* }}} */ -static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) +static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string) { - add_soap_fault_ex(fault, obj, fault_code, fault_string, fault_actor, fault_detail, soap_lang_en); + add_soap_fault_ex(fault, obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ @@ -2982,9 +2982,9 @@ void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault } /* }}} */ -static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) +static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string) { - add_soap_fault(obj, fault_code, fault_string, fault_actor, fault_detail, soap_lang_en); + add_soap_fault(obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang) /* {{{ */ From cb5c303593fe683234506617dfd936e9028d7a84 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:25:56 +0100 Subject: [PATCH 028/211] ext/soap: add const qualifiers to soap fault functions --- ext/soap/php_soap.h | 2 +- ext/soap/soap.c | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 58dbdfed6c05..dfdc1ffa35e3 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -195,7 +195,7 @@ extern zend_class_entry* soap_sdl_class_entry; extern HashTable php_soap_defEncNs, php_soap_defEnc, php_soap_defEncIndex; -void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang); +void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang); #define soap_error0(severity, format) \ php_error(severity, "SOAP-ERROR: " format) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 1ee877c8fb37..9f14168fa79b 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -49,12 +49,12 @@ static void type_to_string(sdlTypePtr type, smart_str *buf, int level); static void clear_soap_fault(zval *obj); static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang); -static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string); -static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang); -static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string); -static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string *name, zend_string *lang); +static void add_soap_fault_en(zval *obj, const char *fault_code, const char *fault_string); +static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang); +static void add_soap_fault_ex_en(zval *fault, zval *obj, const char *fault_code, const char *fault_string); +static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, const char *actor, zval* details, zend_string *name, zend_string *lang); static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr); -static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string *name); +static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string, const char *actor, zval* details, zend_string *name); static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, zend_ulong index, int); static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length); @@ -1875,7 +1875,7 @@ static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeade } /* }}} */ -static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string* name, zend_string *lang) /* {{{ */ +static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, const char *actor, zval* details, zend_string* name, zend_string *lang) /* {{{ */ { zval ret; @@ -1887,7 +1887,7 @@ static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *acto } /* }}} */ -static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string* name) +static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string, const char *actor, zval* details, zend_string* name) { soap_server_fault(code, string, actor, details, name, soap_lang_en); } @@ -2953,7 +2953,7 @@ static void clear_soap_fault(zval *obj) /* {{{ */ } /* }}} */ -static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ +static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { ZVAL_NULL(fault); set_soap_fault(fault, NULL, fault_code, fault_string, fault_actor, fault_detail, NULL, lang); @@ -2970,19 +2970,19 @@ static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fa } /* }}} */ -static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string) +static void add_soap_fault_ex_en(zval *fault, zval *obj, const char *fault_code, const char *fault_string) { add_soap_fault_ex(fault, obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } -void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ +void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { zval fault; add_soap_fault_ex(&fault, obj, fault_code, fault_string, fault_actor, fault_detail, lang); } /* }}} */ -static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string) +static void add_soap_fault_en(zval *obj, const char *fault_code, const char *fault_string) { add_soap_fault(obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } From 56d41ff03471266d080f41de6b21dc6bcedf3187 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:31:20 +0100 Subject: [PATCH 029/211] ext/soap: remove parameter of soap_server_fault_en() that are always NULL --- ext/soap/soap.c | 70 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 9f14168fa79b..12a72301249d 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -54,7 +54,7 @@ static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, co static void add_soap_fault_ex_en(zval *fault, zval *obj, const char *fault_code, const char *fault_string); static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, const char *actor, zval* details, zend_string *name, zend_string *lang); static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr); -static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string, const char *actor, zval* details, zend_string *name); +static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string); static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, zend_ulong index, int); static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length); @@ -1328,7 +1328,7 @@ PHP_METHOD(SoapServer, handle) SOAP_GLOBAL(soap_version) = service->version; if (arg && ZEND_SIZE_T_INT_OVFL(arg_len)) { - soap_server_fault_en("Server", "Input string is too long", NULL, NULL, NULL); + soap_server_fault_en("Server", "Input string is too long"); SOAP_SERVER_END_CODE(); return; } @@ -1354,13 +1354,13 @@ PHP_METHOD(SoapServer, handle) php_stream_passthru(stream); php_stream_close(stream); } else { - soap_server_fault_en("Server", "Couldn't find WSDL", NULL, NULL, NULL); + soap_server_fault_en("Server", "Couldn't find WSDL"); } SOAP_SERVER_END_CODE(); return; } else { - soap_server_fault_en("Server", "WSDL generation is not supported yet", NULL, NULL, NULL); + soap_server_fault_en("Server", "WSDL generation is not supported yet"); /* sapi_add_header("Content-Type: text/xml; charset=utf-8", sizeof("Content-Type: text/xml; charset=utf-8"), 1); PUTS("\nchildren,"Envelope"); @@ -1445,7 +1445,7 @@ PHP_METHOD(SoapServer, handle) } } xmlFreeDoc(doc_request); - soap_server_fault_en("Server", "DTD are not supported by SOAP", NULL, NULL, NULL); + soap_server_fault_en("Server", "DTD are not supported by SOAP"); } old_sdl = SOAP_GLOBAL(sdl); @@ -1503,7 +1503,7 @@ PHP_METHOD(SoapServer, handle) soap_obj = tmp_soap_p; } else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) { /* See #51561, communicate limitation to user */ - soap_server_fault_en("Server", "SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue.", NULL, NULL, NULL); + soap_server_fault_en("Server", "SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue."); } } } @@ -1562,7 +1562,7 @@ PHP_METHOD(SoapServer, handle) #if 0 if (service->sdl && !h->function && !h->hdr) { if (h->mustUnderstand) { - soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); + soap_server_fault_en("MustUnderstand","Header not understood"); } else { continue; } @@ -1601,7 +1601,7 @@ PHP_METHOD(SoapServer, handle) goto fail; } } else if (h->mustUnderstand) { - soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); + soap_server_fault_en("MustUnderstand","Header not understood"); } } } @@ -1887,9 +1887,9 @@ static ZEND_NORETURN void soap_server_fault(const char *code, const char *string } /* }}} */ -static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string, const char *actor, zval* details, zend_string* name) +static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string) { - soap_server_fault(code, string, actor, details, name, soap_lang_en); + soap_server_fault(code, string, NULL, NULL, NULL, soap_lang_en); } static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) /* {{{ */ @@ -3109,7 +3109,7 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u sdlParamPtr param = NULL; if (function != NULL && (param = zend_hash_index_find_ptr(function->requestParameters, cur_param)) == NULL) { - soap_server_fault_en("Client", "Error cannot find parameter", NULL, NULL, NULL); + soap_server_fault_en("Client", "Error cannot find parameter"); } if (param == NULL) { enc = NULL; @@ -3124,7 +3124,7 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u } } if (num_of_params > cur_param) { - soap_server_fault_en("Client","Missing parameter", NULL, NULL, NULL); + soap_server_fault_en("Client","Missing parameter"); } (*parameters) = tmp_parameters; (*num_params) = num_of_params; @@ -3215,7 +3215,7 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns return trav; } - soap_server_fault_en("VersionMismatch", "Wrong Version", NULL, NULL, NULL); + soap_server_fault_en("VersionMismatch", "Wrong Version"); } trav = trav->next; } @@ -3235,18 +3235,18 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c /* Get element */ env = get_envelope(request->children, version, &envelope_ns); if (!env) { - soap_server_fault_en("Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got XML without \"Envelope\" element"); } attr = env->properties; while (attr != NULL) { if (attr->ns == NULL) { - soap_server_fault_en("Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Envelope element cannot have non Namespace qualified attributes"); } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault_en("Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Envelope"); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style"); } } attr = attr->next; @@ -3276,26 +3276,26 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c trav = trav->next; } if (body == NULL) { - soap_server_fault_en("Client", "Body must be present in a SOAP envelope", NULL, NULL, NULL); + soap_server_fault_en("Client", "Body must be present in a SOAP envelope"); } attr = body->properties; while (attr != NULL) { if (attr->ns == NULL) { if (*version == SOAP_1_2) { - soap_server_fault_en("Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Body element cannot have non Namespace qualified attributes"); } } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault_en("Client", "encodingStyle cannot be specified on the Body", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Body"); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style"); } } attr = attr->next; } if (trav != NULL && *version == SOAP_1_2) { - soap_server_fault_en("Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP 1.2 envelope can contain only Header and Body"); } func = NULL; @@ -3304,7 +3304,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (trav->type == XML_ELEMENT_NODE) { /* if (func != NULL) { - soap_server_fault_en("Client", "looks like we got \"Body\" with several functions call", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got \"Body\" with several functions call"); } */ func = trav; @@ -3321,19 +3321,19 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (function) { ZVAL_STRING(function_name, (char *)function->functionName); } else { - soap_server_fault_en("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got \"Body\" without function call"); } } } else { if (*version == SOAP_1_1) { attr = get_attribute_ex(func->properties,"encodingStyle",SOAP_1_1_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault_en("Client","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("Client","Unknown Data Encoding Style"); } } else { attr = get_attribute_ex(func->properties,"encodingStyle",SOAP_1_2_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_2_ENC_NAMESPACE) != 0) { - soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style"); } } if (!function) { @@ -3341,7 +3341,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c } if (sdl != NULL && function == NULL) { if (*version == SOAP_1_2) { - soap_server_fault_en("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL); + soap_server_fault_en("rpc:ProcedureNotPresent","Procedure not present"); } else { php_error(E_ERROR, "Procedure '%s' not present", func->name); } @@ -3355,12 +3355,12 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c attr = head->properties; while (attr != NULL) { if (attr->ns == NULL) { - soap_server_fault_en("Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Header element cannot have non Namespace qualified attributes"); } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault_en("Client", "encodingStyle cannot be specified on the Header", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Header"); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style"); } } attr = attr->next; @@ -3374,7 +3374,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (*version == SOAP_1_1) { attr = get_attribute_ex(hdr_func->properties,"encodingStyle",SOAP_1_1_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault_en("Client","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("Client","Unknown Data Encoding Style"); } attr = get_attribute_ex(hdr_func->properties,"actor",envelope_ns); if (attr != NULL) { @@ -3386,7 +3386,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c } else if (*version == SOAP_1_2) { attr = get_attribute_ex(hdr_func->properties,"encodingStyle",SOAP_1_2_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_2_ENC_NAMESPACE) != 0) { - soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style"); } attr = get_attribute_ex(hdr_func->properties,"role",envelope_ns); if (attr != NULL) { @@ -3406,7 +3406,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c strcmp((char*)attr->children->content,"false") == 0) { mustUnderstand = 0; } else { - soap_server_fault_en("Client","mustUnderstand value is not boolean", NULL, NULL, NULL); + soap_server_fault_en("Client","mustUnderstand value is not boolean"); } } h = emalloc(sizeof(soapHeader)); @@ -3635,7 +3635,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *fu ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENV_NAMESPACE), BAD_CAST(SOAP_1_2_ENV_NS_PREFIX)); xmlSetNs(envelope,ns); } else { - soap_server_fault_en("Server", "Unknown SOAP version", NULL, NULL, NULL); + soap_server_fault_en("Server", "Unknown SOAP version"); } xmlDocSetRootElement(doc, envelope); From 14fb8692a5d762ace518d6c55ec5a137c7600cc3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:37:22 +0100 Subject: [PATCH 030/211] ext/soap: convert fault_actor parameter to zend_string* This removes some reallocations by being able to just increase a refcount --- ext/soap/php_packet_soap.c | 2 +- ext/soap/php_soap.h | 2 +- ext/soap/soap.c | 32 +++++++++++++++++--------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index b3aceb413890..006f9cca9c5b 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -261,7 +261,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio master_to_zval_with_doc_cleanup(&details, NULL, tmp, response); } } - add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor ? ZSTR_VAL(faultactor) : NULL, &details, lang); + add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor, &details, lang); if (faultstring) { zend_string_release_ex(faultstring, 0); } diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index dfdc1ffa35e3..f35f357ae394 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -195,7 +195,7 @@ extern zend_class_entry* soap_sdl_class_entry; extern HashTable php_soap_defEncNs, php_soap_defEnc, php_soap_defEncIndex; -void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang); +void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *lang); #define soap_error0(severity, format) \ php_error(severity, "SOAP-ERROR: " format) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 12a72301249d..8d9ad77d26e8 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -48,11 +48,11 @@ static void function_to_string(sdlFunctionPtr function, smart_str *buf); static void type_to_string(sdlTypePtr type, smart_str *buf, int level); static void clear_soap_fault(zval *obj); -static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang); +static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang); static void add_soap_fault_en(zval *obj, const char *fault_code, const char *fault_string); -static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang); +static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *lang); static void add_soap_fault_ex_en(zval *fault, zval *obj, const char *fault_code, const char *fault_string); -static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, const char *actor, zval* details, zend_string *name, zend_string *lang); +static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, zend_string *actor, zval* details, zend_string *name, zend_string *lang); static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr); static ZEND_NORETURN void soap_server_fault_en(const char *code, const char *string); @@ -693,8 +693,9 @@ static void soap_fault_dtor_properties(zval *obj) /* {{{ SoapFault constructor */ PHP_METHOD(SoapFault, __construct) { - char *fault_string = NULL, *fault_code = NULL, *fault_actor = NULL, *fault_code_ns = NULL; - size_t fault_string_len, fault_actor_len = 0, fault_code_len = 0; + char *fault_string = NULL, *fault_code = NULL, *fault_code_ns = NULL; + size_t fault_string_len, fault_code_len = 0; + zend_string *fault_actor = NULL; zend_string *name = NULL; zend_string *lang = ZSTR_EMPTY_ALLOC(); zval *details = NULL, *headerfault = NULL, *this_ptr; @@ -705,7 +706,7 @@ PHP_METHOD(SoapFault, __construct) Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(code_ht, code_str) Z_PARAM_STRING(fault_string, fault_string_len) Z_PARAM_OPTIONAL - Z_PARAM_STRING_OR_NULL(fault_actor, fault_actor_len) + Z_PARAM_STR_OR_NULL(fault_actor) Z_PARAM_ZVAL_OR_NULL(details) Z_PARAM_STR_OR_NULL(name) Z_PARAM_ZVAL_OR_NULL(headerfault) @@ -1760,16 +1761,17 @@ PHP_METHOD(SoapServer, handle) /* {{{ Issue SoapFault indicating an error */ PHP_METHOD(SoapServer, fault) { - char *code, *string, *actor=NULL; - size_t code_len, string_len, actor_len = 0; + char *code, *string; + size_t code_len, string_len; + zend_string *actor = NULL; zval* details = NULL; zend_string *name = NULL; zend_string *lang = ZSTR_EMPTY_ALLOC(); soapServicePtr service; xmlCharEncodingHandlerPtr old_encoding; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|szSP", - &code, &code_len, &string, &string_len, &actor, &actor_len, &details, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|SzSP", + &code, &code_len, &string, &string_len, &actor, &details, &name, &lang) == FAILURE) { RETURN_THROWS(); } @@ -1875,7 +1877,7 @@ static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeade } /* }}} */ -static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, const char *actor, zval* details, zend_string* name, zend_string *lang) /* {{{ */ +static ZEND_NORETURN void soap_server_fault(const char *code, const char *string, zend_string *actor, zval* details, zend_string* name, zend_string *lang) /* {{{ */ { zval ret; @@ -2953,7 +2955,7 @@ static void clear_soap_fault(zval *obj) /* {{{ */ } /* }}} */ -static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ +static void add_soap_fault_ex(zval *fault, zval *obj, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { ZVAL_NULL(fault); set_soap_fault(fault, NULL, fault_code, fault_string, fault_actor, fault_detail, NULL, lang); @@ -2975,7 +2977,7 @@ static void add_soap_fault_ex_en(zval *fault, zval *obj, const char *fault_code, add_soap_fault_ex(fault, obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } -void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ +void add_soap_fault(zval *obj, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { zval fault; add_soap_fault_ex(&fault, obj, fault_code, fault_string, fault_actor, fault_detail, lang); @@ -2987,7 +2989,7 @@ static void add_soap_fault_en(zval *obj, const char *fault_code, const char *fau add_soap_fault(obj, fault_code, fault_string, NULL, NULL, soap_lang_en); } -static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang) /* {{{ */ +static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, zend_string *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang) /* {{{ */ { if (Z_TYPE_P(obj) != IS_OBJECT) { object_init_ex(obj, soap_fault_class_entry); @@ -3030,7 +3032,7 @@ static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fau } } if (fault_actor != NULL) { - ZVAL_STRING(Z_FAULT_ACTOR_P(obj), fault_actor); + ZVAL_STR_COPY(Z_FAULT_ACTOR_P(obj), fault_actor); } if (fault_detail != NULL && Z_TYPE_P(fault_detail) != IS_UNDEF) { ZVAL_COPY(Z_FAULT_DETAIL_P(obj), fault_detail); From db5845e3217ff6a7b5a1fcee49fcb2c35c2925a7 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:42:00 +0100 Subject: [PATCH 031/211] ext/soap: refactor parse_packet_soap() Use zend_string* rather than a pair and remove unused param --- ext/soap/php_packet_soap.c | 6 +++--- ext/soap/php_packet_soap.h | 2 +- ext/soap/soap.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 006f9cca9c5b..2a2f94cb349a 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -39,7 +39,7 @@ static void master_to_zval_with_doc_cleanup(zval *ret, encodePtr encode, xmlNode } /* SOAP client calls this function to parse response from SOAP server */ -bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctionPtr fn, char *fn_name, zval *return_value, zval *soap_headers) +bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, zval *return_value, zval *soap_headers) { char* envelope_ns = NULL; xmlDocPtr response; @@ -52,12 +52,12 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio ZVAL_NULL(return_value); /* Response for one-way operation */ - if (buffer_size == 0) { + if (ZSTR_LEN(buffer) == 0) { return true; } /* Parse XML packet */ - response = soap_xmlParseMemory(buffer, buffer_size); + response = soap_xmlParseMemory(ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (!response) { add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, soap_lang_en); diff --git a/ext/soap/php_packet_soap.h b/ext/soap/php_packet_soap.h index 569b23949659..dc4b44176867 100644 --- a/ext/soap/php_packet_soap.h +++ b/ext/soap/php_packet_soap.h @@ -17,6 +17,6 @@ #ifndef PHP_PACKET_SOAP_H #define PHP_PACKET_SOAP_H -bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctionPtr fn, char *fn_name, zval *return_value, zval *soap_headers); +bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, zval *return_value, zval *soap_headers); #endif diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 8d9ad77d26e8..d8f72e0191aa 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2447,7 +2447,7 @@ static void do_soap_call(zend_execute_data *execute_data, encode_reset_ns(); zend_try { - ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), fn, NULL, return_value, output_headers); + ret = parse_packet_soap(this_ptr, Z_STR(response), fn, return_value, output_headers); } zend_catch { parse_bailout = true; } zend_end_try(); @@ -2501,7 +2501,7 @@ static void do_soap_call(zend_execute_data *execute_data, encode_reset_ns(); zend_try { - ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), NULL, NULL, return_value, output_headers); + ret = parse_packet_soap(this_ptr, Z_STR(response), NULL, return_value, output_headers); } zend_catch { parse_bailout = true; } zend_end_try(); From 98e920c22ae0f73a7f88b6d2c3c5fd25c7a84fbf Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 22:46:55 +0100 Subject: [PATCH 032/211] ext/soap: minor refactorings of parse_packet_soap() - Add const qualifiers - Reduce scope of variable - Reuse computed strlen() - Use uint32_t type instead of int type --- ext/soap/php_packet_soap.c | 45 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 2a2f94cb349a..68ed9a7a7689 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -41,11 +41,11 @@ static void master_to_zval_with_doc_cleanup(zval *ret, encodePtr encode, xmlNode /* SOAP client calls this function to parse response from SOAP server */ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, zval *return_value, zval *soap_headers) { - char* envelope_ns = NULL; + const char *envelope_ns = NULL; xmlDocPtr response; - xmlNodePtr trav, env, head, body, resp, cur, fault; + xmlNodePtr trav, env, head, body, resp, fault; xmlAttrPtr attr; - int param_count = 0; + uint32_t param_count = 0; int soap_version = SOAP_1_1; HashTable *hdrs = NULL; @@ -107,7 +107,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; - } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { + } else if (strcmp((const char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -196,7 +196,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z /* Check if contains element */ fault = get_node_ex(body->children,"Fault",envelope_ns); if (fault != NULL) { - char *faultcode = NULL; + const char *faultcode = NULL; zend_string *lang = ZSTR_EMPTY_ALLOC(); zend_string *faultstring = NULL, *faultactor = NULL; zval details; @@ -206,7 +206,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z if (soap_version == SOAP_1_1) { tmp = get_node(fault->children, "faultcode"); if (tmp != NULL && tmp->children != NULL) { - faultcode = (char*)tmp->children->content; + faultcode = (const char*)tmp->children->content; } tmp = get_node(fault->children, "faultstring"); @@ -234,7 +234,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z if (tmp != NULL && tmp->children != NULL) { tmp = get_node(tmp->children, "Value"); if (tmp != NULL && tmp->children != NULL) { - faultcode = (char*)tmp->children->content; + faultcode = (const char*)tmp->children->content; } } @@ -287,16 +287,15 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z /* Function has WSDL description */ sdlParamPtr param = NULL; xmlNodePtr val = NULL; - char *name, *ns = NULL; zval tmp; sdlSoapBindingFunctionPtr fnb = (sdlSoapBindingFunctionPtr)fn->bindingAttributes; - int res_count; hdrs = fnb->output.headers; if (fn->responseParameters) { - res_count = zend_hash_num_elements(fn->responseParameters); + uint32_t res_count = zend_hash_num_elements(fn->responseParameters); ZEND_HASH_FOREACH_PTR(fn->responseParameters, param) { + const char *name, *ns = NULL; if (fnb->style == SOAP_DOCUMENT) { if (param->element) { name = param->element->name; @@ -314,7 +313,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z } /* Get value of parameter */ - cur = get_node_ex(resp, name, ns); + xmlNodePtr cur = get_node_ex(resp, name, ns); if (!cur) { cur = get_node(resp, name); /* TODO: produce warning invalid ns */ @@ -373,20 +372,22 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z if (val != NULL) { if (!node_is_equal_ex(val,"result",RPC_SOAP12_NAMESPACE)) { zval tmp; - zval *arr; master_to_zval_with_doc_cleanup(&tmp, NULL, val, response); if (val->name) { - if ((arr = zend_hash_str_find(Z_ARRVAL_P(return_value), (char*)val->name, strlen((char*)val->name))) != NULL) { + const char *val_name = (const char*) val->name; + size_t val_name_len = strlen(val_name); + zval *arr = zend_hash_str_find(Z_ARRVAL_P(return_value), val_name, val_name_len); + if (arr != NULL) { add_next_index_zval(arr, &tmp); - } else if (val->next && get_node(val->next, (char*)val->name)) { - zval arr; + } else if (val->next && get_node(val->next, val_name)) { + zval new_arr; - array_init(&arr); - add_next_index_zval(&arr, &tmp); - add_assoc_zval(return_value, (char*)val->name, &arr); + array_init(&new_arr); + add_next_index_zval(&new_arr, &tmp); + add_assoc_zval_ex(return_value, val_name, val_name_len, &new_arr); } else { - add_assoc_zval(return_value, (char*)val->name, &tmp); + add_assoc_zval_ex(return_value, val_name, val_name_len, &tmp); } } else { add_next_index_zval(return_value, &tmp); @@ -430,10 +431,10 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z sdlSoapBindingFunctionHeaderPtr hdr; if (trav->ns) { - smart_str_appends(&key, (char*)trav->ns->href); + smart_str_appends(&key, (const char*)trav->ns->href); smart_str_appendc(&key,':'); } - smart_str_appends(&key, (char*)trav->name); + smart_str_appends(&key, (const char*)trav->name); smart_str_0(&key); if ((hdr = zend_hash_find_ptr(hdrs, key.s)) != NULL) { enc = hdr->encode; @@ -441,7 +442,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z smart_str_free(&key); } master_to_zval_with_doc_cleanup(&val, enc, trav, response); - add_assoc_zval(soap_headers, (char*)trav->name, &val); + add_assoc_zval(soap_headers, (const char*)trav->name, &val); } trav = trav->next; } From ac1e1ded773813a7fd37d5c6bd9b46badbf34df3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 23:12:08 +0100 Subject: [PATCH 033/211] ext/soap: use bool type instead of int type in php_xml.c --- ext/soap/php_xml.c | 26 +++++++++++++------------- ext/soap/php_xml.h | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c index d5daaef15983..a4c638410a73 100644 --- a/ext/soap/php_xml.c +++ b/ext/soap/php_xml.c @@ -148,7 +148,7 @@ xmlNsPtr node_find_ns(xmlNodePtr node) } } -int attr_is_equal_ex(xmlAttrPtr node, const char *name, const char *ns) +bool attr_is_equal_ex(xmlAttrPtr node, const char *name, const char *ns) { if (node->name && strcmp((const char *) node->name, name) == 0) { xmlNsPtr nsPtr = node->ns; @@ -156,17 +156,17 @@ int attr_is_equal_ex(xmlAttrPtr node, const char *name, const char *ns) if (nsPtr) { return (strcmp((const char *) nsPtr->href, ns) == 0); } else { - return FALSE; + return false; } } else if (nsPtr) { - return FALSE; + return false; } - return TRUE; + return true; } - return FALSE; + return false; } -int node_is_equal_ex(xmlNodePtr node, const char *name, const char *ns) +bool node_is_equal_ex(xmlNodePtr node, const char *name, const char *ns) { if (name == NULL || ((node->name) && strcmp((char*)node->name, name) == 0)) { if (ns) { @@ -174,29 +174,29 @@ int node_is_equal_ex(xmlNodePtr node, const char *name, const char *ns) if (nsPtr) { return strcmp((const char *) nsPtr->href, ns) == 0; } else { - return FALSE; + return false; } } - return TRUE; + return true; } - return FALSE; + return false; } -int node_is_equal_ex_one_of(xmlNodePtr node, const char *name, const char *const *namespaces) +bool node_is_equal_ex_one_of(xmlNodePtr node, const char *name, const char *const *namespaces) { if ((node->name) && strcmp((char*)node->name, name) == 0) { xmlNsPtr nsPtr = node_find_ns(node); if (nsPtr) { do { if (strcmp((const char *) nsPtr->href, *namespaces) == 0) { - return TRUE; + return true; } namespaces++; } while (*namespaces != NULL); } - return FALSE; + return false; } - return FALSE; + return false; } xmlAttrPtr get_attribute_any_ns(xmlAttrPtr node, const char *name) diff --git a/ext/soap/php_xml.h b/ext/soap/php_xml.h index 4a6d76427dd1..d87f4a758748 100644 --- a/ext/soap/php_xml.h +++ b/ext/soap/php_xml.h @@ -29,9 +29,9 @@ xmlDocPtr soap_xmlParseFile(const char *filename); xmlDocPtr soap_xmlParseMemory(const void *buf, size_t size); xmlNsPtr node_find_ns(xmlNodePtr node); -int attr_is_equal_ex(xmlAttrPtr node, const char *name, const char *ns); -int node_is_equal_ex(xmlNodePtr node, const char *name, const char *ns); -int node_is_equal_ex_one_of(xmlNodePtr node, const char *name, const char *const *namespaces); +bool attr_is_equal_ex(xmlAttrPtr node, const char *name, const char *ns); +bool node_is_equal_ex(xmlNodePtr node, const char *name, const char *ns); +bool node_is_equal_ex_one_of(xmlNodePtr node, const char *name, const char *const *namespaces); xmlAttrPtr get_attribute_any_ns(xmlAttrPtr node, const char *name); xmlAttrPtr get_attribute_ex(xmlAttrPtr node, const char *name, const char *ns); xmlNodePtr get_node_ex(xmlNodePtr node, const char *name, const char *ns); From 8c008042cb691d556587875d7d5e86f3abbc35fa Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 23:26:28 +0100 Subject: [PATCH 034/211] ext/soap: use bool type instead of int type in php_http.c --- ext/soap/php_http.c | 115 +++++++++++++++++++++++--------------------- ext/soap/php_http.h | 6 +-- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 2594be75fe15..5d7485af8838 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -20,14 +20,14 @@ static char *get_http_header_value_nodup(char *headers, char *type, size_t *len); static char *get_http_header_value(char *headers, char *type); -static zend_string *get_http_body(php_stream *socketd, int close, char *headers); +static zend_string *get_http_body(php_stream *socketd, bool close, char *headers); static zend_string *get_http_headers(php_stream *socketd); #define smart_str_append_const(str, const) \ smart_str_appendl(str,const,sizeof(const)-1) /* Proxy HTTP Authentication */ -int proxy_authentication(zval* this_ptr, smart_str* soap_headers) +bool proxy_authentication(zval* this_ptr, smart_str* soap_headers) { zval *login = Z_CLIENT_PROXY_LOGIN_P(this_ptr); if (Z_TYPE_P(login) == IS_STRING) { @@ -44,15 +44,15 @@ int proxy_authentication(zval* this_ptr, smart_str* soap_headers) smart_str_append_const(soap_headers, "Proxy-Authorization: Basic "); smart_str_append(soap_headers, buf); smart_str_append_const(soap_headers, "\r\n"); - zend_string_release_ex(buf, 0); + zend_string_release_ex(buf, false); smart_str_free(&auth); - return 1; + return true; } - return 0; + return false; } /* HTTP Authentication */ -int basic_authentication(zval* this_ptr, smart_str* soap_headers) +bool basic_authentication(zval* this_ptr, smart_str* soap_headers) { zval *login = Z_CLIENT_LOGIN_P(this_ptr); zval *use_digest = Z_CLIENT_USE_DIGEST_P(this_ptr); @@ -70,11 +70,11 @@ int basic_authentication(zval* this_ptr, smart_str* soap_headers) smart_str_append_const(soap_headers, "Authorization: Basic "); smart_str_append(soap_headers, buf); smart_str_append_const(soap_headers, "\r\n"); - zend_string_release_ex(buf, 0); + zend_string_release_ex(buf, false); smart_str_free(&auth); - return 1; + return true; } - return 0; + return false; } static void http_context_add_header(const char *s, @@ -161,7 +161,7 @@ void http_context_headers(php_stream_context* context, } } -static php_stream* http_connect(zval* this_ptr, php_uri *uri, int use_ssl, php_stream_context *context, int *use_proxy) +static php_stream* http_connect(zval* this_ptr, php_uri *uri, bool use_ssl, php_stream_context *context, int *use_proxy) { php_stream *stream; zval *tmp, ssl_proxy_peer_name; @@ -334,7 +334,7 @@ static bool in_domain(const zend_string *host, const zend_string *domain) } } -int make_http_soap_request( +bool make_http_soap_request( zval *this_ptr, zend_string *buf, zend_string *location, char *soapaction, int soap_version, zend_string *uri_parser_class, zval *return_value ) { @@ -346,14 +346,13 @@ int make_http_soap_request( php_stream *stream; zval *tmp; int use_proxy = 0; - int use_ssl; zend_string *http_body; char *content_type, *http_version, *cookie_itt; size_t cookie_len; - int http_close; + bool http_close; zend_string *http_headers; char *connection; - int http_1_1; + bool http_1_1; int http_status; int content_type_xml = 0; zend_long redirect_max = 20; @@ -366,7 +365,7 @@ int make_http_soap_request( bool has_cookies = false; if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) { - return FALSE; + return false; } request = buf; @@ -412,7 +411,7 @@ int make_http_soap_request( zend_string_release_ex(request, 0); } smart_str_free(&soap_headers_z); - return FALSE; + return false; } } } @@ -432,7 +431,7 @@ int make_http_soap_request( const php_uri_parser *uri_parser = php_uri_get_parser(uri_parser_class); if (uri_parser == NULL) { zend_argument_value_error(6, "must be a valid URI parser name"); - return FALSE; + return false; } uri = php_uri_parse_to_struct(uri_parser, ZSTR_VAL(location), ZSTR_LEN(location), PHP_URI_COMPONENT_READ_MODE_RAW, true); } @@ -461,12 +460,12 @@ int make_http_soap_request( add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } - use_ssl = 0; + bool use_ssl = false; if (uri->scheme != NULL && zend_string_equals_literal(uri->scheme, "https")) { - use_ssl = 1; + use_ssl = true; } else if (uri->scheme == NULL || !zend_string_equals_literal(uri->scheme, "http")) { php_uri_struct_free(uri); if (request != buf) { @@ -475,7 +474,7 @@ int make_http_soap_request( add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } old_allow_url_fopen = PG(allow_url_fopen); @@ -489,7 +488,7 @@ int make_http_soap_request( PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } if (uri->port == 0) { @@ -542,7 +541,7 @@ int make_http_soap_request( PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } } PG(allow_url_fopen) = old_allow_url_fopen; @@ -565,9 +564,9 @@ int make_http_soap_request( (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL && Z_TYPE_P(tmp) == IS_DOUBLE && Z_DVAL_P(tmp) == 1.0) { - http_1_1 = 0; + http_1_1 = false; } else { - http_1_1 = 1; + http_1_1 = true; } smart_str_append_const(&soap_headers, "POST "); @@ -694,7 +693,7 @@ int make_http_soap_request( smart_str_free(&soap_headers_z); smart_str_free(&soap_headers); efree(http_msg); - return FALSE; + return false; } php_hash_bin2hex(cnonce, nonce, sizeof(nonce)); @@ -912,14 +911,14 @@ int make_http_soap_request( add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } smart_str_free(&soap_headers); } else { add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } http_headers = NULL; @@ -936,7 +935,7 @@ int make_http_soap_request( add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } if (client_trace) { @@ -945,14 +944,14 @@ int make_http_soap_request( } /* Check to see what HTTP status was sent */ - http_1_1 = 0; + http_1_1 = false; http_status = 0; http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/"); if (http_version) { char *tmp; if (!strncmp(http_version,"1.1", 3)) { - http_1_1 = 1; + http_1_1 = true; } tmp = strstr(http_version," "); @@ -1069,41 +1068,41 @@ int make_http_soap_request( /* See if the server requested a close */ if (http_1_1) { - http_close = FALSE; + http_close = false; if (use_proxy && !use_ssl) { connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:"); if (connection) { if (strncasecmp(connection, "close", sizeof("close")-1) == 0) { - http_close = TRUE; + http_close = true; } efree(connection); } } - if (http_close == FALSE) { + if (http_close == false) { connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:"); if (connection) { if (strncasecmp(connection, "close", sizeof("close")-1) == 0) { - http_close = TRUE; + http_close = true; } efree(connection); } } } else { - http_close = TRUE; + http_close = true; if (use_proxy && !use_ssl) { connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:"); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { - http_close = FALSE; + http_close = false; } efree(connection); } } - if (http_close == TRUE) { + if (http_close == true) { connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:"); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { - http_close = FALSE; + http_close = false; } efree(connection); } @@ -1125,7 +1124,7 @@ int make_http_soap_request( efree(http_msg); } smart_str_free(&soap_headers_z); - return FALSE; + return false; } if (request != buf) { @@ -1148,7 +1147,7 @@ int make_http_soap_request( if (uri_parser == NULL) { efree(loc); zend_argument_value_error(6, "must be a valid URI parser name"); - return FALSE; + return false; } php_uri *new_uri = php_uri_parse_to_struct(uri_parser, loc, strlen(loc), PHP_URI_COMPONENT_READ_MODE_RAW, true); @@ -1185,7 +1184,7 @@ int make_http_soap_request( add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); - return FALSE; + return false; } goto try_again; @@ -1288,7 +1287,7 @@ int make_http_soap_request( efree(content_type); zend_string_release_ex(http_headers, 0); efree(http_body); - return FALSE; + return false; } */ } @@ -1319,7 +1318,7 @@ int make_http_soap_request( efree(http_msg); } add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL, soap_lang_en); - return FALSE; + return false; } zend_call_known_function(decompression_fn, NULL, NULL, &retval, 1, params, NULL); if (Z_TYPE(retval) == IS_STRING) { @@ -1334,7 +1333,7 @@ int make_http_soap_request( if (http_msg) { efree(http_msg); } - return FALSE; + return false; } efree(content_encoding); } else { @@ -1366,7 +1365,7 @@ int make_http_soap_request( ZVAL_UNDEF(return_value); add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL, soap_lang_en); efree(http_msg); - return FALSE; + return false; } } @@ -1374,7 +1373,7 @@ int make_http_soap_request( efree(http_msg); } - return TRUE; + return true; } static char *get_http_header_value_nodup(char *headers, char *type, size_t *len) @@ -1444,22 +1443,27 @@ static char *get_http_header_value(char *headers, char *type) return NULL; } -static zend_string* get_http_body(php_stream *stream, int close, char *headers) +static zend_string* get_http_body(php_stream *stream, bool close, char *headers) { zend_string *http_buf = NULL; char *header; - int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0; + bool header_close = close, header_chunked = false; + int header_length = 0, http_buf_size = 0; if (!close) { header = get_http_header_value(headers, "Connection:"); if (header) { - if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1; + if (!strncasecmp(header, "close", sizeof("close")-1)) { + header_close = true; + } efree(header); } } header = get_http_header_value(headers, "Transfer-Encoding:"); if (header) { - if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1; + if (!strncasecmp(header, "chunked", sizeof("chunked")-1)) { + header_chunked = true; + } efree(header); } header = get_http_header_value(headers, "Content-Length:"); @@ -1473,9 +1477,8 @@ static zend_string* get_http_body(php_stream *stream, int close, char *headers) } if (header_chunked) { - char ch, done, headerbuf[8192]; - - done = FALSE; + char ch, headerbuf[8192]; + bool done = false; while (!done) { int buf_size = 0; @@ -1502,7 +1505,7 @@ static zend_string* get_http_body(php_stream *stream, int close, char *headers) ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size); if (len_read <= 0) { /* Error or EOF */ - done = TRUE; + done = true; break; } len_size += len_read; @@ -1530,7 +1533,7 @@ static zend_string* get_http_body(php_stream *stream, int close, char *headers) return NULL; } if (buf_size == 0) { - done = TRUE; + done = true; } } diff --git a/ext/soap/php_http.h b/ext/soap/php_http.h index a01ea35f9849..4f13b79f1164 100644 --- a/ext/soap/php_http.h +++ b/ext/soap/php_http.h @@ -17,13 +17,13 @@ #ifndef PHP_HTTP_H #define PHP_HTTP_H -int make_http_soap_request( +bool make_http_soap_request( zval *this_ptr, zend_string *buf, zend_string *location, char *soapaction, int soap_version, zend_string *uri_parser_class, zval *return_value ); -int proxy_authentication(zval* this_ptr, smart_str* soap_headers); -int basic_authentication(zval* this_ptr, smart_str* soap_headers); +bool proxy_authentication(zval* this_ptr, smart_str* soap_headers); +bool basic_authentication(zval* this_ptr, smart_str* soap_headers); void http_context_headers(php_stream_context* context, bool has_authorization, bool has_proxy_authorization, From 8c1e00e379a2623e834744b5e7ea8e8c79dc6714 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jul 2026 23:37:12 +0100 Subject: [PATCH 035/211] ext/soap: use zend_string* in php_http.c where possible --- ext/soap/php_http.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 5d7485af8838..4fe272ea6f13 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -19,8 +19,8 @@ #include "ext/uri/php_uri.h" static char *get_http_header_value_nodup(char *headers, char *type, size_t *len); -static char *get_http_header_value(char *headers, char *type); -static zend_string *get_http_body(php_stream *socketd, bool close, char *headers); +static char *get_http_header_value(zend_string *headers, char *type); +static zend_string *get_http_body(php_stream *socketd, bool close, zend_string *headers); static zend_string *get_http_headers(php_stream *socketd); #define smart_str_append_const(str, const) \ @@ -946,7 +946,7 @@ bool make_http_soap_request( /* Check to see what HTTP status was sent */ http_1_1 = false; http_status = 0; - http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/"); + http_version = get_http_header_value(http_headers, "HTTP/"); if (http_version) { char *tmp; @@ -1070,7 +1070,7 @@ bool make_http_soap_request( if (http_1_1) { http_close = false; if (use_proxy && !use_ssl) { - connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:"); + connection = get_http_header_value(http_headers, "Proxy-Connection:"); if (connection) { if (strncasecmp(connection, "close", sizeof("close")-1) == 0) { http_close = true; @@ -1079,7 +1079,7 @@ bool make_http_soap_request( } } if (http_close == false) { - connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:"); + connection = get_http_header_value(http_headers, "Connection:"); if (connection) { if (strncasecmp(connection, "close", sizeof("close")-1) == 0) { http_close = true; @@ -1090,7 +1090,7 @@ bool make_http_soap_request( } else { http_close = true; if (use_proxy && !use_ssl) { - connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:"); + connection = get_http_header_value(http_headers, "Proxy-Connection:"); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { http_close = false; @@ -1099,7 +1099,7 @@ bool make_http_soap_request( } } if (http_close == true) { - connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:"); + connection = get_http_header_value(http_headers, "Connection:"); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { http_close = false; @@ -1110,7 +1110,7 @@ bool make_http_soap_request( } - http_body = get_http_body(stream, http_close, ZSTR_VAL(http_headers)); + http_body = get_http_body(stream, http_close, http_headers); if (!http_body) { if (request != buf) { zend_string_release_ex(request, 0); @@ -1142,7 +1142,7 @@ bool make_http_soap_request( if (http_status >= 300 && http_status < 400) { char *loc; - if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location:")) != NULL) { + if ((loc = get_http_header_value(http_headers, "Location:")) != NULL) { const php_uri_parser *uri_parser = php_uri_get_parser(uri_parser_class); if (uri_parser == NULL) { efree(loc); @@ -1195,7 +1195,7 @@ bool make_http_soap_request( zval *digest = Z_CLIENT_DIGEST_P(this_ptr); zval *login = Z_CLIENT_LOGIN_P(this_ptr); zval *password = Z_CLIENT_PASSWORD_P(this_ptr); - char *auth = get_http_header_value(ZSTR_VAL(http_headers), "WWW-Authenticate:"); + char *auth = get_http_header_value(http_headers, "WWW-Authenticate:"); if (auth && strstr(auth, "Digest") == auth && Z_TYPE_P(digest) != IS_ARRAY && Z_TYPE_P(login) == IS_STRING && Z_TYPE_P(password) == IS_STRING) { char *s; @@ -1265,7 +1265,7 @@ bool make_http_soap_request( smart_str_free(&soap_headers_z); /* Check and see if the server even sent a xml document */ - content_type = get_http_header_value(ZSTR_VAL(http_headers), "Content-Type:"); + content_type = get_http_header_value(http_headers, "Content-Type:"); if (content_type) { char *pos = NULL; int cmplen; @@ -1295,7 +1295,7 @@ bool make_http_soap_request( } /* Decompress response */ - content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding:"); + content_encoding = get_http_header_value(http_headers, "Content-Encoding:"); if (content_encoding) { zval retval; zval params[1]; @@ -1429,12 +1429,12 @@ static char *get_http_header_value_nodup(char *headers, char *type, size_t *len) return NULL; } -static char *get_http_header_value(char *headers, char *type) +static char *get_http_header_value(zend_string *headers, char *type) { size_t len; char *value; - value = get_http_header_value_nodup(headers, type, &len); + value = get_http_header_value_nodup(ZSTR_VAL(headers), type, &len); if (value) { return estrndup(value, len); @@ -1443,7 +1443,7 @@ static char *get_http_header_value(char *headers, char *type) return NULL; } -static zend_string* get_http_body(php_stream *stream, bool close, char *headers) +static zend_string* get_http_body(php_stream *stream, bool close, zend_string *headers) { zend_string *http_buf = NULL; char *header; From 4999bcb29cbf1da7eb90365f0eb8cab416b8778a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Fri, 3 Jul 2026 14:34:11 +0200 Subject: [PATCH 036/211] Fix Ipv6 and ipvfuture address validation to use PHP's built-in memory manager (#22571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tim Düsterhus --- ext/uri/uri_parser_rfc3986.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index 88201914d593..f4f5f2bf5bd9 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -737,13 +737,13 @@ ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_validate_host(const ze if (len >= 2 && (p[1] == 'v' || p[1] == 'V')) { return php_uri_parser_rfc3986_validate_component_result( - uriIsWellFormedHostIpFutureA(p + 1, p + len - 1) == URI_SUCCESS, + uriIsWellFormedHostIpFutureMmA(p + 1, p + len - 1, mm) == URI_SUCCESS, "host" ); } return php_uri_parser_rfc3986_validate_component_result( - uriIsWellFormedHostIp6A(p + 1, p + len - 1) == URI_SUCCESS, + uriIsWellFormedHostIp6MmA(p + 1, p + len - 1, mm) == URI_SUCCESS, "host" ); } From 6aca6e30bb4ecf062d3d863de254b8b1d0914a8f Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Fri, 3 Jul 2026 19:15:40 +0530 Subject: [PATCH 037/211] ext/openssl: fix OpenSSL 4 build and test compatibility (#22267) * Fix OpenSSL 4 test compatibility * Fix OpenSSL 4 build compatibility * Use OpenSSL 4 in macOS CI * Fix OpenSSL 1.1 build compatibility * Preserve raw CN matching with OpenSSL 4 --- .github/actions/brew/action.yml | 4 +- .github/actions/configure-macos/action.yml | 2 +- ext/openssl/openssl.c | 10 ++-- ext/openssl/openssl_backend_common.c | 33 +++++++++---- ext/openssl/openssl_backend_v3.c | 2 +- ext/openssl/php_openssl.h | 1 + ext/openssl/php_openssl_backend.h | 12 +++-- ext/openssl/tests/bug28382.phpt | 8 +++- ext/openssl/tests/ecc_custom_params.phpt | 24 ++++++++++ .../openssl_x509_parse_basic_openssl32.phpt | 20 +++++++- ext/openssl/xp_ssl.c | 46 ++++++++++++++----- 11 files changed, 129 insertions(+), 33 deletions(-) diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml index f90a66f239de..db43010f396f 100644 --- a/.github/actions/brew/action.yml +++ b/.github/actions/brew/action.yml @@ -33,4 +33,6 @@ runs: t1lib \ libxml2 \ libjpeg \ - libxslt + libxslt \ + openssl@4 + brew link --force --overwrite openssl@4 diff --git a/.github/actions/configure-macos/action.yml b/.github/actions/configure-macos/action.yml index f0af959d2102..b9739a790530 100644 --- a/.github/actions/configure-macos/action.yml +++ b/.github/actions/configure-macos/action.yml @@ -11,7 +11,7 @@ runs: set -x BREW_OPT="$(brew --prefix)"/opt export PATH="$BREW_OPT/bison/bin:$PATH" - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/openssl/lib/pkgconfig" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/openssl@4/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/curl/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/libffi/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/libxml2/lib/pkgconfig" diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 1e63eb1381f6..e11ad5c3428c 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -821,7 +821,7 @@ PHP_MINIT_FUNCTION(openssl) ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL); php_stream_xport_register("ssl", php_openssl_ssl_socket_factory); -#ifndef OPENSSL_NO_SSL3 +#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3) php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory); #endif php_stream_xport_register("tls", php_openssl_ssl_socket_factory); @@ -895,7 +895,7 @@ PHP_MSHUTDOWN_FUNCTION(openssl) php_unregister_url_stream_wrapper("ftps"); php_stream_xport_unregister("ssl"); -#ifndef OPENSSL_NO_SSL3 +#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3) php_stream_xport_unregister("sslv3"); #endif php_stream_xport_unregister("tls"); @@ -1395,8 +1395,8 @@ PHP_FUNCTION(openssl_x509_parse) zval subitem; zval critext; int critcount = 0; - X509_EXTENSION *extension; - X509_NAME *subject_name; + PHP_OPENSSL_X509_EXTENSION *extension; + const X509_NAME *subject_name; char *cert_name; char *extname; BIO *bio_out; @@ -2383,7 +2383,7 @@ PHP_FUNCTION(openssl_csr_get_subject) zend_object *csr_obj; zend_string *csr_str; bool use_shortnames = 1; - X509_NAME *subject; + const X509_NAME *subject; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str) diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 8115a7c91b3a..7261a617af32 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -33,16 +33,16 @@ /* true global; readonly after module startup */ static char default_ssl_conf_filename[MAXPATHLEN]; -void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname) +void php_openssl_add_assoc_name_entry(zval * val, char * key, const X509_NAME * name, int shortname) { zval *data; zval subitem, tmp; int i; char *sname; int nid; - X509_NAME_ENTRY * ne; - ASN1_STRING * str = NULL; - ASN1_OBJECT * obj; + const X509_NAME_ENTRY * ne; + const ASN1_STRING * str = NULL; + const ASN1_OBJECT * obj; if (key != NULL) { array_init(&subitem); @@ -106,7 +106,7 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, } } -void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) +void php_openssl_add_assoc_asn1_string(zval * val, char * key, const ASN1_STRING * str) { add_assoc_stringl(val, key, (const char *)ASN1_STRING_get0_data(str), ASN1_STRING_length(str)); } @@ -622,11 +622,11 @@ zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool r /* Special handling of subjectAltName, see CVE-2013-4073 * Christian Heimes */ -int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension) +int openssl_x509v3_subjectAltName(BIO *bio, PHP_OPENSSL_X509_EXTENSION *extension) { GENERAL_NAMES *names; const X509V3_EXT_METHOD *method = NULL; - ASN1_OCTET_STRING *extension_data; + const ASN1_OCTET_STRING *extension_data; long i, length, num; const unsigned char *p; @@ -982,7 +982,12 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr, zval *item, *subitem; zend_string *strindex = NULL; - subj = X509_REQ_get_subject_name(csr); + subj = X509_NAME_new(); + if (subj == NULL) { + php_openssl_store_errors(); + return FAILURE; + } + /* apply values from the dn hash */ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(dn), strindex, item) { if (strindex) { @@ -991,10 +996,12 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr, if (Z_TYPE_P(item) == IS_ARRAY) { ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(item), i, subitem) { if (php_openssl_csr_add_subj_entry(subitem, subj, nid) == FAILURE) { + X509_NAME_free(subj); return FAILURE; } } ZEND_HASH_FOREACH_END(); } else if (php_openssl_csr_add_subj_entry(item, subj, nid) == FAILURE) { + X509_NAME_free(subj); return FAILURE; } } else { @@ -1045,13 +1052,23 @@ zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr, if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_UTF8, (unsigned char*)v->value, -1, -1, 0)) { php_openssl_store_errors(); php_error_docref(NULL, E_WARNING, "add_entry_by_txt %s -> %s (failed)", type, v->value); + X509_NAME_free(subj); return FAILURE; } if (!X509_NAME_entry_count(subj)) { php_error_docref(NULL, E_WARNING, "No objects specified in config file"); + X509_NAME_free(subj); return FAILURE; } } + + if (!X509_REQ_set_subject_name(csr, subj)) { + php_openssl_store_errors(); + X509_NAME_free(subj); + return FAILURE; + } + X509_NAME_free(subj); + if (attribs) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(attribs), strindex, item) { int nid; diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index 339ff53f7b62..375c0104fac5 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -402,7 +402,7 @@ EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { } OPENSSL_PKEY_SET_BN(data, cofactor); - if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor) || + if ((cofactor && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor)) || !EC_GROUP_set_generator(group, point_g, order, cofactor)) { goto cleanup; } diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h index b88a6c59e4e9..588cca0e93d8 100644 --- a/ext/openssl/php_openssl.h +++ b/ext/openssl/php_openssl.h @@ -23,6 +23,7 @@ extern zend_module_entry openssl_module_entry; #include "php_version.h" #define PHP_OPENSSL_VERSION PHP_VERSION +#include #include /* OpenSSL version check */ #if OPENSSL_VERSION_NUMBER < 0x30000000L diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index bd12a5fe312f..fcbb8d42542f 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -35,6 +35,12 @@ #include #include +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 4 +typedef const X509_EXTENSION PHP_OPENSSL_X509_EXTENSION; +#else +typedef X509_EXTENSION PHP_OPENSSL_X509_EXTENSION; +#endif + /* number conversion flags checks */ #define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name, _arg_num) \ do { \ @@ -166,8 +172,8 @@ struct php_x509_request { const EVP_CIPHER * priv_key_encrypt_cipher; }; -void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname); -void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str); +void php_openssl_add_assoc_name_entry(zval * val, char * key, const X509_NAME * name, int shortname); +void php_openssl_add_assoc_asn1_string(zval * val, char * key, const ASN1_STRING * str); time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr); int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, CONF *config); char *php_openssl_conf_get_string(CONF *conf, const char *group, const char *name); @@ -265,7 +271,7 @@ X509 *php_openssl_x509_from_zval( zend_string* php_openssl_x509_fingerprint( X509 *peer, const char *method, bool raw, struct _php_stream *stream); -int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension); +int openssl_x509v3_subjectAltName(BIO *bio, PHP_OPENSSL_X509_EXTENSION *extension); STACK_OF(X509) *php_openssl_load_all_certs_from_file( char *cert_file, size_t cert_file_len, uint32_t arg_num); diff --git a/ext/openssl/tests/bug28382.phpt b/ext/openssl/tests/bug28382.phpt index e2842023135c..7627da2931b5 100644 --- a/ext/openssl/tests/bug28382.phpt +++ b/ext/openssl/tests/bug28382.phpt @@ -6,6 +6,12 @@ openssl = 0x40000000) { + $extensions = &$ext['extensions']; + $extensions['crlDistributionPoints'] = preg_replace('/^Full Name:\R\s*/', '', trim($extensions['crlDistributionPoints'])); +} + var_dump($ext['extensions']); /* * The reason for %A at the end of crlDistributionPoints and authorityKeyIdentifier is that @@ -30,7 +36,7 @@ array(11) { string(59) "B0:A7:FF:F9:41:15:DE:23:39:BD:DD:31:0F:97:A0:B2:A2:74:E0:FC" ["authorityKeyIdentifier"]=> string(%d) "DirName:/C=RO/ST=Romania/L=Craiova/O=Sergiu/OU=Sergiu SRL/CN=Sergiu CA/emailAddress=n_sergiu@hotmail.com -serial:00%A" +serial:%A" ["keyUsage"]=> string(71) "Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment" ["nsBaseUrl"]=> diff --git a/ext/openssl/tests/ecc_custom_params.phpt b/ext/openssl/tests/ecc_custom_params.phpt index 0c63af1450ba..e3854eefa4d8 100644 --- a/ext/openssl/tests/ecc_custom_params.phpt +++ b/ext/openssl/tests/ecc_custom_params.phpt @@ -5,6 +5,30 @@ openssl --SKIPIF-- = 0x40000000) { + $d = hex2bin('8D0AC65AAEA0D6B96254C65817D4A143A9E7A03876F1A37D'); + $p = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D46F4C318CEFE4AFE3B6B8551F'); + $a = hex2bin('BB8E5E8FBC115E139FE6A814FE48AAA6F0ADA1AA5DF91985'); + $b = hex2bin('1854BEBDC31B21B7AEFC80AB0ECD10D5B1B3308E6DBF11C1'); + $g_x = hex2bin('4AD5F7048DE709AD51236DE65E4D4B482C836DC6E4106640'); + $g_y = hex2bin('02BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2'); + $order = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D40FC962195DFAE76F56564677'); + + if (@openssl_pkey_new(array( + 'ec' => array( + 'p' => $p, + 'a' => $a, + 'b' => $b, + 'order' => $order, + 'g_x' => $g_x, + 'g_y' => $g_y, + 'd' => $d, + ), + )) === false) { + die("skip EC custom params unsupported with OpenSSL 4"); + } +} ?> --FILE-- = 3.2'); ?> --FILE-- --EXPECTF-- bool(true) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 93a9971b4a78..d4158634e5bc 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -93,7 +93,7 @@ #define HAVE_SEC_LEVEL 1 #endif -#ifndef OPENSSL_NO_SSL3 +#if OPENSSL_VERSION_NUMBER < 0x40000000L && !defined(OPENSSL_NO_SSL3) #define HAVE_SSL3 1 #define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3 #else @@ -274,7 +274,7 @@ static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, bool i errno = EAGAIN; retry = is_init ? true : sslsock->s.is_blocked; if (!retry) { - sslsock->last_status = err == SSL_ERROR_WANT_READ ? + sslsock->last_status = err == SSL_ERROR_WANT_READ ? STREAM_CRYPTO_STATUS_WANT_READ : STREAM_CRYPTO_STATUS_WANT_WRITE; } break; @@ -566,26 +566,50 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) / static bool php_openssl_matches_common_name(php_stream *stream, const X509 *peer, const char *subject_name) /* {{{ */ { - char buf[1024]; - X509_NAME *cert_name; + unsigned char *cert_name = NULL; +#if PHP_OPENSSL_API_VERSION < 0x30000 + X509_NAME *name; +#else + const X509_NAME *name; +#endif + const X509_NAME_ENTRY *name_entry; + const ASN1_STRING *name_asn1; bool is_match = false; + int name_index; int cert_name_len; - cert_name = X509_get_subject_name(peer); - cert_name_len = X509_NAME_get_text_by_NID(cert_name, NID_commonName, buf, sizeof(buf)); + name = X509_get_subject_name(peer); + name_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1); + if (name_index == -1) { + php_stream_warn(stream, NetworkRecvFailed, "Unable to locate peer certificate CN"); + return false; + } - if (cert_name_len == -1) { + name_entry = X509_NAME_get_entry(name, name_index); + if (name_entry == NULL) { php_stream_warn(stream, NetworkRecvFailed, "Unable to locate peer certificate CN"); - } else if ((size_t)cert_name_len != strlen(buf)) { - php_stream_warn(stream, AuthFailed, "Peer certificate CN=`%.*s' is malformed", cert_name_len, buf); - } else if (php_openssl_matches_wildcard_name(subject_name, buf)) { + return false; + } + name_asn1 = X509_NAME_ENTRY_get_data(name_entry); + cert_name_len = ASN1_STRING_length(name_asn1); + cert_name = (unsigned char *) OPENSSL_strndup((const char *) ASN1_STRING_get0_data(name_asn1), cert_name_len); + if (cert_name == NULL) { + php_stream_warn(stream, NetworkRecvFailed, "Unable to locate peer certificate CN"); + return false; + } + + if ((size_t)cert_name_len != strlen((const char *)cert_name)) { + php_stream_warn(stream, AuthFailed, "Peer certificate CN=`%.*s' is malformed", cert_name_len, (const char *)cert_name); + } else if (php_openssl_matches_wildcard_name(subject_name, (const char *)cert_name)) { is_match = true; } else { php_stream_warn(stream, AuthFailed, "Peer certificate CN=`%.*s' did not match expected CN=`%s'", - cert_name_len, buf, subject_name); + cert_name_len, (const char *)cert_name, subject_name); } + OPENSSL_free(cert_name); + return is_match; } /* }}} */ From 52b56eb2543c9acb9a6ef5169b5fcda098acb833 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 4 Jul 2026 01:32:47 +0800 Subject: [PATCH 038/211] ext/Intl: Fix IntlDateFormatter offset type validation (#22533) IntlDateFormatter::parse()/datefmt_parse() and IntlDateFormatter::localtime()/datefmt_localtime() now raise a TypeError when the offset argument is not of type int instead of silently converting the value. --- NEWS | 3 ++ UPGRADING | 4 ++ ext/intl/dateformat/dateformat_parse.cpp | 18 +++++--- ...fmt_parse_localtime_offset_type_error.phpt | 44 +++++++++++++++++++ 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 ext/intl/tests/datefmt_parse_localtime_offset_type_error.phpt diff --git a/NEWS b/NEWS index caddbd717490..216f3b2b52b9 100644 --- a/NEWS +++ b/NEWS @@ -132,6 +132,9 @@ PHP NEWS locale_get_display_keyword_value() respectively. (Weilin Du) . Fix incorrect argument positions for invalid start/end arguments in transliterator_transliterate(). (Weilin Du) + . IntlDateFormatter::parse()/datefmt_parse() and + IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError + when the offset argument is not of type int. (Weilin Du) . Fixed IntlTimeZone::getDisplayName() to synchronize object error state for invalid display types. (Weilin Du) . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the diff --git a/UPGRADING b/UPGRADING index eb6d182c54a7..a42d5ec355a6 100644 --- a/UPGRADING +++ b/UPGRADING @@ -56,6 +56,10 @@ PHP 8.6 UPGRADE NOTES . ResourceBundle::get() and resourcebundle_get() now report fallback-disabled resource lookups with "without fallback to " instead of the malformed "without fallback from to ". + . IntlDateFormatter::parse()/datefmt_parse() and + IntlDateFormatter::localtime()/datefmt_localtime() now raise a TypeError + when the offset argument is not of type int instead of silently converting + the value. - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is diff --git a/ext/intl/dateformat/dateformat_parse.cpp b/ext/intl/dateformat/dateformat_parse.cpp index d818627439e3..0537b42feec5 100644 --- a/ext/intl/dateformat/dateformat_parse.cpp +++ b/ext/intl/dateformat/dateformat_parse.cpp @@ -147,9 +147,12 @@ U_CFUNC PHP_FUNCTION(datefmt_parse) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - const zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); + bool failed; + const zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); + if (failed) { + zend_argument_type_error(hasThis() ? 2 : 3, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); + RETURN_THROWS(); + } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "String index is out of valid range."); @@ -229,9 +232,12 @@ U_CFUNC PHP_FUNCTION(datefmt_localtime) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - const zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); + bool failed; + const zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); + if (failed) { + zend_argument_type_error(hasThis() ? 2 : 3, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); + RETURN_THROWS(); + } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "String index is out of valid range."); diff --git a/ext/intl/tests/datefmt_parse_localtime_offset_type_error.phpt b/ext/intl/tests/datefmt_parse_localtime_offset_type_error.phpt new file mode 100644 index 000000000000..5c3d03cd4f65 --- /dev/null +++ b/ext/intl/tests/datefmt_parse_localtime_offset_type_error.phpt @@ -0,0 +1,44 @@ +--TEST-- +datefmt_parse() and datefmt_localtime() validate offset type +--EXTENSIONS-- +intl +--FILE-- +setPattern('VV'); + +$offset = 'offset'; +try { + $fmt->parse('America/Los_Angeles', $offset); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$offset = 'offset'; +try { + datefmt_parse($fmt, 'America/Los_Angeles', $offset); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$offset = 'offset'; +try { + $fmt->localtime('America/Los_Angeles', $offset); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +$offset = 'offset'; +try { + datefmt_localtime($fmt, 'America/Los_Angeles', $offset); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +IntlDateFormatter::parse(): Argument #2 ($offset) must be of type int, string given +datefmt_parse(): Argument #3 ($offset) must be of type int, string given +IntlDateFormatter::localtime(): Argument #2 ($offset) must be of type int, string given +datefmt_localtime(): Argument #3 ($offset) must be of type int, string given From 4fc0819165f8dcc307ccf7f2434342a612e000ac Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 4 Jul 2026 01:52:58 +0800 Subject: [PATCH 039/211] [skip ci] Fix NEWS entry for intl fix This moves the NEWS entry of #22533 to 8.6 alpha2 --- NEWS | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 216f3b2b52b9..3ea51b89f234 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ PHP NEWS . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) +- Intl: + . IntlDateFormatter::parse()/datefmt_parse() and + IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError + when the offset argument is not of type int. (Weilin Du) + - Opcache: . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache preloaded trait). (iliaal) @@ -132,9 +137,6 @@ PHP NEWS locale_get_display_keyword_value() respectively. (Weilin Du) . Fix incorrect argument positions for invalid start/end arguments in transliterator_transliterate(). (Weilin Du) - . IntlDateFormatter::parse()/datefmt_parse() and - IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError - when the offset argument is not of type int. (Weilin Du) . Fixed IntlTimeZone::getDisplayName() to synchronize object error state for invalid display types. (Weilin Du) . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the From dd5769440296460502b3674f72a6be8d43769ac3 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 3 Jul 2026 07:12:46 -0400 Subject: [PATCH 040/211] Fix GH-22570: stack overflow serializing a deeply nested Dom\XMLDocument The new-DOM XML serializer recurses through dom_xml_serialization_algorithm() for every element child, so a document nested deeply enough overflows the C stack and crashes during saveXml() or innerHTML. Add a stack-limit check at the dispatcher, throwing an Error on overflow, mirroring bd724bd. Gate the "Could not save document" warning and the innerHTML not-well-formed exception with !EG(exception) so the thrown Error propagates cleanly instead of being accompanied by a warning or replaced with the wrong exception type. Fixes GH-22570 Closes GH-22576 --- NEWS | 4 +++ ext/dom/document.c | 4 ++- ext/dom/inner_html_mixin.c | 4 ++- ext/dom/tests/modern/xml/gh22570.phpt | 40 +++++++++++++++++++++++++++ ext/dom/xml_serializer.c | 14 ++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 ext/dom/tests/modern/xml/gh22570.phpt diff --git a/NEWS b/NEWS index 6d4d1865d790..08a852a5a0d8 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ PHP NEWS - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) +- DOM: + . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested + Dom\XMLDocument). (iliaal) + - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) diff --git a/ext/dom/document.c b/ext/dom/document.c index e4d285c990fe..9c269e4cb14d 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1678,7 +1678,9 @@ static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry } if (!res) { - php_error_docref(NULL, E_WARNING, "Could not save document"); + if (!EG(exception)) { + php_error_docref(NULL, E_WARNING, "Could not save document"); + } RETURN_FALSE; } else { RETURN_NEW_STR(res); diff --git a/ext/dom/inner_html_mixin.c b/ext/dom/inner_html_mixin.c index 19f640e6b4ac..e81ca5a7b238 100644 --- a/ext/dom/inner_html_mixin.c +++ b/ext/dom/inner_html_mixin.c @@ -103,7 +103,9 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval) } if (UNEXPECTED(status < 0)) { smart_str_free_ex(&str, false); - php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true); + if (!EG(exception)) { + php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true); + } return FAILURE; } ZVAL_STR(retval, smart_str_extract(&str)); diff --git a/ext/dom/tests/modern/xml/gh22570.phpt b/ext/dom/tests/modern/xml/gh22570.phpt new file mode 100644 index 000000000000..af78acf00dfa --- /dev/null +++ b/ext/dom/tests/modern/xml/gh22570.phpt @@ -0,0 +1,40 @@ +--TEST-- +GH-22570 (Stack overflow when serializing a deeply nested Dom\XMLDocument) +--EXTENSIONS-- +dom +--SKIPIF-- + +--INI-- +zend.max_allowed_stack_size=512K +--FILE-- +appendChild($doc->createElement('root')); +for ($i = 0; $i < 100000; $i++) { + $node = $node->appendChild($doc->createElement('a')); +} + +try { + $doc->saveXml(); +} catch (\Error $e) { + echo "saveXml: ", $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + $doc->documentElement->innerHTML; +} catch (\Error $e) { + echo "innerHTML: ", $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +saveXml: Error: Maximum call stack size reached. Infinite recursion? +innerHTML: Error: Maximum call stack size reached. Infinite recursion? diff --git a/ext/dom/xml_serializer.c b/ext/dom/xml_serializer.c index 5e6a6f93b91c..2d9e55a7b706 100644 --- a/ext/dom/xml_serializer.c +++ b/ext/dom/xml_serializer.c @@ -1250,6 +1250,15 @@ static int dom_xml_serializing_a_document_node( return 0; } +static zend_always_inline bool dom_xml_serialize_check_stack_limit(void) +{ +#ifdef ZEND_CHECK_STACK_LIMIT + return zend_call_stack_overflowed(EG(stack_limit)); +#else + return false; +#endif +} + /* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-algorithm */ static int dom_xml_serialization_algorithm( dom_xml_serialize_ctx *ctx, @@ -1261,6 +1270,11 @@ static int dom_xml_serialization_algorithm( bool require_well_formed ) { + if (UNEXPECTED(dom_xml_serialize_check_stack_limit())) { + zend_throw_error(NULL, "Maximum call stack size reached. Infinite recursion?"); + return -1; + } + /* If node's interface is: */ switch (node->type) { case XML_ELEMENT_NODE: From 154881aa4ecaded7bee07b07debf108da061e8a3 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 4 Jul 2026 02:03:08 +0800 Subject: [PATCH 041/211] ext/intl: Fix NumberFormatter parse offset overflow (#22572) Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to reject offset values outside the 32-bit range instead of silently truncating them. --- NEWS | 3 ++ ext/intl/formatter/formatter_parse.cpp | 16 +++++-- .../formatter_parse_offset_overflow.phpt | 46 +++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 ext/intl/tests/formatter_parse_offset_overflow.phpt diff --git a/NEWS b/NEWS index 3ea51b89f234..61a49cc1750a 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,9 @@ PHP NEWS warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) - Intl: + . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to + reject offset values outside the 32-bit range instead of silently + truncating them. (Weilin Du) . IntlDateFormatter::parse()/datefmt_parse() and IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError when the offset argument is not of type int. (Weilin Du) diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index a475960809b8..63c0fba4329f 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -50,7 +50,12 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) } if (zposition) { - position = (int32_t) zval_get_long(zposition); + zend_long long_position = zval_get_long(zposition); + if (long_position < INT32_MIN || long_position > INT32_MAX) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } /* Fetch the object. */ @@ -155,8 +160,13 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); - if(zposition) { - position = (int32_t) zval_get_long(zposition); + if (zposition) { + zend_long long_position = zval_get_long(zposition); + if (long_position < INT32_MIN || long_position > INT32_MAX) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } icu::ParsePosition pp(position); diff --git a/ext/intl/tests/formatter_parse_offset_overflow.phpt b/ext/intl/tests/formatter_parse_offset_overflow.phpt new file mode 100644 index 000000000000..9421c8897511 --- /dev/null +++ b/ext/intl/tests/formatter_parse_offset_overflow.phpt @@ -0,0 +1,46 @@ +--TEST-- +NumberFormatter parse offset overflow +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; + } +} + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + $fmt->parse('123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + numfmt_parse($fmt, '123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + $currencyFmt->parseCurrency('$123.00', $currency, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + numfmt_parse_currency($currencyFmt, '$123.00', $currency, $offset); +}); +?> +--EXPECT-- +ValueError: NumberFormatter::parse(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse(): Argument #4 ($offset) must be between -2147483648 and 2147483647 +ValueError: NumberFormatter::parseCurrency(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse_currency(): Argument #4 ($offset) must be between -2147483648 and 2147483647 From 4629b3e1fa1abd5e720af5c3885756a975db1812 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 3 Jul 2026 13:03:27 -0700 Subject: [PATCH 042/211] [RFC] Allow `#[\Override]` on class constants (#20478) https://wiki.php.net/rfc/override_constants --- UPGRADING | 2 + .../with_Override_error_constant.phpt | 15 +++++ .../with_Override_error_enum_case.phpt | 15 +++++ .../with_Override_okay.phpt | 4 +- .../override/constants/anon_failure.phpt | 15 +++++ .../override/constants/anon_interface.phpt | 19 +++++++ .../override/constants/anon_parent.phpt | 19 +++++++ .../attributes/override/constants/basic.phpt | 57 +++++++++++++++++++ .../override/constants/enum_failure.phpt | 15 +++++ .../override/constants/enum_failure_case.phpt | 15 +++++ .../override/constants/failure.phpt | 15 +++++ .../override/constants/interface_failure.phpt | 15 +++++ .../override/constants/trait_failure.phpt | 19 +++++++ .../override/constants/trait_interface.phpt | 23 ++++++++ .../override/constants/trait_parent.phpt | 23 ++++++++ .../override/constants/trait_redeclared.phpt | 21 +++++++ .../constants/trait_redeclared_interface.phpt | 25 ++++++++ .../constants/trait_redeclared_parent.phpt | 25 ++++++++ .../override/constants/trait_unused.phpt | 15 +++++ .../override/constants/visibility_01.phpt | 19 +++++++ .../override/constants/visibility_02.phpt | 19 +++++++ .../override/constants/visibility_03.phpt | 19 +++++++ .../override/constants/visibility_04.phpt | 19 +++++++ Zend/zend_attributes.stub.php | 2 +- Zend/zend_attributes_arginfo.h | 4 +- Zend/zend_compile.c | 19 +++++++ Zend/zend_inheritance.c | 15 +++++ 27 files changed, 469 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt create mode 100644 Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt create mode 100644 Zend/tests/attributes/override/constants/anon_failure.phpt create mode 100644 Zend/tests/attributes/override/constants/anon_interface.phpt create mode 100644 Zend/tests/attributes/override/constants/anon_parent.phpt create mode 100644 Zend/tests/attributes/override/constants/basic.phpt create mode 100644 Zend/tests/attributes/override/constants/enum_failure.phpt create mode 100644 Zend/tests/attributes/override/constants/enum_failure_case.phpt create mode 100644 Zend/tests/attributes/override/constants/failure.phpt create mode 100644 Zend/tests/attributes/override/constants/interface_failure.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_failure.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_interface.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_parent.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_redeclared.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt create mode 100644 Zend/tests/attributes/override/constants/trait_unused.phpt create mode 100644 Zend/tests/attributes/override/constants/visibility_01.phpt create mode 100644 Zend/tests/attributes/override/constants/visibility_02.phpt create mode 100644 Zend/tests/attributes/override/constants/visibility_03.phpt create mode 100644 Zend/tests/attributes/override/constants/visibility_04.phpt diff --git a/UPGRADING b/UPGRADING index a42d5ec355a6..b9c659ccc405 100644 --- a/UPGRADING +++ b/UPGRADING @@ -215,6 +215,8 @@ PHP 8.6 UPGRADE NOTES needing to be present beforehand. . It is now possible to define the __debugInfo() magic method on enums. RFC: https://wiki.php.net/rfc/debugable-enums + . #[\Override] can now be applied to class constants, including enum cases. + RFC: https://wiki.php.net/rfc/override_constants - Curl: . curl_getinfo() return array now includes a new size_delivered key, which diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt new file mode 100644 index 000000000000..9ebb8e4c0202 --- /dev/null +++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (class constant) +--FILE-- + +--EXPECTF-- +Fatal error: DemoClass::CLASS_CONSTANT has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt new file mode 100644 index 000000000000..53d75412a849 --- /dev/null +++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (enum case) +--FILE-- + +--EXPECTF-- +Fatal error: DemoEnum::MyCase has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt index dd077f4b9cbd..494d85eaea9c 100644 --- a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt +++ b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt @@ -12,6 +12,8 @@ class Base { set => $value; } + public const CLASS_CONST = ''; + public function printVal() { echo __METHOD__ . "\n"; } @@ -34,7 +36,7 @@ class DemoClass extends Base { } #[DelayedTargetValidation] - #[Override] // Does nothing here + #[Override] // Does something here public const CLASS_CONST = 'FOO'; public function __construct( diff --git a/Zend/tests/attributes/override/constants/anon_failure.phpt b/Zend/tests/attributes/override/constants/anon_failure.phpt new file mode 100644 index 000000000000..c43cedda81d7 --- /dev/null +++ b/Zend/tests/attributes/override/constants/anon_failure.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - anonymous class, no interface or parent class +--FILE-- + +--EXPECTF-- +Fatal error: class@anonymous::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/anon_interface.phpt b/Zend/tests/attributes/override/constants/anon_interface.phpt new file mode 100644 index 000000000000..b19abfbf4d3a --- /dev/null +++ b/Zend/tests/attributes/override/constants/anon_interface.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - anonymous class overrides interface +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/anon_parent.phpt b/Zend/tests/attributes/override/constants/anon_parent.phpt new file mode 100644 index 000000000000..b430705c3c9d --- /dev/null +++ b/Zend/tests/attributes/override/constants/anon_parent.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - anonymous class overrides parent class +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/basic.phpt b/Zend/tests/attributes/override/constants/basic.phpt new file mode 100644 index 000000000000..c89007022051 --- /dev/null +++ b/Zend/tests/attributes/override/constants/basic.phpt @@ -0,0 +1,57 @@ +--TEST-- +#[\Override]: Constants - basic +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/enum_failure.phpt b/Zend/tests/attributes/override/constants/enum_failure.phpt new file mode 100644 index 000000000000..77ba225e9b9e --- /dev/null +++ b/Zend/tests/attributes/override/constants/enum_failure.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - no interface for enum constant +--FILE-- + +--EXPECTF-- +Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/enum_failure_case.phpt b/Zend/tests/attributes/override/constants/enum_failure_case.phpt new file mode 100644 index 000000000000..2f2c6fc84a08 --- /dev/null +++ b/Zend/tests/attributes/override/constants/enum_failure_case.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - no interface for enum case +--FILE-- + +--EXPECTF-- +Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/failure.phpt b/Zend/tests/attributes/override/constants/failure.phpt new file mode 100644 index 000000000000..be621915d927 --- /dev/null +++ b/Zend/tests/attributes/override/constants/failure.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - no interface or parent class +--FILE-- + +--EXPECTF-- +Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/interface_failure.phpt b/Zend/tests/attributes/override/constants/interface_failure.phpt new file mode 100644 index 000000000000..0aa4d33d1301 --- /dev/null +++ b/Zend/tests/attributes/override/constants/interface_failure.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - no parent interface +--FILE-- + +--EXPECTF-- +Fatal error: IFace::I has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/trait_failure.phpt b/Zend/tests/attributes/override/constants/trait_failure.phpt new file mode 100644 index 000000000000..8d4af0e57977 --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_failure.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - on a trait, no interface or parent class +--FILE-- + +--EXPECTF-- +Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/trait_interface.phpt b/Zend/tests/attributes/override/constants/trait_interface.phpt new file mode 100644 index 000000000000..3a2ba799ae4e --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_interface.phpt @@ -0,0 +1,23 @@ +--TEST-- +#[\Override]: Constants - on a trait, overrides interface +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/trait_parent.phpt b/Zend/tests/attributes/override/constants/trait_parent.phpt new file mode 100644 index 000000000000..2753aaf546a2 --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_parent.phpt @@ -0,0 +1,23 @@ +--TEST-- +#[\Override]: Constants - on a trait, overrides parent class +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/trait_redeclared.phpt b/Zend/tests/attributes/override/constants/trait_redeclared.phpt new file mode 100644 index 000000000000..984f9eedb668 --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_redeclared.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\Override]: Constants - trait constant redeclared, not overridden +--FILE-- + +--EXPECTF-- +Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt new file mode 100644 index 000000000000..21a1aa532249 --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt @@ -0,0 +1,25 @@ +--TEST-- +#[\Override]: Constants - trait constant redeclared, overrides interface +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt new file mode 100644 index 000000000000..c3d1e43d2534 --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt @@ -0,0 +1,25 @@ +--TEST-- +#[\Override]: Constants - trait constant redeclared, overrides parent class +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/trait_unused.phpt b/Zend/tests/attributes/override/constants/trait_unused.phpt new file mode 100644 index 000000000000..785f2888e90b --- /dev/null +++ b/Zend/tests/attributes/override/constants/trait_unused.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Override]: Constants - on a trait, unused +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/visibility_01.phpt b/Zend/tests/attributes/override/constants/visibility_01.phpt new file mode 100644 index 000000000000..49671471f758 --- /dev/null +++ b/Zend/tests/attributes/override/constants/visibility_01.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - private constant not overridden (by public constant) +--FILE-- + +--EXPECTF-- +Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/visibility_02.phpt b/Zend/tests/attributes/override/constants/visibility_02.phpt new file mode 100644 index 000000000000..c160a29a6180 --- /dev/null +++ b/Zend/tests/attributes/override/constants/visibility_02.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - private constant not overridden (by private constant) +--FILE-- + +--EXPECTF-- +Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d diff --git a/Zend/tests/attributes/override/constants/visibility_03.phpt b/Zend/tests/attributes/override/constants/visibility_03.phpt new file mode 100644 index 000000000000..db460f24375e --- /dev/null +++ b/Zend/tests/attributes/override/constants/visibility_03.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - protected constant is overridden (by public constant) +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/override/constants/visibility_04.phpt b/Zend/tests/attributes/override/constants/visibility_04.phpt new file mode 100644 index 000000000000..0ad46b022f43 --- /dev/null +++ b/Zend/tests/attributes/override/constants/visibility_04.phpt @@ -0,0 +1,19 @@ +--TEST-- +#[\Override]: Constants - protected constant is overridden (by protected constant) +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index ded9c89593a3..05fd285d5b9f 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -68,7 +68,7 @@ public function __debugInfo(): array {} /** * @strict-properties */ -#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY)] +#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY|Attribute::TARGET_CLASS_CONSTANT)] final class Override { public function __construct() {} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 54a66af29966..8acac398426f 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */ + * Stub hash: bae42fcb945360f90a1e2762c6c22177f25efb9e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -230,7 +230,7 @@ static zend_class_entry *register_class_Override(void) zend_string *attribute_name_Attribute_class_Override_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); zend_attribute *attribute_Attribute_class_Override_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override_0, 1); zend_string_release_ex(attribute_name_Attribute_class_Override_0, true); - ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY); + ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY | ZEND_ATTRIBUTE_TARGET_CLASS_CONST); return class_entry; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 4c1375215888..c75333e6d3c5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9370,6 +9370,15 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } + + const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1); + if (override) { + ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE; + /* We need to be able to remove the flag once the override is + * resolved. See ZEND_ACC_DEPRECATED above. */ + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + } } } } @@ -9829,6 +9838,16 @@ static void zend_compile_enum_case(zend_ast *ast) if (deprecated) { ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED; } + + const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1); + if (override) { + ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE; + /* We need to be able to remove the flag once the override is + * resolved. See ZEND_ACC_DEPRECATED handling in + * zend_compile_class_const_decl(). */ + enum_class->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + enum_class->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + } } } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 848b9d209b2d..583b10d34188 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2115,6 +2115,12 @@ static bool do_inherit_constant_check( ); } + if (!(ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_PRIVATE)) { + if (child_constant->ce == ce) { + ZEND_CLASS_CONST_FLAGS(child_constant) &= ~ZEND_ACC_OVERRIDE; + } + } + if (!(ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_PRIVATE) && ZEND_TYPE_IS_SET(parent_constant->type)) { inheritance_status status = class_constant_types_compatible(parent_constant, child_constant); if (status == INHERITANCE_ERROR) { @@ -2317,6 +2323,15 @@ void zend_inheritance_check_override(const zend_class_entry *ce) } } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, zend_string *name, zend_class_constant *c) { + if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_OVERRIDE) { + zend_error_noreturn( + E_COMPILE_ERROR, + "%s::%s has #[\\Override] attribute, but no matching parent constant exists", + ZSTR_VAL(ce->name), ZSTR_VAL(name)); + } + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, zend_property_info *prop) { if (prop->flags & ZEND_ACC_OVERRIDE) { zend_error_noreturn( From 19f94430d5c487bac4f26d406264d0fe543386e5 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Fri, 3 Jul 2026 23:04:59 +0200 Subject: [PATCH 043/211] ext/session: reject null bytes in save_path and referer_check (#22578) session.cookie_path/cookie_domain/cache_limiter reject null bytes with a warning (OnUpdateSessionStr), but session.save_path silently failed with no diagnostic and session.referer_check didn't check at all. Align both with the existing OnUpdateSessionStr behavior. --- ext/session/session.c | 18 ++++++++++---- ...ion_save_path_referer_check_null_byte.phpt | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 ext/session/tests/session_save_path_referer_check_null_byte.phpt diff --git a/ext/session/session.c b/ext/session/session.c index 1723acc4448c..dd968d453bda 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -647,12 +647,15 @@ static PHP_INI_MH(OnUpdateSaveDir) SESSION_CHECK_ACTIVE_STATE; SESSION_CHECK_OUTPUT_STATE; - /* Only do the open_basedir check at runtime */ - if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) { - if (zend_str_has_nul_byte(new_value)) { - return FAILURE; + if (zend_str_has_nul_byte(new_value)) { + if (stage != ZEND_INI_STAGE_DEACTIVATE) { + php_error_docref(NULL, E_WARNING, "\"%s\" must not contain null bytes", ZSTR_VAL(entry->name)); } + return FAILURE; + } + /* Only do the open_basedir check at runtime */ + if (stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) { /* we do not use zend_memrchr() since path can contain ; itself */ const char *p = strchr(ZSTR_VAL(new_value), ';'); if (p) { @@ -919,6 +922,13 @@ static PHP_INI_MH(OnUpdateRefererCheck) SESSION_CHECK_ACTIVE_STATE; SESSION_CHECK_OUTPUT_STATE; + if (zend_str_has_nul_byte(new_value)) { + if (stage != ZEND_INI_STAGE_DEACTIVATE) { + php_error_docref(NULL, E_WARNING, "\"%s\" must not contain null bytes", ZSTR_VAL(entry->name)); + } + return FAILURE; + } + if (ZSTR_LEN(new_value) != 0) { php_error_docref("session.configuration", E_DEPRECATED, "Usage of session.referer_check INI setting is deprecated"); } diff --git a/ext/session/tests/session_save_path_referer_check_null_byte.phpt b/ext/session/tests/session_save_path_referer_check_null_byte.phpt new file mode 100644 index 000000000000..9c4e87f2aa0a --- /dev/null +++ b/ext/session/tests/session_save_path_referer_check_null_byte.phpt @@ -0,0 +1,24 @@ +--TEST-- +session.save_path and session.referer_check must not contain null bytes +--EXTENSIONS-- +session +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: ini_set(): "session.save_path" must not contain null bytes in %s on line %d +bool(false) + +Warning: ini_set(): "session.referer_check" must not contain null bytes in %s on line %d +bool(false) +Done From d673a1f56ddeb45faacfc27bcb5063942a26f60a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 3 Jul 2026 21:30:16 -0400 Subject: [PATCH 044/211] Fix GH-22516: widen zend_mm srun free counter to cover bin 0 (#22530) The per-page free-slot counter in zend_mm_gc() is stored in a 9-bit field (ZEND_MM_SRUN_FREE_COUNTER_MASK, max 511). Bin 0 holds 512 slots, so a fully-free bin-0 page drives the counter to 512, which overflows the field and reads back as 0; the counter == bin_elements checks then never fire and the page is never reclaimed. Widen the field to 10 bits (0x03ff0000); bit 25 was unused and the write side already stays below it for every other bin. Bin 0 is only reachable where ZEND_MM_MIN_USEABLE_BIN_SIZE == 8 (32-bit, or heap-protection-disabled builds). Fixes GH-22516 --- Zend/zend_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d0f2b221b9a7..0b040743abf1 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -197,7 +197,7 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */ #define ZEND_MM_SRUN_BIN_NUM_MASK 0x0000001f #define ZEND_MM_SRUN_BIN_NUM_OFFSET 0 -#define ZEND_MM_SRUN_FREE_COUNTER_MASK 0x01ff0000 +#define ZEND_MM_SRUN_FREE_COUNTER_MASK 0x03ff0000 #define ZEND_MM_SRUN_FREE_COUNTER_OFFSET 16 #define ZEND_MM_NRUN_OFFSET_MASK 0x01ff0000 From ed56e07dff5e491787181cb118ea2febc8fd5211 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 4 Jul 2026 12:26:19 +0100 Subject: [PATCH 045/211] ext/soap: use {Type|Value}Errors for programming errors in SoapServer constructor (#22573) Add extra tests for option parsing of SoapServer constructor --- ext/soap/soap.c | 176 ++++++++++++------ .../SoapServer_constructor_errors.phpt | 91 +++++++++ .../tests/{bugs => SoapServer}/gh19784.phpt | 8 +- .../invalid-encoding-option-type.phpt | 29 +++ .../SoapServer/invalid-encoding-option.phpt | 4 +- .../invalid-soap_version-option-type.phpt | 29 +++ .../invalid-soap_version-option.phpt | 6 +- .../missing-options-non-wsdl-mode.phpt | 8 +- ext/soap/tests/bugs/gh16256.phpt | 3 +- 9 files changed, 283 insertions(+), 71 deletions(-) create mode 100644 ext/soap/tests/SoapServer/SoapServer_constructor_errors.phpt rename ext/soap/tests/{bugs => SoapServer}/gh19784.phpt (59%) create mode 100644 ext/soap/tests/SoapServer/invalid-encoding-option-type.phpt create mode 100644 ext/soap/tests/SoapServer/invalid-soap_version-option-type.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index d8f72e0191aa..d4845873e689 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -933,103 +933,157 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht) /* {{{ */ /* {{{ SoapServer constructor */ PHP_METHOD(SoapServer, __construct) { - soapServicePtr service; - zval *options = NULL; + HashTable *options = NULL; zend_string *wsdl; int version = SOAP_1_1; zend_long cache_wsdl; HashTable *typemap_ht = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!|a", &wsdl, &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!|h", &wsdl, &options) == FAILURE) { RETURN_THROWS(); } - SOAP_SERVER_BEGIN_CODE(); - - service = emalloc(sizeof(soapService)); + soapServicePtr service = emalloc(sizeof(soapService)); memset(service, 0, sizeof(soapService)); - service->send_errors = 1; + service->send_errors = true; cache_wsdl = SOAP_GLOBAL(cache_enabled) ? SOAP_GLOBAL(cache_mode) : 0; if (options != NULL) { - HashTable *ht = Z_ARRVAL_P(options); - zval *tmp; - - if ((tmp = zend_hash_str_find(ht, "soap_version", sizeof("soap_version")-1)) != NULL) { - if (Z_TYPE_P(tmp) == IS_LONG && - (Z_LVAL_P(tmp) == SOAP_1_1 || Z_LVAL_P(tmp) == SOAP_1_2)) { - version = Z_LVAL_P(tmp); - } else { - php_error_docref(NULL, E_ERROR, "'soap_version' option must be SOAP_1_1 or SOAP_1_2"); + const zval *soap_version_zv = zend_hash_str_find(options, ZEND_STRL("soap_version")); + if (soap_version_zv) { + if (UNEXPECTED(Z_TYPE_P(soap_version_zv) != IS_LONG)) { + zend_argument_type_error(2, "\"soap_version\" option must be of type int, %s given", zend_zval_type_name(soap_version_zv)); + goto cleanup; } + zend_long soap_version = Z_LVAL_P(soap_version_zv); + if (UNEXPECTED(soap_version != SOAP_1_1 && soap_version != SOAP_1_2)) { + zend_argument_value_error(2, "\"soap_version\" option must be SOAP_1_1 or SOAP_1_2"); + goto cleanup; + } + version = soap_version; } - if ((tmp = zend_hash_str_find(ht, "uri", sizeof("uri")-1)) != NULL && - Z_TYPE_P(tmp) == IS_STRING) { - service->uri = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + const zval *uri_zv = zend_hash_str_find(options, ZEND_STRL("uri")); + if (uri_zv) { + if (UNEXPECTED(Z_TYPE_P(uri_zv) != IS_STRING)) { + zend_argument_type_error(2, "\"uri\" option must be of type string, %s given", zend_zval_type_name(uri_zv)); + goto cleanup; + } + service->uri = estrndup(Z_STRVAL_P(uri_zv), Z_STRLEN_P(uri_zv)); } else if (!wsdl) { - php_error_docref(NULL, E_ERROR, "'uri' option is required in nonWSDL mode"); + zend_argument_value_error(2, "\"uri\" option is required when argument #1 ($wsdl) is null"); + goto cleanup; } - if ((tmp = zend_hash_str_find(ht, "actor", sizeof("actor")-1)) != NULL && - Z_TYPE_P(tmp) == IS_STRING) { - service->actor = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + const zval *actor_zv = zend_hash_str_find(options, ZEND_STRL("actor")); + if (actor_zv) { + if (UNEXPECTED(Z_TYPE_P(actor_zv) != IS_STRING)) { + zend_argument_type_error(2, "\"actor\" option must be of type string, %s given", zend_zval_type_name(actor_zv)); + goto cleanup; + } + service->actor = estrndup(Z_STRVAL_P(actor_zv), Z_STRLEN_P(actor_zv)); } - if ((tmp = zend_hash_str_find(ht, "encoding", sizeof("encoding")-1)) != NULL && - Z_TYPE_P(tmp) == IS_STRING) { - xmlCharEncodingHandlerPtr encoding; + const zval *encoding_zv = zend_hash_str_find(options, ZEND_STRL("encoding")); + if (encoding_zv) { + if (UNEXPECTED(Z_TYPE_P(encoding_zv) != IS_STRING)) { + zend_argument_type_error(2, "\"encoding\" option must be of type string, %s given", zend_zval_type_name(encoding_zv)); + goto cleanup; + } - encoding = xmlFindCharEncodingHandler(Z_STRVAL_P(tmp)); - if (encoding == NULL) { - php_error_docref(NULL, E_ERROR, "Invalid 'encoding' option - '%s'", Z_STRVAL_P(tmp)); - } else { - service->encoding = encoding; + // TODO Check for null bytes? + xmlCharEncodingHandlerPtr encoding = xmlFindCharEncodingHandler(Z_STRVAL_P(encoding_zv)); + if (UNEXPECTED(encoding == NULL)) { + zend_argument_value_error(2, "\"encoding\" option must be a valid encoding, \"%s\" given", Z_STRVAL_P(encoding_zv)); + goto cleanup; } + service->encoding = encoding; } - if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && - Z_TYPE_P(tmp) == IS_ARRAY) { - if (HT_IS_PACKED(Z_ARRVAL_P(tmp))) { - php_error_docref(NULL, E_ERROR, "'classmap' option must be an associative array"); + const zval *class_map_zv = zend_hash_str_find(options, ZEND_STRL("classmap")); + if (class_map_zv) { + if (UNEXPECTED(Z_TYPE_P(class_map_zv) != IS_ARRAY)) { + zend_argument_type_error(2, "\"classmap\" option must be of type array, %s given", zend_zval_type_name(class_map_zv)); + goto cleanup; } - service->class_map = zend_array_dup(Z_ARRVAL_P(tmp)); + // TODO: this still accepts mixed keys arrays and not all numerically indexed arrays are packed + if (HT_IS_PACKED(Z_ARRVAL_P(class_map_zv))) { + zend_argument_value_error(2, "\"classmap\" option must be an associative array"); + goto cleanup; + } + service->class_map = zend_array_dup(Z_ARR_P(class_map_zv)); } - if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && - Z_TYPE_P(tmp) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(tmp)) > 0) { - typemap_ht = Z_ARRVAL_P(tmp); + const zval *type_map_zv = zend_hash_str_find(options, ZEND_STRL("typemap")); + if (type_map_zv) { + if (UNEXPECTED(Z_TYPE_P(type_map_zv) != IS_ARRAY)) { + zend_argument_type_error(2, "\"typemap\" option must be of type array, %s given", zend_zval_type_name(type_map_zv)); + goto cleanup; + } + if (zend_hash_num_elements(Z_ARR_P(type_map_zv)) > 0) { + typemap_ht = Z_ARRVAL_P(type_map_zv); + } } - if ((tmp = zend_hash_str_find(ht, "features", sizeof("features")-1)) != NULL && - Z_TYPE_P(tmp) == IS_LONG) { - service->features = Z_LVAL_P(tmp); + const zval *features_zv = zend_hash_str_find(options, ZEND_STRL("features")); + if (features_zv) { + if (UNEXPECTED(Z_TYPE_P(features_zv) != IS_LONG)) { + zend_argument_type_error(2, "\"features\" option must be of type int, %s given", zend_zval_type_name(features_zv)); + goto cleanup; + } + service->features = Z_LVAL_P(features_zv); } - if ((tmp = zend_hash_str_find(ht, "cache_wsdl", sizeof("cache_wsdl")-1)) != NULL && - Z_TYPE_P(tmp) == IS_LONG) { - cache_wsdl = Z_LVAL_P(tmp); + const zval *cache_wsdl_zv = zend_hash_str_find(options, ZEND_STRL("cache_wsdl")); + if (cache_wsdl_zv) { + if (UNEXPECTED(Z_TYPE_P(cache_wsdl_zv) != IS_LONG)) { + zend_argument_type_error(2, "\"cache_wsdl\" option must be of type int, %s given", zend_zval_type_name(cache_wsdl_zv)); + goto cleanup; + } + cache_wsdl = Z_LVAL_P(cache_wsdl_zv); } - if ((tmp = zend_hash_str_find(ht, "send_errors", sizeof("send_errors")-1)) != NULL) { - if (Z_TYPE_P(tmp) == IS_FALSE) { - service->send_errors = 0; - } else if (Z_TYPE_P(tmp) == IS_TRUE) { - service->send_errors = 1; - } else if (Z_TYPE_P(tmp) == IS_LONG) { - service->send_errors = Z_LVAL_P(tmp); + const zval *send_errors_zv = zend_hash_str_find(options, ZEND_STRL("send_errors")); + if (send_errors_zv) { + if (UNEXPECTED(Z_TYPE_P(send_errors_zv) != IS_FALSE && Z_TYPE_P(send_errors_zv) != IS_TRUE && Z_TYPE_P(send_errors_zv) != IS_LONG)) { + zend_argument_type_error(2, "\"send_errors\" option must be of type bool, %s given", zend_zval_type_name(send_errors_zv)); + goto cleanup; + } + switch (Z_TYPE_P(send_errors_zv)) { + case IS_FALSE: + service->send_errors = false; + break; + case IS_TRUE: + service->send_errors = true; + break; + case IS_LONG: + service->send_errors = Z_LVAL_P(send_errors_zv); + break; } } - if ((tmp = zend_hash_find(ht, ZSTR_KNOWN(ZEND_STR_TRACE))) != NULL && - (Z_TYPE_P(tmp) == IS_TRUE || - (Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) == 1))) { - service->trace = true; + const zval *trace_zv = zend_hash_find(options, ZSTR_KNOWN(ZEND_STR_TRACE)); + if (trace_zv) { + if (UNEXPECTED(Z_TYPE_P(trace_zv) != IS_FALSE && Z_TYPE_P(trace_zv) != IS_TRUE && Z_TYPE_P(trace_zv) != IS_LONG)) { + zend_argument_type_error(2, "\"trace\" option must be of type bool, %s given", zend_zval_type_name(trace_zv)); + goto cleanup; + } + switch (Z_TYPE_P(trace_zv)) { + case IS_FALSE: + service->trace = false; + break; + case IS_TRUE: + service->trace = true; + break; + case IS_LONG: + service->trace = Z_LVAL_P(trace_zv); + break; + } } } else if (!wsdl) { - php_error_docref(NULL, E_ERROR, "'uri' option is required in nonWSDL mode"); + zend_argument_value_error(2, "\"uri\" option is required when argument #1 ($wsdl) is null"); + goto cleanup; } service->version = version; @@ -1037,6 +1091,7 @@ PHP_METHOD(SoapServer, __construct) service->soap_functions.functions_all = FALSE; service->soap_functions.ft = zend_new_array(0); + SOAP_SERVER_BEGIN_CODE(); if (wsdl) { zend_try { service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); @@ -1063,6 +1118,11 @@ PHP_METHOD(SoapServer, __construct) server_obj->service = service; SOAP_SERVER_END_CODE(); + return; + +cleanup: + delete_service(service); + RETURN_THROWS(); } /* }}} */ diff --git a/ext/soap/tests/SoapServer/SoapServer_constructor_errors.phpt b/ext/soap/tests/SoapServer/SoapServer_constructor_errors.phpt new file mode 100644 index 000000000000..61b0a352b339 --- /dev/null +++ b/ext/soap/tests/SoapServer/SoapServer_constructor_errors.phpt @@ -0,0 +1,91 @@ +--TEST-- +SoapServer constructor invalid type errors +--EXTENSIONS-- +soap +--FILE-- + 25, +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'actor' => 25, +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'classmap' => 25, +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'typemap' => 25, +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'features' => 'not an int', +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'cache_wsdl' => 'not an int', +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'send_errors' => 'not an int or bool', +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +$options = [ + 'trace' => 'not an int or bool', +]; +try { + $server = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +TypeError: SoapServer::__construct(): Argument #2 ($options) "uri" option must be of type string, int given +TypeError: SoapServer::__construct(): Argument #2 ($options) "actor" option must be of type string, int given +TypeError: SoapServer::__construct(): Argument #2 ($options) "classmap" option must be of type array, int given +TypeError: SoapServer::__construct(): Argument #2 ($options) "typemap" option must be of type array, int given +TypeError: SoapServer::__construct(): Argument #2 ($options) "features" option must be of type int, string given +TypeError: SoapServer::__construct(): Argument #2 ($options) "cache_wsdl" option must be of type int, string given +TypeError: SoapServer::__construct(): Argument #2 ($options) "send_errors" option must be of type bool, string given +TypeError: SoapServer::__construct(): Argument #2 ($options) "trace" option must be of type bool, string given diff --git a/ext/soap/tests/bugs/gh19784.phpt b/ext/soap/tests/SoapServer/gh19784.phpt similarity index 59% rename from ext/soap/tests/bugs/gh19784.phpt rename to ext/soap/tests/SoapServer/gh19784.phpt index 1f718e74c459..d855bc47909a 100644 --- a/ext/soap/tests/bugs/gh19784.phpt +++ b/ext/soap/tests/SoapServer/gh19784.phpt @@ -4,10 +4,10 @@ GH-19784 (SoapServer memory leak) soap --FILE-- $v_5257); -new SoapServer('foobarbaz',$v_5238,); + +$options = ['encoding' => 'UTF-8']; +$server = new SoapServer('foobarbaz', $options); + ?> --EXPECTF-- diff --git a/ext/soap/tests/SoapServer/invalid-encoding-option-type.phpt b/ext/soap/tests/SoapServer/invalid-encoding-option-type.phpt new file mode 100644 index 000000000000..c67e86c322c0 --- /dev/null +++ b/ext/soap/tests/SoapServer/invalid-encoding-option-type.phpt @@ -0,0 +1,29 @@ +--TEST-- +SoapServer constructor with an invalid encoding option +--EXTENSIONS-- +soap +--FILE-- + 25, +]; +try { + $client = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + $client = new ExtendedSoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + + +?> +--EXPECT-- +TypeError: SoapServer::__construct(): Argument #2 ($options) "encoding" option must be of type string, int given +TypeError: SoapServer::__construct(): Argument #2 ($options) "encoding" option must be of type string, int given diff --git a/ext/soap/tests/SoapServer/invalid-encoding-option.phpt b/ext/soap/tests/SoapServer/invalid-encoding-option.phpt index f594d57dd8a4..08b27c43a673 100644 --- a/ext/soap/tests/SoapServer/invalid-encoding-option.phpt +++ b/ext/soap/tests/SoapServer/invalid-encoding-option.phpt @@ -25,5 +25,5 @@ try { ?> --EXPECT-- - -SOAP-ENV:ServerSoapServer::__construct(): Invalid 'encoding' option - 'non-sense' +ValueError: SoapServer::__construct(): Argument #2 ($options) "encoding" option must be a valid encoding, "non-sense" given +ValueError: SoapServer::__construct(): Argument #2 ($options) "encoding" option must be a valid encoding, "non-sense" given diff --git a/ext/soap/tests/SoapServer/invalid-soap_version-option-type.phpt b/ext/soap/tests/SoapServer/invalid-soap_version-option-type.phpt new file mode 100644 index 000000000000..690a35159a7c --- /dev/null +++ b/ext/soap/tests/SoapServer/invalid-soap_version-option-type.phpt @@ -0,0 +1,29 @@ +--TEST-- +SoapServer constructor with an invalid soap_version option +--EXTENSIONS-- +soap +--FILE-- + "I am not an integer", +]; +try { + $client = new SoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + $client = new ExtendedSoapServer($wsdl, $options); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + + +?> +--EXPECT-- +TypeError: SoapServer::__construct(): Argument #2 ($options) "soap_version" option must be of type int, string given +TypeError: SoapServer::__construct(): Argument #2 ($options) "soap_version" option must be of type int, string given diff --git a/ext/soap/tests/SoapServer/invalid-soap_version-option.phpt b/ext/soap/tests/SoapServer/invalid-soap_version-option.phpt index 17e45f2dac1b..6b8b25e44e12 100644 --- a/ext/soap/tests/SoapServer/invalid-soap_version-option.phpt +++ b/ext/soap/tests/SoapServer/invalid-soap_version-option.phpt @@ -1,5 +1,5 @@ --TEST-- -SoapServer constructor with an invalid encoding option +SoapServer constructor with an invalid soap_version option --EXTENSIONS-- soap --FILE-- @@ -25,5 +25,5 @@ try { ?> --EXPECT-- - -SOAP-ENV:ServerSoapServer::__construct(): 'soap_version' option must be SOAP_1_1 or SOAP_1_2 +ValueError: SoapServer::__construct(): Argument #2 ($options) "soap_version" option must be SOAP_1_1 or SOAP_1_2 +ValueError: SoapServer::__construct(): Argument #2 ($options) "soap_version" option must be SOAP_1_1 or SOAP_1_2 diff --git a/ext/soap/tests/SoapServer/missing-options-non-wsdl-mode.phpt b/ext/soap/tests/SoapServer/missing-options-non-wsdl-mode.phpt index 54e6493879e1..4f1fc89195ed 100644 --- a/ext/soap/tests/SoapServer/missing-options-non-wsdl-mode.phpt +++ b/ext/soap/tests/SoapServer/missing-options-non-wsdl-mode.phpt @@ -48,5 +48,9 @@ try { ?> --EXPECT-- $options not provided - -SOAP-ENV:ServerSoapServer::__construct(): 'uri' option is required in nonWSDL mode +ValueError: SoapServer::__construct(): Argument #2 ($options) "uri" option is required when argument #1 ($wsdl) is null +ValueError: SoapServer::__construct(): Argument #2 ($options) "uri" option is required when argument #1 ($wsdl) is null +Empty $options array +ValueError: SoapServer::__construct(): Argument #2 ($options) "uri" option is required when argument #1 ($wsdl) is null +ValueError: SoapServer::__construct(): Argument #2 ($options) "uri" option is required when argument #1 ($wsdl) is null +$options array only sets "uri" option diff --git a/ext/soap/tests/bugs/gh16256.phpt b/ext/soap/tests/bugs/gh16256.phpt index ca8c00af5bbf..ce2ba1314069 100644 --- a/ext/soap/tests/bugs/gh16256.phpt +++ b/ext/soap/tests/bugs/gh16256.phpt @@ -21,5 +21,4 @@ try { ?> --EXPECT-- SoapClient::__construct(): 'classmap' option must be an associative array - -SOAP-ENV:ServerSoapServer::__construct(): 'classmap' option must be an associative array +SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array From 3afe4e3edeaee2d5c7cf7552e783ab9b3f41f75e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 4 Jul 2026 19:52:47 +0800 Subject: [PATCH 046/211] Remove various useless `#include` (#22581) This removes leftover win32/param.h / includes from files that do not use any of the macros provided by those headers (MAXPATHLEN, MAXHOSTNAMELEN, howmany, roundup). It also removes local Windows O_RDONLY -> _O_RDONLY aliases from files where O_RDONLY is not used. --- ext/ffi/ffi.c | 2 -- ext/standard/ftp_fopen_wrapper.c | 4 ---- ext/standard/http_fopen_wrapper.c | 7 ------- main/network.c | 4 ---- main/php_scandir.c | 1 - sapi/phpdbg/phpdbg_prompt.c | 2 -- 6 files changed, 20 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 779b41fcad3d..c3c08327fbe2 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -34,11 +34,9 @@ #ifdef HAVE_LIBDL #ifdef PHP_WIN32 -#include "win32/param.h" #include "win32/winutil.h" #define GET_DL_ERROR() php_win_err() #else -#include #define GET_DL_ERROR() DL_ERROR() #endif #endif diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 457d1410aabe..dfe905a8c220 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -30,10 +30,6 @@ #ifdef PHP_WIN32 #include -#define O_RDONLY _O_RDONLY -#include "win32/param.h" -#else -#include #endif #include "php_standard.h" diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 195394c7ed8d..4b2aa7116a24 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -33,13 +33,6 @@ #include #include -#ifdef PHP_WIN32 -#define O_RDONLY _O_RDONLY -#include "win32/param.h" -#else -#include -#endif - #include "php_standard.h" #ifdef HAVE_SYS_SOCKET_H diff --git a/main/network.c b/main/network.c index 84a755505b21..b4d3ded15196 100644 --- a/main/network.c +++ b/main/network.c @@ -24,10 +24,6 @@ #ifdef PHP_WIN32 # include # include "win32/winutil.h" -# define O_RDONLY _O_RDONLY -# include "win32/param.h" -#else -#include #endif #include diff --git a/main/php_scandir.c b/main/php_scandir.c index 848a0cf96168..c180272e5f48 100644 --- a/main/php_scandir.c +++ b/main/php_scandir.c @@ -27,7 +27,6 @@ #ifndef HAVE_SCANDIR #ifdef PHP_WIN32 -#include "win32/param.h" #include "win32/readdir.h" #endif diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 2da7e4193001..d6249ae068da 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -48,11 +48,9 @@ extern int phpdbg_startup_run; #ifdef HAVE_LIBDL #ifdef PHP_WIN32 -#include "win32/param.h" #include "win32/winutil.h" #define GET_DL_ERROR() php_win_err() #else -#include #define GET_DL_ERROR() DL_ERROR() #endif #endif From a437eab2f68df60e6c51199fa9579c58e7eeeb3e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 4 Jul 2026 04:59:26 +0100 Subject: [PATCH 047/211] ext/soap: fix use of uninitialized params in do_request() on OOM bailout. Fix #22585 close GH-22586 --- NEWS | 4 ++++ ext/soap/soap.c | 4 ++++ ext/soap/tests/gh22585.phpt | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 ext/soap/tests/gh22585.phpt diff --git a/NEWS b/NEWS index e5dd4913e580..3ed4f27a6ac1 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,10 @@ PHP NEWS . Fixed bug GH-21314 (Different session garbage collector behavior between PHP 8.3 and PHP 8.5). (jorgsowa) +- SOAP: + . Fixed bug GH-22585 (OOM on bailout with uninitialized + do_request() parameters). (David Carlier) + - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL and a proxy set). (CVE-2026-12184) (ndossche) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index d4845873e689..a3e03e0fb58b 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2333,6 +2333,10 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co return false; } + ZVAL_UNDEF(¶ms[0]); + ZVAL_UNDEF(¶ms[1]); + ZVAL_UNDEF(¶ms[2]); + zend_try { ZVAL_STRINGL(¶ms[0], buf, buf_size); ZVAL_STRING(¶ms[1], location); diff --git a/ext/soap/tests/gh22585.phpt b/ext/soap/tests/gh22585.phpt new file mode 100644 index 000000000000..9d007d012b44 --- /dev/null +++ b/ext/soap/tests/gh22585.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-22585 (Use of uninitialized params in do_request() on out-of-memory bailout) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +memory_limit=64M +--FILE-- + "http://localhost/soap.php", 'uri' => "http://localhost/"] + ); + $soap->call(1.1); + main(); + } + } + + main(); +} catch (\Throwable $e) { +} +echo "Done" . PHP_EOL; +?> +--EXPECTREGEX-- +(?s)(Done|.*Allowed memory size of \d+ bytes exhausted.*) From cf2f699a6de663554c000bc0cdf26e3a18f5ae9f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 4 Jul 2026 14:20:54 +0100 Subject: [PATCH 048/211] ext/soap: backport GH-22586 fix. close GH-22592 --- ext/soap/soap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index ce12aeaf364c..3d5536ef8628 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2235,6 +2235,10 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co return false; } + ZVAL_UNDEF(¶ms[0]); + ZVAL_UNDEF(¶ms[1]); + ZVAL_UNDEF(¶ms[2]); + zend_try { zval *trace = Z_CLIENT_TRACE_P(this_ptr); if (Z_TYPE_P(trace) == IS_TRUE) { From 21bf7f67ff0e53eee15d79d5a8862e2f1c4a37c4 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Sat, 4 Jul 2026 20:07:49 +0200 Subject: [PATCH 049/211] phpdbg: Fix off-by-one in phpdbg_safe_class_lookup() signal-safe class lookup (#22593) lc_length was set to name_length + 1, one past the actual lowercased string length, so zend_hash_str_find_ptr() (which expects a strlen-style length) never matched an existing class entry. This made class lookups during phpdbg's signal-handler interruption path always fail silently. Replaced the manual emalloc/tolower/efree dance with zend_hash_str_find_ptr_lc(), which performs the lowercase copy and lookup in one call and removes the surface for this class of bug. --- NEWS | 2 ++ sapi/phpdbg/phpdbg_utils.c | 17 ++++------------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 08a852a5a0d8..d950ce969fc1 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,8 @@ PHP NEWS . Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal) . Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break. (jorgsowa) + . Fixed off-by-one in phpdbg_safe_class_lookup() causing class lookups to + always fail during phpdbg's signal-safe interruption path. (jorgsowa) - Reflection: . Fixed bug GH-22324 (Ignore leading namespace separator in diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 7465004e99ad..b3d3445b6272 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -397,29 +397,20 @@ PHPDBG_API void phpdbg_set_async_io(int fd) { int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry **ce) { if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { - char *lc_name, *lc_free; - int lc_length; - if (name == NULL || !name_length) { return FAILURE; } - lc_free = lc_name = emalloc(name_length + 1); - zend_str_tolower_copy(lc_name, name, name_length); - lc_length = name_length + 1; - - if (lc_name[0] == '\\') { - lc_name += 1; - lc_length -= 1; + if (name[0] == '\\') { + name += 1; + name_length -= 1; } phpdbg_try_access { - *ce = zend_hash_str_find_ptr(EG(class_table), lc_name, lc_length); + *ce = zend_hash_str_find_ptr_lc(EG(class_table), name, name_length); } phpdbg_catch_access { phpdbg_error("Could not fetch class %.*s, invalid data source", name_length, name); } phpdbg_end_try_access(); - - efree(lc_free); } else { zend_string *str_name = zend_string_init(name, name_length, 0); *ce = zend_lookup_class(str_name); From 40780218f5dfff88643b662594d89f05b4f6ee06 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 4 Jul 2026 21:00:24 -0700 Subject: [PATCH 050/211] Add and use known string for NoDiscard (#21810) --- Zend/zend_string.h | 1 + build/gen_stub.php | 1 + ext/date/php_date_arginfo.h | 36 +++++++++--------------------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 971902cabd2b..e0d1c026a2f2 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -709,6 +709,7 @@ default: ZEND_UNREACHABLE(); _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ _(ZEND_STR_SINCE, "since") \ + _(ZEND_STR_NODISCARD, "NoDiscard") \ _(ZEND_STR_GET, "get") \ _(ZEND_STR_SET, "set") \ _(ZEND_STR_8_DOT_0, "8.0") \ diff --git a/build/gen_stub.php b/build/gen_stub.php index e60e61885206..1e1d53c5146c 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2976,6 +2976,7 @@ class StringBuilder { // NEW in 8.6 private const PHP_86_KNOWN = [ "arguments" => "ZEND_STR_ARGUMENTS", + "NoDiscard" => "ZEND_STR_NODISCARD", ]; /** diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 4a5a4ad32dec..db2b4d5ea9e1 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1025,65 +1025,47 @@ static zend_class_entry *register_class_DateTimeImmutable(zend_class_entry *clas ZVAL_STR(&attribute_Deprecated_func___wakeup_0->args[1].value, attribute_Deprecated_func___wakeup_0_arg1_str); attribute_Deprecated_func___wakeup_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_modify_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), attribute_name_NoDiscard_func_modify_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_modify_0, true); + zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_modify_0_arg0_str = zend_string_init("as DateTimeImmutable::modify() does not modify the object itself", strlen("as DateTimeImmutable::modify() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_modify_0->args[0].value, attribute_NoDiscard_func_modify_0_arg0_str); attribute_NoDiscard_func_modify_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_add_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), attribute_name_NoDiscard_func_add_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_add_0, true); + zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_add_0_arg0_str = zend_string_init("as DateTimeImmutable::add() does not modify the object itself", strlen("as DateTimeImmutable::add() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_add_0->args[0].value, attribute_NoDiscard_func_add_0_arg0_str); attribute_NoDiscard_func_add_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_sub_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), attribute_name_NoDiscard_func_sub_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_sub_0, true); + zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_sub_0_arg0_str = zend_string_init("as DateTimeImmutable::sub() does not modify the object itself", strlen("as DateTimeImmutable::sub() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_sub_0->args[0].value, attribute_NoDiscard_func_sub_0_arg0_str); attribute_NoDiscard_func_sub_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settimezone_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), attribute_name_NoDiscard_func_settimezone_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settimezone_0, true); + zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settimezone_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimezone() does not modify the object itself", strlen("as DateTimeImmutable::setTimezone() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settimezone_0->args[0].value, attribute_NoDiscard_func_settimezone_0_arg0_str); attribute_NoDiscard_func_settimezone_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settime_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), attribute_name_NoDiscard_func_settime_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settime_0, true); + zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settime_0_arg0_str = zend_string_init("as DateTimeImmutable::setTime() does not modify the object itself", strlen("as DateTimeImmutable::setTime() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settime_0->args[0].value, attribute_NoDiscard_func_settime_0_arg0_str); attribute_NoDiscard_func_settime_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setdate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), attribute_name_NoDiscard_func_setdate_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setdate_0, true); + zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setdate_0_arg0_str = zend_string_init("as DateTimeImmutable::setDate() does not modify the object itself", strlen("as DateTimeImmutable::setDate() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setdate_0->args[0].value, attribute_NoDiscard_func_setdate_0_arg0_str); attribute_NoDiscard_func_setdate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setisodate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), attribute_name_NoDiscard_func_setisodate_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setisodate_0, true); + zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setisodate_0_arg0_str = zend_string_init("as DateTimeImmutable::setISODate() does not modify the object itself", strlen("as DateTimeImmutable::setISODate() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setisodate_0->args[0].value, attribute_NoDiscard_func_setisodate_0_arg0_str); attribute_NoDiscard_func_setisodate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settimestamp_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), attribute_name_NoDiscard_func_settimestamp_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settimestamp_0, true); + zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settimestamp_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimestamp() does not modify the object itself", strlen("as DateTimeImmutable::setTimestamp() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settimestamp_0->args[0].value, attribute_NoDiscard_func_settimestamp_0_arg0_str); attribute_NoDiscard_func_settimestamp_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setmicrosecond_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), attribute_name_NoDiscard_func_setmicrosecond_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setmicrosecond_0, true); + zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setmicrosecond_0_arg0_str = zend_string_init("as DateTimeImmutable::setMicrosecond() does not modify the object itself", strlen("as DateTimeImmutable::setMicrosecond() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setmicrosecond_0->args[0].value, attribute_NoDiscard_func_setmicrosecond_0_arg0_str); attribute_NoDiscard_func_setmicrosecond_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); From aa12a652c33755f2b932b34442f2109b376d5ea7 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 5 Jul 2026 12:28:12 +0800 Subject: [PATCH 051/211] ext/soap: Add `UNEXPECTED()` in obvious error path (#22596) This added `UNEXPECTED()` in several obvious error path for optimization in ext/soap. --- ext/soap/php_encoding.c | 4 ++-- ext/soap/php_http.c | 10 +++++----- ext/soap/php_packet_soap.c | 10 +++++----- ext/soap/php_schema.c | 4 ++-- ext/soap/php_sdl.c | 10 +++++----- ext/soap/soap.c | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index f02a16638025..e474798df6da 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2052,7 +2052,7 @@ static int calc_dimension_12(const char* str) static void soap_array_position_add_digit(int *position, int digit) { - if (*position > (INT_MAX - digit) / 10) { + if (UNEXPECTED(*position > (INT_MAX - digit) / 10)) { soap_error0(E_ERROR, "Encoding: array index out of range"); } @@ -2697,7 +2697,7 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data) i = dimension; while (i > 0) { i--; - if (pos[i] == INT_MAX) { + if (UNEXPECTED(pos[i] == INT_MAX)) { efree(dims); efree(pos); zval_ptr_dtor(ret); diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 4fe272ea6f13..b02942f3dccb 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1488,7 +1488,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h if (buf_size > 0) { size_t len_size = 0; - if (http_buf_size + buf_size + 1 < 0) { + if (UNEXPECTED(http_buf_size + buf_size + 1 < 0)) { if (http_buf) { zend_string_release_ex(http_buf, 0); } @@ -1503,7 +1503,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h while (len_size < buf_size) { ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size); - if (len_read <= 0) { + if (UNEXPECTED(len_read <= 0)) { /* Error or EOF */ done = true; break; @@ -1517,7 +1517,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h if (ch == '\r') { ch = php_stream_getc(stream); } - if (ch != '\n') { + if (UNEXPECTED(ch != '\n')) { /* Something wrong in chunked encoding */ if (http_buf) { zend_string_release_ex(http_buf, 0); @@ -1555,13 +1555,13 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h } } else if (header_length) { - if (header_length < 0 || header_length >= INT_MAX) { + if (UNEXPECTED(header_length < 0 || header_length >= INT_MAX)) { return NULL; } http_buf = zend_string_alloc(header_length, 0); while (http_buf_size < header_length) { ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size); - if (len_read <= 0) { + if (UNEXPECTED(len_read <= 0)) { break; } http_buf_size += len_read; diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 68ed9a7a7689..29ed1294f364 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -59,11 +59,11 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z /* Parse XML packet */ response = soap_xmlParseMemory(ZSTR_VAL(buffer), ZSTR_LEN(buffer)); - if (!response) { + if (UNEXPECTED(!response)) { add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, soap_lang_en); return false; } - if (xmlGetIntSubset(response) != NULL) { + if (UNEXPECTED(xmlGetIntSubset(response) != NULL)) { add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -90,7 +90,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z } trav = trav->next; } - if (env == NULL) { + if (UNEXPECTED(env == NULL)) { add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -139,7 +139,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z while (trav != NULL && trav->type != XML_ELEMENT_NODE) { trav = trav->next; } - if (body == NULL) { + if (UNEXPECTED(body == NULL)) { add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -165,7 +165,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z } attr = attr->next; } - if (trav != NULL && soap_version == SOAP_1_2) { + if (UNEXPECTED(trav != NULL && soap_version == SOAP_1_2)) { add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index d97a0eac05b5..b675b97b469f 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -63,12 +63,12 @@ static int schema_parse_int(const xmlChar *value, const char *name, bool allow_n if (type != IS_LONG) { errno = 0; lval = ZEND_STRTOL(str, NULL, 10); - if (oflow_info || (errno == ERANGE && lval != 0)) { + if (UNEXPECTED(oflow_info || (errno == ERANGE && lval != 0))) { soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); } } - if (ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0)) { + if (UNEXPECTED(ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0))) { soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); } diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 07c5dbf4f12e..73886af5a567 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -1524,22 +1524,22 @@ static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, size_t uri_len char *in, *buf; f = open(fn, O_RDONLY|O_BINARY); - if (f < 0) { + if (UNEXPECTED(f < 0)) { return NULL; } - if (fstat(f, &st) != 0) { + if (UNEXPECTED(fstat(f, &st) != 0)) { close(f); return NULL; } buf = in = emalloc(st.st_size); - if (read(f, in, st.st_size) != st.st_size) { + if (UNEXPECTED(read(f, in, st.st_size) != st.st_size)) { close(f); efree(in); return NULL; } close(f); - if (strncmp(in,"wsdl",4) != 0 || in[4] != WSDL_CACHE_VERSION || in[5] != '\0') { + if (UNEXPECTED(strncmp(in,"wsdl",4) != 0 || in[4] != WSDL_CACHE_VERSION || in[5] != '\0')) { unlink(fn); efree(buf); return NULL; @@ -2096,7 +2096,7 @@ static void add_sdl_to_cache(const char *fn, const char *uri, time_t t, sdlPtr s zend_string *temp_file_path; f = php_open_temporary_fd_ex(SOAP_GLOBAL(cache_dir), "tmp.wsdl.", &temp_file_path, PHP_TMP_FILE_SILENT); - if (f < 0) {return;} + if (UNEXPECTED(f < 0)) {return;} zend_hash_init(&tmp_types, 0, NULL, NULL, 0); zend_hash_init(&tmp_encoders, 0, NULL, NULL, 0); diff --git a/ext/soap/soap.c b/ext/soap/soap.c index a3e03e0fb58b..171b529b3355 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1008,7 +1008,7 @@ PHP_METHOD(SoapServer, __construct) goto cleanup; } // TODO: this still accepts mixed keys arrays and not all numerically indexed arrays are packed - if (HT_IS_PACKED(Z_ARRVAL_P(class_map_zv))) { + if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(class_map_zv)))) { zend_argument_value_error(2, "\"classmap\" option must be an associative array"); goto cleanup; } @@ -2224,7 +2224,7 @@ PHP_METHOD(SoapClient, __construct) xmlCharEncodingHandlerPtr encoding; encoding = xmlFindCharEncodingHandler(Z_STRVAL_P(tmp)); - if (encoding == NULL) { + if (UNEXPECTED(encoding == NULL)) { php_error_docref(NULL, E_ERROR, "Invalid 'encoding' option - '%s'", Z_STRVAL_P(tmp)); } else { xmlCharEncCloseFunc(encoding); @@ -2233,7 +2233,7 @@ PHP_METHOD(SoapClient, __construct) } if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - if (HT_IS_PACKED(Z_ARRVAL_P(tmp))) { + if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(tmp)))) { php_error_docref(NULL, E_ERROR, "'classmap' option must be an associative array"); } ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); From 2ddbc0a67229cd8f870a2e3777fe769abdc959b5 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 5 Jul 2026 03:17:38 -0400 Subject: [PATCH 052/211] main/poll: Record wait() error on every backend (#22326) php_poll_wait() reported a stale error code when a backend's wait syscall failed without recording one: epoll, poll and kqueue returned -1 without setting it. Record it from errno in those backends so php_poll_get_error() reflects the actual failure. eventport and wsapoll already did, the latter from WSAGetLastError(). Closes GH-22326 --- main/poll/poll_backend_epoll.c | 2 ++ main/poll/poll_backend_kqueue.c | 5 +++++ main/poll/poll_backend_poll.c | 7 +++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/main/poll/poll_backend_epoll.c b/main/poll/poll_backend_epoll.c index 685339da77d2..394b0dc52446 100644 --- a/main/poll/poll_backend_epoll.c +++ b/main/poll/poll_backend_epoll.c @@ -195,6 +195,8 @@ static int epoll_backend_wait( events[i].revents = epoll_events_from_native(backend_data->events[i].events); events[i].data = backend_data->events[i].data.ptr; } + } else if (nfds < 0) { + php_poll_set_current_errno_error(ctx); } return nfds; diff --git a/main/poll/poll_backend_kqueue.c b/main/poll/poll_backend_kqueue.c index 59b5efa9cacb..00ba7abc9ee6 100644 --- a/main/poll/poll_backend_kqueue.c +++ b/main/poll/poll_backend_kqueue.c @@ -320,6 +320,11 @@ static int kqueue_backend_wait( int nfds = kevent( backend_data->kqueue_fd, NULL, 0, backend_data->events, required_capacity, timeout); + if (nfds < 0) { + php_poll_set_current_errno_error(ctx); + return -1; + } + if (nfds > 0) { if (ctx->raw_events) { /* Raw events mode - direct 1:1 mapping, no grouping */ diff --git a/main/poll/poll_backend_poll.c b/main/poll/poll_backend_poll.c index cca81b6fc4fb..05a51a04d31d 100644 --- a/main/poll/poll_backend_poll.c +++ b/main/poll/poll_backend_poll.c @@ -215,8 +215,11 @@ static int poll_backend_wait( int timeout_ms = php_poll_timespec_to_ms(timeout); int nfds = poll(backend_data->temp_fds, fd_count, timeout_ms); - if (nfds <= 0) { - return nfds; /* Return 0 for timeout, -1 for error */ + if (nfds < 0) { + php_poll_set_current_errno_error(ctx); + return -1; + } else if (nfds == 0) { + return 0; /* timeout */ } /* Process results - iterate through struct pollfd array directly */ From c889faab35a9ece483552114896cb80c15949888 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 07:00:54 +0100 Subject: [PATCH 053/211] ext/zip: memory leak when zip cancel callback bails out. Fix #22176 A cancel callback that throws during the implicit zip_close() in the shutdown destructor triggers a zend_bailout that longjmps through libzip, skipping its free(filelist). Wrap the call in zend_try/zend_catch and cancel on bailout so libzip can unwind and clean up. While at it, apply the same guard to the progress callback, which is invoked from libzip the same way and is prone to the identical leak. close GH-22177 --- NEWS | 3 +++ ext/zip/php_zip.c | 37 ++++++++++++++++++++++++----- ext/zip/php_zip.h | 1 + ext/zip/tests/gh22176.phpt | 33 +++++++++++++++++++++++++ ext/zip/tests/gh22176_progress.phpt | 33 +++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 ext/zip/tests/gh22176.phpt create mode 100644 ext/zip/tests/gh22176_progress.phpt diff --git a/NEWS b/NEWS index 370095987ea7..e476e2b83036 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,9 @@ PHP NEWS - Zip: . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du) + . Fixed bug GH-22176 (memory leak with ZipArchive::registerCancelBack() + is used with reference returning function during shutdown). + (David Carlier) 02 Jul 2026, PHP 8.6.0alpha1 diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 5df4d47740a8..9bc801455ed6 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -628,6 +628,12 @@ static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ * obj->za = NULL; obj->from_string = false; + + if (obj->bailout_callback) { + obj->bailout_callback = false; + zend_bailout(); + } + return success; } /* }}} */ @@ -1073,10 +1079,16 @@ static void php_zip_object_dtor(zend_object *object) if (intern->za) { if (zip_close(intern->za) != 0) { - php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); + if (!intern->bailout_callback) { + php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); + } zip_discard(intern->za); } intern->za = NULL; + if (intern->bailout_callback) { + intern->bailout_callback = false; + zend_bailout(); + } } } @@ -2985,15 +2997,21 @@ PHP_METHOD(ZipArchive, getStream) #ifdef HAVE_PROGRESS_CALLBACK static void php_zip_progress_callback(zip_t *arch, double state, void *ptr) { - if (!EG(active)) { + ze_zip_object *obj = ptr; + + if (UNEXPECTED(!EG(active) || obj->bailout_callback)) { return; } zval cb_args[1]; - ze_zip_object *obj = ptr; ZVAL_DOUBLE(&cb_args[0], state); - zend_call_known_fcc(&obj->progress_callback, NULL, 1, cb_args, NULL); + + zend_try { + zend_call_known_fcc(&obj->progress_callback, NULL, 1, cb_args, NULL); + } zend_catch { + obj->bailout_callback = true; + } zend_end_try(); } /* {{{ register a progression callback: void callback(double state); */ @@ -3036,11 +3054,18 @@ static int php_zip_cancel_callback(zip_t *arch, void *ptr) zval cb_retval; ze_zip_object *obj = ptr; - if (!EG(active)) { + if (UNEXPECTED(!EG(active) || obj->bailout_callback)) { return 0; } - zend_call_known_fcc(&obj->cancel_callback, &cb_retval, 0, NULL, NULL); + zend_try { + zend_call_known_fcc(&obj->cancel_callback, &cb_retval, 0, NULL, NULL); + } zend_catch { + obj->bailout_callback = true; + /* Cancel if a bailout occurs to allow cleanup to happen */ + return -1; + } zend_end_try(); + if (Z_ISUNDEF(cb_retval)) { /* Cancel if an exception has been thrown */ return -1; diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 9e57a54de1cc..408c29708231 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -74,6 +74,7 @@ typedef struct _ze_zip_object { zip_int64_t last_id; int err_zip; int err_sys; + bool bailout_callback; #ifdef HAVE_PROGRESS_CALLBACK zend_fcall_info_cache progress_callback; #endif diff --git a/ext/zip/tests/gh22176.phpt b/ext/zip/tests/gh22176.phpt new file mode 100644 index 000000000000..90b6327072c9 --- /dev/null +++ b/ext/zip/tests/gh22176.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-22176 (Memory leak when a ZipArchive cancel callback bails out in the shutdown destructor) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh22176_cancel.zip', ZipArchive::CREATE); +$zip->registerCancelCallback(function () { + throw new \Exception('cancel boom'); +}); +$zip->addFromString('test', 'test'); +echo "done\n"; +// The archive is flushed and the object destroyed during request shutdown; +// the thrown exception bails out through libzip's zip_close() without leaking +// its internal state, and the bailout resumes once libzip has unwound. +?> +--CLEAN-- + +--EXPECTF-- +done + +Fatal error: Uncaught Exception: cancel boom in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}() +#1 {main} + thrown in %s on line %d diff --git a/ext/zip/tests/gh22176_progress.phpt b/ext/zip/tests/gh22176_progress.phpt new file mode 100644 index 000000000000..5507fe24ab98 --- /dev/null +++ b/ext/zip/tests/gh22176_progress.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-22176 (Memory leak when a ZipArchive progress callback bails out in the shutdown destructor) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh22176_progress.zip', ZipArchive::CREATE); +$zip->registerProgressCallback(0.5, function ($r) { + throw new \Exception('progress boom'); +}); +$zip->addFromString('test', 'test'); +echo "done\n"; +// The archive is flushed and the object destroyed during request shutdown; +// the thrown exception bails out through libzip's zip_close() without leaking +// its internal state, and the bailout resumes once libzip has unwound. +?> +--CLEAN-- + +--EXPECTF-- +done + +Fatal error: Uncaught Exception: progress boom in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}(0.0) +#1 {main} + thrown in %s on line %d From 8613e0ebdb8ac759029e3350580f5c6468e750d0 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 5 Jul 2026 20:10:00 +0800 Subject: [PATCH 054/211] ext/Intl: Add `UNEXPECTED()` in obvious error path (#22601) This added `UNEXPECTED()` in several obvious error path for optimization in ext/intl. --- .../codepointiterator_internal.cpp | 10 +++---- .../rulebasedbreakiterator_methods.cpp | 8 +++--- ext/intl/common/common_date.cpp | 2 +- ext/intl/converter/converter.cpp | 6 ++--- ext/intl/dateformat/dateformat_format.cpp | 4 +-- ext/intl/formatter/formatter_attr.cpp | 4 +-- ext/intl/formatter/formatter_parse.cpp | 4 +-- ext/intl/grapheme/grapheme_string.cpp | 26 +++++++++---------- ext/intl/grapheme/grapheme_util.cpp | 2 +- ext/intl/idn/idn.cpp | 10 +++---- ext/intl/intl_convert.c | 4 +-- ext/intl/intl_data.h | 4 +-- .../listformatter/listformatter_class.cpp | 2 +- ext/intl/locale/locale_methods.cpp | 16 ++++++------ ext/intl/normalizer/normalizer_normalize.cpp | 16 ++++++------ ext/intl/php_intl.c | 2 +- .../rangeformatter/rangeformatter_class.cpp | 2 +- .../resourcebundle/resourcebundle_class.cpp | 4 +-- ext/intl/spoofchecker/spoofchecker_main.cpp | 4 +-- .../transliterator/transliterator_methods.cpp | 8 +++--- ext/intl/uchar/uchar.cpp | 6 ++--- 21 files changed, 72 insertions(+), 72 deletions(-) diff --git a/ext/intl/breakiterator/codepointiterator_internal.cpp b/ext/intl/breakiterator/codepointiterator_internal.cpp index 47d5df00c1be..0604cfa24167 100644 --- a/ext/intl/breakiterator/codepointiterator_internal.cpp +++ b/ext/intl/breakiterator/codepointiterator_internal.cpp @@ -126,7 +126,7 @@ void CodePointBreakIterator::setText(const UnicodeString &text) void CodePointBreakIterator::setText(UText *text, UErrorCode &status) { - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return; } @@ -234,7 +234,7 @@ CodePointBreakIterator *CodePointBreakIterator::createBufferClone( void *stackBuffer, int32_t &bufferSize, UErrorCode &status) { //see implementation of RuleBasedBreakIterator::createBufferClone() - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return NULL; } @@ -273,17 +273,17 @@ CodePointBreakIterator *CodePointBreakIterator::createBufferClone( CodePointBreakIterator &CodePointBreakIterator::refreshInputText(UText *input, UErrorCode &status) { //see implementation of RuleBasedBreakIterator::createBufferClone() - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return *this; } - if (input == NULL) { + if (UNEXPECTED(input == NULL)) { status = U_ILLEGAL_ARGUMENT_ERROR; return *this; } int64_t pos = utext_getNativeIndex(this->fText); this->fText = utext_clone(this->fText, input, false, true, &status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { return *this; } diff --git a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp index 1e1c2f0f6e5c..4526b9faab13 100644 --- a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp +++ b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp @@ -57,7 +57,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) if (!compiled) { UnicodeString rulesStr; UParseError parseError = UParseError(); - if (intl_stringFromChar(rulesStr, ZSTR_VAL(rules), ZSTR_LEN(rules), &status) == FAILURE) { + if (UNEXPECTED(intl_stringFromChar(rulesStr, ZSTR_VAL(rules), ZSTR_LEN(rules), &status) == FAILURE)) { zend_throw_exception(IntlException_ce_ptr, "IntlRuleBasedBreakIterator::__construct(): rules were not a valid UTF-8 string", 0); RETURN_THROWS(); @@ -65,7 +65,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) rbbi = new RuleBasedBreakIterator(rulesStr, parseError, status); intl_error_set_code(NULL, status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { smart_str parse_error_str; parse_error_str = intl_parse_error_to_string(&parseError); zend_throw_exception_ex(IntlException_ce_ptr, 0, @@ -78,7 +78,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) } } else { // compiled rbbi = new RuleBasedBreakIterator(reinterpret_cast(ZSTR_VAL(rules)), ZSTR_LEN(rules), status); - if (U_FAILURE(status)) { + if (UNEXPECTED(U_FAILURE(status))) { zend_throw_exception(IntlException_ce_ptr, "IntlRuleBasedBreakIterator::__construct(): unable to create instance from compiled rules", 0); delete rbbi; @@ -165,7 +165,7 @@ U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, getBinaryRules) uint32_t rules_len; const uint8_t *rules = fetch_rbbi(bio)->getBinaryRules(rules_len); - if (rules_len > INT_MAX - 1) { + if (UNEXPECTED(rules_len > INT_MAX - 1)) { intl_errors_set(BREAKITER_ERROR_P(bio), BREAKITER_ERROR_CODE(bio), "the rules are too large"); RETURN_FALSE; diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index fb5ca6edaebe..ed18bba70b9f 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -55,7 +55,7 @@ U_CFUNC TimeZone *timezone_convert_datetimezone( minutes = offset_mins - hours * 60; minutes *= minutes > 0 ? 1 : -1; - if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) { + if (UNEXPECTED(offset_mins <= -24 * 60 || offset_mins >= 24 * 60)) { intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR, "object has an time zone offset that's too large"); return NULL; diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index b4d62d4643d1..402a50482485 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -162,7 +162,7 @@ static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicode case IS_LONG: { const zend_long lval = Z_LVAL_P(val); - if ((lval < 0) || (lval > 0x10FFFF)) { + if (UNEXPECTED((lval < 0) || (lval > 0x10FFFF))) { php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "Invalid codepoint U+%04lx", lval); return; } @@ -636,7 +636,7 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, zend_string *ret; UChar *temp; - if (!src_cnv || !dest_cnv) { + if (UNEXPECTED(!src_cnv || !dest_cnv)) { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Internal converters not initialized"); return nullptr; @@ -686,7 +686,7 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, static void php_converter_set_subst_chars(UConverter *cnv, const zend_string *subst, UErrorCode *error) { - if (ZSTR_LEN(subst) > SCHAR_MAX) { + if (UNEXPECTED(ZSTR_LEN(subst) > SCHAR_MAX)) { *error = U_ILLEGAL_ARGUMENT_ERROR; return; } diff --git a/ext/intl/dateformat/dateformat_format.cpp b/ext/intl/dateformat/dateformat_format.cpp index 8b2a28d5c4d2..8890332dd406 100644 --- a/ext/intl/dateformat/dateformat_format.cpp +++ b/ext/intl/dateformat/dateformat_format.cpp @@ -73,8 +73,8 @@ static int32_t internal_get_arr_ele(IntlDateFormatter_object *dfo, intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, message); efree(message); } else { - if (Z_LVAL_P(ele_value) > INT32_MAX || - Z_LVAL_P(ele_value) < INT32_MIN) { + if (UNEXPECTED(Z_LVAL_P(ele_value) > INT32_MAX || + Z_LVAL_P(ele_value) < INT32_MIN)) { spprintf(&message, 0, "value " ZEND_LONG_FMT " is out of " "bounds for a 32-bit integer in key '%s'", Z_LVAL_P(ele_value), key_name); diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp index 4e97ad7f7105..ff215fdffbd7 100644 --- a/ext/intl/formatter/formatter_attr.cpp +++ b/ext/intl/formatter/formatter_attr.cpp @@ -234,7 +234,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_symbol ) UNumberFormatSymbol symbol = static_cast(lsymbol); - if(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) { + if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "invalid symbol value"); RETURN_FALSE; } @@ -276,7 +276,7 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol ) UNumberFormatSymbol symbol = static_cast(lsymbol); - if (symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) { + if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "invalid symbol value"); RETURN_FALSE; } diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index 63c0fba4329f..40107229e0ae 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -51,7 +51,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (long_position < INT32_MIN || long_position > INT32_MAX) { + if (UNEXPECTED(long_position < INT32_MIN || long_position > INT32_MAX)) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); } @@ -162,7 +162,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (long_position < INT32_MIN || long_position > INT32_MAX) { + if (UNEXPECTED(long_position < INT32_MIN || long_position > INT32_MAX)) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); } diff --git a/ext/intl/grapheme/grapheme_string.cpp b/ext/intl/grapheme/grapheme_string.cpp index eb8b7e609b8e..5e614be6ae72 100644 --- a/ext/intl/grapheme/grapheme_string.cpp +++ b/ext/intl/grapheme/grapheme_string.cpp @@ -104,7 +104,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strpos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -158,7 +158,7 @@ U_CFUNC PHP_FUNCTION(grapheme_stripos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -224,7 +224,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strrpos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -283,7 +283,7 @@ U_CFUNC PHP_FUNCTION(grapheme_strripos) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( OUTSIDE_STRING(loffset, haystack_len) ) { + if (UNEXPECTED(OUTSIDE_STRING(loffset, haystack_len))) { zend_argument_value_error(3, "must be contained in argument #1 ($haystack)"); RETURN_THROWS(); } @@ -358,7 +358,7 @@ U_CFUNC PHP_FUNCTION(grapheme_substr) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if (lstart < INT32_MIN || lstart > INT32_MAX) { + if (UNEXPECTED(lstart < INT32_MIN || lstart > INT32_MAX)) { zend_argument_value_error(2, "is too large"); RETURN_THROWS(); } @@ -369,7 +369,7 @@ U_CFUNC PHP_FUNCTION(grapheme_substr) length = str_len; } - if (length < INT32_MIN || length > INT32_MAX) { + if (UNEXPECTED(length < INT32_MIN || length > INT32_MAX)) { zend_argument_value_error(3, "is too large"); RETURN_THROWS(); } @@ -758,17 +758,17 @@ U_CFUNC PHP_FUNCTION(grapheme_extract) RETURN_THROWS(); } - if ( lstart > INT32_MAX || lstart < 0 || (size_t)lstart >= str_len ) { + if (UNEXPECTED(lstart > INT32_MAX || lstart < 0 || (size_t)lstart >= str_len)) { intl_error_set( nullptr, U_ILLEGAL_ARGUMENT_ERROR, "start not contained in string"); RETURN_FALSE; } - if (size < 0) { + if (UNEXPECTED(size < 0)) { zend_argument_value_error(2, "must be greater than or equal to 0"); RETURN_THROWS(); } - if (size > INT32_MAX) { + if (UNEXPECTED(size > INT32_MAX)) { zend_argument_value_error(2, "is too large"); RETURN_THROWS(); } @@ -864,7 +864,7 @@ U_CFUNC PHP_FUNCTION(grapheme_str_split) Z_PARAM_LONG(split_len) ZEND_PARSE_PARAMETERS_END(); - if (split_len <= 0 || split_len > UINT_MAX / 4) { + if (UNEXPECTED(split_len <= 0 || split_len > UINT_MAX / 4)) { zend_argument_value_error(2, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } @@ -943,17 +943,17 @@ U_CFUNC PHP_FUNCTION(grapheme_levenshtein) Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if (cost_ins <= 0 || cost_ins > UINT_MAX / 4) { + if (UNEXPECTED(cost_ins <= 0 || cost_ins > UINT_MAX / 4)) { zend_argument_value_error(3, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } - if (cost_rep <= 0 || cost_rep > UINT_MAX / 4) { + if (UNEXPECTED(cost_rep <= 0 || cost_rep > UINT_MAX / 4)) { zend_argument_value_error(4, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } - if (cost_del <= 0 || cost_del > UINT_MAX / 4) { + if (UNEXPECTED(cost_del <= 0 || cost_del > UINT_MAX / 4)) { zend_argument_value_error(5, "must be greater than 0 and less than or equal to %d", UINT_MAX / 4); RETURN_THROWS(); } diff --git a/ext/intl/grapheme/grapheme_util.cpp b/ext/intl/grapheme/grapheme_util.cpp index 1e6fb94c9408..031f1c717891 100644 --- a/ext/intl/grapheme/grapheme_util.cpp +++ b/ext/intl/grapheme/grapheme_util.cpp @@ -59,7 +59,7 @@ U_CFUNC void grapheme_substr_ascii(char *str, size_t str_len, int32_t f, int32_t int32_t str_len2 = (int32_t)str_len; /* in order to avoid signed/unsigned problems */ *sub_str = NULL; - if(str_len > INT32_MAX) { + if (UNEXPECTED(str_len > INT32_MAX)) { /* We cannot return long strings from ICU functions, so we won't here too */ return; } diff --git a/ext/intl/idn/idn.cpp b/ext/intl/idn/idn.cpp index 28708eb93508..76cca2281296 100644 --- a/ext/intl/idn/idn.cpp +++ b/ext/intl/idn/idn.cpp @@ -40,7 +40,7 @@ enum { static zend_result php_intl_idn_check_status(UErrorCode err, const char *msg) { intl_error_set_code(NULL, err); - if (U_FAILURE(err)) { + if (UNEXPECTED(U_FAILURE(err))) { intl_error_set_custom_msg(NULL, msg); return FAILURE; } @@ -59,7 +59,7 @@ static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS, UIDNAInfo info = UIDNA_INFO_INITIALIZER; uts46 = uidna_openUTS46(option, &status); - if (php_intl_idn_check_status(status, "failed to open UIDNA instance") == FAILURE) { + if (UNEXPECTED(php_intl_idn_check_status(status, "failed to open UIDNA instance") == FAILURE)) { RETURN_FALSE; } @@ -74,7 +74,7 @@ static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS, len = uidna_nameToUnicodeUTF8(uts46, ZSTR_VAL(domain), ZSTR_LEN(domain), ZSTR_VAL(buffer), buffer_capac, &info, &status); } - if (len >= buffer_capac || php_intl_idn_check_status(status, "failed to convert name") == FAILURE) { + if (UNEXPECTED(len >= buffer_capac || php_intl_idn_check_status(status, "failed to convert name") == FAILURE)) { uidna_close(uts46); zend_string_efree(buffer); RETURN_FALSE; @@ -121,7 +121,7 @@ static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode) zend_argument_must_not_be_empty_error(1); RETURN_THROWS(); } - if (ZSTR_LEN(domain) > INT32_MAX - 1) { + if (UNEXPECTED(ZSTR_LEN(domain) > INT32_MAX - 1)) { zend_argument_value_error(1, "must be less than " PRId32 " bytes", INT32_MAX); RETURN_THROWS(); } @@ -133,7 +133,7 @@ static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode) if (idna_info != NULL) { idna_info = zend_try_array_init(idna_info); - if (!idna_info) { + if (UNEXPECTED(!idna_info)) { RETURN_THROWS(); } } diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c index abbcc6c1fa73..5497e84508b4 100644 --- a/ext/intl/intl_convert.c +++ b/ext/intl/intl_convert.c @@ -59,7 +59,7 @@ void intl_convert_utf8_to_utf16( */ *status = U_ZERO_ERROR; - if(src_len > INT32_MAX) { + if (UNEXPECTED(src_len > INT32_MAX)) { /* we cannot fit this string */ *status = U_BUFFER_OVERFLOW_ERROR; return; @@ -120,7 +120,7 @@ zend_string *intl_convert_utf8_to_utf16_zstr( *status = U_ZERO_ERROR; - if(src_len > INT32_MAX) { + if (UNEXPECTED(src_len > INT32_MAX)) { /* we cannot fit this string */ *status = U_BUFFER_OVERFLOW_ERROR; return NULL; diff --git a/ext/intl/intl_data.h b/ext/intl/intl_data.h index c36f0adf5665..d2021caf18e3 100644 --- a/ext/intl/intl_data.h +++ b/ext/intl/intl_data.h @@ -114,7 +114,7 @@ typedef struct _intl_data { #define INTL_MAX_LOCALE_LEN (ULOC_FULLNAME_CAPACITY-1) #define INTL_CHECK_LOCALE_LEN(locale_len) \ - if((locale_len) > INTL_MAX_LOCALE_LEN) { \ + if (UNEXPECTED((locale_len) > INTL_MAX_LOCALE_LEN)) { \ char *_msg; \ spprintf(&_msg, 0, "Locale string too long, should be no longer than %d characters", INTL_MAX_LOCALE_LEN); \ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, _msg); \ @@ -123,7 +123,7 @@ typedef struct _intl_data { } #define INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len) \ - if((locale_len) > INTL_MAX_LOCALE_LEN) { \ + if (UNEXPECTED((locale_len) > INTL_MAX_LOCALE_LEN)) { \ char *_msg; \ spprintf(&_msg, 0, "Locale string too long, should be no longer than %d characters", INTL_MAX_LOCALE_LEN); \ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, _msg); \ diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index 0f432b0e6056..59c01259227d 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -83,7 +83,7 @@ PHP_METHOD(IntlListFormatter, __construct) locale = (char *)intl_locale_get_default(); } - if (locale_len > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(locale_len > INTL_MAX_LOCALE_LEN)) { zend_argument_value_error(1, "must be less than or equal to %d characters", INTL_MAX_LOCALE_LEN); RETURN_THROWS(); } diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp index 0733ffc8abb7..05ec5825d8cb 100644 --- a/ext/intl/locale/locale_methods.cpp +++ b/ext/intl/locale/locale_methods.cpp @@ -353,7 +353,7 @@ static zend_string* get_icu_value_internal( const char* loc_name , const char* t int32_t buflen = 512; UErrorCode status = U_ZERO_ERROR; - if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(strlen(loc_name) > INTL_MAX_LOCALE_LEN)) { return NULL; } @@ -575,7 +575,7 @@ static void get_icu_disp_value_src_php( const char* tag_name, INTERNAL_FUNCTION_ Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len) ZEND_PARSE_PARAMETERS_END(); - if(loc_name_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(loc_name_len > ULOC_FULLNAME_CAPACITY)) { /* See bug 67397: overlong locale names cause trouble in uloc_getDisplayName */ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "name too long"); RETURN_FALSE; @@ -710,7 +710,7 @@ static void get_icu_disp_keyword_value_src_php(const char* tag_name, INTERNAL_FU Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len) ZEND_PARSE_PARAMETERS_END(); - if (loc_name_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(loc_name_len > ULOC_FULLNAME_CAPACITY)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "name too long"); RETURN_FALSE; } @@ -1715,7 +1715,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(http_accept, http_accept_len) ZEND_PARSE_PARAMETERS_END(); - if(http_accept_len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(http_accept_len > ULOC_FULLNAME_CAPACITY)) { /* check each fragment, if any bigger than capacity, can't do it due to bug #72533 */ char *start = http_accept; char *end; @@ -1723,7 +1723,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) do { end = strchr(start, ','); len = end ? end-start : http_accept_len-(start-http_accept); - if(len > ULOC_FULLNAME_CAPACITY) { + if (UNEXPECTED(len > ULOC_FULLNAME_CAPACITY)) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale string too long"); RETURN_FALSE; @@ -1740,7 +1740,7 @@ U_CFUNC PHP_FUNCTION(locale_accept_from_http) &outResult, http_accept, available, &status); uenum_close(available); INTL_CHECK_STATUS(status, "failed to find acceptable locale"); - if (len < 0 || outResult == ULOC_ACCEPT_FAILED) { + if (UNEXPECTED(len < 0 || outResult == ULOC_ACCEPT_FAILED)) { RETURN_FALSE; } RETURN_STRINGL(resultLocale, len); @@ -1779,7 +1779,7 @@ U_CFUNC PHP_FUNCTION(locale_add_likely_subtags) const int32_t maximized_locale_len = uloc_addLikelySubtags(locale, maximized_locale, sizeof(maximized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); - if (maximized_locale_len < 0) { + if (UNEXPECTED(maximized_locale_len < 0)) { RETURN_FALSE; } @@ -1802,7 +1802,7 @@ U_CFUNC PHP_FUNCTION(locale_minimize_subtags) const int32_t minimized_locale_len = uloc_minimizeSubtags(locale, minimized_locale, sizeof(minimized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); - if (minimized_locale_len < 0) { + if (UNEXPECTED(minimized_locale_len < 0)) { RETURN_FALSE; } diff --git a/ext/intl/normalizer/normalizer_normalize.cpp b/ext/intl/normalizer/normalizer_normalize.cpp index 846eda64b4c8..8a77d37ad4d4 100644 --- a/ext/intl/normalizer/normalizer_normalize.cpp +++ b/ext/intl/normalizer/normalizer_normalize.cpp @@ -54,7 +54,7 @@ static const UNormalizer2 *intl_get_normalizer(zend_long form, UErrorCode *err) static int32_t intl_normalize(zend_long form, const UChar *src, int32_t src_len, UChar *dst, int32_t dst_len, UErrorCode *err) {/*{{{*/ const UNormalizer2 *norm = intl_get_normalizer(form, err); - if (U_FAILURE(*err)) { + if (UNEXPECTED(U_FAILURE(*err))) { return -1; } @@ -65,7 +65,7 @@ static UBool intl_is_normalized(zend_long form, const UChar *uinput, int32_t uin {/*{{{*/ const UNormalizer2 *norm = intl_get_normalizer(form, err); - if(U_FAILURE(*err)) { + if (UNEXPECTED(U_FAILURE(*err))) { return false; } @@ -151,7 +151,7 @@ U_CFUNC PHP_FUNCTION( normalizer_normalize ) * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough). * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty). */ - if( U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING ) { + if (UNEXPECTED(U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING)) { intl_error_set_custom_msg( NULL, "Error normalizing string"); efree( uret_buf ); efree( uinput ); @@ -172,7 +172,7 @@ U_CFUNC PHP_FUNCTION( normalizer_normalize ) size_needed = intl_normalize(form, uinput, uinput_len, uret_buf, uret_len, &status); /* Bail out if an unexpected error occurred. */ - if( U_FAILURE(status) ) { + if (UNEXPECTED(U_FAILURE(status))) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error normalizing string"); efree( uret_buf ); @@ -264,7 +264,7 @@ U_CFUNC PHP_FUNCTION( normalizer_is_normalized ) efree( uinput ); /* Bail out if an unexpected error occurred. */ - if( U_FAILURE(status) ) { + if (UNEXPECTED(U_FAILURE(status))) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error testing if string is the given normalization form."); RETURN_FALSE; @@ -304,20 +304,20 @@ U_CFUNC PHP_FUNCTION( normalizer_get_raw_decomposition ) norm = intl_get_normalizer(form, &status); U8_NEXT(input, offset, input_length, codepoint); - if ((size_t)offset != input_length) { + if (UNEXPECTED((size_t)offset != input_length)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Input string must be exactly one UTF-8 encoded code point long."); return; } - if ((codepoint < UCHAR_MIN_VALUE) || (codepoint > UCHAR_MAX_VALUE)) { + if (UNEXPECTED((codepoint < UCHAR_MIN_VALUE) || (codepoint > UCHAR_MAX_VALUE))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Code point out of range"); return; } decomposition_length = unorm2_getRawDecomposition(norm, codepoint, decomposition, 32, &status); - if (decomposition_length == -1) { + if (UNEXPECTED(decomposition_length == -1)) { RETURN_NULL(); } diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index 9882309c6f46..697d77a98bfa 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -106,7 +106,7 @@ char* canonicalize_locale_string(const char* locale) { canonicalized_len = uloc_canonicalize(locale, canonicalized, sizeof(canonicalized), &status); - if (U_FAILURE(status) || canonicalized_len <= 0) { + if (UNEXPECTED(U_FAILURE(status) || canonicalized_len <= 0)) { return NULL; } diff --git a/ext/intl/rangeformatter/rangeformatter_class.cpp b/ext/intl/rangeformatter/rangeformatter_class.cpp index f933e0c06090..2dbb60c5b639 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.cpp +++ b/ext/intl/rangeformatter/rangeformatter_class.cpp @@ -99,7 +99,7 @@ U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, createFromSkeleton) locale = (char *)intl_locale_get_default(); } - if (locale_len > INTL_MAX_LOCALE_LEN) { + if (UNEXPECTED(locale_len > INTL_MAX_LOCALE_LEN)) { zend_argument_value_error(2, "must be no longer than %d characters", INTL_MAX_LOCALE_LEN); RETURN_THROWS(); } diff --git a/ext/intl/resourcebundle/resourcebundle_class.cpp b/ext/intl/resourcebundle/resourcebundle_class.cpp index 7e22e9e8c7df..a997691f6423 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.cpp +++ b/ext/intl/resourcebundle/resourcebundle_class.cpp @@ -108,7 +108,7 @@ static zend_result resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) locale = (char *)intl_locale_get_default(); } - if (bundlename_len >= MAXPATHLEN) { + if (UNEXPECTED(bundlename_len >= MAXPATHLEN)) { zend_argument_value_error(2, "is too long"); return FAILURE; } @@ -349,7 +349,7 @@ U_CFUNC PHP_FUNCTION( resourcebundle_locales ) Z_PARAM_STRING(bundlename, bundlename_len) ZEND_PARSE_PARAMETERS_END(); - if (bundlename_len >= MAXPATHLEN) { + if (UNEXPECTED(bundlename_len >= MAXPATHLEN)) { zend_argument_value_error(1, "is too long"); RETURN_THROWS(); } diff --git a/ext/intl/spoofchecker/spoofchecker_main.cpp b/ext/intl/spoofchecker/spoofchecker_main.cpp index cfd5ad8fb631..1e8e9ac2a78d 100644 --- a/ext/intl/spoofchecker/spoofchecker_main.cpp +++ b/ext/intl/spoofchecker/spoofchecker_main.cpp @@ -77,7 +77,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, areConfusable) ZEND_PARSE_PARAMETERS_END(); SPOOFCHECKER_METHOD_FETCH_OBJECT; - if(ZSTR_LEN(s1) > INT32_MAX || ZSTR_LEN(s2) > INT32_MAX) { + if (UNEXPECTED(ZSTR_LEN(s1) > INT32_MAX || ZSTR_LEN(s2) > INT32_MAX)) { SPOOFCHECKER_ERROR_CODE(co) = U_BUFFER_OVERFLOW_ERROR; } else { ret = uspoof_areConfusableUTF8(co->uspoof, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co)); @@ -179,7 +179,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars) ZEND_PARSE_PARAMETERS_END(); SPOOFCHECKER_METHOD_FETCH_OBJECT; - if (ZSTR_LEN(pattern) > INT32_MAX) { + if (UNEXPECTED(ZSTR_LEN(pattern) > INT32_MAX)) { zend_argument_value_error(1, "must be less than or equal to " ZEND_LONG_FMT " bytes long", INT32_MAX); RETURN_THROWS(); } diff --git a/ext/intl/transliterator/transliterator_methods.cpp b/ext/intl/transliterator/transliterator_methods.cpp index 8efcff95b310..9cdec2611a24 100644 --- a/ext/intl/transliterator/transliterator_methods.cpp +++ b/ext/intl/transliterator/transliterator_methods.cpp @@ -331,17 +331,17 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) RETURN_THROWS(); } - if (limit < -1) { + if (UNEXPECTED(limit < -1)) { zend_argument_value_error(is_method ? 3 : 4, "must be greater than or equal to -1"); goto cleanup_object; } - if (start < 0) { + if (UNEXPECTED(start < 0)) { zend_argument_value_error(is_method ? 2 : 3, "must be greater than or equal to 0"); goto cleanup_object; } - if (limit != -1 && start > limit) { + if (UNEXPECTED(limit != -1 && start > limit)) { zend_argument_value_error(is_method ? 2 : 3, "must be less than or equal to argument #%d ($end)", is_method ? 3 : 4); goto cleanup_object; } @@ -355,7 +355,7 @@ U_CFUNC PHP_FUNCTION( transliterator_transliterate ) /* we've started allocating resources, goto from now on */ - if( ( start > ustr_len ) || (( limit != -1 ) && (limit > ustr_len ) ) ) + if (UNEXPECTED((start > ustr_len) || ((limit != -1) && (limit > ustr_len)))) { char *msg; spprintf( &msg, 0, diff --git a/ext/intl/uchar/uchar.cpp b/ext/intl/uchar/uchar.cpp index 8dc6a58a8287..80bdd2bdb9fa 100644 --- a/ext/intl/uchar/uchar.cpp +++ b/ext/intl/uchar/uchar.cpp @@ -30,21 +30,21 @@ static inline int convert_cp(UChar32* pcp, const zend_string *string_codepoint, int32_t i = 0; const size_t string_codepoint_length = ZSTR_LEN(string_codepoint); - if (ZEND_SIZE_T_INT_OVFL(string_codepoint_length)) { + if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(string_codepoint_length))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Input string is too long."); return FAILURE; } U8_NEXT(ZSTR_VAL(string_codepoint), i, string_codepoint_length, int_codepoint); - if ((size_t)i != string_codepoint_length) { + if (UNEXPECTED((size_t)i != string_codepoint_length)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Passing a UTF-8 character for codepoint requires a string which is exactly one UTF-8 codepoint long."); return FAILURE; } } - if ((int_codepoint < UCHAR_MIN_VALUE) || (int_codepoint > UCHAR_MAX_VALUE)) { + if (UNEXPECTED((int_codepoint < UCHAR_MIN_VALUE) || (int_codepoint > UCHAR_MAX_VALUE))) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "Codepoint out of range"); return FAILURE; From 9cefeea7b105b0e8153958c3908ef063cad6e391 Mon Sep 17 00:00:00 2001 From: kn1g78 Date: Sat, 4 Jul 2026 08:54:57 +0800 Subject: [PATCH 055/211] Sync Boost.Context assembly with 1.91.0 Closes GH-22584 --- .../scripts/download-bundled/boost-context.sh | 4 +- NEWS | 3 + Zend/asm/jump_arm64_aapcs_elf_gas.S | 19 ++++++ Zend/asm/jump_arm64_aapcs_macho_gas.S | 1 + Zend/asm/jump_arm64_aapcs_pe_armasm.asm | 4 +- Zend/asm/jump_arm_aapcs_elf_gas.S | 58 +++++++++++++++++++ Zend/asm/jump_arm_aapcs_macho_gas.S | 1 + Zend/asm/jump_i386_ms_pe_masm.asm | 18 +++--- Zend/asm/jump_i386_sysv_elf_gas.S | 1 + Zend/asm/jump_i386_sysv_macho_gas.S | 5 +- Zend/asm/jump_loongarch64_sysv_elf_gas.S | 1 + Zend/asm/jump_mips32_o32_elf_gas.S | 5 +- Zend/asm/jump_mips64_n64_elf_gas.S | 1 + Zend/asm/jump_ppc32_sysv_elf_gas.S | 1 + Zend/asm/jump_ppc32_sysv_macho_gas.S | 5 +- Zend/asm/jump_ppc64_sysv_elf_gas.S | 14 ++++- Zend/asm/jump_ppc64_sysv_macho_gas.S | 1 + Zend/asm/jump_riscv64_sysv_elf_gas.S | 1 + Zend/asm/jump_s390x_sysv_elf_gas.S | 1 + Zend/asm/jump_x86_64_sysv_elf_gas.S | 9 +-- Zend/asm/jump_x86_64_sysv_macho_gas.S | 1 + Zend/asm/make_arm64_aapcs_elf_gas.S | 19 ++++++ Zend/asm/make_arm64_aapcs_macho_gas.S | 1 + Zend/asm/make_arm64_aapcs_pe_armasm.asm | 2 +- Zend/asm/make_arm_aapcs_elf_gas.S | 27 +++++++++ Zend/asm/make_arm_aapcs_macho_gas.S | 1 + Zend/asm/make_i386_ms_pe_masm.asm | 2 +- Zend/asm/make_i386_sysv_elf_gas.S | 1 + Zend/asm/make_i386_sysv_macho_gas.S | 3 +- Zend/asm/make_loongarch64_sysv_elf_gas.S | 1 + Zend/asm/make_mips32_o32_elf_gas.S | 1 + Zend/asm/make_mips64_n64_elf_gas.S | 3 +- Zend/asm/make_ppc32_sysv_elf_gas.S | 1 + Zend/asm/make_ppc32_sysv_macho_gas.S | 7 ++- Zend/asm/make_ppc64_sysv_elf_gas.S | 1 + Zend/asm/make_ppc64_sysv_macho_gas.S | 1 + Zend/asm/make_riscv64_sysv_elf_gas.S | 1 + Zend/asm/make_s390x_sysv_elf_gas.S | 1 + Zend/asm/make_sparc64_sysv_elf_gas.S | 2 +- Zend/asm/make_x86_64_ms_pe_gas.S | 4 +- Zend/asm/make_x86_64_ms_pe_masm.asm | 4 +- Zend/asm/make_x86_64_sysv_elf_gas.S | 35 ++--------- Zend/asm/make_x86_64_sysv_macho_gas.S | 1 + 43 files changed, 203 insertions(+), 70 deletions(-) diff --git a/.github/scripts/download-bundled/boost-context.sh b/.github/scripts/download-bundled/boost-context.sh index b1fd8708b85b..02febff73c3f 100755 --- a/.github/scripts/download-bundled/boost-context.sh +++ b/.github/scripts/download-bundled/boost-context.sh @@ -5,7 +5,7 @@ cd "$(dirname "$0")/../../.." tmp_dir=/tmp/php-src-download-bundled/boost-context rm -rf "$tmp_dir" -revision=refs/tags/boost-1.86.0 +revision=refs/tags/boost-1.91.0 git clone --depth 1 --revision="$revision" https://github.com/boostorg/context.git "$tmp_dir" @@ -16,12 +16,14 @@ cd Zend/asm # remove unneeded files rm jump_arm_aapcs_pe_armasm.asm +rm jump_arm64_aapcs_pe_armclang.S rm jump_i386_ms_pe_clang_gas.S rm jump_i386_ms_pe_gas.asm rm jump_i386_x86_64_sysv_macho_gas.S rm jump_ppc32_ppc64_sysv_macho_gas.S rm jump_x86_64_ms_pe_clang_gas.S rm make_arm_aapcs_pe_armasm.asm +rm make_arm64_aapcs_pe_armclang.S rm make_i386_ms_pe_clang_gas.S rm make_i386_ms_pe_gas.asm rm make_i386_x86_64_sysv_macho_gas.S diff --git a/NEWS b/NEWS index e476e2b83036..77ea35389d5f 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0alpha2 +- Core: + . Sync Boost.Context assembly with 1.91.0. (kn1g78) + - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/Zend/asm/jump_arm64_aapcs_elf_gas.S b/Zend/asm/jump_arm64_aapcs_elf_gas.S index cefd1830d718..b8abb2ef6bc4 100644 --- a/Zend/asm/jump_arm64_aapcs_elf_gas.S +++ b/Zend/asm/jump_arm64_aapcs_elf_gas.S @@ -53,10 +53,29 @@ .file "jump_arm64_aapcs_elf_gas.S" .text +#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) +/* Mark this object as requiring BTI, enabling the linker to set BTI on the output */ + .pushsection .note.gnu.property, "a", %note + .p2align 3 + .long 4 /* namesz */ + .long 16 /* descsz (16 bytes) */ + .long 5 /* type = NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" + .p2align 3 + .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4 /* datasz = 4 */ + .long 1 /* GNU_PROPERTY_AARCH64_FEATURE_1_BTI */ + .long 0 /* pad to 8-byte alignment */ + .popsection +#endif .align 2 .global jump_fcontext +.hidden jump_fcontext .type jump_fcontext, %function jump_fcontext: +#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) + hint #34 /* bti c: valid indirect-entry target */ +#endif # prepare stack for GP + FPU sub sp, sp, #0xb0 diff --git a/Zend/asm/jump_arm64_aapcs_macho_gas.S b/Zend/asm/jump_arm64_aapcs_macho_gas.S index 31738f745314..12cc02158685 100644 --- a/Zend/asm/jump_arm64_aapcs_macho_gas.S +++ b/Zend/asm/jump_arm64_aapcs_macho_gas.S @@ -52,6 +52,7 @@ *******************************************************/ .text +.private_extern _jump_fcontext .globl _jump_fcontext .balign 16 _jump_fcontext: diff --git a/Zend/asm/jump_arm64_aapcs_pe_armasm.asm b/Zend/asm/jump_arm64_aapcs_pe_armasm.asm index 3100243d68de..afb6d2b2e703 100644 --- a/Zend/asm/jump_arm64_aapcs_pe_armasm.asm +++ b/Zend/asm/jump_arm64_aapcs_pe_armasm.asm @@ -45,7 +45,7 @@ ;* ------------------------------------------------- * ;* | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| * ;* ------------------------------------------------- * -;* | fiber data| base | limit | dealloc | * +;* | base | limit | dealloc | fiber data| * ;* ------------------------------------------------- * ;* ------------------------------------------------- * ;* | 48 | 49 | 50 | 51 | | | * @@ -130,4 +130,4 @@ jump_fcontext proc ret x4 ENDP - END \ No newline at end of file + END diff --git a/Zend/asm/jump_arm_aapcs_elf_gas.S b/Zend/asm/jump_arm_aapcs_elf_gas.S index 86efe9d82146..4415a541fd3e 100644 --- a/Zend/asm/jump_arm_aapcs_elf_gas.S +++ b/Zend/asm/jump_arm_aapcs_elf_gas.S @@ -41,6 +41,7 @@ .file "jump_arm_aapcs_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 2 .type jump_fcontext,%function .syntax unified @@ -48,8 +49,38 @@ jump_fcontext: @ save LR as PC push {lr} @ save hidden,V1-V8,LR + + #if __ARM_ARCH >= 7 + push {a1,v1-v8,lr} + #else + + sub sp, #40 + + str a1, [sp, #0] + str v1, [sp, #4] + str v2, [sp, #8] + str v3, [sp, #12] + str v4, [sp, #16] + + mov v4, v5 + str v4, [sp, #20] + + mov v4, v6 + str v4, [sp, #24] + + mov v4, v7 + str v4, [sp, #28] + + mov v4, v8 + str v4, [sp, #32] + + mov v4, lr + str v4, [sp, #36] + + #endif + @ prepare stack for FPU sub sp, sp, #64 #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) @@ -71,7 +102,34 @@ jump_fcontext: add sp, sp, #64 @ restore hidden,V1-V8,LR + #if __ARM_ARCH >= 7 pop {a4,v1-v8,lr} + #else + + ldr v4, [sp, #20] + mov v5, v4 + + ldr v4, [sp, #24] + mov v6, v4 + + ldr v4, [sp, #28] + mov v7, v4 + + ldr v4, [sp, #32] + mov v8, v4 + + ldr v4, [sp, #36] + mov lr, v4 + + ldr a4, [sp, #0] + ldr v1, [sp, #4] + ldr v2, [sp, #8] + ldr v3, [sp, #12] + ldr v4, [sp, #16] + + add sp, #40 + + #endif @ return transfer_t from jump str a1, [a4, #0] diff --git a/Zend/asm/jump_arm_aapcs_macho_gas.S b/Zend/asm/jump_arm_aapcs_macho_gas.S index 077c36409e83..44e7f2a42274 100644 --- a/Zend/asm/jump_arm_aapcs_macho_gas.S +++ b/Zend/asm/jump_arm_aapcs_macho_gas.S @@ -39,6 +39,7 @@ *******************************************************/ .text +.private_extern _jump_fcontext .globl _jump_fcontext .align 2 _jump_fcontext: diff --git a/Zend/asm/jump_i386_ms_pe_masm.asm b/Zend/asm/jump_i386_ms_pe_masm.asm index 7a9e848f1c3f..a0450d0532f8 100644 --- a/Zend/asm/jump_i386_ms_pe_masm.asm +++ b/Zend/asm/jump_i386_ms_pe_masm.asm @@ -55,17 +55,17 @@ ENDIF mov eax, [edx] mov [esp+018h], eax - mov [esp+01ch], edi ; save EDI - mov [esp+020h], esi ; save ESI - mov [esp+024h], ebx ; save EBX - mov [esp+028h], ebp ; save EBP + mov [esp+01ch], edi ; save EDI + mov [esp+020h], esi ; save ESI + mov [esp+024h], ebx ; save EBX + mov [esp+028h], ebp ; save EBP ; store ESP (pointing to context-data) in EAX mov eax, esp ; firstarg of jump_fcontext() == fcontext to jump to mov ecx, [esp+030h] - + ; restore ESP (pointing to context-data) from ECX mov esp, ecx @@ -98,10 +98,10 @@ ENDIF mov ecx, [esp+02ch] ; restore EIP - mov edi, [esp+01ch] ; restore EDI - mov esi, [esp+020h] ; restore ESI - mov ebx, [esp+024h] ; restore EBX - mov ebp, [esp+028h] ; restore EBP + mov edi, [esp+01ch] ; restore EDI + mov esi, [esp+020h] ; restore ESI + mov ebx, [esp+024h] ; restore EBX + mov ebp, [esp+028h] ; restore EBP ; prepare stack lea esp, [esp+030h] diff --git a/Zend/asm/jump_i386_sysv_elf_gas.S b/Zend/asm/jump_i386_sysv_elf_gas.S index ed83717ce2b8..d00ac4fffd42 100644 --- a/Zend/asm/jump_i386_sysv_elf_gas.S +++ b/Zend/asm/jump_i386_sysv_elf_gas.S @@ -31,6 +31,7 @@ .file "jump_i386_sysv_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 2 .type jump_fcontext,@function jump_fcontext: diff --git a/Zend/asm/jump_i386_sysv_macho_gas.S b/Zend/asm/jump_i386_sysv_macho_gas.S index 8ab7c6f29c66..4cae3252d171 100644 --- a/Zend/asm/jump_i386_sysv_macho_gas.S +++ b/Zend/asm/jump_i386_sysv_macho_gas.S @@ -25,6 +25,7 @@ ****************************************************************************************/ .text +.private_extern _jump_fcontext .globl _jump_fcontext .align 2 _jump_fcontext: @@ -54,8 +55,8 @@ _jump_fcontext: /* return parent fcontext_t */ movl %ecx, %eax - /* returned data is stored in EDX */ - + /* returned data is stored in EDX */ + movl 0x18(%esp), %ecx /* restore EIP */ #if !defined(BOOST_USE_TSX) diff --git a/Zend/asm/jump_loongarch64_sysv_elf_gas.S b/Zend/asm/jump_loongarch64_sysv_elf_gas.S index 74c081e07faa..6f99e719ec00 100644 --- a/Zend/asm/jump_loongarch64_sysv_elf_gas.S +++ b/Zend/asm/jump_loongarch64_sysv_elf_gas.S @@ -41,6 +41,7 @@ .file "jump_loongarch64_sysv_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 2 .type jump_fcontext,@function jump_fcontext: diff --git a/Zend/asm/jump_mips32_o32_elf_gas.S b/Zend/asm/jump_mips32_o32_elf_gas.S index f2b8034d8c5b..38d22d26f6d7 100644 --- a/Zend/asm/jump_mips32_o32_elf_gas.S +++ b/Zend/asm/jump_mips32_o32_elf_gas.S @@ -41,6 +41,7 @@ .file "jump_mips32_o32_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 2 .type jump_fcontext,@function .ent jump_fcontext @@ -102,13 +103,13 @@ jump_fcontext: # adjust stack addiu $sp, $sp, 96 - + # return transfer_t from jump sw $a0, ($v0) # fctx of transfer_t sw $a2, 4($v0) # data of transfer_t # pass transfer_t as first arg in context function # A0 == fctx, A1 == data - move $a1, $a2 + move $a1, $a2 # jump to context jr $t9 diff --git a/Zend/asm/jump_mips64_n64_elf_gas.S b/Zend/asm/jump_mips64_n64_elf_gas.S index e338912bb6e8..60027fe61734 100644 --- a/Zend/asm/jump_mips64_n64_elf_gas.S +++ b/Zend/asm/jump_mips64_n64_elf_gas.S @@ -48,6 +48,7 @@ .file "jump_mips64_n64_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 3 .type jump_fcontext,@function .ent jump_fcontext diff --git a/Zend/asm/jump_ppc32_sysv_elf_gas.S b/Zend/asm/jump_ppc32_sysv_elf_gas.S index 48e09c935e09..e31336640065 100644 --- a/Zend/asm/jump_ppc32_sysv_elf_gas.S +++ b/Zend/asm/jump_ppc32_sysv_elf_gas.S @@ -53,6 +53,7 @@ .file "jump_ppc32_sysv_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .align 2 .type jump_fcontext,@function jump_fcontext: diff --git a/Zend/asm/jump_ppc32_sysv_macho_gas.S b/Zend/asm/jump_ppc32_sysv_macho_gas.S index fef90c295f8c..c2a2061e8774 100644 --- a/Zend/asm/jump_ppc32_sysv_macho_gas.S +++ b/Zend/asm/jump_ppc32_sysv_macho_gas.S @@ -68,12 +68,13 @@ * ------------------------------------------------- * * | 256 | | * * ------------------------------------------------- * - * | DATA| | * + * | DATA| | * * ------------------------------------------------- * * * *******************************************************/ .text +.private_extern _jump_fcontext .globl _jump_fcontext .align 2 _jump_fcontext: @@ -193,7 +194,7 @@ _jump_fcontext: ; adjust stack addi r1, r1, 244 - ; return transfer_t + ; return transfer_t stw r6, 0(r3) stw r5, 4(r3) diff --git a/Zend/asm/jump_ppc64_sysv_elf_gas.S b/Zend/asm/jump_ppc64_sysv_elf_gas.S index 28907db32b8c..debeb3466404 100644 --- a/Zend/asm/jump_ppc64_sysv_elf_gas.S +++ b/Zend/asm/jump_ppc64_sysv_elf_gas.S @@ -68,6 +68,7 @@ .file "jump_ppc64_sysv_elf_gas.S" .globl jump_fcontext +.hidden jump_fcontext #if _CALL_ELF == 2 .text .align 2 @@ -97,7 +98,7 @@ jump_fcontext: # endif #endif # reserve space on stack - subi %r1, %r1, 184 + subi %r1, %r1, 200 #if _CALL_ELF != 2 std %r2, 0(%r1) # save TOC @@ -133,6 +134,10 @@ jump_fcontext: # save LR as PC std %r0, 176(%r1) + # Save VS63 + li %r31, 184 + stvx %v31, %r1, %r31 + # store RSP (pointing to context-data) in R6 mr %r6, %r1 @@ -145,6 +150,11 @@ jump_fcontext: ld %r2, 0(%r1) # restore TOC #endif + + # Restore VS63 + li %r31, 184 + lvx %v31, %r1, %r31 + ld %r14, 8(%r1) # restore R14 ld %r15, 16(%r1) # restore R15 ld %r16, 24(%r1) # restore R16 @@ -180,7 +190,7 @@ jump_fcontext: mtctr %r12 # adjust stack - addi %r1, %r1, 184 + addi %r1, %r1, 200 #if _CALL_ELF == 2 # copy transfer_t into transfer_fn arg registers diff --git a/Zend/asm/jump_ppc64_sysv_macho_gas.S b/Zend/asm/jump_ppc64_sysv_macho_gas.S index dcc6c645db61..e4d8944b563d 100644 --- a/Zend/asm/jump_ppc64_sysv_macho_gas.S +++ b/Zend/asm/jump_ppc64_sysv_macho_gas.S @@ -68,6 +68,7 @@ .text .align 2 +.private_extern _jump_fcontext .globl _jump_fcontext _jump_fcontext: diff --git a/Zend/asm/jump_riscv64_sysv_elf_gas.S b/Zend/asm/jump_riscv64_sysv_elf_gas.S index 5417e5d5e349..879eb7efee62 100644 --- a/Zend/asm/jump_riscv64_sysv_elf_gas.S +++ b/Zend/asm/jump_riscv64_sysv_elf_gas.S @@ -61,6 +61,7 @@ .text .align 1 .global jump_fcontext +.hidden jump_fcontext .type jump_fcontext, %function jump_fcontext: # prepare stack for GP + FPU diff --git a/Zend/asm/jump_s390x_sysv_elf_gas.S b/Zend/asm/jump_s390x_sysv_elf_gas.S index fa71467756d2..59bc58bf370b 100644 --- a/Zend/asm/jump_s390x_sysv_elf_gas.S +++ b/Zend/asm/jump_s390x_sysv_elf_gas.S @@ -46,6 +46,7 @@ .text .align 8 .global jump_fcontext +.hidden jump_fcontext .type jump_fcontext, @function #define ARG_OFFSET 0 diff --git a/Zend/asm/jump_x86_64_sysv_elf_gas.S b/Zend/asm/jump_x86_64_sysv_elf_gas.S index be264bdc2e2b..2eff59a380cb 100644 --- a/Zend/asm/jump_x86_64_sysv_elf_gas.S +++ b/Zend/asm/jump_x86_64_sysv_elf_gas.S @@ -44,6 +44,7 @@ .file "jump_x86_64_sysv_elf_gas.S" .text .globl jump_fcontext +.hidden jump_fcontext .type jump_fcontext,@function .align 16 jump_fcontext: @@ -67,14 +68,6 @@ jump_fcontext: movq %rbx, 0x30(%rsp) /* save RBX */ movq %rbp, 0x38(%rsp) /* save RBP */ -#if BOOST_CONTEXT_SHADOW_STACK - /* grow the stack to reserve space for shadow stack pointer(SSP) */ - leaq -0x8(%rsp), %rsp - /* read the current SSP and store it */ - rdsspq %rcx - movq %rcx, (%rsp) -#endif - #if BOOST_CONTEXT_SHADOW_STACK /* grow the stack to reserve space for shadow stack pointer(SSP) */ leaq -0x8(%rsp), %rsp diff --git a/Zend/asm/jump_x86_64_sysv_macho_gas.S b/Zend/asm/jump_x86_64_sysv_macho_gas.S index afc3e5c126f9..673daa61dfaa 100644 --- a/Zend/asm/jump_x86_64_sysv_macho_gas.S +++ b/Zend/asm/jump_x86_64_sysv_macho_gas.S @@ -25,6 +25,7 @@ ****************************************************************************************/ .text +.private_extern _jump_fcontext .globl _jump_fcontext .align 8 _jump_fcontext: diff --git a/Zend/asm/make_arm64_aapcs_elf_gas.S b/Zend/asm/make_arm64_aapcs_elf_gas.S index 66cfb2da17ea..246525402f5c 100644 --- a/Zend/asm/make_arm64_aapcs_elf_gas.S +++ b/Zend/asm/make_arm64_aapcs_elf_gas.S @@ -53,10 +53,29 @@ .file "make_arm64_aapcs_elf_gas.S" .text +#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) +/* Mark this object as requiring BTI, enabling the linker to set BTI on the output */ + .pushsection .note.gnu.property, "a", %note + .p2align 3 + .long 4 /* namesz */ + .long 16 /* descsz (16 bytes) */ + .long 5 /* type = NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" + .p2align 3 + .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4 /* datasz = 4 */ + .long 1 /* GNU_PROPERTY_AARCH64_FEATURE_1_BTI */ + .long 0 /* pad to 8-byte alignment */ + .popsection +#endif .align 2 .global make_fcontext +.hidden make_fcontext .type make_fcontext, %function make_fcontext: +#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1) + hint #34 /* bti c */ +#endif # shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF diff --git a/Zend/asm/make_arm64_aapcs_macho_gas.S b/Zend/asm/make_arm64_aapcs_macho_gas.S index b30b1e3e5bbb..a6a1314c0dcd 100644 --- a/Zend/asm/make_arm64_aapcs_macho_gas.S +++ b/Zend/asm/make_arm64_aapcs_macho_gas.S @@ -52,6 +52,7 @@ *******************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext .balign 16 diff --git a/Zend/asm/make_arm64_aapcs_pe_armasm.asm b/Zend/asm/make_arm64_aapcs_pe_armasm.asm index 50f9b69876be..c384af039fc4 100644 --- a/Zend/asm/make_arm64_aapcs_pe_armasm.asm +++ b/Zend/asm/make_arm64_aapcs_pe_armasm.asm @@ -73,7 +73,7 @@ make_fcontext proc ; save top address of context_stack as 'base' str x3, [x0, #0xa0] - ; save bottom address of context-stack as 'limit' and 'dealloction stack' + ; save bottom address of context-stack as 'limit' and 'deallocation stack' sub x3, x3, x1 stp x3, x3, [x0, #0xa8] ; save 0 as 'fiber data' diff --git a/Zend/asm/make_arm_aapcs_elf_gas.S b/Zend/asm/make_arm_aapcs_elf_gas.S index 98ae64b43f9d..2367c75a6a77 100644 --- a/Zend/asm/make_arm_aapcs_elf_gas.S +++ b/Zend/asm/make_arm_aapcs_elf_gas.S @@ -41,26 +41,47 @@ .file "make_arm_aapcs_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 2 .type make_fcontext,%function .syntax unified make_fcontext: @ shift address in A1 to lower 16 byte boundary + + #if __ARM_ARCH >= 7 bic a1, a1, #15 + #else + lsrs a1, a1, #4 + lsls a1, a1, #4 + #endif @ reserve space for context-data on context-stack + #if __ARM_ARCH >= 7 sub a1, a1, #124 + #else + subs a1, #124 + #endif @ third arg of make_fcontext() == address of context-function str a3, [a1, #104] @ compute address of returned transfer_t + #if __ARM_ARCH >= 7 add a2, a1, #108 + #else + mov a2, a1 + adds a2, #108 + #endif mov a3, a2 str a3, [a1, #64] + @ compute abs address of label finish + #if __ARM_ARCH >= 7 adr a2, finish + #else + ldr a2, =finish + #endif @ save address of finish as return-address for context-function @ will be entered after context-function returns str a2, [a1, #100] @@ -72,7 +93,13 @@ make_fcontext: finish: @ exit code is zero + #if __ARM_ARCH >=7 mov a1, #0 + #else + movs r3, #0 + movs r0, r3 + #endif + @ exit application bl _exit@PLT .size make_fcontext,.-make_fcontext diff --git a/Zend/asm/make_arm_aapcs_macho_gas.S b/Zend/asm/make_arm_aapcs_macho_gas.S index c909ae9d43a2..de934075d247 100644 --- a/Zend/asm/make_arm_aapcs_macho_gas.S +++ b/Zend/asm/make_arm_aapcs_macho_gas.S @@ -39,6 +39,7 @@ *******************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext .align 2 _make_fcontext: diff --git a/Zend/asm/make_i386_ms_pe_masm.asm b/Zend/asm/make_i386_ms_pe_masm.asm index 5246465cb975..3879396390f2 100644 --- a/Zend/asm/make_i386_ms_pe_masm.asm +++ b/Zend/asm/make_i386_ms_pe_masm.asm @@ -53,7 +53,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT mov [eax+014h], ecx ; second arg of make_fcontext() == size of context-stack mov edx, [esp+08h] - ; negate stack size for LEA instruction (== substraction) + ; negate stack size for LEA instruction (== subtraction) neg edx ; compute bottom address of context stack (limit) lea ecx, [ecx+edx] diff --git a/Zend/asm/make_i386_sysv_elf_gas.S b/Zend/asm/make_i386_sysv_elf_gas.S index c6e0b36558a6..992bae019af2 100644 --- a/Zend/asm/make_i386_sysv_elf_gas.S +++ b/Zend/asm/make_i386_sysv_elf_gas.S @@ -31,6 +31,7 @@ .file "make_i386_sysv_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 2 .type make_fcontext,@function make_fcontext: diff --git a/Zend/asm/make_i386_sysv_macho_gas.S b/Zend/asm/make_i386_sysv_macho_gas.S index 519e406248bb..01076129bcf5 100644 --- a/Zend/asm/make_i386_sysv_macho_gas.S +++ b/Zend/asm/make_i386_sysv_macho_gas.S @@ -25,6 +25,7 @@ ****************************************************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext .align 2 _make_fcontext: @@ -69,7 +70,7 @@ _make_fcontext: addl $finish-2b, %ecx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movl %ecx, 0x14(%eax) + movl %ecx, 0x14(%eax) ret /* return pointer to context-data */ diff --git a/Zend/asm/make_loongarch64_sysv_elf_gas.S b/Zend/asm/make_loongarch64_sysv_elf_gas.S index 55062702f18d..a067baded56f 100644 --- a/Zend/asm/make_loongarch64_sysv_elf_gas.S +++ b/Zend/asm/make_loongarch64_sysv_elf_gas.S @@ -41,6 +41,7 @@ .file "make_loongarch64_sysv_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 2 .type make_fcontext,@function make_fcontext: diff --git a/Zend/asm/make_mips32_o32_elf_gas.S b/Zend/asm/make_mips32_o32_elf_gas.S index 4e11e3d05825..54742f3a8be2 100644 --- a/Zend/asm/make_mips32_o32_elf_gas.S +++ b/Zend/asm/make_mips32_o32_elf_gas.S @@ -41,6 +41,7 @@ .file "make_mips32_o32_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 2 .type make_fcontext,@function .ent make_fcontext diff --git a/Zend/asm/make_mips64_n64_elf_gas.S b/Zend/asm/make_mips64_n64_elf_gas.S index 7bb30b14dee7..c43f2ff368f9 100644 --- a/Zend/asm/make_mips64_n64_elf_gas.S +++ b/Zend/asm/make_mips64_n64_elf_gas.S @@ -48,6 +48,7 @@ .file "make_mips64_n64_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 3 .type make_fcontext,@function .ent make_fcontext @@ -69,7 +70,7 @@ make_fcontext: # save global pointer in context-data sd $gp, 136($v0) - # psudo instruction compute abs address of label finish based on GP + # pseudo instruction compute abs address of label finish based on GP dla $t9, finish # save address of finish as return-address for context-function diff --git a/Zend/asm/make_ppc32_sysv_elf_gas.S b/Zend/asm/make_ppc32_sysv_elf_gas.S index 9616c4ca9b46..0dd77719354b 100644 --- a/Zend/asm/make_ppc32_sysv_elf_gas.S +++ b/Zend/asm/make_ppc32_sysv_elf_gas.S @@ -53,6 +53,7 @@ .file "make_ppc32_sysv_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .align 2 .type make_fcontext,@function make_fcontext: diff --git a/Zend/asm/make_ppc32_sysv_macho_gas.S b/Zend/asm/make_ppc32_sysv_macho_gas.S index 1102ee90ef07..75d1c80e5263 100644 --- a/Zend/asm/make_ppc32_sysv_macho_gas.S +++ b/Zend/asm/make_ppc32_sysv_macho_gas.S @@ -68,12 +68,13 @@ * ------------------------------------------------- * * | 256 | | * * ------------------------------------------------- * - * | DATA| | * + * | DATA| | * * ------------------------------------------------- * * * *******************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext .align 2 _make_fcontext: @@ -101,8 +102,8 @@ _make_fcontext: ; compute address of returned transfer_t addi r0, r3, 252 - mr r4, r0 - stw r4, 228(r3) + mr r4, r0 + stw r4, 228(r3) ; load LR mflr r0 diff --git a/Zend/asm/make_ppc64_sysv_elf_gas.S b/Zend/asm/make_ppc64_sysv_elf_gas.S index c4d7ee59826a..f513d3639ea9 100644 --- a/Zend/asm/make_ppc64_sysv_elf_gas.S +++ b/Zend/asm/make_ppc64_sysv_elf_gas.S @@ -68,6 +68,7 @@ .file "make_ppc64_sysv_elf_gas.S" .globl make_fcontext +.hidden make_fcontext #if _CALL_ELF == 2 .text .align 2 diff --git a/Zend/asm/make_ppc64_sysv_macho_gas.S b/Zend/asm/make_ppc64_sysv_macho_gas.S index dd7bf2c5c0c0..d8cdd2d080db 100644 --- a/Zend/asm/make_ppc64_sysv_macho_gas.S +++ b/Zend/asm/make_ppc64_sysv_macho_gas.S @@ -67,6 +67,7 @@ *******************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext _make_fcontext: ; save return address into R6 diff --git a/Zend/asm/make_riscv64_sysv_elf_gas.S b/Zend/asm/make_riscv64_sysv_elf_gas.S index 5322e0fdbdec..6d97c59e877e 100644 --- a/Zend/asm/make_riscv64_sysv_elf_gas.S +++ b/Zend/asm/make_riscv64_sysv_elf_gas.S @@ -61,6 +61,7 @@ .text .align 1 .global make_fcontext +.hidden make_fcontext .type make_fcontext, %function make_fcontext: # shift address in a0 (allocated stack) to lower 16 byte boundary diff --git a/Zend/asm/make_s390x_sysv_elf_gas.S b/Zend/asm/make_s390x_sysv_elf_gas.S index 4dd423e2a44c..4d6aa62c0ae1 100644 --- a/Zend/asm/make_s390x_sysv_elf_gas.S +++ b/Zend/asm/make_s390x_sysv_elf_gas.S @@ -46,6 +46,7 @@ .text .align 8 .global make_fcontext +.hidden make_fcontext .type make_fcontext, @function #define ARG_OFFSET 0 diff --git a/Zend/asm/make_sparc64_sysv_elf_gas.S b/Zend/asm/make_sparc64_sysv_elf_gas.S index 3e7ee809c918..3d504471afd5 100644 --- a/Zend/asm/make_sparc64_sysv_elf_gas.S +++ b/Zend/asm/make_sparc64_sysv_elf_gas.S @@ -45,7 +45,7 @@ make_fcontext: add %l0, (trampoline - 1b - 8), %l0 stx %l0, [%i0 + I7] - # Save framepointer to first stack frame but first substract the BIAS + # Save framepointer to first stack frame but first subtract the BIAS add %i0, CC64FSZ - BIAS, %l0 stx %l0, [%i0 + SP] diff --git a/Zend/asm/make_x86_64_ms_pe_gas.S b/Zend/asm/make_x86_64_ms_pe_gas.S index 958a2a7b6d0e..7146637c0319 100644 --- a/Zend/asm/make_x86_64_ms_pe_gas.S +++ b/Zend/asm/make_x86_64_ms_pe_gas.S @@ -113,7 +113,7 @@ make_fcontext: /* save top address of context stack as 'base' */ movq %rcx, 0xc8(%rax) /* second arg of make_fcontext() == size of context-stack */ - /* negate stack size for LEA instruction (== substraction) */ + /* negate stack size for LEA instruction (== subtraction) */ negq %rdx /* compute bottom address of context stack (limit) */ leaq (%rcx,%rdx), %rcx @@ -151,7 +151,7 @@ make_fcontext: trampoline: /* store return address on stack */ - /* fix stack alignment */ + /* fix stack alignment */ pushq %rbp /* jump to context-function */ jmp *%rbx diff --git a/Zend/asm/make_x86_64_ms_pe_masm.asm b/Zend/asm/make_x86_64_ms_pe_masm.asm index 8f6c959a8376..a6c2b147e446 100644 --- a/Zend/asm/make_x86_64_ms_pe_masm.asm +++ b/Zend/asm/make_x86_64_ms_pe_masm.asm @@ -110,7 +110,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME ; save top address of context stack as 'base' mov [rax+0c8h], rcx ; second arg of make_fcontext() == size of context-stack - ; negate stack size for LEA instruction (== substraction) + ; negate stack size for LEA instruction (== subtraction) neg rdx ; compute bottom address of context stack (limit) lea rcx, [rcx+rdx] @@ -141,7 +141,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME ; compute abs address of label finish lea rcx, finish ; save address of finish as return-address for context-function in RBP - ; will be entered after context-function returns + ; will be entered after context-function returns mov [rax+0108h], rcx ret ; return pointer to context-data diff --git a/Zend/asm/make_x86_64_sysv_elf_gas.S b/Zend/asm/make_x86_64_sysv_elf_gas.S index b0d0c0341ef3..cabebff6e626 100644 --- a/Zend/asm/make_x86_64_sysv_elf_gas.S +++ b/Zend/asm/make_x86_64_sysv_elf_gas.S @@ -44,6 +44,7 @@ .file "make_x86_64_sysv_elf_gas.S" .text .globl make_fcontext +.hidden make_fcontext .type make_fcontext,@function .align 16 make_fcontext: @@ -76,7 +77,7 @@ make_fcontext: #if defined(BOOST_CONTEXT_TLS_STACK_PROTECTOR) /* save stack guard */ movq %fs:0x28, %rcx /* read stack guard from TLS record */ - movq %rcx, 0x8(%rsp) /* save stack guard */ + movq %rcx, 0x8(%rax) /* save stack guard */ #endif /* compute abs address of label trampoline */ @@ -91,35 +92,6 @@ make_fcontext: /* will be entered after context-function returns */ movq %rcx, 0x38(%rax) -#if BOOST_CONTEXT_SHADOW_STACK - /* Populate the shadow stack and normal stack */ - /* get original SSP */ - rdsspq %r8 - /* restore new shadow stack */ - rstorssp -0x8(%r9) - /* save the restore token on the original shadow stack */ - saveprevssp - /* push the address of "jmp trampoline" to the new shadow stack */ - /* as well as the stack */ - call 1f - jmp trampoline -1: - /* save address of "jmp trampoline" as return-address */ - /* for context-function */ - pop 0x38(%rax) - /* Get the new SSP. */ - rdsspq %r9 - /* restore original shadow stack */ - rstorssp -0x8(%r8) - /* save the restore token on the new shadow stack. */ - saveprevssp - - /* reserve space for the new SSP */ - leaq -0x8(%rax), %rax - /* save the new SSP to this fcontext */ - movq %r9, (%rax) -#endif - #if BOOST_CONTEXT_SHADOW_STACK /* Populate the shadow stack */ @@ -161,6 +133,8 @@ make_fcontext: ret /* return pointer to context-data */ trampoline: + .cfi_startproc + .cfi_undefined rip /* store return address on stack */ /* fix stack alignment */ _CET_ENDBR @@ -175,6 +149,7 @@ trampoline: #endif /* jump to context-function */ jmp *%rbx + .cfi_endproc finish: _CET_ENDBR diff --git a/Zend/asm/make_x86_64_sysv_macho_gas.S b/Zend/asm/make_x86_64_sysv_macho_gas.S index 5d6c5431c598..06357f678ac9 100644 --- a/Zend/asm/make_x86_64_sysv_macho_gas.S +++ b/Zend/asm/make_x86_64_sysv_macho_gas.S @@ -25,6 +25,7 @@ ****************************************************************************************/ .text +.private_extern _make_fcontext .globl _make_fcontext .align 8 _make_fcontext: From f08491bf08cda0b490b20e07a33c4b82e8dd2543 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 3 Jul 2026 13:36:48 +0100 Subject: [PATCH 056/211] ext/standard: getimagesize()/getimagesizefromstring() overflow. close GH-22574 --- NEWS | 2 ++ ext/standard/image.c | 3 +++ .../getimagesizefromstring_iff_overflow.phpt | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 ext/standard/tests/image/getimagesizefromstring_iff_overflow.phpt diff --git a/NEWS b/NEWS index d950ce969fc1..41fc6d053bc2 100644 --- a/NEWS +++ b/NEWS @@ -70,6 +70,8 @@ PHP NEWS incremental flush). (David Carlier) . Fixed bug GH-22395 (base_convert() outputs at most 64 characters). (Weilin Du) + . Fixed integer overflow in getimagesize() and getimagesizefromstring() + when parsing an IFF chunk with a size of INT_MAX. (David Carlier, Weilin Du) - Zip: . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores diff --git a/ext/standard/image.c b/ext/standard/image.c index 15761364c341..4709b93d0a5a 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -875,6 +875,9 @@ static struct gfxinfo *php_handle_iff(php_stream * stream) return NULL; } if ((size & 1) == 1) { + if (size == INT_MAX) { + return NULL; + } size++; } if (chunkId == 0x424d4844) { /* BMHD chunk */ diff --git a/ext/standard/tests/image/getimagesizefromstring_iff_overflow.phpt b/ext/standard/tests/image/getimagesizefromstring_iff_overflow.phpt new file mode 100644 index 000000000000..f6fdea9d8e6b --- /dev/null +++ b/ext/standard/tests/image/getimagesizefromstring_iff_overflow.phpt @@ -0,0 +1,24 @@ +--TEST-- +getimagesizefromstring() IFF chunk size integer overflow (GH-getimagesize_oflow) +--CREDITS-- +Alexandre Daubois +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(false) +bool(false) From 33d243c214253c3aa18618902fa86273d6938784 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 Jul 2026 21:06:28 +0100 Subject: [PATCH 057/211] ext/dom: fix GH-22570 new test --- ext/dom/tests/modern/xml/gh22570.phpt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/dom/tests/modern/xml/gh22570.phpt b/ext/dom/tests/modern/xml/gh22570.phpt index af78acf00dfa..3d648aac5e4d 100644 --- a/ext/dom/tests/modern/xml/gh22570.phpt +++ b/ext/dom/tests/modern/xml/gh22570.phpt @@ -15,13 +15,15 @@ if (getenv('SKIP_ASAN')) { zend.max_allowed_stack_size=512K --FILE-- appendChild($doc->createElement('root')); +$node = $doc->createElement('a'); for ($i = 0; $i < 100000; $i++) { - $node = $node->appendChild($doc->createElement('a')); + $parent = $doc->createElement('a'); + $parent->appendChild($node); + $node = $parent; } +$doc->appendChild($node); try { $doc->saveXml(); From 76c40ecbe33a698899fbb016ad882aed5c093452 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Mon, 6 Jul 2026 19:06:53 +0200 Subject: [PATCH 058/211] Fix incorrect method case for SplFileInfo::getPathname() in gen_stub.php (#22597) getPathName() was called instead of the correctly-cased getPathname(). --- build/gen_stub.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 1e1d53c5146c..715a47182340 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -84,7 +84,7 @@ function processDirectory(string $dir, Context $context): array { RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (str_ends_with($pathName, '.stub.php')) { $pathNames[] = $pathName; } @@ -5561,7 +5561,7 @@ function replacePredefinedConstants(string $targetDirectory, array $constMap, ar ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/(?:[\w\.]*constants[\w\._]*|tokens).xml$/i', basename($pathName))) { continue; } @@ -5742,7 +5742,7 @@ function replaceClassSynopses( ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/\.xml$/i', $pathName)) { continue; } @@ -5959,7 +5959,7 @@ function replaceMethodSynopses( ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/\.xml$/i', $pathName)) { continue; } From 016a1f1bc9e06fc94e83f914a7f0bbb81b3cd4ae Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Mon, 6 Jul 2026 19:20:19 +0200 Subject: [PATCH 059/211] Zend: reuse zend_get_unqualified_name() in zend_add_const_name_literal() (#22598) Avoid duplicating the namespace-separator split logic. --- Zend/zend_compile.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c75333e6d3c5..d9fa3a1cb504 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -673,12 +673,10 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* { int ret = zend_add_literal_string(&name); - size_t ns_len = 0, after_ns_len = ZSTR_LEN(name); - const char *after_ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name)); - if (after_ns) { - after_ns += 1; - ns_len = after_ns - ZSTR_VAL(name) - 1; - after_ns_len = ZSTR_LEN(name) - ns_len - 1; + const char *after_ns; + size_t after_ns_len; + if (zend_get_unqualified_name(name, &after_ns, &after_ns_len)) { + size_t ns_len = after_ns - ZSTR_VAL(name) - 1; /* lowercased namespace name & original constant name */ tmp_name = zend_string_init(ZSTR_VAL(name), ZSTR_LEN(name), 0); @@ -690,6 +688,7 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* { } } else { after_ns = ZSTR_VAL(name); + after_ns_len = ZSTR_LEN(name); } /* original unqualified constant name */ From de6828f50a4823226934d881dba12d144400d12e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Tue, 7 Jul 2026 01:20:33 +0800 Subject: [PATCH 060/211] Remove several useless `include ` (#22611) None of these files have use the sys/param.h file. --- ext/random/random.c | 4 ---- ext/standard/crypt_sha256.c | 1 - ext/standard/crypt_sha512.c | 1 - 3 files changed, 6 deletions(-) diff --git a/ext/random/random.c b/ext/random/random.c index bd6f9f8a9018..e7fe2c21801e 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -45,10 +45,6 @@ # include #endif -#ifdef HAVE_SYS_PARAM_H -# include -#endif - #include "random_arginfo.h" PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random) diff --git a/ext/standard/crypt_sha256.c b/ext/standard/crypt_sha256.c index 2673dcf2b08e..3f3d9cdeb03c 100644 --- a/ext/standard/crypt_sha256.c +++ b/ext/standard/crypt_sha256.c @@ -23,7 +23,6 @@ #ifdef PHP_WIN32 # include #else -# include # include # include #endif diff --git a/ext/standard/crypt_sha512.c b/ext/standard/crypt_sha512.c index e8cedaa55c20..4a308e2f9af2 100644 --- a/ext/standard/crypt_sha512.c +++ b/ext/standard/crypt_sha512.c @@ -22,7 +22,6 @@ #ifdef PHP_WIN32 # include #else -# include # include # include #endif From 5ab3b338ac8b6d921a0cc595d7db31853874a423 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 6 Jul 2026 19:23:26 +0200 Subject: [PATCH 061/211] session: ensure expected INI directives are set (#22577) --- ext/session/tests/bug80774.phpt | 2 ++ ext/session/tests/gh9200.phpt | 2 ++ ext/session/tests/session_regenerate_id_cookie.phpt | 2 ++ ext/session/tests/session_start_partitioned_headers.phpt | 2 ++ 4 files changed, 8 insertions(+) diff --git a/ext/session/tests/bug80774.phpt b/ext/session/tests/bug80774.phpt index ba5d39e1953a..1e98da74e9d6 100644 --- a/ext/session/tests/bug80774.phpt +++ b/ext/session/tests/bug80774.phpt @@ -4,6 +4,8 @@ Bug #80774 (session_name() problem with backslash) session --INI-- session.use_strict_mode=0 +session.cookie_samesite=Lax +session.cookie_httponly=1 --FILE-- Date: Tue, 7 Jul 2026 02:28:19 +0800 Subject: [PATCH 062/211] ext/standard: Fix `sleep` and `usleep` unsigned integer overflow (#22619) sleep(4294967296) actually sleep 0 seconds because of a unsigned int overflow bug. This is also true in usleep. I target this to master as a fix that is classified as a stricter parsing. Now we throw ValueError instead. p.s. in Windows, the sleep function is implemented with SleepEx (t * 1000, TRUE) not php_sleep, phew, so we need to lower the threshold to UINT_MAX / 1000 in Windows --- NEWS | 4 +++ ext/standard/basic_functions.c | 13 +++++++--- .../tests/general_functions/sleep_error.phpt | 2 +- .../sleep_usleep_uint_overflow.phpt | 25 +++++++++++++++++++ .../tests/general_functions/usleep_error.phpt | 2 +- 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt diff --git a/NEWS b/NEWS index 77ea35389d5f..11b470654384 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,10 @@ PHP NEWS . Fixed bug GH-22585 (OOM on bailout with uninitialized do_request() parameters). (David Carlier) +- Standard: + . Fixed sleep() and usleep() to reject values that overflow the underlying + unsigned int timeout. (Weilin Du) + - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL and a proxy set). (CVE-2026-12184) (ndossche) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index f8b1cb5c4fdb..e81050323754 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1123,13 +1123,18 @@ PHP_FUNCTION(flush) PHP_FUNCTION(sleep) { zend_long num; +#ifdef PHP_WIN32 + const unsigned int max = UINT_MAX / 1000; +#else + const unsigned int max = UINT_MAX; +#endif ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(num) ZEND_PARSE_PARAMETERS_END(); - if (num < 0) { - zend_argument_value_error(1, "must be greater than or equal to 0"); + if (num < 0 || (zend_ulong) num > max) { + zend_argument_value_error(1, "must be between 0 and %u", max); RETURN_THROWS(); } @@ -1146,8 +1151,8 @@ PHP_FUNCTION(usleep) Z_PARAM_LONG(num) ZEND_PARSE_PARAMETERS_END(); - if (num < 0) { - zend_argument_value_error(1, "must be greater than or equal to 0"); + if (num < 0 || (zend_ulong) num > UINT_MAX) { + zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX); RETURN_THROWS(); } diff --git a/ext/standard/tests/general_functions/sleep_error.phpt b/ext/standard/tests/general_functions/sleep_error.phpt index bfe66480fa03..47685fc3a5ae 100644 --- a/ext/standard/tests/general_functions/sleep_error.phpt +++ b/ext/standard/tests/general_functions/sleep_error.phpt @@ -7,7 +7,7 @@ sleep(-10); ?> --EXPECTF-- -Fatal error: Uncaught ValueError: sleep(): Argument #1 ($seconds) must be greater than or equal to 0 in %s:%d +Fatal error: Uncaught ValueError: sleep(): Argument #1 ($seconds) must be between 0 and %d in %s:%d Stack trace: #0 %s(%d): sleep(-10) #1 {main} diff --git a/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt b/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt new file mode 100644 index 000000000000..6f0a7b3fa9e8 --- /dev/null +++ b/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt @@ -0,0 +1,25 @@ +--TEST-- +sleep() and usleep() reject values above their unsigned int limits +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } +} +?> +--EXPECTF-- +int(0) +usleep(0) ok +sleep(): Argument #1 ($seconds) must be between 0 and %d +usleep(): Argument #1 ($microseconds) must be between 0 and 4294967295 diff --git a/ext/standard/tests/general_functions/usleep_error.phpt b/ext/standard/tests/general_functions/usleep_error.phpt index 46bd4420580b..9c3825f18b56 100644 --- a/ext/standard/tests/general_functions/usleep_error.phpt +++ b/ext/standard/tests/general_functions/usleep_error.phpt @@ -7,7 +7,7 @@ usleep(-10); ?> --EXPECTF-- -Fatal error: Uncaught ValueError: usleep(): Argument #1 ($microseconds) must be greater than or equal to 0 in %s:%d +Fatal error: Uncaught ValueError: usleep(): Argument #1 ($microseconds) must be between 0 and 4294967295 in %s:%d Stack trace: #0 %s(%d): usleep(-10) #1 {main} From f04eaec41ac88ea75e51592e91aa3f7c65939d21 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 6 Jul 2026 15:26:00 -0400 Subject: [PATCH 063/211] Fix GH-22549: GMP compound pow/shift assign asserts on negative operand (#22582) shift_operator_helper() wrote ZVAL_UNDEF(return_value) on the out-of-range exponent/shift path, but for a compound assignment return_value aliases op1, so pow_function's fallback then reached the scalar conversion with op1 set to IS_UNDEF and hit ZEND_UNREACHABLE(). Drop the stray undef; the helper's other error paths already return FAILURE without touching return_value, leaving the operand intact for the thrown ValueError to propagate. Fixes GH-22549 --- NEWS | 4 +++ ext/gmp/gmp.c | 1 - ext/gmp/tests/gh22549.phpt | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 ext/gmp/tests/gh22549.phpt diff --git a/NEWS b/NEWS index 11b470654384..2a266243a747 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) +- GMP: + . Fixed bug GH-22549 (Assertion failure / UB on a compound GMP power or shift + assignment with a negative exponent). (iliaal) + - Intl: . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to reject offset values outside the 32-bit range instead of silently diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index ef06d0fda30c..59209c4e1be3 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -361,7 +361,6 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val zend_ce_value_error, "%s must be between 0 and %lu", opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX ); - ZVAL_UNDEF(return_value); return FAILURE; } else { mpz_ptr gmpnum_op, gmpnum_result; diff --git a/ext/gmp/tests/gh22549.phpt b/ext/gmp/tests/gh22549.phpt new file mode 100644 index 000000000000..82a66901bd09 --- /dev/null +++ b/ext/gmp/tests/gh22549.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-22549 (Assertion failure in _zendi_try_convert_scalar_to_number with compound GMP shift/pow) +--EXTENSIONS-- +gmp +--FILE-- + [2, fn(&$x) => $x **= gmp_init(-5)], + 'gmp ** gmp' => [gmp_init(2), fn(&$x) => $x **= gmp_init(-5)], + 'array ** gmp' => [[0], fn(&$x) => $x **= gmp_init(-5)], + 'int << gmp' => [8, fn(&$x) => $x <<= gmp_init(-2)], + 'int >> gmp' => [8, fn(&$x) => $x >>= gmp_init(-2)], + 'gmp << gmp' => [gmp_init(8), fn(&$x) => $x <<= gmp_init(-2)], +]; + +foreach ($cases as $label => [$x, $op]) { + try { + $op($x); + echo "$label: no exception\n"; + } catch (\Throwable $e) { + echo "$label: ", $e::class, ": ", $e->getMessage(), "\n"; + } + var_dump($x); +} + +// Valid operations must still succeed (guard against an over-broad regression). +$p = 2; $p **= gmp_init(3); echo "valid pow: ", gmp_strval($p), "\n"; +$s = 1; $s <<= gmp_init(4); echo "valid shift: ", gmp_strval($s), "\n"; +?> +--EXPECTF-- +int ** gmp: ValueError: Exponent must be between 0 and %d +int(2) +gmp ** gmp: ValueError: Exponent must be between 0 and %d +object(GMP)#%d (1) { + ["num"]=> + string(1) "2" +} +array ** gmp: ValueError: Exponent must be between 0 and %d +array(1) { + [0]=> + int(0) +} +int << gmp: ValueError: Shift must be between 0 and %d +int(8) +int >> gmp: ValueError: Shift must be between 0 and %d +int(8) +gmp << gmp: ValueError: Shift must be between 0 and %d +object(GMP)#%d (1) { + ["num"]=> + string(1) "8" +} +valid pow: 8 +valid shift: 16 From 3704518154580db06687c631c1f201644785c80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 6 Jul 2026 21:26:28 +0200 Subject: [PATCH 064/211] ext/uri: Increase minimum uriparser version dependency (#22575) uriparser 1.0.3 has to be released containing some missing symbols in order to be able to build PHP with the "--with-external-uriparser" option. Closes https://github.com/php/php-src/issues/22553 --- ext/uri/config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index a518cf84b3bd..dddf0c6e00b8 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -34,7 +34,7 @@ if test "$PHP_EXTERNAL_URIPARSER" = "no"; then $URIPARSER_DIR/src/UriSetScheme.c $URIPARSER_DIR/src/UriSetUserInfo.c $URIPARSER_DIR/src/UriShorten.c $URIPARSER_DIR/src/UriVersion.c" URI_CFLAGS="-DURI_STATIC_BUILD" else - PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 1.0.0]) + PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 1.0.3]) PHP_EVAL_LIBLINE([$LIBURIPARSER_LIBS]) PHP_EVAL_INCLINE([$LIBURIPARSER_CFLAGS]) fi From d6be30e3252fa6a52ed03f666745ace7b4c717ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Jul 2026 22:46:38 +0200 Subject: [PATCH 065/211] iconv: Fix memory management in autoconf test for `//IGNORE` (#22620) Sometimes running `./configure` for ASAN hangs on my machine. This is reproducible when compiling and running the program manually using clang-16 -g -fsanitize=undefined,address test.c It seems this is related to the LLVM symbolizer getting stuck, and the symbolizer is likely required to report the memory leak. After adding the memory management functions it seems to work (or rather fail) reliably. --- ext/iconv/config.m4 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/iconv/config.m4 b/ext/iconv/config.m4 index e86f3535a97c..bea97b27f4f7 100644 --- a/ext/iconv/config.m4 +++ b/ext/iconv/config.m4 @@ -112,6 +112,8 @@ int main(void) { char *out = malloc(out_left); char *out_p = out; size_t result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left); + free(out); + iconv_close(cd); if(result == (size_t)-1) { return 1; } From e758d885b24ed3b276babe51db6c9ea7332198f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Jul 2026 22:50:17 +0200 Subject: [PATCH 066/211] zend_types: Remove `Z_*CONSTANT_*()` (#22295) This macro is very rarely used and badly named by neither including the `IS` nor the `AST` in its name. --- UPGRADING.INTERNALS | 3 +++ Zend/zend_compile.c | 4 ++-- Zend/zend_ini_parser.y | 2 +- Zend/zend_types.h | 5 ----- Zend/zend_vm_def.h | 4 ++-- Zend/zend_vm_execute.h | 8 ++++---- ext/opcache/jit/zend_jit_ir.c | 2 +- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index a8eb6b805413..adfc91880f29 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -120,6 +120,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added ZEND_CONTAINER_OF(). . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead use php_check_open_basedir() directly. + . The Z_CONSTANT(), Z_CONSTANT_P(), Z_OPT_CONSTANT(), and + Z_OPT_CONSTANT_P() macros have been removed. Check for IS_CONSTANT_AST + directly. . Added zend_reflection_property_set_raw_value_without_lazy_initialization(), zend_reflection_property_set_raw_value() to expose the functionality of ReflectionProperty::setRawValueWithoutLazyInitialization() and diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d9fa3a1cb504..b73e2009075f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9224,7 +9224,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (*value_ast_ptr) { zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); - if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv) + if (ZEND_TYPE_IS_SET(type) && Z_TYPE(value_zv) != IS_CONSTANT_AST && !zend_is_valid_default_value(type, &value_zv)) { zend_string *str = zend_type_to_string(type); if (Z_TYPE(value_zv) == IS_NULL && !ZEND_TYPE_IS_INTERSECTION(type)) { @@ -9348,7 +9348,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); - if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { + if (Z_TYPE(value_zv) != IS_CONSTANT_AST && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { zend_string *type_str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s", diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index d35853ab5acd..47b05de4e692 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -151,7 +151,7 @@ static void zend_ini_get_constant(zval *result, zval *name) && (c = zend_get_constant(Z_STR_P(name))) != 0) { if (Z_TYPE_P(c) != IS_STRING) { ZVAL_COPY_OR_DUP(&tmp, c); - if (Z_OPT_CONSTANT(tmp)) { + if (Z_OPT_TYPE(tmp) == IS_CONSTANT_AST) { zval_update_constant_ex(&tmp, NULL); } convert_to_string(&tmp); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 2cdfdcb1961f..56774e4e9d8f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -1017,8 +1017,6 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 } while(0) /* All data types < IS_STRING have their constructor/destructors skipped */ -#define Z_CONSTANT(zval) (Z_TYPE(zval) == IS_CONSTANT_AST) -#define Z_CONSTANT_P(zval_p) Z_CONSTANT(*(zval_p)) #if 1 /* This optimized version assumes that we have a single "type_flag" */ @@ -1036,9 +1034,6 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 #define Z_OPT_TYPE(zval) (Z_TYPE_INFO(zval) & Z_TYPE_MASK) #define Z_OPT_TYPE_P(zval_p) Z_OPT_TYPE(*(zval_p)) -#define Z_OPT_CONSTANT(zval) (Z_OPT_TYPE(zval) == IS_CONSTANT_AST) -#define Z_OPT_CONSTANT_P(zval_p) Z_OPT_CONSTANT(*(zval_p)) - #define Z_OPT_REFCOUNTED(zval) Z_TYPE_INFO_REFCOUNTED(Z_TYPE_INFO(zval)) #define Z_OPT_REFCOUNTED_P(zval_p) Z_OPT_REFCOUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a0bfad488f85..ec9a97eeb254 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8323,7 +8323,7 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) val = GET_OP2_ZVAL_PTR(BP_VAR_R); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); FREE_OP1(); @@ -8355,7 +8355,7 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) val = GET_OP2_ZVAL_PTR(BP_VAR_R); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); FREE_OP1(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cedc735bbb1e..34b2455ce439 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -8290,7 +8290,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_CONST val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -8325,7 +8325,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_ATTRI val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -60979,7 +60979,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_CONST_SPEC val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -61014,7 +61014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_ATTRIBUTED val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index d62ef95b5513..b0b04456706b 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10924,7 +10924,7 @@ static int zend_jit_recv_init(zend_jit_ctx *jit, const zend_op *opline, const ze zv, true); } - if (Z_CONSTANT_P(zv)) { + if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { jit_SET_EX_OPLINE(jit, opline); ref = ir_CALL_2(IR_I32, ir_CONST_FC_FUNC(zval_update_constant_ex), jit_ZVAL_ADDR(jit, res_addr), From 7330366438084f60cfe857d92f98fa02f98ebc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Jul 2026 22:50:58 +0200 Subject: [PATCH 067/211] zend_ast: Wrap class names in parens during export when they are an expression (#22389) Fixes php/php-src#22387. --- NEWS | 2 + Zend/zend_ast.c | 64 ++++++++++++++++++-------- Zend/zend_ast.h | 3 ++ Zend/zend_compile.c | 12 +---- ext/standard/tests/assert/gh22387.phpt | 62 +++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 ext/standard/tests/assert/gh22387.phpt diff --git a/NEWS b/NEWS index 2a266243a747..705ff85b2a6e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Sync Boost.Context assembly with 1.91.0. (kn1g78) + . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around + RHS of instanceof). (timwolla) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d3ce419c737e..3aa04de860a1 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1686,6 +1686,19 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int } zend_ast_export_ex(str, ast, priority, indent); } +static ZEND_COLD void zend_ast_export_ns_name_or_expression(smart_str *str, zend_ast *ast, int priority, int indent) +{ + switch (ast->kind) { + case ZEND_AST_ZVAL: + case ZEND_AST_VAR: + zend_ast_export_ns_name(str, ast, priority, indent); + break; + default: + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast, priority, indent); + smart_str_appendc(str, ')'); + } +} static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len) { @@ -2523,25 +2536,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_CALL: { - zend_ast *left = ast->child[0]; - switch (left->kind) { - /* ZEND_AST_ZVAL is a regular function call. */ - case ZEND_AST_ZVAL: - /* ZEND_AST_VAR ($foo()) is unambiguous without parens. */ - case ZEND_AST_VAR: - zend_ast_export_ns_name(str, left, 0, indent); - break; - default: - smart_str_appendc(str, '('); - zend_ast_export_ex(str, left, 0, indent); - smart_str_appendc(str, ')'); - break; - } + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2553,7 +2553,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio goto simple_list; } case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2570,7 +2570,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio default: ZEND_UNREACHABLE(); } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); } smart_str_appends(str, "::class"); break; @@ -2649,7 +2649,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } zend_ast_export_class_no_header(str, decl, indent); } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2658,7 +2658,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_INSTANCEOF: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, " instanceof "); - zend_ast_export_ns_name(str, ast->child[1], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[1], 0, indent); break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); @@ -2851,7 +2851,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + if (zend_ast_is_parent_hook_call(ast)) { + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + } else { + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); + } + smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], indent); smart_str_appendc(str, '('); @@ -3082,3 +3087,22 @@ zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast) ZEND_UNREACHABLE(); return NULL; } + +bool zend_ast_is_parent_hook_call(const zend_ast *ast) +{ + ZEND_ASSERT(ast->kind == ZEND_AST_STATIC_CALL); + + const zend_ast *class_ast = ast->child[0]; + zend_ast *method_ast = ast->child[1]; + + return class_ast->kind == ZEND_AST_STATIC_PROP + && !(class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP) + && class_ast->child[0]->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) == IS_STRING + && zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) == ZEND_FETCH_CLASS_PARENT + && class_ast->child[1]->kind == ZEND_AST_ZVAL + && method_ast->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(method_ast)) == IS_STRING + && (zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get") + || zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set")); +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 24b77d7d3493..86a68c1cbaf9 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -440,4 +440,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast); +/* Recognize parent::$prop::get() pattern. */ +bool zend_ast_is_parent_hook_call(const zend_ast *ast); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b73e2009075f..ac5a9d71a6ea 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5353,17 +5353,7 @@ static bool zend_compile_parent_property_hook_call(znode *result, const zend_ast const zend_ast *class_ast = ast->child[0]; zend_ast *method_ast = ast->child[1]; - /* Recognize parent::$prop::get() pattern. */ - if (class_ast->kind != ZEND_AST_STATIC_PROP - || (class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP) - || class_ast->child[0]->kind != ZEND_AST_ZVAL - || Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING - || zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) != ZEND_FETCH_CLASS_PARENT - || class_ast->child[1]->kind != ZEND_AST_ZVAL - || method_ast->kind != ZEND_AST_ZVAL - || Z_TYPE_P(zend_ast_get_zval(method_ast)) != IS_STRING - || (!zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get") - && !zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set"))) { + if (!zend_ast_is_parent_hook_call(ast)) { return false; } diff --git a/ext/standard/tests/assert/gh22387.phpt b/ext/standard/tests/assert/gh22387.phpt new file mode 100644 index 000000000000..38c93921608a --- /dev/null +++ b/ext/standard/tests/assert/gh22387.phpt @@ -0,0 +1,62 @@ +--TEST-- +GH-22387: AST pretty-printing drops meaningful parentheses around RHS of instanceof +--FILE-- +getMessage(), PHP_EOL; +} + +try { + assert(!new (bar)()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::m()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::$p); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::C); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert((baz)::class !== 'stdClass'); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +assert(!$foo instanceof (bar)) +assert(!new (bar)()) +assert(!(bar)::m()) +assert(!(bar)::$p) +assert(!(bar)::C) +assert((baz)::class !== 'stdClass') From 21204bf59a2f95c395470c402d077880c8f11cca Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 7 Jul 2026 11:01:19 +0200 Subject: [PATCH 068/211] Fix performance of gh22443.phpt These millions of iterations don't seem to be necessary to trigger the original bug. --- ext/opcache/tests/jit/gh22443.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/tests/jit/gh22443.phpt b/ext/opcache/tests/jit/gh22443.phpt index 869329b79b5c..4baa3f99fc61 100644 --- a/ext/opcache/tests/jit/gh22443.phpt +++ b/ext/opcache/tests/jit/gh22443.phpt @@ -45,7 +45,7 @@ for ($k = 0; $k < 8; $k++) { $cold = [makeListener([$svc, 'coldMethod'])]; $s = 0; -for ($i = 0; $i < 4000000; $i++) { +for ($i = 0; $i < 400; $i++) { $s += invokeListeners($warm, 'e', [$i & 7, ($i >> 2) & 7]); } for ($j = 0; $j < 5; $j++) { From ba32d70130533736bcad5241b62a0772a459aa4a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 7 Jul 2026 12:55:06 +0200 Subject: [PATCH 069/211] [skip ci] Add -DZEND_VERIFY_TYPE_INFERENCE to CXXFLAGS for variation build Without this, executor globals vary in size and offsets for C vs. C++, which leads to issues since GH-22287. --- .github/workflows/test-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 4c28ccca3561..e8f698e4e091 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -166,7 +166,7 @@ jobs: with: configurationParameters: >- ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }} - ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE"' || '' }} + ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" CXXFLAGS="-DZEND_VERIFY_TYPE_INFERENCE"' || '' }} ${{ (matrix.variation && fromJson(inputs.branch).jobs.LINUX_X64.config.variation_enable_zend_max_execution_timers) && '--enable-zend-max-execution-timers' || '' }} --${{ matrix.debug && 'enable' || 'disable' }}-debug ${{ matrix.debug && 'CXXFLAGS="-D_GLIBCXX_ASSERTIONS"' || '' }} From 53b80f07f0f4dc1ebdb2cc710bbf8b83e799616c Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 7 Jul 2026 08:13:14 -0400 Subject: [PATCH 070/211] Check the stack limit when calling internal functions (#22545) zend_call_function invokes an internal callee's handler directly, with no VM frame and without the stack-limit check the interpreter runs at its call opcodes. An internal function that recurses through zend_call_function, such as a self- or mutually-attached SPL iterator, never yields back to the VM, so nothing bounds the recursion and the C stack overflows into a SEGV. Check zend_call_stack_overflowed() before pushing the call frame and raise the usual "Maximum call stack size reached" error on overflow. Running the check ahead of the frame setup keeps the error path free of teardown. Fixes GH-15672 Fixes GH-15911 --- Zend/zend_execute_API.c | 8 ++++++++ ext/spl/tests/gh15672.phpt | 22 ++++++++++++++++++++++ ext/spl/tests/gh15911.phpt | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 ext/spl/tests/gh15672.phpt create mode 100644 ext/spl/tests/gh15911.phpt diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 71e0c56a51c8..cb48e6234371 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -861,6 +861,14 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } } +#ifdef ZEND_CHECK_STACK_LIMIT + if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) { + zend_call_stack_size_error(); + zend_release_fcall_info_cache(fci_cache); + return SUCCESS; + } +#endif + call = zend_vm_stack_push_call_frame(call_info, func, fci->param_count, object_or_called_scope); uint32_t consumed_args = fci->param_count ? fci->consumed_args : 0; diff --git a/ext/spl/tests/gh15672.phpt b/ext/spl/tests/gh15672.phpt new file mode 100644 index 000000000000..ff3feda46f7e --- /dev/null +++ b/ext/spl/tests/gh15672.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-15672 (MultipleIterator attached to itself overflows the C stack) +--SKIPIF-- + +--EXTENSIONS-- +zend_test +--INI-- +memory_limit=2G +zend.max_allowed_stack_size=512K +--FILE-- +attachIterator(new ArrayIterator([1, 2, 3]), "1"); +$m->attachIterator($m, 3); +foreach ($m as $key => $value) { +} +?> +--EXPECTREGEX-- +.*Maximum call stack size of \d+ bytes.*Infinite recursion\?.*thrown in .* on line \d+ diff --git a/ext/spl/tests/gh15911.phpt b/ext/spl/tests/gh15911.phpt new file mode 100644 index 000000000000..502b2baf616b --- /dev/null +++ b/ext/spl/tests/gh15911.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-15911 (AppendIterator appended to itself overflows the C stack) +--SKIPIF-- + +--EXTENSIONS-- +zend_test +--INI-- +memory_limit=2G +zend.max_allowed_stack_size=512K +--FILE-- +append($it); +foreach ($it as $v) { +} +?> +--EXPECTREGEX-- +.*Maximum call stack size of \d+ bytes.*Infinite recursion\?.*thrown in .* on line \d+ From 4afc970827a038e380551cc97e9a14493cae0854 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 7 Jul 2026 08:31:42 -0400 Subject: [PATCH 071/211] [skip ci] Add NEWS entry for GH-15672 and GH-15911 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 705ff85b2a6e..11c9d1db1af8 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Sync Boost.Context assembly with 1.91.0. (kn1g78) . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around RHS of instanceof). (timwolla) + . Fixed bug GH-15672 and GH-15911 (Stack overflow when an internal function + recurses through zend_call_function, such as a self-attached SPL + iterator). (iliaal) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) From 12c1f1c12f369a06259d239644cd0d3ce37dbe96 Mon Sep 17 00:00:00 2001 From: arshidkv12 Date: Tue, 7 Jul 2026 22:23:11 +0800 Subject: [PATCH 072/211] ext/calendar: Fix integer overflow in GregorianToSdn() and JulianToJd() (#22604) https://github.com/php/php-src/pull/22603 GregorianToSdn() accepted large positive years up to INT_MAX and then added 4800 before doing the SDN calculation, which triggered signed integer overflow. JulianToSdn() used the same adjustment and had the same overflow surface. Reject years that cannot be adjusted safely before the calculation. Add coverage for the reported gregoriantojd() case and the matching juliantojd() case with boundary values. Closes #22602 Closes #22604 --- NEWS | 4 ++++ ext/calendar/gregor.c | 1 + ext/calendar/julian.c | 1 + ext/calendar/tests/gh22602.phpt | 20 ++++++++++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 ext/calendar/tests/gh22602.phpt diff --git a/NEWS b/NEWS index 41fc6d053bc2..5f615701b4ab 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 +- Calendar: + . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with + INT_MAX year). (arshidkv12) + - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/ext/calendar/gregor.c b/ext/calendar/gregor.c index 3aef7ae6ac50..eaee9c8c73c7 100644 --- a/ext/calendar/gregor.c +++ b/ext/calendar/gregor.c @@ -209,6 +209,7 @@ zend_long GregorianToSdn( /* check for invalid dates */ if (inputYear == 0 || inputYear < -4714 || + inputYear > INT_MAX - 4800 || inputMonth <= 0 || inputMonth > 12 || inputDay <= 0 || inputDay > 31) { return (0); diff --git a/ext/calendar/julian.c b/ext/calendar/julian.c index ac580aa08e06..53a207a606f4 100644 --- a/ext/calendar/julian.c +++ b/ext/calendar/julian.c @@ -222,6 +222,7 @@ zend_long JulianToSdn( /* check for invalid dates */ if (inputYear == 0 || inputYear < -4713 || + inputYear > INT_MAX - 4800 || inputMonth <= 0 || inputMonth > 12 || inputDay <= 0 || inputDay > 31) { return (0); diff --git a/ext/calendar/tests/gh22602.phpt b/ext/calendar/tests/gh22602.phpt new file mode 100644 index 000000000000..d165a0174094 --- /dev/null +++ b/ext/calendar/tests/gh22602.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug GH-22602: (gregoriantojd() and juliantojd() integer overflow with INT_MAX year) +--EXTENSIONS-- +calendar +--FILE-- + 2147483647 ? 2147483647 : PHP_INT_MAX; +$min = PHP_INT_MAX > 2147483647 ? -2147483648 : PHP_INT_MIN; + +var_dump(gregoriantojd(5, 5, $max)); +var_dump(gregoriantojd(5, 5, $min)); +var_dump(juliantojd(5, 5, 2147483647)); +var_dump(juliantojd(5, 5, -2147483647)); + +?> +--EXPECT-- +int(0) +int(0) +int(0) +int(0) From dd86a8ab58c32a563047861496b420f87277e9b8 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:44:29 -0700 Subject: [PATCH 073/211] Implement `ReflectionClass::isAnonymous()` using `_class_check_flag()` --- ext/reflection/php_reflection.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 37c45cb02168..d611b5d6bc11 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4362,15 +4362,22 @@ ZEND_METHOD(ReflectionClass, isUserDefined) } /* }}} */ -/* {{{ Returns whether this class is anonymous */ -ZEND_METHOD(ReflectionClass, isAnonymous) +/* {{{ _class_check_flag */ +static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) { reflection_object *intern; zend_class_entry *ce; ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - RETURN_BOOL(ce->ce_flags & ZEND_ACC_ANON_CLASS); + RETVAL_BOOL(ce->ce_flags & mask); +} +/* }}} */ + +/* {{{ Returns whether this class is anonymous */ +ZEND_METHOD(ReflectionClass, isAnonymous) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_ANON_CLASS); } /* }}} */ @@ -4880,18 +4887,6 @@ ZEND_METHOD(ReflectionClass, getReflectionConstant) } /* }}} */ -/* {{{ _class_check_flag */ -static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) -{ - reflection_object *intern; - zend_class_entry *ce; - - ZEND_PARSE_PARAMETERS_NONE(); - GET_REFLECTION_OBJECT_PTR(ce); - RETVAL_BOOL(ce->ce_flags & mask); -} -/* }}} */ - /* {{{ Returns whether this class is instantiable */ ZEND_METHOD(ReflectionClass, isInstantiable) { From ed27dbcdc83fd3a66e0af583c2e114ead3a1b620 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 17:05:54 -0700 Subject: [PATCH 074/211] Reflection: use `RETURN_BOOL` in `_class_check_flag()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d611b5d6bc11..bfff775cc43e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4370,7 +4370,7 @@ static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - RETVAL_BOOL(ce->ce_flags & mask); + RETURN_BOOL(ce->ce_flags & mask); } /* }}} */ From f98ebbf5423676fd65d4215e4bbc0994b5d1f5c6 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:46:34 -0700 Subject: [PATCH 075/211] Implement `ReflectionClassConstant::isEnumCase()` using existing helper Use the `_class_constant_check_flag()` helper function to simplify things --- ext/reflection/php_reflection.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bfff775cc43e..8e3113f7a6ec 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4045,12 +4045,7 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes) ZEND_METHOD(ReflectionClassConstant, isEnumCase) { - reflection_object *intern; - zend_class_constant *ref; - - GET_REFLECTION_OBJECT_PTR(ref); - - RETURN_BOOL(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE); + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_CLASS_CONST_IS_CASE); } ZEND_METHOD(ReflectionClassConstant, isDeprecated) From 22f80dcf9932035bacbde7fc61712373ff72bdb0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:47:41 -0700 Subject: [PATCH 076/211] Implement `ReflectionClassConstant::isDeprecated()` using existing helper Use the `_class_constant_check_flag()` helper function to simplify things --- ext/reflection/php_reflection.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8e3113f7a6ec..f25cd09e067f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4050,14 +4050,7 @@ ZEND_METHOD(ReflectionClassConstant, isEnumCase) ZEND_METHOD(ReflectionClassConstant, isDeprecated) { - reflection_object *intern; - zend_constant *ref; - - ZEND_PARSE_PARAMETERS_NONE(); - - GET_REFLECTION_OBJECT_PTR(ref); - - RETURN_BOOL(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_ACC_DEPRECATED); + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_DEPRECATED); } /* {{{ reflection_class_object_ctor */ From 0a858ea9f4f2ff5c280b0ac7e53d31282d5333b2 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:48:07 -0700 Subject: [PATCH 077/211] Fix inline documentation of `ReflectionProperty::getName()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index f25cd09e067f..0eb458fa9272 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5741,7 +5741,7 @@ ZEND_METHOD(ReflectionProperty, __toString) } /* }}} */ -/* {{{ Returns the class' name */ +/* {{{ Returns the property's name */ ZEND_METHOD(ReflectionProperty, getName) { reflection_object *intern; From 36a602b077f95d5dadd52e9fd0af0136c4c7e695 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:48:50 -0700 Subject: [PATCH 078/211] Update inline documentation of `ReflectionProperty::setAccessible()` Since PHP 8.1 ReflectionProperty has always allowed access to non-public properties and the method is a no-op. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0eb458fa9272..6fdf038bd140 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6392,7 +6392,7 @@ ZEND_METHOD(ReflectionProperty, getAttributes) } /* }}} */ -/* {{{ Sets whether non-public properties can be requested */ +/* {{{ No-op; previously controlled whether non-public properties can be requested */ ZEND_METHOD(ReflectionProperty, setAccessible) { bool visible; From ff3947756473ed5c813018dd8d363944d34beec5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:56:00 -0700 Subject: [PATCH 079/211] Fix inline documentation of `Reflection*::getModifiers()` methods Affected methods: * `ReflectionMethod::getModifiers()` * `ReflectionClassConstant::getModifiers()` * `ReflectionClass::getModifiers()` * `ReflectionProperty::getModifiers()` They were all documented bitfields of "access modifiers", however they all included modifiers unrelated to visibility/access. --- ext/reflection/php_reflection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 6fdf038bd140..be0446e2302a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3735,7 +3735,7 @@ ZEND_METHOD(ReflectionMethod, isDestructor) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this method */ +/* {{{ Returns a bitfield of the modifiers for this method */ ZEND_METHOD(ReflectionMethod, getModifiers) { reflection_object *intern; @@ -3959,7 +3959,7 @@ ZEND_METHOD(ReflectionClassConstant, isFinal) _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL); } -/* {{{ Returns a bitfield of the access modifiers for this constant */ +/* {{{ Returns a bitfield of the modifiers for this constant */ ZEND_METHOD(ReflectionClassConstant, getModifiers) { reflection_object *intern; @@ -4965,7 +4965,7 @@ ZEND_METHOD(ReflectionClass, isAbstract) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this class */ +/* {{{ Returns a bitfield of the modifiers for this class */ ZEND_METHOD(ReflectionClass, getModifiers) { reflection_object *intern; @@ -5865,7 +5865,7 @@ ZEND_METHOD(ReflectionProperty, isPromoted) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this property */ +/* {{{ Returns a bitfield of the modifiers for this property */ ZEND_METHOD(ReflectionProperty, getModifiers) { reflection_object *intern; From 4795c51aecfb2315ee2087ce29896c72e0c22c3b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:57:27 -0700 Subject: [PATCH 080/211] Fix inline documentation of `ReflectionType::allowsNull()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index be0446e2302a..37bbc40f08df 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3058,7 +3058,7 @@ ZEND_METHOD(ReflectionParameter, isPromoted) } /* }}} */ -/* {{{ Returns whether parameter MAY be null */ +/* {{{ Returns whether the type MAY be null */ ZEND_METHOD(ReflectionType, allowsNull) { reflection_object *intern; From 5dfab102bd6edb92050a363d282b9c5aa19e1d0f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:58:33 -0700 Subject: [PATCH 081/211] Fix inline documentation of `ReflectionMethod::getClosure()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 37bbc40f08df..5d5c8eaeb038 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3364,7 +3364,7 @@ ZEND_METHOD(ReflectionMethod, __toString) } /* }}} */ -/* {{{ Invokes the function */ +/* {{{ Returns a dynamically created closure for the function */ ZEND_METHOD(ReflectionMethod, getClosure) { reflection_object *intern; From 6a19160ca561c2ae11313d98105db3f4a0598276 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:03:32 -0700 Subject: [PATCH 082/211] Avoid unnecessary concatenation in `zend_named_reflection_type_to_string()` When a type corresponds to an iterator that may be null, the string representation is known ahead of time to be "?iterable". Use `ZSTR_INIT_LITERAL()` rather than building up a string with `zend_string_concat2()` from a "?" and the "iterable" known string. --- ext/reflection/php_reflection.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5d5c8eaeb038..8d409a127b81 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3074,11 +3074,10 @@ ZEND_METHOD(ReflectionType, allowsNull) /* For BC with iterable for named types */ static zend_string *zend_named_reflection_type_to_string(zend_type type) { if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { - zend_string *iterable = ZSTR_KNOWN(ZEND_STR_ITERABLE); if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_NULL) { - return zend_string_concat2("?", strlen("?"), ZSTR_VAL(iterable), ZSTR_LEN(iterable)); + return ZSTR_INIT_LITERAL("?iterable", false); } - return iterable; + return ZSTR_KNOWN(ZEND_STR_ITERABLE); } return zend_type_to_string(type); } From f44ae1bedb4bd24c797b9082854e2f72cc994a5a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:25:32 -0700 Subject: [PATCH 083/211] Reflection: use `true` and `false` for booleans rather than `0`/`1` Update local variables and arguments to * `zend_read_static_property_ex()` * `zend_string_alloc()` * `zend_string_init()` * `zend_string_release_ex()` * `zend_verify_property_type()` * `zend_verify_ref_assignable_zval()` * Object handlers' get_closure() function * `RETURN_ZVAL` (macro) * `ZEND_TYPE_INIT` (macro) * `ZEND_TYPE_INIT_CODE` (macro) --- ext/reflection/php_reflection.c | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8d409a127b81..07a97fe55ad5 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -212,7 +212,7 @@ static void _free_function(zend_function *fptr) /* {{{ */ if (fptr && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(fptr->internal_function.function_name, 0); + zend_string_release_ex(fptr->internal_function.function_name, false); zend_free_trampoline(fptr); } } @@ -220,7 +220,7 @@ static void _free_function(zend_function *fptr) /* {{{ */ static void reflection_free_property_reference(property_reference *reference) { - zend_string_release_ex(reference->unmangled_name, 0); + zend_string_release_ex(reference->unmangled_name, false); efree(reference); } @@ -567,7 +567,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const smart_str_append_printf(str, "%s }\n", indent); smart_str_append_printf(str, "%s}\n", indent); - zend_string_release_ex(sub_indent, 0); + zend_string_release_ex(sub_indent, false); } /* }}} */ @@ -1227,7 +1227,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_append_printf(str, "%s }\n", indent); } smart_str_free(&str_classes); - zend_string_release_ex(sub_indent, 0); + zend_string_release_ex(sub_indent, false); } smart_str_append_printf(str, "%s}\n", indent); @@ -1587,7 +1587,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_string *name, static void reflection_property_factory_str(zend_class_entry *ce, const char *name_str, size_t name_len, zend_property_info *prop, zval *object) { - zend_string *name = zend_string_init(name_str, name_len, 0); + zend_string *name = zend_string_init(name_str, name_len, false); reflection_property_factory(ce, name, prop, object); zend_string_release(name); } @@ -1847,7 +1847,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) zend_function *closure_func; zend_object *object; if (Z_OBJ_HANDLER(intern->obj, get_closure) - && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS + && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS && closure_func && (called_scope || closure_func->common.scope)) { zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); } @@ -2107,7 +2107,7 @@ ZEND_METHOD(ReflectionFunction, invoke) if (!Z_ISUNDEF(intern->obj)) { Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); + Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); } zend_call_known_fcc(&fcc, &retval, num_args, params, named_params); @@ -2140,7 +2140,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) if (!Z_ISUNDEF(intern->obj)) { Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); + Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); } zend_call_known_fcc(&fcc, &retval, /* num_params */ 0, /* params */ NULL, params); @@ -2460,7 +2460,7 @@ ZEND_METHOD(ReflectionParameter, __construct) struct _zend_arg_info *arg_info; uint32_t num_args; zend_class_entry *ce = NULL; - bool is_closure = 0; + bool is_closure = false; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ZVAL(reference) @@ -2554,7 +2554,7 @@ ZEND_METHOD(ReflectionParameter, __construct) if (instanceof_function(ce, zend_ce_closure)) { fptr = (zend_function *)zend_get_closure_method_def(Z_OBJ_P(reference)); Z_ADDREF_P(reference); - is_closure = 1; + is_closure = true; } else if ((fptr = zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Method %s::%s() does not exist", ZSTR_VAL(ce->name), ZEND_INVOKE_FUNC_NAME); @@ -2628,7 +2628,7 @@ ZEND_METHOD(ReflectionParameter, __construct) failure: if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { - zend_string_release_ex(fptr->common.function_name, 0); + zend_string_release_ex(fptr->common.function_name, false); zend_free_trampoline(fptr); } if (is_closure) { @@ -3168,7 +3168,7 @@ ZEND_METHOD(ReflectionUnionType, getTypes) } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0)); + append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, false, 0)); } type_mask = ZEND_TYPE_PURE_MASK(param->type); @@ -3290,7 +3290,7 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ } tmp_len = tmp - name; - class_name = zend_string_init(name, tmp_len, 0); + class_name = zend_string_init(name, tmp_len, false); method_name = tmp + 2; method_name_len = ZSTR_LEN(arg1_str) - tmp_len - 2; } @@ -4266,12 +4266,12 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue) zend_reference *ref = Z_REF_P(variable_ptr); variable_ptr = Z_REFVAL_P(variable_ptr); - if (!zend_verify_ref_assignable_zval(ref, value, 0)) { + if (!zend_verify_ref_assignable_zval(ref, value, false)) { return; } } - if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, 0)) { + if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, false)) { return; } @@ -4636,7 +4636,7 @@ ZEND_METHOD(ReflectionClass, getProperty) str_name = ZSTR_VAL(name); if ((tmp = strstr(ZSTR_VAL(name), "::")) != NULL) { classname_len = tmp - ZSTR_VAL(name); - classname = zend_string_init(ZSTR_VAL(name), classname_len, 0); + classname = zend_string_init(ZSTR_VAL(name), classname_len, false); str_name_len = ZSTR_LEN(name) - (classname_len + 2); str_name = tmp + 2; @@ -4645,10 +4645,10 @@ ZEND_METHOD(ReflectionClass, getProperty) if (!EG(exception)) { zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" does not exist", ZSTR_VAL(classname)); } - zend_string_release_ex(classname, 0); + zend_string_release_ex(classname, false); RETURN_THROWS(); } - zend_string_release_ex(classname, 0); + zend_string_release_ex(classname, false); if (!instanceof_function(ce, ce2)) { zend_throw_exception_ex(reflection_exception_ptr, -1, "Fully qualified property name %s::$%s does not specify a base class of %s", ZSTR_VAL(ce2->name), str_name, ZSTR_VAL(ce->name)); @@ -5299,7 +5299,7 @@ ZEND_METHOD(ReflectionClass, getLazyInitializer) RETURN_NULL(); } - RETURN_ZVAL(zend_lazy_object_get_initializer_zv(object), 1, 0); + RETURN_ZVAL(zend_lazy_object_get_initializer_zv(object), true, false); } /* }}} */ @@ -5437,11 +5437,11 @@ ZEND_METHOD(ReflectionClass, getTraitAliases) break; } } - zend_string_release_ex(lcname, 0); + zend_string_release_ex(lcname, false); ZEND_ASSERT(class_name != NULL); } - mname = zend_string_alloc(ZSTR_LEN(class_name) + ZSTR_LEN(cur_ref->method_name) + 2, 0); + mname = zend_string_alloc(ZSTR_LEN(class_name) + ZSTR_LEN(cur_ref->method_name) + 2, false); snprintf(ZSTR_VAL(mname), ZSTR_LEN(mname) + 1, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(cur_ref->method_name)); add_assoc_str_ex(return_value, ZSTR_VAL(ce->trait_aliases[i]->alias), ZSTR_LEN(ce->trait_aliases[i]->alias), mname); } @@ -5894,7 +5894,7 @@ ZEND_METHOD(ReflectionProperty, getValue) GET_REFLECTION_OBJECT_PTR(ref); if (prop_get_flags(ref) & ZEND_ACC_STATIC) { - member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 0); + member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, false); if (member_p) { RETURN_COPY_DEREF(member_p); } @@ -6304,7 +6304,7 @@ ZEND_METHOD(ReflectionProperty, isInitialized) GET_REFLECTION_OBJECT_PTR(ref); if (prop_get_flags(ref) & ZEND_ACC_STATIC) { - const zval *member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 1); + const zval *member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, true); if (member_p) { RETURN_BOOL(!Z_ISUNDEF_P(member_p)); } @@ -6437,7 +6437,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType) /* Get-only virtual property can never be written to. */ if (prop->hooks && (prop->flags & ZEND_ACC_VIRTUAL) && !prop->hooks[ZEND_PROPERTY_HOOK_SET]) { - zend_type never_type = ZEND_TYPE_INIT_CODE(IS_NEVER, 0, 0); + zend_type never_type = ZEND_TYPE_INIT_CODE(IS_NEVER, false, 0); reflection_type_factory(never_type, return_value, true); return; } @@ -7142,7 +7142,7 @@ ZEND_METHOD(ReflectionExtension, getDependencies) len += strlen(dep->version) + 1; } - relation = zend_string_alloc(len, 0); + relation = zend_string_alloc(len, false); snprintf(ZSTR_VAL(relation), ZSTR_LEN(relation) + 1, "%s%s%s%s%s", rel_type, dep->rel ? " " : "", @@ -7728,7 +7728,7 @@ ZEND_METHOD(ReflectionEnum, getBackingType) if (ce->enum_backing_type == IS_UNDEF) { RETURN_NULL(); } else { - zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0); + zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, false, 0); reflection_type_factory(type, return_value, false); } } From 4f8f79893657f0205e0d7a4fa1610b9087d2f3f4 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:33:23 -0700 Subject: [PATCH 084/211] Reflection: convert some integers used for conditions to booleans The previous commit addressed places where `0` or `1` where used in places where a boolean was already expected, such a function arguments declared to be booleans. Now, address some inner implementation details of the reflection extension that use integers for conditions: * `dynam_prop` local variable in `ReflectionProperty::__construct()` * `first` local variable in `_extension_string()` * `is_object` parameter to `reflection_class_object_ctor()` * `variadic` parameter to `reflection_method_invoke()` --- ext/reflection/php_reflection.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 07a97fe55ad5..bcc0fc39d98a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1194,14 +1194,14 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c { zend_function *fptr; - int first = 1; + bool first = true; ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), fptr) { if (fptr->common.type==ZEND_INTERNAL_FUNCTION && fptr->internal_function.module == module) { if (first) { smart_str_appends(str, "\n - Functions {\n"); - first = 0; + first = false; } _function_string(str, fptr, NULL, " "); } @@ -3402,7 +3402,7 @@ ZEND_METHOD(ReflectionMethod, getClosure) /* }}} */ /* {{{ reflection_method_invoke */ -static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) +static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, bool variadic) { zval retval; zval *params = NULL, *object; @@ -3503,14 +3503,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) /* {{{ Invokes the method. */ ZEND_METHOD(ReflectionMethod, invoke) { - reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} */ /* {{{ Invokes the function and pass its arguments as array. */ ZEND_METHOD(ReflectionMethod, invokeArgs) { - reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} */ @@ -4053,7 +4053,7 @@ ZEND_METHOD(ReflectionClassConstant, isDeprecated) } /* {{{ reflection_class_object_ctor */ -static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_object) +static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, bool is_object) { zval *object; zend_string *arg_class = NULL; @@ -4100,7 +4100,7 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob /* {{{ Constructor. Takes a string or an instance as an argument */ ZEND_METHOD(ReflectionClass, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} */ @@ -5652,7 +5652,7 @@ ZEND_METHOD(ReflectionClass, getShortName) /* {{{ Constructor. Takes an instance as an argument */ ZEND_METHOD(ReflectionObject, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} */ @@ -5662,7 +5662,7 @@ ZEND_METHOD(ReflectionProperty, __construct) zend_string *classname_str; zend_object *classname_obj; zend_string *name; - int dynam_prop = 0; + bool dynam_prop = false; zval *object; reflection_object *intern; zend_class_entry *ce; @@ -5693,10 +5693,10 @@ ZEND_METHOD(ReflectionProperty, __construct) /* Check for dynamic properties */ if (property_info == NULL && classname_obj) { if (zend_hash_exists(classname_obj->handlers->get_properties(classname_obj), name)) { - dynam_prop = 1; + dynam_prop = true; } } - if (dynam_prop == 0) { + if (!dynam_prop) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Property %s::$%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name)); RETURN_THROWS(); } @@ -5706,7 +5706,7 @@ ZEND_METHOD(ReflectionProperty, __construct) zval_ptr_dtor(prop_name); ZVAL_STR_COPY(prop_name, name); /* Note: class name are always interned, no need to destroy them */ - if (dynam_prop == 0) { + if (!dynam_prop) { ZVAL_STR_COPY(reflection_prop_class(object), property_info->ce->name); } else { ZVAL_STR_COPY(reflection_prop_class(object), ce->name); @@ -7624,7 +7624,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance) ZEND_METHOD(ReflectionEnum, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); if (EG(exception)) { RETURN_THROWS(); } From 5370fc8f1667f9022995a2652bd95066d6139a8f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:44:10 -0700 Subject: [PATCH 085/211] Reflection: remove unneeded indentation support from `_extension_string()` The only caller (`ReflectionExtension::__toString()`) always provided an empty string as the indentation; removing the indentation entirely simplifies the building of the string representation, including replacing some uses of `smart_str_append_printf()` with `smart_str_appends()`, which is more performant. The indentation levels passed to `_extension_ini_string()` and `_extension_class_string()` from within `_extension_string()` are kept for now and will be addressed in follow-up commits. --- ext/reflection/php_reflection.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bcc0fc39d98a..28e16602a77d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -300,7 +300,7 @@ static void _property_string(smart_str *str, const zend_property_info *prop, con static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent); -static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent); +static void _extension_string(smart_str *str, const zend_module_entry *module); static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent); /* {{{ _class_string */ @@ -1110,9 +1110,9 @@ static void _extension_class_string(zend_class_entry *ce, zend_string *key, smar } /* }}} */ -static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent) /* {{{ */ +static void _extension_string(smart_str *str, const zend_module_entry *module) /* {{{ */ { - smart_str_append_printf(str, "%sExtension [ ", indent); + smart_str_appends(str, "Extension [ "); if (module->type == MODULE_PERSISTENT) { smart_str_appends(str, ""); } @@ -1129,7 +1129,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_appends(str, "\n - Dependencies {\n"); while(dep->name) { - smart_str_append_printf(str, "%s Dependency [ %s (", indent, dep->name); + smart_str_append_printf(str, " Dependency [ %s (", dep->name); switch(dep->type) { case MODULE_DEP_REQUIRED: @@ -1155,19 +1155,19 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_appends(str, ") ]\n"); dep++; } - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } { smart_str str_ini = {0}; zend_ini_entry *ini_entry; ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) { - _extension_ini_string(ini_entry, &str_ini, indent, module->module_number); + _extension_ini_string(ini_entry, &str_ini, "", module->module_number); } ZEND_HASH_FOREACH_END(); if (smart_str_get_len(&str_ini) > 0) { smart_str_appends(str, "\n - INI {\n"); smart_str_append_smart_str(str, &str_ini); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_ini); } @@ -1187,7 +1187,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c if (num_constants) { smart_str_append_printf(str, "\n - Constants [%d] {\n", num_constants); smart_str_append_smart_str(str, &str_constants); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_constants); } @@ -1207,30 +1207,28 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c } } ZEND_HASH_FOREACH_END(); if (!first) { - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } } { - zend_string *sub_indent = strpprintf(0, "%s ", indent); smart_str str_classes = {0}; zend_string *key; zend_class_entry *ce; int num_classes = 0; ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) { - _extension_class_string(ce, key, &str_classes, ZSTR_VAL(sub_indent), module, &num_classes); + _extension_class_string(ce, key, &str_classes, " ", module, &num_classes); } ZEND_HASH_FOREACH_END(); if (num_classes) { smart_str_append_printf(str, "\n - Classes [%d] {", num_classes); smart_str_append_smart_str(str, &str_classes); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_classes); - zend_string_release_ex(sub_indent, false); } - smart_str_append_printf(str, "%s}\n", indent); + smart_str_appends(str, "}\n"); } /* }}} */ @@ -6921,7 +6919,7 @@ ZEND_METHOD(ReflectionExtension, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(module); - _extension_string(&str, module, ""); + _extension_string(&str, module); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From 4427cb451682502e4d301506923f4982627ccb01 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:11:58 -0700 Subject: [PATCH 086/211] Reflection: remove unneeded indentation support from `_extension_ini_string()` The only caller (`_extension_string()`) always provided `_extension_ini_string()` with the same indentation that it was called with; after the prior commit removed indentation support from `_extension_string()` it became clear that the INI string function also always received an empty string as the indentation. Removing the indentation entirely simplifies the building of the string representation, including replacing a use of `smart_str_append_printf()` with `smart_str_appends()`, which is more performant. --- ext/reflection/php_reflection.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 28e16602a77d..47c5311e2465 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1065,12 +1065,12 @@ static void _property_string(smart_str *str, const zend_property_info *prop, con } /* }}} */ -static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *str, const char *indent, int number) /* {{{ */ +static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *str, int number) /* {{{ */ { char *comma = ""; if (number == ini_entry->module_number) { - smart_str_append_printf(str, " %sEntry [ %s <", indent, ZSTR_VAL(ini_entry->name)); + smart_str_append_printf(str, " Entry [ %s <", ZSTR_VAL(ini_entry->name)); if (ini_entry->modifiable == ZEND_INI_ALL) { smart_str_appends(str, "ALL"); } else { @@ -1088,11 +1088,11 @@ static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *st } smart_str_appends(str, "> ]\n"); - smart_str_append_printf(str, " %s Current = '%s'\n", indent, ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); + smart_str_append_printf(str, " Current = '%s'\n", ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); if (ini_entry->modified) { - smart_str_append_printf(str, " %s Default = '%s'\n", indent, ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); + smart_str_append_printf(str, " Default = '%s'\n", ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); } - smart_str_append_printf(str, " %s}\n", indent); + smart_str_appends(str, " }\n"); } } /* }}} */ @@ -1162,7 +1162,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module) / smart_str str_ini = {0}; zend_ini_entry *ini_entry; ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) { - _extension_ini_string(ini_entry, &str_ini, "", module->module_number); + _extension_ini_string(ini_entry, &str_ini, module->module_number); } ZEND_HASH_FOREACH_END(); if (smart_str_get_len(&str_ini) > 0) { smart_str_appends(str, "\n - INI {\n"); From 0d680a2390d647f3417d2a02fabd25eb149b628b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:15:29 -0700 Subject: [PATCH 087/211] Reflection: remove unneeded indentation support from `_zend_extension_string()` The only caller (`ReflectionZendExtension::__toString()`) always provided an empty string as the indentation; removing the indentation slightly simplifies the building of the string representation. --- ext/reflection/php_reflection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 47c5311e2465..2c7690d1fe6f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -301,7 +301,7 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent); static void _extension_string(smart_str *str, const zend_module_entry *module); -static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent); +static void _zend_extension_string(smart_str *str, const zend_extension *extension); /* {{{ _class_string */ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent) @@ -1347,9 +1347,9 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut } /* }}} */ -static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent) /* {{{ */ +static void _zend_extension_string(smart_str *str, const zend_extension *extension) /* {{{ */ { - smart_str_append_printf(str, "%sZend Extension [ %s ", indent, extension->name); + smart_str_append_printf(str, "Zend Extension [ %s ", extension->name); if (extension->version) { smart_str_append_printf(str, "%s ", extension->version); @@ -7230,7 +7230,7 @@ ZEND_METHOD(ReflectionZendExtension, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(extension); - _zend_extension_string(&str, extension, ""); + _zend_extension_string(&str, extension); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From 7eef7c9a9a813c8f0768108ee6321130aeecec10 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:45:04 -0700 Subject: [PATCH 088/211] Reflection: access common function fields through `zend_function.common` The `zend_function` union has both an `op_array` member for userland code, and an `internal_function` member for internal code. The elements at the start of each match, and are also made available under the `common` member which only has the fields present in both `op_array` and `internal_function`. For function pointers that have not been checked to determine if they are userland functions or internal functions, access common fields through `common`. While technically there shouldn't be a difference due to the common layout (up through the relevant fields), semantically accessing information about an internal function through `op_array` or about a userland function through `internal_function` does not make sense. --- ext/reflection/php_reflection.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 2c7690d1fe6f..0a670c856eb2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -193,12 +193,12 @@ static inline bool is_closure_invoke(const zend_class_entry *ce, const zend_stri static zend_function *_copy_function(zend_function *fptr) /* {{{ */ { if (fptr - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) + && (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { zend_function *copy_fptr; copy_fptr = emalloc(sizeof(zend_function)); memcpy(copy_fptr, fptr, sizeof(zend_function)); - copy_fptr->internal_function.function_name = zend_string_copy(fptr->internal_function.function_name); + copy_fptr->common.function_name = zend_string_copy(fptr->common.function_name); return copy_fptr; } else { /* no copy needed */ @@ -210,9 +210,9 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */ static void _free_function(zend_function *fptr) /* {{{ */ { if (fptr - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) + && (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(fptr->internal_function.function_name, false); + zend_string_release_ex(fptr->common.function_name, false); zend_free_trampoline(fptr); } } @@ -930,7 +930,7 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze smart_str_appends(str, "function "); } - if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (fptr->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { smart_str_appendc(str, '&'); } smart_str_append_printf(str, "%s ] {\n", ZSTR_VAL(fptr->common.function_name)); @@ -948,7 +948,7 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze } _function_parameter_string(str, fptr, ZSTR_VAL(param_indent.s)); smart_str_free(¶m_indent); - if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { + if ((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { smart_str_append_printf(str, " %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return"); if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type); @@ -2160,7 +2160,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, returnsReference) GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL((fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0); + RETURN_BOOL((fptr->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0); } /* }}} */ @@ -2392,8 +2392,8 @@ ZEND_METHOD(ReflectionGenerator, getFunction) zval closure; ZVAL_OBJ(&closure, ZEND_CLOSURE_OBJECT(func)); reflection_function_factory(func, &closure, return_value); - } else if (func->op_array.scope) { - reflection_method_factory(func->op_array.scope, func, NULL, return_value); + } else if (func->common.scope) { + reflection_method_factory(func->common.scope, func, NULL, return_value); } else { reflection_function_factory(func, NULL, return_value); } @@ -3650,7 +3650,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - RETVAL_BOOL((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); + RETVAL_BOOL((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); } /* }}} */ @@ -3664,7 +3664,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { + if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { RETURN_NULL(); } @@ -3682,7 +3682,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); + RETVAL_BOOL(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); } /* }}} */ @@ -3696,7 +3696,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { + if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { RETURN_NULL(); } From c89e3df7646cc4feeb7b9b6d7a6cf270d0d0e556 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:52:00 -0700 Subject: [PATCH 089/211] `ReflectionNamedType::getName()`: inline logic for legacy behavior Instead of a single-use two-line helper function `zend_type_to_string_without_null()`, move the relevant logic into `ReflectionNamedType::getName()` directly. To maintain the behavior of removing the `MAY_BE_NULL` flag from a *copy* of the stored type information, make an explicit copy of the type before the flag is removed. --- ext/reflection/php_reflection.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0a670c856eb2..271a4aae6e0b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3080,11 +3080,6 @@ static zend_string *zend_named_reflection_type_to_string(zend_type type) { return zend_type_to_string(type); } -static zend_string *zend_type_to_string_without_null(zend_type type) { - ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; - return zend_named_reflection_type_to_string(type); -} - /* {{{ Return the text of the type hint */ ZEND_METHOD(ReflectionType, __toString) { @@ -3107,10 +3102,12 @@ ZEND_METHOD(ReflectionNamedType, getName) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(param); + // Make a copy so that we don't modify the stored type information + zend_type type = param->type; if (param->legacy_behavior) { - RETURN_STR(zend_type_to_string_without_null(param->type)); + ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; } - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(type)); } /* }}} */ From 09eaaae5dd551424ad747e9427180a27a0a811b3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:10:34 -0700 Subject: [PATCH 090/211] `ReflectionFunction::__toString()`: stop accessing `intern->ce` For global functions there is no class entry set, the constructor a few lines above explicitly sets `intern->ce = NULL;`, as does `reflection_function_factory()`. Replace the access with `NULL` to avoid suggesting that there might be a class entry to use. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 271a4aae6e0b..a336ab5b8854 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1767,7 +1767,7 @@ ZEND_METHOD(ReflectionFunction, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(fptr); - _function_string(&str, fptr, intern->ce, ""); + _function_string(&str, fptr, NULL, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From 046596102d607518ca7e629f949a91f853eddaeb Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:32:09 -0700 Subject: [PATCH 091/211] `ReflectionFunctionAbstract::getClosureUsedVariables()`: optimize arrays Avoid `array_init(return_value);` in cases where the function does not correspond to a closure or has no static variables, to avoid an unneeded allocation for an array that will be empty. --- ext/reflection/php_reflection.c | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a336ab5b8854..69ce3ae8a93d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1862,44 +1862,47 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT(); - array_init(return_value); - if (!Z_ISUNDEF(intern->obj)) { - closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); - if (closure_func == NULL || - closure_func->type != ZEND_USER_FUNCTION || - closure_func->op_array.static_variables == NULL) { - return; - } + if (Z_ISUNDEF(intern->obj)) { + RETURN_EMPTY_ARRAY(); + } - const zend_op_array *ops = &closure_func->op_array; + closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); + if (closure_func == NULL || + closure_func->type != ZEND_USER_FUNCTION || + closure_func->op_array.static_variables == NULL + ) { + RETURN_EMPTY_ARRAY(); + } - HashTable *static_variables = ZEND_MAP_PTR_GET(ops->static_variables_ptr); + const zend_op_array *ops = &closure_func->op_array; - if (!static_variables) { - return; - } + HashTable *static_variables = ZEND_MAP_PTR_GET(ops->static_variables_ptr); - zend_op *opline = ops->opcodes + ops->num_args; - if (ops->fn_flags & ZEND_ACC_VARIADIC) { - opline++; - } + if (!static_variables) { + RETURN_EMPTY_ARRAY(); + } - for (; opline->opcode == ZEND_BIND_STATIC; opline++) { - if (!(opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) { - continue; - } + array_init(return_value); + zend_op *opline = ops->opcodes + ops->num_args; + if (ops->fn_flags & ZEND_ACC_VARIADIC) { + opline++; + } - Bucket *bucket = (Bucket*) - (((char*)static_variables->arData) + - (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); + for (; opline->opcode == ZEND_BIND_STATIC; opline++) { + if (!(opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) { + continue; + } - if (Z_ISUNDEF(bucket->val)) { - continue; - } + Bucket *bucket = (Bucket*) + (((char*)static_variables->arData) + + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); - zend_hash_add_new(Z_ARRVAL_P(return_value), bucket->key, &bucket->val); - Z_TRY_ADDREF(bucket->val); + if (Z_ISUNDEF(bucket->val)) { + continue; } + + zend_hash_add_new(Z_ARRVAL_P(return_value), bucket->key, &bucket->val); + Z_TRY_ADDREF(bucket->val); } } /* }}} */ From 08c039c87a06eccf110076960e15a3ec71851953 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:46:16 -0700 Subject: [PATCH 092/211] `ReflectionClass::getStaticProperties()`: optimize array initialization For classes with no static properties, avoid allocating an array that will be empty. Includes a regression test for the function since it was previously untested, to confirm that even when a class declares no static properties of its own, inherited static properties are still returned. --- ext/reflection/php_reflection.c | 12 +++- .../ReflectionClass_getStaticProperties.phpt | 57 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/ReflectionClass_getStaticProperties.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 69ce3ae8a93d..3abd95731ca3 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4159,7 +4159,17 @@ ZEND_METHOD(ReflectionClass, getStaticProperties) RETURN_THROWS(); } - if (ce->default_static_members_count && !CE_STATIC_MEMBERS(ce)) { + // default_static_members_count includes inherited static members, see + // zend_do_inheritance_ex(). + // PHP does not support *dynamic* static properties the same way non-static + // properties can be dynamic (i.e. present on an object without having been + // declared on the class). Thus, default_static_members_count will always + // reflect the count of all static members that a class might have. + if (ce->default_static_members_count == 0) { + RETURN_EMPTY_ARRAY(); + } + + if (!CE_STATIC_MEMBERS(ce)) { zend_class_init_statics(ce); } diff --git a/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt b/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt new file mode 100644 index 000000000000..89d894f83989 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt @@ -0,0 +1,57 @@ +--TEST-- +ReflectionClass::getStaticProperties() +--FILE-- +getStaticProperties()); +var_dump($child->getStaticProperties()); + +echo "\nTyped properties initialized\n:"; +Base::$pubTyped = true; +$base->setStaticPropertyValue('privTyped', true); + +var_dump($base->getStaticProperties()); +var_dump($child->getStaticProperties()); +?> +--EXPECT-- +Start: +array(2) { + ["pub"]=> + NULL + ["priv"]=> + NULL +} +array(1) { + ["pub"]=> + NULL +} + +Typed properties initialized +:array(4) { + ["pub"]=> + NULL + ["priv"]=> + NULL + ["pubTyped"]=> + bool(true) + ["privTyped"]=> + bool(true) +} +array(2) { + ["pub"]=> + NULL + ["pubTyped"]=> + bool(true) +} From dca2c720112041158f5a199c54d44750c59eeee8 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:49:54 -0700 Subject: [PATCH 093/211] Fix inline documentation of tentative return methods Both `ReflectionFunctionAbstract::hasTentativeReturnType()` and `ReflectionFunctionAbstract::getTentativeReturnType()` were documented as checking or retrieving the "return type" of a function rather than the "tentative return type". The two are different; update the documentation accordingly. --- ext/reflection/php_reflection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3abd95731ca3..7a24f084045c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3672,7 +3672,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) } /* }}} */ -/* {{{ Return whether the function has a return type */ +/* {{{ Return whether the function has a tentative return type */ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) { reflection_object *intern; @@ -3686,7 +3686,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) } /* }}} */ -/* {{{ Returns the return type associated with the function */ +/* {{{ Returns the tentative return type associated with the function */ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) { reflection_object *intern; From 4ba865110df53f6d24c3af689c4412c63d166e48 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:51:11 -0700 Subject: [PATCH 094/211] `ReflectionFunctionAbstract::getClosureCalledClass()`: remove pointer cast `called_scope` was declared to be a pointer to a `zend_class_entry`, no cast is needed. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7a24f084045c..b120e8e4eb9a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1847,7 +1847,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) if (Z_OBJ_HANDLER(intern->obj, get_closure) && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS && closure_func && (called_scope || closure_func->common.scope)) { - zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); + zend_reflection_class_factory(called_scope ? called_scope : closure_func->common.scope, return_value); } } } From 9b5f5e7992dbce868e6a183cf99ec860e7bdd6ce Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:07:44 -0700 Subject: [PATCH 095/211] Reflection: remove `reflection_extension_factory()`, avoid extra lookups `reflection_extension_factory()` was used to initialize a `ReflectionExtension` instance based on the name of the extension. It would look for an extension with the given name in the global `module_registry` and, if found, delegate to `reflection_extension_factory_ex()` to do the actual initialization using the located `zend_module_entry` pointer. However, both callers of `reflection_extension_factory()` already had a `zend_module_entry` pointer! Replace both uses of `reflection_extension_factory()` with `reflection_extension_factory_ex()` using the available pointer, and remove the `reflection_extension_factory()` method. In a follow-up commit the _ex variant will be renamed to remove the suffix. --- ext/reflection/php_reflection.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b120e8e4eb9a..a683989a487c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1408,19 +1408,6 @@ static void reflection_extension_factory_ex(zval *object, zend_module_entry *mod } /* }}} */ -/* {{{ reflection_extension_factory */ -static void reflection_extension_factory(zval *object, const char *name_str) -{ - size_t name_len = strlen(name_str); - struct _zend_module_entry *module = zend_hash_str_find_ptr_lc(&module_registry, name_str, name_len); - if (!module) { - return; - } - - reflection_extension_factory_ex(object, module); -} -/* }}} */ - /* {{{ reflection_parameter_factory */ static void reflection_parameter_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) { @@ -2259,7 +2246,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getExtension) internal = (zend_internal_function *)fptr; if (internal->module) { - reflection_extension_factory(return_value, internal->module->name); + reflection_extension_factory_ex(return_value, internal->module); } else { RETURN_NULL(); } @@ -5580,7 +5567,7 @@ ZEND_METHOD(ReflectionClass, getExtension) GET_REFLECTION_OBJECT_PTR(ce); if ((ce->type == ZEND_INTERNAL_CLASS) && ce->info.internal.module) { - reflection_extension_factory(return_value, ce->info.internal.module->name); + reflection_extension_factory_ex(return_value, ce->info.internal.module); } } /* }}} */ From 86b0eaabf09482bff29b175ee01c993fa3fae285 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:10:33 -0700 Subject: [PATCH 096/211] Reflection: rename `reflection_extension_factory_ex()` After the original `reflection_extension_factory()` was removed in the prior commit, the _ex suffix is no longer needed. Remove it. --- ext/reflection/php_reflection.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a683989a487c..4a8db77347e4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1396,8 +1396,8 @@ PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object) } /* }}} */ -/* {{{ reflection_extension_factory_ex */ -static void reflection_extension_factory_ex(zval *object, zend_module_entry *module) +/* {{{ reflection_extension_factory */ +static void reflection_extension_factory(zval *object, zend_module_entry *module) { object_init_ex(object, reflection_extension_ptr); reflection_object *intern = Z_REFLECTION_P(object); @@ -2246,7 +2246,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getExtension) internal = (zend_internal_function *)fptr; if (internal->module) { - reflection_extension_factory_ex(return_value, internal->module); + reflection_extension_factory(return_value, internal->module); } else { RETURN_NULL(); } @@ -5567,7 +5567,7 @@ ZEND_METHOD(ReflectionClass, getExtension) GET_REFLECTION_OBJECT_PTR(ce); if ((ce->type == ZEND_INTERNAL_CLASS) && ce->info.internal.module) { - reflection_extension_factory_ex(return_value, ce->info.internal.module); + reflection_extension_factory(return_value, ce->info.internal.module); } } /* }}} */ @@ -8103,7 +8103,7 @@ static void reflection_constant_find_ext(INTERNAL_FUNCTION_PARAMETERS, bool only if (only_name) { RETURN_STRING(module->name); } - reflection_extension_factory_ex(return_value, module); + reflection_extension_factory(return_value, module); return; } ZEND_HASH_FOREACH_END(); From fc6aaf78ce9c5193f159daac8e43c9af9add3634 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:13:48 -0700 Subject: [PATCH 097/211] Reflection: improve string release in `reflection_property_factory_str()` Use `zend_string_release_ex()` rather than the generic `zend_string_release()` since we know that the string is not marked as persistent (based on the `false` parameter to `zend_string_init()` two lines earlier). --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 4a8db77347e4..54f420b3cb05 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1574,7 +1574,7 @@ static void reflection_property_factory_str(zend_class_entry *ce, const char *na { zend_string *name = zend_string_init(name_str, name_len, false); reflection_property_factory(ce, name, prop, object); - zend_string_release(name); + zend_string_release_ex(name, false); } /* {{{ reflection_class_constant_factory */ From 35f2e885cd3db49a072303e1966f3d64e16d65e3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:36:15 -0700 Subject: [PATCH 098/211] Simplify `ReflectionProperty::getSettableType()` property handling Once the `property_reference` has been dereferenced to identify the `zend_property_info`, no need to do so again since the result was already stored in a variable. --- ext/reflection/php_reflection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 54f420b3cb05..7dc65d09cc24 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6426,7 +6426,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType) const zend_property_info *prop = ref->prop; /* Dynamic property is untyped. */ - if (!ref->prop) { + if (!prop) { RETURN_NULL(); } @@ -6448,10 +6448,10 @@ ZEND_METHOD(ReflectionProperty, getSettableType) } /* Fall back to property type */ - if (!ZEND_TYPE_IS_SET(ref->prop->type)) { + if (!ZEND_TYPE_IS_SET(prop->type)) { RETURN_NULL(); } - reflection_type_factory(ref->prop->type, return_value, true); + reflection_type_factory(prop->type, return_value, true); } /* {{{ Returns whether property has a type */ From e7b1d99ea609ff8358c9b5b47c47512400f7d07a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:38:07 -0700 Subject: [PATCH 099/211] Rename `ReflectionParameter_isDefault.phpt` to reflect actual contents This is testing `ReflectionProperty::isDefault()`, `ReflectionParameter::isDefault()` does not exist. --- ...rameter_isDefault.phpt => ReflectionProperty_isDefault.phpt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ext/reflection/tests/{ReflectionParameter_isDefault.phpt => ReflectionProperty_isDefault.phpt} (94%) diff --git a/ext/reflection/tests/ReflectionParameter_isDefault.phpt b/ext/reflection/tests/ReflectionProperty_isDefault.phpt similarity index 94% rename from ext/reflection/tests/ReflectionParameter_isDefault.phpt rename to ext/reflection/tests/ReflectionProperty_isDefault.phpt index 3ab0d980fd04..ebb55a6a0d94 100644 --- a/ext/reflection/tests/ReflectionParameter_isDefault.phpt +++ b/ext/reflection/tests/ReflectionProperty_isDefault.phpt @@ -1,5 +1,5 @@ --TEST-- -ReflectionParameter::isDefault() +ReflectionProperty::isDefault() --FILE-- Date: Thu, 2 Jul 2026 14:40:29 -0700 Subject: [PATCH 100/211] Update `ReflectionMethod_getClosureThis.phpt` to match actual contents The file name was correct, but the test name mentioned `Reflection::getClosureThis()` (which does not exist). Update the test name to say `ReflectionMethod::getClosureThis()`. --- ext/reflection/tests/ReflectionMethod_getClosureThis.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt b/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt index 656fde814d93..c9237e296427 100644 --- a/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt +++ b/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt @@ -1,5 +1,5 @@ --TEST-- -Reflection::getClosureThis() +ReflectionMethod::getClosureThis() --FILE-- Date: Thu, 2 Jul 2026 14:48:05 -0700 Subject: [PATCH 101/211] Fix inline comment in `ReflectionMethod::isConstructor()` "we we looking at" was presumably meant to be "we are looking at". --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7dc65d09cc24..bcbbf65b110e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3699,7 +3699,7 @@ ZEND_METHOD(ReflectionMethod, isConstructor) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(mptr); - /* we need to check if the ctor is the ctor of the class level we we + /* we need to check if the ctor is the ctor of the class level we are * looking at since we might be looking at an inherited old style ctor * defined in base class. */ RETURN_BOOL((mptr->common.fn_flags & ZEND_ACC_CTOR) && intern->ce->constructor && intern->ce->constructor->common.scope == mptr->common.scope); From 22d2e4186eb45c3d0d463bd9141cf7655cb60b37 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:06:55 -0700 Subject: [PATCH 102/211] Reflection: minor whitespace cleanup Consistently indent with tabs, have a space after the `while` keyword, and adjust the lines of a few braces. --- ext/reflection/php_reflection.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bcbbf65b110e..93c14e0ec72b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1128,7 +1128,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module) / smart_str_appends(str, "\n - Dependencies {\n"); - while(dep->name) { + while (dep->name) { smart_str_append_printf(str, " Dependency [ %s (", dep->name); switch(dep->type) { @@ -3751,13 +3751,13 @@ ZEND_METHOD(ReflectionMethod, getDeclaringClass) /* {{{ Returns whether a method has a prototype or not */ ZEND_METHOD(ReflectionMethod, hasPrototype) { - reflection_object *intern; - zend_function *mptr; + reflection_object *intern; + zend_function *mptr; - ZEND_PARSE_PARAMETERS_NONE(); + ZEND_PARSE_PARAMETERS_NONE(); - GET_REFLECTION_OBJECT_PTR(mptr); - RETURN_BOOL(mptr->common.prototype != NULL); + GET_REFLECTION_OBJECT_PTR(mptr); + RETURN_BOOL(mptr->common.prototype != NULL); } /* }}} */ @@ -7099,13 +7099,12 @@ ZEND_METHOD(ReflectionExtension, getDependencies) dep = module->deps; - if (!dep) - { + if (!dep) { RETURN_EMPTY_ARRAY(); } array_init(return_value); - while(dep->name) { + while (dep->name) { zend_string *relation; char *rel_type; size_t len = 0; @@ -7933,9 +7932,7 @@ static zval *_reflection_write_property(zend_object *object, zend_string *name, zend_throw_exception_ex(reflection_exception_ptr, 0, "Cannot set read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); return &EG(uninitialized_zval); - } - else - { + } else { return zend_std_write_property(object, name, value, cache_slot); } } From f6a4702d89bef2063cfc93297bed719e27b4e9cf Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:08:49 -0700 Subject: [PATCH 103/211] `ReflectionAttribute::getArguments()`: optimize array initialization For attributes without arguments, avoid allocating an array that will be empty. --- ext/reflection/php_reflection.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 93c14e0ec72b..67111157fb1d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7504,6 +7504,10 @@ ZEND_METHOD(ReflectionAttribute, getArguments) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(attr); + if (attr->data->argc == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); for (i = 0; i < attr->data->argc; i++) { From 85f2424398d0c886f539e112a80d5eb561cad01e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:14:21 -0700 Subject: [PATCH 104/211] `ReflectionClass::get(Reflection)Constants()`: optimize array initialization In both `ReflectionClass::getConstants()` and `ReflectionClass::getReflectionConstants()`, for classes without constants, avoid allocating an array that will be empty. --- ext/reflection/php_reflection.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 67111157fb1d..9fd7b778c349 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4772,8 +4772,13 @@ ZEND_METHOD(ReflectionClass, getConstants) GET_REFLECTION_OBJECT_PTR(ce); + const HashTable *constants_table = CE_CONSTANTS_TABLE(ce); + if (zend_hash_num_elements(constants_table) == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, key, constant) { if (UNEXPECTED(Z_TYPE(constant->value) == IS_CONSTANT_AST && zend_update_class_constant(constant, key, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4806,8 +4811,13 @@ ZEND_METHOD(ReflectionClass, getReflectionConstants) GET_REFLECTION_OBJECT_PTR(ce); + const HashTable *constants_table = CE_CONSTANTS_TABLE(ce); + if (zend_hash_num_elements(constants_table) == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), name, constant) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, name, constant) { if (ZEND_CLASS_CONST_FLAGS(constant) & filter) { zval class_const; reflection_class_constant_factory(name, constant, &class_const); From 2e2b03e1ceeef4cf8ba2a5e863ccc2622311dac6 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:15:16 -0700 Subject: [PATCH 105/211] `ReflectionClass::isSubclassOf()`: remove extraneous parentheses --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9fd7b778c349..f04bd020b770 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5504,7 +5504,7 @@ ZEND_METHOD(ReflectionClass, isSubclassOf) GET_REFLECTION_OBJECT_PTR(ce); - RETURN_BOOL((ce != class_ce && instanceof_function(ce, class_ce))); + RETURN_BOOL(ce != class_ce && instanceof_function(ce, class_ce)); } /* }}} */ From 9390b68d5d68c03bbfddf5ae7b48a28751b1b62b Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Tue, 7 Jul 2026 15:35:43 -0300 Subject: [PATCH 106/211] Change bless to clean up absolute paths (#22067) User-specific absolute paths should be avoided in tests, since they aren't portable. Change bless to detect common places where this occurs and make it use %s in EXPECTF if so. This was originally developed as part of GH-12276. While the approved RFC doesn't enable function parameters to be printed for tests, the functionality is generally useful. --- scripts/dev/bless_tests.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/dev/bless_tests.php b/scripts/dev/bless_tests.php index 7493a504729c..b38935503ae3 100755 --- a/scripts/dev/bless_tests.php +++ b/scripts/dev/bless_tests.php @@ -74,6 +74,10 @@ function normalizeOutput(string $out): string { 'Resource ID#%d used as offset, casting to integer (%d)', $out); $out = preg_replace('/string\(\d+\) "([^"]*%d)/', 'string(%d) "$1', $out); + // Inside of strings, replace absolute paths that have been truncated with + // any string. These tend to contain homedirs with usernames, not good. + $out = preg_replace("/'(\/|[A-Z]:\\\\)\S+\\.\\.\\.'/", "'%s'", $out); + $out = preg_replace("/'file:(\/|[A-Z]:\\\\)\S+\\.\\.\\.'/", "'%s'", $out); $out = str_replace("\0", '%0', $out); return $out; } From 8ae6a8f1d688e638494a559a634fd9568226977a Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 3 Jul 2026 09:17:02 +0200 Subject: [PATCH 107/211] Use PHP version for zip extension --- ext/zip/php_zip.c | 1 - ext/zip/php_zip.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 9bc801455ed6..4156a8deb861 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -3209,7 +3209,6 @@ static PHP_MINFO_FUNCTION(zip) php_info_print_table_start(); php_info_print_table_row(2, "Zip", "enabled"); - php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION); #ifdef HAVE_LIBZIP_VERSION if (strcmp(LIBZIP_VERSION, zip_libzip_version())) { php_info_print_table_row(2, "Libzip headers version", LIBZIP_VERSION); diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 408c29708231..e734c4628f02 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -37,7 +37,8 @@ extern zend_module_entry zip_module_entry; /* Additional flags not from libzip */ #define ZIP_FL_OPEN_FILE_NOW (1u<<30) -#define PHP_ZIP_VERSION "1.22.8" +#include "php_version.h" +#define PHP_ZIP_VERSION PHP_VERSION #ifdef HAVE_LIBZIP_VERSION #define LIBZIP_VERSION_STR zip_libzip_version() From ee0f2b9e511185c79c6b062efe30eb47af0dbbb9 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 8 Jul 2026 08:57:05 +0200 Subject: [PATCH 108/211] [ci skip] add CVE ref --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 787589fc6dfc..12ae8c0fb23d 100644 --- a/NEWS +++ b/NEWS @@ -7,7 +7,7 @@ PHP NEWS - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in - openssl_encrypt with AES-WRAP-PAD). (David Carlier) + openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier) 07 May 2026, PHP 8.2.31 From 427089cd9423daecfbe478bc5cdacc394fec8eb7 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 8 Jul 2026 08:58:36 +0200 Subject: [PATCH 109/211] [ci skip] add CVE ref --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 348d062e83a4..3bebd2660a2a 100644 --- a/NEWS +++ b/NEWS @@ -11,7 +11,7 @@ PHP NEWS - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in - openssl_encrypt with AES-WRAP-PAD). (David Carlier) + openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier) 07 May 2026, PHP 8.3.31 From 5ec033c497e3d08c0718d0d16b595a40117faaa5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 8 Jul 2026 09:00:43 +0200 Subject: [PATCH 110/211] [ci skip] add CVE ref --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 5f615701b4ab..cdd6a48a4874 100644 --- a/NEWS +++ b/NEWS @@ -137,7 +137,7 @@ PHP NEWS - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in - openssl_encrypt with AES-WRAP-PAD). (David Carlier) + openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier) - Phar: . Fixed a bypass of the magic ".phar" directory protection in @@ -311,7 +311,7 @@ PHP NEWS - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL - and a proxy set). (ndossche) + and a proxy set). (CVE-2026-12184) (ndossche) - XSL: . Fixed bug GH-21600 (Segfault on module shutdown). (David Carlier) From 735748f5d72cb835c46f785eee2fc32fe5a1dccf Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 8 Jul 2026 09:02:00 +0200 Subject: [PATCH 111/211] [ci skip] add CVE ref --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 2f00f144fbb8..e6178c04c988 100644 --- a/NEWS +++ b/NEWS @@ -140,7 +140,7 @@ PHP NEWS - OpenSSL: . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in - openssl_encrypt with AES-WRAP-PAD). (David Carlier) + openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier) - Phar: . Fixed a bypass of the magic ".phar" directory protection in @@ -327,7 +327,7 @@ PHP NEWS - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL - and a proxy set). (ndossche) + and a proxy set). (CVE-2026-12184) (ndossche) - URI: . Fixed CVE-2026-42371 (uriparser before 1.0.1 has numeric truncation in From edcd94d5816ac072f34cd3a93f59905575bbfe39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Wed, 8 Jul 2026 11:27:30 +0200 Subject: [PATCH 112/211] ext/intl: don't include C++ ICU headers from C translation units (#22636) php_intl.c is compiled as C but transitively includes formatter_data.h and listformatter_class.h, which (since 8.6) include and unconditionally. On ICU >= 64 those C++ headers are a no-op in a C compile, but ICU 63 (Debian Buster) has an unguarded "#include " at the top of unistr.h, so the C compile fails with "fatal error: cstddef: No such file or directory". The C path only uses the "typedef void" shim (NumberFormat*/ListFormatter* are just void*), so it needs no ICU header at all. Move the C++ include inside the existing #ifdef __cplusplus guard. No-op on newer ICU. --- ext/intl/formatter/formatter_data.h | 3 +-- ext/intl/listformatter/listformatter_class.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/intl/formatter/formatter_data.h b/ext/intl/formatter/formatter_data.h index 1208970471bf..46a82cb9bd0f 100644 --- a/ext/intl/formatter/formatter_data.h +++ b/ext/intl/formatter/formatter_data.h @@ -23,9 +23,8 @@ extern "C" { } #endif -#include - #ifdef __cplusplus +#include using icu::NumberFormat; #else typedef void NumberFormat; diff --git a/ext/intl/listformatter/listformatter_class.h b/ext/intl/listformatter/listformatter_class.h index 7e7fd29207fc..bdf21b6886e0 100644 --- a/ext/intl/listformatter/listformatter_class.h +++ b/ext/intl/listformatter/listformatter_class.h @@ -21,9 +21,8 @@ #include "intl_error.h" #include "intl_data.h" -#include - #ifdef __cplusplus +#include using icu::ListFormatter; #else typedef void ListFormatter; From ff480bce5c44e3bfe62bec6a689d62d5cdc20793 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 8 Jul 2026 20:10:26 +0800 Subject: [PATCH 113/211] ext/dom: Fix Dom\DtdNamedNodeMap index overflow in dimension access (#22630) Dom\DtdNamedNodeMap dimension access could overflow the index when reading DTD entities or notations. This commit fix it. --- NEWS | 3 ++ ext/dom/dom_iterators.c | 12 +++++- ext/dom/php_dom.h | 2 +- ...edNodeMap_dimension_index_overflow_64.phpt | 41 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt diff --git a/NEWS b/NEWS index 8971e7d317a3..cd396479e3e2 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,9 @@ PHP NEWS - DOM: . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested Dom\XMLDocument). (iliaal) + . Fixed Dom\DtdNamedNodeMap integer dimension access so negative indexes + return NULL and indexes outside the int range throw ValueError instead of + returning the first entity or notation. (Weilin Du) - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index f97a9ec825d5..7566dd8b61a8 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -79,14 +79,22 @@ void dom_free_notation(xmlEntityPtr entity) /* {{{ */ } /* }}} */ -xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, int index) +xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, zend_long index) { int htsize; + if (index < 0) { + return NULL; + } + if (UNEXPECTED(ZEND_LONG_INT_OVFL(index))) { + zend_value_error("must be between 0 and %d", INT_MAX); + return NULL; + } + if ((htsize = xmlHashSize(ht)) > 0 && index < htsize) { nodeIterator iter; iter.cur = 0; - iter.index = index; + iter.index = (int) index; iter.node = NULL; xmlHashScan(ht, itemHashScanner, &iter); return iter.node; diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 1b19aeee3baa..6f4b5075b659 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -127,7 +127,7 @@ bool dom_node_is_read_only(const xmlNode *node); bool dom_node_children_valid(const xmlNode *node); xmlNodePtr create_notation(xmlDtdPtr parent_dtd, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID); void dom_free_notation(xmlEntityPtr entity); -xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index); +xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, zend_long index); zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref); void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce); xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern); diff --git a/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt b/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt new file mode 100644 index 000000000000..cc2d7a06a2ca --- /dev/null +++ b/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt @@ -0,0 +1,41 @@ +--TEST-- +Dom\DtdNamedNodeMap dimension access handles invalid integer indexes +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- + + +]> + +XML); + +$overflow = 4294967296; + +function dump_access(Closure $callback): void { + try { + var_dump($callback()?->nodeName); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } +} + +dump_access(fn() => $doc->doctype->entities[-1]); +dump_access(fn() => $doc->doctype->entities[$overflow]); + +dump_access(fn() => $doc->doctype->notations[-1]); +dump_access(fn() => $doc->doctype->notations[$overflow]); +?> +--EXPECT-- +NULL +must be between 0 and 2147483647 +NULL +must be between 0 and 2147483647 From c993aa64f260545ee4eef0173e260cb02d41d0d2 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 8 Jul 2026 14:33:07 +0100 Subject: [PATCH 114/211] Zend: add test for <=> comparing against different types (#22634) --- .../comparison/spaceship_type_compare.phpt | 536 ++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 Zend/tests/comparison/spaceship_type_compare.phpt diff --git a/Zend/tests/comparison/spaceship_type_compare.phpt b/Zend/tests/comparison/spaceship_type_compare.phpt new file mode 100644 index 000000000000..7382a593cc84 --- /dev/null +++ b/Zend/tests/comparison/spaceship_type_compare.phpt @@ -0,0 +1,536 @@ +--TEST-- +Compare types via <=> operator +--FILE-- + $r) { + 0 => "EQUAL", + 1 => match ($r <=> $l) { + -1 => "GREATER THAN", + 1 => "UNCOMPARABLE", + }, + -1 => "LESS THAN", + }; +} + +class test { +} + +$values = [ + 'null' => null, + 'false' => false, + 'true' => true, + 'i-5' => -5, + 'i0' => 0, + 'i10' => 10, + 'f-5' => -5.0, + 'f0' => 0.0, + 'f2.5' => 2.5, + 'f10' => 10.0, + 's""' => "", + 's"str"' => "str", + 's"-5"' => "-5", + 's"0"' => "0", + 's"2.5"' => "2.5", + 's"10"' => "10", + 'a[]' => [], + 'a[1, 2, 3]' => [1, 2, 3], + 'c"stdClass"' => new stdClass(), + 'c"test"' => new test(), + 'r"STDOUT"' => STDOUT, + 'r"STDERR"' => STDERR, +]; + +foreach($values as $l_name => $l) { + foreach($values as $r_name => $r) { + echo $l_name, ' ', cmp($l, $r), ' ', $r_name, "\n"; + } +} + +?> +--EXPECT-- +null EQUAL null +null EQUAL false +null LESS THAN true +null LESS THAN i-5 +null EQUAL i0 +null LESS THAN i10 +null LESS THAN f-5 +null EQUAL f0 +null LESS THAN f2.5 +null LESS THAN f10 +null EQUAL s"" +null LESS THAN s"str" +null LESS THAN s"-5" +null LESS THAN s"0" +null LESS THAN s"2.5" +null LESS THAN s"10" +null EQUAL a[] +null LESS THAN a[1, 2, 3] +null LESS THAN c"stdClass" +null LESS THAN c"test" +null LESS THAN r"STDOUT" +null LESS THAN r"STDERR" +false EQUAL null +false EQUAL false +false LESS THAN true +false LESS THAN i-5 +false EQUAL i0 +false LESS THAN i10 +false LESS THAN f-5 +false EQUAL f0 +false LESS THAN f2.5 +false LESS THAN f10 +false EQUAL s"" +false LESS THAN s"str" +false LESS THAN s"-5" +false EQUAL s"0" +false LESS THAN s"2.5" +false LESS THAN s"10" +false EQUAL a[] +false LESS THAN a[1, 2, 3] +false LESS THAN c"stdClass" +false LESS THAN c"test" +false LESS THAN r"STDOUT" +false LESS THAN r"STDERR" +true GREATER THAN null +true GREATER THAN false +true EQUAL true +true EQUAL i-5 +true GREATER THAN i0 +true EQUAL i10 +true EQUAL f-5 +true GREATER THAN f0 +true EQUAL f2.5 +true EQUAL f10 +true GREATER THAN s"" +true EQUAL s"str" +true EQUAL s"-5" +true GREATER THAN s"0" +true EQUAL s"2.5" +true EQUAL s"10" +true GREATER THAN a[] +true EQUAL a[1, 2, 3] +true EQUAL c"stdClass" +true EQUAL c"test" +true EQUAL r"STDOUT" +true EQUAL r"STDERR" +i-5 GREATER THAN null +i-5 GREATER THAN false +i-5 EQUAL true +i-5 EQUAL i-5 +i-5 LESS THAN i0 +i-5 LESS THAN i10 +i-5 EQUAL f-5 +i-5 LESS THAN f0 +i-5 LESS THAN f2.5 +i-5 LESS THAN f10 +i-5 GREATER THAN s"" +i-5 LESS THAN s"str" +i-5 EQUAL s"-5" +i-5 LESS THAN s"0" +i-5 LESS THAN s"2.5" +i-5 LESS THAN s"10" +i-5 LESS THAN a[] +i-5 LESS THAN a[1, 2, 3] +i-5 LESS THAN c"stdClass" +i-5 LESS THAN c"test" +i-5 LESS THAN r"STDOUT" +i-5 LESS THAN r"STDERR" +i0 EQUAL null +i0 EQUAL false +i0 LESS THAN true +i0 GREATER THAN i-5 +i0 EQUAL i0 +i0 LESS THAN i10 +i0 GREATER THAN f-5 +i0 EQUAL f0 +i0 LESS THAN f2.5 +i0 LESS THAN f10 +i0 GREATER THAN s"" +i0 LESS THAN s"str" +i0 GREATER THAN s"-5" +i0 EQUAL s"0" +i0 LESS THAN s"2.5" +i0 LESS THAN s"10" +i0 LESS THAN a[] +i0 LESS THAN a[1, 2, 3] +i0 LESS THAN c"stdClass" +i0 LESS THAN c"test" +i0 LESS THAN r"STDOUT" +i0 LESS THAN r"STDERR" +i10 GREATER THAN null +i10 GREATER THAN false +i10 EQUAL true +i10 GREATER THAN i-5 +i10 GREATER THAN i0 +i10 EQUAL i10 +i10 GREATER THAN f-5 +i10 GREATER THAN f0 +i10 GREATER THAN f2.5 +i10 EQUAL f10 +i10 GREATER THAN s"" +i10 LESS THAN s"str" +i10 GREATER THAN s"-5" +i10 GREATER THAN s"0" +i10 GREATER THAN s"2.5" +i10 EQUAL s"10" +i10 LESS THAN a[] +i10 LESS THAN a[1, 2, 3] +i10 GREATER THAN c"stdClass" +i10 GREATER THAN c"test" +i10 GREATER THAN r"STDOUT" +i10 GREATER THAN r"STDERR" +f-5 GREATER THAN null +f-5 GREATER THAN false +f-5 EQUAL true +f-5 EQUAL i-5 +f-5 LESS THAN i0 +f-5 LESS THAN i10 +f-5 EQUAL f-5 +f-5 LESS THAN f0 +f-5 LESS THAN f2.5 +f-5 LESS THAN f10 +f-5 GREATER THAN s"" +f-5 LESS THAN s"str" +f-5 EQUAL s"-5" +f-5 LESS THAN s"0" +f-5 LESS THAN s"2.5" +f-5 LESS THAN s"10" +f-5 LESS THAN a[] +f-5 LESS THAN a[1, 2, 3] +f-5 LESS THAN c"stdClass" +f-5 LESS THAN c"test" +f-5 LESS THAN r"STDOUT" +f-5 LESS THAN r"STDERR" +f0 EQUAL null +f0 EQUAL false +f0 LESS THAN true +f0 GREATER THAN i-5 +f0 EQUAL i0 +f0 LESS THAN i10 +f0 GREATER THAN f-5 +f0 EQUAL f0 +f0 LESS THAN f2.5 +f0 LESS THAN f10 +f0 GREATER THAN s"" +f0 LESS THAN s"str" +f0 GREATER THAN s"-5" +f0 EQUAL s"0" +f0 LESS THAN s"2.5" +f0 LESS THAN s"10" +f0 LESS THAN a[] +f0 LESS THAN a[1, 2, 3] +f0 LESS THAN c"stdClass" +f0 LESS THAN c"test" +f0 LESS THAN r"STDOUT" +f0 LESS THAN r"STDERR" +f2.5 GREATER THAN null +f2.5 GREATER THAN false +f2.5 EQUAL true +f2.5 GREATER THAN i-5 +f2.5 GREATER THAN i0 +f2.5 LESS THAN i10 +f2.5 GREATER THAN f-5 +f2.5 GREATER THAN f0 +f2.5 EQUAL f2.5 +f2.5 LESS THAN f10 +f2.5 GREATER THAN s"" +f2.5 LESS THAN s"str" +f2.5 GREATER THAN s"-5" +f2.5 GREATER THAN s"0" +f2.5 EQUAL s"2.5" +f2.5 LESS THAN s"10" +f2.5 LESS THAN a[] +f2.5 LESS THAN a[1, 2, 3] +f2.5 GREATER THAN c"stdClass" +f2.5 GREATER THAN c"test" +f2.5 GREATER THAN r"STDOUT" +f2.5 LESS THAN r"STDERR" +f10 GREATER THAN null +f10 GREATER THAN false +f10 EQUAL true +f10 GREATER THAN i-5 +f10 GREATER THAN i0 +f10 EQUAL i10 +f10 GREATER THAN f-5 +f10 GREATER THAN f0 +f10 GREATER THAN f2.5 +f10 EQUAL f10 +f10 GREATER THAN s"" +f10 LESS THAN s"str" +f10 GREATER THAN s"-5" +f10 GREATER THAN s"0" +f10 GREATER THAN s"2.5" +f10 EQUAL s"10" +f10 LESS THAN a[] +f10 LESS THAN a[1, 2, 3] +f10 GREATER THAN c"stdClass" +f10 GREATER THAN c"test" +f10 GREATER THAN r"STDOUT" +f10 GREATER THAN r"STDERR" +s"" EQUAL null +s"" EQUAL false +s"" LESS THAN true +s"" LESS THAN i-5 +s"" LESS THAN i0 +s"" LESS THAN i10 +s"" LESS THAN f-5 +s"" LESS THAN f0 +s"" LESS THAN f2.5 +s"" LESS THAN f10 +s"" EQUAL s"" +s"" LESS THAN s"str" +s"" LESS THAN s"-5" +s"" LESS THAN s"0" +s"" LESS THAN s"2.5" +s"" LESS THAN s"10" +s"" LESS THAN a[] +s"" LESS THAN a[1, 2, 3] +s"" LESS THAN c"stdClass" +s"" LESS THAN c"test" +s"" LESS THAN r"STDOUT" +s"" LESS THAN r"STDERR" +s"str" GREATER THAN null +s"str" GREATER THAN false +s"str" EQUAL true +s"str" GREATER THAN i-5 +s"str" GREATER THAN i0 +s"str" GREATER THAN i10 +s"str" GREATER THAN f-5 +s"str" GREATER THAN f0 +s"str" GREATER THAN f2.5 +s"str" GREATER THAN f10 +s"str" GREATER THAN s"" +s"str" EQUAL s"str" +s"str" GREATER THAN s"-5" +s"str" GREATER THAN s"0" +s"str" GREATER THAN s"2.5" +s"str" GREATER THAN s"10" +s"str" LESS THAN a[] +s"str" LESS THAN a[1, 2, 3] +s"str" LESS THAN c"stdClass" +s"str" LESS THAN c"test" +s"str" LESS THAN r"STDOUT" +s"str" LESS THAN r"STDERR" +s"-5" GREATER THAN null +s"-5" GREATER THAN false +s"-5" EQUAL true +s"-5" EQUAL i-5 +s"-5" LESS THAN i0 +s"-5" LESS THAN i10 +s"-5" EQUAL f-5 +s"-5" LESS THAN f0 +s"-5" LESS THAN f2.5 +s"-5" LESS THAN f10 +s"-5" GREATER THAN s"" +s"-5" LESS THAN s"str" +s"-5" EQUAL s"-5" +s"-5" LESS THAN s"0" +s"-5" LESS THAN s"2.5" +s"-5" LESS THAN s"10" +s"-5" LESS THAN a[] +s"-5" LESS THAN a[1, 2, 3] +s"-5" LESS THAN c"stdClass" +s"-5" LESS THAN c"test" +s"-5" LESS THAN r"STDOUT" +s"-5" LESS THAN r"STDERR" +s"0" GREATER THAN null +s"0" EQUAL false +s"0" LESS THAN true +s"0" GREATER THAN i-5 +s"0" EQUAL i0 +s"0" LESS THAN i10 +s"0" GREATER THAN f-5 +s"0" EQUAL f0 +s"0" LESS THAN f2.5 +s"0" LESS THAN f10 +s"0" GREATER THAN s"" +s"0" LESS THAN s"str" +s"0" GREATER THAN s"-5" +s"0" EQUAL s"0" +s"0" LESS THAN s"2.5" +s"0" LESS THAN s"10" +s"0" LESS THAN a[] +s"0" LESS THAN a[1, 2, 3] +s"0" LESS THAN c"stdClass" +s"0" LESS THAN c"test" +s"0" LESS THAN r"STDOUT" +s"0" LESS THAN r"STDERR" +s"2.5" GREATER THAN null +s"2.5" GREATER THAN false +s"2.5" EQUAL true +s"2.5" GREATER THAN i-5 +s"2.5" GREATER THAN i0 +s"2.5" LESS THAN i10 +s"2.5" GREATER THAN f-5 +s"2.5" GREATER THAN f0 +s"2.5" EQUAL f2.5 +s"2.5" LESS THAN f10 +s"2.5" GREATER THAN s"" +s"2.5" LESS THAN s"str" +s"2.5" GREATER THAN s"-5" +s"2.5" GREATER THAN s"0" +s"2.5" EQUAL s"2.5" +s"2.5" LESS THAN s"10" +s"2.5" LESS THAN a[] +s"2.5" LESS THAN a[1, 2, 3] +s"2.5" LESS THAN c"stdClass" +s"2.5" LESS THAN c"test" +s"2.5" GREATER THAN r"STDOUT" +s"2.5" LESS THAN r"STDERR" +s"10" GREATER THAN null +s"10" GREATER THAN false +s"10" EQUAL true +s"10" GREATER THAN i-5 +s"10" GREATER THAN i0 +s"10" EQUAL i10 +s"10" GREATER THAN f-5 +s"10" GREATER THAN f0 +s"10" GREATER THAN f2.5 +s"10" EQUAL f10 +s"10" GREATER THAN s"" +s"10" LESS THAN s"str" +s"10" GREATER THAN s"-5" +s"10" GREATER THAN s"0" +s"10" GREATER THAN s"2.5" +s"10" EQUAL s"10" +s"10" LESS THAN a[] +s"10" LESS THAN a[1, 2, 3] +s"10" LESS THAN c"stdClass" +s"10" LESS THAN c"test" +s"10" GREATER THAN r"STDOUT" +s"10" GREATER THAN r"STDERR" +a[] EQUAL null +a[] EQUAL false +a[] LESS THAN true +a[] GREATER THAN i-5 +a[] GREATER THAN i0 +a[] GREATER THAN i10 +a[] GREATER THAN f-5 +a[] GREATER THAN f0 +a[] GREATER THAN f2.5 +a[] GREATER THAN f10 +a[] GREATER THAN s"" +a[] GREATER THAN s"str" +a[] GREATER THAN s"-5" +a[] GREATER THAN s"0" +a[] GREATER THAN s"2.5" +a[] GREATER THAN s"10" +a[] EQUAL a[] +a[] LESS THAN a[1, 2, 3] +a[] LESS THAN c"stdClass" +a[] LESS THAN c"test" +a[] GREATER THAN r"STDOUT" +a[] GREATER THAN r"STDERR" +a[1, 2, 3] GREATER THAN null +a[1, 2, 3] GREATER THAN false +a[1, 2, 3] EQUAL true +a[1, 2, 3] GREATER THAN i-5 +a[1, 2, 3] GREATER THAN i0 +a[1, 2, 3] GREATER THAN i10 +a[1, 2, 3] GREATER THAN f-5 +a[1, 2, 3] GREATER THAN f0 +a[1, 2, 3] GREATER THAN f2.5 +a[1, 2, 3] GREATER THAN f10 +a[1, 2, 3] GREATER THAN s"" +a[1, 2, 3] GREATER THAN s"str" +a[1, 2, 3] GREATER THAN s"-5" +a[1, 2, 3] GREATER THAN s"0" +a[1, 2, 3] GREATER THAN s"2.5" +a[1, 2, 3] GREATER THAN s"10" +a[1, 2, 3] GREATER THAN a[] +a[1, 2, 3] EQUAL a[1, 2, 3] +a[1, 2, 3] LESS THAN c"stdClass" +a[1, 2, 3] LESS THAN c"test" +a[1, 2, 3] GREATER THAN r"STDOUT" +a[1, 2, 3] GREATER THAN r"STDERR" +c"stdClass" GREATER THAN null +c"stdClass" GREATER THAN false +c"stdClass" EQUAL true +c"stdClass" GREATER THAN i-5 +c"stdClass" GREATER THAN i0 +c"stdClass" LESS THAN i10 +c"stdClass" GREATER THAN f-5 +c"stdClass" GREATER THAN f0 +c"stdClass" LESS THAN f2.5 +c"stdClass" LESS THAN f10 +c"stdClass" GREATER THAN s"" +c"stdClass" GREATER THAN s"str" +c"stdClass" GREATER THAN s"-5" +c"stdClass" GREATER THAN s"0" +c"stdClass" GREATER THAN s"2.5" +c"stdClass" GREATER THAN s"10" +c"stdClass" GREATER THAN a[] +c"stdClass" GREATER THAN a[1, 2, 3] +c"stdClass" EQUAL c"stdClass" +c"stdClass" UNCOMPARABLE c"test" +c"stdClass" GREATER THAN r"STDOUT" +c"stdClass" GREATER THAN r"STDERR" +c"test" GREATER THAN null +c"test" GREATER THAN false +c"test" EQUAL true +c"test" GREATER THAN i-5 +c"test" GREATER THAN i0 +c"test" LESS THAN i10 +c"test" GREATER THAN f-5 +c"test" GREATER THAN f0 +c"test" LESS THAN f2.5 +c"test" LESS THAN f10 +c"test" GREATER THAN s"" +c"test" GREATER THAN s"str" +c"test" GREATER THAN s"-5" +c"test" GREATER THAN s"0" +c"test" GREATER THAN s"2.5" +c"test" GREATER THAN s"10" +c"test" GREATER THAN a[] +c"test" GREATER THAN a[1, 2, 3] +c"test" UNCOMPARABLE c"stdClass" +c"test" EQUAL c"test" +c"test" GREATER THAN r"STDOUT" +c"test" GREATER THAN r"STDERR" +r"STDOUT" GREATER THAN null +r"STDOUT" GREATER THAN false +r"STDOUT" EQUAL true +r"STDOUT" GREATER THAN i-5 +r"STDOUT" GREATER THAN i0 +r"STDOUT" LESS THAN i10 +r"STDOUT" GREATER THAN f-5 +r"STDOUT" GREATER THAN f0 +r"STDOUT" LESS THAN f2.5 +r"STDOUT" LESS THAN f10 +r"STDOUT" GREATER THAN s"" +r"STDOUT" GREATER THAN s"str" +r"STDOUT" GREATER THAN s"-5" +r"STDOUT" GREATER THAN s"0" +r"STDOUT" LESS THAN s"2.5" +r"STDOUT" LESS THAN s"10" +r"STDOUT" LESS THAN a[] +r"STDOUT" LESS THAN a[1, 2, 3] +r"STDOUT" LESS THAN c"stdClass" +r"STDOUT" LESS THAN c"test" +r"STDOUT" EQUAL r"STDOUT" +r"STDOUT" LESS THAN r"STDERR" +r"STDERR" GREATER THAN null +r"STDERR" GREATER THAN false +r"STDERR" EQUAL true +r"STDERR" GREATER THAN i-5 +r"STDERR" GREATER THAN i0 +r"STDERR" LESS THAN i10 +r"STDERR" GREATER THAN f-5 +r"STDERR" GREATER THAN f0 +r"STDERR" GREATER THAN f2.5 +r"STDERR" LESS THAN f10 +r"STDERR" GREATER THAN s"" +r"STDERR" GREATER THAN s"str" +r"STDERR" GREATER THAN s"-5" +r"STDERR" GREATER THAN s"0" +r"STDERR" GREATER THAN s"2.5" +r"STDERR" LESS THAN s"10" +r"STDERR" LESS THAN a[] +r"STDERR" LESS THAN a[1, 2, 3] +r"STDERR" LESS THAN c"stdClass" +r"STDERR" LESS THAN c"test" +r"STDERR" GREATER THAN r"STDOUT" +r"STDERR" EQUAL r"STDERR" From 35881e12982933fd7eaea30da10994e91c84e1e5 Mon Sep 17 00:00:00 2001 From: Vazha Aptsiauri <37383700+Afcyy@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:10:01 +0400 Subject: [PATCH 115/211] sapi/cli: support HTTP QUERY method in built-in server (#22615) Co-authored-by: Vazha Aptsiauri --- UPGRADING | 4 ++ sapi/cli/php_http_parser.c | 2 + sapi/cli/php_http_parser.h | 1 + .../tests/php_cli_server_query_method.phpt | 40 +++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 sapi/cli/tests/php_cli_server_query_method.phpt diff --git a/UPGRADING b/UPGRADING index b9c659ccc405..6a1f1b12e4a8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -297,6 +297,10 @@ PHP 8.6 UPGRADE NOTES 3. Changes in SAPI modules ======================================== +- CLI: + . The built-in development server now accepts requests using the QUERY HTTP + method instead of returning 501 Not Implemented. + ======================================== 4. Deprecated Functionality ======================================== diff --git a/sapi/cli/php_http_parser.c b/sapi/cli/php_http_parser.c index c7c2ad0caaea..ac3c19d5d963 100644 --- a/sapi/cli/php_http_parser.c +++ b/sapi/cli/php_http_parser.c @@ -82,6 +82,7 @@ static const char *method_strings[] = , "POST" , "PUT" , "PATCH" + , "QUERY" , "CONNECT" , "OPTIONS" , "TRACE" @@ -521,6 +522,7 @@ size_t php_http_parser_execute (php_http_parser *parser, case 'N': parser->method = PHP_HTTP_NOTIFY; break; case 'O': parser->method = PHP_HTTP_OPTIONS; break; case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break; + case 'Q': parser->method = PHP_HTTP_QUERY; break; case 'R': parser->method = PHP_HTTP_REPORT; break; case 'S': parser->method = PHP_HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'T': parser->method = PHP_HTTP_TRACE; break; diff --git a/sapi/cli/php_http_parser.h b/sapi/cli/php_http_parser.h index 345ca405edd0..d7006b6d04d7 100644 --- a/sapi/cli/php_http_parser.h +++ b/sapi/cli/php_http_parser.h @@ -79,6 +79,7 @@ enum php_http_method , PHP_HTTP_POST , PHP_HTTP_PUT , PHP_HTTP_PATCH + , PHP_HTTP_QUERY /* pathological */ , PHP_HTTP_CONNECT , PHP_HTTP_OPTIONS diff --git a/sapi/cli/tests/php_cli_server_query_method.phpt b/sapi/cli/tests/php_cli_server_query_method.phpt new file mode 100644 index 000000000000..58ce95bfd1e3 --- /dev/null +++ b/sapi/cli/tests/php_cli_server_query_method.phpt @@ -0,0 +1,40 @@ +--TEST-- +Support HTTP QUERY method +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +HTTP/1.1 200 OK +Host: %s +Date: %s +Connection: close +X-Powered-By: %s +Content-type: text/html; charset=UTF-8 + +string(5) "QUERY" From e7eebb9b1e834971bedf7bfbd7a19e1680e1ee7a Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Wed, 8 Jul 2026 16:40:23 +0200 Subject: [PATCH 116/211] sapi/phpdbg: remove dead str_type assignments before goto (#22600) The switch in phpdbg_print_breakpoints() assigned str_type per case and jumped to a shared label that immediately recomputed the same value from brake->type, making the pre-goto assignments dead. Set str_type directly per case instead and drop the goto. --- sapi/phpdbg/phpdbg_bp.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index f4e4ef81af2f..1233f0430121 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -1500,33 +1500,24 @@ PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type) /* {{{ */ switch (brake->type) { case PHPDBG_BREAK_METHOD_OPLINE: str_type = "method"; - goto print_opline; + break; case PHPDBG_BREAK_FUNCTION_OPLINE: str_type = "function"; - goto print_opline; + break; case PHPDBG_BREAK_FILE_OPLINE: - str_type = "method"; - - print_opline: { - if (brake->type == PHPDBG_BREAK_METHOD_OPLINE) { - str_type = "method"; - } else if (brake->type == PHPDBG_BREAK_FUNCTION_OPLINE) { - str_type = "function"; - } else if (brake->type == PHPDBG_BREAK_FILE_OPLINE) { - str_type = "file"; - } - - phpdbg_writeln("#%d\t\t#"ZEND_ULONG_FMT"\t\t(%s breakpoint)%s", - brake->id, brake->opline, str_type, - ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : ""); - } break; + str_type = "file"; + break; default: phpdbg_writeln("#%d\t\t#"ZEND_ULONG_FMT"%s", brake->id, brake->opline, ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : ""); - break; + continue; } + + phpdbg_writeln("#%d\t\t#"ZEND_ULONG_FMT"\t\t(%s breakpoint)%s", + brake->id, brake->opline, str_type, + ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : ""); } ZEND_HASH_FOREACH_END(); } break; From 0b9644161c5ff23adda2ee28af86ae11307cbc2b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 8 Jul 2026 10:41:21 -0400 Subject: [PATCH 117/211] ext/iconv/tests/bug76249.phpt: fallback for non-GNU iconv() (#22552) This test fails on musl, where iconv trips over the //IGNORE suffix. It is not obvious that //IGNORE is required to trigger the issue in the first place, but since we are ensuring that a security bug is fixed, it is better to include it where possible. This commit updates the test to append //IGNORE only on the two GNU iconv implementations that are known to support it. POSIX specifies its behavior, but leaves overall support optional. --- ext/iconv/tests/bug76249.phpt | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ext/iconv/tests/bug76249.phpt b/ext/iconv/tests/bug76249.phpt index 37608ccc0447..2fa9b8cc0500 100644 --- a/ext/iconv/tests/bug76249.phpt +++ b/ext/iconv/tests/bug76249.phpt @@ -4,16 +4,27 @@ Bug #76249 (stream filter convert.iconv leads to infinite loop on invalid sequen iconv --FILE-- DONE ---EXPECTF-- -Warning: stream_get_contents(): iconv stream filter ("ucs-2"=>"utf%A8//IGNORE"): invalid multibyte sequence in %sbug76249.php on line %d -string(0) "" +--EXPECTREGEX-- +Warning: stream_get_contents\(\): iconv stream filter \("ucs-2"=>"utf-?8(\/\/IGNORE)?"\): invalid multibyte sequence in .*bug76249\.php on line \d+ +string\(0\) "" DONE From 215ddb732e1481a43a77d5815014a32e2c76751a Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 8 Jul 2026 10:41:58 -0400 Subject: [PATCH 118/211] ext/iconv/tests/bug52211.phpt: use per-iconv charset names (#22543) The charset names used in this test are implementation-specific, and in particular are not known to musl. This causes musl to fall back to utf8, in which the input string is not actually invalid, leading to a failed test. The input string is already invalid in ASCII however, so we solve the general problem by using ASCII as the to/from charset unless the ICONV_IMPL is known to support the originals. --- ext/iconv/tests/bug52211.phpt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ext/iconv/tests/bug52211.phpt b/ext/iconv/tests/bug52211.phpt index f213c764de81..fe599f8ac559 100644 --- a/ext/iconv/tests/bug52211.phpt +++ b/ext/iconv/tests/bug52211.phpt @@ -5,8 +5,22 @@ iconv --FILE-- From 4daefbf9f827b4ebf5861d5b1dd557c408764de9 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 8 Jul 2026 16:39:46 +0100 Subject: [PATCH 119/211] bz2: use C23 enum and pack filter data struct (#22641) --- ext/bz2/bz2_filter.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c index 09c49fa76687..ec042a51a3b2 100644 --- a/ext/bz2/bz2_filter.c +++ b/ext/bz2/bz2_filter.c @@ -21,7 +21,7 @@ /* {{{ data structure */ -enum strm_status { +C23_ENUM(strm_status, uint8_t) { PHP_BZ2_UNINITIALIZED, PHP_BZ2_RUNNING, PHP_BZ2_FINISHED @@ -34,12 +34,11 @@ typedef struct _php_bz2_filter_data { size_t inbuf_len; size_t outbuf_len; - enum strm_status status; /* Decompress option */ - unsigned int small_footprint : 1; /* Decompress option */ - unsigned int expect_concatenated : 1; /* Decompress option */ - unsigned int is_flushed : 1; /* only for compression */ - - int persistent; + bool persistent; + bool expect_concatenated : 1; /* Decompress option */ + bool small_footprint : 1; /* Decompress option */ + bool is_flushed : 1; /* only for compression */ + strm_status status; /* Decompress option */ /* Configuration for reset - immutable */ int blockSize100k; /* compress only */ From e790541cc5e1a09f9c22dbcddb7a8f497b02274f Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 8 Jul 2026 23:45:11 +0800 Subject: [PATCH 120/211] [skip ci] Add various missing UPGRADING entries for 8.6 BC breaks Dom\DtdNamedNodeMap array access negative index behavior: PR #22630 Dom\DtdNamedNodeMap array access INT_MAX index validation: PR #22630 sleep() maximum seconds validation: PR #22619 usleep() maximum microseconds validation: PR #22619 --- UPGRADING | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/UPGRADING b/UPGRADING index 6a1f1b12e4a8..a6cd3bdef1cb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -32,6 +32,11 @@ PHP 8.6 UPGRADE NOTES from global scope" instead of the prior readonly modification error. ReflectionProperty::isWritable() also reports these properties accurately. + . Array access on Dom\DtdNamedNodeMap objects now returns null for negative + integer indexes instead of returning the first node. + . Array access on Dom\DtdNamedNodeMap objects now raises a ValueError when + the integer index is greater than INT_MAX instead of overflowing to a + smaller index. - GD: . imagesetstyle(), imagefilter() and imagecrop() filter their array arguments @@ -188,6 +193,11 @@ PHP 8.6 UPGRADE NOTES value is passed. . number_format() now raises a ValueError when $decimals is outside the integer range instead of silently clamping very large positive values. + . sleep() now raises a ValueError when $seconds is greater than the platform + limit (UINT_MAX seconds, or UINT_MAX / 1000 seconds on Windows) instead of + allowing the value to overflow. + . usleep() now raises a ValueError when $microseconds is greater than + UINT_MAX instead of allowing the value to overflow. . proc_open() now raises a ValueError when the $cwd argument contains NUL bytes. From 9b1cdc5a98bb90ba3dc3e1ce4e380ffc3ef9a3cd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 28 Sep 2024 22:07:39 +0100 Subject: [PATCH 121/211] ext/standard: Add test for headers_sent() by ref args --- .../headers_sent_by_ref_args.phpt | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 ext/standard/tests/general_functions/headers_sent_by_ref_args.phpt diff --git a/ext/standard/tests/general_functions/headers_sent_by_ref_args.phpt b/ext/standard/tests/general_functions/headers_sent_by_ref_args.phpt new file mode 100644 index 000000000000..bd222ca91437 --- /dev/null +++ b/ext/standard/tests/general_functions/headers_sent_by_ref_args.phpt @@ -0,0 +1,119 @@ +--TEST-- +headers_sent() by-ref argument with named arguments +--FILE-- + +--EXPECTF-- +headers_sent(line: $line) prior to sending headers +headers_sent(): +bool(false) +headers_list(): +array(0) { +} +$file: +NULL +$line: +int(0) +headers_sent(filename: $file) prior to sending headers +headers_sent(): +bool(false) +headers_list(): +array(0) { +} +$file: +string(0) "" +$line: +NULL +header(): +NULL +headers_sent(line: $line) after sending headers +headers_sent(): +bool(true) +headers_list(): +array(0) { +} +$file: +NULL +$line: +int(39) +headers_sent(filename: $file) after sending headers +headers_sent(): +bool(true) +headers_list(): +array(0) { +} +$file: +string(%d) "%s" +$line: +NULL +Done From 66966e625e0928633572236b2afd811bfeb66fe6 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 28 Sep 2024 22:14:19 +0100 Subject: [PATCH 122/211] ext/standard/head.c: no need to rely on ZEND_NUM_ARGS() --- Zend/tests/weakrefs/gh17442_1.phpt | 2 +- ext/standard/head.c | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Zend/tests/weakrefs/gh17442_1.phpt b/Zend/tests/weakrefs/gh17442_1.phpt index fc7f60174ed9..d535c307e3be 100644 --- a/Zend/tests/weakrefs/gh17442_1.phpt +++ b/Zend/tests/weakrefs/gh17442_1.phpt @@ -17,6 +17,6 @@ headers_sent($obj,$generator); Fatal error: Uncaught Exception: Test in %s:%d Stack trace: #0 [internal function]: class@anonymous->__destruct() -#1 %s(%d): headers_sent(NULL, 0) +#1 %s(%d): headers_sent(NULL, NULL) #2 {main} thrown in %s on line %d diff --git a/ext/standard/head.c b/ext/standard/head.c index 773b6919b780..34a22327b5e7 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -314,14 +314,15 @@ PHP_FUNCTION(setrawcookie) /* {{{ Returns true if headers have already been sent, false otherwise */ PHP_FUNCTION(headers_sent) { - zval *arg1 = NULL, *arg2 = NULL; - const char *file=""; - int line=0; + zval *by_ref_filename = NULL; + zval *by_ref_line = NULL; + const char *file = ""; + int line = 0; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(arg1) - Z_PARAM_ZVAL(arg2) + Z_PARAM_ZVAL(by_ref_filename) + Z_PARAM_ZVAL(by_ref_line) ZEND_PARSE_PARAMETERS_END(); if (SG(headers_sent)) { @@ -329,17 +330,15 @@ PHP_FUNCTION(headers_sent) file = php_output_get_start_filename(); } - switch(ZEND_NUM_ARGS()) { - case 2: - ZEND_TRY_ASSIGN_REF_LONG(arg2, line); - ZEND_FALLTHROUGH; - case 1: + if (by_ref_filename) { if (file) { - ZEND_TRY_ASSIGN_REF_STRING(arg1, file); + ZEND_TRY_ASSIGN_REF_STRING(by_ref_filename, file); } else { - ZEND_TRY_ASSIGN_REF_EMPTY_STRING(arg1); + ZEND_TRY_ASSIGN_REF_EMPTY_STRING(by_ref_filename); } - break; + } + if (by_ref_line) { + ZEND_TRY_ASSIGN_REF_LONG(by_ref_line, line); } RETURN_BOOL(SG(headers_sent)); From 6f1beedb446a32ea3f41028a729ddf737c19c91c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 8 Jul 2026 13:38:48 +0200 Subject: [PATCH 123/211] [skip ci] Fix CXXFLAGS for type verification build The attempted fix in ba32d70 was incorrect as the flags are later overwritten with -D_GLIBCXX_ASSERTIONS. Closes GH-22639 --- .github/workflows/test-suite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index e8f698e4e091..e269ed336408 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -166,10 +166,10 @@ jobs: with: configurationParameters: >- ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }} - ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" CXXFLAGS="-DZEND_VERIFY_TYPE_INFERENCE"' || '' }} + ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE"' || '' }} ${{ (matrix.variation && fromJson(inputs.branch).jobs.LINUX_X64.config.variation_enable_zend_max_execution_timers) && '--enable-zend-max-execution-timers' || '' }} --${{ matrix.debug && 'enable' || 'disable' }}-debug - ${{ matrix.debug && 'CXXFLAGS="-D_GLIBCXX_ASSERTIONS"' || '' }} + ${{ (matrix.variation || matrix.debug) && format('CXXFLAGS="{0} {1}"', matrix.variation && '-DZEND_VERIFY_TYPE_INFERENCE' || '', matrix.debug && '-D_GLIBCXX_ASSERTIONS' || '') || '' }} --${{ matrix.zts && 'enable' || 'disable' }}-zts asan: ${{ matrix.asan && 'true' || 'false' }} skipSlow: ${{ (matrix.asan && !inputs.all_variations) && 'true' || 'false' }} From ebbfb3c02bcd42bb21ceddb71a26010c1f486d0b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 16 Nov 2024 22:55:07 -0500 Subject: [PATCH 124/211] ext/iconv/tests/eucjp2iso2022jp.phpt: skip for "unknown" iconv This test does a byte comparison of the expected ISO-2022-JP encoding with the result of iconv(). The ISO-2022-JP encoding, however, is stateful; there are many ways to arrive at a correct answer. Musl is known to have an inefficient encoding that causes it to fail this test, so we add a SKIPIF for the "unknown" iconv implementation that musl has. Nothing is wrong here per se, but to support the musl output (and that of other iconvs), an expert would need to verify it. --- ext/iconv/tests/eucjp2iso2022jp.phpt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/iconv/tests/eucjp2iso2022jp.phpt b/ext/iconv/tests/eucjp2iso2022jp.phpt index 6c1a8ec1214b..c3260c370f08 100644 --- a/ext/iconv/tests/eucjp2iso2022jp.phpt +++ b/ext/iconv/tests/eucjp2iso2022jp.phpt @@ -2,6 +2,16 @@ EUC-JP to ISO-2022-JP --EXTENSIONS-- iconv +--SKIPIF-- + --INI-- error_reporting=2039 --FILE-- From 863263a775ec18d25d3c5e035192e780fd828e1c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sat, 16 Nov 2024 23:13:42 -0500 Subject: [PATCH 125/211] ext/iconv/tests/iconv_mime_encode.phpt: skip for "unknown" iconv This test checks the output of iconv_mime_encode() with a target charset of ISO-2022-JP. That charset is stateful, though, and there are many ways to arrive at a correct answer. Musl has an inefficient encoding of it that causes this to fail because the actual output differs from (and is much longer than) the expected output. We add a SKIPIF for the "unknown" iconv implementation that musl has. --- ext/iconv/tests/iconv_mime_encode.phpt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/iconv/tests/iconv_mime_encode.phpt b/ext/iconv/tests/iconv_mime_encode.phpt index e055f53b016a..c6f41c56ad90 100644 --- a/ext/iconv/tests/iconv_mime_encode.phpt +++ b/ext/iconv/tests/iconv_mime_encode.phpt @@ -2,6 +2,16 @@ iconv_mime_encode() --EXTENSIONS-- iconv +--SKIPIF-- + --INI-- iconv.internal_charset=iso-8859-1 --FILE-- From 8d1d4e3cf8e1502297695a66b88379fa727a8a72 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 9 Jul 2026 01:59:34 +0800 Subject: [PATCH 126/211] Zend: Add new zend APIs for `bin2hex` to remove `ext/hash` usage in various extensions (#21724) There are various extensions in the php code base that includes ext/hash/php_hash for the php_hash_bin2hex method. While these method is actually simple, we can make them as APIs in the zend engine to remove the dependence on ext/hash/ This commit added the needed APIs (zend_bin2hex and zend_bin2hex_str). A follow-up PR will be made in the future to replace the usage of ext/hash with the added APIs. --- UPGRADING.INTERNALS | 2 ++ Zend/zend_string.c | 26 ++++++++++++++++++++++++++ Zend/zend_string.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index adfc91880f29..1c602548b1ed 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -68,6 +68,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added Z_PARAM_ENUM(). . Added zend_enum_fetch_case_id(). . Added zend_enum_get_case_by_id(). + . Added zend_bin2hex() and zend_bin2hex_str() as helper functions to remove + dependencies on /ext/hash in various extensions. . ZEND_INI_GET_ADDR() is now a void* pointer instead of a char* pointer. This more correctly represents the generic nature of the returned pointer and allows to remove explicit casts, but possibly breaks pointer arithmetic diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 52fca0cd4346..b1b4a0e17a69 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -52,6 +52,17 @@ ZEND_API zend_string *zend_empty_string = NULL; ZEND_API zend_string *zend_one_char_string[256]; ZEND_API zend_string **zend_known_strings = NULL; +/* this is read-only, so it's ok */ +ZEND_SET_ALIGNED(16, static const char zend_hexconvtab[]) = "0123456789abcdef"; + +static zend_always_inline void zend_bin2hex_impl(char *out, const unsigned char *in, size_t in_len, const char *hexconvtab) +{ + for (size_t i = 0; i < in_len; i++) { + out[i * 2] = hexconvtab[in[i] >> 4]; + out[i * 2 + 1] = hexconvtab[in[i] & 0x0f]; + } +} + ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str) { return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str)); @@ -62,6 +73,21 @@ ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len) return zend_inline_hash_func(str, len); } +ZEND_API void ZEND_FASTCALL zend_bin2hex(char *out, const unsigned char *in, size_t in_len) +{ + zend_bin2hex_impl(out, in, in_len, zend_hexconvtab); +} + +ZEND_API zend_string *zend_bin2hex_str(const unsigned char *in, size_t in_len) +{ + zend_string *result = zend_string_safe_alloc(in_len, 2 * sizeof(char), 0, 0); + + zend_bin2hex(ZSTR_VAL(result), in, in_len); + ZSTR_VAL(result)[in_len * 2] = '\0'; + + return result; +} + static void _str_dtor(zval *zv) { zend_string *str = Z_STR_P(zv); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index e0d1c026a2f2..fdfff2bbc894 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -43,6 +43,8 @@ ZEND_API extern zend_string_init_existing_interned_func_t zend_string_init_exist ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str); ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len); ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str); +ZEND_API void ZEND_FASTCALL zend_bin2hex(char *out, const unsigned char *in, size_t in_len); +ZEND_API zend_string *zend_bin2hex_str(const unsigned char *in, size_t in_len); ZEND_API zend_string *zend_string_concat2( const char *str1, size_t str1_len, From d0ce868a5a531f701730fd4a184a2ff9cc22d0d7 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 8 Jul 2026 19:06:33 +0100 Subject: [PATCH 127/211] iconv: fix test with duplicate SKIPIF from merge up I didn't get a merge conflict here so assumed it was fine. --- ext/iconv/tests/iconv_mime_encode.phpt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/iconv/tests/iconv_mime_encode.phpt b/ext/iconv/tests/iconv_mime_encode.phpt index 7f4b9bfb5608..75c9b56e4291 100644 --- a/ext/iconv/tests/iconv_mime_encode.phpt +++ b/ext/iconv/tests/iconv_mime_encode.phpt @@ -2,6 +2,8 @@ iconv_mime_encode() --EXTENSIONS-- iconv +--INI-- +iconv.internal_charset=iso-8859-1 --SKIPIF-- ---INI-- -iconv.internal_charset=iso-8859-1 ---SKIPIF-- - Date: Thu, 9 Jul 2026 04:37:17 +0800 Subject: [PATCH 128/211] CI: fix encoding errors in iconv tests (#22646) --- ext/iconv/tests/eucjp2iso2022jp.phpt | 4 ++-- ext/iconv/tests/iconv_mime_encode.phpt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/iconv/tests/eucjp2iso2022jp.phpt b/ext/iconv/tests/eucjp2iso2022jp.phpt index db48ea627140..ee64a5a52c96 100644 --- a/ext/iconv/tests/eucjp2iso2022jp.phpt +++ b/ext/iconv/tests/eucjp2iso2022jp.phpt @@ -29,8 +29,8 @@ function hexdump($str) { print "\n"; } -$str = str_repeat("���ܸ�ƥ����Ȥ� English text", 30); -$str .= "���ܸ�"; +$str = str_repeat("ÆüËÜ¸ì¥Æ¥­¥¹¥È¤È English text", 30); +$str .= "ÆüËܸì"; echo hexdump(iconv("EUC-JP", "ISO-2022-JP", $str)); ?> diff --git a/ext/iconv/tests/iconv_mime_encode.phpt b/ext/iconv/tests/iconv_mime_encode.phpt index 75c9b56e4291..d2ec45df68b9 100644 --- a/ext/iconv/tests/iconv_mime_encode.phpt +++ b/ext/iconv/tests/iconv_mime_encode.phpt @@ -33,7 +33,7 @@ $preference = array( for ($line_len= 0; $line_len < 80; ++$line_len) { print "-------- line length=$line_len\n"; $preference["line-length"] = $line_len; - $result = iconv_mime_encode("From", "����ץ�ʸ���󥵥�ץ�ʸ�������ܸ�ƥ�����", $preference); + $result = iconv_mime_encode("From", "¥µ¥ó¥×¥ëʸ»úÎó¥µ¥ó¥×¥ëʸ»úÎóÆüËÜ¸ì¥Æ¥­¥¹¥È", $preference); var_dump($result); if ($result !== false) { $max = max(array_map("strlen", explode("\n", $result))); From 6bc7c26cf67a9480b5ef9d6191aebe87fa931183 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 9 Jul 2026 20:03:34 +0800 Subject: [PATCH 129/211] Replace dependencies on /ext/hash in various extensions (#21832) Followup-PR for #21724. Using the new API in #21724 to replace dependencies on /ext/hash in - ext/opcache/ZendAccelerator/ - ext/soap/php_http.c - Zend/zend_system_id.c In the following ext, there are independent implementation of bin2hex. This PR also change them to directly using the new Zend API. - ext/standard/string.c - ext/hash/php_hash.h --- Zend/zend_system_id.c | 3 +-- ext/hash/hash.c | 8 ++++---- ext/hash/php_hash.h | 11 ----------- ext/opcache/ZendAccelerator.c | 3 +-- ext/soap/php_http.c | 3 +-- ext/standard/string.c | 23 +---------------------- 6 files changed, 8 insertions(+), 43 deletions(-) diff --git a/Zend/zend_system_id.c b/Zend/zend_system_id.c index ead694375b6d..aaeb254386b7 100644 --- a/Zend/zend_system_id.c +++ b/Zend/zend_system_id.c @@ -17,7 +17,6 @@ #include "zend_system_id.h" #include "zend_extensions.h" #include "ext/standard/md5.h" -#include "ext/hash/php_hash.h" ZEND_API char zend_system_id[32]; @@ -88,6 +87,6 @@ void zend_finalize_system_id(void) } PHP_MD5Final(digest, &context); - php_hash_bin2hex(zend_system_id, digest, sizeof digest); + zend_bin2hex(zend_system_id, digest, sizeof digest); finalized = 1; } diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 74abf02acd81..21bee4a64e13 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -401,7 +401,7 @@ static void php_hash_do_hash( } else { zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); - php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); + zend_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; zend_string_efree(digest); RETURN_NEW_STR(hex_digest); @@ -558,7 +558,7 @@ static void php_hash_do_hash_hmac( } else { zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); - php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); + zend_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; zend_string_efree(digest); RETURN_NEW_STR(hex_digest); @@ -819,7 +819,7 @@ PHP_FUNCTION(hash_final) } else { zend_string *hex_digest = zend_string_safe_alloc(digest_len, 2, 0, 0); - php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), digest_len); + zend_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), digest_len); ZSTR_VAL(hex_digest)[2 * digest_len] = 0; zend_string_efree(digest); RETURN_NEW_STR(hex_digest); @@ -1089,7 +1089,7 @@ PHP_FUNCTION(hash_pbkdf2) if (raw_output) { memcpy(ZSTR_VAL(returnval), result, length); } else { - php_hash_bin2hex(ZSTR_VAL(returnval), result, digest_length); + zend_bin2hex(ZSTR_VAL(returnval), result, digest_length); } ZSTR_VAL(returnval)[length] = 0; efree(result); diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h index f89bee8ab010..0a11b4749326 100644 --- a/ext/hash/php_hash.h +++ b/ext/hash/php_hash.h @@ -182,15 +182,4 @@ static inline void php_hash_free_context(const php_hash_ops *ops, void *ctx) { efree(ctx); } -static inline void php_hash_bin2hex(char *out, const unsigned char *in, size_t in_len) -{ - static const char hexits[17] = "0123456789abcdef"; - size_t i; - - for(i = 0; i < in_len; i++) { - out[i * 2] = hexits[in[i] >> 4]; - out[(i * 2) + 1] = hexits[in[i] & 0x0F]; - } -} - #endif /* PHP_HASH_H */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6f606d9d37d5..cedc21c376a4 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -50,7 +50,6 @@ #include "ext/standard/basic_functions.h" #ifdef ZEND_WIN32 -# include "ext/hash/php_hash.h" # include "ext/standard/md5.h" #endif @@ -2532,7 +2531,7 @@ static zend_result accel_gen_uname_id(void) PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t)); PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id)); PHP_MD5Final(digest, &ctx); - php_hash_bin2hex(accel_uname_id, digest, sizeof digest); + zend_bin2hex(accel_uname_id, digest, sizeof digest); return SUCCESS; } #endif diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index b02942f3dccb..fe808404b526 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -15,7 +15,6 @@ */ #include "php_soap.h" -#include "ext/hash/php_hash.h" /* For php_hash_bin2hex() */ #include "ext/uri/php_uri.h" static char *get_http_header_value_nodup(char *headers, char *type, size_t *len); @@ -696,7 +695,7 @@ bool make_http_soap_request( return false; } - php_hash_bin2hex(cnonce, nonce, sizeof(nonce)); + zend_bin2hex(cnonce, nonce, sizeof(nonce)); cnonce[32] = 0; if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL && diff --git a/ext/standard/string.c b/ext/standard/string.c index 006ec3509a67..c6e89dcca2e5 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -45,32 +45,11 @@ #include "zend_simd.h" -/* this is read-only, so it's ok */ -ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef"; - /* localeconv mutex */ #ifdef ZTS static MUTEX_T locale_mutex = NULL; #endif -/* {{{ php_bin2hex */ -static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen) -{ - zend_string *result; - size_t i, j; - - result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0); - - for (i = j = 0; i < oldlen; i++) { - ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4]; - ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15]; - } - ZSTR_VAL(result)[j] = '\0'; - - return result; -} -/* }}} */ - /* {{{ php_hex2bin */ static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen) { @@ -158,7 +137,7 @@ PHP_FUNCTION(bin2hex) Z_PARAM_STR(data) ZEND_PARSE_PARAMETERS_END(); - result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data)); + result = zend_bin2hex_str((unsigned char *) ZSTR_VAL(data), ZSTR_LEN(data)); RETURN_STR(result); } From 29f8f56b1eda0a920bc863dbd7b48caeb3635286 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 9 Jul 2026 15:05:06 +0100 Subject: [PATCH 130/211] Updated to version 2026.3 (2026c) --- ext/date/lib/timezonedb.h | 4303 +++++++++++++++++-------------------- 1 file changed, 2008 insertions(+), 2295 deletions(-) diff --git a/ext/date/lib/timezonedb.h b/ext/date/lib/timezonedb.h index 453041acabcf..a745e76019ba 100644 --- a/ext/date/lib/timezonedb.h +++ b/ext/date/lib/timezonedb.h @@ -20,593 +20,593 @@ const timelib_tzdb_index_entry timezonedb_idx_builtin[598] = { { (char*) "Africa/Bujumbura" , 0x000B2A }, { (char*) "Africa/Cairo" , 0x000BB9 }, { (char*) "Africa/Casablanca" , 0x0010E2 }, - { (char*) "Africa/Ceuta" , 0x00186D }, - { (char*) "Africa/Conakry" , 0x001AB9 }, - { (char*) "Africa/Dakar" , 0x001B63 }, - { (char*) "Africa/Dar_es_Salaam" , 0x001C04 }, - { (char*) "Africa/Djibouti" , 0x001CB1 }, - { (char*) "Africa/Douala" , 0x001D40 }, - { (char*) "Africa/El_Aaiun" , 0x001DCF }, - { (char*) "Africa/Freetown" , 0x002501 }, - { (char*) "Africa/Gaborone" , 0x002651 }, - { (char*) "Africa/Harare" , 0x002711 }, - { (char*) "Africa/Johannesburg" , 0x0027A0 }, - { (char*) "Africa/Juba" , 0x00286A }, - { (char*) "Africa/Kampala" , 0x002A40 }, - { (char*) "Africa/Khartoum" , 0x002B02 }, - { (char*) "Africa/Kigali" , 0x002CD8 }, - { (char*) "Africa/Kinshasa" , 0x002D67 }, - { (char*) "Africa/Lagos" , 0x002E0F }, - { (char*) "Africa/Libreville" , 0x002ECF }, - { (char*) "Africa/Lome" , 0x002F5E }, - { (char*) "Africa/Luanda" , 0x002FEC }, - { (char*) "Africa/Lubumbashi" , 0x00308A }, - { (char*) "Africa/Lusaka" , 0x003145 }, - { (char*) "Africa/Malabo" , 0x0031D4 }, - { (char*) "Africa/Maputo" , 0x003276 }, - { (char*) "Africa/Maseru" , 0x003305 }, - { (char*) "Africa/Mbabane" , 0x0033AE }, - { (char*) "Africa/Mogadishu" , 0x00343F }, - { (char*) "Africa/Monrovia" , 0x0034EC }, - { (char*) "Africa/Nairobi" , 0x00359C }, - { (char*) "Africa/Ndjamena" , 0x003667 }, - { (char*) "Africa/Niamey" , 0x003713 }, - { (char*) "Africa/Nouakchott" , 0x0037C8 }, - { (char*) "Africa/Ouagadougou" , 0x003872 }, - { (char*) "Africa/Porto-Novo" , 0x003900 }, - { (char*) "Africa/Sao_Tome" , 0x0039A2 }, - { (char*) "Africa/Timbuktu" , 0x003A5B }, - { (char*) "Africa/Tripoli" , 0x003AE9 }, - { (char*) "Africa/Tunis" , 0x003CA4 }, - { (char*) "Africa/Windhoek" , 0x003E71 }, - { (char*) "America/Adak" , 0x0040FB }, - { (char*) "America/Anchorage" , 0x0044EA }, - { (char*) "America/Anguilla" , 0x0048DA }, - { (char*) "America/Antigua" , 0x004968 }, - { (char*) "America/Araguaina" , 0x004A09 }, - { (char*) "America/Argentina/Buenos_Aires" , 0x004C6E }, - { (char*) "America/Argentina/Catamarca" , 0x004F53 }, - { (char*) "America/Argentina/ComodRivadavia" , 0x00523E }, - { (char*) "America/Argentina/Cordoba" , 0x00550E }, - { (char*) "America/Argentina/Jujuy" , 0x005814 }, - { (char*) "America/Argentina/La_Rioja" , 0x005ADC }, - { (char*) "America/Argentina/Mendoza" , 0x005DC2 }, - { (char*) "America/Argentina/Rio_Gallegos" , 0x00609E }, - { (char*) "America/Argentina/Salta" , 0x00637D }, - { (char*) "America/Argentina/San_Juan" , 0x006651 }, - { (char*) "America/Argentina/San_Luis" , 0x006937 }, - { (char*) "America/Argentina/Tucuman" , 0x006C1D }, - { (char*) "America/Argentina/Ushuaia" , 0x006F0B }, - { (char*) "America/Aruba" , 0x0071F0 }, - { (char*) "America/Asuncion" , 0x007293 }, - { (char*) "America/Atikokan" , 0x0076DC }, - { (char*) "America/Atka" , 0x0077E9 }, - { (char*) "America/Bahia" , 0x007BBE }, - { (char*) "America/Bahia_Banderas" , 0x007E79 }, - { (char*) "America/Barbados" , 0x008152 }, - { (char*) "America/Belem" , 0x008274 }, - { (char*) "America/Belize" , 0x00841C }, - { (char*) "America/Blanc-Sablon" , 0x00883D }, - { (char*) "America/Boa_Vista" , 0x008932 }, - { (char*) "America/Bogota" , 0x008AF3 }, - { (char*) "America/Boise" , 0x008BB2 }, - { (char*) "America/Buenos_Aires" , 0x008FC5 }, - { (char*) "America/Cambridge_Bay" , 0x009295 }, - { (char*) "America/Campo_Grande" , 0x009628 }, - { (char*) "America/Cancun" , 0x0099FE }, - { (char*) "America/Caracas" , 0x009C30 }, - { (char*) "America/Catamarca" , 0x009CFA }, - { (char*) "America/Cayenne" , 0x009FCA }, - { (char*) "America/Cayman" , 0x00A06D }, - { (char*) "America/Chicago" , 0x00A10E }, - { (char*) "America/Chihuahua" , 0x00A808 }, - { (char*) "America/Ciudad_Juarez" , 0x00AADD }, - { (char*) "America/Coral_Harbour" , 0x00ADD3 }, - { (char*) "America/Cordoba" , 0x00AE74 }, - { (char*) "America/Costa_Rica" , 0x00B144 }, - { (char*) "America/Coyhaique" , 0x00B238 }, - { (char*) "America/Creston" , 0x00B7A2 }, - { (char*) "America/Cuiaba" , 0x00B85E }, - { (char*) "America/Curacao" , 0x00BC1B }, - { (char*) "America/Danmarkshavn" , 0x00BCBE }, - { (char*) "America/Dawson" , 0x00BEA3 }, - { (char*) "America/Dawson_Creek" , 0x00C2C6 }, - { (char*) "America/Denver" , 0x00C59D }, - { (char*) "America/Detroit" , 0x00C9D0 }, - { (char*) "America/Dominica" , 0x00CD78 }, - { (char*) "America/Edmonton" , 0x00CE06 }, - { (char*) "America/Eirunepe" , 0x00D1FE }, - { (char*) "America/El_Salvador" , 0x00D3CD }, - { (char*) "America/Ensenada" , 0x00D489 }, - { (char*) "America/Fort_Nelson" , 0x00D9EC }, - { (char*) "America/Fort_Wayne" , 0x00DFB4 }, - { (char*) "America/Fortaleza" , 0x00E1D3 }, - { (char*) "America/Glace_Bay" , 0x00E3E9 }, - { (char*) "America/Godthab" , 0x00E780 }, - { (char*) "America/Goose_Bay" , 0x00EB51 }, - { (char*) "America/Grand_Turk" , 0x00F1A9 }, - { (char*) "America/Grenada" , 0x00F50A }, - { (char*) "America/Guadeloupe" , 0x00F598 }, - { (char*) "America/Guatemala" , 0x00F626 }, - { (char*) "America/Guayaquil" , 0x00F706 }, - { (char*) "America/Guyana" , 0x00F7D7 }, - { (char*) "America/Halifax" , 0x00F898 }, - { (char*) "America/Havana" , 0x00FF4A }, - { (char*) "America/Hermosillo" , 0x0103B3 }, - { (char*) "America/Indiana/Indianapolis" , 0x0104C7 }, - { (char*) "America/Indiana/Knox" , 0x0106FF }, - { (char*) "America/Indiana/Marengo" , 0x010B18 }, - { (char*) "America/Indiana/Petersburg" , 0x010D72 }, - { (char*) "America/Indiana/Tell_City" , 0x01103C }, - { (char*) "America/Indiana/Vevay" , 0x011266 }, - { (char*) "America/Indiana/Vincennes" , 0x0113FD }, - { (char*) "America/Indiana/Winamac" , 0x011653 }, - { (char*) "America/Indianapolis" , 0x0118D0 }, - { (char*) "America/Inuvik" , 0x011AEF }, - { (char*) "America/Iqaluit" , 0x011E40 }, - { (char*) "America/Jamaica" , 0x0121BC }, - { (char*) "America/Jujuy" , 0x01231B }, - { (char*) "America/Juneau" , 0x0125D9 }, - { (char*) "America/Kentucky/Louisville" , 0x0129BF }, - { (char*) "America/Kentucky/Monticello" , 0x012EC3 }, - { (char*) "America/Knox_IN" , 0x0132AF }, - { (char*) "America/Kralendijk" , 0x0136B3 }, - { (char*) "America/La_Paz" , 0x013770 }, - { (char*) "America/Lima" , 0x013826 }, - { (char*) "America/Los_Angeles" , 0x01394D }, - { (char*) "America/Louisville" , 0x013E6E }, - { (char*) "America/Lower_Princes" , 0x014354 }, - { (char*) "America/Maceio" , 0x014411 }, - { (char*) "America/Managua" , 0x014623 }, - { (char*) "America/Manaus" , 0x014756 }, - { (char*) "America/Marigot" , 0x01490D }, - { (char*) "America/Martinique" , 0x0149CA }, - { (char*) "America/Matamoros" , 0x014A88 }, - { (char*) "America/Mazatlan" , 0x014C75 }, - { (char*) "America/Mendoza" , 0x014F65 }, - { (char*) "America/Menominee" , 0x015235 }, - { (char*) "America/Merida" , 0x0155F5 }, - { (char*) "America/Metlakatla" , 0x0158A0 }, - { (char*) "America/Mexico_City" , 0x015B0D }, - { (char*) "America/Miquelon" , 0x015E2C }, - { (char*) "America/Moncton" , 0x01605E }, - { (char*) "America/Monterrey" , 0x016657 }, - { (char*) "America/Montevideo" , 0x01695E }, - { (char*) "America/Montreal" , 0x016D33 }, - { (char*) "America/Montserrat" , 0x0173F4 }, - { (char*) "America/Nassau" , 0x017482 }, - { (char*) "America/New_York" , 0x01787C }, - { (char*) "America/Nipigon" , 0x017F6C }, - { (char*) "America/Nome" , 0x01862D }, - { (char*) "America/Noronha" , 0x018A15 }, - { (char*) "America/North_Dakota/Beulah" , 0x018C15 }, - { (char*) "America/North_Dakota/Center" , 0x019049 }, - { (char*) "America/North_Dakota/New_Salem" , 0x019448 }, - { (char*) "America/Nuuk" , 0x01984D }, - { (char*) "America/Ojinaga" , 0x019C2F }, - { (char*) "America/Panama" , 0x019F25 }, - { (char*) "America/Pangnirtung" , 0x019FC6 }, - { (char*) "America/Paramaribo" , 0x01A329 }, - { (char*) "America/Phoenix" , 0x01A3F0 }, - { (char*) "America/Port-au-Prince" , 0x01A504 }, - { (char*) "America/Port_of_Spain" , 0x01A745 }, - { (char*) "America/Porto_Acre" , 0x01A7D3 }, - { (char*) "America/Porto_Velho" , 0x01A981 }, - { (char*) "America/Puerto_Rico" , 0x01AB1F }, - { (char*) "America/Punta_Arenas" , 0x01ABDC }, - { (char*) "America/Rainy_River" , 0x01B0BB }, - { (char*) "America/Rankin_Inlet" , 0x01B5D5 }, - { (char*) "America/Recife" , 0x01B91E }, - { (char*) "America/Regina" , 0x01BB18 }, - { (char*) "America/Resolute" , 0x01BDB7 }, - { (char*) "America/Rio_Branco" , 0x01C101 }, - { (char*) "America/Rosario" , 0x01C2B3 }, - { (char*) "America/Santa_Isabel" , 0x01C583 }, - { (char*) "America/Santarem" , 0x01CAE6 }, - { (char*) "America/Santiago" , 0x01CC96 }, - { (char*) "America/Santo_Domingo" , 0x01D1F9 }, - { (char*) "America/Sao_Paulo" , 0x01D342 }, - { (char*) "America/Scoresbysund" , 0x01D73C }, - { (char*) "America/Shiprock" , 0x01DB3D }, - { (char*) "America/Sitka" , 0x01DF5B }, - { (char*) "America/St_Barthelemy" , 0x01E336 }, - { (char*) "America/St_Johns" , 0x01E3F3 }, - { (char*) "America/St_Kitts" , 0x01EB70 }, - { (char*) "America/St_Lucia" , 0x01EBFE }, - { (char*) "America/St_Thomas" , 0x01EC9F }, - { (char*) "America/St_Vincent" , 0x01ED2D }, - { (char*) "America/Swift_Current" , 0x01EDCE }, - { (char*) "America/Tegucigalpa" , 0x01EF5C }, - { (char*) "America/Thule" , 0x01F02A }, - { (char*) "America/Thunder_Bay" , 0x01F20B }, - { (char*) "America/Tijuana" , 0x01F8CC }, - { (char*) "America/Toronto" , 0x01FE3E }, - { (char*) "America/Tortola" , 0x02051D }, - { (char*) "America/Vancouver" , 0x0205AB }, - { (char*) "America/Virgin" , 0x020B02 }, - { (char*) "America/Whitehorse" , 0x020BBF }, - { (char*) "America/Winnipeg" , 0x020FE2 }, - { (char*) "America/Yakutat" , 0x021519 }, - { (char*) "America/Yellowknife" , 0x0218E7 }, - { (char*) "Antarctica/Casey" , 0x021CBD }, - { (char*) "Antarctica/Davis" , 0x021DED }, - { (char*) "Antarctica/DumontDUrville" , 0x021EC3 }, - { (char*) "Antarctica/Macquarie" , 0x021F77 }, - { (char*) "Antarctica/Mawson" , 0x022363 }, - { (char*) "Antarctica/McMurdo" , 0x02240D }, - { (char*) "Antarctica/Palmer" , 0x02273F }, - { (char*) "Antarctica/Rothera" , 0x022AC8 }, - { (char*) "Antarctica/South_Pole" , 0x022B5F }, - { (char*) "Antarctica/Syowa" , 0x022F7E }, - { (char*) "Antarctica/Troll" , 0x023014 }, - { (char*) "Antarctica/Vostok" , 0x0230C3 }, - { (char*) "Arctic/Longyearbyen" , 0x02317F }, - { (char*) "Asia/Aden" , 0x02344C }, - { (char*) "Asia/Almaty" , 0x0234DD }, - { (char*) "Asia/Amman" , 0x023765 }, - { (char*) "Asia/Anadyr" , 0x023B11 }, - { (char*) "Asia/Aqtau" , 0x023E17 }, - { (char*) "Asia/Aqtobe" , 0x024096 }, - { (char*) "Asia/Ashgabat" , 0x024316 }, - { (char*) "Asia/Ashkhabad" , 0x024499 }, - { (char*) "Asia/Atyrau" , 0x02461C }, - { (char*) "Asia/Baghdad" , 0x0248A5 }, - { (char*) "Asia/Bahrain" , 0x024B27 }, - { (char*) "Asia/Baku" , 0x024BE0 }, - { (char*) "Asia/Bangkok" , 0x024ED4 }, - { (char*) "Asia/Barnaul" , 0x024F78 }, - { (char*) "Asia/Beirut" , 0x025283 }, - { (char*) "Asia/Bishkek" , 0x02556B }, - { (char*) "Asia/Brunei" , 0x0257E1 }, - { (char*) "Asia/Calcutta" , 0x025887 }, - { (char*) "Asia/Chita" , 0x02596F }, - { (char*) "Asia/Choibalsan" , 0x025C7D }, - { (char*) "Asia/Chongqing" , 0x025EDB }, - { (char*) "Asia/Chungking" , 0x026070 }, - { (char*) "Asia/Colombo" , 0x026205 }, - { (char*) "Asia/Dacca" , 0x026308 }, - { (char*) "Asia/Damascus" , 0x0263FB }, - { (char*) "Asia/Dhaka" , 0x0268D9 }, - { (char*) "Asia/Dili" , 0x0269CC }, - { (char*) "Asia/Dubai" , 0x026A82 }, - { (char*) "Asia/Dushanbe" , 0x026B13 }, - { (char*) "Asia/Famagusta" , 0x026C8D }, - { (char*) "Asia/Gaza" , 0x027054 }, - { (char*) "Asia/Harbin" , 0x027BF0 }, - { (char*) "Asia/Hebron" , 0x027D85 }, - { (char*) "Asia/Ho_Chi_Minh" , 0x028932 }, - { (char*) "Asia/Hong_Kong" , 0x028A2A }, - { (char*) "Asia/Hovd" , 0x028D3D }, - { (char*) "Asia/Irkutsk" , 0x028FB1 }, - { (char*) "Asia/Istanbul" , 0x0292CF }, - { (char*) "Asia/Jakarta" , 0x02978B }, - { (char*) "Asia/Jayapura" , 0x02989C }, - { (char*) "Asia/Jerusalem" , 0x029989 }, - { (char*) "Asia/Kabul" , 0x029DC7 }, - { (char*) "Asia/Kamchatka" , 0x029E72 }, - { (char*) "Asia/Karachi" , 0x02A167 }, - { (char*) "Asia/Kashgar" , 0x02A27D }, - { (char*) "Asia/Kathmandu" , 0x02A30E }, - { (char*) "Asia/Katmandu" , 0x02A3BB }, - { (char*) "Asia/Khandyga" , 0x02A468 }, - { (char*) "Asia/Kolkata" , 0x02A799 }, - { (char*) "Asia/Krasnoyarsk" , 0x02A881 }, - { (char*) "Asia/Kuala_Lumpur" , 0x02AB8B }, - { (char*) "Asia/Kuching" , 0x02ACAB }, - { (char*) "Asia/Kuwait" , 0x02AE05 }, - { (char*) "Asia/Macao" , 0x02AE96 }, - { (char*) "Asia/Macau" , 0x02B1B9 }, - { (char*) "Asia/Magadan" , 0x02B4DC }, - { (char*) "Asia/Makassar" , 0x02B7E7 }, - { (char*) "Asia/Manila" , 0x02B8FA }, - { (char*) "Asia/Muscat" , 0x02BA18 }, - { (char*) "Asia/Nicosia" , 0x02BAA9 }, - { (char*) "Asia/Novokuznetsk" , 0x02BD18 }, - { (char*) "Asia/Novosibirsk" , 0x02C00B }, - { (char*) "Asia/Omsk" , 0x02C31C }, - { (char*) "Asia/Oral" , 0x02C61A }, - { (char*) "Asia/Phnom_Penh" , 0x02C8A6 }, - { (char*) "Asia/Pontianak" , 0x02C97A }, - { (char*) "Asia/Pyongyang" , 0x02CA93 }, - { (char*) "Asia/Qatar" , 0x02CB56 }, - { (char*) "Asia/Qostanay" , 0x02CBFA }, - { (char*) "Asia/Qyzylorda" , 0x02CE90 }, - { (char*) "Asia/Rangoon" , 0x02D129 }, - { (char*) "Asia/Riyadh" , 0x02D1F0 }, - { (char*) "Asia/Saigon" , 0x02D281 }, - { (char*) "Asia/Sakhalin" , 0x02D379 }, - { (char*) "Asia/Samarkand" , 0x02D690 }, - { (char*) "Asia/Seoul" , 0x02D81B }, - { (char*) "Asia/Shanghai" , 0x02D9C6 }, - { (char*) "Asia/Singapore" , 0x02DB67 }, - { (char*) "Asia/Srednekolymsk" , 0x02DC73 }, - { (char*) "Asia/Taipei" , 0x02DF83 }, - { (char*) "Asia/Tashkent" , 0x02E18E }, - { (char*) "Asia/Tbilisi" , 0x02E319 }, - { (char*) "Asia/Tehran" , 0x02E591 }, - { (char*) "Asia/Tel_Aviv" , 0x02E8C9 }, - { (char*) "Asia/Thimbu" , 0x02ED07 }, - { (char*) "Asia/Thimphu" , 0x02EDAD }, - { (char*) "Asia/Tokyo" , 0x02EE53 }, - { (char*) "Asia/Tomsk" , 0x02EF34 }, - { (char*) "Asia/Ujung_Pandang" , 0x02F23F }, - { (char*) "Asia/Ulaanbaatar" , 0x02F309 }, - { (char*) "Asia/Ulan_Bator" , 0x02F577 }, - { (char*) "Asia/Urumqi" , 0x02F7D5 }, - { (char*) "Asia/Ust-Nera" , 0x02F873 }, - { (char*) "Asia/Vientiane" , 0x02FB96 }, - { (char*) "Asia/Vladivostok" , 0x02FC7C }, - { (char*) "Asia/Yakutsk" , 0x02FF81 }, - { (char*) "Asia/Yangon" , 0x030285 }, - { (char*) "Asia/Yekaterinburg" , 0x03034C }, - { (char*) "Asia/Yerevan" , 0x03065E }, - { (char*) "Atlantic/Azores" , 0x03092E }, - { (char*) "Atlantic/Bermuda" , 0x030EB9 }, - { (char*) "Atlantic/Canary" , 0x0312C5 }, - { (char*) "Atlantic/Cape_Verde" , 0x0314BD }, - { (char*) "Atlantic/Faeroe" , 0x031578 }, - { (char*) "Atlantic/Faroe" , 0x03173D }, - { (char*) "Atlantic/Jan_Mayen" , 0x031902 }, - { (char*) "Atlantic/Madeira" , 0x031BCF }, - { (char*) "Atlantic/Reykjavik" , 0x032146 }, - { (char*) "Atlantic/South_Georgia" , 0x032443 }, - { (char*) "Atlantic/St_Helena" , 0x0324D3 }, - { (char*) "Atlantic/Stanley" , 0x032574 }, - { (char*) "Australia/ACT" , 0x032895 }, - { (char*) "Australia/Adelaide" , 0x032C29 }, - { (char*) "Australia/Brisbane" , 0x032FDD }, - { (char*) "Australia/Broken_Hill" , 0x033121 }, - { (char*) "Australia/Canberra" , 0x0334F6 }, - { (char*) "Australia/Currie" , 0x03388A }, - { (char*) "Australia/Darwin" , 0x033C81 }, - { (char*) "Australia/Eucla" , 0x033D89 }, - { (char*) "Australia/Hobart" , 0x033EE8 }, - { (char*) "Australia/LHI" , 0x0342E7 }, - { (char*) "Australia/Lindeman" , 0x0345A7 }, - { (char*) "Australia/Lord_Howe" , 0x034717 }, - { (char*) "Australia/Melbourne" , 0x0349E7 }, - { (char*) "Australia/North" , 0x034D83 }, - { (char*) "Australia/NSW" , 0x034E79 }, - { (char*) "Australia/Perth" , 0x03520D }, - { (char*) "Australia/Queensland" , 0x035369 }, - { (char*) "Australia/South" , 0x035496 }, - { (char*) "Australia/Sydney" , 0x03583B }, - { (char*) "Australia/Tasmania" , 0x035BEB }, - { (char*) "Australia/Victoria" , 0x035FE2 }, - { (char*) "Australia/West" , 0x036376 }, - { (char*) "Australia/Yancowinna" , 0x0364B4 }, - { (char*) "Brazil/Acre" , 0x03686D }, - { (char*) "Brazil/DeNoronha" , 0x036A1B }, - { (char*) "Brazil/East" , 0x036C0B }, - { (char*) "Brazil/West" , 0x036FCF }, - { (char*) "Canada/Atlantic" , 0x037177 }, - { (char*) "Canada/Central" , 0x03780B }, - { (char*) "Canada/Eastern" , 0x037D25 }, - { (char*) "Canada/Mountain" , 0x0383E6 }, - { (char*) "Canada/Newfoundland" , 0x0387BC }, - { (char*) "Canada/Pacific" , 0x038F1E }, - { (char*) "Canada/Saskatchewan" , 0x03945C }, - { (char*) "Canada/Yukon" , 0x0396E6 }, - { (char*) "CET" , 0x039AF7 }, - { (char*) "Chile/Continental" , 0x039F52 }, - { (char*) "Chile/EasterIsland" , 0x03A4A8 }, - { (char*) "CST6CDT" , 0x03A94A }, - { (char*) "Cuba" , 0x03B030 }, - { (char*) "EET" , 0x03B499 }, - { (char*) "Egypt" , 0x03B74F }, - { (char*) "Eire" , 0x03BC78 }, - { (char*) "EST" , 0x03C25C }, - { (char*) "EST5EDT" , 0x03C2FD }, - { (char*) "Etc/GMT" , 0x03C9D9 }, - { (char*) "Etc/GMT+0" , 0x03CA54 }, - { (char*) "Etc/GMT+1" , 0x03CACF }, - { (char*) "Etc/GMT+10" , 0x03CB4C }, - { (char*) "Etc/GMT+11" , 0x03CBCA }, - { (char*) "Etc/GMT+12" , 0x03CC48 }, - { (char*) "Etc/GMT+2" , 0x03CCC6 }, - { (char*) "Etc/GMT+3" , 0x03CD43 }, - { (char*) "Etc/GMT+4" , 0x03CDC0 }, - { (char*) "Etc/GMT+5" , 0x03CE3D }, - { (char*) "Etc/GMT+6" , 0x03CEBA }, - { (char*) "Etc/GMT+7" , 0x03CF37 }, - { (char*) "Etc/GMT+8" , 0x03CFB4 }, - { (char*) "Etc/GMT+9" , 0x03D031 }, - { (char*) "Etc/GMT-0" , 0x03D0AE }, - { (char*) "Etc/GMT-1" , 0x03D129 }, - { (char*) "Etc/GMT-10" , 0x03D1A7 }, - { (char*) "Etc/GMT-11" , 0x03D226 }, - { (char*) "Etc/GMT-12" , 0x03D2A5 }, - { (char*) "Etc/GMT-13" , 0x03D324 }, - { (char*) "Etc/GMT-14" , 0x03D3A3 }, - { (char*) "Etc/GMT-2" , 0x03D422 }, - { (char*) "Etc/GMT-3" , 0x03D4A0 }, - { (char*) "Etc/GMT-4" , 0x03D51E }, - { (char*) "Etc/GMT-5" , 0x03D59C }, - { (char*) "Etc/GMT-6" , 0x03D61A }, - { (char*) "Etc/GMT-7" , 0x03D698 }, - { (char*) "Etc/GMT-8" , 0x03D716 }, - { (char*) "Etc/GMT-9" , 0x03D794 }, - { (char*) "Etc/GMT0" , 0x03D812 }, - { (char*) "Etc/Greenwich" , 0x03D88D }, - { (char*) "Etc/UCT" , 0x03D908 }, - { (char*) "Etc/Universal" , 0x03D983 }, - { (char*) "Etc/UTC" , 0x03D9FE }, - { (char*) "Etc/Zulu" , 0x03DA79 }, - { (char*) "Europe/Amsterdam" , 0x03DAF4 }, - { (char*) "Europe/Andorra" , 0x03DF2F }, - { (char*) "Europe/Astrakhan" , 0x03E0C0 }, - { (char*) "Europe/Athens" , 0x03E3B4 }, - { (char*) "Europe/Belfast" , 0x03E66A }, - { (char*) "Europe/Belgrade" , 0x03ECB5 }, - { (char*) "Europe/Berlin" , 0x03EE9F }, - { (char*) "Europe/Bratislava" , 0x03F17B }, - { (char*) "Europe/Brussels" , 0x03F45A }, - { (char*) "Europe/Bucharest" , 0x03F8B5 }, - { (char*) "Europe/Budapest" , 0x03FB56 }, - { (char*) "Europe/Busingen" , 0x03FE60 }, - { (char*) "Europe/Chisinau" , 0x040065 }, - { (char*) "Europe/Copenhagen" , 0x040528 }, - { (char*) "Europe/Dublin" , 0x0407A3 }, - { (char*) "Europe/Gibraltar" , 0x040D87 }, - { (char*) "Europe/Guernsey" , 0x041257 }, - { (char*) "Europe/Helsinki" , 0x0418AE }, - { (char*) "Europe/Isle_of_Man" , 0x041A9B }, - { (char*) "Europe/Istanbul" , 0x0420E6 }, - { (char*) "Europe/Jersey" , 0x0425A2 }, - { (char*) "Europe/Kaliningrad" , 0x042BF9 }, - { (char*) "Europe/Kiev" , 0x042FA1 }, - { (char*) "Europe/Kirov" , 0x0431DB }, - { (char*) "Europe/Kyiv" , 0x0434D4 }, - { (char*) "Europe/Lisbon" , 0x04371D }, - { (char*) "Europe/Ljubljana" , 0x043CF3 }, - { (char*) "Europe/London" , 0x043EDD }, - { (char*) "Europe/Luxembourg" , 0x044528 }, - { (char*) "Europe/Madrid" , 0x044973 }, - { (char*) "Europe/Malta" , 0x044D10 }, - { (char*) "Europe/Mariehamn" , 0x0450BC }, - { (char*) "Europe/Minsk" , 0x0452A9 }, - { (char*) "Europe/Monaco" , 0x0455DD }, - { (char*) "Europe/Moscow" , 0x045A43 }, - { (char*) "Europe/Nicosia" , 0x045DEF }, - { (char*) "Europe/Oslo" , 0x046050 }, - { (char*) "Europe/Paris" , 0x046300 }, - { (char*) "Europe/Podgorica" , 0x04675D }, - { (char*) "Europe/Prague" , 0x046947 }, - { (char*) "Europe/Riga" , 0x046C26 }, - { (char*) "Europe/Rome" , 0x046EE8 }, - { (char*) "Europe/Samara" , 0x0472A7 }, - { (char*) "Europe/San_Marino" , 0x0475A8 }, - { (char*) "Europe/Sarajevo" , 0x047967 }, - { (char*) "Europe/Saratov" , 0x047B51 }, - { (char*) "Europe/Simferopol" , 0x047E43 }, - { (char*) "Europe/Skopje" , 0x0481B6 }, - { (char*) "Europe/Sofia" , 0x0483A0 }, - { (char*) "Europe/Stockholm" , 0x0485FC }, - { (char*) "Europe/Tallinn" , 0x0487F9 }, - { (char*) "Europe/Tirane" , 0x048AA8 }, - { (char*) "Europe/Tiraspol" , 0x048D10 }, - { (char*) "Europe/Ulyanovsk" , 0x0491D3 }, - { (char*) "Europe/Uzhgorod" , 0x0494E9 }, - { (char*) "Europe/Vaduz" , 0x049723 }, - { (char*) "Europe/Vatican" , 0x04990D }, - { (char*) "Europe/Vienna" , 0x049CCC }, - { (char*) "Europe/Vilnius" , 0x049F6A }, - { (char*) "Europe/Volgograd" , 0x04A21A }, - { (char*) "Europe/Warsaw" , 0x04A529 }, - { (char*) "Europe/Zagreb" , 0x04A8D0 }, - { (char*) "Europe/Zaporozhye" , 0x04AABA }, - { (char*) "Europe/Zurich" , 0x04ACF4 }, - { (char*) "Factory" , 0x04AEF1 }, - { (char*) "GB" , 0x04AF6E }, - { (char*) "GB-Eire" , 0x04B5B9 }, - { (char*) "GMT" , 0x04BC04 }, - { (char*) "GMT+0" , 0x04BC7F }, - { (char*) "GMT-0" , 0x04BCFA }, - { (char*) "GMT0" , 0x04BD75 }, - { (char*) "Greenwich" , 0x04BDF0 }, - { (char*) "Hongkong" , 0x04BE6B }, - { (char*) "HST" , 0x04C17E }, - { (char*) "Iceland" , 0x04C267 }, - { (char*) "Indian/Antananarivo" , 0x04C2F5 }, - { (char*) "Indian/Chagos" , 0x04C3A1 }, - { (char*) "Indian/Christmas" , 0x04C445 }, - { (char*) "Indian/Cocos" , 0x04C4D6 }, - { (char*) "Indian/Comoro" , 0x04C56E }, - { (char*) "Indian/Kerguelen" , 0x04C5FD }, - { (char*) "Indian/Mahe" , 0x04C68E }, - { (char*) "Indian/Maldives" , 0x04C71F }, - { (char*) "Indian/Mauritius" , 0x04C7C3 }, - { (char*) "Indian/Mayotte" , 0x04C882 }, - { (char*) "Indian/Reunion" , 0x04C911 }, - { (char*) "Iran" , 0x04C9A2 }, - { (char*) "Israel" , 0x04CCDA }, - { (char*) "Jamaica" , 0x04D118 }, - { (char*) "Japan" , 0x04D277 }, - { (char*) "Kwajalein" , 0x04D358 }, - { (char*) "Libya" , 0x04D43F }, - { (char*) "MET" , 0x04D5FA }, - { (char*) "Mexico/BajaNorte" , 0x04DA55 }, - { (char*) "Mexico/BajaSur" , 0x04DFB8 }, - { (char*) "Mexico/General" , 0x04E276 }, - { (char*) "MST" , 0x04E587 }, - { (char*) "MST7MDT" , 0x04E683 }, - { (char*) "Navajo" , 0x04EAA1 }, - { (char*) "NZ" , 0x04EEBF }, - { (char*) "NZ-CHAT" , 0x04F2DE }, - { (char*) "Pacific/Apia" , 0x04F612 }, - { (char*) "Pacific/Auckland" , 0x04F7B5 }, - { (char*) "Pacific/Bougainville" , 0x04FBE7 }, - { (char*) "Pacific/Chatham" , 0x04FCC8 }, - { (char*) "Pacific/Chuuk" , 0x05000B }, - { (char*) "Pacific/Easter" , 0x0500E9 }, - { (char*) "Pacific/Efate" , 0x050598 }, - { (char*) "Pacific/Enderbury" , 0x0506FA }, - { (char*) "Pacific/Fakaofo" , 0x0507B2 }, - { (char*) "Pacific/Fiji" , 0x050857 }, - { (char*) "Pacific/Funafuti" , 0x0509EF }, - { (char*) "Pacific/Galapagos" , 0x050A81 }, - { (char*) "Pacific/Gambier" , 0x050B4D }, - { (char*) "Pacific/Guadalcanal" , 0x050BEC }, - { (char*) "Pacific/Guam" , 0x050C7E }, - { (char*) "Pacific/Honolulu" , 0x050DE8 }, - { (char*) "Pacific/Johnston" , 0x050ED7 }, - { (char*) "Pacific/Kanton" , 0x050FC0 }, - { (char*) "Pacific/Kiritimati" , 0x051087 }, - { (char*) "Pacific/Kosrae" , 0x05114D }, - { (char*) "Pacific/Kwajalein" , 0x051251 }, - { (char*) "Pacific/Majuro" , 0x051341 }, - { (char*) "Pacific/Marquesas" , 0x05143F }, - { (char*) "Pacific/Midway" , 0x0514E7 }, - { (char*) "Pacific/Nauru" , 0x0515AA }, - { (char*) "Pacific/Niue" , 0x05166D }, - { (char*) "Pacific/Norfolk" , 0x051713 }, - { (char*) "Pacific/Noumea" , 0x05180C }, - { (char*) "Pacific/Pago_Pago" , 0x0518DE }, - { (char*) "Pacific/Palau" , 0x05197C }, - { (char*) "Pacific/Pitcairn" , 0x051A1C }, - { (char*) "Pacific/Pohnpei" , 0x051AC1 }, - { (char*) "Pacific/Ponape" , 0x051BB1 }, - { (char*) "Pacific/Port_Moresby" , 0x051C43 }, - { (char*) "Pacific/Rarotonga" , 0x051D01 }, - { (char*) "Pacific/Saipan" , 0x051EA3 }, - { (char*) "Pacific/Samoa" , 0x052004 }, - { (char*) "Pacific/Tahiti" , 0x0520A2 }, - { (char*) "Pacific/Tarawa" , 0x052142 }, - { (char*) "Pacific/Tongatapu" , 0x0521E3 }, - { (char*) "Pacific/Truk" , 0x0522DC }, - { (char*) "Pacific/Wake" , 0x052382 }, - { (char*) "Pacific/Wallis" , 0x05241F }, - { (char*) "Pacific/Yap" , 0x0524B1 }, - { (char*) "Poland" , 0x052557 }, - { (char*) "Portugal" , 0x0528FE }, - { (char*) "PRC" , 0x052EC1 }, - { (char*) "PST8PDT" , 0x053056 }, - { (char*) "ROC" , 0x053570 }, - { (char*) "ROK" , 0x05377B }, - { (char*) "Singapore" , 0x053926 }, - { (char*) "Turkey" , 0x053A32 }, - { (char*) "UCT" , 0x053EEE }, - { (char*) "Universal" , 0x053F69 }, - { (char*) "US/Alaska" , 0x053FE4 }, - { (char*) "US/Aleutian" , 0x0543C1 }, - { (char*) "US/Arizona" , 0x054796 }, - { (char*) "US/Central" , 0x054892 }, - { (char*) "US/East-Indiana" , 0x054F78 }, - { (char*) "US/Eastern" , 0x055197 }, - { (char*) "US/Hawaii" , 0x055873 }, - { (char*) "US/Indiana-Starke" , 0x05595C }, - { (char*) "US/Michigan" , 0x055D60 }, - { (char*) "US/Mountain" , 0x0560EF }, - { (char*) "US/Pacific" , 0x05650D }, - { (char*) "US/Samoa" , 0x056A27 }, - { (char*) "UTC" , 0x056AC5 }, - { (char*) "W-SU" , 0x056B40 }, - { (char*) "WET" , 0x056ED8 }, - { (char*) "Zulu" , 0x05749B }, + { (char*) "Africa/Ceuta" , 0x001407 }, + { (char*) "Africa/Conakry" , 0x001653 }, + { (char*) "Africa/Dakar" , 0x0016FD }, + { (char*) "Africa/Dar_es_Salaam" , 0x00179E }, + { (char*) "Africa/Djibouti" , 0x00184B }, + { (char*) "Africa/Douala" , 0x0018DA }, + { (char*) "Africa/El_Aaiun" , 0x001969 }, + { (char*) "Africa/Freetown" , 0x001C35 }, + { (char*) "Africa/Gaborone" , 0x001D85 }, + { (char*) "Africa/Harare" , 0x001E45 }, + { (char*) "Africa/Johannesburg" , 0x001ED4 }, + { (char*) "Africa/Juba" , 0x001F9E }, + { (char*) "Africa/Kampala" , 0x002174 }, + { (char*) "Africa/Khartoum" , 0x002236 }, + { (char*) "Africa/Kigali" , 0x00240C }, + { (char*) "Africa/Kinshasa" , 0x00249B }, + { (char*) "Africa/Lagos" , 0x002543 }, + { (char*) "Africa/Libreville" , 0x002603 }, + { (char*) "Africa/Lome" , 0x002692 }, + { (char*) "Africa/Luanda" , 0x002720 }, + { (char*) "Africa/Lubumbashi" , 0x0027BE }, + { (char*) "Africa/Lusaka" , 0x002879 }, + { (char*) "Africa/Malabo" , 0x002908 }, + { (char*) "Africa/Maputo" , 0x0029AA }, + { (char*) "Africa/Maseru" , 0x002A39 }, + { (char*) "Africa/Mbabane" , 0x002AE2 }, + { (char*) "Africa/Mogadishu" , 0x002B73 }, + { (char*) "Africa/Monrovia" , 0x002C20 }, + { (char*) "Africa/Nairobi" , 0x002CD0 }, + { (char*) "Africa/Ndjamena" , 0x002D9B }, + { (char*) "Africa/Niamey" , 0x002E47 }, + { (char*) "Africa/Nouakchott" , 0x002EFC }, + { (char*) "Africa/Ouagadougou" , 0x002FA6 }, + { (char*) "Africa/Porto-Novo" , 0x003034 }, + { (char*) "Africa/Sao_Tome" , 0x0030D6 }, + { (char*) "Africa/Timbuktu" , 0x00318F }, + { (char*) "Africa/Tripoli" , 0x00321D }, + { (char*) "Africa/Tunis" , 0x0033D8 }, + { (char*) "Africa/Windhoek" , 0x0035A5 }, + { (char*) "America/Adak" , 0x00382F }, + { (char*) "America/Anchorage" , 0x003C1E }, + { (char*) "America/Anguilla" , 0x00400E }, + { (char*) "America/Antigua" , 0x00409C }, + { (char*) "America/Araguaina" , 0x00413D }, + { (char*) "America/Argentina/Buenos_Aires" , 0x0043A2 }, + { (char*) "America/Argentina/Catamarca" , 0x004687 }, + { (char*) "America/Argentina/ComodRivadavia" , 0x004972 }, + { (char*) "America/Argentina/Cordoba" , 0x004C42 }, + { (char*) "America/Argentina/Jujuy" , 0x004F48 }, + { (char*) "America/Argentina/La_Rioja" , 0x005210 }, + { (char*) "America/Argentina/Mendoza" , 0x0054F6 }, + { (char*) "America/Argentina/Rio_Gallegos" , 0x0057D2 }, + { (char*) "America/Argentina/Salta" , 0x005AB1 }, + { (char*) "America/Argentina/San_Juan" , 0x005D85 }, + { (char*) "America/Argentina/San_Luis" , 0x00606B }, + { (char*) "America/Argentina/Tucuman" , 0x006351 }, + { (char*) "America/Argentina/Ushuaia" , 0x00663F }, + { (char*) "America/Aruba" , 0x006924 }, + { (char*) "America/Asuncion" , 0x0069C7 }, + { (char*) "America/Atikokan" , 0x006E10 }, + { (char*) "America/Atka" , 0x006F1D }, + { (char*) "America/Bahia" , 0x0072F2 }, + { (char*) "America/Bahia_Banderas" , 0x0075AD }, + { (char*) "America/Barbados" , 0x007886 }, + { (char*) "America/Belem" , 0x0079A8 }, + { (char*) "America/Belize" , 0x007B50 }, + { (char*) "America/Blanc-Sablon" , 0x007F71 }, + { (char*) "America/Boa_Vista" , 0x008066 }, + { (char*) "America/Bogota" , 0x008227 }, + { (char*) "America/Boise" , 0x0082E6 }, + { (char*) "America/Buenos_Aires" , 0x0086F9 }, + { (char*) "America/Cambridge_Bay" , 0x0089C9 }, + { (char*) "America/Campo_Grande" , 0x008D5C }, + { (char*) "America/Cancun" , 0x009132 }, + { (char*) "America/Caracas" , 0x009364 }, + { (char*) "America/Catamarca" , 0x00942E }, + { (char*) "America/Cayenne" , 0x0096FE }, + { (char*) "America/Cayman" , 0x0097A1 }, + { (char*) "America/Chicago" , 0x009842 }, + { (char*) "America/Chihuahua" , 0x009F3C }, + { (char*) "America/Ciudad_Juarez" , 0x00A211 }, + { (char*) "America/Coral_Harbour" , 0x00A507 }, + { (char*) "America/Cordoba" , 0x00A5A8 }, + { (char*) "America/Costa_Rica" , 0x00A878 }, + { (char*) "America/Coyhaique" , 0x00A96C }, + { (char*) "America/Creston" , 0x00AED6 }, + { (char*) "America/Cuiaba" , 0x00AF92 }, + { (char*) "America/Curacao" , 0x00B34F }, + { (char*) "America/Danmarkshavn" , 0x00B3F2 }, + { (char*) "America/Dawson" , 0x00B5D7 }, + { (char*) "America/Dawson_Creek" , 0x00B9FA }, + { (char*) "America/Denver" , 0x00BCD1 }, + { (char*) "America/Detroit" , 0x00C104 }, + { (char*) "America/Dominica" , 0x00C4AC }, + { (char*) "America/Edmonton" , 0x00C53A }, + { (char*) "America/Eirunepe" , 0x00CA84 }, + { (char*) "America/El_Salvador" , 0x00CC53 }, + { (char*) "America/Ensenada" , 0x00CD0F }, + { (char*) "America/Fort_Nelson" , 0x00D272 }, + { (char*) "America/Fort_Wayne" , 0x00D83A }, + { (char*) "America/Fortaleza" , 0x00DA59 }, + { (char*) "America/Glace_Bay" , 0x00DC6F }, + { (char*) "America/Godthab" , 0x00E006 }, + { (char*) "America/Goose_Bay" , 0x00E3D7 }, + { (char*) "America/Grand_Turk" , 0x00EA2F }, + { (char*) "America/Grenada" , 0x00ED90 }, + { (char*) "America/Guadeloupe" , 0x00EE1E }, + { (char*) "America/Guatemala" , 0x00EEAC }, + { (char*) "America/Guayaquil" , 0x00EF8C }, + { (char*) "America/Guyana" , 0x00F05D }, + { (char*) "America/Halifax" , 0x00F11E }, + { (char*) "America/Havana" , 0x00F7D0 }, + { (char*) "America/Hermosillo" , 0x00FC39 }, + { (char*) "America/Indiana/Indianapolis" , 0x00FD4D }, + { (char*) "America/Indiana/Knox" , 0x00FF85 }, + { (char*) "America/Indiana/Marengo" , 0x01039E }, + { (char*) "America/Indiana/Petersburg" , 0x0105F8 }, + { (char*) "America/Indiana/Tell_City" , 0x0108C2 }, + { (char*) "America/Indiana/Vevay" , 0x010AEC }, + { (char*) "America/Indiana/Vincennes" , 0x010C83 }, + { (char*) "America/Indiana/Winamac" , 0x010ED9 }, + { (char*) "America/Indianapolis" , 0x011156 }, + { (char*) "America/Inuvik" , 0x011375 }, + { (char*) "America/Iqaluit" , 0x0116C6 }, + { (char*) "America/Jamaica" , 0x011A42 }, + { (char*) "America/Jujuy" , 0x011BA1 }, + { (char*) "America/Juneau" , 0x011E5F }, + { (char*) "America/Kentucky/Louisville" , 0x012245 }, + { (char*) "America/Kentucky/Monticello" , 0x012749 }, + { (char*) "America/Knox_IN" , 0x012B35 }, + { (char*) "America/Kralendijk" , 0x012F39 }, + { (char*) "America/La_Paz" , 0x012FF6 }, + { (char*) "America/Lima" , 0x0130AC }, + { (char*) "America/Los_Angeles" , 0x0131D3 }, + { (char*) "America/Louisville" , 0x0136F4 }, + { (char*) "America/Lower_Princes" , 0x013BDA }, + { (char*) "America/Maceio" , 0x013C97 }, + { (char*) "America/Managua" , 0x013EA9 }, + { (char*) "America/Manaus" , 0x013FDC }, + { (char*) "America/Marigot" , 0x014193 }, + { (char*) "America/Martinique" , 0x014250 }, + { (char*) "America/Matamoros" , 0x01430E }, + { (char*) "America/Mazatlan" , 0x0144FB }, + { (char*) "America/Mendoza" , 0x0147EB }, + { (char*) "America/Menominee" , 0x014ABB }, + { (char*) "America/Merida" , 0x014E7B }, + { (char*) "America/Metlakatla" , 0x015126 }, + { (char*) "America/Mexico_City" , 0x015393 }, + { (char*) "America/Miquelon" , 0x0156B2 }, + { (char*) "America/Moncton" , 0x0158E4 }, + { (char*) "America/Monterrey" , 0x015EDD }, + { (char*) "America/Montevideo" , 0x0161E4 }, + { (char*) "America/Montreal" , 0x0165B9 }, + { (char*) "America/Montserrat" , 0x016C7A }, + { (char*) "America/Nassau" , 0x016D08 }, + { (char*) "America/New_York" , 0x017102 }, + { (char*) "America/Nipigon" , 0x0177F2 }, + { (char*) "America/Nome" , 0x017EB3 }, + { (char*) "America/Noronha" , 0x01829B }, + { (char*) "America/North_Dakota/Beulah" , 0x01849B }, + { (char*) "America/North_Dakota/Center" , 0x0188CF }, + { (char*) "America/North_Dakota/New_Salem" , 0x018CCE }, + { (char*) "America/Nuuk" , 0x0190D3 }, + { (char*) "America/Ojinaga" , 0x0194B5 }, + { (char*) "America/Panama" , 0x0197AB }, + { (char*) "America/Pangnirtung" , 0x01984C }, + { (char*) "America/Paramaribo" , 0x019BAF }, + { (char*) "America/Phoenix" , 0x019C76 }, + { (char*) "America/Port-au-Prince" , 0x019D8A }, + { (char*) "America/Port_of_Spain" , 0x019FCB }, + { (char*) "America/Porto_Acre" , 0x01A059 }, + { (char*) "America/Porto_Velho" , 0x01A207 }, + { (char*) "America/Puerto_Rico" , 0x01A3A5 }, + { (char*) "America/Punta_Arenas" , 0x01A462 }, + { (char*) "America/Rainy_River" , 0x01A941 }, + { (char*) "America/Rankin_Inlet" , 0x01AE5B }, + { (char*) "America/Recife" , 0x01B1A4 }, + { (char*) "America/Regina" , 0x01B39E }, + { (char*) "America/Resolute" , 0x01B63D }, + { (char*) "America/Rio_Branco" , 0x01B987 }, + { (char*) "America/Rosario" , 0x01BB39 }, + { (char*) "America/Santa_Isabel" , 0x01BE09 }, + { (char*) "America/Santarem" , 0x01C36C }, + { (char*) "America/Santiago" , 0x01C51C }, + { (char*) "America/Santo_Domingo" , 0x01CA7F }, + { (char*) "America/Sao_Paulo" , 0x01CBC8 }, + { (char*) "America/Scoresbysund" , 0x01CFC2 }, + { (char*) "America/Shiprock" , 0x01D3C3 }, + { (char*) "America/Sitka" , 0x01D7E1 }, + { (char*) "America/St_Barthelemy" , 0x01DBBC }, + { (char*) "America/St_Johns" , 0x01DC79 }, + { (char*) "America/St_Kitts" , 0x01E3F6 }, + { (char*) "America/St_Lucia" , 0x01E484 }, + { (char*) "America/St_Thomas" , 0x01E525 }, + { (char*) "America/St_Vincent" , 0x01E5B3 }, + { (char*) "America/Swift_Current" , 0x01E654 }, + { (char*) "America/Tegucigalpa" , 0x01E7E2 }, + { (char*) "America/Thule" , 0x01E8B0 }, + { (char*) "America/Thunder_Bay" , 0x01EA91 }, + { (char*) "America/Tijuana" , 0x01F152 }, + { (char*) "America/Toronto" , 0x01F6C4 }, + { (char*) "America/Tortola" , 0x01FDA3 }, + { (char*) "America/Vancouver" , 0x01FE31 }, + { (char*) "America/Virgin" , 0x0204DB }, + { (char*) "America/Whitehorse" , 0x020598 }, + { (char*) "America/Winnipeg" , 0x0209BB }, + { (char*) "America/Yakutat" , 0x020EF2 }, + { (char*) "America/Yellowknife" , 0x0212C0 }, + { (char*) "Antarctica/Casey" , 0x0217ED }, + { (char*) "Antarctica/Davis" , 0x02191D }, + { (char*) "Antarctica/DumontDUrville" , 0x0219F3 }, + { (char*) "Antarctica/Macquarie" , 0x021AA7 }, + { (char*) "Antarctica/Mawson" , 0x021E93 }, + { (char*) "Antarctica/McMurdo" , 0x021F3D }, + { (char*) "Antarctica/Palmer" , 0x02226F }, + { (char*) "Antarctica/Rothera" , 0x0225F8 }, + { (char*) "Antarctica/South_Pole" , 0x02268F }, + { (char*) "Antarctica/Syowa" , 0x022AAE }, + { (char*) "Antarctica/Troll" , 0x022B44 }, + { (char*) "Antarctica/Vostok" , 0x022BF3 }, + { (char*) "Arctic/Longyearbyen" , 0x022CAF }, + { (char*) "Asia/Aden" , 0x022F7C }, + { (char*) "Asia/Almaty" , 0x02300D }, + { (char*) "Asia/Amman" , 0x023295 }, + { (char*) "Asia/Anadyr" , 0x023641 }, + { (char*) "Asia/Aqtau" , 0x023947 }, + { (char*) "Asia/Aqtobe" , 0x023BC6 }, + { (char*) "Asia/Ashgabat" , 0x023E46 }, + { (char*) "Asia/Ashkhabad" , 0x023FC9 }, + { (char*) "Asia/Atyrau" , 0x02414C }, + { (char*) "Asia/Baghdad" , 0x0243D5 }, + { (char*) "Asia/Bahrain" , 0x024657 }, + { (char*) "Asia/Baku" , 0x024710 }, + { (char*) "Asia/Bangkok" , 0x024A04 }, + { (char*) "Asia/Barnaul" , 0x024AA8 }, + { (char*) "Asia/Beirut" , 0x024DB3 }, + { (char*) "Asia/Bishkek" , 0x02509B }, + { (char*) "Asia/Brunei" , 0x025311 }, + { (char*) "Asia/Calcutta" , 0x0253B7 }, + { (char*) "Asia/Chita" , 0x02549F }, + { (char*) "Asia/Choibalsan" , 0x0257AD }, + { (char*) "Asia/Chongqing" , 0x025A0B }, + { (char*) "Asia/Chungking" , 0x025BA0 }, + { (char*) "Asia/Colombo" , 0x025D35 }, + { (char*) "Asia/Dacca" , 0x025E38 }, + { (char*) "Asia/Damascus" , 0x025F2B }, + { (char*) "Asia/Dhaka" , 0x026409 }, + { (char*) "Asia/Dili" , 0x0264FC }, + { (char*) "Asia/Dubai" , 0x0265B2 }, + { (char*) "Asia/Dushanbe" , 0x026643 }, + { (char*) "Asia/Famagusta" , 0x0267BD }, + { (char*) "Asia/Gaza" , 0x026B84 }, + { (char*) "Asia/Harbin" , 0x027720 }, + { (char*) "Asia/Hebron" , 0x0278B5 }, + { (char*) "Asia/Ho_Chi_Minh" , 0x028462 }, + { (char*) "Asia/Hong_Kong" , 0x028556 }, + { (char*) "Asia/Hovd" , 0x028869 }, + { (char*) "Asia/Irkutsk" , 0x028ADD }, + { (char*) "Asia/Istanbul" , 0x028DFB }, + { (char*) "Asia/Jakarta" , 0x0292B7 }, + { (char*) "Asia/Jayapura" , 0x0293C8 }, + { (char*) "Asia/Jerusalem" , 0x0294B5 }, + { (char*) "Asia/Kabul" , 0x0298F3 }, + { (char*) "Asia/Kamchatka" , 0x02999E }, + { (char*) "Asia/Karachi" , 0x029C93 }, + { (char*) "Asia/Kashgar" , 0x029DA9 }, + { (char*) "Asia/Kathmandu" , 0x029E3A }, + { (char*) "Asia/Katmandu" , 0x029EE7 }, + { (char*) "Asia/Khandyga" , 0x029F94 }, + { (char*) "Asia/Kolkata" , 0x02A2C5 }, + { (char*) "Asia/Krasnoyarsk" , 0x02A3AD }, + { (char*) "Asia/Kuala_Lumpur" , 0x02A6B7 }, + { (char*) "Asia/Kuching" , 0x02A7D7 }, + { (char*) "Asia/Kuwait" , 0x02A931 }, + { (char*) "Asia/Macao" , 0x02A9C2 }, + { (char*) "Asia/Macau" , 0x02ACE5 }, + { (char*) "Asia/Magadan" , 0x02B008 }, + { (char*) "Asia/Makassar" , 0x02B313 }, + { (char*) "Asia/Manila" , 0x02B426 }, + { (char*) "Asia/Muscat" , 0x02B544 }, + { (char*) "Asia/Nicosia" , 0x02B5D5 }, + { (char*) "Asia/Novokuznetsk" , 0x02B844 }, + { (char*) "Asia/Novosibirsk" , 0x02BB37 }, + { (char*) "Asia/Omsk" , 0x02BE48 }, + { (char*) "Asia/Oral" , 0x02C146 }, + { (char*) "Asia/Phnom_Penh" , 0x02C3D2 }, + { (char*) "Asia/Pontianak" , 0x02C4A2 }, + { (char*) "Asia/Pyongyang" , 0x02C5BB }, + { (char*) "Asia/Qatar" , 0x02C67E }, + { (char*) "Asia/Qostanay" , 0x02C722 }, + { (char*) "Asia/Qyzylorda" , 0x02C9B8 }, + { (char*) "Asia/Rangoon" , 0x02CC51 }, + { (char*) "Asia/Riyadh" , 0x02CD18 }, + { (char*) "Asia/Saigon" , 0x02CDA9 }, + { (char*) "Asia/Sakhalin" , 0x02CE9D }, + { (char*) "Asia/Samarkand" , 0x02D1B4 }, + { (char*) "Asia/Seoul" , 0x02D33F }, + { (char*) "Asia/Shanghai" , 0x02D4EA }, + { (char*) "Asia/Singapore" , 0x02D68B }, + { (char*) "Asia/Srednekolymsk" , 0x02D797 }, + { (char*) "Asia/Taipei" , 0x02DAA7 }, + { (char*) "Asia/Tashkent" , 0x02DCB2 }, + { (char*) "Asia/Tbilisi" , 0x02DE3D }, + { (char*) "Asia/Tehran" , 0x02E0B5 }, + { (char*) "Asia/Tel_Aviv" , 0x02E3ED }, + { (char*) "Asia/Thimbu" , 0x02E82B }, + { (char*) "Asia/Thimphu" , 0x02E8D1 }, + { (char*) "Asia/Tokyo" , 0x02E977 }, + { (char*) "Asia/Tomsk" , 0x02EA58 }, + { (char*) "Asia/Ujung_Pandang" , 0x02ED63 }, + { (char*) "Asia/Ulaanbaatar" , 0x02EE2D }, + { (char*) "Asia/Ulan_Bator" , 0x02F09B }, + { (char*) "Asia/Urumqi" , 0x02F2F9 }, + { (char*) "Asia/Ust-Nera" , 0x02F397 }, + { (char*) "Asia/Vientiane" , 0x02F6BA }, + { (char*) "Asia/Vladivostok" , 0x02F79C }, + { (char*) "Asia/Yakutsk" , 0x02FAA1 }, + { (char*) "Asia/Yangon" , 0x02FDA5 }, + { (char*) "Asia/Yekaterinburg" , 0x02FE6C }, + { (char*) "Asia/Yerevan" , 0x03017E }, + { (char*) "Atlantic/Azores" , 0x03044E }, + { (char*) "Atlantic/Bermuda" , 0x0309D9 }, + { (char*) "Atlantic/Canary" , 0x030DE5 }, + { (char*) "Atlantic/Cape_Verde" , 0x030FDD }, + { (char*) "Atlantic/Faeroe" , 0x031098 }, + { (char*) "Atlantic/Faroe" , 0x03125D }, + { (char*) "Atlantic/Jan_Mayen" , 0x031422 }, + { (char*) "Atlantic/Madeira" , 0x0316EF }, + { (char*) "Atlantic/Reykjavik" , 0x031C66 }, + { (char*) "Atlantic/South_Georgia" , 0x031F63 }, + { (char*) "Atlantic/St_Helena" , 0x031FF3 }, + { (char*) "Atlantic/Stanley" , 0x032094 }, + { (char*) "Australia/ACT" , 0x0323B5 }, + { (char*) "Australia/Adelaide" , 0x032749 }, + { (char*) "Australia/Brisbane" , 0x032AFD }, + { (char*) "Australia/Broken_Hill" , 0x032C41 }, + { (char*) "Australia/Canberra" , 0x033016 }, + { (char*) "Australia/Currie" , 0x0333AA }, + { (char*) "Australia/Darwin" , 0x0337A1 }, + { (char*) "Australia/Eucla" , 0x0338A9 }, + { (char*) "Australia/Hobart" , 0x033A08 }, + { (char*) "Australia/LHI" , 0x033E07 }, + { (char*) "Australia/Lindeman" , 0x0340C7 }, + { (char*) "Australia/Lord_Howe" , 0x034237 }, + { (char*) "Australia/Melbourne" , 0x034507 }, + { (char*) "Australia/North" , 0x0348A3 }, + { (char*) "Australia/NSW" , 0x034999 }, + { (char*) "Australia/Perth" , 0x034D2D }, + { (char*) "Australia/Queensland" , 0x034E89 }, + { (char*) "Australia/South" , 0x034FB6 }, + { (char*) "Australia/Sydney" , 0x03535B }, + { (char*) "Australia/Tasmania" , 0x03570B }, + { (char*) "Australia/Victoria" , 0x035B02 }, + { (char*) "Australia/West" , 0x035E96 }, + { (char*) "Australia/Yancowinna" , 0x035FD4 }, + { (char*) "Brazil/Acre" , 0x03638D }, + { (char*) "Brazil/DeNoronha" , 0x03653B }, + { (char*) "Brazil/East" , 0x03672B }, + { (char*) "Brazil/West" , 0x036AEF }, + { (char*) "Canada/Atlantic" , 0x036C97 }, + { (char*) "Canada/Central" , 0x03732B }, + { (char*) "Canada/Eastern" , 0x037845 }, + { (char*) "Canada/Mountain" , 0x037F06 }, + { (char*) "Canada/Newfoundland" , 0x038433 }, + { (char*) "Canada/Pacific" , 0x038B95 }, + { (char*) "Canada/Saskatchewan" , 0x03922A }, + { (char*) "Canada/Yukon" , 0x0394B4 }, + { (char*) "CET" , 0x0398C5 }, + { (char*) "Chile/Continental" , 0x039D20 }, + { (char*) "Chile/EasterIsland" , 0x03A276 }, + { (char*) "CST6CDT" , 0x03A718 }, + { (char*) "Cuba" , 0x03ADFE }, + { (char*) "EET" , 0x03B267 }, + { (char*) "Egypt" , 0x03B51D }, + { (char*) "Eire" , 0x03BA46 }, + { (char*) "EST" , 0x03C02A }, + { (char*) "EST5EDT" , 0x03C0CB }, + { (char*) "Etc/GMT" , 0x03C7A7 }, + { (char*) "Etc/GMT+0" , 0x03C822 }, + { (char*) "Etc/GMT+1" , 0x03C89D }, + { (char*) "Etc/GMT+10" , 0x03C91A }, + { (char*) "Etc/GMT+11" , 0x03C998 }, + { (char*) "Etc/GMT+12" , 0x03CA16 }, + { (char*) "Etc/GMT+2" , 0x03CA94 }, + { (char*) "Etc/GMT+3" , 0x03CB11 }, + { (char*) "Etc/GMT+4" , 0x03CB8E }, + { (char*) "Etc/GMT+5" , 0x03CC0B }, + { (char*) "Etc/GMT+6" , 0x03CC88 }, + { (char*) "Etc/GMT+7" , 0x03CD05 }, + { (char*) "Etc/GMT+8" , 0x03CD82 }, + { (char*) "Etc/GMT+9" , 0x03CDFF }, + { (char*) "Etc/GMT-0" , 0x03CE7C }, + { (char*) "Etc/GMT-1" , 0x03CEF7 }, + { (char*) "Etc/GMT-10" , 0x03CF75 }, + { (char*) "Etc/GMT-11" , 0x03CFF4 }, + { (char*) "Etc/GMT-12" , 0x03D073 }, + { (char*) "Etc/GMT-13" , 0x03D0F2 }, + { (char*) "Etc/GMT-14" , 0x03D171 }, + { (char*) "Etc/GMT-2" , 0x03D1F0 }, + { (char*) "Etc/GMT-3" , 0x03D26E }, + { (char*) "Etc/GMT-4" , 0x03D2EC }, + { (char*) "Etc/GMT-5" , 0x03D36A }, + { (char*) "Etc/GMT-6" , 0x03D3E8 }, + { (char*) "Etc/GMT-7" , 0x03D466 }, + { (char*) "Etc/GMT-8" , 0x03D4E4 }, + { (char*) "Etc/GMT-9" , 0x03D562 }, + { (char*) "Etc/GMT0" , 0x03D5E0 }, + { (char*) "Etc/Greenwich" , 0x03D65B }, + { (char*) "Etc/UCT" , 0x03D6D6 }, + { (char*) "Etc/Universal" , 0x03D751 }, + { (char*) "Etc/UTC" , 0x03D7CC }, + { (char*) "Etc/Zulu" , 0x03D847 }, + { (char*) "Europe/Amsterdam" , 0x03D8C2 }, + { (char*) "Europe/Andorra" , 0x03DCFD }, + { (char*) "Europe/Astrakhan" , 0x03DE8E }, + { (char*) "Europe/Athens" , 0x03E182 }, + { (char*) "Europe/Belfast" , 0x03E438 }, + { (char*) "Europe/Belgrade" , 0x03EA83 }, + { (char*) "Europe/Berlin" , 0x03EC6D }, + { (char*) "Europe/Bratislava" , 0x03EF49 }, + { (char*) "Europe/Brussels" , 0x03F228 }, + { (char*) "Europe/Bucharest" , 0x03F683 }, + { (char*) "Europe/Budapest" , 0x03F924 }, + { (char*) "Europe/Busingen" , 0x03FC2E }, + { (char*) "Europe/Chisinau" , 0x03FE33 }, + { (char*) "Europe/Copenhagen" , 0x0402F6 }, + { (char*) "Europe/Dublin" , 0x040571 }, + { (char*) "Europe/Gibraltar" , 0x040B55 }, + { (char*) "Europe/Guernsey" , 0x041025 }, + { (char*) "Europe/Helsinki" , 0x04167C }, + { (char*) "Europe/Isle_of_Man" , 0x041869 }, + { (char*) "Europe/Istanbul" , 0x041EB4 }, + { (char*) "Europe/Jersey" , 0x042370 }, + { (char*) "Europe/Kaliningrad" , 0x0429C7 }, + { (char*) "Europe/Kiev" , 0x042D6F }, + { (char*) "Europe/Kirov" , 0x042FA9 }, + { (char*) "Europe/Kyiv" , 0x0432A2 }, + { (char*) "Europe/Lisbon" , 0x0434EB }, + { (char*) "Europe/Ljubljana" , 0x043AC1 }, + { (char*) "Europe/London" , 0x043CAB }, + { (char*) "Europe/Luxembourg" , 0x0442F6 }, + { (char*) "Europe/Madrid" , 0x044741 }, + { (char*) "Europe/Malta" , 0x044ADE }, + { (char*) "Europe/Mariehamn" , 0x044E8A }, + { (char*) "Europe/Minsk" , 0x045077 }, + { (char*) "Europe/Monaco" , 0x0453AB }, + { (char*) "Europe/Moscow" , 0x045811 }, + { (char*) "Europe/Nicosia" , 0x045BBD }, + { (char*) "Europe/Oslo" , 0x045E1E }, + { (char*) "Europe/Paris" , 0x0460CE }, + { (char*) "Europe/Podgorica" , 0x04652B }, + { (char*) "Europe/Prague" , 0x046715 }, + { (char*) "Europe/Riga" , 0x0469F4 }, + { (char*) "Europe/Rome" , 0x046CB6 }, + { (char*) "Europe/Samara" , 0x047075 }, + { (char*) "Europe/San_Marino" , 0x047376 }, + { (char*) "Europe/Sarajevo" , 0x047735 }, + { (char*) "Europe/Saratov" , 0x04791F }, + { (char*) "Europe/Simferopol" , 0x047C11 }, + { (char*) "Europe/Skopje" , 0x047F84 }, + { (char*) "Europe/Sofia" , 0x04816E }, + { (char*) "Europe/Stockholm" , 0x0483CA }, + { (char*) "Europe/Tallinn" , 0x0485C7 }, + { (char*) "Europe/Tirane" , 0x048876 }, + { (char*) "Europe/Tiraspol" , 0x048ADE }, + { (char*) "Europe/Ulyanovsk" , 0x048FA1 }, + { (char*) "Europe/Uzhgorod" , 0x0492B7 }, + { (char*) "Europe/Vaduz" , 0x0494F1 }, + { (char*) "Europe/Vatican" , 0x0496DB }, + { (char*) "Europe/Vienna" , 0x049A9A }, + { (char*) "Europe/Vilnius" , 0x049D38 }, + { (char*) "Europe/Volgograd" , 0x049FE8 }, + { (char*) "Europe/Warsaw" , 0x04A2F7 }, + { (char*) "Europe/Zagreb" , 0x04A69E }, + { (char*) "Europe/Zaporozhye" , 0x04A888 }, + { (char*) "Europe/Zurich" , 0x04AAC2 }, + { (char*) "Factory" , 0x04ACBF }, + { (char*) "GB" , 0x04AD3C }, + { (char*) "GB-Eire" , 0x04B387 }, + { (char*) "GMT" , 0x04B9D2 }, + { (char*) "GMT+0" , 0x04BA4D }, + { (char*) "GMT-0" , 0x04BAC8 }, + { (char*) "GMT0" , 0x04BB43 }, + { (char*) "Greenwich" , 0x04BBBE }, + { (char*) "Hongkong" , 0x04BC39 }, + { (char*) "HST" , 0x04BF4C }, + { (char*) "Iceland" , 0x04C035 }, + { (char*) "Indian/Antananarivo" , 0x04C0C3 }, + { (char*) "Indian/Chagos" , 0x04C16F }, + { (char*) "Indian/Christmas" , 0x04C213 }, + { (char*) "Indian/Cocos" , 0x04C2A4 }, + { (char*) "Indian/Comoro" , 0x04C33C }, + { (char*) "Indian/Kerguelen" , 0x04C3CB }, + { (char*) "Indian/Mahe" , 0x04C45C }, + { (char*) "Indian/Maldives" , 0x04C4ED }, + { (char*) "Indian/Mauritius" , 0x04C591 }, + { (char*) "Indian/Mayotte" , 0x04C650 }, + { (char*) "Indian/Reunion" , 0x04C6DF }, + { (char*) "Iran" , 0x04C770 }, + { (char*) "Israel" , 0x04CAA8 }, + { (char*) "Jamaica" , 0x04CEE6 }, + { (char*) "Japan" , 0x04D045 }, + { (char*) "Kwajalein" , 0x04D126 }, + { (char*) "Libya" , 0x04D20D }, + { (char*) "MET" , 0x04D3C8 }, + { (char*) "Mexico/BajaNorte" , 0x04D823 }, + { (char*) "Mexico/BajaSur" , 0x04DD86 }, + { (char*) "Mexico/General" , 0x04E044 }, + { (char*) "MST" , 0x04E355 }, + { (char*) "MST7MDT" , 0x04E451 }, + { (char*) "Navajo" , 0x04E86F }, + { (char*) "NZ" , 0x04EC8D }, + { (char*) "NZ-CHAT" , 0x04F0AC }, + { (char*) "Pacific/Apia" , 0x04F3E0 }, + { (char*) "Pacific/Auckland" , 0x04F583 }, + { (char*) "Pacific/Bougainville" , 0x04F9B5 }, + { (char*) "Pacific/Chatham" , 0x04FA96 }, + { (char*) "Pacific/Chuuk" , 0x04FDD9 }, + { (char*) "Pacific/Easter" , 0x04FEB7 }, + { (char*) "Pacific/Efate" , 0x050366 }, + { (char*) "Pacific/Enderbury" , 0x0504C8 }, + { (char*) "Pacific/Fakaofo" , 0x050580 }, + { (char*) "Pacific/Fiji" , 0x050625 }, + { (char*) "Pacific/Funafuti" , 0x0507BD }, + { (char*) "Pacific/Galapagos" , 0x05084F }, + { (char*) "Pacific/Gambier" , 0x05091B }, + { (char*) "Pacific/Guadalcanal" , 0x0509BA }, + { (char*) "Pacific/Guam" , 0x050A4C }, + { (char*) "Pacific/Honolulu" , 0x050BB6 }, + { (char*) "Pacific/Johnston" , 0x050CA5 }, + { (char*) "Pacific/Kanton" , 0x050D8E }, + { (char*) "Pacific/Kiritimati" , 0x050E55 }, + { (char*) "Pacific/Kosrae" , 0x050F1B }, + { (char*) "Pacific/Kwajalein" , 0x05101F }, + { (char*) "Pacific/Majuro" , 0x05110F }, + { (char*) "Pacific/Marquesas" , 0x05120D }, + { (char*) "Pacific/Midway" , 0x0512B5 }, + { (char*) "Pacific/Nauru" , 0x051378 }, + { (char*) "Pacific/Niue" , 0x05143B }, + { (char*) "Pacific/Norfolk" , 0x0514E1 }, + { (char*) "Pacific/Noumea" , 0x0515DA }, + { (char*) "Pacific/Pago_Pago" , 0x0516AC }, + { (char*) "Pacific/Palau" , 0x05174A }, + { (char*) "Pacific/Pitcairn" , 0x0517EA }, + { (char*) "Pacific/Pohnpei" , 0x05188F }, + { (char*) "Pacific/Ponape" , 0x05197F }, + { (char*) "Pacific/Port_Moresby" , 0x051A11 }, + { (char*) "Pacific/Rarotonga" , 0x051ACF }, + { (char*) "Pacific/Saipan" , 0x051C71 }, + { (char*) "Pacific/Samoa" , 0x051DD2 }, + { (char*) "Pacific/Tahiti" , 0x051E70 }, + { (char*) "Pacific/Tarawa" , 0x051F10 }, + { (char*) "Pacific/Tongatapu" , 0x051FB1 }, + { (char*) "Pacific/Truk" , 0x0520AA }, + { (char*) "Pacific/Wake" , 0x052150 }, + { (char*) "Pacific/Wallis" , 0x0521ED }, + { (char*) "Pacific/Yap" , 0x05227F }, + { (char*) "Poland" , 0x052325 }, + { (char*) "Portugal" , 0x0526CC }, + { (char*) "PRC" , 0x052C8F }, + { (char*) "PST8PDT" , 0x052E24 }, + { (char*) "ROC" , 0x05333E }, + { (char*) "ROK" , 0x053549 }, + { (char*) "Singapore" , 0x0536F4 }, + { (char*) "Turkey" , 0x053800 }, + { (char*) "UCT" , 0x053CBC }, + { (char*) "Universal" , 0x053D37 }, + { (char*) "US/Alaska" , 0x053DB2 }, + { (char*) "US/Aleutian" , 0x05418F }, + { (char*) "US/Arizona" , 0x054564 }, + { (char*) "US/Central" , 0x054660 }, + { (char*) "US/East-Indiana" , 0x054D46 }, + { (char*) "US/Eastern" , 0x054F65 }, + { (char*) "US/Hawaii" , 0x055641 }, + { (char*) "US/Indiana-Starke" , 0x05572A }, + { (char*) "US/Michigan" , 0x055B2E }, + { (char*) "US/Mountain" , 0x055EBD }, + { (char*) "US/Pacific" , 0x0562DB }, + { (char*) "US/Samoa" , 0x0567F5 }, + { (char*) "UTC" , 0x056893 }, + { (char*) "W-SU" , 0x05690E }, + { (char*) "WET" , 0x056CA6 }, + { (char*) "Zulu" , 0x057269 }, }; -const unsigned char timelib_timezone_db_data_builtin[357654] = { +const unsigned char timelib_timezone_db_data_builtin[357092] = { /* Africa/Abidjan */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x43, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -919,7 +919,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0xC5, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0x96, 0x51, 0xF9, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0xFF, 0x14, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x58, 0xAC, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xD9, 0xED, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xA1, 0x32, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0x35, 0xA4, 0x00, 0xFF, @@ -955,86 +955,16 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x64, 0x44, 0x91, 0x20, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x66, 0x1B, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x67, 0xF1, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x69, 0xBF, 0x4D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x67, 0xD0, 0x20, 0x00, -0x00, 0x00, 0x00, 0x6B, 0x95, 0xF4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x35, 0x3D, 0x20, 0x00, -0x00, 0x00, 0x00, 0x6D, 0x6C, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x0B, 0xE4, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x6F, 0x3A, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0xD9, 0x51, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x71, 0x10, 0xB0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x72, 0xAF, 0xF9, 0x20, 0x00, -0x00, 0x00, 0x00, 0x72, 0xDE, 0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x86, 0xA0, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x74, 0xB4, 0xC5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x76, 0x54, 0x0D, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x76, 0x8B, 0x6C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2A, 0xB5, 0x20, 0x00, -0x00, 0x00, 0x00, 0x78, 0x58, 0xD9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, 0x22, 0x20, 0x00, -0x00, 0x00, 0x00, 0x7A, 0x2F, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xCE, 0xC9, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x7C, 0x06, 0x28, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xA5, 0x71, 0x20, 0x00, -0x00, 0x00, 0x00, 0x7D, 0xD3, 0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x72, 0xDE, 0x20, 0x00, -0x00, 0x00, 0x00, 0x7F, 0xAA, 0x3D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x81, 0x49, 0x85, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x81, 0x77, 0xAA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x83, 0x20, 0x2D, 0x20, 0x00, -0x00, 0x00, 0x00, 0x83, 0x4E, 0x51, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x84, 0xED, 0x9A, 0x20, 0x00, -0x00, 0x00, 0x00, 0x85, 0x24, 0xF9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x86, 0xC4, 0x41, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x86, 0xF2, 0x66, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x91, 0xAE, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x88, 0xC9, 0x0D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x68, 0x56, 0x20, 0x00, -0x00, 0x00, 0x00, 0x8A, 0x9F, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x3E, 0xFD, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x8C, 0x6D, 0x22, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x0C, 0x6A, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x8E, 0x43, 0xC9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE3, 0x12, 0x20, 0x00, -0x00, 0x00, 0x00, 0x90, 0x11, 0x36, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB9, 0xB9, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x91, 0xE7, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x93, 0x87, 0x26, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x93, 0xBE, 0x85, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x95, 0x5D, 0xCE, 0x20, 0x00, -0x00, 0x00, 0x00, 0x95, 0x8B, 0xF2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x97, 0x2B, 0x3B, 0x20, 0x00, -0x00, 0x00, 0x00, 0x97, 0x62, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x99, 0x01, 0xE2, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x99, 0x39, 0x41, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xD8, 0x8A, 0x20, 0x00, -0x00, 0x00, 0x00, 0x9B, 0x06, 0xAE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xA5, 0xF7, 0x20, 0x00, -0x00, 0x00, 0x00, 0x9C, 0xDD, 0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x7C, 0x9E, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x9E, 0xAA, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x53, 0x46, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA0, 0x81, 0x6A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x20, 0xB3, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA2, 0x58, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA3, 0xF7, 0x5A, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xA4, 0x25, 0x7F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xC4, 0xC7, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xA5, 0xFC, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA7, 0x9B, 0x6F, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA7, 0xD2, 0xCE, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x72, 0x16, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xA9, 0xA0, 0x3B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x3F, 0x83, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xAB, 0x76, 0xE2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x16, 0x2B, 0x20, 0x00, -0x00, 0x00, 0x00, 0xAD, 0x44, 0x4F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAE, 0xEC, 0xD2, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xAF, 0x1A, 0xF7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xBA, 0x3F, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB0, 0xF1, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x90, 0xE7, 0x20, 0x00, -0x00, 0x00, 0x00, 0xB2, 0xBF, 0x0B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x5E, 0x54, 0x20, 0x00, -0x00, 0x00, 0x00, 0xB4, 0x95, 0xB3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x34, 0xFB, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB6, 0x6C, 0x5A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x0B, 0xA3, 0x20, 0x00, -0x00, 0x00, 0x00, 0xB8, 0x39, 0xC7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xD9, 0x10, 0x20, 0x00, -0x00, 0x00, 0x00, 0xBA, 0x10, 0x6F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xAF, 0xB7, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xBB, 0xDD, 0xDC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x86, 0x5F, 0x20, 0x00, -0x00, 0x00, 0x00, 0xBD, 0xB4, 0x83, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x53, 0xCC, 0x20, 0x00, -0x00, 0x00, 0x00, 0xBF, 0x8B, 0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x2A, 0x73, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xC1, 0x58, 0x98, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xF7, 0xE0, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xC3, 0x2F, 0x3F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xCE, 0x88, 0x20, 0x00, -0x00, 0x00, 0x00, 0xC5, 0x05, 0xE7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xA5, 0x2F, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xC6, 0xD3, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x72, 0x9C, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xC8, 0xA9, 0xFB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x49, 0x44, 0x20, 0x00, -0x00, 0x00, 0x00, 0xCA, 0x77, 0x68, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x1F, 0xEB, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xCC, 0x4E, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xED, 0x58, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xCE, 0x24, 0xB7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xC4, 0x00, 0x20, 0x00, -0x00, 0x00, 0x00, 0xCF, 0xF2, 0x24, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x91, 0x6D, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD1, 0xC8, 0xCC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x68, 0x14, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xD3, 0x96, 0x39, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x3E, 0xBC, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD5, 0x6C, 0xE0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x0C, 0x29, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD7, 0x43, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xE2, 0xD0, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xD9, 0x10, 0xF5, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB9, 0x78, 0x20, 0x00, -0x00, 0x00, 0x00, 0xDA, 0xE7, 0x9C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x86, 0xE5, 0x20, 0x00, -0x00, 0x00, 0x00, 0xDC, 0xBE, 0x44, 0x20, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0xFF, 0xFF, 0xF8, 0xE4, -0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x0E, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, -0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x31, 0x3E, 0x2D, 0x31, 0x0A, 0x00, -0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, 0x42, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x69, 0xBF, 0x4D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAF, 0x30, 0x10, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, +0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, +0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x01, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, +0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x30, 0x3E, 0x30, 0x0A, 0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, +0x42, 0x00, 0x00, 0x00, 0x00, /* Africa/Ceuta */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1142,7 +1072,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0xBA, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x48, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xD1, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x61, 0x47, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC9, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x8E, 0xF2, 0x70, 0x00, @@ -1173,86 +1103,15 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x66, 0x1B, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x67, 0xF1, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0xBF, 0x4D, 0x20, 0x00, -0x00, 0x00, 0x00, 0x6B, 0x67, 0xD0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x95, 0xF4, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x6D, 0x35, 0x3D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x6C, 0x9C, 0x20, 0x00, -0x00, 0x00, 0x00, 0x6F, 0x0B, 0xE4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x3A, 0x09, 0x20, 0x00, -0x00, 0x00, 0x00, 0x70, 0xD9, 0x51, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x71, 0x10, 0xB0, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x72, 0xAF, 0xF9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0xDE, 0x1D, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x74, 0x86, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x74, 0xB4, 0xC5, 0x20, 0x00, -0x00, 0x00, 0x00, 0x76, 0x54, 0x0D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x8B, 0x6C, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x78, 0x2A, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x78, 0x58, 0xD9, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x79, 0xF8, 0x22, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x2F, 0x81, 0x20, 0x00, -0x00, 0x00, 0x00, 0x7B, 0xCE, 0xC9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x06, 0x28, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x7D, 0xA5, 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xD3, 0x95, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x7F, 0x72, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xAA, 0x3D, 0x20, 0x00, -0x00, 0x00, 0x00, 0x81, 0x49, 0x85, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x81, 0x77, 0xAA, 0x20, 0x00, -0x00, 0x00, 0x00, 0x83, 0x20, 0x2D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x83, 0x4E, 0x51, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x84, 0xED, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x85, 0x24, 0xF9, 0x20, 0x00, -0x00, 0x00, 0x00, 0x86, 0xC4, 0x41, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x86, 0xF2, 0x66, 0x20, 0x00, -0x00, 0x00, 0x00, 0x88, 0x91, 0xAE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x88, 0xC9, 0x0D, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x8A, 0x68, 0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x9F, 0xB5, 0x20, 0x00, -0x00, 0x00, 0x00, 0x8C, 0x3E, 0xFD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x6D, 0x22, 0x20, 0x00, -0x00, 0x00, 0x00, 0x8E, 0x0C, 0x6A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x43, 0xC9, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x8F, 0xE3, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x90, 0x11, 0x36, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x91, 0xB9, 0xB9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x91, 0xE7, 0xDE, 0x20, 0x00, -0x00, 0x00, 0x00, 0x93, 0x87, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x93, 0xBE, 0x85, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x95, 0x5D, 0xCE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x95, 0x8B, 0xF2, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x97, 0x2B, 0x3B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x97, 0x62, 0x9A, 0x20, 0x00, -0x00, 0x00, 0x00, 0x99, 0x01, 0xE2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x99, 0x39, 0x41, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x9A, 0xD8, 0x8A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x06, 0xAE, 0xA0, 0x00, -0x00, 0x00, 0x00, 0x9C, 0xA5, 0xF7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xDD, 0x56, 0x20, 0x00, -0x00, 0x00, 0x00, 0x9E, 0x7C, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xAA, 0xC3, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA0, 0x53, 0x46, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x81, 0x6A, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xA2, 0x20, 0xB3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x58, 0x12, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA3, 0xF7, 0x5A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x25, 0x7F, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA5, 0xC4, 0xC7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xFC, 0x26, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xA7, 0x9B, 0x6F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xD2, 0xCE, 0x20, 0x00, -0x00, 0x00, 0x00, 0xA9, 0x72, 0x16, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA9, 0xA0, 0x3B, 0x20, 0x00, -0x00, 0x00, 0x00, 0xAB, 0x3F, 0x83, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x76, 0xE2, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xAD, 0x16, 0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x44, 0x4F, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xAE, 0xEC, 0xD2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x1A, 0xF7, 0x20, 0x00, -0x00, 0x00, 0x00, 0xB0, 0xBA, 0x3F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xF1, 0x9E, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB2, 0x90, 0xE7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xBF, 0x0B, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB4, 0x5E, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x95, 0xB3, 0x20, 0x00, -0x00, 0x00, 0x00, 0xB6, 0x34, 0xFB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x6C, 0x5A, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB8, 0x0B, 0xA3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x39, 0xC7, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xB9, 0xD9, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x10, 0x6F, 0x20, 0x00, -0x00, 0x00, 0x00, 0xBB, 0xAF, 0xB7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xDD, 0xDC, 0x20, 0x00, -0x00, 0x00, 0x00, 0xBD, 0x86, 0x5F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBD, 0xB4, 0x83, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xBF, 0x53, 0xCC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x8B, 0x2B, 0x20, 0x00, -0x00, 0x00, 0x00, 0xC1, 0x2A, 0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x58, 0x98, 0x20, 0x00, -0x00, 0x00, 0x00, 0xC2, 0xF7, 0xE0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x2F, 0x3F, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xC4, 0xCE, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x05, 0xE7, 0x20, 0x00, -0x00, 0x00, 0x00, 0xC6, 0xA5, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xD3, 0x54, 0x20, 0x00, -0x00, 0x00, 0x00, 0xC8, 0x72, 0x9C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC8, 0xA9, 0xFB, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xCA, 0x49, 0x44, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x77, 0x68, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xCC, 0x1F, 0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x4E, 0x10, 0x20, 0x00, -0x00, 0x00, 0x00, 0xCD, 0xED, 0x58, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x24, 0xB7, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xCF, 0xC4, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xF2, 0x24, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xD1, 0x91, 0x6D, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xC8, 0xCC, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD3, 0x68, 0x14, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x96, 0x39, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD5, 0x3E, 0xBC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x6C, 0xE0, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xD7, 0x0C, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x43, 0x88, 0x20, 0x00, -0x00, 0x00, 0x00, 0xD8, 0xE2, 0xD0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x10, 0xF5, 0x20, 0x00, -0x00, 0x00, 0x00, 0xDA, 0xB9, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xE7, 0x9C, 0xA0, 0x00, -0x00, 0x00, 0x00, 0xDC, 0x86, 0xE5, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xBE, 0x44, 0x20, 0x01, +0x00, 0x00, 0x00, 0x6A, 0xAF, 0x30, 0x10, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, -0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0xFF, 0xFF, 0xF3, 0xA0, 0x00, 0x00, 0xFF, -0xFF, 0xF1, 0xF0, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x08, 0x4C, 0x4D, 0x54, -0x00, 0x2D, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, 0x2B, -0x30, 0x31, 0x3E, 0x2D, 0x31, 0x0A, 0x00, 0xB2, 0xC1, 0xB8, 0x00, 0xFE, 0x84, 0x40, 0x00, 0x00, -0x00, 0x00, +0x03, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x05, 0x03, 0xFF, 0xFF, 0xF3, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x04, +0x00, 0x00, 0x0E, 0x10, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, +0x01, 0x0C, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2D, 0x30, 0x31, 0x00, +0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x30, 0x3E, 0x30, 0x0A, +0x00, 0xB2, 0xC1, 0xB8, 0x00, 0xFE, 0x84, 0x40, 0x00, 0x00, 0x00, 0x00, /* Africa/Freetown */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x53, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -4186,7 +4045,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x59, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, 0xFF, @@ -4231,20 +4090,41 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, 0x00, -0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, 0x00, +0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, 0x00, +0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, 0x00, +0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, 0x00, +0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, 0x00, +0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, 0x00, +0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, 0x00, +0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, 0x00, +0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, 0x00, +0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, 0x00, +0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, 0x00, +0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, 0x00, +0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, -0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, -0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, -0x54, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, -0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0xDB, 0x0A, 0x38, 0x00, 0x65, -0x85, 0x95, 0x00, 0x00, 0x00, 0x22, 0x4D, 0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x2D, -0x20, 0x41, 0x42, 0x2C, 0x20, 0x42, 0x43, 0x28, 0x45, 0x29, 0x2C, 0x20, 0x4E, 0x54, 0x28, 0x45, -0x29, 0x2C, 0x20, 0x53, 0x4B, 0x28, 0x57, 0x29, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, +0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, +0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, +0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, +0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, +0x0A, 0x00, 0xDB, 0x0A, 0x38, 0x00, 0x65, 0x85, 0x95, 0x00, 0x00, 0x00, 0x1D, 0x43, 0x53, 0x54, +0x20, 0x2D, 0x20, 0x41, 0x42, 0x2C, 0x20, 0x42, 0x43, 0x28, 0x45, 0x29, 0x2C, 0x20, 0x4E, 0x54, +0x28, 0x45, 0x29, 0x2C, 0x20, 0x53, 0x4B, 0x28, 0x57, 0x29, /* America/Eirunepe */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x42, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -9445,7 +9325,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x81, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, 0x76, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x15, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, @@ -9510,7 +9390,27 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x40, 0x6F, 0xDC, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xBE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, -0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, +0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, +0x00, 0x00, 0x00, 0x47, 0xD3, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, +0x00, 0x00, 0x00, 0x49, 0xB3, 0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, +0x00, 0x00, 0x00, 0x4B, 0x9C, 0xB3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x7C, 0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, +0x00, 0x00, 0x00, 0x4F, 0x5C, 0x77, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, +0x00, 0x00, 0x00, 0x51, 0x3C, 0x59, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, +0x00, 0x00, 0x00, 0x53, 0x1C, 0x3B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, +0x00, 0x00, 0x00, 0x54, 0xFC, 0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, +0x00, 0x00, 0x00, 0x56, 0xE5, 0x3A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, +0x00, 0x00, 0x00, 0x58, 0xC5, 0x1C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5A, 0xA4, 0xFE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5C, 0x84, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5E, 0x64, 0xC2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, +0x00, 0x00, 0x00, 0x60, 0x4D, 0xDE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, +0x00, 0x00, 0x00, 0x62, 0x2D, 0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, +0x00, 0x00, 0x00, 0x64, 0x0D, 0xA2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, +0x00, 0x00, 0x00, 0x65, 0xED, 0x84, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, +0x00, 0x00, 0x00, 0x67, 0xCD, 0x66, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, +0x00, 0x00, 0x00, 0x69, 0xAD, 0x48, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x02, +0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, @@ -9518,14 +9418,15 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, -0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, -0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, -0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x0A, 0x50, 0x53, 0x54, 0x38, 0x50, -0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, -0x30, 0x0A, 0x00, 0xD4, 0x81, 0x0A, 0x00, 0x56, 0xCC, 0x0D, 0x00, 0x00, 0x00, 0x19, 0x50, 0x61, -0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x2D, 0x20, 0x42, 0x43, 0x20, 0x28, 0x6D, 0x6F, 0x73, 0x74, -0x20, 0x61, 0x72, 0x65, 0x61, 0x73, 0x29, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, +0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, +0xFF, 0x9D, 0x90, 0x01, 0x10, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, +0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, +0x53, 0x54, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x0A, 0x00, 0xD4, 0x81, 0x0A, 0x00, 0x56, 0xCC, +0x0D, 0x00, 0x00, 0x00, 0x15, 0x4D, 0x53, 0x54, 0x20, 0x2D, 0x20, 0x42, 0x43, 0x20, 0x28, 0x6D, +0x6F, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65, 0x61, 0x73, 0x29, /* America/Virgin */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -9765,7 +9666,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x59, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, 0xFF, @@ -9810,18 +9711,39 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, 0x00, -0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, 0x00, +0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, 0x00, +0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, 0x00, +0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, 0x00, +0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, 0x00, +0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, 0x00, +0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, 0x00, +0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, 0x00, +0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, 0x00, +0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, 0x00, +0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, 0x00, +0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, 0x00, +0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, -0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, -0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, -0x54, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, -0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, +0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, +0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, +0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, +0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, +0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Antarctica/Casey */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -11681,17 +11603,17 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x15, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0xB6, 0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3D, 0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, -0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, -0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x4C, 0x4D, -0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, -0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x99, 0xBB, 0x78, -0x01, 0xB5, 0x6B, 0x2A, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x63, 0xF6, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, +0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x50, 0x4C, +0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, +0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x99, 0xBB, 0x78, 0x01, 0xB5, 0x6B, 0x2A, +0x00, 0x00, 0x00, 0x00, /* Asia/Hong_Kong */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x48, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -12775,15 +12697,15 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x15, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x45, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x00, 0x62, 0x5C, -0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, -0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, -0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, -0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x9A, 0xF3, 0xF8, 0x01, 0xB2, 0xBF, 0x92, -0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, +0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, +0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, +0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x9A, 0xF3, 0xF8, 0x01, 0xB2, 0xBF, 0x92, 0x00, 0x00, 0x00, 0x00, + /* Asia/Pontianak */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -12954,17 +12876,17 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x15, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0xB6, 0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3D, 0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, -0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, -0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x4C, 0x4D, -0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, -0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x63, 0xF6, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, +0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x50, 0x4C, +0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, +0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, /* Asia/Sakhalin */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -13663,16 +13585,16 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x15, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x47, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x51, 0x61, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x00, 0x00, -0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, -0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x4C, 0x4D, 0x54, 0x00, -0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, -0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0xA4, 0xBE, 0x7A, 0x01, 0xAF, -0x36, 0xA0, 0x00, 0x00, 0x00, 0x00, +0x60, 0x30, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, +0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x50, 0x4C, 0x4D, 0x54, +0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, +0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0xA4, 0xBE, 0x7A, 0x01, 0xAF, 0x36, 0xA0, 0x00, 0x00, +0x00, 0x00, /* Asia/Vladivostok */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -15969,7 +15891,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x59, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, 0xFF, @@ -16014,18 +15936,39 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, 0x00, -0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, 0x00, +0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, 0x00, +0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, 0x00, +0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, 0x00, +0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, 0x00, +0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, 0x00, +0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, 0x00, +0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, 0x00, +0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, 0x00, +0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, 0x00, +0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, 0x00, +0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, 0x00, +0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, 0x00, +0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, 0x00, +0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, -0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, -0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, -0x54, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, -0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, +0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, +0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, +0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, +0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, +0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Canada/Newfoundland */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -16154,7 +16097,7 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x81, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, 0x76, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x15, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, @@ -16219,7 +16162,27 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x00, 0x00, 0x00, 0x40, 0x6F, 0xDC, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xBE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, -0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, +0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, +0x00, 0x00, 0x00, 0x47, 0xD3, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, +0x00, 0x00, 0x00, 0x49, 0xB3, 0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, +0x00, 0x00, 0x00, 0x4B, 0x9C, 0xB3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x7C, 0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, +0x00, 0x00, 0x00, 0x4F, 0x5C, 0x77, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, +0x00, 0x00, 0x00, 0x51, 0x3C, 0x59, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, +0x00, 0x00, 0x00, 0x53, 0x1C, 0x3B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, +0x00, 0x00, 0x00, 0x54, 0xFC, 0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, +0x00, 0x00, 0x00, 0x56, 0xE5, 0x3A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, +0x00, 0x00, 0x00, 0x58, 0xC5, 0x1C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5A, 0xA4, 0xFE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5C, 0x84, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, +0x00, 0x00, 0x00, 0x5E, 0x64, 0xC2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, +0x00, 0x00, 0x00, 0x60, 0x4D, 0xDE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, +0x00, 0x00, 0x00, 0x62, 0x2D, 0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, +0x00, 0x00, 0x00, 0x64, 0x0D, 0xA2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, +0x00, 0x00, 0x00, 0x65, 0xED, 0x84, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, +0x00, 0x00, 0x00, 0x67, 0xCD, 0x66, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, +0x00, 0x00, 0x00, 0x69, 0xAD, 0x48, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x02, +0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, @@ -16227,12 +16190,14 @@ const unsigned char timelib_timezone_db_data_builtin[357654] = { 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, -0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, -0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, -0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x0A, 0x50, 0x53, 0x54, 0x38, 0x50, -0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, -0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, +0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, +0xFF, 0x9D, 0x90, 0x01, 0x10, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, +0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, +0x53, 0x54, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, /* Canada/Saskatchewan */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -24474,593 +24439,593 @@ const timelib_tzdb_index_entry timezonedb_idx_builtin[598] = { { (char*) "Africa/Bujumbura" , 0x000F1A }, { (char*) "Africa/Cairo" , 0x000FBB }, { (char*) "Africa/Casablanca" , 0x001926 }, - { (char*) "Africa/Ceuta" , 0x0022AF }, - { (char*) "Africa/Conakry" , 0x002ACD }, - { (char*) "Africa/Dakar" , 0x002BA9 }, - { (char*) "Africa/Dar_es_Salaam" , 0x002C6B }, - { (char*) "Africa/Djibouti" , 0x002D4C }, - { (char*) "Africa/Douala" , 0x002DED }, - { (char*) "Africa/El_Aaiun" , 0x002E8E }, - { (char*) "Africa/Freetown" , 0x003791 }, - { (char*) "Africa/Gaborone" , 0x00396D }, - { (char*) "Africa/Harare" , 0x003A64 }, - { (char*) "Africa/Johannesburg" , 0x003B05 }, - { (char*) "Africa/Juba" , 0x003C07 }, - { (char*) "Africa/Kampala" , 0x003EBA }, - { (char*) "Africa/Khartoum" , 0x003FC1 }, - { (char*) "Africa/Kigali" , 0x004274 }, - { (char*) "Africa/Kinshasa" , 0x004315 }, - { (char*) "Africa/Lagos" , 0x0043CF }, - { (char*) "Africa/Libreville" , 0x0044C6 }, - { (char*) "Africa/Lome" , 0x004567 }, - { (char*) "Africa/Luanda" , 0x004607 }, - { (char*) "Africa/Lubumbashi" , 0x0046CE }, - { (char*) "Africa/Lusaka" , 0x0047AA }, - { (char*) "Africa/Malabo" , 0x00484B }, - { (char*) "Africa/Maputo" , 0x00490E }, - { (char*) "Africa/Maseru" , 0x0049AF }, - { (char*) "Africa/Mbabane" , 0x004A7B }, - { (char*) "Africa/Mogadishu" , 0x004B1F }, - { (char*) "Africa/Monrovia" , 0x004C00 }, - { (char*) "Africa/Nairobi" , 0x004CDC }, - { (char*) "Africa/Ndjamena" , 0x004DF1 }, - { (char*) "Africa/Niamey" , 0x004EC4 }, - { (char*) "Africa/Nouakchott" , 0x004FA9 }, - { (char*) "Africa/Ouagadougou" , 0x005085 }, - { (char*) "Africa/Porto-Novo" , 0x005125 }, - { (char*) "Africa/Sao_Tome" , 0x0051E8 }, - { (char*) "Africa/Timbuktu" , 0x0052F2 }, - { (char*) "Africa/Tripoli" , 0x005392 }, - { (char*) "Africa/Tunis" , 0x00560F }, - { (char*) "Africa/Windhoek" , 0x0058CC }, - { (char*) "America/Adak" , 0x005C93 }, - { (char*) "America/Anchorage" , 0x0065ED }, - { (char*) "America/Anguilla" , 0x006F4F }, - { (char*) "America/Antigua" , 0x006FEF }, - { (char*) "America/Araguaina" , 0x0070B1 }, - { (char*) "America/Argentina/Buenos_Aires" , 0x00742C }, - { (char*) "America/Argentina/Catamarca" , 0x007873 }, - { (char*) "America/Argentina/ComodRivadavia" , 0x007CC0 }, - { (char*) "America/Argentina/Cordoba" , 0x0080F2 }, - { (char*) "America/Argentina/Jujuy" , 0x00855A }, - { (char*) "America/Argentina/La_Rioja" , 0x00897A }, - { (char*) "America/Argentina/Mendoza" , 0x008DC7 }, - { (char*) "America/Argentina/Rio_Gallegos" , 0x009205 }, - { (char*) "America/Argentina/Salta" , 0x009646 }, - { (char*) "America/Argentina/San_Juan" , 0x009A72 }, - { (char*) "America/Argentina/San_Luis" , 0x009EBF }, - { (char*) "America/Argentina/Tucuman" , 0x00A318 }, - { (char*) "America/Argentina/Ushuaia" , 0x00A772 }, - { (char*) "America/Aruba" , 0x00ABB9 }, - { (char*) "America/Asuncion" , 0x00AC7F }, - { (char*) "America/Atikokan" , 0x00B2F7 }, - { (char*) "America/Atka" , 0x00B474 }, - { (char*) "America/Bahia" , 0x00BDB4 }, - { (char*) "America/Bahia_Banderas" , 0x00C1B7 }, - { (char*) "America/Barbados" , 0x00C620 }, - { (char*) "America/Belem" , 0x00C7E0 }, - { (char*) "America/Belize" , 0x00CA30 }, - { (char*) "America/Blanc-Sablon" , 0x00D08A }, - { (char*) "America/Boa_Vista" , 0x00D1DC }, - { (char*) "America/Bogota" , 0x00D459 }, - { (char*) "America/Boise" , 0x00D54D }, - { (char*) "America/Buenos_Aires" , 0x00DEE3 }, - { (char*) "America/Cambridge_Bay" , 0x00E315 }, - { (char*) "America/Campo_Grande" , 0x00EC03 }, - { (char*) "America/Cancun" , 0x00F1B7 }, - { (char*) "America/Caracas" , 0x00F52F }, - { (char*) "America/Catamarca" , 0x00F635 }, - { (char*) "America/Cayenne" , 0x00FA67 }, - { (char*) "America/Cayman" , 0x00FB2B }, - { (char*) "America/Chicago" , 0x00FBED }, - { (char*) "America/Chihuahua" , 0x010A15 }, - { (char*) "America/Ciudad_Juarez" , 0x010E85 }, - { (char*) "America/Coral_Harbour" , 0x0114AF }, - { (char*) "America/Cordoba" , 0x011571 }, - { (char*) "America/Costa_Rica" , 0x0119A3 }, - { (char*) "America/Coyhaique" , 0x011AEB }, - { (char*) "America/Creston" , 0x012351 }, - { (char*) "America/Cuiaba" , 0x01243F }, - { (char*) "America/Curacao" , 0x0129D0 }, - { (char*) "America/Danmarkshavn" , 0x012A96 }, - { (char*) "America/Dawson" , 0x012D76 }, - { (char*) "America/Dawson_Creek" , 0x0133E2 }, - { (char*) "America/Denver" , 0x013828 }, - { (char*) "America/Detroit" , 0x0141E5 }, - { (char*) "America/Dominica" , 0x014AC0 }, - { (char*) "America/Edmonton" , 0x014B60 }, - { (char*) "America/Eirunepe" , 0x0154AA }, - { (char*) "America/El_Salvador" , 0x015747 }, - { (char*) "America/Ensenada" , 0x015833 }, - { (char*) "America/Fort_Nelson" , 0x016399 }, - { (char*) "America/Fort_Wayne" , 0x016C79 }, - { (char*) "America/Fortaleza" , 0x017317 }, - { (char*) "America/Glace_Bay" , 0x017607 }, - { (char*) "America/Godthab" , 0x017EBE }, - { (char*) "America/Goose_Bay" , 0x01862B }, - { (char*) "America/Grand_Turk" , 0x0192E1 }, - { (char*) "America/Grenada" , 0x019A17 }, - { (char*) "America/Guadeloupe" , 0x019AB7 }, - { (char*) "America/Guatemala" , 0x019B57 }, - { (char*) "America/Guayaquil" , 0x019C7B }, - { (char*) "America/Guyana" , 0x019D81 }, - { (char*) "America/Halifax" , 0x019E85 }, - { (char*) "America/Havana" , 0x01AC0F }, - { (char*) "America/Hermosillo" , 0x01B58B }, - { (char*) "America/Indiana/Indianapolis" , 0x01B721 }, - { (char*) "America/Indiana/Knox" , 0x01BDD8 }, - { (char*) "America/Indiana/Marengo" , 0x01C785 }, - { (char*) "America/Indiana/Petersburg" , 0x01CE72 }, - { (char*) "America/Indiana/Tell_City" , 0x01D611 }, - { (char*) "America/Indiana/Vevay" , 0x01DCD5 }, - { (char*) "America/Indiana/Vincennes" , 0x01E291 }, - { (char*) "America/Indiana/Winamac" , 0x01E967 }, - { (char*) "America/Indianapolis" , 0x01F08B }, - { (char*) "America/Inuvik" , 0x01F729 }, - { (char*) "America/Iqaluit" , 0x01FF63 }, - { (char*) "America/Jamaica" , 0x020822 }, - { (char*) "America/Jujuy" , 0x020A10 }, - { (char*) "America/Juneau" , 0x020E26 }, - { (char*) "America/Kentucky/Louisville" , 0x021777 }, - { (char*) "America/Kentucky/Monticello" , 0x022285 }, - { (char*) "America/Knox_IN" , 0x022BE5 }, - { (char*) "America/Kralendijk" , 0x02357D }, - { (char*) "America/La_Paz" , 0x02367F }, - { (char*) "America/Lima" , 0x023765 }, - { (char*) "America/Los_Angeles" , 0x0238F9 }, - { (char*) "America/Louisville" , 0x024430 }, - { (char*) "America/Lower_Princes" , 0x024F20 }, - { (char*) "America/Maceio" , 0x025022 }, - { (char*) "America/Managua" , 0x025318 }, - { (char*) "America/Manaus" , 0x0254D2 }, - { (char*) "America/Marigot" , 0x02573B }, - { (char*) "America/Martinique" , 0x02583D }, - { (char*) "America/Matamoros" , 0x025931 }, - { (char*) "America/Mazatlan" , 0x025EF3 }, - { (char*) "America/Mendoza" , 0x026355 }, - { (char*) "America/Menominee" , 0x026787 }, - { (char*) "America/Merida" , 0x027094 }, - { (char*) "America/Metlakatla" , 0x02749D }, - { (char*) "America/Mexico_City" , 0x027A4F }, - { (char*) "America/Miquelon" , 0x027F2F }, - { (char*) "America/Moncton" , 0x0285AF }, - { (char*) "America/Monterrey" , 0x029225 }, - { (char*) "America/Montevideo" , 0x0296C1 }, - { (char*) "America/Montreal" , 0x029CA5 }, - { (char*) "America/Montserrat" , 0x02AA57 }, - { (char*) "America/Nassau" , 0x02AAF7 }, - { (char*) "America/New_York" , 0x02B457 }, - { (char*) "America/Nipigon" , 0x02C257 }, - { (char*) "America/Nome" , 0x02D009 }, - { (char*) "America/Noronha" , 0x02D961 }, - { (char*) "America/North_Dakota/Beulah" , 0x02DC3B }, - { (char*) "America/North_Dakota/Center" , 0x02E5B8 }, - { (char*) "America/North_Dakota/New_Salem" , 0x02EF35 }, - { (char*) "America/Nuuk" , 0x02F8B8 }, - { (char*) "America/Ojinaga" , 0x030036 }, - { (char*) "America/Panama" , 0x030652 }, - { (char*) "America/Pangnirtung" , 0x030714 }, - { (char*) "America/Paramaribo" , 0x030FBA }, - { (char*) "America/Phoenix" , 0x0310BE }, - { (char*) "America/Port-au-Prince" , 0x03124A }, - { (char*) "America/Port_of_Spain" , 0x0317F0 }, - { (char*) "America/Porto_Acre" , 0x031890 }, - { (char*) "America/Porto_Velho" , 0x031B02 }, - { (char*) "America/Puerto_Rico" , 0x031D48 }, - { (char*) "America/Punta_Arenas" , 0x031E4A }, - { (char*) "America/Rainy_River" , 0x0325D5 }, - { (char*) "America/Rankin_Inlet" , 0x033115 }, - { (char*) "America/Recife" , 0x033949 }, - { (char*) "America/Regina" , 0x033C1D }, - { (char*) "America/Resolute" , 0x034012 }, - { (char*) "America/Rio_Branco" , 0x034847 }, - { (char*) "America/Rosario" , 0x034ABD }, - { (char*) "America/Santa_Isabel" , 0x034EEF }, - { (char*) "America/Santarem" , 0x035A55 }, - { (char*) "America/Santiago" , 0x035CB8 }, - { (char*) "America/Santo_Domingo" , 0x0366A4 }, - { (char*) "America/Sao_Paulo" , 0x03687A }, - { (char*) "America/Scoresbysund" , 0x036E52 }, - { (char*) "America/Shiprock" , 0x03760A }, - { (char*) "America/Sitka" , 0x037FB2 }, - { (char*) "America/St_Barthelemy" , 0x0388EA }, - { (char*) "America/St_Johns" , 0x0389EC }, - { (char*) "America/St_Kitts" , 0x03985A }, - { (char*) "America/St_Lucia" , 0x0398FA }, - { (char*) "America/St_Thomas" , 0x0399BC }, - { (char*) "America/St_Vincent" , 0x039A5C }, - { (char*) "America/Swift_Current" , 0x039B1E }, - { (char*) "America/Tegucigalpa" , 0x039D6C }, - { (char*) "America/Thule" , 0x039E74 }, - { (char*) "America/Thunder_Bay" , 0x03A46C }, - { (char*) "America/Tijuana" , 0x03B21E }, - { (char*) "America/Toronto" , 0x03BD93 }, - { (char*) "America/Tortola" , 0x03CB63 }, - { (char*) "America/Vancouver" , 0x03CC03 }, - { (char*) "America/Virgin" , 0x03D774 }, - { (char*) "America/Whitehorse" , 0x03D876 }, - { (char*) "America/Winnipeg" , 0x03DEE2 }, - { (char*) "America/Yakutat" , 0x03EA3F }, - { (char*) "America/Yellowknife" , 0x03F35C }, - { (char*) "Antarctica/Casey" , 0x03FC84 }, - { (char*) "Antarctica/Davis" , 0x03FE3C }, - { (char*) "Antarctica/DumontDUrville" , 0x03FF68 }, - { (char*) "Antarctica/Macquarie" , 0x040038 }, - { (char*) "Antarctica/Mawson" , 0x040928 }, - { (char*) "Antarctica/McMurdo" , 0x0409F3 }, - { (char*) "Antarctica/Palmer" , 0x0411EE }, - { (char*) "Antarctica/Rothera" , 0x04177C }, - { (char*) "Antarctica/South_Pole" , 0x041825 }, - { (char*) "Antarctica/Syowa" , 0x0421B6 }, - { (char*) "Antarctica/Troll" , 0x04225E }, - { (char*) "Antarctica/Vostok" , 0x0426EB }, - { (char*) "Arctic/Longyearbyen" , 0x0427D2 }, - { (char*) "Asia/Aden" , 0x0430D8 }, - { (char*) "Asia/Almaty" , 0x04317B }, - { (char*) "Asia/Amman" , 0x043570 }, - { (char*) "Asia/Anadyr" , 0x043B15 }, - { (char*) "Asia/Aqtau" , 0x043FCA }, - { (char*) "Asia/Aqtobe" , 0x0443B4 }, - { (char*) "Asia/Ashgabat" , 0x0447B2 }, - { (char*) "Asia/Ashkhabad" , 0x044A1B }, - { (char*) "Asia/Atyrau" , 0x044C84 }, - { (char*) "Asia/Baghdad" , 0x045076 }, - { (char*) "Asia/Bahrain" , 0x04544B }, - { (char*) "Asia/Baku" , 0x045536 }, - { (char*) "Asia/Bangkok" , 0x0459FF }, - { (char*) "Asia/Barnaul" , 0x045AC4 }, - { (char*) "Asia/Beirut" , 0x045F95 }, - { (char*) "Asia/Bishkek" , 0x04680B }, - { (char*) "Asia/Brunei" , 0x046BE0 }, - { (char*) "Asia/Calcutta" , 0x046CA9 }, - { (char*) "Asia/Chita" , 0x046DD2 }, - { (char*) "Asia/Choibalsan" , 0x0472A9 }, - { (char*) "Asia/Chongqing" , 0x047622 }, - { (char*) "Asia/Chungking" , 0x04785F }, - { (char*) "Asia/Colombo" , 0x047A9C }, - { (char*) "Asia/Dacca" , 0x047C0E }, - { (char*) "Asia/Damascus" , 0x047D5D }, - { (char*) "Asia/Dhaka" , 0x0484BA }, - { (char*) "Asia/Dili" , 0x048609 }, - { (char*) "Asia/Dubai" , 0x048716 }, - { (char*) "Asia/Dushanbe" , 0x0487B9 }, - { (char*) "Asia/Famagusta" , 0x048A06 }, - { (char*) "Asia/Gaza" , 0x04920D }, - { (char*) "Asia/Harbin" , 0x04A127 }, - { (char*) "Asia/Hebron" , 0x04A364 }, - { (char*) "Asia/Ho_Chi_Minh" , 0x04B299 }, - { (char*) "Asia/Hong_Kong" , 0x04B3F6 }, - { (char*) "Asia/Hovd" , 0x04B8D3 }, - { (char*) "Asia/Irkutsk" , 0x04BC62 }, - { (char*) "Asia/Istanbul" , 0x04C155 }, - { (char*) "Asia/Jakarta" , 0x04C8EE }, - { (char*) "Asia/Jayapura" , 0x04CA86 }, - { (char*) "Asia/Jerusalem" , 0x04CBA5 }, - { (char*) "Asia/Kabul" , 0x04D505 }, - { (char*) "Asia/Kamchatka" , 0x04D5D3 }, - { (char*) "Asia/Karachi" , 0x04DA71 }, - { (char*) "Asia/Kashgar" , 0x04DBF8 }, - { (char*) "Asia/Kathmandu" , 0x04DC9B }, - { (char*) "Asia/Katmandu" , 0x04DD6D }, - { (char*) "Asia/Khandyga" , 0x04DE3F }, - { (char*) "Asia/Kolkata" , 0x04E352 }, - { (char*) "Asia/Krasnoyarsk" , 0x04E47B }, - { (char*) "Asia/Kuala_Lumpur" , 0x04E949 }, - { (char*) "Asia/Kuching" , 0x04EAFA }, - { (char*) "Asia/Kuwait" , 0x04ECE9 }, - { (char*) "Asia/Macao" , 0x04ED8C }, - { (char*) "Asia/Macau" , 0x04F263 }, - { (char*) "Asia/Magadan" , 0x04F73A }, - { (char*) "Asia/Makassar" , 0x04FC0E }, - { (char*) "Asia/Manila" , 0x04FD61 }, - { (char*) "Asia/Muscat" , 0x04FF13 }, - { (char*) "Asia/Nicosia" , 0x04FFB6 }, - { (char*) "Asia/Novokuznetsk" , 0x0507A2 }, - { (char*) "Asia/Novosibirsk" , 0x050C3E }, - { (char*) "Asia/Omsk" , 0x051115 }, - { (char*) "Asia/Oral" , 0x0515D7 }, - { (char*) "Asia/Phnom_Penh" , 0x0519D1 }, - { (char*) "Asia/Pontianak" , 0x051AF6 }, - { (char*) "Asia/Pyongyang" , 0x051C79 }, - { (char*) "Asia/Qatar" , 0x051D72 }, - { (char*) "Asia/Qostanay" , 0x051E37 }, - { (char*) "Asia/Qyzylorda" , 0x05225E }, - { (char*) "Asia/Rangoon" , 0x05267A }, - { (char*) "Asia/Riyadh" , 0x052784 }, - { (char*) "Asia/Saigon" , 0x052827 }, - { (char*) "Asia/Sakhalin" , 0x052984 }, - { (char*) "Asia/Samarkand" , 0x052E4C }, - { (char*) "Asia/Seoul" , 0x05309C }, - { (char*) "Asia/Shanghai" , 0x053311 }, - { (char*) "Asia/Singapore" , 0x05355A }, - { (char*) "Asia/Srednekolymsk" , 0x0536F7 }, - { (char*) "Asia/Taipei" , 0x053BCB }, - { (char*) "Asia/Tashkent" , 0x053ED0 }, - { (char*) "Asia/Tbilisi" , 0x05412E }, - { (char*) "Asia/Tehran" , 0x054529 }, - { (char*) "Asia/Tel_Aviv" , 0x054A15 }, - { (char*) "Asia/Thimbu" , 0x055375 }, - { (char*) "Asia/Thimphu" , 0x05543E }, - { (char*) "Asia/Tokyo" , 0x055507 }, - { (char*) "Asia/Tomsk" , 0x055648 }, - { (char*) "Asia/Ujung_Pandang" , 0x055B19 }, - { (char*) "Asia/Ulaanbaatar" , 0x055C23 }, - { (char*) "Asia/Ulan_Bator" , 0x055FAC }, - { (char*) "Asia/Urumqi" , 0x056325 }, - { (char*) "Asia/Ust-Nera" , 0x0563D5 }, - { (char*) "Asia/Vientiane" , 0x0568CB }, - { (char*) "Asia/Vladivostok" , 0x056A0C }, - { (char*) "Asia/Yakutsk" , 0x056ED5 }, - { (char*) "Asia/Yangon" , 0x05739D }, - { (char*) "Asia/Yekaterinburg" , 0x0574A7 }, - { (char*) "Asia/Yerevan" , 0x05798E }, - { (char*) "Atlantic/Azores" , 0x057E0B }, - { (char*) "Atlantic/Bermuda" , 0x058B8F }, - { (char*) "Atlantic/Canary" , 0x0594F7 }, - { (char*) "Atlantic/Cape_Verde" , 0x059C7A }, - { (char*) "Atlantic/Faeroe" , 0x059D86 }, - { (char*) "Atlantic/Faroe" , 0x05A4A9 }, - { (char*) "Atlantic/Jan_Mayen" , 0x05ABCC }, - { (char*) "Atlantic/Madeira" , 0x05B4D2 }, - { (char*) "Atlantic/Reykjavik" , 0x05C21E }, - { (char*) "Atlantic/South_Georgia" , 0x05C6B4 }, - { (char*) "Atlantic/St_Helena" , 0x05C756 }, - { (char*) "Atlantic/Stanley" , 0x05C818 }, - { (char*) "Australia/ACT" , 0x05CCD4 }, - { (char*) "Australia/Adelaide" , 0x05D56E }, - { (char*) "Australia/Brisbane" , 0x05DE29 }, - { (char*) "Australia/Broken_Hill" , 0x05DFEF }, - { (char*) "Australia/Canberra" , 0x05E8CC }, - { (char*) "Australia/Currie" , 0x05F166 }, - { (char*) "Australia/Darwin" , 0x05FAA8 }, - { (char*) "Australia/Eucla" , 0x05FC0B }, - { (char*) "Australia/Hobart" , 0x05FDF8 }, - { (char*) "Australia/LHI" , 0x060742 }, - { (char*) "Australia/Lindeman" , 0x060E84 }, - { (char*) "Australia/Lord_Howe" , 0x06108A }, - { (char*) "Australia/Melbourne" , 0x0617DC }, - { (char*) "Australia/North" , 0x06207E }, - { (char*) "Australia/NSW" , 0x0621CF }, - { (char*) "Australia/Perth" , 0x062A69 }, - { (char*) "Australia/Queensland" , 0x062C51 }, - { (char*) "Australia/South" , 0x062E00 }, - { (char*) "Australia/Sydney" , 0x0636AC }, - { (char*) "Australia/Tasmania" , 0x063F62 }, - { (char*) "Australia/Victoria" , 0x0648A4 }, - { (char*) "Australia/West" , 0x06513E }, - { (char*) "Australia/Yancowinna" , 0x065308 }, - { (char*) "Brazil/Acre" , 0x065BC9 }, - { (char*) "Brazil/DeNoronha" , 0x065E3B }, - { (char*) "Brazil/East" , 0x066105 }, - { (char*) "Brazil/West" , 0x0666A7 }, - { (char*) "Canada/Atlantic" , 0x066901 }, - { (char*) "Canada/Central" , 0x06766D }, - { (char*) "Canada/Eastern" , 0x0681AD }, - { (char*) "Canada/Mountain" , 0x068F5F }, - { (char*) "Canada/Newfoundland" , 0x069887 }, - { (char*) "Canada/Pacific" , 0x06A6DA }, - { (char*) "Canada/Saskatchewan" , 0x06B232 }, - { (char*) "Canada/Yukon" , 0x06B612 }, - { (char*) "CET" , 0x06BC6C }, - { (char*) "Chile/Continental" , 0x06C7ED }, - { (char*) "Chile/EasterIsland" , 0x06D1CC }, - { (char*) "CST6CDT" , 0x06DA83 }, - { (char*) "Cuba" , 0x06E897 }, - { (char*) "EET" , 0x06F213 }, - { (char*) "Egypt" , 0x06FAF5 }, - { (char*) "Eire" , 0x070460 }, - { (char*) "EST" , 0x071210 }, - { (char*) "EST5EDT" , 0x0712D2 }, - { (char*) "Etc/GMT" , 0x0720BE }, - { (char*) "Etc/GMT+0" , 0x07213C }, - { (char*) "Etc/GMT+1" , 0x0721BA }, - { (char*) "Etc/GMT+10" , 0x07223A }, - { (char*) "Etc/GMT+11" , 0x0722BB }, - { (char*) "Etc/GMT+12" , 0x07233C }, - { (char*) "Etc/GMT+2" , 0x0723BD }, - { (char*) "Etc/GMT+3" , 0x07243D }, - { (char*) "Etc/GMT+4" , 0x0724BD }, - { (char*) "Etc/GMT+5" , 0x07253D }, - { (char*) "Etc/GMT+6" , 0x0725BD }, - { (char*) "Etc/GMT+7" , 0x07263D }, - { (char*) "Etc/GMT+8" , 0x0726BD }, - { (char*) "Etc/GMT+9" , 0x07273D }, - { (char*) "Etc/GMT-0" , 0x0727BD }, - { (char*) "Etc/GMT-1" , 0x07283B }, - { (char*) "Etc/GMT-10" , 0x0728BC }, - { (char*) "Etc/GMT-11" , 0x07293E }, - { (char*) "Etc/GMT-12" , 0x0729C0 }, - { (char*) "Etc/GMT-13" , 0x072A42 }, - { (char*) "Etc/GMT-14" , 0x072AC4 }, - { (char*) "Etc/GMT-2" , 0x072B46 }, - { (char*) "Etc/GMT-3" , 0x072BC7 }, - { (char*) "Etc/GMT-4" , 0x072C48 }, - { (char*) "Etc/GMT-5" , 0x072CC9 }, - { (char*) "Etc/GMT-6" , 0x072D4A }, - { (char*) "Etc/GMT-7" , 0x072DCB }, - { (char*) "Etc/GMT-8" , 0x072E4C }, - { (char*) "Etc/GMT-9" , 0x072ECD }, - { (char*) "Etc/GMT0" , 0x072F4E }, - { (char*) "Etc/Greenwich" , 0x072FCC }, - { (char*) "Etc/UCT" , 0x07304A }, - { (char*) "Etc/Universal" , 0x0730C8 }, - { (char*) "Etc/UTC" , 0x073146 }, - { (char*) "Etc/Zulu" , 0x0731C4 }, - { (char*) "Europe/Amsterdam" , 0x073242 }, - { (char*) "Europe/Andorra" , 0x073DAC }, - { (char*) "Europe/Astrakhan" , 0x074486 }, - { (char*) "Europe/Athens" , 0x074923 }, - { (char*) "Europe/Belfast" , 0x075205 }, - { (char*) "Europe/Belgrade" , 0x076061 }, - { (char*) "Europe/Berlin" , 0x0767ED }, - { (char*) "Europe/Bratislava" , 0x077102 }, - { (char*) "Europe/Brussels" , 0x077A0B }, - { (char*) "Europe/Bucharest" , 0x07858C }, - { (char*) "Europe/Budapest" , 0x078E20 }, - { (char*) "Europe/Busingen" , 0x07976C }, - { (char*) "Europe/Chisinau" , 0x079EF5 }, - { (char*) "Europe/Copenhagen" , 0x07A879 }, - { (char*) "Europe/Dublin" , 0x07B0DE }, - { (char*) "Europe/Gibraltar" , 0x07BE8E }, - { (char*) "Europe/Guernsey" , 0x07CA96 }, - { (char*) "Europe/Helsinki" , 0x07D936 }, - { (char*) "Europe/Isle_of_Man" , 0x07E0AE }, - { (char*) "Europe/Istanbul" , 0x07EEFA }, - { (char*) "Europe/Jersey" , 0x07F693 }, - { (char*) "Europe/Kaliningrad" , 0x080533 }, - { (char*) "Europe/Kiev" , 0x080B28 }, - { (char*) "Europe/Kirov" , 0x08137C }, - { (char*) "Europe/Kyiv" , 0x081837 }, - { (char*) "Europe/Lisbon" , 0x08209A }, - { (char*) "Europe/Ljubljana" , 0x082E80 }, - { (char*) "Europe/London" , 0x08360C }, - { (char*) "Europe/Luxembourg" , 0x084468 }, - { (char*) "Europe/Madrid" , 0x084FF6 }, - { (char*) "Europe/Malta" , 0x085A48 }, - { (char*) "Europe/Mariehamn" , 0x086490 }, - { (char*) "Europe/Minsk" , 0x086C08 }, - { (char*) "Europe/Monaco" , 0x08712F }, - { (char*) "Europe/Moscow" , 0x087CBB }, - { (char*) "Europe/Nicosia" , 0x0882DA }, - { (char*) "Europe/Oslo" , 0x088AB8 }, - { (char*) "Europe/Paris" , 0x089378 }, - { (char*) "Europe/Podgorica" , 0x089F16 }, - { (char*) "Europe/Prague" , 0x08A6A2 }, - { (char*) "Europe/Riga" , 0x08AFAB }, - { (char*) "Europe/Rome" , 0x08B84D }, - { (char*) "Europe/Samara" , 0x08C2AA }, - { (char*) "Europe/San_Marino" , 0x08C780 }, - { (char*) "Europe/Sarajevo" , 0x08D1DD }, - { (char*) "Europe/Saratov" , 0x08D969 }, - { (char*) "Europe/Simferopol" , 0x08DE16 }, - { (char*) "Europe/Skopje" , 0x08E3E5 }, - { (char*) "Europe/Sofia" , 0x08EB71 }, - { (char*) "Europe/Stockholm" , 0x08F39A }, - { (char*) "Europe/Tallinn" , 0x08FB1B }, - { (char*) "Europe/Tirane" , 0x09038B }, - { (char*) "Europe/Tiraspol" , 0x090BBB }, - { (char*) "Europe/Ulyanovsk" , 0x09153F }, - { (char*) "Europe/Uzhgorod" , 0x091A42 }, - { (char*) "Europe/Vaduz" , 0x092296 }, - { (char*) "Europe/Vatican" , 0x092A02 }, - { (char*) "Europe/Vienna" , 0x09345F }, - { (char*) "Europe/Vilnius" , 0x093D03 }, - { (char*) "Europe/Volgograd" , 0x094581 }, - { (char*) "Europe/Warsaw" , 0x094A48 }, - { (char*) "Europe/Zagreb" , 0x0954B2 }, - { (char*) "Europe/Zaporozhye" , 0x095C3E }, - { (char*) "Europe/Zurich" , 0x096492 }, - { (char*) "Factory" , 0x096C13 }, - { (char*) "GB" , 0x096C93 }, - { (char*) "GB-Eire" , 0x097AEF }, - { (char*) "GMT" , 0x09894B }, - { (char*) "GMT+0" , 0x0989C9 }, - { (char*) "GMT-0" , 0x098A47 }, - { (char*) "GMT0" , 0x098AC5 }, - { (char*) "Greenwich" , 0x098B43 }, - { (char*) "Hongkong" , 0x098BC1 }, - { (char*) "HST" , 0x09909E }, - { (char*) "Iceland" , 0x0991F3 }, - { (char*) "Indian/Antananarivo" , 0x099293 }, - { (char*) "Indian/Chagos" , 0x09937A }, - { (char*) "Indian/Christmas" , 0x09943F }, - { (char*) "Indian/Cocos" , 0x0994E2 }, - { (char*) "Indian/Comoro" , 0x09958E }, - { (char*) "Indian/Kerguelen" , 0x09962F }, - { (char*) "Indian/Mahe" , 0x0996D2 }, - { (char*) "Indian/Maldives" , 0x099775 }, - { (char*) "Indian/Mauritius" , 0x09983A }, - { (char*) "Indian/Mayotte" , 0x099929 }, - { (char*) "Indian/Reunion" , 0x0999CA }, - { (char*) "Iran" , 0x099A6D }, - { (char*) "Israel" , 0x099F59 }, - { (char*) "Jamaica" , 0x09A8B9 }, - { (char*) "Japan" , 0x09AAA7 }, - { (char*) "Kwajalein" , 0x09ABE8 }, - { (char*) "Libya" , 0x09AD22 }, - { (char*) "MET" , 0x09AF9F }, - { (char*) "Mexico/BajaNorte" , 0x09BB20 }, - { (char*) "Mexico/BajaSur" , 0x09C686 }, - { (char*) "Mexico/General" , 0x09CAB6 }, - { (char*) "MST" , 0x09CF88 }, - { (char*) "MST7MDT" , 0x09D0FC }, - { (char*) "Navajo" , 0x09DAA4 }, - { (char*) "NZ" , 0x09E44C }, - { (char*) "NZ-CHAT" , 0x09EDDD }, - { (char*) "Pacific/Apia" , 0x09F5EF }, - { (char*) "Pacific/Auckland" , 0x09F851 }, - { (char*) "Pacific/Bougainville" , 0x0A01F5 }, - { (char*) "Pacific/Chatham" , 0x0A030B }, - { (char*) "Pacific/Chuuk" , 0x0A0B2C }, - { (char*) "Pacific/Easter" , 0x0A0C46 }, - { (char*) "Pacific/Efate" , 0x0A150A }, - { (char*) "Pacific/Enderbury" , 0x0A1722 }, - { (char*) "Pacific/Fakaofo" , 0x0A180A }, - { (char*) "Pacific/Fiji" , 0x0A18D0 }, - { (char*) "Pacific/Funafuti" , 0x0A1B10 }, - { (char*) "Pacific/Galapagos" , 0x0A1BB4 }, - { (char*) "Pacific/Gambier" , 0x0A1CB1 }, - { (char*) "Pacific/Guadalcanal" , 0x0A1D62 }, - { (char*) "Pacific/Guam" , 0x0A1E06 }, - { (char*) "Pacific/Honolulu" , 0x0A2000 }, - { (char*) "Pacific/Johnston" , 0x0A215B }, - { (char*) "Pacific/Kanton" , 0x0A22B0 }, - { (char*) "Pacific/Kiritimati" , 0x0A23A7 }, - { (char*) "Pacific/Kosrae" , 0x0A249F }, - { (char*) "Pacific/Kwajalein" , 0x0A2602 }, - { (char*) "Pacific/Majuro" , 0x0A2745 }, - { (char*) "Pacific/Marquesas" , 0x0A2891 }, - { (char*) "Pacific/Midway" , 0x0A294D }, - { (char*) "Pacific/Nauru" , 0x0A2A40 }, - { (char*) "Pacific/Niue" , 0x0A2B3A }, - { (char*) "Pacific/Norfolk" , 0x0A2C03 }, - { (char*) "Pacific/Noumea" , 0x0A2F71 }, - { (char*) "Pacific/Pago_Pago" , 0x0A309F }, - { (char*) "Pacific/Palau" , 0x0A315A }, - { (char*) "Pacific/Pitcairn" , 0x0A320C }, - { (char*) "Pacific/Pohnpei" , 0x0A32D4 }, - { (char*) "Pacific/Ponape" , 0x0A340F }, - { (char*) "Pacific/Port_Moresby" , 0x0A34B3 }, - { (char*) "Pacific/Rarotonga" , 0x0A3583 }, - { (char*) "Pacific/Saipan" , 0x0A37DC }, - { (char*) "Pacific/Samoa" , 0x0A39C8 }, - { (char*) "Pacific/Tahiti" , 0x0A3A83 }, - { (char*) "Pacific/Tarawa" , 0x0A3B35 }, - { (char*) "Pacific/Tongatapu" , 0x0A3BE8 }, - { (char*) "Pacific/Truk" , 0x0A3D5A }, - { (char*) "Pacific/Wake" , 0x0A3E12 }, - { (char*) "Pacific/Wallis" , 0x0A3EC1 }, - { (char*) "Pacific/Yap" , 0x0A3F65 }, - { (char*) "Poland" , 0x0A401D }, - { (char*) "Portugal" , 0x0A4A87 }, - { (char*) "PRC" , 0x0A585A }, - { (char*) "PST8PDT" , 0x0A5A97 }, - { (char*) "ROC" , 0x0A65C7 }, - { (char*) "ROK" , 0x0A68CC }, - { (char*) "Singapore" , 0x0A6B41 }, - { (char*) "Turkey" , 0x0A6CDE }, - { (char*) "UCT" , 0x0A7477 }, - { (char*) "Universal" , 0x0A74F5 }, - { (char*) "US/Alaska" , 0x0A7573 }, - { (char*) "US/Aleutian" , 0x0A7EC2 }, - { (char*) "US/Arizona" , 0x0A8802 }, - { (char*) "US/Central" , 0x0A8976 }, - { (char*) "US/East-Indiana" , 0x0A978A }, - { (char*) "US/Eastern" , 0x0A9E28 }, - { (char*) "US/Hawaii" , 0x0AAC14 }, - { (char*) "US/Indiana-Starke" , 0x0AAD69 }, - { (char*) "US/Michigan" , 0x0AB701 }, - { (char*) "US/Mountain" , 0x0ABFC3 }, - { (char*) "US/Pacific" , 0x0AC96B }, - { (char*) "US/Samoa" , 0x0AD49B }, - { (char*) "UTC" , 0x0AD556 }, - { (char*) "W-SU" , 0x0AD5D4 }, - { (char*) "WET" , 0x0ADBDF }, - { (char*) "Zulu" , 0x0AE9B2 }, + { (char*) "Africa/Ceuta" , 0x001DE2 }, + { (char*) "Africa/Conakry" , 0x002600 }, + { (char*) "Africa/Dakar" , 0x0026DC }, + { (char*) "Africa/Dar_es_Salaam" , 0x00279E }, + { (char*) "Africa/Djibouti" , 0x00287F }, + { (char*) "Africa/Douala" , 0x002920 }, + { (char*) "Africa/El_Aaiun" , 0x0029C1 }, + { (char*) "Africa/Freetown" , 0x002DF7 }, + { (char*) "Africa/Gaborone" , 0x002FD3 }, + { (char*) "Africa/Harare" , 0x0030CA }, + { (char*) "Africa/Johannesburg" , 0x00316B }, + { (char*) "Africa/Juba" , 0x00326D }, + { (char*) "Africa/Kampala" , 0x003520 }, + { (char*) "Africa/Khartoum" , 0x003627 }, + { (char*) "Africa/Kigali" , 0x0038DA }, + { (char*) "Africa/Kinshasa" , 0x00397B }, + { (char*) "Africa/Lagos" , 0x003A35 }, + { (char*) "Africa/Libreville" , 0x003B2C }, + { (char*) "Africa/Lome" , 0x003BCD }, + { (char*) "Africa/Luanda" , 0x003C6D }, + { (char*) "Africa/Lubumbashi" , 0x003D34 }, + { (char*) "Africa/Lusaka" , 0x003E10 }, + { (char*) "Africa/Malabo" , 0x003EB1 }, + { (char*) "Africa/Maputo" , 0x003F74 }, + { (char*) "Africa/Maseru" , 0x004015 }, + { (char*) "Africa/Mbabane" , 0x0040E1 }, + { (char*) "Africa/Mogadishu" , 0x004185 }, + { (char*) "Africa/Monrovia" , 0x004266 }, + { (char*) "Africa/Nairobi" , 0x004342 }, + { (char*) "Africa/Ndjamena" , 0x004457 }, + { (char*) "Africa/Niamey" , 0x00452A }, + { (char*) "Africa/Nouakchott" , 0x00460F }, + { (char*) "Africa/Ouagadougou" , 0x0046EB }, + { (char*) "Africa/Porto-Novo" , 0x00478B }, + { (char*) "Africa/Sao_Tome" , 0x00484E }, + { (char*) "Africa/Timbuktu" , 0x004958 }, + { (char*) "Africa/Tripoli" , 0x0049F8 }, + { (char*) "Africa/Tunis" , 0x004C75 }, + { (char*) "Africa/Windhoek" , 0x004F32 }, + { (char*) "America/Adak" , 0x0052F9 }, + { (char*) "America/Anchorage" , 0x005C53 }, + { (char*) "America/Anguilla" , 0x0065B5 }, + { (char*) "America/Antigua" , 0x006655 }, + { (char*) "America/Araguaina" , 0x006717 }, + { (char*) "America/Argentina/Buenos_Aires" , 0x006A92 }, + { (char*) "America/Argentina/Catamarca" , 0x006ED9 }, + { (char*) "America/Argentina/ComodRivadavia" , 0x007326 }, + { (char*) "America/Argentina/Cordoba" , 0x007758 }, + { (char*) "America/Argentina/Jujuy" , 0x007BC0 }, + { (char*) "America/Argentina/La_Rioja" , 0x007FE0 }, + { (char*) "America/Argentina/Mendoza" , 0x00842D }, + { (char*) "America/Argentina/Rio_Gallegos" , 0x00886B }, + { (char*) "America/Argentina/Salta" , 0x008CAC }, + { (char*) "America/Argentina/San_Juan" , 0x0090D8 }, + { (char*) "America/Argentina/San_Luis" , 0x009525 }, + { (char*) "America/Argentina/Tucuman" , 0x00997E }, + { (char*) "America/Argentina/Ushuaia" , 0x009DD8 }, + { (char*) "America/Aruba" , 0x00A21F }, + { (char*) "America/Asuncion" , 0x00A2E5 }, + { (char*) "America/Atikokan" , 0x00A95D }, + { (char*) "America/Atka" , 0x00AADA }, + { (char*) "America/Bahia" , 0x00B41A }, + { (char*) "America/Bahia_Banderas" , 0x00B81D }, + { (char*) "America/Barbados" , 0x00BC86 }, + { (char*) "America/Belem" , 0x00BE46 }, + { (char*) "America/Belize" , 0x00C096 }, + { (char*) "America/Blanc-Sablon" , 0x00C6F0 }, + { (char*) "America/Boa_Vista" , 0x00C842 }, + { (char*) "America/Bogota" , 0x00CABF }, + { (char*) "America/Boise" , 0x00CBB3 }, + { (char*) "America/Buenos_Aires" , 0x00D549 }, + { (char*) "America/Cambridge_Bay" , 0x00D97B }, + { (char*) "America/Campo_Grande" , 0x00E269 }, + { (char*) "America/Cancun" , 0x00E81D }, + { (char*) "America/Caracas" , 0x00EB95 }, + { (char*) "America/Catamarca" , 0x00EC9B }, + { (char*) "America/Cayenne" , 0x00F0CD }, + { (char*) "America/Cayman" , 0x00F191 }, + { (char*) "America/Chicago" , 0x00F253 }, + { (char*) "America/Chihuahua" , 0x01007B }, + { (char*) "America/Ciudad_Juarez" , 0x0104EB }, + { (char*) "America/Coral_Harbour" , 0x010B15 }, + { (char*) "America/Cordoba" , 0x010BD7 }, + { (char*) "America/Costa_Rica" , 0x011009 }, + { (char*) "America/Coyhaique" , 0x011151 }, + { (char*) "America/Creston" , 0x0119B7 }, + { (char*) "America/Cuiaba" , 0x011AA5 }, + { (char*) "America/Curacao" , 0x012036 }, + { (char*) "America/Danmarkshavn" , 0x0120FC }, + { (char*) "America/Dawson" , 0x0123DC }, + { (char*) "America/Dawson_Creek" , 0x012A48 }, + { (char*) "America/Denver" , 0x012E8E }, + { (char*) "America/Detroit" , 0x01384B }, + { (char*) "America/Dominica" , 0x014126 }, + { (char*) "America/Edmonton" , 0x0141C6 }, + { (char*) "America/Eirunepe" , 0x0149DD }, + { (char*) "America/El_Salvador" , 0x014C7A }, + { (char*) "America/Ensenada" , 0x014D66 }, + { (char*) "America/Fort_Nelson" , 0x0158CC }, + { (char*) "America/Fort_Wayne" , 0x0161AC }, + { (char*) "America/Fortaleza" , 0x01684A }, + { (char*) "America/Glace_Bay" , 0x016B3A }, + { (char*) "America/Godthab" , 0x0173F1 }, + { (char*) "America/Goose_Bay" , 0x017B5E }, + { (char*) "America/Grand_Turk" , 0x018814 }, + { (char*) "America/Grenada" , 0x018F4A }, + { (char*) "America/Guadeloupe" , 0x018FEA }, + { (char*) "America/Guatemala" , 0x01908A }, + { (char*) "America/Guayaquil" , 0x0191AE }, + { (char*) "America/Guyana" , 0x0192B4 }, + { (char*) "America/Halifax" , 0x0193B8 }, + { (char*) "America/Havana" , 0x01A142 }, + { (char*) "America/Hermosillo" , 0x01AABE }, + { (char*) "America/Indiana/Indianapolis" , 0x01AC54 }, + { (char*) "America/Indiana/Knox" , 0x01B30B }, + { (char*) "America/Indiana/Marengo" , 0x01BCB8 }, + { (char*) "America/Indiana/Petersburg" , 0x01C3A5 }, + { (char*) "America/Indiana/Tell_City" , 0x01CB44 }, + { (char*) "America/Indiana/Vevay" , 0x01D208 }, + { (char*) "America/Indiana/Vincennes" , 0x01D7C4 }, + { (char*) "America/Indiana/Winamac" , 0x01DE9A }, + { (char*) "America/Indianapolis" , 0x01E5BE }, + { (char*) "America/Inuvik" , 0x01EC5C }, + { (char*) "America/Iqaluit" , 0x01F496 }, + { (char*) "America/Jamaica" , 0x01FD55 }, + { (char*) "America/Jujuy" , 0x01FF43 }, + { (char*) "America/Juneau" , 0x020359 }, + { (char*) "America/Kentucky/Louisville" , 0x020CAA }, + { (char*) "America/Kentucky/Monticello" , 0x0217B8 }, + { (char*) "America/Knox_IN" , 0x022118 }, + { (char*) "America/Kralendijk" , 0x022AB0 }, + { (char*) "America/La_Paz" , 0x022BB2 }, + { (char*) "America/Lima" , 0x022C98 }, + { (char*) "America/Los_Angeles" , 0x022E2C }, + { (char*) "America/Louisville" , 0x023963 }, + { (char*) "America/Lower_Princes" , 0x024453 }, + { (char*) "America/Maceio" , 0x024555 }, + { (char*) "America/Managua" , 0x02484B }, + { (char*) "America/Manaus" , 0x024A05 }, + { (char*) "America/Marigot" , 0x024C6E }, + { (char*) "America/Martinique" , 0x024D70 }, + { (char*) "America/Matamoros" , 0x024E64 }, + { (char*) "America/Mazatlan" , 0x025426 }, + { (char*) "America/Mendoza" , 0x025888 }, + { (char*) "America/Menominee" , 0x025CBA }, + { (char*) "America/Merida" , 0x0265C7 }, + { (char*) "America/Metlakatla" , 0x0269D0 }, + { (char*) "America/Mexico_City" , 0x026F82 }, + { (char*) "America/Miquelon" , 0x027462 }, + { (char*) "America/Moncton" , 0x027AE2 }, + { (char*) "America/Monterrey" , 0x028758 }, + { (char*) "America/Montevideo" , 0x028BF4 }, + { (char*) "America/Montreal" , 0x0291D8 }, + { (char*) "America/Montserrat" , 0x029F8A }, + { (char*) "America/Nassau" , 0x02A02A }, + { (char*) "America/New_York" , 0x02A98A }, + { (char*) "America/Nipigon" , 0x02B78A }, + { (char*) "America/Nome" , 0x02C53C }, + { (char*) "America/Noronha" , 0x02CE94 }, + { (char*) "America/North_Dakota/Beulah" , 0x02D16E }, + { (char*) "America/North_Dakota/Center" , 0x02DAEB }, + { (char*) "America/North_Dakota/New_Salem" , 0x02E468 }, + { (char*) "America/Nuuk" , 0x02EDEB }, + { (char*) "America/Ojinaga" , 0x02F569 }, + { (char*) "America/Panama" , 0x02FB85 }, + { (char*) "America/Pangnirtung" , 0x02FC47 }, + { (char*) "America/Paramaribo" , 0x0304ED }, + { (char*) "America/Phoenix" , 0x0305F1 }, + { (char*) "America/Port-au-Prince" , 0x03077D }, + { (char*) "America/Port_of_Spain" , 0x030D23 }, + { (char*) "America/Porto_Acre" , 0x030DC3 }, + { (char*) "America/Porto_Velho" , 0x031035 }, + { (char*) "America/Puerto_Rico" , 0x03127B }, + { (char*) "America/Punta_Arenas" , 0x03137D }, + { (char*) "America/Rainy_River" , 0x031B08 }, + { (char*) "America/Rankin_Inlet" , 0x032648 }, + { (char*) "America/Recife" , 0x032E7C }, + { (char*) "America/Regina" , 0x033150 }, + { (char*) "America/Resolute" , 0x033545 }, + { (char*) "America/Rio_Branco" , 0x033D7A }, + { (char*) "America/Rosario" , 0x033FF0 }, + { (char*) "America/Santa_Isabel" , 0x034422 }, + { (char*) "America/Santarem" , 0x034F88 }, + { (char*) "America/Santiago" , 0x0351EB }, + { (char*) "America/Santo_Domingo" , 0x035BD7 }, + { (char*) "America/Sao_Paulo" , 0x035DAD }, + { (char*) "America/Scoresbysund" , 0x036385 }, + { (char*) "America/Shiprock" , 0x036B3D }, + { (char*) "America/Sitka" , 0x0374E5 }, + { (char*) "America/St_Barthelemy" , 0x037E1D }, + { (char*) "America/St_Johns" , 0x037F1F }, + { (char*) "America/St_Kitts" , 0x038D8D }, + { (char*) "America/St_Lucia" , 0x038E2D }, + { (char*) "America/St_Thomas" , 0x038EEF }, + { (char*) "America/St_Vincent" , 0x038F8F }, + { (char*) "America/Swift_Current" , 0x039051 }, + { (char*) "America/Tegucigalpa" , 0x03929F }, + { (char*) "America/Thule" , 0x0393A7 }, + { (char*) "America/Thunder_Bay" , 0x03999F }, + { (char*) "America/Tijuana" , 0x03A751 }, + { (char*) "America/Toronto" , 0x03B2C6 }, + { (char*) "America/Tortola" , 0x03C096 }, + { (char*) "America/Vancouver" , 0x03C136 }, + { (char*) "America/Virgin" , 0x03CB75 }, + { (char*) "America/Whitehorse" , 0x03CC77 }, + { (char*) "America/Winnipeg" , 0x03D2E3 }, + { (char*) "America/Yakutat" , 0x03DE40 }, + { (char*) "America/Yellowknife" , 0x03E75D }, + { (char*) "Antarctica/Casey" , 0x03EF57 }, + { (char*) "Antarctica/Davis" , 0x03F10F }, + { (char*) "Antarctica/DumontDUrville" , 0x03F23B }, + { (char*) "Antarctica/Macquarie" , 0x03F30B }, + { (char*) "Antarctica/Mawson" , 0x03FBFB }, + { (char*) "Antarctica/McMurdo" , 0x03FCC6 }, + { (char*) "Antarctica/Palmer" , 0x0404C1 }, + { (char*) "Antarctica/Rothera" , 0x040A4F }, + { (char*) "Antarctica/South_Pole" , 0x040AF8 }, + { (char*) "Antarctica/Syowa" , 0x041489 }, + { (char*) "Antarctica/Troll" , 0x041531 }, + { (char*) "Antarctica/Vostok" , 0x0419BE }, + { (char*) "Arctic/Longyearbyen" , 0x041AA5 }, + { (char*) "Asia/Aden" , 0x0423AB }, + { (char*) "Asia/Almaty" , 0x04244E }, + { (char*) "Asia/Amman" , 0x042843 }, + { (char*) "Asia/Anadyr" , 0x042DE8 }, + { (char*) "Asia/Aqtau" , 0x04329D }, + { (char*) "Asia/Aqtobe" , 0x043687 }, + { (char*) "Asia/Ashgabat" , 0x043A85 }, + { (char*) "Asia/Ashkhabad" , 0x043CEE }, + { (char*) "Asia/Atyrau" , 0x043F57 }, + { (char*) "Asia/Baghdad" , 0x044349 }, + { (char*) "Asia/Bahrain" , 0x04471E }, + { (char*) "Asia/Baku" , 0x044809 }, + { (char*) "Asia/Bangkok" , 0x044CD2 }, + { (char*) "Asia/Barnaul" , 0x044D97 }, + { (char*) "Asia/Beirut" , 0x045268 }, + { (char*) "Asia/Bishkek" , 0x045ADE }, + { (char*) "Asia/Brunei" , 0x045EB3 }, + { (char*) "Asia/Calcutta" , 0x045F7C }, + { (char*) "Asia/Chita" , 0x0460A5 }, + { (char*) "Asia/Choibalsan" , 0x04657C }, + { (char*) "Asia/Chongqing" , 0x0468F5 }, + { (char*) "Asia/Chungking" , 0x046B32 }, + { (char*) "Asia/Colombo" , 0x046D6F }, + { (char*) "Asia/Dacca" , 0x046EE1 }, + { (char*) "Asia/Damascus" , 0x047030 }, + { (char*) "Asia/Dhaka" , 0x04778D }, + { (char*) "Asia/Dili" , 0x0478DC }, + { (char*) "Asia/Dubai" , 0x0479E9 }, + { (char*) "Asia/Dushanbe" , 0x047A8C }, + { (char*) "Asia/Famagusta" , 0x047CD9 }, + { (char*) "Asia/Gaza" , 0x0484E0 }, + { (char*) "Asia/Harbin" , 0x0493FA }, + { (char*) "Asia/Hebron" , 0x049637 }, + { (char*) "Asia/Ho_Chi_Minh" , 0x04A56C }, + { (char*) "Asia/Hong_Kong" , 0x04A6C1 }, + { (char*) "Asia/Hovd" , 0x04AB9E }, + { (char*) "Asia/Irkutsk" , 0x04AF2D }, + { (char*) "Asia/Istanbul" , 0x04B420 }, + { (char*) "Asia/Jakarta" , 0x04BBB9 }, + { (char*) "Asia/Jayapura" , 0x04BD51 }, + { (char*) "Asia/Jerusalem" , 0x04BE70 }, + { (char*) "Asia/Kabul" , 0x04C7D0 }, + { (char*) "Asia/Kamchatka" , 0x04C89E }, + { (char*) "Asia/Karachi" , 0x04CD3C }, + { (char*) "Asia/Kashgar" , 0x04CEC3 }, + { (char*) "Asia/Kathmandu" , 0x04CF66 }, + { (char*) "Asia/Katmandu" , 0x04D038 }, + { (char*) "Asia/Khandyga" , 0x04D10A }, + { (char*) "Asia/Kolkata" , 0x04D61D }, + { (char*) "Asia/Krasnoyarsk" , 0x04D746 }, + { (char*) "Asia/Kuala_Lumpur" , 0x04DC14 }, + { (char*) "Asia/Kuching" , 0x04DDC5 }, + { (char*) "Asia/Kuwait" , 0x04DFB4 }, + { (char*) "Asia/Macao" , 0x04E057 }, + { (char*) "Asia/Macau" , 0x04E52E }, + { (char*) "Asia/Magadan" , 0x04EA05 }, + { (char*) "Asia/Makassar" , 0x04EED9 }, + { (char*) "Asia/Manila" , 0x04F02C }, + { (char*) "Asia/Muscat" , 0x04F1DE }, + { (char*) "Asia/Nicosia" , 0x04F281 }, + { (char*) "Asia/Novokuznetsk" , 0x04FA6D }, + { (char*) "Asia/Novosibirsk" , 0x04FF09 }, + { (char*) "Asia/Omsk" , 0x0503E0 }, + { (char*) "Asia/Oral" , 0x0508A2 }, + { (char*) "Asia/Phnom_Penh" , 0x050C9C }, + { (char*) "Asia/Pontianak" , 0x050DB9 }, + { (char*) "Asia/Pyongyang" , 0x050F3C }, + { (char*) "Asia/Qatar" , 0x051035 }, + { (char*) "Asia/Qostanay" , 0x0510FA }, + { (char*) "Asia/Qyzylorda" , 0x051521 }, + { (char*) "Asia/Rangoon" , 0x05193D }, + { (char*) "Asia/Riyadh" , 0x051A47 }, + { (char*) "Asia/Saigon" , 0x051AEA }, + { (char*) "Asia/Sakhalin" , 0x051C3F }, + { (char*) "Asia/Samarkand" , 0x052107 }, + { (char*) "Asia/Seoul" , 0x052357 }, + { (char*) "Asia/Shanghai" , 0x0525CC }, + { (char*) "Asia/Singapore" , 0x052815 }, + { (char*) "Asia/Srednekolymsk" , 0x0529B2 }, + { (char*) "Asia/Taipei" , 0x052E86 }, + { (char*) "Asia/Tashkent" , 0x05318B }, + { (char*) "Asia/Tbilisi" , 0x0533E9 }, + { (char*) "Asia/Tehran" , 0x0537E4 }, + { (char*) "Asia/Tel_Aviv" , 0x053CD0 }, + { (char*) "Asia/Thimbu" , 0x054630 }, + { (char*) "Asia/Thimphu" , 0x0546F9 }, + { (char*) "Asia/Tokyo" , 0x0547C2 }, + { (char*) "Asia/Tomsk" , 0x054903 }, + { (char*) "Asia/Ujung_Pandang" , 0x054DD4 }, + { (char*) "Asia/Ulaanbaatar" , 0x054EDE }, + { (char*) "Asia/Ulan_Bator" , 0x055267 }, + { (char*) "Asia/Urumqi" , 0x0555E0 }, + { (char*) "Asia/Ust-Nera" , 0x055690 }, + { (char*) "Asia/Vientiane" , 0x055B86 }, + { (char*) "Asia/Vladivostok" , 0x055CBF }, + { (char*) "Asia/Yakutsk" , 0x056188 }, + { (char*) "Asia/Yangon" , 0x056650 }, + { (char*) "Asia/Yekaterinburg" , 0x05675A }, + { (char*) "Asia/Yerevan" , 0x056C41 }, + { (char*) "Atlantic/Azores" , 0x0570BE }, + { (char*) "Atlantic/Bermuda" , 0x057E42 }, + { (char*) "Atlantic/Canary" , 0x0587AA }, + { (char*) "Atlantic/Cape_Verde" , 0x058F2D }, + { (char*) "Atlantic/Faeroe" , 0x059039 }, + { (char*) "Atlantic/Faroe" , 0x05975C }, + { (char*) "Atlantic/Jan_Mayen" , 0x059E7F }, + { (char*) "Atlantic/Madeira" , 0x05A785 }, + { (char*) "Atlantic/Reykjavik" , 0x05B4D1 }, + { (char*) "Atlantic/South_Georgia" , 0x05B967 }, + { (char*) "Atlantic/St_Helena" , 0x05BA09 }, + { (char*) "Atlantic/Stanley" , 0x05BACB }, + { (char*) "Australia/ACT" , 0x05BF87 }, + { (char*) "Australia/Adelaide" , 0x05C821 }, + { (char*) "Australia/Brisbane" , 0x05D0DC }, + { (char*) "Australia/Broken_Hill" , 0x05D2A2 }, + { (char*) "Australia/Canberra" , 0x05DB7F }, + { (char*) "Australia/Currie" , 0x05E419 }, + { (char*) "Australia/Darwin" , 0x05ED5B }, + { (char*) "Australia/Eucla" , 0x05EEBE }, + { (char*) "Australia/Hobart" , 0x05F0AB }, + { (char*) "Australia/LHI" , 0x05F9F5 }, + { (char*) "Australia/Lindeman" , 0x060137 }, + { (char*) "Australia/Lord_Howe" , 0x06033D }, + { (char*) "Australia/Melbourne" , 0x060A8F }, + { (char*) "Australia/North" , 0x061331 }, + { (char*) "Australia/NSW" , 0x061482 }, + { (char*) "Australia/Perth" , 0x061D1C }, + { (char*) "Australia/Queensland" , 0x061F04 }, + { (char*) "Australia/South" , 0x0620B3 }, + { (char*) "Australia/Sydney" , 0x06295F }, + { (char*) "Australia/Tasmania" , 0x063215 }, + { (char*) "Australia/Victoria" , 0x063B57 }, + { (char*) "Australia/West" , 0x0643F1 }, + { (char*) "Australia/Yancowinna" , 0x0645BB }, + { (char*) "Brazil/Acre" , 0x064E7C }, + { (char*) "Brazil/DeNoronha" , 0x0650EE }, + { (char*) "Brazil/East" , 0x0653B8 }, + { (char*) "Brazil/West" , 0x06595A }, + { (char*) "Canada/Atlantic" , 0x065BB4 }, + { (char*) "Canada/Central" , 0x066920 }, + { (char*) "Canada/Eastern" , 0x067460 }, + { (char*) "Canada/Mountain" , 0x068212 }, + { (char*) "Canada/Newfoundland" , 0x068A0C }, + { (char*) "Canada/Pacific" , 0x06985F }, + { (char*) "Canada/Saskatchewan" , 0x06A289 }, + { (char*) "Canada/Yukon" , 0x06A669 }, + { (char*) "CET" , 0x06ACC3 }, + { (char*) "Chile/Continental" , 0x06B844 }, + { (char*) "Chile/EasterIsland" , 0x06C223 }, + { (char*) "CST6CDT" , 0x06CADA }, + { (char*) "Cuba" , 0x06D8EE }, + { (char*) "EET" , 0x06E26A }, + { (char*) "Egypt" , 0x06EB4C }, + { (char*) "Eire" , 0x06F4B7 }, + { (char*) "EST" , 0x070267 }, + { (char*) "EST5EDT" , 0x070329 }, + { (char*) "Etc/GMT" , 0x071115 }, + { (char*) "Etc/GMT+0" , 0x071193 }, + { (char*) "Etc/GMT+1" , 0x071211 }, + { (char*) "Etc/GMT+10" , 0x071291 }, + { (char*) "Etc/GMT+11" , 0x071312 }, + { (char*) "Etc/GMT+12" , 0x071393 }, + { (char*) "Etc/GMT+2" , 0x071414 }, + { (char*) "Etc/GMT+3" , 0x071494 }, + { (char*) "Etc/GMT+4" , 0x071514 }, + { (char*) "Etc/GMT+5" , 0x071594 }, + { (char*) "Etc/GMT+6" , 0x071614 }, + { (char*) "Etc/GMT+7" , 0x071694 }, + { (char*) "Etc/GMT+8" , 0x071714 }, + { (char*) "Etc/GMT+9" , 0x071794 }, + { (char*) "Etc/GMT-0" , 0x071814 }, + { (char*) "Etc/GMT-1" , 0x071892 }, + { (char*) "Etc/GMT-10" , 0x071913 }, + { (char*) "Etc/GMT-11" , 0x071995 }, + { (char*) "Etc/GMT-12" , 0x071A17 }, + { (char*) "Etc/GMT-13" , 0x071A99 }, + { (char*) "Etc/GMT-14" , 0x071B1B }, + { (char*) "Etc/GMT-2" , 0x071B9D }, + { (char*) "Etc/GMT-3" , 0x071C1E }, + { (char*) "Etc/GMT-4" , 0x071C9F }, + { (char*) "Etc/GMT-5" , 0x071D20 }, + { (char*) "Etc/GMT-6" , 0x071DA1 }, + { (char*) "Etc/GMT-7" , 0x071E22 }, + { (char*) "Etc/GMT-8" , 0x071EA3 }, + { (char*) "Etc/GMT-9" , 0x071F24 }, + { (char*) "Etc/GMT0" , 0x071FA5 }, + { (char*) "Etc/Greenwich" , 0x072023 }, + { (char*) "Etc/UCT" , 0x0720A1 }, + { (char*) "Etc/Universal" , 0x07211F }, + { (char*) "Etc/UTC" , 0x07219D }, + { (char*) "Etc/Zulu" , 0x07221B }, + { (char*) "Europe/Amsterdam" , 0x072299 }, + { (char*) "Europe/Andorra" , 0x072E03 }, + { (char*) "Europe/Astrakhan" , 0x0734DD }, + { (char*) "Europe/Athens" , 0x07397A }, + { (char*) "Europe/Belfast" , 0x07425C }, + { (char*) "Europe/Belgrade" , 0x0750B8 }, + { (char*) "Europe/Berlin" , 0x075844 }, + { (char*) "Europe/Bratislava" , 0x076159 }, + { (char*) "Europe/Brussels" , 0x076A62 }, + { (char*) "Europe/Bucharest" , 0x0775E3 }, + { (char*) "Europe/Budapest" , 0x077E77 }, + { (char*) "Europe/Busingen" , 0x0787C3 }, + { (char*) "Europe/Chisinau" , 0x078F4C }, + { (char*) "Europe/Copenhagen" , 0x0798D0 }, + { (char*) "Europe/Dublin" , 0x07A135 }, + { (char*) "Europe/Gibraltar" , 0x07AEE5 }, + { (char*) "Europe/Guernsey" , 0x07BAED }, + { (char*) "Europe/Helsinki" , 0x07C98D }, + { (char*) "Europe/Isle_of_Man" , 0x07D105 }, + { (char*) "Europe/Istanbul" , 0x07DF51 }, + { (char*) "Europe/Jersey" , 0x07E6EA }, + { (char*) "Europe/Kaliningrad" , 0x07F58A }, + { (char*) "Europe/Kiev" , 0x07FB7F }, + { (char*) "Europe/Kirov" , 0x0803D3 }, + { (char*) "Europe/Kyiv" , 0x08088E }, + { (char*) "Europe/Lisbon" , 0x0810F1 }, + { (char*) "Europe/Ljubljana" , 0x081ED7 }, + { (char*) "Europe/London" , 0x082663 }, + { (char*) "Europe/Luxembourg" , 0x0834BF }, + { (char*) "Europe/Madrid" , 0x08404D }, + { (char*) "Europe/Malta" , 0x084A9F }, + { (char*) "Europe/Mariehamn" , 0x0854E7 }, + { (char*) "Europe/Minsk" , 0x085C5F }, + { (char*) "Europe/Monaco" , 0x086186 }, + { (char*) "Europe/Moscow" , 0x086D12 }, + { (char*) "Europe/Nicosia" , 0x087331 }, + { (char*) "Europe/Oslo" , 0x087B0F }, + { (char*) "Europe/Paris" , 0x0883CF }, + { (char*) "Europe/Podgorica" , 0x088F6D }, + { (char*) "Europe/Prague" , 0x0896F9 }, + { (char*) "Europe/Riga" , 0x08A002 }, + { (char*) "Europe/Rome" , 0x08A8A4 }, + { (char*) "Europe/Samara" , 0x08B301 }, + { (char*) "Europe/San_Marino" , 0x08B7D7 }, + { (char*) "Europe/Sarajevo" , 0x08C234 }, + { (char*) "Europe/Saratov" , 0x08C9C0 }, + { (char*) "Europe/Simferopol" , 0x08CE6D }, + { (char*) "Europe/Skopje" , 0x08D43C }, + { (char*) "Europe/Sofia" , 0x08DBC8 }, + { (char*) "Europe/Stockholm" , 0x08E3F1 }, + { (char*) "Europe/Tallinn" , 0x08EB72 }, + { (char*) "Europe/Tirane" , 0x08F3E2 }, + { (char*) "Europe/Tiraspol" , 0x08FC12 }, + { (char*) "Europe/Ulyanovsk" , 0x090596 }, + { (char*) "Europe/Uzhgorod" , 0x090A99 }, + { (char*) "Europe/Vaduz" , 0x0912ED }, + { (char*) "Europe/Vatican" , 0x091A59 }, + { (char*) "Europe/Vienna" , 0x0924B6 }, + { (char*) "Europe/Vilnius" , 0x092D5A }, + { (char*) "Europe/Volgograd" , 0x0935D8 }, + { (char*) "Europe/Warsaw" , 0x093A9F }, + { (char*) "Europe/Zagreb" , 0x094509 }, + { (char*) "Europe/Zaporozhye" , 0x094C95 }, + { (char*) "Europe/Zurich" , 0x0954E9 }, + { (char*) "Factory" , 0x095C6A }, + { (char*) "GB" , 0x095CEA }, + { (char*) "GB-Eire" , 0x096B46 }, + { (char*) "GMT" , 0x0979A2 }, + { (char*) "GMT+0" , 0x097A20 }, + { (char*) "GMT-0" , 0x097A9E }, + { (char*) "GMT0" , 0x097B1C }, + { (char*) "Greenwich" , 0x097B9A }, + { (char*) "Hongkong" , 0x097C18 }, + { (char*) "HST" , 0x0980F5 }, + { (char*) "Iceland" , 0x09824A }, + { (char*) "Indian/Antananarivo" , 0x0982EA }, + { (char*) "Indian/Chagos" , 0x0983D1 }, + { (char*) "Indian/Christmas" , 0x098496 }, + { (char*) "Indian/Cocos" , 0x098539 }, + { (char*) "Indian/Comoro" , 0x0985E5 }, + { (char*) "Indian/Kerguelen" , 0x098686 }, + { (char*) "Indian/Mahe" , 0x098729 }, + { (char*) "Indian/Maldives" , 0x0987CC }, + { (char*) "Indian/Mauritius" , 0x098891 }, + { (char*) "Indian/Mayotte" , 0x098980 }, + { (char*) "Indian/Reunion" , 0x098A21 }, + { (char*) "Iran" , 0x098AC4 }, + { (char*) "Israel" , 0x098FB0 }, + { (char*) "Jamaica" , 0x099910 }, + { (char*) "Japan" , 0x099AFE }, + { (char*) "Kwajalein" , 0x099C3F }, + { (char*) "Libya" , 0x099D79 }, + { (char*) "MET" , 0x099FF6 }, + { (char*) "Mexico/BajaNorte" , 0x09AB77 }, + { (char*) "Mexico/BajaSur" , 0x09B6DD }, + { (char*) "Mexico/General" , 0x09BB0D }, + { (char*) "MST" , 0x09BFDF }, + { (char*) "MST7MDT" , 0x09C153 }, + { (char*) "Navajo" , 0x09CAFB }, + { (char*) "NZ" , 0x09D4A3 }, + { (char*) "NZ-CHAT" , 0x09DE34 }, + { (char*) "Pacific/Apia" , 0x09E646 }, + { (char*) "Pacific/Auckland" , 0x09E8A8 }, + { (char*) "Pacific/Bougainville" , 0x09F24C }, + { (char*) "Pacific/Chatham" , 0x09F362 }, + { (char*) "Pacific/Chuuk" , 0x09FB83 }, + { (char*) "Pacific/Easter" , 0x09FC9D }, + { (char*) "Pacific/Efate" , 0x0A0561 }, + { (char*) "Pacific/Enderbury" , 0x0A0779 }, + { (char*) "Pacific/Fakaofo" , 0x0A0861 }, + { (char*) "Pacific/Fiji" , 0x0A0927 }, + { (char*) "Pacific/Funafuti" , 0x0A0B67 }, + { (char*) "Pacific/Galapagos" , 0x0A0C0B }, + { (char*) "Pacific/Gambier" , 0x0A0D08 }, + { (char*) "Pacific/Guadalcanal" , 0x0A0DB9 }, + { (char*) "Pacific/Guam" , 0x0A0E5D }, + { (char*) "Pacific/Honolulu" , 0x0A1057 }, + { (char*) "Pacific/Johnston" , 0x0A11B2 }, + { (char*) "Pacific/Kanton" , 0x0A1307 }, + { (char*) "Pacific/Kiritimati" , 0x0A13FE }, + { (char*) "Pacific/Kosrae" , 0x0A14F6 }, + { (char*) "Pacific/Kwajalein" , 0x0A1659 }, + { (char*) "Pacific/Majuro" , 0x0A179C }, + { (char*) "Pacific/Marquesas" , 0x0A18E8 }, + { (char*) "Pacific/Midway" , 0x0A19A4 }, + { (char*) "Pacific/Nauru" , 0x0A1A97 }, + { (char*) "Pacific/Niue" , 0x0A1B91 }, + { (char*) "Pacific/Norfolk" , 0x0A1C5A }, + { (char*) "Pacific/Noumea" , 0x0A1FC8 }, + { (char*) "Pacific/Pago_Pago" , 0x0A20F6 }, + { (char*) "Pacific/Palau" , 0x0A21B1 }, + { (char*) "Pacific/Pitcairn" , 0x0A2263 }, + { (char*) "Pacific/Pohnpei" , 0x0A232B }, + { (char*) "Pacific/Ponape" , 0x0A2466 }, + { (char*) "Pacific/Port_Moresby" , 0x0A250A }, + { (char*) "Pacific/Rarotonga" , 0x0A25DA }, + { (char*) "Pacific/Saipan" , 0x0A2833 }, + { (char*) "Pacific/Samoa" , 0x0A2A1F }, + { (char*) "Pacific/Tahiti" , 0x0A2ADA }, + { (char*) "Pacific/Tarawa" , 0x0A2B8C }, + { (char*) "Pacific/Tongatapu" , 0x0A2C3F }, + { (char*) "Pacific/Truk" , 0x0A2DB1 }, + { (char*) "Pacific/Wake" , 0x0A2E69 }, + { (char*) "Pacific/Wallis" , 0x0A2F18 }, + { (char*) "Pacific/Yap" , 0x0A2FBC }, + { (char*) "Poland" , 0x0A3074 }, + { (char*) "Portugal" , 0x0A3ADE }, + { (char*) "PRC" , 0x0A48B1 }, + { (char*) "PST8PDT" , 0x0A4AEE }, + { (char*) "ROC" , 0x0A561E }, + { (char*) "ROK" , 0x0A5923 }, + { (char*) "Singapore" , 0x0A5B98 }, + { (char*) "Turkey" , 0x0A5D35 }, + { (char*) "UCT" , 0x0A64CE }, + { (char*) "Universal" , 0x0A654C }, + { (char*) "US/Alaska" , 0x0A65CA }, + { (char*) "US/Aleutian" , 0x0A6F19 }, + { (char*) "US/Arizona" , 0x0A7859 }, + { (char*) "US/Central" , 0x0A79CD }, + { (char*) "US/East-Indiana" , 0x0A87E1 }, + { (char*) "US/Eastern" , 0x0A8E7F }, + { (char*) "US/Hawaii" , 0x0A9C6B }, + { (char*) "US/Indiana-Starke" , 0x0A9DC0 }, + { (char*) "US/Michigan" , 0x0AA758 }, + { (char*) "US/Mountain" , 0x0AB01A }, + { (char*) "US/Pacific" , 0x0AB9C2 }, + { (char*) "US/Samoa" , 0x0AC4F2 }, + { (char*) "UTC" , 0x0AC5AD }, + { (char*) "W-SU" , 0x0AC62B }, + { (char*) "WET" , 0x0ACC36 }, + { (char*) "Zulu" , 0x0ADA09 }, }; -const unsigned char timelib_timezone_db_data_builtin[715312] = { +const unsigned char timelib_timezone_db_data_builtin[711303] = { /* Africa/Abidjan */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x43, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -25504,7 +25469,7 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Africa/Casablanca */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x4D, 0x41, 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, 0x5F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0x96, 0x51, 0xF9, 0x9C, +0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x96, 0x51, 0xF9, 0x9C, 0xC6, 0xFF, 0x14, 0x80, 0xC7, 0x58, 0xAC, 0x70, 0xC7, 0xD9, 0xED, 0x80, 0xD2, 0xA1, 0x32, 0xF0, 0xDB, 0x35, 0xA4, 0x00, 0xDB, 0xEE, 0x27, 0xF0, 0xFB, 0x25, 0x72, 0x40, 0xFB, 0xC2, 0xEF, 0x70, 0x08, 0x6B, 0x84, 0x80, 0x08, 0xC6, 0x6D, 0xF0, 0x0B, 0xE8, 0x0C, 0x00, 0x0C, 0x61, 0x47, 0xF0, @@ -25522,139 +25487,62 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5E, 0x9B, 0xB0, 0xA0, 0x5E, 0xD3, 0x0F, 0xA0, 0x60, 0x72, 0x58, 0x20, 0x60, 0xA0, 0x7C, 0xA0, 0x62, 0x3F, 0xC5, 0x20, 0x62, 0x77, 0x24, 0x20, 0x64, 0x16, 0x6C, 0xA0, 0x64, 0x44, 0x91, 0x20, 0x65, 0xED, 0x14, 0x20, 0x66, 0x1B, 0x38, 0xA0, 0x67, 0xBA, 0x81, 0x20, 0x67, 0xF1, 0xE0, 0x20, -0x69, 0x91, 0x28, 0xA0, 0x69, 0xBF, 0x4D, 0x20, 0x6B, 0x67, 0xD0, 0x20, 0x6B, 0x95, 0xF4, 0xA0, -0x6D, 0x35, 0x3D, 0x20, 0x6D, 0x6C, 0x9C, 0x20, 0x6F, 0x0B, 0xE4, 0xA0, 0x6F, 0x3A, 0x09, 0x20, -0x70, 0xD9, 0x51, 0xA0, 0x71, 0x10, 0xB0, 0xA0, 0x72, 0xAF, 0xF9, 0x20, 0x72, 0xDE, 0x1D, 0xA0, -0x74, 0x86, 0xA0, 0xA0, 0x74, 0xB4, 0xC5, 0x20, 0x76, 0x54, 0x0D, 0xA0, 0x76, 0x8B, 0x6C, 0xA0, -0x78, 0x2A, 0xB5, 0x20, 0x78, 0x58, 0xD9, 0xA0, 0x79, 0xF8, 0x22, 0x20, 0x7A, 0x2F, 0x81, 0x20, -0x7B, 0xCE, 0xC9, 0xA0, 0x7C, 0x06, 0x28, 0xA0, 0x7D, 0xA5, 0x71, 0x20, 0x7D, 0xD3, 0x95, 0xA0, -0x7F, 0x72, 0xDE, 0x20, 0x7F, 0xAA, 0x3D, 0x20, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x69, 0x91, 0x28, 0xA0, 0x69, 0xBF, 0x4D, 0x20, 0x6A, 0xAF, 0x30, 0x10, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0x04, -0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, -0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, -0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, -0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x04, 0x00, -0x00, 0x00, 0x00, 0x01, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, -0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, +0x03, 0x04, 0x03, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, +0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, +0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x31, 0x00, +0x2B, 0x30, 0x30, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0xC5, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xFF, -0xFF, 0x96, 0x51, 0xF9, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0xFF, 0x14, 0x80, 0xFF, 0xFF, 0xFF, -0xFF, 0xC7, 0x58, 0xAC, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xD9, 0xED, 0x80, 0xFF, 0xFF, 0xFF, -0xFF, 0xD2, 0xA1, 0x32, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0x35, 0xA4, 0x00, 0xFF, 0xFF, 0xFF, -0xFF, 0xDB, 0xEE, 0x27, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x25, 0x72, 0x40, 0xFF, 0xFF, 0xFF, -0xFF, 0xFB, 0xC2, 0xEF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x08, 0x6B, 0x84, 0x80, 0x00, 0x00, 0x00, -0x00, 0x08, 0xC6, 0x6D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0C, 0x61, 0x47, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC9, 0x3F, 0x80, 0x00, 0x00, 0x00, -0x00, 0x0E, 0x8E, 0xF2, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xD3, 0x51, 0x80, 0x00, 0x00, 0x00, -0x00, 0x10, 0x27, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xB7, 0xA6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x1E, 0x18, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0xE6, 0x80, 0x00, 0x00, 0x00, -0x00, 0x48, 0xBB, 0x22, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x23, 0x1A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x4A, 0x8D, 0xD5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xDC, 0xC0, 0x80, 0x00, 0x00, 0x00, -0x00, 0x4C, 0x5D, 0xE5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x97, 0xB8, 0x80, 0x00, 0x00, 0x00, -0x00, 0x4E, 0x34, 0x8C, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x9C, 0xA0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x50, 0x08, 0xBB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x31, 0x9A, 0x20, 0x00, 0x00, 0x00, -0x00, 0x50, 0x67, 0xA7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x51, 0x7C, 0x82, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x51, 0xD8, 0xCB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x05, 0x9E, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x52, 0x6C, 0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0x37, 0x7A, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x53, 0xAE, 0x21, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xDC, 0x46, 0x20, 0x00, 0x00, 0x00, -0x00, 0x54, 0x4C, 0x55, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x55, 0x17, 0x5C, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x55, 0x7C, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAB, 0x04, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x56, 0x2C, 0x37, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF7, 0x3E, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x57, 0x53, 0x87, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x57, 0x81, 0xAC, 0x20, 0x00, 0x00, 0x00, -0x00, 0x58, 0x15, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0xD7, 0x20, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x59, 0x20, 0xF4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x58, 0x53, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x59, 0xF5, 0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xB7, 0x02, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x5A, 0xF7, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x25, 0xC0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x5B, 0xD5, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xCE, 0x43, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x5C, 0xFC, 0x68, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x9B, 0xB0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x5E, 0xD3, 0x0F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x60, 0x72, 0x58, 0x20, 0x00, 0x00, 0x00, -0x00, 0x60, 0xA0, 0x7C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x62, 0x3F, 0xC5, 0x20, 0x00, 0x00, 0x00, -0x00, 0x62, 0x77, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0x64, 0x16, 0x6C, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x64, 0x44, 0x91, 0x20, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, 0x00, 0x00, 0x00, -0x00, 0x66, 0x1B, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, 0x00, 0x00, 0x00, -0x00, 0x67, 0xF1, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x69, 0xBF, 0x4D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x67, 0xD0, 0x20, 0x00, 0x00, 0x00, -0x00, 0x6B, 0x95, 0xF4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x35, 0x3D, 0x20, 0x00, 0x00, 0x00, -0x00, 0x6D, 0x6C, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x0B, 0xE4, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x6F, 0x3A, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0xD9, 0x51, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x71, 0x10, 0xB0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x72, 0xAF, 0xF9, 0x20, 0x00, 0x00, 0x00, -0x00, 0x72, 0xDE, 0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x86, 0xA0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x74, 0xB4, 0xC5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x76, 0x54, 0x0D, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x76, 0x8B, 0x6C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2A, 0xB5, 0x20, 0x00, 0x00, 0x00, -0x00, 0x78, 0x58, 0xD9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, 0x22, 0x20, 0x00, 0x00, 0x00, -0x00, 0x7A, 0x2F, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xCE, 0xC9, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x7C, 0x06, 0x28, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xA5, 0x71, 0x20, 0x00, 0x00, 0x00, -0x00, 0x7D, 0xD3, 0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x72, 0xDE, 0x20, 0x00, 0x00, 0x00, -0x00, 0x7F, 0xAA, 0x3D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x81, 0x49, 0x85, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x81, 0x77, 0xAA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x83, 0x20, 0x2D, 0x20, 0x00, 0x00, 0x00, -0x00, 0x83, 0x4E, 0x51, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x84, 0xED, 0x9A, 0x20, 0x00, 0x00, 0x00, -0x00, 0x85, 0x24, 0xF9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x86, 0xC4, 0x41, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x86, 0xF2, 0x66, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x91, 0xAE, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x88, 0xC9, 0x0D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x68, 0x56, 0x20, 0x00, 0x00, 0x00, -0x00, 0x8A, 0x9F, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x3E, 0xFD, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x8C, 0x6D, 0x22, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x0C, 0x6A, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x8E, 0x43, 0xC9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE3, 0x12, 0x20, 0x00, 0x00, 0x00, -0x00, 0x90, 0x11, 0x36, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB9, 0xB9, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x91, 0xE7, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x93, 0x87, 0x26, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x93, 0xBE, 0x85, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x95, 0x5D, 0xCE, 0x20, 0x00, 0x00, 0x00, -0x00, 0x95, 0x8B, 0xF2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x97, 0x2B, 0x3B, 0x20, 0x00, 0x00, 0x00, -0x00, 0x97, 0x62, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x99, 0x01, 0xE2, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x99, 0x39, 0x41, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xD8, 0x8A, 0x20, 0x00, 0x00, 0x00, -0x00, 0x9B, 0x06, 0xAE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xA5, 0xF7, 0x20, 0x00, 0x00, 0x00, -0x00, 0x9C, 0xDD, 0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x7C, 0x9E, 0xA0, 0x00, 0x00, 0x00, -0x00, 0x9E, 0xAA, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x53, 0x46, 0x20, 0x00, 0x00, 0x00, -0x00, 0xA0, 0x81, 0x6A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x20, 0xB3, 0x20, 0x00, 0x00, 0x00, -0x00, 0xA2, 0x58, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA3, 0xF7, 0x5A, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xA4, 0x25, 0x7F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xC4, 0xC7, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xA5, 0xFC, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA7, 0x9B, 0x6F, 0x20, 0x00, 0x00, 0x00, -0x00, 0xA7, 0xD2, 0xCE, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x72, 0x16, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xA9, 0xA0, 0x3B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x3F, 0x83, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xAB, 0x76, 0xE2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x16, 0x2B, 0x20, 0x00, 0x00, 0x00, -0x00, 0xAD, 0x44, 0x4F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAE, 0xEC, 0xD2, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xAF, 0x1A, 0xF7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xBA, 0x3F, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xB0, 0xF1, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x90, 0xE7, 0x20, 0x00, 0x00, 0x00, -0x00, 0xB2, 0xBF, 0x0B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x5E, 0x54, 0x20, 0x00, 0x00, 0x00, -0x00, 0xB4, 0x95, 0xB3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x34, 0xFB, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xB6, 0x6C, 0x5A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x0B, 0xA3, 0x20, 0x00, 0x00, 0x00, -0x00, 0xB8, 0x39, 0xC7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xD9, 0x10, 0x20, 0x00, 0x00, 0x00, -0x00, 0xBA, 0x10, 0x6F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xAF, 0xB7, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xBB, 0xDD, 0xDC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x86, 0x5F, 0x20, 0x00, 0x00, 0x00, -0x00, 0xBD, 0xB4, 0x83, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x53, 0xCC, 0x20, 0x00, 0x00, 0x00, -0x00, 0xBF, 0x8B, 0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x2A, 0x73, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xC1, 0x58, 0x98, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xF7, 0xE0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xC3, 0x2F, 0x3F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xCE, 0x88, 0x20, 0x00, 0x00, 0x00, -0x00, 0xC5, 0x05, 0xE7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xA5, 0x2F, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xC6, 0xD3, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x72, 0x9C, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xC8, 0xA9, 0xFB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x49, 0x44, 0x20, 0x00, 0x00, 0x00, -0x00, 0xCA, 0x77, 0x68, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x1F, 0xEB, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xCC, 0x4E, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xED, 0x58, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xCE, 0x24, 0xB7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xC4, 0x00, 0x20, 0x00, 0x00, 0x00, -0x00, 0xCF, 0xF2, 0x24, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x91, 0x6D, 0x20, 0x00, 0x00, 0x00, -0x00, 0xD1, 0xC8, 0xCC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x68, 0x14, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xD3, 0x96, 0x39, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x3E, 0xBC, 0x20, 0x00, 0x00, 0x00, -0x00, 0xD5, 0x6C, 0xE0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x0C, 0x29, 0x20, 0x00, 0x00, 0x00, -0x00, 0xD7, 0x43, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xE2, 0xD0, 0xA0, 0x00, 0x00, 0x00, -0x00, 0xD9, 0x10, 0xF5, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB9, 0x78, 0x20, 0x00, 0x00, 0x00, -0x00, 0xDA, 0xE7, 0x9C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x86, 0xE5, 0x20, 0x00, 0x00, 0x00, -0x00, 0xDC, 0xBE, 0x44, 0x20, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, -0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x31, 0x00, -0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x31, 0x3E, 0x2D, 0x31, 0x0A, 0x00, 0xBC, 0xAC, -0xC8, 0x01, 0x07, 0x16, 0x42, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, +0xFF, 0xFF, 0xFF, 0xFF, 0x96, 0x51, 0xF9, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0xFF, 0x14, 0x80, +0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x58, 0xAC, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xD9, 0xED, 0x80, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xA1, 0x32, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0x35, 0xA4, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xEE, 0x27, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x25, 0x72, 0x40, +0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xC2, 0xEF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x08, 0x6B, 0x84, 0x80, +0x00, 0x00, 0x00, 0x00, 0x08, 0xC6, 0x6D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE8, 0x0C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0C, 0x61, 0x47, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC9, 0x3F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0E, 0x8E, 0xF2, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xD3, 0x51, 0x80, +0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xB7, 0xA6, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1E, 0x18, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0xE6, 0x80, +0x00, 0x00, 0x00, 0x00, 0x48, 0xBB, 0x22, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x23, 0x1A, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4A, 0x8D, 0xD5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xDC, 0xC0, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4C, 0x5D, 0xE5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x97, 0xB8, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4E, 0x34, 0x8C, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x9C, 0xA0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x50, 0x08, 0xBB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x31, 0x9A, 0x20, +0x00, 0x00, 0x00, 0x00, 0x50, 0x67, 0xA7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x51, 0x7C, 0x82, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x51, 0xD8, 0xCB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x05, 0x9E, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x52, 0x6C, 0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0x37, 0x7A, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x53, 0xAE, 0x21, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xDC, 0x46, 0x20, +0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x55, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x55, 0x17, 0x5C, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x55, 0x7C, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAB, 0x04, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x56, 0x2C, 0x37, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF7, 0x3E, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x57, 0x53, 0x87, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x57, 0x81, 0xAC, 0x20, +0x00, 0x00, 0x00, 0x00, 0x58, 0x15, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0xD7, 0x20, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x59, 0x20, 0xF4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x58, 0x53, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x59, 0xF5, 0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xB7, 0x02, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x5A, 0xF7, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x25, 0xC0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x5B, 0xD5, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xCE, 0x43, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x5C, 0xFC, 0x68, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x9B, 0xB0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x5E, 0xD3, 0x0F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x60, 0x72, 0x58, 0x20, +0x00, 0x00, 0x00, 0x00, 0x60, 0xA0, 0x7C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x62, 0x3F, 0xC5, 0x20, +0x00, 0x00, 0x00, 0x00, 0x62, 0x77, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0x64, 0x16, 0x6C, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x64, 0x44, 0x91, 0x20, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, +0x00, 0x00, 0x00, 0x00, 0x66, 0x1B, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, +0x00, 0x00, 0x00, 0x00, 0x67, 0xF1, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x69, 0xBF, 0x4D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAF, 0x30, 0x10, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, +0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, +0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x04, +0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, +0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x30, 0x3E, 0x30, 0x0A, +0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, 0x42, 0x00, 0x00, 0x00, 0x00, /* Africa/Ceuta */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -25865,7 +25753,7 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Africa/El_Aaiun */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x45, 0x48, 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, 0x54, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0xBC, 0x48, 0xF0, 0xE0, +0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0xBC, 0x48, 0xF0, 0xE0, 0x0B, 0xD1, 0xB0, 0x90, 0x0B, 0xE8, 0x0C, 0x00, 0x0C, 0x61, 0x47, 0xF0, 0x0D, 0xC9, 0x3F, 0x80, 0x0E, 0x8E, 0xF2, 0x70, 0x0F, 0xD3, 0x51, 0x80, 0x10, 0x27, 0xA3, 0x70, 0x48, 0x41, 0xE6, 0x80, 0x48, 0xBB, 0x22, 0x70, 0x4A, 0x23, 0x1A, 0x00, 0x4A, 0x8D, 0xD5, 0x70, 0x4B, 0xDC, 0xC0, 0x80, @@ -25880,134 +25768,57 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5C, 0xFC, 0x68, 0x20, 0x5E, 0x9B, 0xB0, 0xA0, 0x5E, 0xD3, 0x0F, 0xA0, 0x60, 0x72, 0x58, 0x20, 0x60, 0xA0, 0x7C, 0xA0, 0x62, 0x3F, 0xC5, 0x20, 0x62, 0x77, 0x24, 0x20, 0x64, 0x16, 0x6C, 0xA0, 0x64, 0x44, 0x91, 0x20, 0x65, 0xED, 0x14, 0x20, 0x66, 0x1B, 0x38, 0xA0, 0x67, 0xBA, 0x81, 0x20, -0x67, 0xF1, 0xE0, 0x20, 0x69, 0x91, 0x28, 0xA0, 0x69, 0xBF, 0x4D, 0x20, 0x6B, 0x67, 0xD0, 0x20, -0x6B, 0x95, 0xF4, 0xA0, 0x6D, 0x35, 0x3D, 0x20, 0x6D, 0x6C, 0x9C, 0x20, 0x6F, 0x0B, 0xE4, 0xA0, -0x6F, 0x3A, 0x09, 0x20, 0x70, 0xD9, 0x51, 0xA0, 0x71, 0x10, 0xB0, 0xA0, 0x72, 0xAF, 0xF9, 0x20, -0x72, 0xDE, 0x1D, 0xA0, 0x74, 0x86, 0xA0, 0xA0, 0x74, 0xB4, 0xC5, 0x20, 0x76, 0x54, 0x0D, 0xA0, -0x76, 0x8B, 0x6C, 0xA0, 0x78, 0x2A, 0xB5, 0x20, 0x78, 0x58, 0xD9, 0xA0, 0x79, 0xF8, 0x22, 0x20, -0x7A, 0x2F, 0x81, 0x20, 0x7B, 0xCE, 0xC9, 0xA0, 0x7C, 0x06, 0x28, 0xA0, 0x7D, 0xA5, 0x71, 0x20, -0x7D, 0xD3, 0x95, 0xA0, 0x7F, 0x72, 0xDE, 0x20, 0x7F, 0xAA, 0x3D, 0x20, 0x01, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0xFF, 0xFF, 0xF3, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x10, -0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, -0x0E, 0x10, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2D, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x31, 0x00, -0x2B, 0x30, 0x30, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0xBA, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, -0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x48, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xD1, 0xB0, 0x90, -0x00, 0x00, 0x00, 0x00, 0x0B, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x61, 0x47, 0xF0, -0x00, 0x00, 0x00, 0x00, 0x0D, 0xC9, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x8E, 0xF2, 0x70, -0x00, 0x00, 0x00, 0x00, 0x0F, 0xD3, 0x51, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0xA3, 0x70, -0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0xE6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48, 0xBB, 0x22, 0x70, -0x00, 0x00, 0x00, 0x00, 0x4A, 0x23, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x8D, 0xD5, 0x70, -0x00, 0x00, 0x00, 0x00, 0x4B, 0xDC, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x5D, 0xE5, 0x70, -0x00, 0x00, 0x00, 0x00, 0x4D, 0x97, 0xB8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x34, 0x8C, 0xF0, -0x00, 0x00, 0x00, 0x00, 0x4F, 0x9C, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x08, 0xBB, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x50, 0x31, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x67, 0xA7, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x51, 0x7C, 0x82, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x51, 0xD8, 0xCB, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x52, 0x05, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6C, 0x73, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x53, 0x37, 0x7A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xAE, 0x21, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x53, 0xDC, 0x46, 0x20, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x55, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x55, 0x17, 0x5C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x55, 0x7C, 0xE0, 0x20, -0x00, 0x00, 0x00, 0x00, 0x55, 0xAB, 0x04, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x2C, 0x37, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x56, 0xF7, 0x3E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x57, 0x53, 0x87, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x57, 0x81, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x15, 0x54, 0x20, -0x00, 0x00, 0x00, 0x00, 0x58, 0xD7, 0x20, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x20, 0xF4, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x59, 0x58, 0x53, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, 0xF5, 0x36, 0x20, -0x00, 0x00, 0x00, 0x00, 0x5A, 0xB7, 0x02, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xF7, 0x9C, 0x20, -0x00, 0x00, 0x00, 0x00, 0x5B, 0x25, 0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xD5, 0x18, 0x20, -0x00, 0x00, 0x00, 0x00, 0x5C, 0xCE, 0x43, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xFC, 0x68, 0x20, -0x00, 0x00, 0x00, 0x00, 0x5E, 0x9B, 0xB0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5E, 0xD3, 0x0F, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x60, 0x72, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x60, 0xA0, 0x7C, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x62, 0x3F, 0xC5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x62, 0x77, 0x24, 0x20, -0x00, 0x00, 0x00, 0x00, 0x64, 0x16, 0x6C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x64, 0x44, 0x91, 0x20, -0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x66, 0x1B, 0x38, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x67, 0xF1, 0xE0, 0x20, -0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0xBF, 0x4D, 0x20, -0x00, 0x00, 0x00, 0x00, 0x6B, 0x67, 0xD0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x95, 0xF4, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x6D, 0x35, 0x3D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x6C, 0x9C, 0x20, -0x00, 0x00, 0x00, 0x00, 0x6F, 0x0B, 0xE4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x3A, 0x09, 0x20, -0x00, 0x00, 0x00, 0x00, 0x70, 0xD9, 0x51, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x71, 0x10, 0xB0, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x72, 0xAF, 0xF9, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0xDE, 0x1D, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x74, 0x86, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x74, 0xB4, 0xC5, 0x20, -0x00, 0x00, 0x00, 0x00, 0x76, 0x54, 0x0D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x8B, 0x6C, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x78, 0x2A, 0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x78, 0x58, 0xD9, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, 0x22, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x2F, 0x81, 0x20, -0x00, 0x00, 0x00, 0x00, 0x7B, 0xCE, 0xC9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x06, 0x28, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x7D, 0xA5, 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xD3, 0x95, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x7F, 0x72, 0xDE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xAA, 0x3D, 0x20, -0x00, 0x00, 0x00, 0x00, 0x81, 0x49, 0x85, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x81, 0x77, 0xAA, 0x20, -0x00, 0x00, 0x00, 0x00, 0x83, 0x20, 0x2D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x83, 0x4E, 0x51, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x84, 0xED, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x85, 0x24, 0xF9, 0x20, -0x00, 0x00, 0x00, 0x00, 0x86, 0xC4, 0x41, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x86, 0xF2, 0x66, 0x20, -0x00, 0x00, 0x00, 0x00, 0x88, 0x91, 0xAE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x88, 0xC9, 0x0D, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x8A, 0x68, 0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x9F, 0xB5, 0x20, -0x00, 0x00, 0x00, 0x00, 0x8C, 0x3E, 0xFD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x6D, 0x22, 0x20, -0x00, 0x00, 0x00, 0x00, 0x8E, 0x0C, 0x6A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x43, 0xC9, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x8F, 0xE3, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x90, 0x11, 0x36, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x91, 0xB9, 0xB9, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x91, 0xE7, 0xDE, 0x20, -0x00, 0x00, 0x00, 0x00, 0x93, 0x87, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x93, 0xBE, 0x85, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x95, 0x5D, 0xCE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x95, 0x8B, 0xF2, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x97, 0x2B, 0x3B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x97, 0x62, 0x9A, 0x20, -0x00, 0x00, 0x00, 0x00, 0x99, 0x01, 0xE2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x99, 0x39, 0x41, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x9A, 0xD8, 0x8A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x06, 0xAE, 0xA0, -0x00, 0x00, 0x00, 0x00, 0x9C, 0xA5, 0xF7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xDD, 0x56, 0x20, -0x00, 0x00, 0x00, 0x00, 0x9E, 0x7C, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xAA, 0xC3, 0x20, -0x00, 0x00, 0x00, 0x00, 0xA0, 0x53, 0x46, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x81, 0x6A, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xA2, 0x20, 0xB3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x58, 0x12, 0x20, -0x00, 0x00, 0x00, 0x00, 0xA3, 0xF7, 0x5A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x25, 0x7F, 0x20, -0x00, 0x00, 0x00, 0x00, 0xA5, 0xC4, 0xC7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xFC, 0x26, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xA7, 0x9B, 0x6F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xD2, 0xCE, 0x20, -0x00, 0x00, 0x00, 0x00, 0xA9, 0x72, 0x16, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xA9, 0xA0, 0x3B, 0x20, -0x00, 0x00, 0x00, 0x00, 0xAB, 0x3F, 0x83, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x76, 0xE2, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xAD, 0x16, 0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0xAD, 0x44, 0x4F, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xAE, 0xEC, 0xD2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x1A, 0xF7, 0x20, -0x00, 0x00, 0x00, 0x00, 0xB0, 0xBA, 0x3F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xF1, 0x9E, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xB2, 0x90, 0xE7, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xBF, 0x0B, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xB4, 0x5E, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x95, 0xB3, 0x20, -0x00, 0x00, 0x00, 0x00, 0xB6, 0x34, 0xFB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x6C, 0x5A, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xB8, 0x0B, 0xA3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x39, 0xC7, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xB9, 0xD9, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x10, 0x6F, 0x20, -0x00, 0x00, 0x00, 0x00, 0xBB, 0xAF, 0xB7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xDD, 0xDC, 0x20, -0x00, 0x00, 0x00, 0x00, 0xBD, 0x86, 0x5F, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBD, 0xB4, 0x83, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xBF, 0x53, 0xCC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x8B, 0x2B, 0x20, -0x00, 0x00, 0x00, 0x00, 0xC1, 0x2A, 0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x58, 0x98, 0x20, -0x00, 0x00, 0x00, 0x00, 0xC2, 0xF7, 0xE0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x2F, 0x3F, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xC4, 0xCE, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x05, 0xE7, 0x20, -0x00, 0x00, 0x00, 0x00, 0xC6, 0xA5, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xD3, 0x54, 0x20, -0x00, 0x00, 0x00, 0x00, 0xC8, 0x72, 0x9C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xC8, 0xA9, 0xFB, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xCA, 0x49, 0x44, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x77, 0x68, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xCC, 0x1F, 0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x4E, 0x10, 0x20, -0x00, 0x00, 0x00, 0x00, 0xCD, 0xED, 0x58, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x24, 0xB7, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xCF, 0xC4, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xF2, 0x24, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xD1, 0x91, 0x6D, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xC8, 0xCC, 0x20, -0x00, 0x00, 0x00, 0x00, 0xD3, 0x68, 0x14, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x96, 0x39, 0x20, -0x00, 0x00, 0x00, 0x00, 0xD5, 0x3E, 0xBC, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD5, 0x6C, 0xE0, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xD7, 0x0C, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x43, 0x88, 0x20, -0x00, 0x00, 0x00, 0x00, 0xD8, 0xE2, 0xD0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x10, 0xF5, 0x20, -0x00, 0x00, 0x00, 0x00, 0xDA, 0xB9, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xE7, 0x9C, 0xA0, -0x00, 0x00, 0x00, 0x00, 0xDC, 0x86, 0xE5, 0x20, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xBE, 0x44, 0x20, +0x67, 0xF1, 0xE0, 0x20, 0x69, 0x91, 0x28, 0xA0, 0x69, 0xBF, 0x4D, 0x20, 0x6A, 0xAF, 0x30, 0x10, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0xFF, 0xFF, 0xF3, 0xA0, 0x00, 0x00, -0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x08, 0x4C, 0x4D, -0x54, 0x00, 0x2D, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x0A, 0x3C, -0x2B, 0x30, 0x31, 0x3E, 0x2D, 0x31, 0x0A, 0x00, 0xB2, 0xC1, 0xB8, 0x00, 0xFE, 0x84, 0x40, 0x00, -0x00, 0x00, 0x00, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x03, 0xFF, 0xFF, 0xF3, +0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x08, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x0E, 0x10, 0x00, +0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2D, 0x30, 0x31, 0x00, 0x2B, +0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x3D, 0x00, 0x00, 0x00, 0x07, 0x00, +0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x48, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x0B, +0xD1, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE8, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, +0x61, 0x47, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC9, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0E, +0x8E, 0xF2, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xD3, 0x51, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, +0x27, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0xE6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x48, +0xBB, 0x22, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x23, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, +0x8D, 0xD5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xDC, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4C, +0x5D, 0xE5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x97, 0xB8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4E, +0x34, 0x8C, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x9C, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, +0x08, 0xBB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x31, 0x9A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, +0x67, 0xA7, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x51, 0x7C, 0x82, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x51, +0xD8, 0xCB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x05, 0x9E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, +0x6C, 0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0x37, 0x7A, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, +0xAE, 0x21, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xDC, 0x46, 0x20, 0x00, 0x00, 0x00, 0x00, 0x54, +0x4C, 0x55, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x55, 0x17, 0x5C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x55, +0x7C, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAB, 0x04, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, +0x2C, 0x37, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF7, 0x3E, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x57, +0x53, 0x87, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x57, 0x81, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, +0x15, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0xD7, 0x20, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, +0x20, 0xF4, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x58, 0x53, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x59, +0xF5, 0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xB7, 0x02, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5A, +0xF7, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x25, 0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5B, +0xD5, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xCE, 0x43, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5C, +0xFC, 0x68, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x9B, 0xB0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x5E, +0xD3, 0x0F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x60, 0x72, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x60, +0xA0, 0x7C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x62, 0x3F, 0xC5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x62, +0x77, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0x64, 0x16, 0x6C, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x64, +0x44, 0x91, 0x20, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x66, +0x1B, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0xBA, 0x81, 0x20, 0x00, 0x00, 0x00, 0x00, 0x67, +0xF1, 0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x28, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, +0xBF, 0x4D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAF, 0x30, 0x10, 0x01, 0x03, 0x02, 0x03, 0x02, +0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, +0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, +0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x03, 0xFF, 0xFF, 0xF3, 0xA0, 0x00, 0x00, 0xFF, 0xFF, +0xF1, 0xF0, 0x00, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, +0x00, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, +0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2D, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, +0x30, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x30, 0x3E, 0x30, 0x0A, 0x00, 0xB2, 0xC1, 0xB8, 0x00, 0xFE, +0x84, 0x40, 0x00, 0x00, 0x00, 0x00, /* Africa/Freetown */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x53, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -30652,8 +30463,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* America/Edmonton */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x43, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x88, 0xDE, 0xCE, 0xE0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x88, 0xDE, 0xCE, 0xE0, 0x9E, 0xB8, 0xAF, 0x90, 0x9F, 0xBB, 0x07, 0x80, 0xA0, 0x98, 0x91, 0x90, 0xA0, 0xD2, 0x85, 0x80, 0xA2, 0x8A, 0xE8, 0x90, 0xA3, 0x84, 0x06, 0x00, 0xA4, 0x6A, 0xCA, 0x90, 0xA5, 0x35, 0xC3, 0x80, 0xA6, 0x53, 0xE7, 0x10, 0xA7, 0x15, 0xA5, 0x80, 0xA8, 0x33, 0xC9, 0x10, 0xA8, 0xFE, 0xC2, 0x00, @@ -30685,121 +30496,102 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5D, 0xBE, 0x89, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x5F, 0x9E, 0x6B, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x61, 0x87, 0x87, 0x80, 0x62, 0x2D, 0xB2, 0x90, 0x63, 0x67, 0x69, 0x80, 0x64, 0x0D, 0x94, 0x90, 0x65, 0x47, 0x4B, 0x80, 0x65, 0xED, 0x76, 0x90, 0x67, 0x27, 0x2D, 0x80, 0x67, 0xCD, 0x58, 0x90, -0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x6B, 0x96, 0x57, 0x10, -0x6C, 0xD0, 0x0E, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x6E, 0xAF, 0xF0, 0x00, 0x6F, 0x56, 0x1B, 0x10, -0x70, 0x8F, 0xD2, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x72, 0x6F, 0xB4, 0x00, 0x73, 0x15, 0xDF, 0x10, -0x74, 0x4F, 0x96, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x76, 0x38, 0xB2, 0x80, 0x76, 0xDE, 0xDD, 0x90, -0x78, 0x18, 0x94, 0x80, 0x78, 0xBE, 0xBF, 0x90, 0x79, 0xF8, 0x76, 0x80, 0x7A, 0x9E, 0xA1, 0x90, -0x7B, 0xD8, 0x58, 0x80, 0x7C, 0x7E, 0x83, 0x90, 0x7D, 0xB8, 0x3A, 0x80, 0x7E, 0x5E, 0x65, 0x90, -0x7F, 0x98, 0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, -0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, -0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, -0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, -0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, -0xE8, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, -0xCA, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, -0xE7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, -0xC9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, -0x0C, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, -0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, -0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, -0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, -0xDE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, -0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, -0xA2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, -0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, -0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, -0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, -0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, -0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, -0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, -0x27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, -0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, -0xEB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, -0xCD, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, -0xAF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, -0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, -0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, -0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, -0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, -0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, -0x70, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, -0x52, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, -0x34, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, -0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, -0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, -0xDA, 0x80, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, -0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, -0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, -0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, -0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, -0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, -0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, -0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, -0x5F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, -0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, -0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, -0x5C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, -0x3E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, -0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, -0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, -0xE4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, -0xC6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, -0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, -0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, -0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, -0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, -0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, -0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, -0x69, 0x80, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, -0x4B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, -0x2D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, -0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, -0xF1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x96, 0x57, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xD0, -0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xAF, -0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x56, 0x1B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8F, -0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, -0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xDF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4F, -0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, -0xB2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDE, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, -0x94, 0x80, 0x00, 0x00, 0x00, 0x00, 0x78, 0xBE, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, -0x76, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x9E, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xD8, -0x58, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xB8, -0x3A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5E, 0x65, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x98, -0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, -0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x95, 0xA0, +0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, +0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x14, +0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, +0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, +0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, 0xCA, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, 0xE7, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, 0xC9, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x0C, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x18, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xDC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xFC, 0x80, +0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xDE, 0x80, +0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xC0, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xA2, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xBF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xA1, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x83, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x65, 0x00, +0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x47, 0x00, +0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x29, 0x00, +0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x45, 0x80, +0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x27, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x09, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xEB, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xCD, 0x80, +0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xAF, 0x80, +0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xCC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xAE, 0x00, +0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x90, 0x00, +0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x72, 0x00, +0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x70, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x52, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x34, 0x80, +0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x16, 0x80, +0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0xF8, 0x80, +0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xDA, 0x80, +0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0xF7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xD9, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xBB, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x9D, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, +0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, +0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, +0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, +0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, +0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, +0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, +0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, +0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, -0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, -0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x00, 0x01, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, -0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0xDB, 0x0A, 0x38, -0x00, 0x65, 0x85, 0x95, 0x00, 0x00, 0x00, 0x22, 0x4D, 0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, -0x20, 0x2D, 0x20, 0x41, 0x42, 0x2C, 0x20, 0x42, 0x43, 0x28, 0x45, 0x29, 0x2C, 0x20, 0x4E, 0x54, -0x28, 0x45, 0x29, 0x2C, 0x20, 0x53, 0x4B, 0x28, 0x57, 0x29, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, +0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, +0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, +0xAB, 0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, +0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, 0x0A, 0x00, 0xDB, +0x0A, 0x38, 0x00, 0x65, 0x85, 0x95, 0x00, 0x00, 0x00, 0x1D, 0x43, 0x53, 0x54, 0x20, 0x2D, 0x20, +0x41, 0x42, 0x2C, 0x20, 0x42, 0x43, 0x28, 0x45, 0x29, 0x2C, 0x20, 0x4E, 0x54, 0x28, 0x45, 0x29, +0x2C, 0x20, 0x53, 0x4B, 0x28, 0x57, 0x29, /* America/Eirunepe */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x42, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -41185,8 +40977,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* America/Vancouver */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x43, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x9E, 0xB8, 0xBD, 0xA0, 0x9F, 0xBB, 0x15, 0x90, 0xCB, 0x89, 0x1A, 0xA0, 0xD2, 0x23, 0xF4, 0x70, 0xD2, 0x61, 0x26, 0x10, 0xD3, 0x76, 0x0F, 0x20, 0xD4, 0x41, 0x08, 0x10, 0xD5, 0x55, 0xF1, 0x20, 0xD6, 0x20, 0xEA, 0x10, 0xD7, 0x35, 0xD3, 0x20, 0xD8, 0x00, 0xCC, 0x10, 0xD9, 0x15, 0xB5, 0x20, @@ -41228,13 +41020,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5D, 0xBE, 0x97, 0x10, 0x5E, 0x64, 0xC2, 0x20, 0x5F, 0x9E, 0x79, 0x10, 0x60, 0x4D, 0xDE, 0xA0, 0x61, 0x87, 0x95, 0x90, 0x62, 0x2D, 0xC0, 0xA0, 0x63, 0x67, 0x77, 0x90, 0x64, 0x0D, 0xA2, 0xA0, 0x65, 0x47, 0x59, 0x90, 0x65, 0xED, 0x84, 0xA0, 0x67, 0x27, 0x3B, 0x90, 0x67, 0xCD, 0x66, 0xA0, -0x69, 0x07, 0x1D, 0x90, 0x69, 0xAD, 0x48, 0xA0, 0x6A, 0xE6, 0xFF, 0x90, 0x6B, 0x96, 0x65, 0x20, -0x6C, 0xD0, 0x1C, 0x10, 0x6D, 0x76, 0x47, 0x20, 0x6E, 0xAF, 0xFE, 0x10, 0x6F, 0x56, 0x29, 0x20, -0x70, 0x8F, 0xE0, 0x10, 0x71, 0x36, 0x0B, 0x20, 0x72, 0x6F, 0xC2, 0x10, 0x73, 0x15, 0xED, 0x20, -0x74, 0x4F, 0xA4, 0x10, 0x74, 0xFF, 0x09, 0xA0, 0x76, 0x38, 0xC0, 0x90, 0x76, 0xDE, 0xEB, 0xA0, -0x78, 0x18, 0xA2, 0x90, 0x78, 0xBE, 0xCD, 0xA0, 0x79, 0xF8, 0x84, 0x90, 0x7A, 0x9E, 0xAF, 0xA0, -0x7B, 0xD8, 0x66, 0x90, 0x7C, 0x7E, 0x91, 0xA0, 0x7D, 0xB8, 0x48, 0x90, 0x7E, 0x5E, 0x73, 0xA0, -0x7F, 0x98, 0x2A, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x69, 0x07, 0x1D, 0x90, 0x69, 0xAD, 0x48, 0xA0, 0x6A, 0xE6, 0xFF, 0x90, 0x02, 0x01, 0x02, 0x03, +0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, @@ -41244,130 +41031,115 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, +0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, +0x01, 0x10, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, +0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x54, 0x5A, 0x69, 0x66, +0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, 0x76, 0xEC, +0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x15, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x76, 0x0F, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0x41, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xF1, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xEA, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x35, 0xD3, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x00, 0xCC, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x15, 0xB5, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0xE0, 0xAE, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0xFE, 0xD1, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xC0, 0x90, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xDE, 0xB3, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xA9, 0xAC, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xBE, 0x95, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x89, 0x8E, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x9E, 0x77, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x69, 0x70, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE2, 0x7E, 0x59, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x49, 0x52, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x5E, 0x3B, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE5, 0x29, 0x34, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE6, 0x47, 0x58, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x12, 0x51, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x27, 0x3A, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0xF2, 0x33, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x07, 0x1C, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0xD2, 0x15, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xE6, 0xFE, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xB1, 0xF7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC6, 0xE0, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x91, 0xD9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xAF, 0xFC, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x71, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8F, 0xDE, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x7F, 0xC1, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x6F, 0xC0, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x5F, 0xA3, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x4F, 0xA2, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x3F, 0x85, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x2F, 0x84, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x28, 0xA2, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x0F, 0x66, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x08, 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0xF8, 0x83, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE8, 0x66, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD8, 0x65, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC8, 0x48, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xB8, 0x47, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x29, 0x20, +0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x0B, 0x20, +0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x27, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, 0x09, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xEB, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xCD, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xAF, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xCD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x91, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xAF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xAE, 0x20, +0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x90, 0x20, +0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x72, 0x20, +0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x54, 0x20, +0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x36, 0x20, +0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x18, 0x20, +0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x34, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x16, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xF9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xF8, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xDB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x2B, 0x20, +0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xBD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, 0x0D, 0x20, +0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xDA, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xEF, 0x20, +0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xBC, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xD1, 0x20, +0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x9E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xED, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xCF, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xB1, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x7E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x93, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x75, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x92, 0x20, +0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x74, 0x20, +0x00, 0x00, 0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x56, 0x20, +0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xE8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x38, 0x20, +0x00, 0x00, 0x00, 0x00, 0x38, 0x1C, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x1A, 0x20, +0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xE7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xFC, 0x20, +0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xC9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x18, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0xAB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xFA, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x8D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xDC, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xBE, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0xA0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, +0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xB5, 0x20, +0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x97, 0x20, +0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xB3, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x95, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x77, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x59, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x3B, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x1D, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x3A, 0x20, +0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x1C, 0x20, +0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xFE, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xE0, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xC2, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xDE, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xC0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0xA2, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x84, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x66, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x48, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, -0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, -0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, -0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5A, -0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xBE, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, -0x76, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, -0x15, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, -0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x76, -0x0F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0x41, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, -0xF1, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xEA, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x35, -0xD3, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x00, 0xCC, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x15, -0xB5, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0xE0, 0xAE, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0xFE, -0xD1, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xC0, 0x90, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xDE, -0xB3, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xA9, 0xAC, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xBE, -0x95, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x89, 0x8E, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x9E, -0x77, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x69, 0x70, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE2, 0x7E, -0x59, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x49, 0x52, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x5E, -0x3B, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE5, 0x29, 0x34, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE6, 0x47, -0x58, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x12, 0x51, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x27, -0x3A, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0xF2, 0x33, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x07, -0x1C, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0xD2, 0x15, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xE6, -0xFE, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xB1, 0xF7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC6, -0xE0, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x91, 0xD9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xAF, -0xFC, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x71, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8F, -0xDE, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x7F, 0xC1, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x6F, -0xC0, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x5F, 0xA3, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x4F, -0xA2, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x3F, 0x85, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x2F, -0x84, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x28, 0xA2, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x0F, -0x66, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x08, 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0xF8, -0x83, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE8, 0x66, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD8, -0x65, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC8, 0x48, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xB8, -0x47, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, -0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, -0x0B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, -0x27, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, -0x09, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, -0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, -0xCD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, -0xAF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xCD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, -0x91, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xAF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, -0xAE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, -0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, -0x72, 0x20, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, -0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, -0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, -0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, -0x34, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, -0x16, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xF9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, -0xF8, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xDB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, -0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xBD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, -0x0D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xDA, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, -0xEF, 0x20, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xBC, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, -0xD1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x9E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, -0xED, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, -0xCF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, -0xB1, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x7E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, -0x93, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, -0x75, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, -0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, -0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, -0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xE8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, -0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1C, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, -0x1A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xE7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, -0xFC, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xC9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, -0x18, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0xAB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, -0xFA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x8D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, -0xDC, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, -0xBE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, -0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, -0xD3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, -0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, -0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, -0xB3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, -0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, -0x77, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, -0x59, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, -0x3B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, -0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, -0x3A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, -0x1C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, -0xFE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, -0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, -0xC2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, -0xDE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, -0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, -0xA2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, -0x84, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, -0x66, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, -0x48, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x96, -0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xD0, 0x1C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, -0x47, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xAF, 0xFE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x56, -0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8F, 0xE0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x71, 0x36, -0x0B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, 0xC2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, -0xED, 0x20, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4F, 0xA4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFF, -0x09, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0xC0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDE, -0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0xA2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xBE, -0xCD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, 0x84, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x9E, -0xAF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xD8, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, -0x91, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xB8, 0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5E, -0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x98, 0x2A, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, -0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, -0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, -0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x00, 0x01, 0x0A, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, -0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0xD4, 0x81, 0x0A, -0x00, 0x56, 0xCC, 0x0D, 0x00, 0x00, 0x00, 0x19, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, -0x2D, 0x20, 0x42, 0x43, 0x20, 0x28, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65, 0x61, 0x73, -0x29, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, +0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, +0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0xFF, 0xFF, +0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, +0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x0A, 0x00, 0xD4, +0x81, 0x0A, 0x00, 0x56, 0xCC, 0x0D, 0x00, 0x00, 0x00, 0x15, 0x4D, 0x53, 0x54, 0x20, 0x2D, 0x20, +0x42, 0x43, 0x20, 0x28, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65, 0x61, 0x73, 0x29, /* America/Virgin */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -41827,8 +41599,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* America/Yellowknife */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x88, 0xDE, 0xCE, 0xE0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x88, 0xDE, 0xCE, 0xE0, 0x9E, 0xB8, 0xAF, 0x90, 0x9F, 0xBB, 0x07, 0x80, 0xA0, 0x98, 0x91, 0x90, 0xA0, 0xD2, 0x85, 0x80, 0xA2, 0x8A, 0xE8, 0x90, 0xA3, 0x84, 0x06, 0x00, 0xA4, 0x6A, 0xCA, 0x90, 0xA5, 0x35, 0xC3, 0x80, 0xA6, 0x53, 0xE7, 0x10, 0xA7, 0x15, 0xA5, 0x80, 0xA8, 0x33, 0xC9, 0x10, 0xA8, 0xFE, 0xC2, 0x00, @@ -41860,119 +41632,100 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5D, 0xBE, 0x89, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x5F, 0x9E, 0x6B, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x61, 0x87, 0x87, 0x80, 0x62, 0x2D, 0xB2, 0x90, 0x63, 0x67, 0x69, 0x80, 0x64, 0x0D, 0x94, 0x90, 0x65, 0x47, 0x4B, 0x80, 0x65, 0xED, 0x76, 0x90, 0x67, 0x27, 0x2D, 0x80, 0x67, 0xCD, 0x58, 0x90, -0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x6B, 0x96, 0x57, 0x10, -0x6C, 0xD0, 0x0E, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x6E, 0xAF, 0xF0, 0x00, 0x6F, 0x56, 0x1B, 0x10, -0x70, 0x8F, 0xD2, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x72, 0x6F, 0xB4, 0x00, 0x73, 0x15, 0xDF, 0x10, -0x74, 0x4F, 0x96, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x76, 0x38, 0xB2, 0x80, 0x76, 0xDE, 0xDD, 0x90, -0x78, 0x18, 0x94, 0x80, 0x78, 0xBE, 0xBF, 0x90, 0x79, 0xF8, 0x76, 0x80, 0x7A, 0x9E, 0xA1, 0x90, -0x7B, 0xD8, 0x58, 0x80, 0x7C, 0x7E, 0x83, 0x90, 0x7D, 0xB8, 0x3A, 0x80, 0x7E, 0x5E, 0x65, 0x90, -0x7F, 0x98, 0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, -0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, -0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, -0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, -0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, -0xE8, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, -0xCA, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, -0xE7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, -0xC9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, -0x0C, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, -0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, -0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, -0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, -0xDE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, -0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, -0xA2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, -0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, -0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, -0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, -0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, -0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, -0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, -0x27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, -0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, -0xEB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, -0xCD, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, -0xAF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, -0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, -0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, -0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, -0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, -0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, -0x70, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, -0x52, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, -0x34, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, -0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, -0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, -0xDA, 0x80, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, -0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, -0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, -0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, -0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, -0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, -0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, -0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, -0x5F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, -0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, -0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, -0x5C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, -0x3E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, -0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, -0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, -0xE4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, -0xC6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, -0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, -0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, -0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, -0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, -0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, -0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, -0x69, 0x80, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, -0x4B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, -0x2D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, -0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, -0xF1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x96, 0x57, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xD0, -0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xAF, -0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x56, 0x1B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8F, -0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, -0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xDF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4F, -0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, -0xB2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDE, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, -0x94, 0x80, 0x00, 0x00, 0x00, 0x00, 0x78, 0xBE, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, -0x76, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x9E, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xD8, -0x58, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xB8, -0x3A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5E, 0x65, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x98, -0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, -0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x95, 0xA0, +0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, +0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x14, +0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, +0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, +0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, 0xCA, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, 0xE7, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, 0xC9, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x0C, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x18, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xDC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xFC, 0x80, +0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xDE, 0x80, +0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xC0, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xA2, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xBF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xA1, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x83, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x65, 0x00, +0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x47, 0x00, +0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x29, 0x00, +0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x45, 0x80, +0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x27, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x09, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xEB, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xCD, 0x80, +0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xAF, 0x80, +0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xCC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xAE, 0x00, +0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x90, 0x00, +0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x72, 0x00, +0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x70, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x52, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x34, 0x80, +0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x16, 0x80, +0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0xF8, 0x80, +0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xDA, 0x80, +0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0xF7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xD9, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xBB, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x9D, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, +0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, +0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, +0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, +0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, +0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, +0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, +0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, +0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, -0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, -0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x00, 0x01, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, -0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, +0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, +0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, +0xAB, 0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, +0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, 0x0A, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Antarctica/Casey */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -45005,26 +44758,26 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Asia/Ho_Chi_Minh */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x56, 0x4E, 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, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x88, 0x8C, 0x43, 0x8A, +0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x88, 0x8C, 0x43, 0x8A, 0x91, 0xA3, 0x2B, 0x0A, 0xCD, 0x35, 0xE6, 0x80, 0xD1, 0x59, 0xCE, 0x70, 0xD2, 0x3B, 0x3E, 0xF0, 0xD5, 0x32, 0xBB, 0x10, 0xE4, 0xB6, 0xF2, 0x90, 0xED, 0x2F, 0x98, 0x00, 0x0A, 0x3D, 0xC7, 0x00, -0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, -0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, -0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, -0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, -0x39, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x01, 0x00, +0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, +0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, +0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, +0x69, 0x66, 0x32, 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, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0xFF, 0xFF, -0xFF, 0xFF, 0x88, 0x8C, 0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, -0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, -0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, -0xFF, 0xFF, 0xE4, 0xB6, 0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0A, 0x3D, 0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, -0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, -0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, -0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, -0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, -0x0A, 0x00, 0x99, 0xBB, 0x78, 0x01, 0xB5, 0x6B, 0x2A, 0x00, 0x00, 0x00, 0x00, +0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, +0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, +0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, +0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0xB6, +0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3D, +0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, +0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, +0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, +0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, +0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x99, 0xBB, 0x78, 0x01, 0xB5, 0x6B, +0x2A, 0x00, 0x00, 0x00, 0x00, /* Asia/Hong_Kong */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x48, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -46735,23 +46488,22 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Asia/Phnom_Penh */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x48, 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, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x88, 0x8C, 0x45, 0x24, +0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x88, 0x8C, 0x45, 0x24, 0x91, 0xA3, 0x2B, 0x0A, 0xCD, 0x35, 0xE6, 0x80, 0xD1, 0x59, 0xCE, 0x70, 0xD2, 0x3B, 0x3E, 0xF0, -0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x00, 0x62, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, -0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, -0x90, 0x00, 0x11, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, 0x4D, -0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, -0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x00, 0x62, 0x5C, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, +0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, +0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, +0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, -0x45, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, -0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, -0x3E, 0xF0, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x00, 0x62, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x63, -0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, -0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, -0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, -0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x9A, 0xF3, 0xF8, 0x01, 0xB2, 0xBF, -0x92, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, +0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x45, 0x24, 0xFF, 0xFF, +0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, +0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0x01, 0x02, +0x03, 0x04, 0x02, 0x00, 0x00, 0x62, 0x5C, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, +0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, +0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, +0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, +0x0A, 0x00, 0x9A, 0xF3, 0xF8, 0x01, 0xB2, 0xBF, 0x92, 0x00, 0x00, 0x00, 0x00, /* Asia/Pontianak */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -46985,26 +46737,26 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Asia/Saigon */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 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, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x88, 0x8C, 0x43, 0x8A, +0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x88, 0x8C, 0x43, 0x8A, 0x91, 0xA3, 0x2B, 0x0A, 0xCD, 0x35, 0xE6, 0x80, 0xD1, 0x59, 0xCE, 0x70, 0xD2, 0x3B, 0x3E, 0xF0, 0xD5, 0x32, 0xBB, 0x10, 0xE4, 0xB6, 0xF2, 0x90, 0xED, 0x2F, 0x98, 0x00, 0x0A, 0x3D, 0xC7, 0x00, -0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, -0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, -0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, -0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, -0x39, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x01, 0x00, +0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, 0x80, 0x00, +0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, +0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, +0x69, 0x66, 0x32, 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, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0xFF, 0xFF, -0xFF, 0xFF, 0x88, 0x8C, 0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, -0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, -0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, -0xFF, 0xFF, 0xE4, 0xB6, 0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0A, 0x3D, 0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, -0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, -0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, -0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, -0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, -0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, +0x43, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, +0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, +0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0xB6, +0xF2, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x2F, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3D, +0xC7, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x63, 0xF6, 0x00, +0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x00, 0x00, 0x70, +0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, 0x70, 0x00, 0x05, 0x50, +0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, +0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, /* Asia/Sakhalin */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -48073,25 +47825,24 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Asia/Vientiane */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x4C, 0x41, 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, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x88, 0x8C, 0x47, 0x50, +0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x88, 0x8C, 0x47, 0x50, 0x91, 0xA3, 0x2B, 0x0A, 0xCD, 0x35, 0xE6, 0x80, 0xD1, 0x59, 0xCE, 0x70, 0xD2, 0x3B, 0x3E, 0xF0, 0xD5, 0x32, 0xBB, 0x10, 0xE4, 0x51, 0x61, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x00, -0x00, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, -0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x11, 0x00, 0x00, 0x62, -0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, -0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x07, 0x00, 0x00, 0x00, 0x06, -0x00, 0x00, 0x00, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x47, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, -0x91, 0xA3, 0x2B, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, -0xD1, 0x59, 0xCE, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, -0xD5, 0x32, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x51, 0x61, 0x00, 0x01, 0x02, 0x03, 0x04, -0x02, 0x03, 0x02, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x04, 0x00, -0x00, 0x62, 0x70, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x7E, 0x90, 0x00, -0x11, 0x00, 0x00, 0x62, 0x70, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4C, 0x4D, 0x54, 0x00, -0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, -0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0xA4, 0xBE, 0x7A, 0x01, 0xAF, 0x36, 0xA0, 0x00, 0x00, 0x00, -0x00, +0x00, 0x60, 0x30, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, +0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, +0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, +0x2B, 0x30, 0x39, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 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, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, +0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x8C, 0x47, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0xA3, 0x2B, 0x0A, +0xFF, 0xFF, 0xFF, 0xFF, 0xCD, 0x35, 0xE6, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0x59, 0xCE, 0x70, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x3B, 0x3E, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x32, 0xBB, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x51, 0x61, 0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x03, 0x02, 0x00, +0x00, 0x60, 0x30, 0x00, 0x01, 0x00, 0x00, 0x63, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, +0x05, 0x00, 0x00, 0x70, 0x80, 0x00, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x0D, 0x00, 0x00, 0x62, +0x70, 0x00, 0x05, 0x50, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x38, 0x00, +0x2B, 0x30, 0x39, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A, 0x00, 0xA4, 0xBE, +0x7A, 0x01, 0xAF, 0x36, 0xA0, 0x00, 0x00, 0x00, 0x00, /* Asia/Vladivostok */ 0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -52909,8 +52660,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Canada/Mountain */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x88, 0xDE, 0xCE, 0xE0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x88, 0xDE, 0xCE, 0xE0, 0x9E, 0xB8, 0xAF, 0x90, 0x9F, 0xBB, 0x07, 0x80, 0xA0, 0x98, 0x91, 0x90, 0xA0, 0xD2, 0x85, 0x80, 0xA2, 0x8A, 0xE8, 0x90, 0xA3, 0x84, 0x06, 0x00, 0xA4, 0x6A, 0xCA, 0x90, 0xA5, 0x35, 0xC3, 0x80, 0xA6, 0x53, 0xE7, 0x10, 0xA7, 0x15, 0xA5, 0x80, 0xA8, 0x33, 0xC9, 0x10, 0xA8, 0xFE, 0xC2, 0x00, @@ -52942,119 +52693,100 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5D, 0xBE, 0x89, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x5F, 0x9E, 0x6B, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x61, 0x87, 0x87, 0x80, 0x62, 0x2D, 0xB2, 0x90, 0x63, 0x67, 0x69, 0x80, 0x64, 0x0D, 0x94, 0x90, 0x65, 0x47, 0x4B, 0x80, 0x65, 0xED, 0x76, 0x90, 0x67, 0x27, 0x2D, 0x80, 0x67, 0xCD, 0x58, 0x90, -0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x6B, 0x96, 0x57, 0x10, -0x6C, 0xD0, 0x0E, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x6E, 0xAF, 0xF0, 0x00, 0x6F, 0x56, 0x1B, 0x10, -0x70, 0x8F, 0xD2, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x72, 0x6F, 0xB4, 0x00, 0x73, 0x15, 0xDF, 0x10, -0x74, 0x4F, 0x96, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x76, 0x38, 0xB2, 0x80, 0x76, 0xDE, 0xDD, 0x90, -0x78, 0x18, 0x94, 0x80, 0x78, 0xBE, 0xBF, 0x90, 0x79, 0xF8, 0x76, 0x80, 0x7A, 0x9E, 0xA1, 0x90, -0x7B, 0xD8, 0x58, 0x80, 0x7C, 0x7E, 0x83, 0x90, 0x7D, 0xB8, 0x3A, 0x80, 0x7E, 0x5E, 0x65, 0x90, -0x7F, 0x98, 0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x69, 0x07, 0x0F, 0x80, 0x69, 0xAD, 0x3A, 0x90, 0x6A, 0xE6, 0xF1, 0x80, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, -0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, -0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, -0x00, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, -0xAF, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, -0x91, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, -0xE8, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, -0xCA, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, -0xE7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, -0xC9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, -0x0C, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, -0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, -0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, -0xFC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, -0xDE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, -0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, -0xA2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, -0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, -0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, -0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, -0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, -0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, -0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, -0x27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, -0x09, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, -0xEB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, -0xCD, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, -0xAF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, -0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, -0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, -0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, -0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, -0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, -0x70, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, -0x52, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, -0x34, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, -0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, -0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, -0xDA, 0x80, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, -0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, -0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, -0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, -0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, -0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, -0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, -0x7D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, -0x5F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, -0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, -0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, -0x5C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, -0x3E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, -0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, -0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, -0xE4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, -0xC6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, -0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, -0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, -0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, -0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, -0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, -0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, -0x69, 0x80, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, -0x4B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, -0x2D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, -0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, -0xF1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x96, 0x57, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xD0, -0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, 0x39, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xAF, -0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x56, 0x1B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8F, -0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xFD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, -0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xDF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4F, -0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFE, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, -0xB2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDE, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, -0x94, 0x80, 0x00, 0x00, 0x00, 0x00, 0x78, 0xBE, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, -0x76, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x9E, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xD8, -0x58, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xB8, -0x3A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5E, 0x65, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x98, -0x1C, 0x80, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, -0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x95, 0xA0, +0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, +0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x14, +0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, +0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, +0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xDE, 0xCE, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xAF, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x98, 0x91, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0xD2, 0x85, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA2, 0x8A, 0xE8, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA3, 0x84, 0x06, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA4, 0x6A, 0xCA, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xA5, 0x35, 0xC3, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA6, 0x53, 0xE7, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0x15, 0xA5, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x33, 0xC9, 0x10, +0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0xFE, 0xC2, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x0C, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x18, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xE3, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xDC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x19, 0x90, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xFC, 0x80, +0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xDE, 0x80, +0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xDD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xC0, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xBF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xA2, 0x80, +0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xA1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xBF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xA1, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xA0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x83, 0x00, +0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x82, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x65, 0x00, +0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x47, 0x00, +0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x29, 0x00, +0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x45, 0x80, +0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x27, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x26, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x09, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xEB, 0x80, +0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xCD, 0x80, +0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x1D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xAF, 0x80, +0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xCC, 0x00, +0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xE1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xAE, 0x00, +0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xC3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x90, 0x00, +0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xDF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x72, 0x00, +0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xC1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xA3, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x70, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x85, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x52, 0x80, +0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x67, 0x90, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x34, 0x80, +0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x84, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x16, 0x80, +0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x66, 0x10, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0xF8, 0x80, +0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xDA, 0x80, +0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0xF7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xD9, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xEE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xBB, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x9D, 0x00, +0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x7F, 0x00, +0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x9B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x7D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0x92, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x5F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xC5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x7C, 0x00, +0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xA7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x5E, 0x00, +0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x89, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x40, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xA5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x5C, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x87, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x3E, 0x80, +0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x69, 0x90, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x20, 0x80, +0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x4B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x02, 0x80, +0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x2D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xE4, 0x80, +0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x0F, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xC6, 0x80, +0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x2C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xE3, 0x00, +0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xC5, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xA7, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xD2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x89, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x6B, 0x00, +0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xD0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x87, 0x80, +0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xB2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x69, 0x80, +0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0x94, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x4B, 0x80, +0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x76, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x2D, 0x80, +0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x58, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x0F, 0x80, +0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x3A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xF1, 0x80, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, -0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, -0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, -0x54, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x00, 0x01, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, -0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, +0xFF, 0xFF, 0x95, 0xA0, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x9D, 0x90, +0x00, 0x08, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x0C, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x10, 0xFF, 0xFF, +0xAB, 0xA0, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, +0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, 0x0A, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Canada/Newfoundland */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -53290,8 +53022,8 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { /* Canada/Pacific */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x9E, 0xB8, 0xBD, 0xA0, 0x9F, 0xBB, 0x15, 0x90, 0xCB, 0x89, 0x1A, 0xA0, 0xD2, 0x23, 0xF4, 0x70, 0xD2, 0x61, 0x26, 0x10, 0xD3, 0x76, 0x0F, 0x20, 0xD4, 0x41, 0x08, 0x10, 0xD5, 0x55, 0xF1, 0x20, 0xD6, 0x20, 0xEA, 0x10, 0xD7, 0x35, 0xD3, 0x20, 0xD8, 0x00, 0xCC, 0x10, 0xD9, 0x15, 0xB5, 0x20, @@ -53333,13 +53065,9 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x5D, 0xBE, 0x97, 0x10, 0x5E, 0x64, 0xC2, 0x20, 0x5F, 0x9E, 0x79, 0x10, 0x60, 0x4D, 0xDE, 0xA0, 0x61, 0x87, 0x95, 0x90, 0x62, 0x2D, 0xC0, 0xA0, 0x63, 0x67, 0x77, 0x90, 0x64, 0x0D, 0xA2, 0xA0, 0x65, 0x47, 0x59, 0x90, 0x65, 0xED, 0x84, 0xA0, 0x67, 0x27, 0x3B, 0x90, 0x67, 0xCD, 0x66, 0xA0, -0x69, 0x07, 0x1D, 0x90, 0x69, 0xAD, 0x48, 0xA0, 0x6A, 0xE6, 0xFF, 0x90, 0x6B, 0x96, 0x65, 0x20, -0x6C, 0xD0, 0x1C, 0x10, 0x6D, 0x76, 0x47, 0x20, 0x6E, 0xAF, 0xFE, 0x10, 0x6F, 0x56, 0x29, 0x20, -0x70, 0x8F, 0xE0, 0x10, 0x71, 0x36, 0x0B, 0x20, 0x72, 0x6F, 0xC2, 0x10, 0x73, 0x15, 0xED, 0x20, -0x74, 0x4F, 0xA4, 0x10, 0x74, 0xFF, 0x09, 0xA0, 0x76, 0x38, 0xC0, 0x90, 0x76, 0xDE, 0xEB, 0xA0, -0x78, 0x18, 0xA2, 0x90, 0x78, 0xBE, 0xCD, 0xA0, 0x79, 0xF8, 0x84, 0x90, 0x7A, 0x9E, 0xAF, 0xA0, -0x7B, 0xD8, 0x66, 0x90, 0x7C, 0x7E, 0x91, 0xA0, 0x7D, 0xB8, 0x48, 0x90, 0x7E, 0x5E, 0x73, 0xA0, -0x7F, 0x98, 0x2A, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x69, 0x07, 0x1D, 0x90, 0x69, 0xAD, 0x48, 0xA0, 0x6A, 0xE6, 0xFF, 0x90, 0x02, 0x01, 0x02, 0x03, +0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, @@ -53348,129 +53076,114 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x05, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, +0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, +0x01, 0x10, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, +0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x54, 0x5A, 0x69, 0x66, +0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, 0x76, 0xEC, +0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, 0x15, 0x90, +0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, 0xF4, 0x70, +0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x76, 0x0F, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0x41, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, 0xF1, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xEA, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x35, 0xD3, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x00, 0xCC, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x15, 0xB5, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0xE0, 0xAE, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0xFE, 0xD1, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xC0, 0x90, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xDE, 0xB3, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xA9, 0xAC, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xBE, 0x95, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x89, 0x8E, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x9E, 0x77, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x69, 0x70, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE2, 0x7E, 0x59, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x49, 0x52, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x5E, 0x3B, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xE5, 0x29, 0x34, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE6, 0x47, 0x58, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x12, 0x51, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x27, 0x3A, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0xF2, 0x33, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x07, 0x1C, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0xD2, 0x15, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xE6, 0xFE, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xB1, 0xF7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC6, 0xE0, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x91, 0xD9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xAF, 0xFC, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x71, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8F, 0xDE, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x7F, 0xC1, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x6F, 0xC0, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x5F, 0xA3, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x4F, 0xA2, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x3F, 0x85, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x2F, 0x84, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x28, 0xA2, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x0F, 0x66, 0xA0, +0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x08, 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0xF8, 0x83, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE8, 0x66, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD8, 0x65, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC8, 0x48, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xB8, 0x47, 0x20, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x29, 0x20, +0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x0B, 0x20, +0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, 0x27, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, 0x09, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0xEB, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xCD, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, 0xAF, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xCD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, 0x91, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xAF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, 0xAE, 0x20, +0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x90, 0x20, +0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x72, 0x20, +0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x54, 0x20, +0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x36, 0x20, +0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x18, 0x20, +0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, 0x34, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, 0x16, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xF9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, 0xF8, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xDB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x2B, 0x20, +0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xBD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, 0x0D, 0x20, +0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xDA, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xEF, 0x20, +0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xBC, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xD1, 0x20, +0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x9E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, 0xED, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, 0xCF, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, 0xB1, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x7E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, 0x93, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, 0x75, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x92, 0x20, +0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x74, 0x20, +0x00, 0x00, 0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x56, 0x20, +0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xE8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x38, 0x20, +0x00, 0x00, 0x00, 0x00, 0x38, 0x1C, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, 0x1A, 0x20, +0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xE7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, 0xFC, 0x20, +0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xC9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, 0x18, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0xAB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, 0xFA, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x8D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0xDC, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, 0xBE, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, 0xA0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, 0xD3, 0x20, +0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, 0xB5, 0x20, +0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, 0x97, 0x20, +0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, 0xB3, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, 0x95, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, 0x77, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, 0x59, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, 0x3B, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, 0x1D, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, 0x3A, 0x20, +0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, 0x1C, 0x20, +0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, 0xFE, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, 0xE0, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, 0xC2, 0x20, +0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, 0xDE, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, 0xC0, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, 0xA2, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, 0x84, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, 0x66, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, 0x48, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, -0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, -0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, -0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5A, -0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xBE, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x5E, 0x3D, -0x76, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0xB8, 0xBD, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xBB, -0x15, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x89, 0x1A, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x23, -0xF4, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x61, 0x26, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x76, -0x0F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0x41, 0x08, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD5, 0x55, -0xF1, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6, 0x20, 0xEA, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x35, -0xD3, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD8, 0x00, 0xCC, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x15, -0xB5, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0xE0, 0xAE, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0xFE, -0xD1, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xC0, 0x90, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xDE, -0xB3, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xA9, 0xAC, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xBE, -0x95, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0x89, 0x8E, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x9E, -0x77, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x69, 0x70, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE2, 0x7E, -0x59, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0x49, 0x52, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE4, 0x5E, -0x3B, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE5, 0x29, 0x34, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xE6, 0x47, -0x58, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x12, 0x51, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x27, -0x3A, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0xF2, 0x33, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x07, -0x1C, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0xD2, 0x15, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xE6, -0xFE, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEC, 0xB1, 0xF7, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC6, -0xE0, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x91, 0xD9, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xAF, -0xFC, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x71, 0xBB, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8F, -0xDE, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x7F, 0xC1, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x6F, -0xC0, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF4, 0x5F, 0xA3, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x4F, -0xA2, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x3F, 0x85, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x2F, -0x84, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x28, 0xA2, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x0F, -0x66, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x08, 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0xF8, -0x83, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE8, 0x66, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD8, -0x65, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xC8, 0x48, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xB8, -0x47, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA8, 0x2A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, -0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, -0x0B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, -0x27, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, -0x09, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, -0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xCE, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, -0xCD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xF0, 0xB0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xE0, -0xAF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xD9, 0xCD, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xC0, -0x91, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xB9, 0xAF, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xA9, -0xAE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, -0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, -0x72, 0x20, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, -0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, -0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, -0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xF2, -0x34, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xE2, 0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xD2, -0x16, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xC1, 0xF9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xB1, -0xF8, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xA1, 0xDB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, -0x2B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xBD, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, -0x0D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6A, 0xDA, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, -0xEF, 0x20, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4A, 0xBC, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, -0xD1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2A, 0x9E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xFE, -0xED, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0A, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xDE, -0xCF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xEA, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xBE, -0xB1, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xD3, 0x7E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x9E, -0x93, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xB3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x7E, -0x75, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, -0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, -0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, -0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xE8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, -0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1C, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xE7, -0x1A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0xE7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xC6, -0xFC, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0xC9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xB0, -0x18, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0xAB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x8F, -0xFA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x8D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, -0xDC, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xA9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4F, -0xBE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2F, -0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x6D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xF3, -0xD3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2D, 0x8A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xD3, -0xB5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0D, 0x6C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xB3, -0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xED, 0x4E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x9C, -0xB3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xD6, 0x6A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x7C, -0x95, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xB6, 0x4C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x5C, -0x77, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x2E, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3C, -0x59, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1C, -0x3B, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xF2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xFC, -0x1D, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xD4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xE5, -0x3A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1E, 0xF1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xC5, -0x1C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xFE, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA4, -0xFE, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xDE, 0xB5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x84, -0xE0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBE, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x64, -0xC2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9E, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4D, -0xDE, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2D, -0xC0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0D, -0xA2, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xED, -0x84, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x3B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xCD, -0x66, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAD, -0x48, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE6, 0xFF, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x96, -0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xD0, 0x1C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, -0x47, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xAF, 0xFE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x56, -0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8F, 0xE0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x71, 0x36, -0x0B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, 0xC2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, -0xED, 0x20, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4F, 0xA4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0xFF, -0x09, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0xC0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDE, -0xEB, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0xA2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xBE, -0xCD, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xF8, 0x84, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x9E, -0xAF, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xD8, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7E, -0x91, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xB8, 0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5E, -0x73, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x98, 0x2A, 0x90, 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, -0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, -0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, -0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, -0x00, 0x00, 0x00, 0x01, 0x0A, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, -0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x05, +0xFF, 0xFF, 0x8C, 0x94, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, +0x00, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x0C, 0xFF, 0xFF, 0x9D, 0x90, 0x01, 0x10, 0xFF, 0xFF, +0x9D, 0x90, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, +0x50, 0x57, 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x0A, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Canada/Saskatchewan */ 0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -71315,4 +71028,4 @@ const unsigned char timelib_timezone_db_data_builtin[715312] = { }; #endif -const timelib_tzdb timezonedb_builtin = { "2026.1", 598, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; +const timelib_tzdb timezonedb_builtin = { "2026.3", 598, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; From d83851dae86dbfbf4370ec42750b606e79de7ac3 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 9 Jul 2026 15:29:05 +0100 Subject: [PATCH 131/211] Update timelib to 2022.17, which has fixes for PHP bugs --- NEWS | 7 +++++++ ext/date/lib/parse_date.re | 4 ++-- ext/date/lib/timelib.h | 6 +++--- ext/date/lib/timelib_private.h | 2 ++ ext/date/lib/tm2unixtime.c | 31 +++---------------------------- ext/date/lib/unixtime2tm.c | 27 ++++++++++++++++++--------- 6 files changed, 35 insertions(+), 42 deletions(-) diff --git a/NEWS b/NEWS index cdd6a48a4874..d64ce32ab995 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,13 @@ PHP NEWS . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with INT_MAX year). (arshidkv12) +- Date: + . Update timelib to 2022.17. (Derick) + . Fixed bug GH-19803 (Parsing a string with a single white space does create + an error). (Derick) + . Fixed Unix timestamps in February of the year 0 are misparsed with + @-notation. (LukasGelbmann) + - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index f4a392753249..07d9c0e73488 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -555,7 +555,6 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l int len = 0; /* Skip over non-numeric chars */ - while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { if (**ptr == '\0') { add_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Found unexpected data"); @@ -569,6 +568,7 @@ static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_l str[0] = '+'; /* First position is the sign */ str_ptr = str + 1; + while ((**ptr == '+') || (**ptr == '-')) { if (**ptr == '-') { str[0] = str[0] == '+' ? '-' : '+'; @@ -2020,7 +2020,7 @@ timelib_time *timelib_strtotime(const char *s, size_t len, timelib_error_contain in.errors->error_messages = NULL; if (len > 0) { - while (isspace((unsigned char)*s) && s < e) { + while (isspace((unsigned char)*s) && s <= e) { s++; } while (isspace((unsigned char)*e) && e > s) { diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index acae8e32533d..d08aa77ea5e5 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202215 -#define TIMELIB_EXTENDED_VERSION 20221501 -#define TIMELIB_ASCII_VERSION "2022.15" +#define TIMELIB_VERSION 202217 +#define TIMELIB_EXTENDED_VERSION 20221701 +#define TIMELIB_ASCII_VERSION "2022.17" #include #include diff --git a/ext/date/lib/timelib_private.h b/ext/date/lib/timelib_private.h index 3c5f9b22147c..42cbaa68dbbf 100644 --- a/ext/date/lib/timelib_private.h +++ b/ext/date/lib/timelib_private.h @@ -135,6 +135,7 @@ #ifndef TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW # define TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 0 #endif + struct _ttinfo { int32_t offset; @@ -165,6 +166,7 @@ extern "C" { #endif /* From unixtime2tm.c */ +void timelib_date_from_epoch_days(timelib_sll epoch_days, timelib_sll *y, timelib_sll *m, timelib_sll *d); int timelib_apply_localtime(timelib_time *t, unsigned int localtime); /* From parse_posix.c */ diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index f13a9412406f..4c0252e244dd 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -197,32 +197,6 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt) do_range_limit(0, 12, 12, &rt->m, &rt->y); } -static void magic_date_calc(timelib_time *time) -{ - timelib_sll y, ddd, mi, mm, dd, g; - - /* The algorithm doesn't work before the year 1 */ - if (time->d < -719498) { - return; - } - - g = time->d + HINNANT_EPOCH_SHIFT - 1; - - y = (10000 * g + 14780) / 3652425; - ddd = g - ((365*y) + (y/4) - (y/100) + (y/400)); - if (ddd < 0) { - y--; - ddd = g - ((365*y) + (y/4) - (y/100) + (y/400)); - } - mi = (100 * ddd + 52) / 3060; - mm = ((mi + 2) % 12) + 1; - y = y + (mi + 2) / 12; - dd = ddd - ((mi * 306 + 5) / 10) + 1; - time->y = y; - time->m = mm; - time->d = dd; -} - void timelib_do_normalize(timelib_time* time) { if (time->us != TIMELIB_UNSET) do_range_limit(0, 1000000, 1000000, &time->us, &time->s); @@ -232,8 +206,9 @@ void timelib_do_normalize(timelib_time* time) do_range_limit(1, 13, 12, &time->m, &time->y); /* Short cut if we're doing things against the Epoch */ - if (time->y == 1970 && time->m == 1 && time->d != 1) { - magic_date_calc(time); + if (time->y == 1970 && time->m == 1) { + timelib_date_from_epoch_days(time->d - 1, &time->y, &time->m, &time->d); + return; } do {} while (do_range_limit_days(&time->y, &time->m, &time->d)); diff --git a/ext/date/lib/unixtime2tm.c b/ext/date/lib/unixtime2tm.c index 30b3d00c7f44..9c4ba901a93d 100644 --- a/ext/date/lib/unixtime2tm.c +++ b/ext/date/lib/unixtime2tm.c @@ -26,20 +26,16 @@ #include "timelib.h" #include "timelib_private.h" -void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d) +/* Converts Unix epoch days (days since 1970-01-01) to a date. Algorithm from: + * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */ +void timelib_date_from_epoch_days(timelib_sll epoch_days, timelib_sll *y, timelib_sll *m, timelib_sll *d) { - timelib_sll days, era, t; + timelib_sll days, era; timelib_ull day_of_era, year_of_era, day_of_year, month_portion; /* Calculate days since algorithm's epoch (0000-03-01) */ - days = ts / SECS_PER_DAY + HINNANT_EPOCH_SHIFT; - - /* Adjustment for a negative time portion */ - t = ts % SECS_PER_DAY; - days += (t < 0) ? -1 : 0; + days = epoch_days + HINNANT_EPOCH_SHIFT; - /* Calculate year, month, and day. Algorithm from: - * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */ era = (days >= 0 ? days : days - DAYS_PER_ERA + 1) / DAYS_PER_ERA; day_of_era = days - era * DAYS_PER_ERA; year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / DAYS_PER_YEAR; @@ -49,6 +45,19 @@ void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timel *d = day_of_year - (153 * month_portion + 2) / 5 + 1; *m = month_portion + (month_portion < 10 ? 3 : -9); *y += (*m <= 2); +} + +void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d) +{ + timelib_sll epoch_days, t; + + epoch_days = ts / SECS_PER_DAY; + + /* Adjustment for a negative time portion */ + t = ts % SECS_PER_DAY; + epoch_days += (t < 0) ? -1 : 0; + + timelib_date_from_epoch_days(epoch_days, y, m, d); TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, *y, *m, *d);); } From c3f1015e5655ac5d3ddb06f6a7c222886890bc83 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Thu, 9 Jul 2026 18:44:23 +0100 Subject: [PATCH 132/211] Add information about PIE extensions (#22613) Adds information about PIE to the PHP extensions section of the readme, also indicating that PECL is now deprecated. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f4e0534ede2..d83203d74c0b 100644 --- a/README.md +++ b/README.md @@ -130,8 +130,10 @@ permissions. ## PHP extensions Extensions provide additional functionality on top of PHP. PHP consists of many -essential bundled extensions. Additional extensions can be found in the PHP -Extension Community Library - [PECL](https://pecl.php.net). +essential bundled extensions. Additional extensions can be found on the +[PIE Extensions](https://packagist.org/extensions) list, and installed with +[🥧 PIE, the PHP Installer for Extensions](https://github.com/php/pie) or the +deprecated PHP Extension Community Library - [PECL](https://pecl.php.net). ## Contributing From 201e3cc3d20d947ad38ebf6c3fddfee9c957a720 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 16 Nov 2025 15:47:32 +0000 Subject: [PATCH 133/211] ext/zip: stricter numeric handling for archive array options. Use zval_try_get_long() for the numeric entries of the options array (comp_method, comp_flags, enc_method, flags) so numeric strings are coerced and non-numeric values raise a TypeError instead of a warning. remove_all_path is now strictly bool. php_zip_status()/_sys() return values are widened to zend_long. close GH-20507 --- UPGRADING | 4 ++++ ext/zip/php_zip.c | 51 ++++++++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/UPGRADING b/UPGRADING index a6cd3bdef1cb..031b6751d853 100644 --- a/UPGRADING +++ b/UPGRADING @@ -209,6 +209,10 @@ PHP 8.6 UPGRADE NOTES - Zip: . ZipArchive::extractTo now raises a TypeError for the files argument if one or more of the entries is not a string. + . ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError + when the "remove_all_path" option is not of type bool, or when the + "comp_method", "comp_flags", or "enc_method" options are not of type int + (numeric strings are coerced), instead of emitting a warning. - Zlib: . deflate_init() now raises a TypeError when the value for option "level", diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 4156a8deb861..1301c6fc6b17 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -360,6 +360,8 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) /* {{{ */ { zval *option; + zend_long tmp; + bool failed = false; /* default values */ opts->flags = ZIP_FL_OVERWRITE; @@ -370,43 +372,45 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) if ((option = zend_hash_str_find(options, "remove_all_path", sizeof("remove_all_path") - 1)) != NULL) { if (Z_TYPE_P(option) != IS_FALSE && Z_TYPE_P(option) != IS_TRUE) { - php_error_docref(NULL, E_WARNING, "Option \"remove_all_path\" must be of type bool, %s given", - zend_zval_value_name(option)); + zend_type_error("Option \"remove_all_path\" must be of type bool, %s given", zend_zval_value_name(option)); + return FAILURE; } - opts->remove_all_path = zval_get_long(option); + opts->remove_all_path = Z_TYPE_P(option) == IS_TRUE; } if ((option = zend_hash_str_find(options, "comp_method", sizeof("comp_method") - 1)) != NULL) { - if (Z_TYPE_P(option) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be of type int, %s given", - zend_zval_value_name(option)); + tmp = zval_try_get_long(option, &failed); + if (failed) { + zend_type_error("Option \"comp_method\" must be of type int, %s given", zend_zval_value_name(option)); + return FAILURE; } - zend_long comp_method = zval_get_long(option); + zend_long comp_method = tmp; if (comp_method < 0 || comp_method > INT_MAX) { php_error_docref(NULL, E_WARNING, "Option \"comp_method\" must be between 0 and %d", INT_MAX); } opts->comp_method = (zip_int32_t)comp_method; if ((option = zend_hash_str_find(options, "comp_flags", sizeof("comp_flags") - 1)) != NULL) { - if (Z_TYPE_P(option) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be of type int, %s given", - zend_zval_value_name(option)); + tmp = zval_try_get_long(option, &failed); + if (failed) { + zend_type_error("Option \"comp_flags\" must be of type int, %s given", zend_zval_value_name(option)); + return FAILURE; } - zend_long comp_flags = zval_get_long(option); - if (comp_flags < 0 || comp_flags > USHRT_MAX) { + if (tmp < 0 || tmp > USHRT_MAX) { php_error_docref(NULL, E_WARNING, "Option \"comp_flags\" must be between 0 and %u", USHRT_MAX); } - opts->comp_flags = (zip_uint32_t)comp_flags; + opts->comp_flags = (zip_uint32_t)tmp; } } #ifdef HAVE_ENCRYPTION if ((option = zend_hash_str_find(options, "enc_method", sizeof("enc_method") - 1)) != NULL) { - if (Z_TYPE_P(option) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "Option \"enc_method\" must be of type int, %s given", - zend_zval_value_name(option)); + tmp = zval_try_get_long(option, &failed); + if (failed) { + zend_type_error("Option \"enc_method\" must be of type int, %s given", zend_zval_value_name(option)); + return FAILURE; } - opts->enc_method = zval_get_long(option); + opts->enc_method = tmp; if ((option = zend_hash_str_find(options, "enc_password", sizeof("enc_password") - 1)) != NULL) { if (Z_TYPE_P(option) != IS_STRING) { @@ -460,12 +464,13 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) } if ((option = zend_hash_str_find(options, "flags", sizeof("flags") - 1)) != NULL) { - if (Z_TYPE_P(option) != IS_LONG) { + tmp = zval_try_get_long(option, &failed); + if (failed) { zend_type_error("Option \"flags\" must be of type int, %s given", zend_zval_value_name(option)); return FAILURE; } - opts->flags = Z_LVAL_P(option); + opts->flags = tmp; } return SUCCESS; @@ -515,13 +520,13 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) static zend_long php_zip_status(ze_zip_object *obj) /* {{{ */ { - int zep = obj->err_zip; /* saved err if closed */ + zend_long zep = (zend_long)obj->err_zip; /* saved err if closed */ if (obj->za) { zip_error_t *err; err = zip_get_error(obj->za); - zep = zip_error_code_zip(err); + zep = (zend_long)zip_error_code_zip(err); zip_error_fini(err); } return zep; @@ -536,13 +541,13 @@ static zend_long php_zip_last_id(ze_zip_object *obj) /* {{{ */ static zend_long php_zip_status_sys(ze_zip_object *obj) /* {{{ */ { - int syp = obj->err_sys; /* saved err if closed */ + zend_long syp = (zend_long)obj->err_sys; /* saved err if closed */ if (obj->za) { zip_error_t *err; err = zip_get_error(obj->za); - syp = zip_error_code_system(err); + syp = (zend_long)zip_error_code_system(err); zip_error_fini(err); } return syp; From ebed41a03b8b1f9135874d9aac1320d17eb37e95 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 10 Jul 2026 16:37:52 +0800 Subject: [PATCH 134/211] Fix GH-22649: avoid ZipArchive comment reset crash (#22652) Rewrite the PHP_ZIP_SET_FILE_COMMENT macro to solve this uaf that occurs after overwriting an existing entry and setting its inherited unchanged comment again. My solution is to simply add a if branch. I also add some defensive checks in the function by the way (ZEND_ASSERT(comment_len <= 0xffff);) just in case someone will re-use this helper in the future. Fixes #22649 --- NEWS | 3 +++ ext/zip/php_zip.c | 40 +++++++++++++++++---------- ext/zip/tests/gh22649.phpt | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 ext/zip/tests/gh22649.phpt diff --git a/NEWS b/NEWS index d64ce32ab995..7aada8213bb5 100644 --- a/NEWS +++ b/NEWS @@ -85,6 +85,9 @@ PHP NEWS when parsing an IFF chunk with a size of INT_MAX. (David Carlier, Weilin Du) - Zip: + . Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex() + could crash after overwriting an entry and resetting its inherited + unchanged comment). (Weilin Du) . Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du) diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 16df403dcda5..ccc474bc715c 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -64,17 +64,26 @@ static int le_zip_entry; } /* }}} */ -/* {{{ PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) */ -#define PHP_ZIP_SET_FILE_COMMENT(za, index, comment, comment_len) \ - if (comment_len == 0) { \ - /* Passing NULL remove the existing comment */ \ - if (zip_file_set_comment(za, index, NULL, 0, 0) < 0) { \ - RETURN_FALSE; \ - } \ - } else if (zip_file_set_comment(za, index, comment, comment_len, 0) < 0) { \ - RETURN_FALSE; \ - } \ - RETURN_TRUE; +/* {{{ php_zip_set_file_comment */ +static bool php_zip_set_file_comment(struct zip *za, zip_uint64_t index, const char *comment, size_t comment_len) +{ + zip_uint32_t current_comment_len; + const char *current_comment = zip_file_get_comment(za, index, ¤t_comment_len, ZIP_FL_ENC_RAW); + + /* Avoid a libzip use-after-free when resetting an unchanged inherited comment. */ + if (current_comment && current_comment_len == comment_len + && memcmp(current_comment, comment, comment_len) == 0) { + return true; + } + + if (comment_len == 0) { + /* Passing NULL removes the existing comment. */ + return zip_file_set_comment(za, index, NULL, 0, 0) == 0; + } + + ZEND_ASSERT(comment_len <= 0xffff); + return zip_file_set_comment(za, index, comment, (zip_uint16_t) comment_len, 0) == 0; +} /* }}} */ # define add_ascii_assoc_string add_assoc_string @@ -2185,7 +2194,7 @@ PHP_METHOD(ZipArchive, setCommentName) zval *self = ZEND_THIS; size_t comment_len, name_len; char * comment, *name; - int idx; + zip_int64_t idx; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &comment, &comment_len) == FAILURE) { @@ -2208,7 +2217,7 @@ PHP_METHOD(ZipArchive, setCommentName) if (idx < 0) { RETURN_FALSE; } - PHP_ZIP_SET_FILE_COMMENT(intern, idx, comment, comment_len); + RETURN_BOOL(php_zip_set_file_comment(intern, (zip_uint64_t) idx, comment, comment_len)); } /* }}} */ @@ -2221,6 +2230,7 @@ PHP_METHOD(ZipArchive, setCommentIndex) size_t comment_len; char * comment; struct zip_stat sb; + zip_uint64_t idx; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &index, &comment, &comment_len) == FAILURE) { @@ -2235,7 +2245,9 @@ PHP_METHOD(ZipArchive, setCommentIndex) } PHP_ZIP_STAT_INDEX(intern, index, 0, sb); - PHP_ZIP_SET_FILE_COMMENT(intern, index, comment, comment_len); + idx = (zip_uint64_t) index; + + RETURN_BOOL(php_zip_set_file_comment(intern, idx, comment, comment_len)); } /* }}} */ diff --git a/ext/zip/tests/gh22649.phpt b/ext/zip/tests/gh22649.phpt new file mode 100644 index 000000000000..d6d0a0928184 --- /dev/null +++ b/ext/zip/tests/gh22649.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-22649 (setCommentName/Index after addFromString should not segfault) +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE)) { + exit('failed'); +} + +$zip->addFromString('dir/entry2d.txt', 'entry #2'); +var_dump($zip->setCommentName('dir/entry2d.txt', 'dir/entry2d.txt')); + +$zip->addFromString('dir/entry3d.txt', 'entry #3'); +var_dump($zip->setCommentIndex($zip->lastId, 'dir/entry3d.txt')); + +$zip->close(); + +if (!$zip->open($file, ZipArchive::CREATE)) { + exit('failed'); +} + +$zip->addFromString('dir/entry2d.txt', 'updated entry #2'); +var_dump($zip->setCommentName('dir/entry2d.txt', 'dir/entry2d.txt')); + +$zip->addFromString('dir/entry3d.txt', 'updated entry #3'); +var_dump($zip->setCommentIndex($zip->lastId, 'dir/entry3d.txt')); + +$zip->close(); + +if (!$zip->open($file)) { + exit('failed'); +} + +var_dump($zip->getCommentName('dir/entry2d.txt')); +var_dump($zip->getCommentName('dir/entry3d.txt')); + +$zip->close(); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +string(15) "dir/entry2d.txt" +string(15) "dir/entry3d.txt" +--CLEAN-- + From 34951bf55c835baf6bdcfb57a9eff780ed9d2ff6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 10 Jul 2026 13:04:34 +0100 Subject: [PATCH 135/211] [skip ci] NEWS entry for zip extension --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 0045d19c4f14..919fda31c992 100644 --- a/NEWS +++ b/NEWS @@ -91,6 +91,9 @@ PHP NEWS . Fixed bug GH-22176 (memory leak with ZipArchive::registerCancelBack() is used with reference returning function during shutdown). (David Carlier) + . ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError + for invalid "remove_all_path", "comp_method", "comp_flags", and + "enc_method" options instead of emitting a warning. (David Carlier) 02 Jul 2026, PHP 8.6.0alpha1 From 3c15cf1e4f3ec9919c0ccbc67afa74e76ccd161f Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 10 Jul 2026 13:37:11 +0100 Subject: [PATCH 136/211] Add support for -1 as valid year for idate() output. (#22657) * Add support for -1 as valid year for idate() output. Closes GH-13273 * Simplify error handling * Add note in UPGRADING.INTERNALS about API change --- NEWS | 1 + UPGRADING.INTERNALS | 4 +++ ext/date/php_date.c | 59 ++++++++++++++++++++++++--------------------- ext/date/php_date.h | 2 +- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/NEWS b/NEWS index 919fda31c992..6d8d5df08ed8 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ PHP NEWS an error). (Derick) . Fixed Unix timestamps in February of the year 0 are misparsed with @-notation. (LukasGelbmann) + . Fixed bug GH-11368 (idate() doesn't work for the year -1). (Derick) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 1c602548b1ed..bdf874b382d0 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -169,6 +169,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES 3. Module changes ======================== +- ext/date: + . php_idate() now returns the result state, and moves the return value into an + out parameter. + - ext/mbstring: . Added GB18030-2022 to default encoding list for zh-CN. diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 1dbcf51e72df..e14aef1e0855 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -901,13 +901,13 @@ PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_ /* }}} */ /* {{{ php_idate */ -PHPAPI int php_idate(char format, time_t ts, bool localtime) +PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result) { timelib_time *t; timelib_tzinfo *tzi; - int retval = -1; timelib_time_offset *offset = NULL; timelib_sll isoweek, isoyear; + bool success = true; t = timelib_time_ctor(); @@ -947,46 +947,49 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime) switch (format) { /* day */ - case 'd': case 'j': retval = (int) t->d; break; + case 'd': case 'j': *result = (int) t->d; break; - case 'N': retval = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break; - case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break; - case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break; + case 'N': *result = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break; + case 'w': *result = (int) timelib_day_of_week(t->y, t->m, t->d); break; + case 'z': *result = (int) timelib_day_of_year(t->y, t->m, t->d); break; /* week */ - case 'W': retval = (int) isoweek; break; /* iso weeknr */ + case 'W': *result = (int) isoweek; break; /* iso weeknr */ /* month */ - case 'm': case 'n': retval = (int) t->m; break; - case 't': retval = (int) timelib_days_in_month(t->y, t->m); break; + case 'm': case 'n': *result = (int) t->m; break; + case 't': *result = (int) timelib_days_in_month(t->y, t->m); break; /* year */ - case 'L': retval = (int) timelib_is_leap((int) t->y); break; - case 'y': retval = (int) (t->y % 100); break; - case 'Y': retval = (int) t->y; break; - case 'o': retval = (int) isoyear; break; /* iso year */ + case 'L': *result = (int) timelib_is_leap((int) t->y); break; + case 'y': *result = (int) (t->y % 100); break; + case 'Y': *result = (int) t->y; break; + case 'o': *result = (int) isoyear; break; /* iso year */ /* Swatch Beat a.k.a. Internet Time */ case 'B': - retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10); - if (retval < 0) { - retval += 864000; + *result = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10); + if (*result < 0) { + *result += 864000; } /* Make sure to do this on a positive int to avoid rounding errors */ - retval = (retval / 864) % 1000; + *result = (*result / 864) % 1000; break; /* time */ - case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break; - case 'H': case 'G': retval = (int) t->h; break; - case 'i': retval = (int) t->i; break; - case 's': retval = (int) t->s; break; + case 'g': case 'h': *result = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break; + case 'H': case 'G': *result = (int) t->h; break; + case 'i': *result = (int) t->i; break; + case 's': *result = (int) t->s; break; /* timezone */ - case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break; - case 'Z': retval = (int) (!localtime ? offset->offset : 0); break; + case 'I': *result = (int) (!localtime ? offset->is_dst : 0); break; + case 'Z': *result = (int) (!localtime ? offset->offset : 0); break; - case 'U': retval = (int) t->sse; break; + case 'U': *result = (int) t->sse; break; + + default: + success = false; } if (!localtime) { @@ -994,7 +997,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime) } timelib_time_dtor(t); - return retval; + return success; } /* }}} */ @@ -1018,7 +1021,7 @@ PHP_FUNCTION(idate) zend_string *format; zend_long ts; bool ts_is_null = 1; - int ret; + int ret = 0; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(format) @@ -1035,8 +1038,8 @@ PHP_FUNCTION(idate) ts = php_time(); } - ret = php_idate(ZSTR_VAL(format)[0], ts, 0); - if (ret == -1) { + bool ok = php_idate(ZSTR_VAL(format)[0], ts, 0, &ret); + if (!ok) { php_error_docref(NULL, E_WARNING, "Unrecognized date format token"); RETURN_FALSE; } diff --git a/ext/date/php_date.h b/ext/date/php_date.h index f26617c15ea9..651cc28225fd 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -124,7 +124,7 @@ PHPAPI time_t php_time(void); /* Backwards compatibility wrapper */ PHPAPI zend_long php_parse_date(const char *string, zend_long *now); PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, bool gmt); -PHPAPI int php_idate(char format, time_t ts, bool localtime); +PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result); #define _php_strftime php_strftime From d1d144e44bd3a23c7f80c6d29dd77d4d43e089a0 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Fri, 10 Jul 2026 14:37:32 +0200 Subject: [PATCH 137/211] Throw an error on null byte in curl array options (#22662) This is already done for string options in php_curl_option_str (see bug #68089). Similarly, array arguments should also not contain null bytes, so we throw the same error for these. This was extracted from https://github.com/php/php-src/pull/22651, as this seems less controversial. Using null bytes is clearly not supported and curl just throws away everything after the null byte, so https://wiki.php.net/rfc/policy-exempt-type-value-error-bc-policy seems to apply here. --- ext/curl/interface.c | 76 ++++++++++--------- .../tests/curl_setopt_error_nul_byte.phpt | 52 +++++++++++++ 2 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 ext/curl/tests/curl_setopt_error_nul_byte.phpt diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 1dff14b7c8c2..09c7009068b6 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2030,42 +2030,42 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue HashTable *ph; zend_string *val, *tmp_val; struct curl_slist *slist = NULL; + const char *name = NULL; - if (Z_TYPE_P(zvalue) != IS_ARRAY) { - const char *name = NULL; - switch (option) { - case CURLOPT_HTTPHEADER: - name = "CURLOPT_HTTPHEADER"; - break; - case CURLOPT_QUOTE: - name = "CURLOPT_QUOTE"; - break; - case CURLOPT_HTTP200ALIASES: - name = "CURLOPT_HTTP200ALIASES"; - break; - case CURLOPT_POSTQUOTE: - name = "CURLOPT_POSTQUOTE"; - break; - case CURLOPT_PREQUOTE: - name = "CURLOPT_PREQUOTE"; - break; - case CURLOPT_TELNETOPTIONS: - name = "CURLOPT_TELNETOPTIONS"; - break; - case CURLOPT_MAIL_RCPT: - name = "CURLOPT_MAIL_RCPT"; - break; - case CURLOPT_RESOLVE: - name = "CURLOPT_RESOLVE"; - break; - case CURLOPT_PROXYHEADER: - name = "CURLOPT_PROXYHEADER"; - break; - case CURLOPT_CONNECT_TO: - name = "CURLOPT_CONNECT_TO"; - break; - } + switch (option) { + case CURLOPT_HTTPHEADER: + name = "CURLOPT_HTTPHEADER"; + break; + case CURLOPT_QUOTE: + name = "CURLOPT_QUOTE"; + break; + case CURLOPT_HTTP200ALIASES: + name = "CURLOPT_HTTP200ALIASES"; + break; + case CURLOPT_POSTQUOTE: + name = "CURLOPT_POSTQUOTE"; + break; + case CURLOPT_PREQUOTE: + name = "CURLOPT_PREQUOTE"; + break; + case CURLOPT_TELNETOPTIONS: + name = "CURLOPT_TELNETOPTIONS"; + break; + case CURLOPT_MAIL_RCPT: + name = "CURLOPT_MAIL_RCPT"; + break; + case CURLOPT_RESOLVE: + name = "CURLOPT_RESOLVE"; + break; + case CURLOPT_PROXYHEADER: + name = "CURLOPT_PROXYHEADER"; + break; + case CURLOPT_CONNECT_TO: + name = "CURLOPT_CONNECT_TO"; + break; + } + if (Z_TYPE_P(zvalue) != IS_ARRAY) { zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name); return FAILURE; } @@ -2074,6 +2074,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue ZEND_HASH_FOREACH_VAL(ph, current) { ZVAL_DEREF(current); val = zval_get_tmp_string(current, &tmp_val); + + if (zend_str_has_nul_byte(val)) { + curl_slist_free_all(slist); + zend_tmp_string_release(tmp_val); + zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name); + return FAILURE; + } + struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val)); zend_tmp_string_release(tmp_val); if (!new_slist) { diff --git a/ext/curl/tests/curl_setopt_error_nul_byte.phpt b/ext/curl/tests/curl_setopt_error_nul_byte.phpt new file mode 100644 index 000000000000..23be973b82c2 --- /dev/null +++ b/ext/curl/tests/curl_setopt_error_nul_byte.phpt @@ -0,0 +1,52 @@ +--TEST-- +curl_setopt() throws ValueError for NUL bytes in lists +--EXTENSIONS-- +curl +--FILE-- +getMessage() . "\n\n"; + } +} + +$ch = null; +?> +--EXPECT-- +CURLOPT_HTTP200ALIASES: curl_setopt(): cURL option CURLOPT_HTTP200ALIASES must not contain any null bytes + +CURLOPT_HTTPHEADER: curl_setopt(): cURL option CURLOPT_HTTPHEADER must not contain any null bytes + +CURLOPT_POSTQUOTE: curl_setopt(): cURL option CURLOPT_POSTQUOTE must not contain any null bytes + +CURLOPT_PREQUOTE: curl_setopt(): cURL option CURLOPT_PREQUOTE must not contain any null bytes + +CURLOPT_QUOTE: curl_setopt(): cURL option CURLOPT_QUOTE must not contain any null bytes + +CURLOPT_TELNETOPTIONS: curl_setopt(): cURL option CURLOPT_TELNETOPTIONS must not contain any null bytes + +CURLOPT_MAIL_RCPT: curl_setopt(): cURL option CURLOPT_MAIL_RCPT must not contain any null bytes + +CURLOPT_RESOLVE: curl_setopt(): cURL option CURLOPT_RESOLVE must not contain any null bytes + +CURLOPT_PROXYHEADER: curl_setopt(): cURL option CURLOPT_PROXYHEADER must not contain any null bytes + +CURLOPT_CONNECT_TO: curl_setopt(): cURL option CURLOPT_CONNECT_TO must not contain any null bytes From 8b1079a890a55b4865585001a6e5dae4e396ac0b Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 10 Jul 2026 13:38:56 +0100 Subject: [PATCH 138/211] Fix GH-11310: __debugInfo does nothing on userland classes extending Date classes (#22655) * Fix GH-11310: __debugInfo does nothing on userland classes extending Date classes * Add tests to make sure not calling the parent constructor has any effect --- NEWS | 2 ++ ext/date/php_date.c | 24 ++++++++++++++++++-- ext/date/tests/bug-gh11310-1.phpt | 37 +++++++++++++++++++++++++++++++ ext/date/tests/bug-gh11310-2.phpt | 37 +++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 ext/date/tests/bug-gh11310-1.phpt create mode 100644 ext/date/tests/bug-gh11310-2.phpt diff --git a/NEWS b/NEWS index 7aada8213bb5..2d0abd7eb42f 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ PHP NEWS an error). (Derick) . Fixed Unix timestamps in February of the year 0 are misparsed with @-notation. (LukasGelbmann) + . Fixed bug GH-11310 (__debugInfo does nothing on userland classes extending + Date classes). (Derick) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 08a91a6318e9..e16a61035c5b 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1995,12 +1995,22 @@ static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_ php_date_obj *dateobj; switch (purpose) { - case ZEND_PROP_PURPOSE_DEBUG: case ZEND_PROP_PURPOSE_SERIALIZE: case ZEND_PROP_PURPOSE_VAR_EXPORT: case ZEND_PROP_PURPOSE_JSON: case ZEND_PROP_PURPOSE_ARRAY_CAST: break; + case ZEND_PROP_PURPOSE_DEBUG: { + if (object->ce->__debugInfo) { + int is_temp = 0; + HashTable *ht = zend_std_get_debug_info(object, &is_temp); + if (ht && !is_temp) { + GC_TRY_ADDREF(ht); + } + return ht; + } + break; + } default: return zend_std_get_properties_for(object, purpose); } @@ -2117,12 +2127,22 @@ static HashTable *date_object_get_properties_for_timezone(zend_object *object, z php_timezone_obj *tzobj; switch (purpose) { - case ZEND_PROP_PURPOSE_DEBUG: case ZEND_PROP_PURPOSE_SERIALIZE: case ZEND_PROP_PURPOSE_VAR_EXPORT: case ZEND_PROP_PURPOSE_JSON: case ZEND_PROP_PURPOSE_ARRAY_CAST: break; + case ZEND_PROP_PURPOSE_DEBUG: { + if (object->ce->__debugInfo) { + int is_temp = 0; + HashTable *ht = zend_std_get_debug_info(object, &is_temp); + if (ht && !is_temp) { + GC_TRY_ADDREF(ht); + } + return ht; + } + break; + } default: return zend_std_get_properties_for(object, purpose); } diff --git a/ext/date/tests/bug-gh11310-1.phpt b/ext/date/tests/bug-gh11310-1.phpt new file mode 100644 index 000000000000..c12b0201294d --- /dev/null +++ b/ext/date/tests/bug-gh11310-1.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug GH-11310: __debugInfo does nothing on userland classes extending Date classes +--FILE-- + 'zzz']; } } +class UDateTimeImmutable extends DateTimeImmutable { public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDateTimeZone extends DateTimeZone { public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDateInterval extends DateInterval { public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDatePeriod extends DatePeriod { public function __debugInfo(): array { return ['value' => 'zzz']; } } + +$d = new UDateTime(); var_dump($d); +$d = new UDateTimeImmutable(); var_dump($d); +$d = new UDateTimeZone("Europe/Kyiv"); var_dump($d); +$d = new UDateInterval("P3D"); var_dump($d); +$d = UDatePeriod::createFromISO8601String("2026-07-09T17:23:06Z/P3D/R5"); var_dump($d); +?> +--EXPECTF-- +object(UDateTime)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateTimeImmutable)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateTimeZone)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateInterval)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDatePeriod)#%d (1) { + ["value"]=> + string(3) "zzz" +} diff --git a/ext/date/tests/bug-gh11310-2.phpt b/ext/date/tests/bug-gh11310-2.phpt new file mode 100644 index 000000000000..8dce510a19d6 --- /dev/null +++ b/ext/date/tests/bug-gh11310-2.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug GH-11310: __debugInfo does nothing on userland classes extending Date classes +--FILE-- + 'zzz']; } } +class UDateTimeImmutable extends DateTimeImmutable { public function __construct() {} public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDateTimeZone extends DateTimeZone { public function __construct() {} public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDateInterval extends DateInterval { public function __construct() {} public function __debugInfo(): array { return ['value' => 'zzz']; } } +class UDatePeriod extends DatePeriod { public function __construct() {} public function __debugInfo(): array { return ['value' => 'zzz']; } } + +$d = new UDateTime(); var_dump($d); +$d = new UDateTimeImmutable(); var_dump($d); +$d = new UDateTimeZone("Europe/Kyiv"); var_dump($d); +$d = new UDateInterval("P3D"); var_dump($d); +$d = UDatePeriod::createFromISO8601String("2026-07-09T17:23:06Z/P3D/R5"); var_dump($d); +?> +--EXPECTF-- +object(UDateTime)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateTimeImmutable)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateTimeZone)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDateInterval)#%d (1) { + ["value"]=> + string(3) "zzz" +} +object(UDatePeriod)#%d (1) { + ["value"]=> + string(3) "zzz" +} From 5b00437a09de5bb0ae1631527d519e6ad9851a33 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 9 Jul 2026 11:39:37 -0700 Subject: [PATCH 139/211] Add regression tests for `ReflectionConstant::__toString()` with null bytes So that when the output is fixed the change can be confirmed in tests --- .../tests/ReflectionConstant_null_byte_value.phpt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ext/reflection/tests/ReflectionConstant_null_byte_value.phpt diff --git a/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt b/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt new file mode 100644 index 000000000000..5e1b855eed05 --- /dev/null +++ b/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-22658: ReflectionConstant with a string value with a null byte +--FILE-- +getValue() ); + +?> +--EXPECTF-- +Constant [ string DEMO ] { f } +string(4) "f%0oo" From ed3e06a6738b5ab9b7ed8e92cda79832cd172ea1 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 9 Jul 2026 11:41:30 -0700 Subject: [PATCH 140/211] GH-22658: avoid truncation on null bytes in `ReflectionConstant::__toString()` --- ext/reflection/php_reflection.c | 2 +- ext/reflection/tests/ReflectionConstant_null_byte_value.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e747a642778c..661b1eb1f3a4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -585,7 +585,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c if (Z_TYPE_P(value) == IS_ARRAY) { smart_str_append(str, ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED)); } else if (Z_TYPE_P(value) == IS_STRING) { - smart_str_appends(str, Z_STRVAL_P(value)); + smart_str_append(str, Z_STR_P(value)); } else { zend_string *tmp_value_str; zend_string *value_str = zval_get_tmp_string(value, &tmp_value_str); diff --git a/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt b/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt index 5e1b855eed05..8b6a8e85c2fc 100644 --- a/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt +++ b/ext/reflection/tests/ReflectionConstant_null_byte_value.phpt @@ -10,5 +10,5 @@ var_dump( $r->getValue() ); ?> --EXPECTF-- -Constant [ string DEMO ] { f } +Constant [ string DEMO ] { f%0oo } string(4) "f%0oo" From 834bdcedd1c89cdbc722567a6c4f23c3df1d2554 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 07:22:08 -0700 Subject: [PATCH 141/211] NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 2d0abd7eb42f..7324e5c591aa 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,8 @@ PHP NEWS ReflectionParameter::__construct()). (jorgsowa) . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore dynamic properties shadowing a private parent property). (iliaal) + . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value + with null bytes truncates output). (DanielEScherzer) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between From 032b2d392de231c0cdbbc886be5f00fa59dc98c1 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Fri, 10 Jul 2026 16:52:26 +0100 Subject: [PATCH 142/211] Fixed __debugInfo() usage on PHP 8.5 and later for DatePeriod (#22677) --- ext/date/php_date.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index bcf4bcdded06..ad4b4a51101e 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -6076,6 +6076,17 @@ static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string * static HashTable *date_period_get_properties_for(zend_object *object, zend_prop_purpose purpose) { + if (purpose == ZEND_PROP_PURPOSE_DEBUG) { + if (object->ce->__debugInfo) { + int is_temp = 0; + HashTable *ht = zend_std_get_debug_info(object, &is_temp); + if (ht && !is_temp) { + GC_TRY_ADDREF(ht); + } + return ht; + } + } + php_period_obj *period_obj = php_period_obj_from_obj(object); HashTable *props = zend_array_dup(zend_std_get_properties(object)); if (!period_obj->initialized) { From 955a2ce5195ea8ab94ba7a5afadd828e74bce5b6 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 11:02:22 -0700 Subject: [PATCH 143/211] [8.5] NEWS: add entry for bugfix from GH-22659 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 3851713db0db..ecd2c1267112 100644 --- a/NEWS +++ b/NEWS @@ -82,6 +82,8 @@ PHP NEWS ReflectionParameter::__construct()). (jorgsowa) . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore dynamic properties shadowing a private parent property). (iliaal) + . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value + with null bytes truncates output). (DanielEScherzer) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between From 65a69b4a09ff454e0ed2c597d23f4c9dc52d1507 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 15:33:45 -0400 Subject: [PATCH 144/211] Fix GH-22671: assert.bail must not unwind with a pending exception (#22679) zend_throw_unwind_exit() requires !EG(exception), but assert.bail called it right after zend_exception_error(), which can itself re-throw while printing the callback's exception (a throwing __toString, a throwing __destruct when the exception is released, or the call-stack guard re-tripping at the stack limit). Only throw the unwind exit when no exception is pending; otherwise let the re-thrown exception propagate, matching exit() and phar. Fixes GH-22671 --- NEWS | 2 ++ ext/standard/assert.c | 4 ++- ext/standard/tests/assert/gh22671.phpt | 40 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/assert/gh22671.phpt diff --git a/NEWS b/NEWS index ac709587f699..cd259f06660e 100644 --- a/NEWS +++ b/NEWS @@ -80,6 +80,8 @@ PHP NEWS - Standard: . Fixed sleep() and usleep() to reject values that overflow the underlying unsigned int timeout. (Weilin Du) + . Fixed bug GH-22671 (assert.bail aborts the process when the assert callback + throws an exception whose reporting re-throws). (iliaal) - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL diff --git a/ext/standard/assert.c b/ext/standard/assert.c index a976aeda1494..618f69a2cf74 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -245,7 +245,9 @@ PHP_FUNCTION(assert) * exception so we can avoid bailout and use unwind_exit. */ zend_exception_error(EG(exception), E_WARNING); } - zend_throw_unwind_exit(); + if (!EG(exception)) { + zend_throw_unwind_exit(); + } RETURN_THROWS(); } else { RETURN_FALSE; diff --git a/ext/standard/tests/assert/gh22671.phpt b/ext/standard/tests/assert/gh22671.phpt new file mode 100644 index 000000000000..3487b0687ccd --- /dev/null +++ b/ext/standard/tests/assert/gh22671.phpt @@ -0,0 +1,40 @@ +--TEST-- +GH-22671 (assert.bail must not call zend_throw_unwind_exit with a pending exception) +--INI-- +zend.assertions=1 +assert.bail=1 +assert.exception=0 +assert.callback=cb +error_reporting=0 +--FILE-- + +--EXPECT-- +shutdown reached From 9073875eb98416595f1666df0f6a728bab2bc5c7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 6 Jul 2026 23:25:57 +0100 Subject: [PATCH 145/211] ext/dom: Use-after-free with namespace nodes from XSLTProcessor::registerPHPFunctions(). Fix #22621 Namespace parents from an external document() were wrapped directly instead of through the proxy factory, so they skipped the copy that keeps non-main-document nodes alive. A DOMNameSpaceNode retained by userland then dangled once xsltFreeTransformContext() freed the external document. Route the namespace parent through the proxy factory too. close GH-22623 --- NEWS | 2 ++ ext/dom/xpath_callbacks.c | 6 ++-- ext/xsl/tests/gh22621.phpt | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 ext/xsl/tests/gh22621.phpt diff --git a/NEWS b/NEWS index cd259f06660e..f40c6dfd8da6 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,8 @@ PHP NEWS . Fixed Dom\DtdNamedNodeMap integer dimension access so negative indexes return NULL and indexes outside the int range throw ValueError instead of returning the first entity or notation. (Weilin Du) + . Fixed bug GH-22623 (use after free with namespace nodes from + XSLTProcessor::registerFunctions())/ (David Carlier) - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 622dd187dd84..349c304c9f25 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -347,15 +347,15 @@ static zval *php_dom_xpath_callback_fetch_args(xmlXPathParserContextPtr ctxt, ui xmlNodePtr node = obj->nodesetval->nodeTab[j]; zval child; if (UNEXPECTED(node->type == XML_NAMESPACE_DECL)) { - xmlNodePtr nsparent = node->_private; xmlNsPtr original = (xmlNsPtr) node; /* Make sure parent dom object exists, so we can take an extra reference. */ zval parent_zval; /* don't destroy me, my lifetime is transferred to the fake namespace decl */ - php_dom_create_object(nsparent, &parent_zval, intern); + proxy_factory(node->_private, &parent_zval, intern, ctxt); dom_object *parent_intern = Z_DOMOBJ_P(&parent_zval); + xmlNodePtr parent = dom_object_get_node(parent_intern); - php_dom_create_fake_namespace_decl(nsparent, original, &child, parent_intern); + php_dom_create_fake_namespace_decl(parent, original, &child, parent_intern); } else { proxy_factory(node, &child, intern, ctxt); } diff --git a/ext/xsl/tests/gh22621.phpt b/ext/xsl/tests/gh22621.phpt new file mode 100644 index 000000000000..b31671eff630 --- /dev/null +++ b/ext/xsl/tests/gh22621.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-22621 (UAF via XSLTProcessor::registerPHPFunctions() retaining namespace nodes from an external document) +--EXTENSIONS-- +dom +xsl +--CREDITS-- +ExPatch-LLC +--FILE-- +secret'); +$uri = 'file:///' . ltrim(str_replace('\\', '/', $cDIR), '/') . '/gh22621_external.xml'; + +$stored_ns = null; + +function capture_ns($nodes) { + global $stored_ns; + foreach ($nodes as $node) { + if ($node instanceof DOMNameSpaceNode) { + $stored_ns = $node; + } + } + return "captured"; +} + +$xsl = new DOMDocument(); +$xsl->loadXML(<< + + + + + + + +XSL); + +$doc = new DOMDocument(); +$doc->loadXML(''); + +$proc = new XSLTProcessor(); +$proc->registerPHPFunctions(); +$proc->importStylesheet($xsl); +echo $proc->transformToXml($doc); + +var_dump($stored_ns->localName); +var_dump($stored_ns->namespaceURI); +var_dump($stored_ns->parentNode->localName); +var_dump($stored_ns->ownerDocument instanceof DOMDocument); +?> +--CLEAN-- + +--EXPECTF-- + +captured +string(6) "custom" +string(10) "urn:custom" +string(4) "root" +bool(true) From 6e2048025023d85f08064eb34ea2918eb5932e5c Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 11 Jul 2026 17:06:34 +0800 Subject: [PATCH 146/211] Doc: Rename ZSTR_INIT to ZSTR_INIT_LITERAL in documentation We don't have a macro named ZSTR_INIT. This should be ZSTR_INIT_LITERAL instead. --- docs/source/core/data-structures/zend_string.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst index 292028e43497..d341347277a4 100644 --- a/docs/source/core/data-structures/zend_string.rst +++ b/docs/source/core/data-structures/zend_string.rst @@ -70,7 +70,7 @@ new strings. - - Function/Macro [#persistent]_ - Description - - - ``ZSTR_INIT(s, p)`` + - - ``ZSTR_INIT_LITERAL(s, p)`` - Creates a new string from a string literal. - - ``zend_string_init(s, l, p)`` From 838a9c2ef8075650f330d7d788062f1421599f72 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 12 Jul 2026 00:11:52 +0800 Subject: [PATCH 147/211] Doc: Fix typo in zend_string.rst documentation --- docs/source/core/data-structures/zend_string.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst index d341347277a4..d6b20a49a74c 100644 --- a/docs/source/core/data-structures/zend_string.rst +++ b/docs/source/core/data-structures/zend_string.rst @@ -122,7 +122,7 @@ strings. - - ``ZSTR_HASH`` - ``Z_STRHASH[_P]`` - - Computes the string has if it hasn't already been, and returns it. + - Computes the string hash if it hasn't already been, and returns it. - - ``ZSTR_H`` - \- From 1fd15d425417e091f2ef1a479d7376a11c7126ff Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 12 Jul 2026 06:45:34 +0800 Subject: [PATCH 148/211] ext/soap: Fix SOAP xsd:hexBinary odd-length decoding (#22698) xsd:hexBinary values must contain an even number of hexadecimal digits. However, previously we allocated strlen(content) / 2 bytes and decoded only complete byte pairs. As a result, an odd-length value such as ABC was accepted as ab (what...?), silently ignoring the trailing nibble. Lets reject odd-length xsd:hexBinary values instead and throw an Error here which makes better sense. The error message is copied from other decoding errors. (I think in the future, we can make the decoding error message more useful, in bulk.) This is in master as a stricter parsing PR --- NEWS | 2 ++ ext/soap/php_encoding.c | 8 +++++- ext/soap/tests/hexbin_odd_length.phpt | 37 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/hexbin_odd_length.phpt diff --git a/NEWS b/NEWS index f40c6dfd8da6..190c6c16a935 100644 --- a/NEWS +++ b/NEWS @@ -78,6 +78,8 @@ PHP NEWS - SOAP: . Fixed bug GH-22585 (OOM on bailout with uninitialized do_request() parameters). (David Carlier) + . Fixed xsd:hexBinary decoding to reject odd-length values instead of + silently truncating the last nibble. (Weilin Du) - Standard: . Fixed sleep() and usleep() to reject values that overflow the underlying diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index e474798df6da..301cdd8588b4 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -766,6 +766,7 @@ static zval *to_zval_base64(zval *ret, encodeTypePtr type, xmlNodePtr data) static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) { zend_string *str; + size_t content_len; size_t i, j; unsigned char c; @@ -778,7 +779,12 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); return ret; } - str = zend_string_alloc(strlen((char*)data->children->content) / 2, 0); + content_len = strlen((char*) data->children->content); + if (content_len % 2 != 0) { + soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + return ret; + } + str = zend_string_alloc(content_len / 2, 0); for (i = j = 0; i < ZSTR_LEN(str); i++) { c = data->children->content[j++]; if (c >= '0' && c <= '9') { diff --git a/ext/soap/tests/hexbin_odd_length.phpt b/ext/soap/tests/hexbin_odd_length.phpt new file mode 100644 index 000000000000..bfe84ab643db --- /dev/null +++ b/ext/soap/tests/hexbin_odd_length.phpt @@ -0,0 +1,37 @@ +--TEST-- +SOAP rejects odd-length xsd:hexBinary values +--EXTENSIONS-- +soap +--FILE-- + + + + + ABC + + + +XML; + } +} + +$client = new TestSoapClient(null, [ + 'location' => 'test://', + 'uri' => 'urn:test', + 'exceptions' => true, +]); + +try { + var_dump(bin2hex($client->test())); +} catch (SoapFault $e) { + echo $e->faultstring, "\n"; +} +?> +--EXPECT-- +SOAP-ERROR: Encoding: Violation of encoding rules From 8ce99bc2cec1bb3ff47b5569d573151395bf1fc6 Mon Sep 17 00:00:00 2001 From: arshidkv12 Date: Sun, 12 Jul 2026 18:13:05 +0200 Subject: [PATCH 149/211] Fix GH-21720: macOS posix_spawn_file_actions_addchdir availability handling On Apple, select the addchdir variant by deployment target instead of the configure check: the link check passes whenever the SDK exports the symbol even if the running system is older, leaving a weakly linked reference that resolves to NULL at runtime and crashing proc_open() when $cwd is used. Closes GH-21722 --- ext/standard/proc_open.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 29744018a16d..beeb157e53a5 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -42,10 +42,20 @@ * to be really buggy. */ #include +#ifdef __APPLE__ +#include +#endif #define USE_POSIX_SPAWN -/* The non-_np variant is in macOS 26 (and _np deprecated) */ -#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR +/* The non-_np variant is in macOS 26 (and _np deprecated). On Apple, it has to be selected by the + * deployment target rather than the configure check: the link check passes whenever the SDK + * exports the symbol even if the running system is older, in which case the weakly linked + * reference resolves to NULL at runtime. */ +#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000 +#define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir +#elif defined(__APPLE__) +#define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir_np +#elif defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR) #define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir #else #define POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR posix_spawn_file_actions_addchdir_np From 93fa5f3c882e5b51342fa0674d25d5d29d7a0546 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Fri, 5 Jun 2026 15:51:50 +0100 Subject: [PATCH 150/211] ext/curl: add CURLOPT_SEEKFUNCTION --- NEWS | 6 ++ UPGRADING | 10 +++ ext/curl/curl.stub.php | 20 ++++++ ext/curl/curl_arginfo.h | 6 +- ext/curl/curl_private.h | 1 + ext/curl/interface.c | 60 ++++++++++++++++ ext/curl/tests/curl_copy_handle_seek.phpt | 44 ++++++++++++ ext/curl/tests/curl_seekfunction.phpt | 51 ++++++++++++++ ext/curl/tests/curl_seekfunction_error.phpt | 77 +++++++++++++++++++++ ext/curl/tests/curl_setopt_callables.phpt | 3 + ext/curl/tests/responder/get.inc | 5 ++ 11 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 ext/curl/tests/curl_copy_handle_seek.phpt create mode 100644 ext/curl/tests/curl_seekfunction.phpt create mode 100644 ext/curl/tests/curl_seekfunction_error.phpt diff --git a/NEWS b/NEWS index 190c6c16a935..4be8d1e554c5 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,12 @@ PHP NEWS . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with INT_MAX year). (arshidkv12) +- Curl: + . Added CURLOPT_SEEKFUNCTION and the CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL + and CURL_SEEKFUNC_CANTSEEK constants, letting libcurl rewind a streamed + request body to resend it on a redirect, multi-pass authentication or a + retried reused connection. (GrahamCampbell) + - Date: . Update timelib to 2022.17. (Derick) . Fixed bug GH-19803 (Parsing a string with a single white space does create diff --git a/UPGRADING b/UPGRADING index 031b6751d853..a809687d1961 100644 --- a/UPGRADING +++ b/UPGRADING @@ -238,6 +238,12 @@ PHP 8.6 UPGRADE NOTES This value can also be obtained by passing CURLINFO_SIZE_DELIVERED as the $option parameter. Requires libcurl 8.20.0 or later. + . Added CURLOPT_SEEKFUNCTION to register a callback that repositions a + streamed request body so libcurl can rewind and resend it on a redirect, + multi-pass authentication, or a retried reused connection instead of + failing with CURLE_SEND_FAIL_REWIND. The callback receives the CurlHandle, + offset and origin, and must return one of CURL_SEEKFUNC_OK, + CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK. - Fileinfo: . finfo_file() now works with remote streams. @@ -467,6 +473,10 @@ PHP 8.6 UPGRADE NOTES - Curl: . CURLINFO_SIZE_DELIVERED (libcurl >= 8.20.0). + . CURLOPT_SEEKFUNCTION. + . CURL_SEEKFUNC_OK. + . CURL_SEEKFUNC_FAIL. + . CURL_SEEKFUNC_CANTSEEK. - Sockets: . TCP_USER_TIMEOUT (Linux only). diff --git a/ext/curl/curl.stub.php b/ext/curl/curl.stub.php index d3bd58510e40..70e87cc9b146 100644 --- a/ext/curl/curl.stub.php +++ b/ext/curl/curl.stub.php @@ -343,6 +343,11 @@ * @cvalue CURLOPT_RETURNTRANSFER */ const CURLOPT_RETURNTRANSFER = UNKNOWN; +/** + * @var int + * @cvalue CURLOPT_SEEKFUNCTION + */ +const CURLOPT_SEEKFUNCTION = UNKNOWN; /** * @var int * @cvalue CURLOPT_SHARE @@ -1788,6 +1793,21 @@ * @cvalue CURL_READFUNC_PAUSE */ const CURL_READFUNC_PAUSE = UNKNOWN; +/** + * @var int + * @cvalue CURL_SEEKFUNC_OK + */ +const CURL_SEEKFUNC_OK = UNKNOWN; +/** + * @var int + * @cvalue CURL_SEEKFUNC_FAIL + */ +const CURL_SEEKFUNC_FAIL = UNKNOWN; +/** + * @var int + * @cvalue CURL_SEEKFUNC_CANTSEEK + */ +const CURL_SEEKFUNC_CANTSEEK = UNKNOWN; /** * @var int * @cvalue CURL_WRITEFUNC_PAUSE diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index 6a05b73572cb..f2929f60c4e2 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit curl.stub.php instead. - * Stub hash: 1ecf692ba8494e7b486986c0170686c33768deb5 */ + * Stub hash: d55adb230c533f4dde05e95759477dd9e1dd6efb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -294,6 +294,7 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURLOPT_REFERER", CURLOPT_REFERER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLOPT_RESUME_FROM", CURLOPT_RESUME_FROM, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLOPT_RETURNTRANSFER", CURLOPT_RETURNTRANSFER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURLOPT_SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLOPT_SHARE", CURLOPT_SHARE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLOPT_SSLCERT", CURLOPT_SSLCERT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLOPT_SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD, CONST_PERSISTENT); @@ -574,6 +575,9 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURLPAUSE_SEND", CURLPAUSE_SEND, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLPAUSE_SEND_CONT", CURLPAUSE_SEND_CONT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURL_READFUNC_PAUSE", CURL_READFUNC_PAUSE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SEEKFUNC_OK", CURL_SEEKFUNC_OK, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SEEKFUNC_FAIL", CURL_SEEKFUNC_FAIL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CURL_SEEKFUNC_CANTSEEK", CURL_SEEKFUNC_CANTSEEK, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURL_WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS4A", CURLPROXY_SOCKS4A, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CURLPROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5_HOSTNAME, CONST_PERSISTENT); diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h index 77b0628ee42a..25cc37b6e458 100644 --- a/ext/curl/curl_private.h +++ b/ext/curl/curl_private.h @@ -74,6 +74,7 @@ typedef struct { php_curl_write *write_header; php_curl_read *read; zval std_err; + zend_fcall_info_cache seek; zend_fcall_info_cache progress; zend_fcall_info_cache xferinfo; zend_fcall_info_cache fnmatch; diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 09c7009068b6..b7ec015abf62 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -456,6 +456,10 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n) zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream); } + if (ZEND_FCC_INITIALIZED(curl->handlers.seek)) { + zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.seek); + } + if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) { zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress); } @@ -831,6 +835,52 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx) } /* }}} */ +/* {{{ curl_seek */ +static int curl_seek(void *clientp, curl_off_t offset, int origin) +{ + php_curl *ch = (php_curl *)clientp; + int rval = CURL_SEEKFUNC_CANTSEEK; /* safe default if unset or the callback misbehaves */ + +#if PHP_CURL_DEBUG + fprintf(stderr, "curl_seek() called\n"); + fprintf(stderr, "clientp = %x, offset = %ld, origin = %d\n", clientp, offset, origin); +#endif + if (!ZEND_FCC_INITIALIZED(ch->handlers.seek)) { + return rval; + } + + zval args[3]; + zval retval; + + GC_ADDREF(&ch->std); + ZVAL_OBJ(&args[0], &ch->std); + ZVAL_LONG(&args[1], offset); + ZVAL_LONG(&args[2], origin); + + ch->in_callback = true; + zend_call_known_fcc(&ch->handlers.seek, &retval, /* param_count */ 3, args, /* named_params */ NULL); + ch->in_callback = false; + + if (!Z_ISUNDEF(retval)) { + _php_curl_verify_handlers(ch, /* reporterror */ true); + if (Z_TYPE(retval) == IS_LONG) { + zend_long retval_long = Z_LVAL(retval); + if (retval_long == CURL_SEEKFUNC_OK || retval_long == CURL_SEEKFUNC_FAIL || retval_long == CURL_SEEKFUNC_CANTSEEK) { + rval = retval_long; + } else { + zend_value_error("The CURLOPT_SEEKFUNCTION callback must return one of CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK"); + } + } else { + zend_type_error("The CURLOPT_SEEKFUNCTION callback must return one of CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK"); + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&args[0]); + return rval; +} +/* }}} */ + /* {{{ curl_write_header */ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx) { @@ -1038,6 +1088,7 @@ void init_curl_handle(php_curl *ch) ch->handlers.write = ecalloc(1, sizeof(php_curl_write)); ch->handlers.write_header = ecalloc(1, sizeof(php_curl_write)); ch->handlers.read = ecalloc(1, sizeof(php_curl_read)); + ch->handlers.seek = empty_fcall_info_cache; ch->handlers.progress = empty_fcall_info_cache; ch->handlers.xferinfo = empty_fcall_info_cache; ch->handlers.fnmatch = empty_fcall_info_cache; @@ -1208,6 +1259,7 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch); + php_curl_copy_fcc_with_option(ch, CURLOPT_SEEKDATA, &ch->handlers.seek, &source->handlers.seek); php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress); php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo); php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch); @@ -1577,6 +1629,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header, PHP_CURL_IGNORE); HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read, PHP_CURL_DIRECT); + HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SEEK, handlers.seek, curl_seek); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo); HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch); @@ -2794,6 +2847,9 @@ static void curl_free_obj(zend_object *object) efree(ch->handlers.write_header); efree(ch->handlers.read); + if (ZEND_FCC_INITIALIZED(ch->handlers.seek)) { + zend_fcc_dtor(&ch->handlers.seek); + } if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { zend_fcc_dtor(&ch->handlers.progress); } @@ -2878,6 +2934,10 @@ static void _php_curl_reset_handlers(php_curl *ch) ZVAL_UNDEF(&ch->handlers.std_err); } + if (ZEND_FCC_INITIALIZED(ch->handlers.seek)) { + zend_fcc_dtor(&ch->handlers.seek); + } + if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) { zend_fcc_dtor(&ch->handlers.progress); } diff --git a/ext/curl/tests/curl_copy_handle_seek.phpt b/ext/curl/tests/curl_copy_handle_seek.phpt new file mode 100644 index 000000000000..c2a2f37bbe13 --- /dev/null +++ b/ext/curl/tests/curl_copy_handle_seek.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test curl_copy_handle() with CURLOPT_SEEKFUNCTION +--EXTENSIONS-- +curl +--FILE-- + 0); +var_dump(str_contains($response, $body)); +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/ext/curl/tests/curl_seekfunction.phpt b/ext/curl/tests/curl_seekfunction.phpt new file mode 100644 index 000000000000..d2daa1e7d8dc --- /dev/null +++ b/ext/curl/tests/curl_seekfunction.phpt @@ -0,0 +1,51 @@ +--TEST-- +CURLOPT_SEEKFUNCTION is called to rewind a streamed upload across a redirect +--EXTENSIONS-- +curl +--FILE-- + 0); +var_dump(str_contains($response, $body)); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/curl/tests/curl_seekfunction_error.phpt b/ext/curl/tests/curl_seekfunction_error.phpt new file mode 100644 index 000000000000..134e8115dc5f --- /dev/null +++ b/ext/curl/tests/curl_seekfunction_error.phpt @@ -0,0 +1,77 @@ +--TEST-- +CURLOPT_SEEKFUNCTION callback error handling and option validation +--EXTENSIONS-- +curl +--FILE-- + 'not an int'); +} catch (\TypeError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\nReturning an out-of-range int:\n"; +try { + run_upload($host, fn($ch, $offset, $origin) => 42); +} catch (\ValueError $e) { + echo $e->getMessage(), "\n"; +} + +echo "\nThrowing from the callback:\n"; +try { + run_upload($host, function ($ch, $offset, $origin) { + throw new \RuntimeException('boom from seek'); + }); +} catch (\RuntimeException $e) { + echo $e->getMessage(), "\n"; +} + +echo "\nSetting the callback to null:\n"; +var_dump(curl_setopt(curl_init(), CURLOPT_SEEKFUNCTION, null)); + +echo "\nSetting a non-callable scalar:\n"; +try { + curl_setopt(curl_init(), CURLOPT_SEEKFUNCTION, 42); +} catch (\TypeError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Returning a non-int: +The CURLOPT_SEEKFUNCTION callback must return one of CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK + +Returning an out-of-range int: +The CURLOPT_SEEKFUNCTION callback must return one of CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK + +Throwing from the callback: +boom from seek + +Setting the callback to null: +bool(true) + +Setting a non-callable scalar: +curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_SEEKFUNCTION, no array or string given diff --git a/ext/curl/tests/curl_setopt_callables.phpt b/ext/curl/tests/curl_setopt_callables.phpt index aaa83102afac..9de8d6705710 100644 --- a/ext/curl/tests/curl_setopt_callables.phpt +++ b/ext/curl/tests/curl_setopt_callables.phpt @@ -27,6 +27,7 @@ testOption($ch, CURLOPT_FNMATCH_FUNCTION); testOption($ch, CURLOPT_WRITEFUNCTION); testOption($ch, CURLOPT_HEADERFUNCTION); testOption($ch, CURLOPT_READFUNCTION); +testOption($ch, CURLOPT_SEEKFUNCTION); ?> --EXPECT-- @@ -42,3 +43,5 @@ TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for opti TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_HEADERFUNCTION, function "undefined" not found or invalid function name TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_READFUNCTION, function "undefined" not found or invalid function name TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_READFUNCTION, function "undefined" not found or invalid function name +TypeError: curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_SEEKFUNCTION, function "undefined" not found or invalid function name +TypeError: curl_setopt_array(): Argument #2 ($options) must be a valid callback for option CURLOPT_SEEKFUNCTION, function "undefined" not found or invalid function name diff --git a/ext/curl/tests/responder/get.inc b/ext/curl/tests/responder/get.inc index c139c8c7d43a..2ef1e4a89dd3 100644 --- a/ext/curl/tests/responder/get.inc +++ b/ext/curl/tests/responder/get.inc @@ -46,6 +46,11 @@ case 'method': echo $_SERVER['REQUEST_METHOD']; break; + case 'redirect': + // A 307 preserves the method and body, so libcurl must rewind the upload + // (via CURLOPT_SEEKFUNCTION) before resending it to the new location. + header('Location: /get.inc?test=input', true, 307); + break; default: echo "Hello World!\n"; echo "Hello World!"; From f40c8f2b982143e1e3ec135ce3ebb6453fdd2113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 12 Jul 2026 19:50:36 +0200 Subject: [PATCH 151/211] Fix macOS compatibility of update-lexbor.sh [Tim: Extracted this as a new commit from GH-22699] --- ext/dom/lexbor/patches/update-lexbor.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/dom/lexbor/patches/update-lexbor.sh b/ext/dom/lexbor/patches/update-lexbor.sh index b5eff3846e11..6645f5d0e1e5 100755 --- a/ext/dom/lexbor/patches/update-lexbor.sh +++ b/ext/dom/lexbor/patches/update-lexbor.sh @@ -19,7 +19,10 @@ git clone "$LEXBOR_REPO" "$LEXBOR_TMP_DIR" (cd "$LEXBOR_TMP_DIR" && git checkout "$LEXBOR_REF") # Apply patches -mapfile -t patches < <(ls "$PATCHES_DIR"/*.patch) +patches=() +for f in "$PATCHES_DIR"/*.patch; do + [ -e "$f" ] && patches+=("$f") +done cd "$LEXBOR_TMP_DIR" for patch in "${patches[@]}"; do if ! git am -3 "$patch"; then From 2014eb2ccabf579a688f50d0216ca4fd7899b442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 12 Jul 2026 19:54:52 +0200 Subject: [PATCH 152/211] ext/uri: Fix GH-22629 WHATWG validation error incorrect with empty host and non-empty userinfo (#22699) The returned error code (LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS) apparently contradicts the specification: "If atSignSeen is true and buffer is the empty string, host-missing validation error, return failure." --- NEWS | 2 + ext/lexbor/lexbor/url/url.c | 2 +- ...nd-column-information-for-use-in-PHP.patch | 2 +- ...d-added-nodes-for-options-use-in-PHP.patch | 2 +- ...and-data-structure-to-be-able-to-gen.patch | 2 +- ...ve-unused-upper-case-tag-static-data.patch | 2 +- ...nk-size-of-static-binary-search-tree.patch | 2 +- ...0006-Patch-out-unused-CSS-style-code.patch | 2 +- ...07-URL-fixed-setters-for-empty-hosts.patch | 2 +- ...ialized-memory-in-the-path-buffer-gr.patch | 2 +- ...URL-containing-empty-host-and-userin.patch | 48 +++++++++++++++++++ .../whatwg/parsing/host_error_empty2.phpt | 2 +- 12 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch diff --git a/NEWS b/NEWS index ecd2c1267112..bf4e4d2bdb50 100644 --- a/NEWS +++ b/NEWS @@ -104,6 +104,8 @@ PHP NEWS - URI: . Fixed behavior of Uri\WhatWg\Url wither methods with regards to empty opaque hosts. (kocsismate) + . Fixed bug GH-22629 (WHATWG Validation error incorrect with empty host and + non-empty userinfo). (kocsismate) - Zip: . Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex() diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index b6c0a1e8f65e..19ec23829a23 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -1814,7 +1814,7 @@ lxb_url_parse_basic_h(lxb_url_parser_t *parser, lxb_url_t *url, if (at_sign) { if (begin == p || begin == p - 1) { status = lxb_url_log_append(parser, p, - LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS); + LXB_URL_ERROR_TYPE_HOST_MISSING); if (status != LXB_STATUS_OK) { lxb_url_parse_return(orig_data, buf, status); } diff --git a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch index 04136b29f58f..e106b413c22c 100644 --- a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch +++ b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:08:59 +0200 -Subject: [PATCH 1/8] Expose line and column information for use in PHP +Subject: [PATCH 1/9] Expose line and column information for use in PHP --- source/lexbor/dom/interfaces/node.h | 2 ++ diff --git a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch index 615655d7f2ec..9e83700198e1 100644 --- a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch +++ b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:18:51 +0200 -Subject: [PATCH 2/8] Track implied added nodes for options use in PHP +Subject: [PATCH 2/9] Track implied added nodes for options use in PHP --- source/lexbor/html/tree.h | 3 +++ diff --git a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch index 73c5afa19e12..9ed36fda109d 100644 --- a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch +++ b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 24 Aug 2023 22:57:48 +0200 -Subject: [PATCH 3/8] Patch utilities and data structure to be able to generate +Subject: [PATCH 3/9] Patch utilities and data structure to be able to generate smaller lookup tables Changed the generation script to check if everything fits in 32-bits. diff --git a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch index cc0a65a7cd5c..9e9fcaf8db7a 100644 --- a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch +++ b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:26:47 +0100 -Subject: [PATCH 4/8] Remove unused upper case tag static data +Subject: [PATCH 4/9] Remove unused upper case tag static data --- source/lexbor/tag/res.h | 2 ++ diff --git a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch index b84120bf6c8c..ce3448169395 100644 --- a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch +++ b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:29:31 +0100 -Subject: [PATCH 5/8] Shrink size of static binary search tree +Subject: [PATCH 5/9] Shrink size of static binary search tree This also makes it more efficient on the data cache. --- diff --git a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch index 196a5a8a62de..c8f485f34807 100644 --- a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch +++ b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:59:28 +0100 -Subject: [PATCH 6/8] Patch out unused CSS style code +Subject: [PATCH 6/9] Patch out unused CSS style code --- source/lexbor/css/rule.h | 2 ++ diff --git a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch index 2592372c6b02..56e206f63d2f 100644 --- a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch +++ b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Fri, 26 Jun 2026 18:55:56 +0300 -Subject: [PATCH 7/8] URL: fixed setters for empty hosts. +Subject: [PATCH 7/9] URL: fixed setters for empty hosts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch index 243053e87fa4..c8cd2332eeff 100644 --- a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch +++ b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Fri, 5 Jun 2026 22:13:32 +0300 -Subject: [PATCH 8/8] URL: fixed uninitialized memory in the path buffer +Subject: [PATCH 8/9] URL: fixed uninitialized memory in the path buffer growth. When a path was long enough to outgrow the on-stack buffer, the first diff --git a/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch new file mode 100644 index 000000000000..ef7743e61539 --- /dev/null +++ b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= +Date: Thu, 9 Jul 2026 21:51:05 +0200 +Subject: [PATCH 9/9] Fix parsing for URL containing empty host and userinfo + +The returned error code (LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS) apparently contradicts the specification: + +"If atSignSeen is true and buffer is the empty string, host-missing validation error, return failure." +--- + source/lexbor/url/url.c | 2 +- + test/files/lexbor/url/username_password.ton | 8 +++++++- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/source/lexbor/url/url.c b/source/lexbor/url/url.c +index b6c0a1e..19ec238 100644 +--- a/source/lexbor/url/url.c ++++ b/source/lexbor/url/url.c +@@ -1814,7 +1814,7 @@ again: + if (at_sign) { + if (begin == p || begin == p - 1) { + status = lxb_url_log_append(parser, p, +- LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS); ++ LXB_URL_ERROR_TYPE_HOST_MISSING); + if (status != LXB_STATUS_OK) { + lxb_url_parse_return(orig_data, buf, status); + } +diff --git a/test/files/lexbor/url/username_password.ton b/test/files/lexbor/url/username_password.ton +index 28a27fd..5a5e63e 100644 +--- a/test/files/lexbor/url/username_password.ton ++++ b/test/files/lexbor/url/username_password.ton +@@ -1,5 +1,5 @@ + [ +- /* Test count: 11 */ ++ /* Test count: 12 */ + /* 1 */ + { + "url": "https://user:password@lexbor.com", +@@ -124,4 +124,10 @@ + "failed": false, + "encoding": "utf-8" + } ++ /* 12 */ ++ { ++ "url": "https://user:pass@", ++ "failed": true, ++ "encoding": "utf-8" ++ } + ] diff --git a/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt b/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt index 934185455641..726241674fae 100644 --- a/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt +++ b/ext/uri/tests/whatwg/parsing/host_error_empty2.phpt @@ -11,4 +11,4 @@ try { ?> --EXPECT-- -Uri\WhatWg\InvalidUrlException: The specified URI is malformed +Uri\WhatWg\InvalidUrlException: The specified URI is malformed (HostMissing) From c6c8db5a0061c352a9b208d0c00124978afb96b5 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 12 Jul 2026 20:20:53 +0200 Subject: [PATCH 153/211] main/poll: Fix kqueue event buffer overflow in grouped mode (#22606) Cap each kevent() request at the number of entries that can still be delivered instead of requesting twice the buffer size. Events that are not retrieved (including oneshot and edge-triggered ones) stay pending in the kqueue and are delivered by the next wait, matching the epoll behavior. Merged read+write events can under-fill the buffer, so top up with zero timeout rounds, and base the suitable max events on the filter count so a default sized wait retrieves everything in one call. --- .../poll/poll_stream_sock_rw_max_events.phpt | 38 +++ .../poll/poll_stream_tcp_read_max_events.phpt | 45 +++ ...l_stream_tcp_read_one_shot_max_events.phpt | 46 +++ main/poll/poll_backend_kqueue.c | 278 ++++++++++-------- 4 files changed, 281 insertions(+), 126 deletions(-) create mode 100644 ext/standard/tests/poll/poll_stream_sock_rw_max_events.phpt create mode 100644 ext/standard/tests/poll/poll_stream_tcp_read_max_events.phpt create mode 100644 ext/standard/tests/poll/poll_stream_tcp_read_one_shot_max_events.phpt diff --git a/ext/standard/tests/poll/poll_stream_sock_rw_max_events.phpt b/ext/standard/tests/poll/poll_stream_sock_rw_max_events.phpt new file mode 100644 index 000000000000..d78d3f4fde35 --- /dev/null +++ b/ext/standard/tests/poll/poll_stream_sock_rw_max_events.phpt @@ -0,0 +1,38 @@ +--TEST-- +Poll stream - socket read/write events with maxEvents equal to the FD count +--FILE-- + [Io\Poll\Event::Read, Io\Poll\Event::Write], + 'data' => "sock$i", + 'read' => "test $i data", + ]; +} + +pt_expect_events($poll_ctx->wait(0, 100000, 8), $expected); + +// All read data was drained above, so only write events remain +$expected = []; +for ($i = 0; $i < 4; $i++) { + $expected[] = ['events' => [Io\Poll\Event::Write], 'data' => "sock$i"]; +} +pt_expect_events($poll_ctx->wait(0, 100000, 8), $expected); + +?> +--EXPECT-- +Events matched - count: 4 +Events matched - count: 4 diff --git a/ext/standard/tests/poll/poll_stream_tcp_read_max_events.phpt b/ext/standard/tests/poll/poll_stream_tcp_read_max_events.phpt new file mode 100644 index 000000000000..f927e73ed4d3 --- /dev/null +++ b/ext/standard/tests/poll/poll_stream_tcp_read_max_events.phpt @@ -0,0 +1,45 @@ +--TEST-- +Poll stream - TCP read with maxEvents smaller than the number of ready FDs +--FILE-- +wait(0, 100000, 4); + echo "Round $round count: " . count($watchers) . "\n"; + foreach ($watchers as $watcher) { + $data = $watcher->getData(); + if (isset($seen[$data])) { + echo "Duplicate event for $data\n"; + } + $seen[$data] = true; + // Drain so level triggering does not re-report the FD + fread($watcher->getHandle()->getStream(), 1024); + } +} + +ksort($seen); +echo "Seen: " . implode(',', array_keys($seen)) . "\n"; + +pt_expect_events($poll_ctx->wait(0), []); + +?> +--EXPECT-- +Round 1 count: 4 +Round 2 count: 4 +Seen: server0,server1,server2,server3,server4,server5,server6,server7 +Events matched - count: 0 diff --git a/ext/standard/tests/poll/poll_stream_tcp_read_one_shot_max_events.phpt b/ext/standard/tests/poll/poll_stream_tcp_read_one_shot_max_events.phpt new file mode 100644 index 000000000000..d7a2e48be6f4 --- /dev/null +++ b/ext/standard/tests/poll/poll_stream_tcp_read_one_shot_max_events.phpt @@ -0,0 +1,46 @@ +--TEST-- +Poll stream - TCP read oneshot with maxEvents smaller than the number of ready FDs +--FILE-- +wait(0, 100000, 4); + echo "Round $round count: " . count($watchers) . "\n"; + foreach ($watchers as $watcher) { + $data = $watcher->getData(); + if (isset($seen[$data])) { + echo "Duplicate event for $data\n"; + } + $seen[$data] = true; + } +} + +ksort($seen); +echo "Seen: " . implode(',', array_keys($seen)) . "\n"; + +// Every oneshot watcher fired once, so new data must not be reported +pt_write_sleep($clients[0], "more data"); +pt_expect_events($poll_ctx->wait(0, 100000), []); + +?> +--EXPECT-- +Round 1 count: 4 +Round 2 count: 4 +Seen: server0,server1,server2,server3,server4,server5,server6,server7 +Events matched - count: 0 diff --git a/main/poll/poll_backend_kqueue.c b/main/poll/poll_backend_kqueue.c index 00ba7abc9ee6..19ff0ad6d228 100644 --- a/main/poll/poll_backend_kqueue.c +++ b/main/poll/poll_backend_kqueue.c @@ -303,149 +303,179 @@ static int kqueue_backend_wait( { kqueue_backend_data_t *backend_data = (kqueue_backend_data_t *) ctx->backend_data; - /* For raw events, we need capacity for max_events. - * For grouped events, kqueue can return up to 2 events per FD, so we need 2x capacity. */ - int required_capacity = ctx->raw_events ? max_events : (max_events * 2); - if (required_capacity > backend_data->events_capacity) { + /* Cap every kevent() request at the entries we can still deliver, like epoll's maxevents. + * Unretrieved events stay pending in the kqueue (EV_ONESHOT deletes and EV_CLEAR resets only + * at retrieval), so nothing is lost and the caller's buffer cannot overflow. */ + if (max_events > backend_data->events_capacity) { struct kevent *new_events = php_poll_realloc( - backend_data->events, required_capacity * sizeof(struct kevent), ctx->persistent); + backend_data->events, max_events * sizeof(struct kevent), ctx->persistent); if (!new_events) { php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM); return -1; } backend_data->events = new_events; - backend_data->events_capacity = required_capacity; + backend_data->events_capacity = max_events; } - int nfds = kevent( - backend_data->kqueue_fd, NULL, 0, backend_data->events, required_capacity, timeout); + if (ctx->raw_events) { + /* Raw events mode - direct 1:1 mapping, no grouping */ + int nfds = kevent( + backend_data->kqueue_fd, NULL, 0, backend_data->events, max_events, timeout); - if (nfds < 0) { - php_poll_set_current_errno_error(ctx); - return -1; + if (nfds < 0) { + php_poll_set_current_errno_error(ctx); + return -1; + } + + for (int i = 0; i < nfds; i++) { + events[i].fd = (int) backend_data->events[i].ident; + events[i].events = 0; + events[i].revents = 0; + events[i].data = backend_data->events[i].udata; + + /* Convert kqueue filter to poll event */ + if (backend_data->events[i].filter == EVFILT_READ) { + events[i].revents |= PHP_POLL_READ; + } else if (backend_data->events[i].filter == EVFILT_WRITE) { + events[i].revents |= PHP_POLL_WRITE; + } + + /* Convert kqueue flags to poll events */ + if (backend_data->events[i].flags & EV_EOF) { + events[i].revents |= PHP_POLL_HUP; + } + if (backend_data->events[i].flags & EV_ERROR) { + events[i].revents |= PHP_POLL_ERROR; + } + } + + return nfds; } - if (nfds > 0) { - if (ctx->raw_events) { - /* Raw events mode - direct 1:1 mapping, no grouping */ - for (int i = 0; i < nfds && i < max_events; i++) { - events[i].fd = (int) backend_data->events[i].ident; - events[i].events = 0; - events[i].revents = 0; - events[i].data = backend_data->events[i].udata; - - /* Convert kqueue filter to poll event */ - if (backend_data->events[i].filter == EVFILT_READ) { - events[i].revents |= PHP_POLL_READ; - } else if (backend_data->events[i].filter == EVFILT_WRITE) { - events[i].revents |= PHP_POLL_WRITE; - } + /* Grouped events mode with improved oneshot tracking. + * + * kqueue reports one event per (FD, filter) pair, so a READ+WRITE pair merges into a single + * entry and one round can fill fewer than max_events entries while more events are pending. + * Top up with zero-timeout rounds, each requesting only the remaining free entries. Both + * FreeBSD and XNU re-enqueue delivered level-triggered events at the tail of the active queue, + * so top-up rounds see not-yet-delivered events first, matching epoll's round-robin fill. */ + int unique_events = 0, garbage_events = 0; + const struct timespec zero_timeout = {0, 0}; + const struct timespec *round_timeout = timeout; + + while (true) { + int slots = max_events - unique_events; + int nfds = kevent( + backend_data->kqueue_fd, NULL, 0, backend_data->events, slots, round_timeout); + + if (nfds < 0) { + if (unique_events > 0) { + /* Deliver what was already gathered */ + break; + } + php_poll_set_current_errno_error(ctx); + return -1; + } - /* Convert kqueue flags to poll events */ - if (backend_data->events[i].flags & EV_EOF) { - events[i].revents |= PHP_POLL_HUP; - } - if (backend_data->events[i].flags & EV_ERROR) { - events[i].revents |= PHP_POLL_ERROR; - } + int new_unique_events = 0; + + for (int i = 0; i < nfds; i++) { + int fd = (int) backend_data->events[i].ident; + uint32_t revents = 0; + void *data = backend_data->events[i].udata; + bool is_oneshot = (backend_data->events[i].flags & EV_ONESHOT) != 0; + + /* Convert this event */ + if (backend_data->events[i].filter == EVFILT_READ) { + revents |= PHP_POLL_READ; + } else if (backend_data->events[i].filter == EVFILT_WRITE) { + revents |= PHP_POLL_WRITE; } - return nfds > max_events ? max_events : nfds; - } else { - /* Grouped events mode with improved oneshot tracking */ - int unique_events = 0, garbage_events = 0, fd; - - for (int i = 0; i < nfds; i++) { - fd = (int) backend_data->events[i].ident; - uint32_t revents = 0; - void *data = backend_data->events[i].udata; - bool is_oneshot = (backend_data->events[i].flags & EV_ONESHOT) != 0; - - /* Convert this event */ - if (backend_data->events[i].filter == EVFILT_READ) { - revents |= PHP_POLL_READ; - } else if (backend_data->events[i].filter == EVFILT_WRITE) { - revents |= PHP_POLL_WRITE; - } - if (backend_data->events[i].flags & EV_EOF) { - revents |= PHP_POLL_HUP; - } - if (backend_data->events[i].flags & EV_ERROR) { - revents |= PHP_POLL_ERROR; - } + if (backend_data->events[i].flags & EV_EOF) { + revents |= PHP_POLL_HUP; + } + if (backend_data->events[i].flags & EV_ERROR) { + revents |= PHP_POLL_ERROR; + } - /* Look for existing event for this FD */ - bool found = false; - for (int j = 0; j < unique_events; j++) { - if (events[j].fd == fd) { - /* Combine with existing event */ - events[j].revents |= revents; - found = true; - break; - } + /* Look for existing event for this FD */ + bool found = false; + for (int j = 0; j < unique_events; j++) { + if (events[j].fd == fd) { + /* Combine with existing event */ + events[j].revents |= revents; + found = true; + break; } + } - if (!found) { - /* New FD, create new event */ - ZEND_ASSERT(unique_events < max_events); - events[unique_events].fd = fd; - events[unique_events].events = 0; - events[unique_events].revents = revents; - events[unique_events].data = data; - unique_events++; - - /* Handle oneshot tracking */ - if (is_oneshot) { - zval *tracking = zend_hash_index_find(backend_data->fd_tracking, fd); - if (tracking && (Z_LVAL_P(tracking) & KQUEUE_FD_ONESHOT_COMPLETE)) { - /* Mark which filter fired for garbage collection */ - zend_long flags = Z_LVAL_P(tracking); - flags &= ~KQUEUE_FD_ONESHOT_COMPLETE; /* Clear complete flag */ - if (revents & PHP_POLL_READ) { - flags |= KQUEUE_FD_GARBAGE_READ; /* Need to clean write */ - } - if (revents & PHP_POLL_WRITE) { - flags |= KQUEUE_FD_GARBAGE_WRITE; /* Need to clean read */ - } - ZVAL_LONG(tracking, flags); - backend_data->fd_count--; - garbage_events++; - } - } - } else if (is_oneshot) { - /* Second filter for same FD fired - clear garbage flags */ + if (!found) { + /* New FD, create new event */ + ZEND_ASSERT(unique_events < max_events); + events[unique_events].fd = fd; + events[unique_events].events = 0; + events[unique_events].revents = revents; + events[unique_events].data = data; + unique_events++; + new_unique_events++; + + /* Handle oneshot tracking */ + if (is_oneshot) { zval *tracking = zend_hash_index_find(backend_data->fd_tracking, fd); - if (tracking) { - /* Remove FD from tracking as it gets deleted from kqueue as well */ - zend_hash_index_del(backend_data->fd_tracking, fd); - garbage_events--; + if (tracking && (Z_LVAL_P(tracking) & KQUEUE_FD_ONESHOT_COMPLETE)) { + /* Mark which filter fired for garbage collection */ + zend_long flags = Z_LVAL_P(tracking); + flags &= ~KQUEUE_FD_ONESHOT_COMPLETE; /* Clear complete flag */ + if (revents & PHP_POLL_READ) { + flags |= KQUEUE_FD_GARBAGE_READ; /* Need to clean write */ + } + if (revents & PHP_POLL_WRITE) { + flags |= KQUEUE_FD_GARBAGE_WRITE; /* Need to clean read */ + } + ZVAL_LONG(tracking, flags); + backend_data->fd_count--; + garbage_events++; } } - } - - if (garbage_events > 0) { - /* Clean up orphaned filters from complete oneshot FDs */ - struct kevent cleanup_change; - ZEND_HASH_FOREACH_NUM_KEY_VAL(backend_data->fd_tracking, zend_ulong fd_key, zval *tracking) - { - zend_long flags = Z_LVAL_P(tracking); - if (flags & KQUEUE_FD_HAS_GARBAGE) { - int filter = (flags & KQUEUE_FD_GARBAGE_READ) ? EVFILT_WRITE : EVFILT_READ; - EV_SET(&cleanup_change, fd_key, filter, EV_DELETE, 0, 0, NULL); - kevent(backend_data->kqueue_fd, &cleanup_change, 1, NULL, 0, NULL); - - /* Remove FD from tracking after cleanup */ - zend_hash_index_del(backend_data->fd_tracking, fd_key); - } + } else if (is_oneshot) { + /* Second filter for same FD fired - clear garbage flags */ + zval *tracking = zend_hash_index_find(backend_data->fd_tracking, fd); + if (tracking) { + /* Remove FD from tracking as it gets deleted from kqueue as well */ + zend_hash_index_del(backend_data->fd_tracking, fd); + garbage_events--; } - ZEND_HASH_FOREACH_END(); } + } - return unique_events; + /* Stop on a partial round (drained or timed out), a full buffer, or a full round with no + * new FDs (kernel re-reported level-triggered events already merged above - don't spin) */ + if (nfds < slots || unique_events == max_events || new_unique_events == 0) { + break; } + round_timeout = &zero_timeout; } - return nfds; + if (garbage_events > 0) { + /* Clean up orphaned filters from complete oneshot FDs */ + struct kevent cleanup_change; + ZEND_HASH_FOREACH_NUM_KEY_VAL(backend_data->fd_tracking, zend_ulong fd_key, zval *tracking) + { + zend_long flags = Z_LVAL_P(tracking); + if (flags & KQUEUE_FD_HAS_GARBAGE) { + int filter = (flags & KQUEUE_FD_GARBAGE_READ) ? EVFILT_WRITE : EVFILT_READ; + EV_SET(&cleanup_change, fd_key, filter, EV_DELETE, 0, 0, NULL); + kevent(backend_data->kqueue_fd, &cleanup_change, 1, NULL, 0, NULL); + + /* Remove FD from tracking after cleanup */ + zend_hash_index_del(backend_data->fd_tracking, fd_key); + } + } + ZEND_HASH_FOREACH_END(); + } + + return unique_events; } static bool kqueue_backend_is_available(void) @@ -466,15 +496,11 @@ static int kqueue_backend_get_suitable_max_events(php_poll_ctx *ctx) return -1; } - if (ctx->raw_events) { - /* For raw events, return the total number of filters */ - int active_filters = backend_data->filter_count; - return active_filters == 0 ? 1 : active_filters; - } else { - /* For grouped events, return the number of unique FDs */ - int active_fds = backend_data->fd_count; - return active_fds == 0 ? 1 : active_fds; - } + /* kqueue reports one event per filter even in grouped mode (events are merged into + * per-FD entries afterwards) and the wait request is capped at max_events, so size + * for the total filter count to allow retrieving all pending events in one call. */ + int active_filters = backend_data->filter_count; + return active_filters == 0 ? 1 : active_filters; } const php_poll_backend_ops php_poll_backend_kqueue_ops = { From c025bc47dbbd47c687d06e11c622d32e21f283e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 12 Jul 2026 20:50:11 +0200 Subject: [PATCH 154/211] ext/uri: Fix GH-22628 Percent-encoding of caret in WHATWG URL paths is not performed (#22700) The caret (^) is part of the path percent-encode set: "The path percent-encode set is a percent-encode set consisting of the query percent-encode set and U+003F (?), U+005E (^), U+0060 (`), U+007B ({), and U+007D (})." Until now, this character wasn't percent-encoded in the path likely due to a copy-paste error. This mistake is fixed by adding LXB_URL_MAP_PATH to the lxb_url_map entry for the caret. --- NEWS | 2 ++ ext/lexbor/lexbor/url/url.c | 2 +- ...nd-column-information-for-use-in-PHP.patch | 2 +- ...d-added-nodes-for-options-use-in-PHP.patch | 2 +- ...and-data-structure-to-be-able-to-gen.patch | 4 +-- ...ve-unused-upper-case-tag-static-data.patch | 2 +- ...nk-size-of-static-binary-search-tree.patch | 2 +- ...0006-Patch-out-unused-CSS-style-code.patch | 2 +- ...07-URL-fixed-setters-for-empty-hosts.patch | 2 +- ...ialized-memory-in-the-path-buffer-gr.patch | 2 +- ...URL-containing-empty-host-and-userin.patch | 2 +- ...Percent-encode-the-caret-in-the-path.patch | 29 +++++++++++++++++++ .../path_success_auto_encoded.phpt | 4 +-- .../path_success_percent_encode_set2.phpt | 4 +-- 14 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch diff --git a/NEWS b/NEWS index bf4e4d2bdb50..a4f1a24223ec 100644 --- a/NEWS +++ b/NEWS @@ -104,6 +104,8 @@ PHP NEWS - URI: . Fixed behavior of Uri\WhatWg\Url wither methods with regards to empty opaque hosts. (kocsismate) + . Fixed bug GH-22628 (Percent-encoding of caret in WHATWG URL paths is not + performed). (kocsismate) . Fixed bug GH-22629 (WHATWG Validation error incorrect with empty host and non-empty userinfo). (kocsismate) diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 19ec23829a23..de19239936a1 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -159,7 +159,7 @@ static const uint8_t lxb_url_map[256] = LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5b ([) */ LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5c (\) */ LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5d (]) */ - LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */ + LXB_URL_MAP_USERINFO|LXB_URL_MAP_PATH|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */ LXB_URL_MAP_UNDEF, /* 0x5f (_) */ LXB_URL_MAP_PATH|LXB_URL_MAP_FRAGMENT|LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x60 (`) */ LXB_URL_MAP_UNDEF, /* 0x61 (a) */ diff --git a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch index e106b413c22c..533598837822 100644 --- a/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch +++ b/ext/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:08:59 +0200 -Subject: [PATCH 1/9] Expose line and column information for use in PHP +Subject: [PATCH 01/10] Expose line and column information for use in PHP --- source/lexbor/dom/interfaces/node.h | 2 ++ diff --git a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch index 9e83700198e1..8814d5955354 100644 --- a/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch +++ b/ext/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:18:51 +0200 -Subject: [PATCH 2/9] Track implied added nodes for options use in PHP +Subject: [PATCH 02/10] Track implied added nodes for options use in PHP --- source/lexbor/html/tree.h | 3 +++ diff --git a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch index 9ed36fda109d..aa4802320491 100644 --- a/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch +++ b/ext/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch @@ -1,8 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 24 Aug 2023 22:57:48 +0200 -Subject: [PATCH 3/9] Patch utilities and data structure to be able to generate - smaller lookup tables +Subject: [PATCH 03/10] Patch utilities and data structure to be able to + generate smaller lookup tables Changed the generation script to check if everything fits in 32-bits. And change the actual field types to 32-bits. This decreases the hash diff --git a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch index 9e9fcaf8db7a..1a28b21ccdc5 100644 --- a/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch +++ b/ext/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:26:47 +0100 -Subject: [PATCH 4/9] Remove unused upper case tag static data +Subject: [PATCH 04/10] Remove unused upper case tag static data --- source/lexbor/tag/res.h | 2 ++ diff --git a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch index ce3448169395..a1dda1fcd112 100644 --- a/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch +++ b/ext/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:29:31 +0100 -Subject: [PATCH 5/9] Shrink size of static binary search tree +Subject: [PATCH 05/10] Shrink size of static binary search tree This also makes it more efficient on the data cache. --- diff --git a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch index c8f485f34807..57f1e0e92fcb 100644 --- a/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch +++ b/ext/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:59:28 +0100 -Subject: [PATCH 6/9] Patch out unused CSS style code +Subject: [PATCH 06/10] Patch out unused CSS style code --- source/lexbor/css/rule.h | 2 ++ diff --git a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch index 56e206f63d2f..b55f5aac5894 100644 --- a/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch +++ b/ext/lexbor/patches/0007-URL-fixed-setters-for-empty-hosts.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Fri, 26 Jun 2026 18:55:56 +0300 -Subject: [PATCH 7/9] URL: fixed setters for empty hosts. +Subject: [PATCH 07/10] URL: fixed setters for empty hosts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch index c8cd2332eeff..d967c8ca4eb9 100644 --- a/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch +++ b/ext/lexbor/patches/0008-URL-fixed-uninitialized-memory-in-the-path-buffer-gr.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Fri, 5 Jun 2026 22:13:32 +0300 -Subject: [PATCH 8/9] URL: fixed uninitialized memory in the path buffer +Subject: [PATCH 08/10] URL: fixed uninitialized memory in the path buffer growth. When a path was long enough to outgrow the on-stack buffer, the first diff --git a/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch index ef7743e61539..1e7aa4d1087d 100644 --- a/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch +++ b/ext/lexbor/patches/0009-Fix-parsing-for-URL-containing-empty-host-and-userin.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 9 Jul 2026 21:51:05 +0200 -Subject: [PATCH 9/9] Fix parsing for URL containing empty host and userinfo +Subject: [PATCH 09/10] Fix parsing for URL containing empty host and userinfo The returned error code (LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS) apparently contradicts the specification: diff --git a/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch b/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch new file mode 100644 index 000000000000..04d91bda68a9 --- /dev/null +++ b/ext/lexbor/patches/0010-Percent-encode-the-caret-in-the-path.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= +Date: Fri, 10 Jul 2026 22:31:16 +0200 +Subject: [PATCH 10/10] Percent-encode the caret in the path + +The caret (^) is part of the path percent-encode set: + +"The path percent-encode set is a percent-encode set consisting of the query percent-encode set and U+003F (?), U+005E (^), U+0060 (`), U+007B ({), and U+007D (})." + +Until now, this character wasn't percent-encoded in the path likely due to a copy-paste error. This is mistake is fixed by adding LXB_URL_MAP_PATH to the lxb_url_map entry for the caret. + +Originally reported at https://github.com/php/php-src/issues/22628 +--- + source/lexbor/url/url.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/source/lexbor/url/url.c b/source/lexbor/url/url.c +index 19ec238..de19239 100644 +--- a/source/lexbor/url/url.c ++++ b/source/lexbor/url/url.c +@@ -159,7 +159,7 @@ static const uint8_t lxb_url_map[256] = + LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5b ([) */ + LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5c (\) */ + LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5d (]) */ +- LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */ ++ LXB_URL_MAP_USERINFO|LXB_URL_MAP_PATH|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x5e (^) */ + LXB_URL_MAP_UNDEF, /* 0x5f (_) */ + LXB_URL_MAP_PATH|LXB_URL_MAP_FRAGMENT|LXB_URL_MAP_USERINFO|LXB_URL_MAP_COMPONENT|LXB_URL_MAP_X_WWW_FORM, /* 0x60 (`) */ + LXB_URL_MAP_UNDEF, /* 0x61 (a) */ diff --git a/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt index 59f539124c68..8a168b928820 100644 --- a/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt +++ b/ext/uri/tests/whatwg/modification/path_success_auto_encoded.phpt @@ -15,5 +15,5 @@ var_dump($url2->toAsciiString()); ?> --EXPECT-- string(1) "/" -string(8) "/p^th%23" -string(27) "https://example.com/p^th%23" +string(10) "/p%5Eth%23" +string(29) "https://example.com/p%5Eth%23" diff --git a/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt index 9419c817fbbd..dd82beeff101 100644 --- a/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt +++ b/ext/uri/tests/whatwg/parsing/path_success_percent_encode_set2.phpt @@ -22,10 +22,10 @@ object(Uri\WhatWg\Url)#%d (%d) { ["port"]=> NULL ["path"]=> - string(28) "/foo%22/%3Cbar%3E/^%7Bbaz%7D" + string(30) "/foo%22/%3Cbar%3E/%5E%7Bbaz%7D" ["query"]=> NULL ["fragment"]=> NULL } -string(47) "https://example.com/foo%22/%3Cbar%3E/^%7Bbaz%7D" +string(49) "https://example.com/foo%22/%3Cbar%3E/%5E%7Bbaz%7D" From 2a712fb55775da4cdee2334c15ad391ff8852b20 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 12 Jul 2026 21:02:45 +0200 Subject: [PATCH 155/211] Fix flaky stream_socket_get_crypto_status handshake test (#22707) The test could intermittently fail because a non-blocking TLS handshake can complete in a single stream_socket_enable_crypto() call, so WANT_READ/WANT_WRITE was never observed and $sawWant stayed false. Route the connection through a plain TCP proxy that forwards the server handshake flight in fragments, forcing the client to see a partial TLS record and report WANT_READ at least once. --- ...am_socket_get_crypto_status_handshake.phpt | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt b/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt index 2a1c554a79d0..7e0f660c980d 100644 --- a/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt +++ b/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt @@ -27,6 +27,46 @@ $serverCode = <<<'CODE' CODE; $serverCode = sprintf($serverCode, $certFile); +/* Plain TCP proxy that forwards the server handshake flight in fragments, so the client's + * non-blocking handshake sees a partial TLS record and reports WANT_READ instead of completing + * in a single call (which can otherwise happen depending on timing). */ +$proxyCode = <<<'CODE' + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT); + stream_set_blocking($upstream, false); + + $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; + $server = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr, $flags); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server); + stream_set_blocking($conn, false); + + $read = [$upstream, $conn]; + while (stream_select($read, $write, $except, 1)) { + foreach ($read as $fp) { + $data = stream_get_contents($fp); + if ($data === '') { + continue; + } + if ($fp === $conn) { + fwrite($upstream, $data); + } else { + /* Fragment server -> client to force a partial TLS record. */ + foreach (str_split($data, (int) ceil(strlen($data) / 3)) as $part) { + fwrite($conn, $part); + usleep(50000); + } + } + } + if (feof($upstream) || feof($conn)) { + break; + } + $read = [$upstream, $conn]; + } + + phpt_wait(); +CODE; + /* Client connects over plain TCP, then completes the TLS handshake in non-blocking mode, using * the reported crypto status to select the right direction to wait on. */ $clientCode = <<<'CODE' @@ -73,7 +113,8 @@ $clientCode = <<<'CODE' stream_set_blocking($client, true); echo trim(fgets($client)), "\n"; - phpt_notify(); + phpt_notify('server'); + phpt_notify('proxy'); fclose($client); CODE; $clientCode = sprintf($clientCode, $peerName); @@ -83,7 +124,10 @@ $certificateGenerator = new CertificateGenerator(); $certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); include 'ServerClientTestCase.inc'; -ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +ServerClientTestCase::getInstance()->run($clientCode, [ + 'server' => $serverCode, + 'proxy' => $proxyCode, +]); ?> --CLEAN-- Date: Sun, 12 Jul 2026 21:05:09 +0200 Subject: [PATCH 156/211] json: Report unterminated JSON strings as syntax errors (#22528) Fixes php/php-src#22527. --- NEWS | 3 + ext/json/json_scanner.re | 9 ++- ext/json/tests/gh22527.phpt | 31 +++++++ ...son_last_error_msg_error_location_001.phpt | 5 +- ...son_last_error_msg_error_location_002.phpt | 41 +++++----- ...son_last_error_msg_error_location_004.phpt | 13 ++- ...son_last_error_msg_error_location_005.phpt | 41 +++++----- ...son_last_error_msg_error_location_006.phpt | 5 +- ...son_last_error_msg_error_location_007.phpt | 5 +- ...son_last_error_msg_error_location_008.phpt | 81 +++++++++---------- ...son_last_error_msg_error_location_009.phpt | 21 +++-- ...son_last_error_msg_error_location_010.phpt | 17 ++-- 12 files changed, 152 insertions(+), 120 deletions(-) create mode 100644 ext/json/tests/gh22527.phpt diff --git a/NEWS b/NEWS index 48b13b5bc90a..0459373092ff 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,9 @@ PHP NEWS IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError when the offset argument is not of type int. (Weilin Du) +- JSON: + . Report unterminated JSON strings as syntax errors. (timwolla) + - Opcache: . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache preloaded trait). (iliaal) diff --git a/ext/json/json_scanner.re b/ext/json/json_scanner.re index e4d25009132a..0c64a6423baf 100644 --- a/ext/json/json_scanner.re +++ b/ext/json/json_scanner.re @@ -262,7 +262,14 @@ std: s->errcode = PHP_JSON_ERROR_UTF8; return PHP_JSON_T_ERROR; } - + EOI { + if (s->limit < s->cursor) { + s->errcode = PHP_JSON_ERROR_SYNTAX; + } else { + s->errcode = PHP_JSON_ERROR_CTRL_CHAR; + } + return PHP_JSON_T_ERROR; + } CTRL { s->errcode = PHP_JSON_ERROR_CTRL_CHAR; return PHP_JSON_T_ERROR; diff --git a/ext/json/tests/gh22527.phpt b/ext/json/tests/gh22527.phpt new file mode 100644 index 000000000000..cb0dd982d50b --- /dev/null +++ b/ext/json/tests/gh22527.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-22527: Unterminated JSON strings are misleadingly reported as “Control character error†+--FILE-- + +--EXPECT-- +NULL +int(4) +string(30) "Syntax error near location 1:1" +NULL +int(4) +string(30) "Syntax error near location 1:1" +NULL +int(4) +string(30) "Syntax error near location 1:9" +NULL +int(3) +string(71) "Control character error, possibly incorrectly encoded near location 1:1" diff --git a/ext/json/tests/json_last_error_msg_error_location_001.phpt b/ext/json/tests/json_last_error_msg_error_location_001.phpt index e0553f9f7d65..72cb1c42f23f 100644 --- a/ext/json/tests/json_last_error_msg_error_location_001.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_001.phpt @@ -66,8 +66,8 @@ string(30) "Syntax error near location 1:1" Error at position 1:10: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error at position 1:9: bool(false) @@ -118,4 +118,3 @@ Error at position 1:10: bool(false) int(3) string(72) "Control character error, possibly incorrectly encoded near location 1:10" - diff --git a/ext/json/tests/json_last_error_msg_error_location_002.phpt b/ext/json/tests/json_last_error_msg_error_location_002.phpt index df7fc981ccba..31438255b31a 100644 --- a/ext/json/tests/json_last_error_msg_error_location_002.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_002.phpt @@ -53,51 +53,50 @@ Testing error locations with Unicode UTF-8 characters Error after Japanese characters: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:12" +int(4) +string(31) "Syntax error near location 1:12" Error after Russian characters: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error after Chinese characters: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:8" +int(4) +string(30) "Syntax error near location 1:8" Error after Arabic characters: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error after Emoji: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" +int(4) +string(31) "Syntax error near location 1:11" Error in mixed ASCII and UTF-8: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:27" +int(4) +string(31) "Syntax error near location 1:27" Error with UTF-8 escaped sequences: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error in object with multiple UTF-8 keys: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:22" +int(4) +string(31) "Syntax error near location 1:22" Error in array with UTF-8 strings: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:18" +int(4) +string(31) "Syntax error near location 1:18" Error in nested object with UTF-8: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:15" - +int(4) +string(31) "Syntax error near location 1:15" diff --git a/ext/json/tests/json_last_error_msg_error_location_004.phpt b/ext/json/tests/json_last_error_msg_error_location_004.phpt index 165449600fb3..fcde3faa38a3 100644 --- a/ext/json/tests/json_last_error_msg_error_location_004.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_004.phpt @@ -53,8 +53,8 @@ Testing error locations in deeply nested structures Error in deeply nested object: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:31" +int(4) +string(31) "Syntax error near location 1:31" Error in deeply nested array: bool(true) @@ -78,16 +78,15 @@ string(31) "Syntax error near location 1:21" Error in complex structure: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:93" +int(4) +string(31) "Syntax error near location 1:93" Error in array of objects: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:68" +int(4) +string(31) "Syntax error near location 1:68" Error in object with array values: bool(false) int(2) string(61) "State mismatch (invalid or malformed JSON) near location 1:82" - diff --git a/ext/json/tests/json_last_error_msg_error_location_005.phpt b/ext/json/tests/json_last_error_msg_error_location_005.phpt index d12ce387e73e..ee1800eb8273 100644 --- a/ext/json/tests/json_last_error_msg_error_location_005.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_005.phpt @@ -53,51 +53,50 @@ Testing error locations with UTF-16 surrogate pairs and escape sequences Error after UTF-16 escaped emoji: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" +int(4) +string(31) "Syntax error near location 1:11" Error after multiple UTF-16 pairs: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with mixed UTF-8 and UTF-16: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" +int(4) +string(31) "Syntax error near location 1:11" Error with UTF-16 in key: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error with multiple UTF-16 keys: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:22" +int(4) +string(31) "Syntax error near location 1:22" Error with BMP characters: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with supplementary plane: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" +int(4) +string(31) "Syntax error near location 1:11" Error in array with UTF-16: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:12" +int(4) +string(31) "Syntax error near location 1:12" Error in nested structure with UTF-16: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:18" +int(4) +string(31) "Syntax error near location 1:18" Error with UTF-16 and control chars: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" - +int(4) +string(31) "Syntax error near location 1:10" diff --git a/ext/json/tests/json_last_error_msg_error_location_006.phpt b/ext/json/tests/json_last_error_msg_error_location_006.phpt index e6aab1af8f27..4a6c221c8f92 100644 --- a/ext/json/tests/json_last_error_msg_error_location_006.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_006.phpt @@ -107,8 +107,8 @@ string(33) "Syntax error near location 1:1011" Error with very long key: bool(false) -int(3) -string(73) "Control character error, possibly incorrectly encoded near location 1:506" +int(4) +string(32) "Syntax error near location 1:506" Error after empty object: bool(false) @@ -149,4 +149,3 @@ Error with mixed whitespace: bool(false) int(3) string(71) "Control character error, possibly incorrectly encoded near location 3:2" - diff --git a/ext/json/tests/json_last_error_msg_error_location_007.phpt b/ext/json/tests/json_last_error_msg_error_location_007.phpt index 0e24889bbbbe..168afc4dbfe2 100644 --- a/ext/json/tests/json_last_error_msg_error_location_007.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_007.phpt @@ -118,8 +118,8 @@ string(30) "Syntax error near location 1:9" Unclosed string: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Invalid escape sequence: bool(false) @@ -175,4 +175,3 @@ Missing comma between object properties: bool(false) int(4) string(30) "Syntax error near location 1:9" - diff --git a/ext/json/tests/json_last_error_msg_error_location_008.phpt b/ext/json/tests/json_last_error_msg_error_location_008.phpt index 4d8a1012316b..60dd9513472b 100644 --- a/ext/json/tests/json_last_error_msg_error_location_008.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_008.phpt @@ -82,101 +82,100 @@ Testing error locations with various UTF-8 multi-byte character widths Error with 2-byte UTF-8 (Latin Extended): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with 2-byte UTF-8 (Greek): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:14" +int(4) +string(31) "Syntax error near location 1:14" Error with 2-byte UTF-8 (Cyrillic): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:12" +int(4) +string(31) "Syntax error near location 1:12" Error with 3-byte UTF-8 (Chinese): bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:8" +int(4) +string(30) "Syntax error near location 1:8" Error with 3-byte UTF-8 (Japanese Hiragana): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with 3-byte UTF-8 (Japanese Katakana): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with 3-byte UTF-8 (Korean): bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:8" +int(4) +string(30) "Syntax error near location 1:8" Error with 4-byte UTF-8 (Emoji faces): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" +int(4) +string(31) "Syntax error near location 1:11" Error with 4-byte UTF-8 (Emoji objects): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:13" +int(4) +string(31) "Syntax error near location 1:13" Error with 4-byte UTF-8 (Mathematical symbols): bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with mixed 1-2-3 byte UTF-8: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error with mixed 2-3-4 byte UTF-8: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error with all byte widths: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error with UTF-8 key at start: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:7" +int(4) +string(30) "Syntax error near location 1:7" Error with multiple UTF-8 keys: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:35" +int(4) +string(31) "Syntax error near location 1:35" Error in array with mixed UTF-8: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:25" +int(4) +string(31) "Syntax error near location 1:25" Error in nested structure with various UTF-8: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:22" +int(4) +string(31) "Syntax error near location 1:22" Error with combining diacritical marks: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with Hebrew: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" +int(4) +string(31) "Syntax error near location 1:10" Error with Arabic with diacritics: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:11" - +int(4) +string(31) "Syntax error near location 1:11" diff --git a/ext/json/tests/json_last_error_msg_error_location_009.phpt b/ext/json/tests/json_last_error_msg_error_location_009.phpt index 406179693ef6..9d4403838f63 100644 --- a/ext/json/tests/json_last_error_msg_error_location_009.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_009.phpt @@ -75,18 +75,18 @@ string(46) "Maximum stack depth exceeded near location 1:7" Syntax error at deep nesting level: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:31" +int(4) +string(31) "Syntax error near location 1:31" Syntax error in deep array: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:6" +int(4) +string(30) "Syntax error near location 1:6" Error after valid deep structure: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:48" +int(4) +string(31) "Syntax error near location 1:48" Error in middle of nested structure: bool(false) @@ -95,16 +95,15 @@ string(31) "Syntax error near location 1:29" Error in array with nested objects: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:30" +int(4) +string(31) "Syntax error near location 1:30" Error in deep UTF-8 structure: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:16" +int(4) +string(31) "Syntax error near location 1:16" Valid deep structure within limit: bool(true) int(0) string(8) "No error" - diff --git a/ext/json/tests/json_last_error_msg_error_location_010.phpt b/ext/json/tests/json_last_error_msg_error_location_010.phpt index 108570205838..d187afff86cc 100644 --- a/ext/json/tests/json_last_error_msg_error_location_010.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_010.phpt @@ -119,13 +119,13 @@ string(30) "Syntax error near location 4:2" Error in string with spaces: bool(false) -int(3) -string(71) "Control character error, possibly incorrectly encoded near location 1:9" +int(4) +string(30) "Syntax error near location 1:9" Error with whitespace around colon: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:12" +int(4) +string(31) "Syntax error near location 1:12" Error with whitespace around comma: bool(true) @@ -154,11 +154,10 @@ string(72) "Control character error, possibly incorrectly encoded near location Error in compact JSON: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:22" +int(4) +string(31) "Syntax error near location 1:22" Error with regular spaces: bool(false) -int(3) -string(72) "Control character error, possibly incorrectly encoded near location 1:10" - +int(4) +string(31) "Syntax error near location 1:10" From dbf6d1f82e6b97bb861a1536af657e09e7a7dc15 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Mon, 13 Jul 2026 03:56:22 +0800 Subject: [PATCH 157/211] [skip ci] Update UPGRADING.INTERNALS with ext/intl changes #22446 Added compatibility helpers for ICU version differences in ext/intl. --- UPGRADING.INTERNALS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index bdf874b382d0..7775c9316040 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -173,6 +173,14 @@ PHP 8.6 INTERNALS UPGRADE NOTES . php_idate() now returns the result state, and moves the return value into an out parameter. +- ext/intl: + . Added intl_icu_compat.h with helpers and feature macros for ICU + version-specific API differences. Code in ext/intl should use the + intl_icu_compat_* helpers and INTL_ICU_HAS_* macros instead of adding + direct U_ICU_VERSION_* guards for supported ICU API variants. + . The internal grapheme_get_break_iterator() helper no longer accepts a + stack buffer argument; pass only the UErrorCode* status argument. + - ext/mbstring: . Added GB18030-2022 to default encoding list for zh-CN. From 3eaa3387c25d442b7dfc87a25fa8592ea99c6b26 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 12 Jul 2026 22:16:06 +0200 Subject: [PATCH 158/211] Fix flaky poll socket close tests on Solaris event ports (#22708) After the peer closes, Solaris event ports fire a one-shot snapshot as soon as the socket is writable and may not yet have folded the peer close into a POLLHUP, so the wait() sometimes reports Write without HangUp. Make HangUp optional for the EventPorts backend in the affected tests while keeping Write (and all other backends) strict. The pt_events_equal() helper now treats a nested array in the expected events as an optional slot, and pt_event_array_to_string() renders it in brackets. A manual self-test for this matching logic is included in poll.inc (runs only when the file is executed directly, never in CI). --- ext/standard/tests/poll/poll.inc | 117 ++++++++++++++++-- .../tests/poll/poll_stream_sock_rw_close.phpt | 6 +- .../poll/poll_stream_sock_rw_multi_level.phpt | 11 +- 3 files changed, 124 insertions(+), 10 deletions(-) diff --git a/ext/standard/tests/poll/poll.inc b/ext/standard/tests/poll/poll.inc index a1ca01fc7d4a..ce001220bbac 100644 --- a/ext/standard/tests/poll/poll.inc +++ b/ext/standard/tests/poll/poll.inc @@ -106,7 +106,12 @@ function pt_write_sleep($stream, $data, $delay = 10000): int|false { function pt_event_array_to_string(array $events): string { $names = []; foreach ($events as $event) { - $names[] = $event->name; + if (is_array($event)) { + // Optional slot, rendered in brackets. + $names[] = '[' . pt_event_array_to_string($event) . ']'; + } else { + $names[] = $event->name; + } } return empty($names) ? 'NONE' : implode('|', $names); } @@ -242,17 +247,48 @@ function pt_events_equal($actual, $expected): bool { return false; } - if (count($actual) !== count($expected)) { - return false; + // An expected item that is itself an array marks an optional slot: it may be + // absent, or filled by exactly one of the events it lists. Plain enums are + // required. This lets a backend accept e.g. Write with an optional HangUp + // that may not have propagated yet, without weakening the required events. + $required = []; + $optional_groups = []; + foreach ($expected as $item) { + if (is_array($item)) { + $optional_groups[] = array_map(fn($e) => $e->name, $item); + } else { + $required[] = $item->name; + } } - // Sort both arrays by event name for comparison $actual_names = array_map(fn($e) => $e->name, $actual); - $expected_names = array_map(fn($e) => $e->name, $expected); - sort($actual_names); - sort($expected_names); - return $actual_names === $expected_names; + // Every required event must be present. + foreach ($required as $name) { + $idx = array_search($name, $actual_names, true); + if ($idx === false) { + return false; + } + unset($actual_names[$idx]); + } + + // Each leftover actual event must be accounted for by a distinct optional + // slot that lists it. + foreach ($actual_names as $name) { + $matched = false; + foreach ($optional_groups as $gi => $group) { + if (in_array($name, $group, true)) { + unset($optional_groups[$gi]); + $matched = true; + break; + } + } + if (!$matched) { + return false; + } + } + + return true; } function pt_resolve_backend_specific_value($backend_map, $current_backend) { @@ -319,3 +355,68 @@ function pt_print_mismatched_events($actual_watchers, $expected_watchers, $match echo $match_status . "\n"; } } + +/* + * Self-test for the pt_* event-matching helpers. + * + * This is deliberately NOT a .phpt: the CI only runs .phpt files, never .inc, + * so this never executes there. It only runs when poll.inc is executed as the + * main script, which lets the (fiddly) optional-slot matching logic be verified + * by hand: + * + * sapi/cli/php ext/standard/tests/poll/poll.inc + */ +function pt_run_self_test(): void { + if (!enum_exists('Io\\Poll\\Event')) { + echo "Io\\Poll\\Event not available; build with poll support to run the self-test\n"; + return; + } + + $R = Io\Poll\Event::Read; + $W = Io\Poll\Event::Write; + $E = Io\Poll\Event::Error; + $H = Io\Poll\Event::HangUp; + + $failures = 0; + $check = function (string $label, bool $cond) use (&$failures) { + if ($cond) { + echo "ok - $label\n"; + } else { + echo "FAIL - $label\n"; + ++$failures; + } + }; + + // Plain required sets are order independent and exact. + $check('exact set matches', pt_events_equal([$W, $H], [$H, $W])); + $check('missing required fails', !pt_events_equal([$W], [$W, $H])); + $check('extra unexpected fails', !pt_events_equal([$W, $H], [$W])); + + // Optional slot: [$W, [$H]] means Write required, HangUp allowed but not + // required. + $check('optional present matches', pt_events_equal([$W, $H], [$W, [$H]])); + $check('optional absent matches', pt_events_equal([$W], [$W, [$H]])); + $check('optional cannot satisfy a required event', !pt_events_equal([$H], [$W, [$H]])); + $check('unlisted extra with optional fails', !pt_events_equal([$W, $E], [$W, [$H]])); + + // A single optional slot accepts at most one of its listed events; separate + // slots can each accept one. + $check('optional picks one of group', pt_events_equal([$W, $H], [$W, [$H, $E]])); + $check('optional group may stay empty', pt_events_equal([$W], [$W, [$H, $E]])); + $check('single slot rejects two', !pt_events_equal([$W, $H, $E], [$W, [$H, $E]])); + $check('two slots accept two', pt_events_equal([$W, $H, $E], [$W, [$H], [$E]])); + + // Rendering. + $check('renders plain set', pt_event_array_to_string([$W, $H]) === 'Write|HangUp'); + $check('renders optional slot', pt_event_array_to_string([$W, [$H]]) === 'Write|[HangUp]'); + $check('renders empty as NONE', pt_event_array_to_string([]) === 'NONE'); + + echo $failures === 0 + ? "\nAll self-tests passed\n" + : "\n$failures self-test(s) FAILED\n"; +} + +if (isset($_SERVER['SCRIPT_FILENAME']) + && realpath($_SERVER['SCRIPT_FILENAME']) === __FILE__) { + pt_run_self_test(); +} diff --git a/ext/standard/tests/poll/poll_stream_sock_rw_close.phpt b/ext/standard/tests/poll/poll_stream_sock_rw_close.phpt index 4edd1619d309..3909dea58637 100644 --- a/ext/standard/tests/poll/poll_stream_sock_rw_close.phpt +++ b/ext/standard/tests/poll/poll_stream_sock_rw_close.phpt @@ -17,7 +17,11 @@ pt_expect_events($poll_ctx->wait(0, 100000), [ [ 'events' => [ 'default' => [Io\Poll\Event::Write, Io\Poll\Event::Error, Io\Poll\Event::HangUp], - 'Kqueue|EventPorts' => [Io\Poll\Event::Write, Io\Poll\Event::HangUp], + 'Kqueue' => [Io\Poll\Event::Write, Io\Poll\Event::HangUp], + // On Solaris event ports the peer close may not be folded into a + // POLLHUP in the same snapshot as the POLLOUT readiness, so HangUp + // is optional here. + 'EventPorts' => [Io\Poll\Event::Write, [Io\Poll\Event::HangUp]], ], 'data' => 'socket2_data' ] diff --git a/ext/standard/tests/poll/poll_stream_sock_rw_multi_level.phpt b/ext/standard/tests/poll/poll_stream_sock_rw_multi_level.phpt index 57fc335c26c4..8bc8ea7fcc3d 100644 --- a/ext/standard/tests/poll/poll_stream_sock_rw_multi_level.phpt +++ b/ext/standard/tests/poll/poll_stream_sock_rw_multi_level.phpt @@ -43,7 +43,16 @@ pt_expect_events($poll_ctx->wait(0, 100000), [ fclose($socket1r); pt_expect_events($poll_ctx->wait(0, 100000), [ - ['events' => [Io\Poll\Event::Write, Io\Poll\Event::HangUp], 'data' => 'socket2_data'] + [ + 'events' => [ + 'default' => [Io\Poll\Event::Write, Io\Poll\Event::HangUp], + // On Solaris event ports the peer close may not be folded into a + // POLLHUP in the same snapshot as the POLLOUT readiness, so HangUp + // is optional here. + 'EventPorts' => [Io\Poll\Event::Write, [Io\Poll\Event::HangUp]], + ], + 'data' => 'socket2_data' + ] ], $poll_ctx); fclose($socket1w); From f36660d64507e0d5d9fce73739031faac8139ded Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 2 Jul 2026 20:10:35 +0100 Subject: [PATCH 159/211] ext/dom: fix use-after-free with XPath callback returning foreign-document node. Fix GH-22554 A PHP XPath callback that returns a node belonging to a document created inside the callback (e.g. $d->documentElement of a throwaway DOMDocument) parks that node in the DOMXPath node_list to keep it alive. When a sibling callback consumes a node navigated into that foreign document, the proxy object was created with the DOMXPath's own dom as parent, so it took a reference on the wrong document and none on the foreign one. On teardown the foreign document could be freed while the proxy still referenced it. Route the proxy factory through dom_xpath_intern_for_doc() so the created object shares the ref_obj of the node's actual document, mirroring the query-result path. close GH-22562 --- NEWS | 2 ++ ext/dom/tests/gh22554.phpt | 38 ++++++++++++++++++++++++++++++++++++++ ext/dom/xpath.c | 3 ++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 ext/dom/tests/gh22554.phpt diff --git a/NEWS b/NEWS index 0459373092ff..9807e07945df 100644 --- a/NEWS +++ b/NEWS @@ -41,6 +41,8 @@ PHP NEWS returning the first entity or notation. (Weilin Du) . Fixed bug GH-22623 (use after free with namespace nodes from XSLTProcessor::registerFunctions())/ (David Carlier) + . Fixed bug GH-22554 (use-after-free with XPath callback returning a node + from a foreign document). (David Carlier) - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" diff --git a/ext/dom/tests/gh22554.phpt b/ext/dom/tests/gh22554.phpt new file mode 100644 index 000000000000..c8db1d87390b --- /dev/null +++ b/ext/dom/tests/gh22554.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-22554 (Use-after-free when an XPath callback returns a node from a document created inside the callback) +--CREDITS-- +waseem-cve +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); + +$xp = new DOMXPath($doc); +$xp->registerNamespace('my', 'my.ns'); + +$xp->registerPHPFunctionNS('my.ns', 'include', function () { + $d = new DOMDocument; + $d->loadXML(''); + + return $d->documentElement; +}); + +$xp->registerPHPFunctionNS('my.ns', 'process', function ($arg) { + echo "process arg: ", get_class($arg[0]), " ", $arg[0]->nodeName, "\n"; + return 'x'; +}); + +$result = $xp->query('my:process(my:include()/uaf)'); +var_dump($result->length); +unset($xp); + +echo "Done\n"; + +?> +--EXPECT-- +process arg: DOMElement uaf +int(0) +Done diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index bd9947b4ad10..dc9e1b852fae 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -77,7 +77,8 @@ static void dom_xpath_proxy_factory(xmlNodePtr node, zval *child, dom_object *in ZEND_ASSERT(node->type != XML_NAMESPACE_DECL); - php_dom_create_object(node, child, intern); + dom_xpath_object *xobj = php_xpath_obj_from_obj(&intern->std); + php_dom_create_object(node, child, dom_xpath_intern_for_doc(xobj, node->doc)); } static dom_xpath_object *dom_xpath_ext_fetch_intern(xmlXPathParserContextPtr ctxt) From 7f97b839764b55f68ad820f98836c8897eaf6648 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 11 Jul 2026 21:33:37 +0100 Subject: [PATCH 160/211] ext/dom: getElementsByClassName() item() returns wrong element on random access. The loop never advanced past the current match, so a cold item($n) with $n >= 1, or a backwards seek, returned the first match. Fixed by mirroring dom_map_get_elements_item(): advance to the next match once per remaining index. close GH-22701 --- NEWS | 2 + ext/dom/obj_map.c | 16 ++-- ...lementsByClassName_item_random_access.phpt | 84 +++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 ext/dom/tests/modern/common/Element_getElementsByClassName_item_random_access.phpt diff --git a/NEWS b/NEWS index a4f1a24223ec..9cb1c52aacdd 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,8 @@ PHP NEWS - DOM: . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested Dom\XMLDocument). (iliaal) + . Fixed getElementsByClassName() item() returning the wrong element on + random access. (David Carlier) - Exif: . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index 6e65143648d8..800dadbfb422 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -346,22 +346,20 @@ static void dom_map_get_by_class_name_item(dom_nnodemap_object *map, zend_long i if (nodep && index >= 0) { dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); if (start_point.node) { - if (start_point.index > 0) { - /* Only start iteration at next point if we actually have an index to seek to. */ - itemnode = php_dom_next_in_tree_order(start_point.node, nodep); - } else { - itemnode = start_point.node; - } + itemnode = start_point.node; } else { itemnode = php_dom_first_child_of_container_node(nodep); + while (itemnode != NULL && !dom_matches_class_name(map, itemnode)) { + itemnode = php_dom_next_in_tree_order(itemnode, nodep); + } } - do { - --start_point.index; + for (; start_point.index > 0 && itemnode != NULL; --start_point.index) { + itemnode = php_dom_next_in_tree_order(itemnode, nodep); while (itemnode != NULL && !dom_matches_class_name(map, itemnode)) { itemnode = php_dom_next_in_tree_order(itemnode, nodep); } - } while (start_point.index > 0 && itemnode); + } } dom_ret_node_to_zobj(map, itemnode, return_value); if (itemnode) { diff --git a/ext/dom/tests/modern/common/Element_getElementsByClassName_item_random_access.phpt b/ext/dom/tests/modern/common/Element_getElementsByClassName_item_random_access.phpt new file mode 100644 index 000000000000..005ad45d3c25 --- /dev/null +++ b/ext/dom/tests/modern/common/Element_getElementsByClassName_item_random_access.phpt @@ -0,0 +1,84 @@ +--TEST-- +Dom\Element::getElementsByClassName() item() random access (cold and backwards) +--EXTENSIONS-- +dom +--FILE-- + + + +
+ +

+ + +HTML); + +$body = $dom->getElementsByTagName('body')->item(0); + +function id(?Dom\Element $e): string { + return $e === null ? 'NULL' : $e->id; +} + +echo "-- cold random access (fresh collection per call) --\n"; +foreach ([0, 1, 2, 3, 4, 5] as $i) { + $collection = $body->getElementsByClassName('x'); + echo "item($i) = ", id($collection->item($i)), "\n"; +} + +echo "-- backwards seek on one collection --\n"; +$collection = $body->getElementsByClassName('x'); +foreach ([4, 2, 0, 3, 1] as $i) { + echo "item($i) = ", id($collection->item($i)), "\n"; +} + +echo "-- item() seed then foreach --\n"; +$collection = $body->getElementsByClassName('x'); +$collection->item(3); +$ids = []; +foreach ($collection as $node) { + $ids[] = $node->id; +} +echo implode(" ", $ids), "\n"; + +echo "-- last-element idiom --\n"; +$collection = $body->getElementsByClassName('x'); +echo "length = ", $collection->length, ", last = ", id($collection->item($collection->length - 1)), "\n"; + +echo "-- live collection after mutation --\n"; +$collection = $body->getElementsByClassName('x'); +echo "item(1) = ", id($collection->item(1)), "\n"; +$dom->getElementById('E1')->remove(); +echo "item(1) = ", id($collection->item(1)), ", length = ", $collection->length, "\n"; + +?> +--EXPECT-- +-- cold random access (fresh collection per call) -- +item(0) = E0 +item(1) = E1 +item(2) = E2 +item(3) = E3 +item(4) = E4 +item(5) = NULL +-- backwards seek on one collection -- +item(4) = E4 +item(2) = E2 +item(0) = E0 +item(3) = E3 +item(1) = E1 +-- item() seed then foreach -- +E0 E1 E2 E3 E4 +-- last-element idiom -- +length = 5, last = E4 +-- live collection after mutation -- +item(1) = E1 +item(1) = E2, length = 4 From 1e9f56cdb4126fe9dc693f9fed2385dda31fa4a8 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 12:48:49 -0400 Subject: [PATCH 161/211] Fix GH-22678: array_multisort() use-after-free on mutating comparator array_multisort() snapshotted each input array's buckets without holding a reference, then ran a comparator that under SORT_STRING invokes Stringable::__toString(); a callback that unset or reassigned the array freed its backing store and elements mid-sort, a use-after-free read in the comparator and a write-back through the freed table during the restructure. Hold a reference on each input HashTable for the duration of the sort and restructure the cached tables, mirroring zend_array_sort_ex() (GH-16648). HT_ALLOW_COW_VIOLATION allows the in-place repack under the held reference. Fixes GH-22678 Closes GH-22680 --- NEWS | 2 ++ ext/standard/array.c | 27 +++++++++++---- ext/standard/tests/array/gh22678.phpt | 48 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/array/gh22678.phpt diff --git a/NEWS b/NEWS index 7324e5c591aa..ee23c0ec7a18 100644 --- a/NEWS +++ b/NEWS @@ -87,6 +87,8 @@ PHP NEWS (Weilin Du) . Fixed integer overflow in getimagesize() and getimagesizefromstring() when parsing an IFF chunk with a size of INT_MAX. (David Carlier, Weilin Du) + . Fixed bug GH-22678 (Use-after-free in array_multisort() when the comparator + mutates the array being sorted). (azchin, iliaal) - Zip: . Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex() diff --git a/ext/standard/array.c b/ext/standard/array.c index 13731592d836..4527d9a80df8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -6046,6 +6046,7 @@ PHP_FUNCTION(array_multisort) { zval* args; zval** arrays; + HashTable** hashes; Bucket** indirect; uint32_t idx; HashTable* hash; @@ -6167,11 +6168,17 @@ PHP_FUNCTION(array_multisort) for (i = 0; i < array_size; i++) { indirect[i] = indirects + (i * (num_arrays + 1)); } + hashes = safe_emalloc(num_arrays, sizeof(HashTable *), 0); + for (i = 0; i < num_arrays; i++) { + hashes[i] = Z_ARRVAL_P(arrays[i]); + GC_ADDREF(hashes[i]); + HT_ALLOW_COW_VIOLATION(hashes[i]); + } for (i = 0; i < num_arrays; i++) { k = 0; - if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) { - zval *zv = Z_ARRVAL_P(arrays[i])->arPacked; - for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) { + if (HT_IS_PACKED(hashes[i])) { + zval *zv = hashes[i]->arPacked; + for (idx = 0; idx < hashes[i]->nNumUsed; idx++, zv++) { if (Z_TYPE_P(zv) == IS_UNDEF) continue; ZVAL_COPY_VALUE(&indirect[k][i].val, zv); indirect[k][i].h = idx; @@ -6179,8 +6186,8 @@ PHP_FUNCTION(array_multisort) k++; } } else { - Bucket *p = Z_ARRVAL_P(arrays[i])->arData; - for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) { + Bucket *p = hashes[i]->arData; + for (idx = 0; idx < hashes[i]->nNumUsed; idx++, p++) { if (Z_TYPE(p->val) == IS_UNDEF) continue; indirect[k][i] = *p; k++; @@ -6200,7 +6207,7 @@ PHP_FUNCTION(array_multisort) /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */ for (i = 0; i < num_arrays; i++) { - hash = Z_ARRVAL_P(arrays[i]); + hash = hashes[i]; hash->nNumUsed = array_size; hash->nNextFreeElement = array_size; hash->nInternalPointer = 0; @@ -6229,6 +6236,14 @@ PHP_FUNCTION(array_multisort) RETVAL_TRUE; clean_up: + for (i = 0; i < num_arrays; i++) { + if (UNEXPECTED(GC_DELREF(hashes[i]) == 0)) { + zend_array_destroy(hashes[i]); + } else { + gc_check_possible_root((zend_refcounted *)hashes[i]); + } + } + efree(hashes); efree(indirects); efree(indirect); efree(func); diff --git a/ext/standard/tests/array/gh22678.phpt b/ext/standard/tests/array/gh22678.phpt new file mode 100644 index 000000000000..c1b71b592520 --- /dev/null +++ b/ext/standard/tests/array/gh22678.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-22678 (Use-after-free in array_multisort() when the comparator mutates the array) +--FILE-- + 'c', 3 => 'a', 1 => 'b']; +array_multisort($b, SORT_STRING); +echo "repacked: ", implode(',', $b), "\n"; + +class Boom { + public function __toString(): string { + throw new Exception("boom"); + } +} + +$c = [new Boom(), new Boom()]; +try { + array_multisort($c, SORT_STRING); +} catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +freed mid-sort: bool(true) +repacked: a,b,c +Exception: boom From 1feb20156bbfc4450e455a92a026e5a53eba5937 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 22 Jun 2026 10:27:06 -0400 Subject: [PATCH 162/211] Fix use-after-free on re-entrant ftp_close() during a transfer A user stream wrapper passed to a blocking or non-blocking FTP transfer can re-enter the engine from its stream_read/stream_write handler while the engine still holds the freed ftpbuf_t/databuf_t on the C stack, so the transfer resumes on freed memory. Guard every operation that opens a data connection with an in_use flag: ftp_close() throws while it is set, and the transfer and listing functions refuse to run. The non-blocking checks sit at the userland entry, before the wrapper mutates ftp->direction/stream, so a rejected re-entrant call cannot corrupt the outer transfer; the nb cleanup clears ftp->stream before closing the stream so a stream_close() that calls ftp_close() cannot double-close it. Closes GH-22400 --- ext/ftp/ftp.c | 53 +++++++++++++++++++ ext/ftp/ftp.h | 1 + ext/ftp/php_ftp.c | 30 +++++++++-- ext/ftp/tests/ftp_close_during_transfer.phpt | 44 +++++++++++++++ .../tests/ftp_nb_close_during_transfer.phpt | 44 +++++++++++++++ .../tests/ftp_nb_get_during_nb_transfer.phpt | 44 +++++++++++++++ ext/ftp/tests/ftp_nb_get_during_transfer.phpt | 40 ++++++++++++++ 7 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 ext/ftp/tests/ftp_close_during_transfer.phpt create mode 100644 ext/ftp/tests/ftp_nb_close_during_transfer.phpt create mode 100644 ext/ftp/tests/ftp_nb_get_during_nb_transfer.phpt create mode 100644 ext/ftp/tests/ftp_nb_get_during_transfer.phpt diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 17a3e10e0d61..1345e378d8e3 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -892,6 +892,11 @@ ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t pat if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -967,9 +972,11 @@ ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t pat goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -1057,6 +1064,11 @@ ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *inst if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -1097,9 +1109,11 @@ ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *inst if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) { goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -1114,6 +1128,11 @@ ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -1141,9 +1160,11 @@ ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) { goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -2055,6 +2076,10 @@ ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *pa char **entry; char *text; + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return NULL; + } if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) { php_error_docref(NULL, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory."); @@ -2156,6 +2181,11 @@ ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t return PHP_FTP_FAILED; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } + if (ftp->data != NULL) { /* If there is a transfer in action, abort it. * If we don't, we get an invalid state and memory leaks when the new connection gets opened. */ @@ -2223,11 +2253,17 @@ ftp_nb_continue_read(ftpbuf_t *ftp) data = ftp->data; + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } + /* check if there is already more data */ if (!data_available(ftp, data->fd, false)) { return PHP_FTP_MOREDATA; } + ftp->in_use = true; type = ftp->type; lastch = ftp->lastch; @@ -2251,6 +2287,7 @@ ftp_nb_continue_read(ftpbuf_t *ftp) } ftp->lastch = lastch; + ftp->in_use = false; return PHP_FTP_MOREDATA; } @@ -2265,9 +2302,11 @@ ftp_nb_continue_read(ftpbuf_t *ftp) } ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FINISHED; bail: ftp->nb = 0; + ftp->in_use = false; data_close(ftp); return PHP_FTP_FAILED; } @@ -2283,6 +2322,10 @@ ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } if (!ftp_type(ftp, type)) { goto bail; } @@ -2330,16 +2373,24 @@ ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i int ftp_nb_continue_write(ftpbuf_t *ftp) { + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } + /* check if we can write more data */ if (!data_writeable(ftp, ftp->data->fd)) { return PHP_FTP_MOREDATA; } + ftp->in_use = true; + if (ftp_send_stream_to_data_socket(ftp, ftp->data, ftp->stream, ftp->type, true) != SUCCESS) { goto bail; } if (!php_stream_eof(ftp->stream)) { + ftp->in_use = false; return PHP_FTP_MOREDATA; } @@ -2349,10 +2400,12 @@ ftp_nb_continue_write(ftpbuf_t *ftp) goto bail; } ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FINISHED; bail: data_close(ftp); ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FAILED; } /* }}} */ diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h index 94abc588ca85..91aafad02b79 100644 --- a/ext/ftp/ftp.h +++ b/ext/ftp/ftp.h @@ -73,6 +73,7 @@ typedef struct ftpbuf databuf_t *data; /* Data connection for "nonblocking" transfers */ php_stream *stream; /* output stream for "nonblocking" transfers */ bool nb; /* "nonblocking" transfer in progress */ + bool in_use; /* engine transfer in progress; blocks re-entrant ftp_close */ char lastch; /* last char of previous call */ bool direction; /* recv = 0 / send = 1 */ bool closestream;/* close or not close stream */ diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 8fa7675c6e9d..432270cff190 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -648,6 +648,11 @@ PHP_FUNCTION(ftp_nb_fget) } /* configuration */ + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + RETURN_FALSE; + } + ftp->direction = 0; /* recv */ ftp->closestream = 0; /* do not close */ @@ -761,6 +766,10 @@ PHP_FUNCTION(ftp_nb_get) RETURN_THROWS(); } GET_FTPBUF(ftp, z_ftp); + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + RETURN_FALSE; + } XTYPE(xtype, mode); /* ignore autoresume if autoseek is switched off */ @@ -798,8 +807,8 @@ PHP_FUNCTION(ftp_nb_get) ftp->closestream = 1; /* do close */ if ((ret = ftp_nb_get(ftp, outstream, remote, remote_len, xtype, resumepos)) == PHP_FTP_FAILED) { - php_stream_close(outstream); ftp->stream = NULL; + php_stream_close(outstream); VCWD_UNLINK(local); if (*ftp->inbuf) { php_error_docref(NULL, E_WARNING, "%s", ftp->inbuf); @@ -808,8 +817,8 @@ PHP_FUNCTION(ftp_nb_get) } if (ret == PHP_FTP_FINISHED){ - php_stream_close(outstream); ftp->stream = NULL; + php_stream_close(outstream); } RETURN_LONG(ret); @@ -937,6 +946,11 @@ PHP_FUNCTION(ftp_nb_fput) } /* configuration */ + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + RETURN_FALSE; + } + ftp->direction = 1; /* send */ ftp->closestream = 0; /* do not close */ @@ -1077,6 +1091,12 @@ PHP_FUNCTION(ftp_nb_put) } } + if (ftp->in_use) { + php_stream_close(instream); + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + RETURN_FALSE; + } + /* configuration */ ftp->direction = 1; /* send */ ftp->closestream = 1; /* do close */ @@ -1084,8 +1104,8 @@ PHP_FUNCTION(ftp_nb_put) ret = ftp_nb_put(ftp, remote, remote_len, instream, xtype, startpos); if (ret != PHP_FTP_MOREDATA) { - php_stream_close(instream); ftp->stream = NULL; + php_stream_close(instream); } if (ret == PHP_FTP_FAILED) { @@ -1220,6 +1240,10 @@ PHP_FUNCTION(ftp_close) obj = ftp_object_from_zend_object(Z_OBJ_P(z_ftp)); if (obj->ftp) { + if (obj->ftp->in_use) { + zend_throw_error(NULL, "Cannot close FTP\\Connection while a transfer is in progress"); + RETURN_THROWS(); + } success = ftp_quit(obj->ftp); ftp_close(obj->ftp); obj->ftp = NULL; diff --git a/ext/ftp/tests/ftp_close_during_transfer.phpt b/ext/ftp/tests/ftp_close_during_transfer.phpt new file mode 100644 index 000000000000..72e410603921 --- /dev/null +++ b/ext/ftp/tests/ftp_close_during_transfer.phpt @@ -0,0 +1,44 @@ +--TEST-- +ftp_close() from a stream wrapper during a transfer throws instead of freeing the connection +--EXTENSIONS-- +ftp +pcntl +--FILE-- +getMessage(), "\n"; +} + +ftp_close($ftp); +echo "closed\n"; +?> +--EXPECT-- +bool(true) +Cannot close FTP\Connection while a transfer is in progress +closed diff --git a/ext/ftp/tests/ftp_nb_close_during_transfer.phpt b/ext/ftp/tests/ftp_nb_close_during_transfer.phpt new file mode 100644 index 000000000000..702648f69592 --- /dev/null +++ b/ext/ftp/tests/ftp_nb_close_during_transfer.phpt @@ -0,0 +1,44 @@ +--TEST-- +ftp_close() from a stream wrapper during a non-blocking transfer throws instead of freeing the connection +--EXTENSIONS-- +ftp +pcntl +--FILE-- +getMessage(), "\n"; +} + +ftp_close($ftp); +echo "closed\n"; +?> +--EXPECT-- +bool(true) +Cannot close FTP\Connection while a transfer is in progress +closed diff --git a/ext/ftp/tests/ftp_nb_get_during_nb_transfer.phpt b/ext/ftp/tests/ftp_nb_get_during_nb_transfer.phpt new file mode 100644 index 000000000000..deb1698c77c5 --- /dev/null +++ b/ext/ftp/tests/ftp_nb_get_during_nb_transfer.phpt @@ -0,0 +1,44 @@ +--TEST-- +Re-entrant ftp_nb_get() from a stream wrapper during an active non-blocking ftp_nb_get() must not corrupt the outer transfer +--EXTENSIONS-- +ftp +pcntl +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +closed diff --git a/ext/ftp/tests/ftp_nb_get_during_transfer.phpt b/ext/ftp/tests/ftp_nb_get_during_transfer.phpt new file mode 100644 index 000000000000..c7920496feb4 --- /dev/null +++ b/ext/ftp/tests/ftp_nb_get_during_transfer.phpt @@ -0,0 +1,40 @@ +--TEST-- +Re-entrant ftp_nb_get() from a stream wrapper during a blocking ftp_get() must not free the active data connection +--EXTENSIONS-- +ftp +pcntl +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +closed From b51f575ba1a15f426c1356b104a9a8b1084029d6 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 12 Jul 2026 19:21:54 -0400 Subject: [PATCH 163/211] ext/gd/tests/gh19{666,739}.phpt: skip for external libgd These two are regression tests for the bundled libgd, we have to skip them if an external libgd is used. close GH-22711 --- ext/gd/tests/gh19666.phpt | 4 ++++ ext/gd/tests/gh19739.phpt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ext/gd/tests/gh19666.phpt b/ext/gd/tests/gh19666.phpt index 2d82b9b44760..b1409946aa0a 100644 --- a/ext/gd/tests/gh19666.phpt +++ b/ext/gd/tests/gh19666.phpt @@ -2,6 +2,10 @@ GH-19666 (Unexpected nan value in imageconvolution) --EXTENSIONS-- gd +--SKIPIF-- + --FILE-- --FILE-- Date: Thu, 2 Jul 2026 17:46:05 +0530 Subject: [PATCH 164/211] ext/ldap: fix ldap_explode_dn result ordering Closes GH-22550 --- ext/ldap/ldap.c | 12 +++++++++--- ext/ldap/tests/ldap_explode_dn.phpt | 16 ++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 2c9fdb8e3761..8c0a04729deb 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -2209,6 +2209,8 @@ PHP_FUNCTION(ldap_explode_dn) zend_long with_attrib; char *dn, **ldap_value; size_t dn_len; + int i, count; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "pl", &dn, &dn_len, &with_attrib) != SUCCESS) { RETURN_THROWS(); @@ -2220,11 +2222,15 @@ PHP_FUNCTION(ldap_explode_dn) } array_init(return_value); - int i; - for (i = 0; ldap_value[i] != NULL; i++) { + i = 0; + while (ldap_value[i] != NULL) i++; + count = i; + + add_assoc_long(return_value, "count", count); + + for (i = 0; i < count; i++) { add_index_string(return_value, i, ldap_value[i]); } - add_assoc_long(return_value, "count", i); ldap_memvfree((void **)ldap_value); } diff --git a/ext/ldap/tests/ldap_explode_dn.phpt b/ext/ldap/tests/ldap_explode_dn.phpt index 047078c7beb7..2012506c3ef0 100644 --- a/ext/ldap/tests/ldap_explode_dn.phpt +++ b/ext/ldap/tests/ldap_explode_dn.phpt @@ -34,16 +34,18 @@ echo "Done\n"; ?> --EXPECT-- array(4) { + ["count"]=> + int(3) [0]=> string(6) "cn=bob" [1]=> string(10) "dc=example" [2]=> string(6) "dc=com" - ["count"]=> - int(3) } array(5) { + ["count"]=> + int(4) [0]=> string(6) "cn=bob" [1]=> @@ -52,20 +54,20 @@ array(5) { string(10) "dc=example" [3]=> string(6) "dc=com" - ["count"]=> - int(4) } array(4) { + ["count"]=> + int(3) [0]=> string(3) "bob" [1]=> string(7) "example" [2]=> string(3) "com" - ["count"]=> - int(3) } array(5) { + ["count"]=> + int(4) [0]=> string(3) "bob" [1]=> @@ -74,8 +76,6 @@ array(5) { string(7) "example" [3]=> string(3) "com" - ["count"]=> - int(4) } bool(false) bool(false) From e2c5b3c684fddb7c22a47f6e518435176ec715fb Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:52:27 +0700 Subject: [PATCH 165/211] zend_objects: Readonly properties must be re-locked after clone-with (#22654) --- NEWS | 2 ++ Zend/tests/clone/clone_with_014.phpt | 41 ++++++++++++++++++++++++++++ Zend/zend_objects.c | 8 ++++++ 3 files changed, 51 insertions(+) create mode 100644 Zend/tests/clone/clone_with_014.phpt diff --git a/NEWS b/NEWS index 46f23c3aa6d1..ed10d3bf2595 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ PHP NEWS containing NUL). (iliaal) . Fixed bug GH-22206 (missing return in global register detection). (P3p111n0) + . Lock unmodified readonly properties for modification after clone-with. + (NickSdot) - Calendar: . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with diff --git a/Zend/tests/clone/clone_with_014.phpt b/Zend/tests/clone/clone_with_014.phpt new file mode 100644 index 000000000000..bfbee40e163c --- /dev/null +++ b/Zend/tests/clone/clone_with_014.phpt @@ -0,0 +1,41 @@ +--TEST-- +Properties are still readonly after clone-with +--FILE-- +a = 1; + $this->b = 2; + } +} + +$test = clone(new Test(), ['a' => 3]); +var_dump($test); + +try { + $test->b = 4; +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +var_dump($test); + +?> +--EXPECT-- +object(Test)#2 (2) { + ["a"]=> + int(3) + ["b"]=> + int(2) +} +Error: Cannot modify readonly property Test::$b +object(Test)#2 (2) { + ["a"]=> + int(3) + ["b"]=> + int(2) +} diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 6f6a82638944..d92d54d3f4a4 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -322,6 +322,14 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const } ZEND_HASH_FOREACH_END(); EG(fake_scope) = old_scope; + + /* Lock readonly properties once more. */ + if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { + for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { + zval* prop = OBJ_PROP_NUM(new_object, i); + Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; + } + } } return new_object; From 841b5bfcf20daa97cdd682949df1b980d9359442 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Mon, 13 Jul 2026 09:10:23 +0200 Subject: [PATCH 166/211] CI: Revert timeout fix for Symfony community build (#22682) This reverts the FileInputHelperTest skip added in 1182b14b7f1. --- .github/workflows/test-suite.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index e269ed336408..b7609608f8be 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -573,10 +573,6 @@ jobs: php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);' # Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668 php -r '$c = file_get_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php"); $c = str_replace("public function testCastNonTrailingCharPointer()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testCastNonTrailingCharPointer()", $c); file_put_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php", $c);' - # Causes massive amounts of system calls with USE_ZEND_ALLOC=0, exceeding the timeout - if [ -e 'src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php' ]; then - php -r '$c = file_get_contents("src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php"); $c = str_replace("public function testReadWithPasteDetectionAbortsBeyondMaxBytes()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testReadWithPasteDetectionAbortsBeyondMaxBytes()", $c); file_put_contents("src/Symfony/Component/Console/Tests/Helper/FileInputHelperTest.php", $c);' - fi export SYMFONY_DEPRECATIONS_HELPER=max[total]=999 X=0 for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do From cf3c803b506709c2af84c2be84e7746a97868a3e Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 8 Jul 2026 12:21:58 +0200 Subject: [PATCH 167/211] `abort()` in OOM case Closes GH-22637 --- NEWS | 1 + UPGRADING | 4 ++++ Zend/zend_alloc.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b2544cb5abdf..2fa31ab34fde 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ PHP NEWS iterator). (iliaal) . Lock unmodified readonly properties for modification after clone-with. (NickSdot) + . abort() instead of exit() on hard OOM. (realFlowControl) - Calendar: . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with diff --git a/UPGRADING b/UPGRADING index a809687d1961..729dfed0363e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -533,6 +533,10 @@ PHP 8.6 UPGRADE NOTES 13. Other Changes ======================================== +- Core: + . In case of a hard OOM PHP now calls abort() instead of exit(1), changing + the exit code to 134 and possibly creating a core dump. + ======================================== 14. Performance Improvements ======================================== diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 0b040743abf1..5ff2860d8223 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2974,7 +2974,7 @@ ZEND_API void refresh_memory_manager(void) static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void) { fprintf(stderr, "Out of memory\n"); - exit(1); + abort(); } #if ZEND_MM_CUSTOM From 13169cd2fec73f9ea0ecac3c555618c3c32fb8aa Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 13 Jul 2026 17:59:42 +0700 Subject: [PATCH 168/211] json: Stop tracking columns in the success path (#22487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of tracking columns during lexing, which costs performance for something that will only be required in the unexpected failure path, we now only track the line numbers and calculate the column on demand when an error occurs. As part of this we also fix GH-22487 by adjusting the logic to report the column based on the (UTF-8) column in the source, instead of treating `\uXXXX` escapes as a single column, which users will need to count manually instead of being able to rely on their editor or `mb_substr()` or similar. [tim: (re)written the commit message] Co-authored-by: Tim Düsterhus --- NEWS | 4 + ext/json/json.c | 2 +- ext/json/json_parser.y | 35 ++++++--- ext/json/json_scanner.re | 77 +++---------------- ext/json/php_json.h | 4 +- ext/json/php_json_parser.h | 8 -- ext/json/php_json_scanner.h | 16 +--- ext/json/tests/bug68546.phpt | 2 +- ext/json/tests/gh22514.phpt | 21 +++++ ...son_last_error_msg_error_location_005.phpt | 14 ++-- 10 files changed, 73 insertions(+), 110 deletions(-) create mode 100644 ext/json/tests/gh22514.phpt diff --git a/NEWS b/NEWS index 2fa31ab34fde..35eb80d675ca 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,10 @@ PHP NEWS - JSON: . Report unterminated JSON strings as syntax errors. (timwolla) + . Improve performance error position tracking during JSON decoding. + (henderkes) + . Fixed bug GH-22514 (Incorrect error column in PHP 8.6 JSON parser). + (henderkes, timwolla) - Opcache: . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache diff --git a/ext/json/json.c b/ext/json/json.c index ac033c057ac4..04a62f52152f 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -184,7 +184,7 @@ static zend_string *php_json_get_error_msg_with_location(const php_json_error_de const char *base_msg = php_json_get_error_msg(details->code); if (details->line > 0 && details->column > 0) { - return zend_strpprintf(0, "%s near location %zu:%zu", base_msg, details->line, details->column); + return zend_strpprintf(0, "%s near location %" PRIu64 ":%" PRIu64, base_msg, details->line, details->column); } return zend_string_init(base_msg, strlen(base_msg), 0); diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y index 0d3b90b29e1e..0fd3e2c4e364 100644 --- a/ext/json/json_parser.y +++ b/ext/json/json_parser.y @@ -39,7 +39,6 @@ int json_yydebug = 1; } -%locations %define api.prefix {php_json_yy} %define api.pure full %param { php_json_parser *parser } @@ -64,8 +63,8 @@ int json_yydebug = 1; %destructor { zval_ptr_dtor_nogc(&$$); } %code { -static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser); -static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg); +static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser); +static void php_json_yyerror(php_json_parser *parser, char const *msg); static int php_json_parser_array_create(php_json_parser *parser, zval *array); static int php_json_parser_object_create(php_json_parser *parser, zval *array); @@ -275,7 +274,7 @@ static int php_json_parser_object_update_validate(php_json_parser *parser, zval return SUCCESS; } -static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_parser *parser) +static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser) { int token = php_json_scan(&parser->scanner); @@ -291,15 +290,10 @@ static int php_json_yylex(union YYSTYPE *value, YYLTYPE *location, php_json_pars value->value = parser->scanner.value; } - location->first_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_column); - location->first_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, first_line); - location->last_column = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_column); - location->last_line = PHP_JSON_SCANNER_LOCATION(parser->scanner, last_line); - return token; } -static void php_json_yyerror(YYLTYPE *location, php_json_parser *parser, char const *msg) +static void php_json_yyerror(php_json_parser *parser, char const *msg) { if (!parser->scanner.errcode) { parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX; @@ -311,11 +305,28 @@ PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parse return parser->scanner.errcode; } +static uint64_t php_json_compute_error_column(const php_json_scanner *s) +{ + const php_json_ctype *p = s->line_start; + const php_json_ctype *end = s->token; + /* Count characters from the start of the line to the failing token, + * folding UTF-8 continuation bytes into their leading byte. */ + uint64_t column = 1; + + while (p < end) { + if ((*p & 0b11000000) != 0b10000000) { + column++; + } + p++; + } + return column; +} + PHP_JSON_API void php_json_parser_error_details(const php_json_parser *parser, php_json_error_details *out) { out->code = parser->scanner.errcode; - out->line = parser->scanner.errloc.first_line; - out->column = parser->scanner.errloc.first_column; + out->line = parser->scanner.line; + out->column = php_json_compute_error_column(&parser->scanner); } static const php_json_parser_methods default_parser_methods = diff --git a/ext/json/json_scanner.re b/ext/json/json_scanner.re index 0c64a6423baf..be62875a00e0 100644 --- a/ext/json/json_scanner.re +++ b/ext/json/json_scanner.re @@ -51,7 +51,6 @@ #define PHP_JSON_INT_MAX_LENGTH (MAX_LENGTH_OF_LONG - 1) #define PHP_JSON_TOKEN_LENGTH() ((size_t) (s->cursor - s->token)) -#define PHP_JSON_TOKEN_LOCATION(location) (s)->errloc.location static void php_json_scanner_copy_string(php_json_scanner *s, size_t esc_size) { @@ -96,10 +95,8 @@ void php_json_scanner_init(php_json_scanner *s, const char *str, size_t str_len, s->cursor = (php_json_ctype *) str; s->limit = (php_json_ctype *) str + str_len; s->options = options; - PHP_JSON_TOKEN_LOCATION(first_column) = 1; - PHP_JSON_TOKEN_LOCATION(first_line) = 1; - PHP_JSON_TOKEN_LOCATION(last_column) = 1; - PHP_JSON_TOKEN_LOCATION(last_line) = 1; + s->line = 1; + s->line_start = (php_json_ctype *) str; PHP_JSON_CONDITION_SET(JS); } @@ -108,8 +105,6 @@ int php_json_scan(php_json_scanner *s) ZVAL_NULL(&s->value); std: - PHP_JSON_TOKEN_LOCATION(first_column) = s->errloc.last_column; - PHP_JSON_TOKEN_LOCATION(first_line) = s->errloc.last_line; s->token = s->cursor; /*!re2c @@ -155,49 +150,27 @@ std: UTF16_3 = UTFPREF ( ( ( HEXC | [efEF] ) HEX ) | ( [dD] HEX7 ) ) HEX{2} ; UTF16_4 = UTFPREF [dD] [89abAB] HEX{2} UTFPREF [dD] [c-fC-F] HEX{2} ; - "{" { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return '{'; - } - "}" { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return '}'; - } - "[" { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return '['; - } - "]" { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return ']'; - } - ":" { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return ':'; - } - "," { - PHP_JSON_TOKEN_LOCATION(last_column)++; - return ','; - } + "{" { return '{'; } + "}" { return '}'; } + "[" { return '['; } + "]" { return ']'; } + ":" { return ':'; } + "," { return ','; } "null" { - PHP_JSON_TOKEN_LOCATION(last_column) += 4; ZVAL_NULL(&s->value); return PHP_JSON_T_NUL; } "true" { - PHP_JSON_TOKEN_LOCATION(last_column) += 4; ZVAL_TRUE(&s->value); return PHP_JSON_T_TRUE; } "false" { - PHP_JSON_TOKEN_LOCATION(last_column) += 5; ZVAL_FALSE(&s->value); return PHP_JSON_T_FALSE; } INT { bool bigint = 0, negative = s->token[0] == '-'; size_t digits = PHP_JSON_TOKEN_LENGTH(); - PHP_JSON_TOKEN_LOCATION(last_column) += digits; digits -= negative; if (digits >= PHP_JSON_INT_MAX_LENGTH) { if (digits == PHP_JSON_INT_MAX_LENGTH) { @@ -221,19 +194,15 @@ std: } } FLOAT|EXP { - PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH(); ZVAL_DOUBLE(&s->value, zend_strtod((char *) s->token, NULL)); return PHP_JSON_T_DOUBLE; } NL { - PHP_JSON_TOKEN_LOCATION(last_line)++; - PHP_JSON_TOKEN_LOCATION(last_column) = 1; - goto std; - } - WS { - PHP_JSON_TOKEN_LOCATION(last_column) += PHP_JSON_TOKEN_LENGTH(); + s->line++; + s->line_start = s->cursor; goto std; } + WS { goto std; } EOI { if (s->limit < s->cursor) { return PHP_JSON_T_EOI; @@ -243,7 +212,6 @@ std: } } ["] { - PHP_JSON_TOKEN_LOCATION(last_column)++; s->str_start = s->cursor; s->str_esc = 0; s->utf8_invalid = 0; @@ -275,22 +243,18 @@ std: return PHP_JSON_T_ERROR; } UTF16_1 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 5; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_2 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 4; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_3 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 3; PHP_JSON_CONDITION_GOTO(STR_P1); } UTF16_4 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; s->str_esc += 8; PHP_JSON_CONDITION_GOTO(STR_P1); } @@ -299,7 +263,6 @@ std: return PHP_JSON_T_ERROR; } ESC { - PHP_JSON_TOKEN_LOCATION(last_column) += 2; s->str_esc++; PHP_JSON_CONDITION_GOTO(STR_P1); } @@ -308,7 +271,6 @@ std: return PHP_JSON_T_ERROR; } ["] { - PHP_JSON_TOKEN_LOCATION(last_column)++; zend_string *str; size_t len = (size_t)(s->cursor - s->str_start - s->str_esc - 1 + s->utf8_invalid_count); if (len == 0) { @@ -329,22 +291,7 @@ std: return PHP_JSON_T_STRING; } } - UTF8_1 { - PHP_JSON_TOKEN_LOCATION(last_column)++; - PHP_JSON_CONDITION_GOTO(STR_P1); - } - UTF8_2 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; - PHP_JSON_CONDITION_GOTO(STR_P1); - } - UTF8_3 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; - PHP_JSON_CONDITION_GOTO(STR_P1); - } - UTF8_4 { - PHP_JSON_TOKEN_LOCATION(last_column) += 1; - PHP_JSON_CONDITION_GOTO(STR_P1); - } + UTF8 { PHP_JSON_CONDITION_GOTO(STR_P1); } ANY { if (s->options & (PHP_JSON_INVALID_UTF8_IGNORE | PHP_JSON_INVALID_UTF8_SUBSTITUTE)) { if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) { diff --git a/ext/json/php_json.h b/ext/json/php_json.h index f34684e149d8..f20b20964a71 100644 --- a/ext/json/php_json.h +++ b/ext/json/php_json.h @@ -54,8 +54,8 @@ typedef enum { typedef struct php_json_error_details { php_json_error_code code; - size_t line; - size_t column; + uint64_t line; + uint64_t column; } php_json_error_details; static inline void php_json_error_details_clear(php_json_error_details *out) { diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h index 888a0d317fe0..ae927d880aeb 100644 --- a/ext/json/php_json_parser.h +++ b/ext/json/php_json_parser.h @@ -48,20 +48,12 @@ typedef struct _php_json_parser_methods { php_json_parser_func_object_end_t object_end; } php_json_parser_methods; - typedef struct _php_json_parser_location { - size_t first_line; - size_t first_column; - size_t last_line; - size_t last_column; -} php_json_parser_location; - struct _php_json_parser { php_json_scanner scanner; zval *return_value; int depth; int max_depth; php_json_parser_methods methods; - php_json_parser_location *location; }; PHP_JSON_API void php_json_parser_init_ex( diff --git a/ext/json/php_json_scanner.h b/ext/json/php_json_scanner.h index 90460cf9952a..f432f66b6d78 100644 --- a/ext/json/php_json_scanner.h +++ b/ext/json/php_json_scanner.h @@ -20,17 +20,6 @@ typedef unsigned char php_json_ctype; -typedef struct _php_json_error_location { - /** first column of the error */ - size_t first_column; - /** first line of the error */ - size_t first_line; - /** last column of the error */ - size_t last_column; - /** last line of the error */ - size_t last_line; -} php_json_error_location; - typedef struct _php_json_scanner { php_json_ctype *cursor; /* cursor position */ php_json_ctype *token; /* token position */ @@ -39,18 +28,17 @@ typedef struct _php_json_scanner { php_json_ctype *ctxmarker; /* marker position for context backtracking */ php_json_ctype *str_start; /* start position of the string */ php_json_ctype *pstr; /* string pointer for escapes conversion */ + php_json_ctype *line_start; /* start position of the current line */ + uint64_t line; /* current line number (1-based) */ zval value; /* value */ int str_esc; /* number of extra characters for escaping */ int state; /* condition state */ int options; /* options */ php_json_error_code errcode; /* error type if there is an error */ - php_json_error_location errloc; /* error location */ int utf8_invalid; /* whether utf8 is invalid */ int utf8_invalid_count; /* number of extra character for invalid utf8 */ } php_json_scanner; -#define PHP_JSON_SCANNER_LOCATION(scanner, slocation) (scanner).errloc.slocation - void php_json_scanner_init(php_json_scanner *scanner, const char *str, size_t str_len, int options); int php_json_scan(php_json_scanner *s); diff --git a/ext/json/tests/bug68546.phpt b/ext/json/tests/bug68546.phpt index 1847eabf3a8e..40ea085ba23b 100644 --- a/ext/json/tests/bug68546.phpt +++ b/ext/json/tests/bug68546.phpt @@ -16,5 +16,5 @@ NULL bool(true) NULL bool(true) -string(55) "The decoded property name is invalid near location 1:27" +string(55) "The decoded property name is invalid near location 1:37" Done diff --git a/ext/json/tests/gh22514.phpt b/ext/json/tests/gh22514.phpt new file mode 100644 index 000000000000..9e7d2adfff8a --- /dev/null +++ b/ext/json/tests/gh22514.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-22514: Incorrect error column in JSON parser after escape sequences +--FILE-- + +--EXPECT-- +NULL +string(31) "Syntax error near location 2:10" +NULL +string(30) "Syntax error near location 2:5" diff --git a/ext/json/tests/json_last_error_msg_error_location_005.phpt b/ext/json/tests/json_last_error_msg_error_location_005.phpt index ee1800eb8273..ca798fcb01fe 100644 --- a/ext/json/tests/json_last_error_msg_error_location_005.phpt +++ b/ext/json/tests/json_last_error_msg_error_location_005.phpt @@ -19,8 +19,8 @@ json_validate_trycatchdump('{"test": "\uD83D\uDE00\uD83C\uDF89}'); echo "\nError with mixed UTF-8 and UTF-16:\n"; json_validate_trycatchdump('{"mixed": "Hello \u4E16\u754C world}'); -// UTF-16 in key and value -echo "\nError with UTF-16 in key:\n"; +// Missing closing quote after a UTF-16 key +echo "\nError on unterminated value after UTF-16 key:\n"; json_validate_trycatchdump('{"\u30D7\u30EC\u30B9": "value}'); // Multiple keys with UTF-16 @@ -66,15 +66,15 @@ bool(false) int(4) string(31) "Syntax error near location 1:11" -Error with UTF-16 in key: +Error on unterminated value after UTF-16 key: bool(false) int(4) -string(30) "Syntax error near location 1:9" +string(31) "Syntax error near location 1:24" Error with multiple UTF-16 keys: bool(false) int(4) -string(31) "Syntax error near location 1:22" +string(31) "Syntax error near location 1:42" Error with BMP characters: bool(false) @@ -89,12 +89,12 @@ string(31) "Syntax error near location 1:11" Error in array with UTF-16: bool(false) int(4) -string(31) "Syntax error near location 1:12" +string(31) "Syntax error near location 1:22" Error in nested structure with UTF-16: bool(false) int(4) -string(31) "Syntax error near location 1:18" +string(31) "Syntax error near location 1:28" Error with UTF-16 and control chars: bool(false) From 0b0284eb16dba22bcb6a56d8da9604b8ec4228cc Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 4 Jul 2026 21:25:52 +0700 Subject: [PATCH 169/211] perf: move AG and SCNG into native __thread storage Replay of GH-22231 Closes GH-22595 --- NEWS | 1 + TSRM/TSRM.c | 66 ++++++++++++++++++++++++++++++++---- TSRM/TSRM.h | 4 +++ UPGRADING.INTERNALS | 8 +++++ Zend/zend.c | 6 ++-- Zend/zend_alloc.c | 16 +++------ Zend/zend_alloc.h | 4 --- Zend/zend_globals.h | 4 +-- Zend/zend_globals_macros.h | 14 ++++++-- Zend/zend_language_scanner.l | 6 +++- main/main.c | 6 +--- 11 files changed, 99 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index 35eb80d675ca..8c29466dcc64 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS . Lock unmodified readonly properties for modification after clone-with. (NickSdot) . abort() instead of exit() on hard OOM. (realFlowControl) + . perf: ZTS: move AG and SCNG into native __thread storage. (henderkes) - Calendar: . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 4222e88755d6..a5032e456aae 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -41,6 +41,8 @@ typedef struct { ts_allocate_ctor ctor; ts_allocate_dtor dtor; ptrdiff_t fast_offset; + /* When set, storage comes from __thread memory instead of being allocated by TSRM. */ + void *(*tls_addr)(void); int done; } tsrm_resource_type; @@ -164,14 +166,20 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb static void ts_free_resources(tsrm_tls_entry *thread_resources) { + bool own_thread = thread_resources->thread_id == tsrm_thread_id(); + /* Need to destroy in reverse order to respect dependencies. */ for (int i = thread_resources->count - 1; i >= 0; i--) { if (!resource_types_table[i].done) { + /* A __thread block of a foreign thread is inaccessible. */ + if (resource_types_table[i].tls_addr && !own_thread) { + continue; + } if (resource_types_table[i].dtor) { resource_types_table[i].dtor(thread_resources->storage[i]); } - if (!resource_types_table[i].fast_offset) { + if (!resource_types_table[i].fast_offset && !resource_types_table[i].tls_addr) { free(thread_resources->storage[i]); } } @@ -180,7 +188,9 @@ static void ts_free_resources(tsrm_tls_entry *thread_resources) free(thread_resources->storage); } -/* Shutdown TSRM (call once for the entire process) */ +/* Shutdown TSRM (call once for the entire process). Tears down every thread left + * in the table. For resources allocated with ts_allocate_tls_id(), only the dtor + * of the calling thread is invoked. */ TSRM_API void tsrm_shutdown(void) {/*{{{*/ if (is_thread_shutdown) { @@ -258,7 +268,10 @@ static void tsrm_update_active_threads(void) p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count); for (j=p->count; jthread_id == tsrm_thread_id()); + p->storage[j] = resource_types_table[j].tls_addr(); + } else if (resource_types_table[j].fast_offset) { p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset); } else { p->storage[j] = (void *) malloc(resource_types_table[j].size); @@ -303,6 +316,7 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; tsrm_update_active_threads(); @@ -381,6 +395,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; tsrm_update_active_threads(); @@ -390,6 +405,41 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, return *rsrc_id; } +/* allocates a resource id whose per-thread storage is a native __thread block */ +TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) +{ + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new TLS resource id, %d bytes", size)); + + tsrm_mutex_lock(tsmm_mutex); + + *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); + + if (resource_types_table_size < id_count) { + tsrm_resource_type *_tmp; + _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count); + if (!_tmp) { + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource")); + *rsrc_id = 0; + tsrm_mutex_unlock(tsmm_mutex); + return 0; + } + resource_types_table = _tmp; + resource_types_table_size = id_count; + } + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = tls_addr; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; + + tsrm_update_active_threads(); + tsrm_mutex_unlock(tsmm_mutex); + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new TLS resource id %d", *rsrc_id)); + return *rsrc_id; +} + static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) { tsrm_tls_set(thread_resource); @@ -422,7 +472,9 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_ if (resource_types_table[i].done) { (*thread_resources_ptr)->storage[i] = NULL; } else { - if (resource_types_table[i].fast_offset) { + if (resource_types_table[i].tls_addr) { + (*thread_resources_ptr)->storage[i] = resource_types_table[i].tls_addr(); + } else if (resource_types_table[i].fast_offset) { (*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset); } else { (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); @@ -510,7 +562,9 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) /* In case that extensions don't use the pointer passed from the dtor, but incorrectly * use the global pointer, we need to setup the global pointer temporarily here. */ set_thread_local_storage_resource_to(thread_resources); - /* Free up the old resource from the old thread instance */ + /* Dead thread with a recycled id: its __thread blocks are gone, and this + * thread's blocks were never constructed, so keep tls dtors from running. */ + thread_resources->thread_id = 0; ts_free_resources(thread_resources); free((char *) thread_resources - tsrm_reserved_front); /* Allocate a new resource at the same point in the linked list, and relink the next pointer */ @@ -584,7 +638,7 @@ void ts_free_id(ts_rsrc_id id) if (resource_types_table[rsrc_id].dtor) { resource_types_table[rsrc_id].dtor(p->storage[rsrc_id]); } - if (!resource_types_table[rsrc_id].fast_offset) { + if (!resource_types_table[rsrc_id].fast_offset && !resource_types_table[rsrc_id].tls_addr) { free(p->storage[rsrc_id]); } } diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 2e8cbddfcda7..752ea1803f9c 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -100,6 +100,10 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz TSRM_API void tsrm_reserve_fast_front(size_t size); TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); +/* Resource whose per-thread storage is a native __thread block. + * Must be called at startup before any other thread exists. */ +TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); + /* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id); #define ts_resource(id) ts_resource_ex(id, NULL) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 7775c9316040..462e567f3f36 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -235,3 +235,11 @@ PHP 8.6 INTERNALS UPGRADE NOTES ======================== 5. SAPI changes ======================== + +- SAPIs should explicitly release a thread's resources by calling + ts_free_thread() before terminating it. tsrm_shutdown() can only release the + resources of the calling thread, for resources allocated with + ts_allocate_tls_id(). + +- AG and SCNG are now allocated with ts_allocate_tls_id() and live in native + __thread storage on ZTS builds. diff --git a/Zend/zend.c b/Zend/zend.c index 9411b92a2018..07692db85196 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -54,6 +54,9 @@ ZEND_API int compiler_globals_id; ZEND_API int executor_globals_id; ZEND_API size_t compiler_globals_offset; ZEND_API size_t executor_globals_offset; +/* ts_allocate_tls_id takes a callback so each thread resolves its own block. + * A plain &language_scanner_globals would capture only the registering thread's address. */ +static void *language_scanner_globals_tls_addr(void) { return &language_scanner_globals; } static HashTable *global_function_table = NULL; static HashTable *global_class_table = NULL; static HashTable *global_constants_table = NULL; @@ -1021,10 +1024,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ #ifdef ZTS ts_allocate_fast_id_at(&compiler_globals_id, &compiler_globals_offset, ZEND_CG_OFFSET, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor); ts_allocate_fast_id_at(&executor_globals_id, &executor_globals_offset, ZEND_EG_OFFSET, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor); - ts_allocate_fast_id_at(&language_scanner_globals_id, &language_scanner_globals_offset, ZEND_SCNG_OFFSET, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL); + ts_allocate_tls_id(&language_scanner_globals_id, language_scanner_globals_tls_addr, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL); ZEND_ASSERT(compiler_globals_offset == ZEND_CG_OFFSET); ZEND_ASSERT(executor_globals_offset == ZEND_EG_OFFSET); - ZEND_ASSERT(language_scanner_globals_offset == ZEND_SCNG_OFFSET); ts_allocate_fast_id(&ini_scanner_globals_id, &ini_scanner_globals_offset, sizeof(zend_ini_scanner_globals), (ts_allocate_ctor) ini_scanner_globals_ctor, NULL); compiler_globals = ts_resource(compiler_globals_id); executor_globals = ts_resource(executor_globals_id); diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 5ff2860d8223..fc7bc1f4d9d4 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2613,13 +2613,12 @@ typedef struct _zend_alloc_globals { #ifdef ZTS static int alloc_globals_id; -static size_t alloc_globals_offset; -# define ZEND_AG_OFFSET (ZEND_SCNG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_alloc_globals))) -# define AG(v) ZEND_TSRMG_FAST(ZEND_AG_OFFSET, zend_alloc_globals *, v) +static TSRM_TLS TSRM_TLS_MODEL_ATTR zend_alloc_globals alloc_globals; +static void *alloc_globals_tls_addr(void) { return &alloc_globals; } #else -# define AG(v) (alloc_globals.v) static zend_alloc_globals alloc_globals; #endif +#define AG(v) (alloc_globals.v) ZEND_API bool is_zend_mm(void) { @@ -3336,8 +3335,7 @@ ZEND_API void start_memory_manager(void) # endif #endif #ifdef ZTS - ts_allocate_fast_id_at(&alloc_globals_id, &alloc_globals_offset, ZEND_AG_OFFSET, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); - ZEND_ASSERT(alloc_globals_offset == ZEND_AG_OFFSET); + ts_allocate_tls_id(&alloc_globals_id, alloc_globals_tls_addr, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); #else alloc_globals_ctor(&alloc_globals); #endif @@ -3583,9 +3581,3 @@ ZEND_API char * __zend_strdup(const char *s) zend_out_of_memory(); } -#ifdef ZTS -size_t zend_mm_globals_size(void) -{ - return sizeof(zend_alloc_globals); -} -#endif diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index ff51c4fe8652..7e3ea031dc72 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -403,10 +403,6 @@ static void apc_init_heap(void) */ -#ifdef ZTS -size_t zend_mm_globals_size(void); -#endif - END_EXTERN_C() #endif diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 61499c0cc23d..f78567cfaa74 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -330,11 +330,9 @@ struct _zend_executor_globals { }; #ifdef ZTS -/* Compile-time offsets of the hot globals, in a reserved region just before the - * cache pointer. ZEND_AG_OFFSET is furthest, in zend_alloc.c. */ +/* Compile-time offsets of the hot globals, in a reserved region just before *_tsrm_ls_cache. */ # define ZEND_CG_OFFSET (-(ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals))) # define ZEND_EG_OFFSET (ZEND_CG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals))) -# define ZEND_SCNG_OFFSET (ZEND_EG_OFFSET - (ptrdiff_t) TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals))) #endif #define EG_FLAGS_INITIAL (0) diff --git a/Zend/zend_globals_macros.h b/Zend/zend_globals_macros.h index 2d2948e50a86..adb3913ece71 100644 --- a/Zend/zend_globals_macros.h +++ b/Zend/zend_globals_macros.h @@ -48,12 +48,20 @@ extern ZEND_API zend_executor_globals executor_globals; /* Language Scanner */ #ifdef ZTS -# define LANG_SCNG(v) ZEND_TSRMG_FAST(ZEND_SCNG_OFFSET, zend_php_scanner_globals *, v) extern ZEND_API ts_rsrc_id language_scanner_globals_id; -extern ZEND_API size_t language_scanner_globals_offset; +# if defined(ZEND_WIN32) && !defined(LIBZEND_EXPORTS) +# define LANG_SCNG(v) TSRMG(language_scanner_globals_id, zend_php_scanner_globals *, v) +# else +# ifdef ZEND_WIN32 +extern TSRM_TLS zend_php_scanner_globals language_scanner_globals; +# else +extern ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_php_scanner_globals language_scanner_globals; +# endif +# define LANG_SCNG(v) (language_scanner_globals.v) +# endif #else -# define LANG_SCNG(v) (language_scanner_globals.v) extern ZEND_API zend_php_scanner_globals language_scanner_globals; +# define LANG_SCNG(v) (language_scanner_globals.v) #endif diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 07f2d44cb5c6..1d7e41a2a7d3 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -84,7 +84,11 @@ #define SCNG LANG_SCNG #ifdef ZTS ZEND_API ts_rsrc_id language_scanner_globals_id; -ZEND_API size_t language_scanner_globals_offset; +# ifdef ZEND_WIN32 +TSRM_TLS zend_php_scanner_globals language_scanner_globals; +# else +ZEND_API TSRM_TLS TSRM_TLS_MODEL_ATTR zend_php_scanner_globals language_scanner_globals; +# endif #else ZEND_API zend_php_scanner_globals language_scanner_globals; #endif diff --git a/main/main.c b/main/main.c index 48e4a757513b..9e77c99a45df 100644 --- a/main/main.c +++ b/main/main.c @@ -2819,13 +2819,11 @@ PHPAPI void php_reserve_tsrm_memory(void) tsrm_reserve( TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals)) + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals)) + - TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals)) + TSRM_ALIGNED_SIZE(sizeof(zend_ini_scanner_globals)) + TSRM_ALIGNED_SIZE(sizeof(virtual_cwd_globals)) + #ifdef ZEND_SIGNALS TSRM_ALIGNED_SIZE(sizeof(zend_signal_globals_t)) + #endif - TSRM_ALIGNED_SIZE(zend_mm_globals_size()) + TSRM_ALIGNED_SIZE(zend_gc_globals_size()) + TSRM_ALIGNED_SIZE(sizeof(php_core_globals)) + TSRM_ALIGNED_SIZE(sizeof(sapi_globals_struct)) + @@ -2845,9 +2843,7 @@ PHPAPI bool php_tsrm_startup_ex(int expected_threads) /* Must cover the total size of every ZEND_*_OFFSET global, or the furthest underflows the block. */ tsrm_reserve_fast_front( TSRM_ALIGNED_SIZE(sizeof(zend_compiler_globals)) + - TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals)) + - TSRM_ALIGNED_SIZE(sizeof(zend_php_scanner_globals)) + - TSRM_ALIGNED_SIZE(zend_mm_globals_size())); // AG size, exposed through function call + TSRM_ALIGNED_SIZE(sizeof(zend_executor_globals))); (void)ts_resource(0); return ret; } From a095c57f03293109d8391c08aeaac24ab04abe6c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 8 Jul 2026 13:50:15 -0400 Subject: [PATCH 170/211] Alpine CI: don't install gnu-libiconv-dev If we do not install GNU libiconv, the musl libc iconv() implementation should take over, allowing it to be tested on the CI. Closes GH-22645 --- .github/actions/apk/action.yml | 1 - .github/actions/configure-alpine/action.yml | 2 +- ext/iconv/tests/bug48147.phpt | 32 ++++++++++++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/.github/actions/apk/action.yml b/.github/actions/apk/action.yml index 0cda4963a6a9..8d0fee161187 100644 --- a/.github/actions/apk/action.yml +++ b/.github/actions/apk/action.yml @@ -28,7 +28,6 @@ runs: curl-dev \ freetype-dev \ gettext-dev \ - gnu-libiconv-dev \ gmp-dev \ icu-dev \ icu-data-full \ diff --git a/.github/actions/configure-alpine/action.yml b/.github/actions/configure-alpine/action.yml index fe02dacfcdaf..d1b4cfea0412 100644 --- a/.github/actions/configure-alpine/action.yml +++ b/.github/actions/configure-alpine/action.yml @@ -43,7 +43,7 @@ runs: --enable-pcntl \ --with-readline \ --enable-mbstring \ - --with-iconv=/usr \ + --with-iconv \ --with-curl \ --with-gettext \ --enable-sockets \ diff --git a/ext/iconv/tests/bug48147.phpt b/ext/iconv/tests/bug48147.phpt index ce304eecfb3c..434f71e517bb 100644 --- a/ext/iconv/tests/bug48147.phpt +++ b/ext/iconv/tests/bug48147.phpt @@ -2,17 +2,41 @@ Bug #48147 (iconv with //IGNORE cuts the string) --EXTENSIONS-- iconv +--SKIPIF-- + --FILE-- --EXPECTF-- Notice: iconv(): Detected an illegal character in input string in %s on line %d @@ -20,8 +44,8 @@ bool(false) string(10) "aa%C3%B8aa" Notice: iconv(): Detected an incomplete multibyte character in input string in %s on line %d -string(0) "" +bool(false) string(8) "%C3%B8aa" Notice: iconv(): Detected an incomplete multibyte character in input string in %s on line %d -string(0) "" +bool(false) From c559d6f24c4aeeeeac5547daa9296da594dda223 Mon Sep 17 00:00:00 2001 From: Khaled Alam Date: Mon, 13 Jul 2026 18:21:32 +0400 Subject: [PATCH 171/211] Fix GH-22683: Reflection(Class)Constant::__toString() should not warn on NAN conversions (#22694) --- NEWS | 2 ++ ext/reflection/php_reflection.c | 4 ++++ ext/reflection/tests/gh22683.phpt | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 ext/reflection/tests/gh22683.phpt diff --git a/NEWS b/NEWS index ed10d3bf2595..947f2fdedd9b 100644 --- a/NEWS +++ b/NEWS @@ -88,6 +88,8 @@ PHP NEWS dynamic properties shadowing a private parent property). (iliaal) . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value with null bytes truncates output). (DanielEScherzer) + . Fixed bug GH-22683 (Reflection(Class)Constant::__toString() should not warn + on NAN conversions). (Khaled Alam) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index fc2e06d3b08c..bc176cca16b2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -617,6 +617,8 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c smart_str_append(str, ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED)); } else if (Z_TYPE_P(value) == IS_STRING) { smart_str_append(str, Z_STR_P(value)); + } else if (Z_TYPE_P(value) == IS_DOUBLE) { + smart_str_append_double(str, Z_DVAL_P(value), (int) EG(precision), false); } else { zend_string *tmp_value_str; zend_string *value_str = zval_get_tmp_string(value, &tmp_value_str); @@ -649,6 +651,8 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl smart_str_appends(str, "Array"); } else if (Z_TYPE(c->value) == IS_OBJECT) { smart_str_appends(str, "Object"); + } else if (Z_TYPE(c->value) == IS_DOUBLE) { + smart_str_append_double(str, Z_DVAL(c->value), (int) EG(precision), false); } else { zend_string *tmp_value_str; zend_string *value_str = zval_get_tmp_string(&c->value, &tmp_value_str); diff --git a/ext/reflection/tests/gh22683.phpt b/ext/reflection/tests/gh22683.phpt new file mode 100644 index 000000000000..9dfe8eba25c2 --- /dev/null +++ b/ext/reflection/tests/gh22683.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-22683 (Reflection(Class)Constant::__toString() should not warn on NAN conversions) +--FILE-- + +--EXPECT-- +Constant [ float NAN ] { NAN } +Constant [ float INF ] { INF } +Constant [ public float MY_NAN ] { NAN } +Constant [ public float MY_INF ] { INF } +Constant [ public float MY_FLOAT ] { 1.5 } +Constant [ public float MY_WHOLE ] { 2 } From 3129b3baab5ab5052a3aa7f7c5cf3d5151983b4f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 13 Jul 2026 17:26:52 +0100 Subject: [PATCH 172/211] bz2: refactor filter creation to do parameter validation (#22307) --- ext/bz2/bz2_filter.c | 191 +++++++++++------- ext/bz2/tests/bug72447.phpt | 4 +- ext/bz2/tests/bz2_filter_compress_errors.phpt | 67 ++++++ .../tests/bz2_filter_decompress_errors.phpt | 19 ++ ext/bz2/tests/bz2_filter_unknown_errors.phpt | 16 ++ 5 files changed, 222 insertions(+), 75 deletions(-) create mode 100644 ext/bz2/tests/bz2_filter_compress_errors.phpt create mode 100644 ext/bz2/tests/bz2_filter_decompress_errors.phpt create mode 100644 ext/bz2/tests/bz2_filter_unknown_errors.phpt diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c index ec042a51a3b2..845c11865f15 100644 --- a/ext/bz2/bz2_filter.c +++ b/ext/bz2/bz2_filter.c @@ -375,22 +375,8 @@ static const php_stream_filter_ops php_bz2_compress_ops = { /* }}} */ -/* {{{ bzip2.* common factory */ - -static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, bool persistent) -{ - const php_stream_filter_ops *fops = NULL; - php_stream_filter_seekable_t write_seekable; - php_bz2_filter_data *data; - int status = BZ_OK; - - if (php_stream_filter_parse_write_seek_mode(filterparams, &write_seekable) == FAILURE) { - return NULL; - } - - /* Create this filter */ - data = pecalloc(1, sizeof(php_bz2_filter_data), persistent); - +static php_bz2_filter_data *php_bz2_filter_data_new(bool persistent) { + php_bz2_filter_data *data = pecalloc(1, sizeof(php_bz2_filter_data), persistent); /* Circular reference */ data->strm.opaque = (void *) data; @@ -401,86 +387,143 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi data->strm.next_in = data->inbuf = (char *) pemalloc(data->inbuf_len, persistent); data->strm.avail_in = 0; data->strm.next_out = data->outbuf = (char *) pemalloc(data->outbuf_len, persistent); + return data; +} - if (strcasecmp(filtername, "bzip2.decompress") == 0) { - data->small_footprint = 0; - data->expect_concatenated = 0; - - if (filterparams) { - zval *tmpzval = NULL; +static php_stream_filter *php_bz2_decompress_filter_create(zval *filter_params, bool persistent) { + php_stream_filter_seekable_t write_seekable = PSFS_SEEKABLE_ALWAYS; + bool small_footprint = false; + bool expect_concatenated = false; + + if (filter_params) { + if (UNEXPECTED( + Z_TYPE_P(filter_params) != IS_TRUE + && Z_TYPE_P(filter_params) != IS_FALSE + && Z_TYPE_P(filter_params) != IS_ARRAY + && Z_TYPE_P(filter_params) != IS_OBJECT + )) { + php_error_docref(NULL, E_WARNING, + "Filter parameters for bzip2.decompress filter must be of type array|object|bool, %s given", + zend_zval_type_name(filter_params) + ); + return NULL; + } - if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) { - HashTable *ht = HASH_OF(filterparams); + if (Z_TYPE_P(filter_params) == IS_TRUE || Z_TYPE_P(filter_params) == IS_FALSE) { + small_footprint = Z_TYPE_P(filter_params) == IS_TRUE; + } else { + ZEND_ASSERT(Z_TYPE_P(filter_params) == IS_ARRAY || Z_TYPE_P(filter_params) == IS_OBJECT); - if ((tmpzval = zend_hash_str_find_ind(ht, "concatenated", sizeof("concatenated")-1))) { - data->expect_concatenated = zend_is_true(tmpzval); - tmpzval = NULL; - } + const HashTable *filter_params_ht = HASH_OF(filter_params); + /* TODO: convert php_stream_filter_parse_write_seek_mode() to take HashTable */ + if (php_stream_filter_parse_write_seek_mode(filter_params, &write_seekable) == FAILURE) { + return NULL; + } - tmpzval = zend_hash_str_find_ind(ht, "small", sizeof("small")-1); - } else { - tmpzval = filterparams; + const zval *concatenated = zend_hash_str_find_ind(filter_params_ht, ZEND_STRL("concatenated")); + if (concatenated) { + expect_concatenated = zend_is_true(concatenated); } - if (tmpzval) { - data->small_footprint = zend_is_true(tmpzval); + const zval *small = zend_hash_str_find_ind(filter_params_ht, ZEND_STRL("small")); + if (small) { + small_footprint = zend_is_true(small); } } + } - data->status = PHP_BZ2_UNINITIALIZED; - fops = &php_bz2_decompress_ops; - } else if (strcasecmp(filtername, "bzip2.compress") == 0) { - int blockSize100k = PHP_BZ2_FILTER_DEFAULT_BLOCKSIZE; - int workFactor = PHP_BZ2_FILTER_DEFAULT_WORKFACTOR; - - if (filterparams) { - zval *tmpzval; - - if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) { - HashTable *ht = HASH_OF(filterparams); - - if ((tmpzval = zend_hash_str_find_ind(ht, "blocks", sizeof("blocks")-1))) { - /* How much memory to allocate (1 - 9) x 100kb */ - zend_long blocks = zval_get_long(tmpzval); - if (blocks < 1 || blocks > 9) { - php_error_docref(NULL, E_WARNING, "Invalid parameter given for number of blocks to allocate (" ZEND_LONG_FMT ")", blocks); - } else { - blockSize100k = (int) blocks; - } - } + php_bz2_filter_data *data = php_bz2_filter_data_new(persistent); + /* Save configuration for reset */ + data->small_footprint = small_footprint; + data->expect_concatenated = expect_concatenated; + data->status = PHP_BZ2_UNINITIALIZED; - if ((tmpzval = zend_hash_str_find_ind(ht, "work", sizeof("work")-1))) { - /* Work Factor (0 - 250) */ - zend_long work = zval_get_long(tmpzval); - if (work < 0 || work > 250) { - php_error_docref(NULL, E_WARNING, "Invalid parameter given for work factor (" ZEND_LONG_FMT ")", work); - } else { - workFactor = (int) work; - } - } - } + return php_stream_filter_alloc(&php_bz2_decompress_ops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +} + +static php_stream_filter *php_bz2_compress_filter_create(zval *filter_params, bool persistent) { + php_stream_filter_seekable_t write_seekable = PSFS_SEEKABLE_ALWAYS; + int blockSize100k = PHP_BZ2_FILTER_DEFAULT_BLOCKSIZE; + int workFactor = PHP_BZ2_FILTER_DEFAULT_WORKFACTOR; + + if (filter_params) { + if (UNEXPECTED(Z_TYPE_P(filter_params) != IS_ARRAY && Z_TYPE_P(filter_params) != IS_OBJECT)) { + php_error_docref(NULL, E_WARNING, + "Filter parameters for bzip2.compress filter must be of type array|object, %s given", + zend_zval_type_name(filter_params) + ); + return NULL; } - /* Save configuration for reset */ - data->blockSize100k = blockSize100k; - data->workFactor = workFactor; + const HashTable *filter_params_ht = HASH_OF(filter_params); + /* TODO: convert php_stream_filter_parse_write_seek_mode() to take HashTable */ + if (php_stream_filter_parse_write_seek_mode(filter_params, &write_seekable) == FAILURE) { + return NULL; + } - status = BZ2_bzCompressInit(&(data->strm), blockSize100k, 0, workFactor); - data->is_flushed = 1; - fops = &php_bz2_compress_ops; - } else { - status = BZ_DATA_ERROR; + const zval *blocks_zv = zend_hash_str_find_ind(filter_params_ht, ZEND_STRL("blocks")); + if (blocks_zv) { + ZEND_ASSERT(Z_TYPE_P(blocks_zv) != IS_INDIRECT); + bool failed = false; + /* How much memory to allocate (1 - 9) x 100kb */ + zend_long blocks = zval_try_get_long(blocks_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Number of blocks parameter must be of type int, %s given", zend_zval_type_name(blocks_zv)); + return NULL; + } else if (blocks < 1 || blocks > 9) { + php_error_docref(NULL, E_WARNING, "Number of blocks to allocate must be between 1 and 9, " ZEND_LONG_FMT " given", blocks); + return NULL; + } else { + blockSize100k = (int) blocks; + } + } + + const zval *work_zv = zend_hash_str_find_ind(filter_params_ht, ZEND_STRL("work")); + if (work_zv) { + ZEND_ASSERT(Z_TYPE_P(work_zv) != IS_INDIRECT); + bool failed = false; + /* Work Factor (0 - 250) */ + zend_long work = zval_try_get_long(work_zv, &failed); + if (UNEXPECTED(failed)) { + php_error_docref(NULL, E_WARNING, "Work factor parameter must be of type int, %s given", zend_zval_type_name(work_zv)); + return NULL; + } else if (work < 0 || work > 250) { + php_error_docref(NULL, E_WARNING, "Work factor must be between 0 and 250, " ZEND_LONG_FMT " given", work); + return NULL; + } else { + workFactor = (int) work; + } + } } - if (status != BZ_OK) { + php_bz2_filter_data *data = php_bz2_filter_data_new(persistent); + /* Save configuration for reset */ + data->blockSize100k = blockSize100k; + data->workFactor = workFactor; + + int status = BZ2_bzCompressInit(&(data->strm), blockSize100k, 0, workFactor); + if (UNEXPECTED(status != BZ_OK)) { /* Unspecified (probably strm) error, let stream-filter error do its own whining */ pefree(data->strm.next_in, persistent); pefree(data->strm.next_out, persistent); pefree(data, persistent); return NULL; } + data->is_flushed = true; + + return php_stream_filter_alloc(&php_bz2_compress_ops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +} - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, write_seekable); +/* {{{ bzip2.* common factory */ +static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, bool persistent) +{ + if (strcasecmp(filtername, "bzip2.decompress") == 0) { + return php_bz2_decompress_filter_create(filterparams, persistent); + } else if (strcasecmp(filtername, "bzip2.compress") == 0) { + return php_bz2_compress_filter_create(filterparams, persistent); + } else { + return NULL; + } } const php_stream_filter_factory php_bz2_filter_factory = { diff --git a/ext/bz2/tests/bug72447.phpt b/ext/bz2/tests/bug72447.phpt index 11f3bd9136b5..8e2fc2b79802 100644 --- a/ext/bz2/tests/bug72447.phpt +++ b/ext/bz2/tests/bug72447.phpt @@ -16,4 +16,6 @@ fclose($fp); unlink('testfile'); ?> --EXPECTF-- -Warning: stream_filter_append(): Invalid parameter given for number of blocks to allocate (0) in %s%ebug72447.php on line %d +Warning: stream_filter_append(): Number of blocks parameter must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d diff --git a/ext/bz2/tests/bz2_filter_compress_errors.phpt b/ext/bz2/tests/bz2_filter_compress_errors.phpt new file mode 100644 index 000000000000..48f6759d642b --- /dev/null +++ b/ext/bz2/tests/bz2_filter_compress_errors.phpt @@ -0,0 +1,67 @@ +--TEST-- +bzip2.compress filter param errors +--EXTENSIONS-- +bz2 +--FILE-- + 'not an int']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +$param = ['blocks' => '15']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +$param = ['blocks' => '0']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +$param = ['work' => 'not an int']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +$param = ['work' => '251']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +$param = ['work' => '-1']; +var_dump(stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param)); + +fclose($fp); + +?> +--EXPECTF-- +Warning: stream_filter_append(): Filter parameters for bzip2.compress filter must be of type array|object, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Number of blocks parameter must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Number of blocks to allocate must be between 1 and 9, 15 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Number of blocks to allocate must be between 1 and 9, 0 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Work factor parameter must be of type int, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Work factor must be between 0 and 250, 251 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) + +Warning: stream_filter_append(): Work factor must be between 0 and 250, -1 given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s on line %d +bool(false) diff --git a/ext/bz2/tests/bz2_filter_decompress_errors.phpt b/ext/bz2/tests/bz2_filter_decompress_errors.phpt new file mode 100644 index 000000000000..f89029bd50c3 --- /dev/null +++ b/ext/bz2/tests/bz2_filter_decompress_errors.phpt @@ -0,0 +1,19 @@ +--TEST-- +bzip2.decompress filter param errors +--EXTENSIONS-- +bz2 +--FILE-- + +--EXPECTF-- +Warning: stream_filter_append(): Filter parameters for bzip2.decompress filter must be of type array|object|bool, string given in %s on line %d + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.decompress" in %s on line %d +bool(false) diff --git a/ext/bz2/tests/bz2_filter_unknown_errors.phpt b/ext/bz2/tests/bz2_filter_unknown_errors.phpt new file mode 100644 index 000000000000..7364481185a5 --- /dev/null +++ b/ext/bz2/tests/bz2_filter_unknown_errors.phpt @@ -0,0 +1,16 @@ +--TEST-- +bzip2 filter error on unknown filter name +--EXTENSIONS-- +bz2 +--FILE-- + +--EXPECTF-- +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.i_dont_exist" in %s on line %d +bool(false) From 4fa10e251364cb0497efdef3ab6a642b6c784805 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Tue, 14 Jul 2026 04:27:52 +0800 Subject: [PATCH 173/211] ext/soap: Improve SOAP scalar decoding error messages (#22706) SOAP encoding errors now report the affected type or failing operation in the error message instead of the generic "Encoding: Violation of encoding rules" message. Code that compares the exact message may need to be updated. --- NEWS | 2 + UPGRADING | 4 ++ ext/soap/php_encoding.c | 43 +++++++++++-------- ext/soap/tests/bugs/bug39832.phpt | 2 +- ext/soap/tests/hexbin_odd_length.phpt | 2 +- ext/soap/tests/scalar_error_messages.phpt | 52 +++++++++++++++++++++++ ext/soap/tests/soap12/T27.phpt | 2 +- ext/soap/tests/soap12/T58.phpt | 2 +- 8 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 ext/soap/tests/scalar_error_messages.phpt diff --git a/NEWS b/NEWS index fb54cab61c0f..702d9d04fe3d 100644 --- a/NEWS +++ b/NEWS @@ -103,6 +103,8 @@ PHP NEWS do_request() parameters). (David Carlier) . Fixed xsd:hexBinary decoding to reject odd-length values instead of silently truncating the last nibble. (Weilin Du) + . Made SOAP encoding errors report the affected type or failing operation + instead of the generic "Violation of encoding rules" message. (Weilin Du) - Standard: . Fixed sleep() and usleep() to reject values that overflow the underlying diff --git a/UPGRADING b/UPGRADING index 729dfed0363e..74a5e066483f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -147,6 +147,10 @@ PHP 8.6 UPGRADE NOTES . WSDL/XML Schema parsing now rejects out-of-range integer values for occurrence constraints and integer restriction facets. Negative minOccurs and maxOccurs values are rejected as well. + . SOAP encoding errors now report the affected type or failing operation in + the error message instead of the generic "Encoding: Violation of encoding + rules" message. Code that compares the exact message may need to be + updated. - Sodium: . The password-hashing functions sodium_crypto_pwhash(), diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 301cdd8588b4..d08c5a683189 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -82,6 +82,11 @@ static xmlNodePtr to_xml_any(encodeTypePtr type, zval *data, int style, xmlNodeP static zval *guess_zval_convert(zval *ret, encodeTypePtr type, xmlNodePtr data); static xmlNodePtr guess_xml_convert(encodeTypePtr type, zval *data, int style, xmlNodePtr parent); +static zend_always_inline const char *soap_type_name(encodeTypePtr type) +{ + return (type && type->type_str) ? type->type_str : "unknown"; +} + static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *out_type); static xmlNodePtr check_and_resolve_href(xmlNodePtr data); @@ -660,7 +665,7 @@ static zval *to_zval_string(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -693,7 +698,7 @@ static zval *to_zval_stringr(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -726,7 +731,7 @@ static zval *to_zval_stringc(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { ZVAL_STRING(ret, (char*)data->children->content); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -745,17 +750,17 @@ static zval *to_zval_base64(zval *ret, encodeTypePtr type, xmlNodePtr data) whiteSpace_collapse(data->children->content); str = php_base64_decode(data->children->content, strlen((char*)data->children->content)); if (!str) { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } ZVAL_STR(ret, str); } else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) { str = php_base64_decode(data->children->content, strlen((char*)data->children->content)); if (!str) { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } ZVAL_STR(ret, str); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_EMPTY_STRING(ret); @@ -776,12 +781,12 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) if (data->children->type == XML_TEXT_NODE && data->children->next == NULL) { whiteSpace_collapse(data->children->content); } else if (data->children->type != XML_CDATA_SECTION_NODE || data->children->next != NULL) { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); return ret; } content_len = strlen((char*) data->children->content); if (content_len % 2 != 0) { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain an even number of hexadecimal digits", soap_type_name(type)); return ret; } str = zend_string_alloc(content_len / 2, 0); @@ -794,7 +799,7 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (c >= 'A' && c <= 'F') { ZSTR_VAL(str)[i] = (c - 'A' + 10) << 4; } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } c = data->children->content[j++]; if (c >= '0' && c <= '9') { @@ -804,7 +809,7 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (c >= 'A' && c <= 'F') { ZSTR_VAL(str)[i] |= c - 'A' + 10; } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } } ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0'; @@ -1025,11 +1030,11 @@ static zval *to_zval_double(zval *ret, encodeTypePtr type, xmlNodePtr data) } else if (strncasecmp((char*)data->children->content, "-INF", sizeof("-INF")-1) == 0) { ZVAL_DOUBLE(ret, -php_get_inf()); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } } } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_NULL(ret); @@ -1058,10 +1063,10 @@ static zval *to_zval_long(zval *ret, encodeTypePtr type, xmlNodePtr data) ZVAL_DOUBLE(ret, dval); break; default: - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type)); } } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } } else { ZVAL_NULL(ret); @@ -1127,7 +1132,7 @@ static zval *to_zval_bool(zval *ret, encodeTypePtr type, xmlNodePtr data) } if (data->children->type != XML_TEXT_NODE || data->children->next != NULL) { // TODO Convert to exception? - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type)); } whiteSpace_collapse(data->children->content); @@ -3096,7 +3101,9 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP } smart_str_appends(&list, (char*)dummy->children->content); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error2(E_ERROR, + "Encoding: Failed to encode list item of type '%s' for list type '%s'", + soap_type_name(&list_enc->details), soap_type_name(enc)); } xmlUnlinkNode(dummy); xmlFreeNode(dummy); @@ -3138,7 +3145,9 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP } smart_str_appends(&list, (char*)dummy->children->content); } else { - soap_error0(E_ERROR, "Encoding: Violation of encoding rules"); + soap_error2(E_ERROR, + "Encoding: Failed to encode list item of type '%s' for list type '%s'", + soap_type_name(&list_enc->details), soap_type_name(enc)); } xmlUnlinkNode(dummy); xmlFreeNode(dummy); diff --git a/ext/soap/tests/bugs/bug39832.phpt b/ext/soap/tests/bugs/bug39832.phpt index f5742c709840..bc8e8e861ab3 100644 --- a/ext/soap/tests/bugs/bug39832.phpt +++ b/ext/soap/tests/bugs/bug39832.phpt @@ -26,4 +26,4 @@ $x->handle($HTTP_RAW_POST_DATA); ?> --EXPECT-- -SOAP-ENV:ServerSOAP-ERROR: Encoding: Violation of encoding rules +SOAP-ENV:ServerSOAP-ERROR: Encoding: Invalid value for type 'integer' diff --git a/ext/soap/tests/hexbin_odd_length.phpt b/ext/soap/tests/hexbin_odd_length.phpt index bfe84ab643db..cc1cdcbab1fd 100644 --- a/ext/soap/tests/hexbin_odd_length.phpt +++ b/ext/soap/tests/hexbin_odd_length.phpt @@ -34,4 +34,4 @@ try { } ?> --EXPECT-- -SOAP-ERROR: Encoding: Violation of encoding rules +SOAP-ERROR: Encoding: Type 'hexBinary' value must contain an even number of hexadecimal digits diff --git a/ext/soap/tests/scalar_error_messages.phpt b/ext/soap/tests/scalar_error_messages.phpt new file mode 100644 index 000000000000..fde4449d6d84 --- /dev/null +++ b/ext/soap/tests/scalar_error_messages.phpt @@ -0,0 +1,52 @@ +--TEST-- +SOAP reports specific scalar encoding errors +--EXTENSIONS-- +soap +--FILE-- +response; + } +} + +function soap_response(string $type, string $value): string { + return << + + + + $value + + + +XML; +} + +function run_case(string $label, string $type, string $value): void { + $client = new ScalarErrorClient(null, [ + 'location' => 'test://', + 'uri' => 'urn:test', + 'exceptions' => true, + ]); + $client->response = soap_response($type, $value); + + try { + $client->__soapCall('test', []); + } catch (SoapFault $e) { + echo $label, ': ', $e->faultstring, "\n"; + } +} + +run_case('double', 'xsd:double', 'abc'); +run_case('long', 'xsd:long', 'abc'); +run_case('base64Binary node', 'xsd:base64Binary', 'abc'); +?> +--EXPECT-- +double: SOAP-ERROR: Encoding: Invalid value for type 'double' +long: SOAP-ERROR: Encoding: Invalid value for type 'long' +base64Binary node: SOAP-ERROR: Encoding: Type 'base64Binary' value must contain a single text or CDATA node diff --git a/ext/soap/tests/soap12/T27.phpt b/ext/soap/tests/soap12/T27.phpt index 09591b7faebd..5bb6572e3df2 100644 --- a/ext/soap/tests/soap12/T27.phpt +++ b/ext/soap/tests/soap12/T27.phpt @@ -25,4 +25,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules +env:ReceiverSOAP-ERROR: Encoding: Type 'string' value must contain a single text or CDATA node diff --git a/ext/soap/tests/soap12/T58.phpt b/ext/soap/tests/soap12/T58.phpt index 69c20a4f8670..a8619d20b298 100644 --- a/ext/soap/tests/soap12/T58.phpt +++ b/ext/soap/tests/soap12/T58.phpt @@ -24,4 +24,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules +env:ReceiverSOAP-ERROR: Encoding: Type 'int' value must contain a single text or CDATA node From 1db6c840500192ecd26979a199068a23610cbc93 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 12:52:05 -0700 Subject: [PATCH 174/211] Add regression tests for `Reflection*::__toString()` with null bytes So that when the outputs are fixed the changes can be confirmed in tests --- .../ReflectionClassConstant_doc_comment.phpt | 44 ++++++++++++++++++ .../gh22681/ReflectionClass_doc_comment.phpt | 37 +++++++++++++++ .../gh22681/ReflectionConstant_name.phpt | 15 ++++++ .../ReflectionEnum_case_doc_comment.phpt | 46 +++++++++++++++++++ .../ReflectionExtension_ini_value.phpt | 23 ++++++++++ ...eflectionFunctionAbstract_doc_comment.phpt | 36 +++++++++++++++ .../ReflectionProperty_doc_comment.phpt | 44 ++++++++++++++++++ .../ReflectionProperty_dynamic_name.phpt | 37 +++++++++++++++ 8 files changed, 282 insertions(+) create mode 100644 ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionConstant_name.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt diff --git a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt new file mode 100644 index 000000000000..49d646a4a191 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClassConstant::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F +Constant [ public bool DEMO ] { 1 } +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [1] { + /** F + Constant [ public bool DEMO ] { 1 } + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt new file mode 100644 index 000000000000..c2da0400dcbd --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClass::__toString() +--FILE-- +getDocComment() ); + +?> +--EXPECTF-- +/** F +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt new file mode 100644 index 000000000000..a322596fa042 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionConstant::__toString() +--FILE-- +getName() ); + +?> +--EXPECTF-- +Constant [ bool F ] { 1 } +string(4) "F%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt new file mode 100644 index 000000000000..e76c0007c51c --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionEnum::__toString() +--FILE-- +getDocComment() ); +?> +--EXPECTF-- +Class [ final class Demo implements UnitEnum ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [1] { + /** F + Constant [ public Demo C ] { Object } + } + + - Static properties [0] { + } + + - Static methods [1] { + Method [ static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + } + + - Properties [1] { + Property [ public protected(set) readonly string $name ] + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt new file mode 100644 index 000000000000..42777ffd2fd2 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-22681: null bytes in INI values truncate ReflectionExtension::__toString() +--FILE-- +getINIEntries()['arg_separator.output'] ); + +?> +--EXPECTF-- +Entry [ arg_separator.output ] + Current = 'f' + Default = '&' + } + +string(4) "f%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt new file mode 100644 index 000000000000..b7673de6c567 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionFunctionAbstract::__toString() +--FILE-- +getDocComment() ); + +$r = new ReflectionMethod(Demo::class, 'demo'); +echo $r; +var_dump( $r->getDocComment() ); + +?> +--EXPECTF-- +/** F +Function [ function demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** F%0oo */" +/** B +Method [ public method demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** B%0ar */" diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt new file mode 100644 index 000000000000..4f6f7c875e17 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionProperty::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F +Property [ public $prop = NULL ] +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [1] { + /** F + Property [ public $prop = NULL ] + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt new file mode 100644 index 000000000000..921a738ea117 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionProperty::__toString() +--FILE-- + true]; +$r = new ReflectionProperty($obj, "F\0oo"); +echo $r; +var_dump( $r->getDocComment() ); + +echo new ReflectionObject($obj); + +?> +--EXPECT-- +Property [ public $F ] +bool(false) +Object of class [ class stdClass ] { + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Dynamic properties [1] { + Property [ public $F ] + } + + - Methods [0] { + } +} From 8706cd43f6fa9fae1058cedaf4caf46afcea0d63 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 13:14:49 -0700 Subject: [PATCH 175/211] GH-22681: avoid truncation on null bytes in `Reflection*::__toString()` Given that there is not an unchecked version of `smart_str_append_printf()`, to avoid compiler warnings from using `%S` to format `zend_string` pointers (which the smart string printing machinery supports, but printf does not support) in a few places the format specifier is declared in a variable rather than being specified directly in the `smart_str_append_printf()` call. --- ext/reflection/php_reflection.c | 65 +++++++++++++------ .../ReflectionClassConstant_doc_comment.phpt | 4 +- .../gh22681/ReflectionClass_doc_comment.phpt | 2 +- .../gh22681/ReflectionConstant_name.phpt | 2 +- .../ReflectionEnum_case_doc_comment.phpt | 2 +- .../ReflectionExtension_ini_value.phpt | 2 +- ...eflectionFunctionAbstract_doc_comment.phpt | 4 +- .../ReflectionProperty_doc_comment.phpt | 4 +- .../ReflectionProperty_dynamic_name.phpt | 6 +- 9 files changed, 59 insertions(+), 32 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 661b1eb1f3a4..0945c9574c62 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -308,9 +308,9 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{ } /* }}} */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent); +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent); static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent); -static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent); +static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent); static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent); static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent); @@ -324,7 +324,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* TBD: Repair indenting of doc comment (or is this to be done in the parser?) */ if (ce->doc_comment) { - smart_str_append_printf(str, "%s%s", indent, ZSTR_VAL(ce->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, ce->doc_comment); smart_str_appendc(str, '\n'); } @@ -494,7 +495,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const if (prop_name && ZSTR_LEN(prop_name) && ZSTR_VAL(prop_name)[0]) { /* skip all private and protected properties */ if (!zend_hash_exists(&ce->properties_info, prop_name)) { count++; - _property_string(&prop_str, NULL, ZSTR_VAL(prop_name), ZSTR_VAL(sub_indent)); + _property_string(&prop_str, NULL, prop_name, ZSTR_VAL(sub_indent)); } } } ZEND_HASH_FOREACH_END(); @@ -549,7 +550,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* }}} */ /* {{{ _const_string */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent) +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent) { const char *type = zend_zval_type_name(value); uint32_t flags = Z_CONSTANT_FLAGS_P(value); @@ -579,7 +580,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c smart_str_appends(str, type); smart_str_appendc(str, ' '); - smart_str_appends(str, name); + smart_str_append(str, name); smart_str_appends(str, " ] { "); if (Z_TYPE_P(value) == IS_ARRAY) { @@ -610,7 +611,9 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); if (c->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, c->doc_comment); + smart_str_appendc(str, '\n'); } smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, ZSTR_VAL(name)); @@ -827,9 +830,13 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent * swallowed, leading to an unaligned comment. */ if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->op_array.doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, fptr->op_array.doc_comment); + smart_str_appendc(str, '\n'); } else if (fptr->type == ZEND_INTERNAL_FUNCTION && fptr->internal_function.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->internal_function.doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, fptr->internal_function.doc_comment); + smart_str_appendc(str, '\n'); } smart_str_appendl(str, indent, strlen(indent)); @@ -939,14 +946,18 @@ static zval *property_get_default(zend_property_info *prop_info) { } /* {{{ _property_string */ -static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent) +static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent) { if (prop && prop->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(prop->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, prop->doc_comment); + smart_str_appendc(str, '\n'); } smart_str_append_printf(str, "%sProperty [ ", indent); if (!prop) { - smart_str_append_printf(str, " public $%s", prop_name); + ZEND_ASSERT(prop_name && "Properties without info must have a name provided"); + smart_str_appends(str, " public $"); + smart_str_append(str, prop_name); } else { if (prop->flags & ZEND_ACC_ABSTRACT) { smart_str_appends(str, "abstract "); @@ -989,11 +1000,15 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha smart_str_appendc(str, ' '); zend_string_release(type_str); } + smart_str_appendc(str, '$'); if (!prop_name) { const char *class_name; - zend_unmangle_property_name(prop->name, &class_name, &prop_name); + const char *prop_name_cstr; + zend_unmangle_property_name(prop->name, &class_name, &prop_name_cstr); + smart_str_appends(str, prop_name_cstr); + } else { + smart_str_append(str, prop_name); } - smart_str_append_printf(str, "$%s", prop_name); zval *default_value = property_get_default(prop); if (default_value && !Z_ISUNDEF_P(default_value)) { @@ -1031,9 +1046,21 @@ static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *st } smart_str_appends(str, "> ]\n"); - smart_str_append_printf(str, " %s Current = '%s'\n", indent, ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); + if (ini_entry->value) { + smart_str_append_printf(str, " %s Current = '", indent); + smart_str_append(str, ini_entry->value); + smart_str_appends(str, "'\n"); + } else { + smart_str_append_printf(str, " %s Current = ''\n", indent); + } if (ini_entry->modified) { - smart_str_append_printf(str, " %s Default = '%s'\n", indent, ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); + if (ini_entry->orig_value) { + smart_str_append_printf(str, " %s Default = '", indent); + smart_str_append(str, ini_entry->orig_value); + smart_str_appends(str, "'\n"); + } else { + smart_str_append_printf(str, " %s Default = ''\n", indent); + } } smart_str_append_printf(str, " %s}\n", indent); } @@ -1122,7 +1149,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) { if (ZEND_CONSTANT_MODULE_NUMBER(constant) == module->module_number) { - _const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, " "); + _const_string(&str_constants, constant->name, &constant->value, " "); num_constants++; } } ZEND_HASH_FOREACH_END(); @@ -5894,7 +5921,7 @@ ZEND_METHOD(ReflectionProperty, __toString) RETURN_THROWS(); } GET_REFLECTION_OBJECT_PTR(ref); - _property_string(&str, ref->prop, ZSTR_VAL(ref->unmangled_name), ""); + _property_string(&str, ref->prop, ref->unmangled_name, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ @@ -7955,7 +7982,7 @@ ZEND_METHOD(ReflectionConstant, __toString) } GET_REFLECTION_OBJECT_PTR(const_); - _const_string(&str, ZSTR_VAL(const_->name), &const_->value, ""); + _const_string(&str, const_->name, &const_->value, ""); RETURN_STR(smart_str_extract(&str)); } diff --git a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt index 49d646a4a191..5002f066ff69 100644 --- a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt @@ -19,14 +19,14 @@ echo new ReflectionClass(Demo::class); ?> --EXPECTF-- -/** F +/** F%0oo */ Constant [ public bool DEMO ] { 1 } string(11) "/** F%0oo */" Class [ class Demo ] { @@ %s(%d) : eval()'d code %d-%d - Constants [1] { - /** F + /** F%0oo */ Constant [ public bool DEMO ] { 1 } } diff --git a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt index c2da0400dcbd..fc34cd95b1e3 100644 --- a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt @@ -15,7 +15,7 @@ var_dump( $r->getDocComment() ); ?> --EXPECTF-- -/** F +/** F%0oo */ Class [ class Demo ] { @@ %s(%d) : eval()'d code %d-%d diff --git a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt index a322596fa042..c43397200c82 100644 --- a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt +++ b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt @@ -11,5 +11,5 @@ var_dump( $r->getName() ); ?> --EXPECTF-- -Constant [ bool F ] { 1 } +Constant [ bool F%0oo ] { 1 } string(4) "F%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt index e76c0007c51c..10afacade8f7 100644 --- a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -20,7 +20,7 @@ Class [ final class Demo implements UnitEnum ] { @@ %s(%d) : eval()'d code %d-%d - Constants [1] { - /** F + /** F%0oo */ Constant [ public Demo C ] { Object } } diff --git a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt index 42777ffd2fd2..f12088b3a9e0 100644 --- a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt +++ b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt @@ -16,7 +16,7 @@ var_dump( $r->getINIEntries()['arg_separator.output'] ); ?> --EXPECTF-- Entry [ arg_separator.output ] - Current = 'f' + Current = 'f%0oo' Default = '&' } diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt index b7673de6c567..fdd42903c3be 100644 --- a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt @@ -24,12 +24,12 @@ var_dump( $r->getDocComment() ); ?> --EXPECTF-- -/** F +/** F%0oo */ Function [ function demo ] { @@ %s(%d) : eval()'d code %d - %d } string(11) "/** F%0oo */" -/** B +/** B%0ar */ Method [ public method demo ] { @@ %s(%d) : eval()'d code %d - %d } diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt index 4f6f7c875e17..ca218216b2c3 100644 --- a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt @@ -19,7 +19,7 @@ echo new ReflectionClass(Demo::class); ?> --EXPECTF-- -/** F +/** F%0oo */ Property [ public $prop = NULL ] string(11) "/** F%0oo */" Class [ class Demo ] { @@ -35,7 +35,7 @@ Class [ class Demo ] { } - Properties [1] { - /** F + /** F%0oo */ Property [ public $prop = NULL ] } diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt index 921a738ea117..d8cecb0002aa 100644 --- a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt +++ b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt @@ -11,8 +11,8 @@ var_dump( $r->getDocComment() ); echo new ReflectionObject($obj); ?> ---EXPECT-- -Property [ public $F ] +--EXPECTF-- +Property [ public $F%0oo ] bool(false) Object of class [ class stdClass ] { @@ -29,7 +29,7 @@ Object of class [ class stdClass ] { } - Dynamic properties [1] { - Property [ public $F ] + Property [ public $F%0oo ] } - Methods [0] { From 4804d17a135ad6da22a8bd68c899cb5f188293d2 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 13 Jul 2026 14:12:32 -0700 Subject: [PATCH 176/211] NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index ee23c0ec7a18..e71a94ac12ec 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,8 @@ PHP NEWS dynamic properties shadowing a private parent property). (iliaal) . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value with null bytes truncates output). (DanielEScherzer) + . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes). + (DanielEScherzer) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between From c3236b922d1c37a1ad0c512db41ae859b197248a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 15:45:53 -0400 Subject: [PATCH 177/211] Install the SQLite ODBC driver in CI The ext/odbc and pdo_odbc regression tests connect through a real ODBC driver (Driver=SQLite3), which was never installed, so they skipped on every lane. libsqliteodbc registers the SQLite3 driver and supports Database=:memory:, so the tests run without a database server. Alpine has no such package, so those lanes keep skipping. Closes GH-22686 --- .github/actions/apt-x64/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/apt-x64/action.yml b/.github/actions/apt-x64/action.yml index 7ae0a88ff755..3e654ef9fdc4 100644 --- a/.github/actions/apt-x64/action.yml +++ b/.github/actions/apt-x64/action.yml @@ -63,6 +63,7 @@ runs: snmp-mibs-downloader \ freetds-dev \ unixodbc-dev \ + libsqliteodbc \ llvm \ clang \ dovecot-core \ From 4ba42f6b07c708ee1369cd454ecab25e87d83e86 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Tue, 14 Jul 2026 13:51:49 +0200 Subject: [PATCH 178/211] curl: Use correct format string in curl debug messages (#22723) Use `%p` for pointers, `%zu` for size_t. This only applies when PHP_CURL_DEBUG is enabled. If so, it solves compiler warnings, avoids undefined behavior and prints values correctly. --- ext/curl/interface.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index b7ec015abf62..c0286069776e 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -528,7 +528,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) #if PHP_CURL_DEBUG fprintf(stderr, "curl_write() called\n"); - fprintf(stderr, "data = %s, size = %d, nmemb = %d, ctx = %x\n", data, size, nmemb, ctx); + fprintf(stderr, "data = %s, size = %zu, nmemb = %zu, ctx = %p\n", data, size, nmemb, (void *) ctx); #endif switch (write_handler->method) { @@ -610,7 +610,7 @@ static int curl_progress(void *clientp, double dltotal, double dlnow, double ult #if PHP_CURL_DEBUG fprintf(stderr, "curl_progress() called\n"); - fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow); + fprintf(stderr, "clientp = %p, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow); #endif if (!ZEND_FCC_INITIALIZED(ch->handlers.progress)) { return rval; @@ -651,7 +651,7 @@ static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cu #if PHP_CURL_DEBUG fprintf(stderr, "curl_xferinfo() called\n"); - fprintf(stderr, "clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", clientp, dltotal, dlnow, ultotal, ulnow); + fprintf(stderr, "clientp = %p, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\n", clientp, dltotal, dlnow, ultotal, ulnow); #endif if (!ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) { return rval; @@ -746,7 +746,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, #if PHP_CURL_DEBUG fprintf(stderr, "curl_ssh_hostkeyfunction() called\n"); - fprintf(stderr, "clientp = %x, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); + fprintf(stderr, "clientp = %p, keytype = %d, key = %s, keylen = %zu\n", clientp, keytype, key, keylen); #endif zval args[4]; @@ -843,7 +843,7 @@ static int curl_seek(void *clientp, curl_off_t offset, int origin) #if PHP_CURL_DEBUG fprintf(stderr, "curl_seek() called\n"); - fprintf(stderr, "clientp = %x, offset = %ld, origin = %d\n", clientp, offset, origin); + fprintf(stderr, "clientp = %p, offset = %ld, origin = %d\n", clientp, offset, origin); #endif if (!ZEND_FCC_INITIALIZED(ch->handlers.seek)) { return rval; @@ -2801,7 +2801,7 @@ static void curl_free_obj(zend_object *object) php_curl *ch = curl_from_obj(object); #if PHP_CURL_DEBUG - fprintf(stderr, "DTOR CALLED, ch = %x\n", ch); + fprintf(stderr, "DTOR CALLED, ch = %p\n", (void *) ch); #endif if (!ch->cp) { From 7d1118b3c8f153843c9f005faf22dba01443a966 Mon Sep 17 00:00:00 2001 From: Joe Ferguson Date: Tue, 14 Jul 2026 13:04:28 +0000 Subject: [PATCH 179/211] [ci skip] Update NEWS for 8.6.0alpha3 --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6f2e69aa8294..84f532f19afe 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.6.0alpha2 +?? ??? ????, PHP 8.6.0alpha3 + + +16 Jul 2026, PHP 8.6.0alpha2 - Core: . Sync Boost.Context assembly with 1.91.0. (kn1g78) From 0fca39de812864a4262c1734143cafdd0efdd399 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 13 Jul 2026 14:30:19 -0700 Subject: [PATCH 180/211] Add regression tests for `ReflectionEnum::__toString()` with null bytes So that when the output is fixed the changes can be confirmed in tests --- .../gh22681/ReflectionEnum_backed_value.phpt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt new file mode 100644 index 000000000000..8131a3adafed --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt @@ -0,0 +1,61 @@ +--TEST-- +GH-22681: null bytes in backed value truncate ReflectionEnum::__toString() +--FILE-- +getBackingValue() ); +?> +--EXPECTF-- +Enum [ enum Demo: string implements UnitEnum, BackedEnum ] { + @@ %s %d-%d + + - Enum cases [1] { + Case DEMO = F + } + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [3] { + Method [ static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + + Method [ static public method from ] { + + - Parameters [1] { + Parameter #0 [ string|int $value ] + } + - Return [ static ] + } + + Method [ static public method tryFrom ] { + + - Parameters [1] { + Parameter #0 [ string|int $value ] + } + - Return [ ?static ] + } + } + + - Properties [2] { + Property [ public protected(set) readonly string $name ] + Property [ public protected(set) readonly string $value ] + } + + - Methods [0] { + } +} +string(4) "F%0oo" From 1b2c0abbdb333cbe350aff69904f3312c55c18f1 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 13 Jul 2026 14:34:10 -0700 Subject: [PATCH 181/211] GH-22681: avoid truncation on null bytes in `ReflectionEnum::__toString()` --- ext/reflection/php_reflection.c | 8 ++++++-- .../tests/gh22681/ReflectionEnum_backed_value.phpt | 2 +- .../tests/gh22681/ReflectionEnum_case_doc_comment.phpt | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a3e996b654c1..bf7fde62d499 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -677,7 +677,9 @@ static void _enum_case_string(smart_str *str, const zend_string *name, zend_clas } if (c->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, c->doc_comment); + smart_str_appendc(str, '\n'); } smart_str_append_printf(str, "%sCase %s", indent, ZSTR_VAL(name)); if (c->ce->enum_backing_type == IS_UNDEF) { @@ -691,7 +693,9 @@ static void _enum_case_string(smart_str *str, const zend_string *name, zend_clas zval *enum_val = zend_enum_fetch_case_value(Z_OBJ(c->value)); zend_string *tmp_value_str; zend_string *value_str = zval_get_tmp_string(enum_val, &tmp_value_str); - smart_str_append_printf(str, " = %s\n", ZSTR_VAL(value_str)); + smart_str_appends(str, " = "); + smart_str_append(str, value_str); + smart_str_appendc(str, '\n'); zend_tmp_string_release(tmp_value_str); } } diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt index 8131a3adafed..f28c39daa100 100644 --- a/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt +++ b/ext/reflection/tests/gh22681/ReflectionEnum_backed_value.phpt @@ -16,7 +16,7 @@ Enum [ enum Demo: string implements UnitEnum, BackedEnum ] { @@ %s %d-%d - Enum cases [1] { - Case DEMO = F + Case DEMO = F%0oo } - Constants [0] { diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt index 68bde0bed637..3945ad8639e7 100644 --- a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -20,7 +20,7 @@ Enum [ enum Demo implements UnitEnum ] { @@ %s(%d) : eval()'d code %d-%d - Enum cases [1] { - /** F + /** F%0oo */ Case C } From e2c975d4fcc2769cac8a6c94e5d5b634a11ffbfb Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 14 Jul 2026 06:39:29 -0700 Subject: [PATCH 182/211] Update NEWS This just missed the alpha2 tag so needs a new entry for alpha3 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 84f532f19afe..01ab6b2a97a1 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0alpha3 +- Reflection: + . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes). + (DanielEScherzer) 16 Jul 2026, PHP 8.6.0alpha2 From 3199f8932bac5e09d084e1c6743ae545431a8490 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Tue, 14 Jul 2026 23:41:38 +0800 Subject: [PATCH 183/211] [skip ci] UPGRADING: document several added functions and classes --- UPGRADING | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/UPGRADING b/UPGRADING index 74a5e066483f..a27ef2f8f4ac 100644 --- a/UPGRADING +++ b/UPGRADING @@ -393,17 +393,19 @@ PHP 8.6 UPGRADE NOTES 6. New Functions ======================================== +- Intl: + . grapheme_strrev() + RFC: https://wiki.php.net/rfc/grapheme_strrev + . Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue() + RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue + - Reflection: . ReflectionConstant::inNamespace() . ReflectionProperty::isReadable() and ReflectionProperty::isWritable() RFC: https://wiki.php.net/rfc/isreadable-iswriteable - . ReflectionParameter::getDocComment(). + . ReflectionParameter::getDocComment() RFC: https://wiki.php.net/rfc/parameter-doccomments -- Intl: - . grapheme_strrev() returns strrev for grapheme cluster unit. - RFC: https://wiki.php.net/rfc/grapheme_strrev - - mysqli: . Added mysqli::quote_string() and mysqli_quote_string(). RFC: https://wiki.php.net/rfc/mysqli_quote_string @@ -412,9 +414,15 @@ PHP 8.6 UPGRADE NOTES . clamp() returns the given value if in range, else return the nearest bound. RFC: https://wiki.php.net/rfc/clamp_v2 - . stream_last_errors() and stream_clear_errors(). + . stream_last_errors() and stream_clear_errors() RFC: https://wiki.php.net/rfc/stream_errors - . stream_socket_get_crypto_status(). + . stream_socket_get_crypto_status() + +- URI: + . Uri\Rfc3986\Uri::getUriType() and Uri\WhatWg\Url::isSpecialScheme() + RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection + . Uri\Rfc3986\Uri::getHostType() and Uri\WhatWg\Url::getHostType() + RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection - Zip: . ZipArchive::openString() @@ -424,6 +432,9 @@ PHP 8.6 UPGRADE NOTES 7. New Classes and Interfaces ======================================== +- Intl: + . IntlNumberRangeFormatter + - OpenSSL: . Openssl\OpensslException . Openssl\Session @@ -457,6 +468,10 @@ PHP 8.6 UPGRADE NOTES . Io\Poll\InvalidHandleException . StreamPollHandle +- URI: + . Uri\Rfc3986\UriBuilder + RFC: https://wiki.php.net/rfc/uri_followup#uri_building + ======================================== 8. Removed Extensions and SAPIs ======================================== From df5e361b66bb3642162225aae8c300cf13b909a2 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Tue, 14 Jul 2026 23:44:21 +0800 Subject: [PATCH 184/211] [skip ci] UPGRADING: sort extensions alphabetically --- UPGRADING | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UPGRADING b/UPGRADING index a27ef2f8f4ac..57a1f6a0ebad 100644 --- a/UPGRADING +++ b/UPGRADING @@ -399,6 +399,10 @@ PHP 8.6 UPGRADE NOTES . Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue() RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue +- mysqli: + . Added mysqli::quote_string() and mysqli_quote_string(). + RFC: https://wiki.php.net/rfc/mysqli_quote_string + - Reflection: . ReflectionConstant::inNamespace() . ReflectionProperty::isReadable() and ReflectionProperty::isWritable() @@ -406,10 +410,6 @@ PHP 8.6 UPGRADE NOTES . ReflectionParameter::getDocComment() RFC: https://wiki.php.net/rfc/parameter-doccomments -- mysqli: - . Added mysqli::quote_string() and mysqli_quote_string(). - RFC: https://wiki.php.net/rfc/mysqli_quote_string - - Standard: . clamp() returns the given value if in range, else return the nearest bound. From 103c84ff4c1bb4cdc4b078c306c2c8b8b584f1b1 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 07:57:25 -0400 Subject: [PATCH 185/211] Fix GH-22667: pdo_odbc heap over-read on oversized column value odbc_stmt_describe() binds a colsize+1 buffer for short columns and stores that capacity in datalen, but odbc_stmt_get_col() built the result string from the driver-reported fetched_len. A conforming driver truncates an over-long value into the buffer yet reports the full length, so ZVAL_STRINGL_FAST over-read past the allocation and returned adjacent heap to userland. Clamp fetched_len to the bound capacity, guarded by !is_long so the long-column SQLGetData path (whose fixed buffer is unrelated to datalen) is left untouched. Fixes GH-22667 Closes GH-22672 --- NEWS | 2 ++ ext/pdo_odbc/odbc_stmt.c | 9 ++++++++- ext/pdo_odbc/tests/config.inc | 3 +++ ext/pdo_odbc/tests/gh22667.phpt | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_odbc/tests/config.inc create mode 100644 ext/pdo_odbc/tests/gh22667.phpt diff --git a/NEWS b/NEWS index e71a94ac12ec..9bcb68e52712 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,8 @@ PHP NEWS - PDO_ODBC: . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN carries no credentials). (iliaal) + . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the + driver-reported display size). (iliaal) - Phar: . Fixed inconsistent handling of the magic ".phar" directory. Paths such as diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index c114f721a7a7..5b9104895854 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -744,7 +744,14 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo return 1; } else if (C->fetched_len >= 0) { /* it was stored perfectly */ - ZVAL_STRINGL_FAST(result, C->data, C->fetched_len); + SQLLEN data_len = C->fetched_len; + if (!C->is_long) { + SQLLEN max_len = C->is_unicode ? (SQLLEN)C->datalen + 1 : (SQLLEN)C->datalen; + if (data_len > max_len) { + data_len = max_len; + } + } + ZVAL_STRINGL_FAST(result, C->data, data_len); if (C->is_unicode) { goto unicode_conv; } diff --git a/ext/pdo_odbc/tests/config.inc b/ext/pdo_odbc/tests/config.inc new file mode 100644 index 000000000000..185602bb7ed6 --- /dev/null +++ b/ext/pdo_odbc/tests/config.inc @@ -0,0 +1,3 @@ + +--FILE-- +query("SELECT printf('%.*c', 4096, 'A') AS data"); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +$s = $row['data']; + +echo "clamped to buffer: "; var_dump(strlen($s) < 4096); +echo "only value bytes: "; var_dump(strlen($s) === substr_count($s, 'A')); +?> +--EXPECT-- +clamped to buffer: bool(true) +only value bytes: bool(true) From 0fef3c3895fa80f8fc593164b2e1cb59e664d9a6 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 15 Jul 2026 02:14:51 +0800 Subject: [PATCH 186/211] CI: fix unstable test \ext\sockets\tests\socket_recvfrom_afpacket_no_port.phpt (#22724) Fix flaky CI test: \ext\sockets\tests\socket_recvfrom_afpacket_no_port.phpt failed run example: https://github.com/php/php-src/actions/runs/29266463698/job/86873055452 --- .../socket_recvfrom_afpacket_no_port.phpt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ext/sockets/tests/socket_recvfrom_afpacket_no_port.phpt b/ext/sockets/tests/socket_recvfrom_afpacket_no_port.phpt index a66398c3a0e1..fd6c4c3a4daf 100644 --- a/ext/sockets/tests/socket_recvfrom_afpacket_no_port.phpt +++ b/ext/sockets/tests/socket_recvfrom_afpacket_no_port.phpt @@ -29,10 +29,30 @@ $ethertype = pack("n", 0x9000); $payload = "no port test"; $frame = str_pad($dst_mac . $src_mac . $ethertype . $payload, 60, "\x00"); +// ETH_P_ALL sockets on loopback can observe unrelated localhost traffic from +// the parallel test runner. Read until we see our own frame, then validate the +// address returned by recvfrom() without the optional port argument. +function recv_matching(Socket $s, string $header, int $maxlen = 65536, ?string &$addr = null, &$bytes = null): string|false { + socket_set_nonblock($s); + $deadline = microtime(true) + 5.0; + while (microtime(true) < $deadline) { + $recvBytes = @socket_recvfrom($s, $buf, $maxlen, 0, $addr); + if ($recvBytes !== false && is_string($buf) && str_starts_with($buf, $header)) { + $bytes = $recvBytes; + return $buf; + } + if ($recvBytes === false) { + usleep(1000); + } + } + return false; +} + socket_sendto($s_send, $frame, strlen($frame), 0, "lo", 1); // recvfrom without the optional 6th argument (port/ifindex). -$bytes = socket_recvfrom($s_recv, $buf, 65536, 0, $addr); +$bytes = 0; +$buf = recv_matching($s_recv, $dst_mac . $src_mac . $ethertype, 65536, $addr, $bytes); var_dump($bytes >= 60); var_dump($addr === 'lo'); From 207dd1d23f75654f0350d55b246cee40e451645a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 14 Jul 2026 14:23:34 -0400 Subject: [PATCH 187/211] Fix GH-22480: phpdbg use-after-free re-watching a watched variable (#22493) phpdbg_add_watch_element() frees the passed element and returns the existing one when the variable is already watched, but several callers kept using the freed pointer, so re-watching a variable was a use-after-free in more places than the two reported. Report the duplicate back through the parse callback and register nothing, and reject a recursive watch's hashtable element that collides with an existing watch, at creation and recreation, rather than freeing a still-referenced one. A recursive watch over an already-array-watched variable now watches only the root and skips the shared hashtable. Running the tests under ASAN exposed a second, latent use-after-free at shutdown: the watch tables are module-scoped but hold request-pool memory, so under the tracked allocator freeing a watched variable during executor teardown re-queues an element after the last drain, tracked_free_all frees the pool, and phpdbg_destroy_watchpoints reads it at module shutdown. Tear the watch state down in a post_deactivate handler, which runs after the executor frees values but before the memory manager is torn down. Dropping watch_006's xfail surfaced a separate pre-existing bug on large-page platforms: the fault handler rechecks every watchpoint on the faulting page, so on 16 KB pages a value-only write to an element can fault the page holding a watched hashtable's own struct and run the recursive rescan, which re-reported existing elements as freshly added. Only run that rescan when the element count actually changed. Fixes GH-22480 --- NEWS | 4 + sapi/phpdbg/phpdbg.c | 11 +- sapi/phpdbg/phpdbg_watch.c | 212 +++++++++++++++++++++++++------ sapi/phpdbg/phpdbg_watch.h | 1 + sapi/phpdbg/tests/gh22480.phpt | 34 +++++ sapi/phpdbg/tests/gh22480_2.phpt | 30 +++++ sapi/phpdbg/tests/watch_006.phpt | 7 - 7 files changed, 253 insertions(+), 46 deletions(-) create mode 100644 sapi/phpdbg/tests/gh22480.phpt create mode 100644 sapi/phpdbg/tests/gh22480_2.phpt diff --git a/NEWS b/NEWS index 018f454f57ba..c7bee9b7e958 100644 --- a/NEWS +++ b/NEWS @@ -340,6 +340,10 @@ PHP NEWS . Mark Phar::buildFromIterator() base directory argument as a path. (ndossche) +- phpdbg: + . Fixed GH-22480 (Use-after-free when re-watching an already-watched + variable). (iliaal) + - Posix: . Added validity check to the flags argument for posix_access(). (arshidkv12) diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 3c0d5e836dd0..8cdc1aee9d48 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -239,6 +239,13 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ return SUCCESS; } /* }}} */ +static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(phpdbg) /* {{{ */ +{ + phpdbg_release_watch_elements(); + + return SUCCESS; +} /* }}} */ + /* {{{ Attempt to set the execution context for phpdbg If the execution context was set previously it is returned If the execution context was not set previously boolean true is returned @@ -681,7 +688,9 @@ static zend_module_entry sapi_phpdbg_module_entry = { PHP_RSHUTDOWN(phpdbg), NULL, PHPDBG_VERSION, - STANDARD_MODULE_PROPERTIES + NO_MODULE_GLOBALS, + ZEND_MODULE_POST_ZEND_DEACTIVATE_N(phpdbg), + STANDARD_MODULE_PROPERTIES_EX }; static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */ diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c index b6d5e543a19c..ed6e6dfc70b9 100644 --- a/sapi/phpdbg/phpdbg_watch.c +++ b/sapi/phpdbg/phpdbg_watch.c @@ -501,8 +501,39 @@ void phpdbg_free_watch_element(phpdbg_watch_element *element); void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch); void phpdbg_watch_parent_ht(phpdbg_watch_element *element); -phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdbg_watch_element *element) { +static bool phpdbg_watch_element_collides(void *addr, phpdbg_watchtype type, phpdbg_watch_element *element) { + phpdbg_watch_element *old; + phpdbg_btree_result *res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) addr); + + if (res) { + phpdbg_watchpoint_t *watch = res->ptr; + if (watch->type == type && (old = zend_hash_find_ptr(&watch->elements, element->str)) && old != element) { + return true; + } + } + + return (old = zend_hash_find_ptr(&PHPDBG_G(watch_recreation), element->str)) && old != element; +} + +static bool phpdbg_ht_watch_element_collides(zval *zv, phpdbg_watch_element *element) { + HashTable *ht = HT_FROM_ZVP(zv); + + if (!ht) { + return false; + } + + return phpdbg_watch_element_collides(HT_WATCH_OFFSET + (char *) ht, WATCH_ON_HASHTABLE, element); +} + +static bool phpdbg_bucket_watch_element_collides(zval *zv, phpdbg_watch_element *element) { + return phpdbg_watch_element_collides((Bucket *) zv, WATCH_ON_BUCKET, element); +} + +phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdbg_watch_element *element, bool *is_new) { phpdbg_btree_result *res; + if (is_new) { + *is_new = true; + } if ((res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr)) == NULL) { phpdbg_watchpoint_t *mem = emalloc(sizeof(*mem)); *mem = *watch; @@ -520,6 +551,9 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb if (element != old_element) { phpdbg_free_watch_element(element); } + if (is_new) { + *is_new = false; + } return old_element; } } @@ -534,25 +568,35 @@ phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdb return element; } -phpdbg_watch_element *phpdbg_add_bucket_watch_element(Bucket *bucket, phpdbg_watch_element *element) { +phpdbg_watch_element *phpdbg_add_bucket_watch_element(Bucket *bucket, phpdbg_watch_element *element, bool *is_new) { phpdbg_watchpoint_t watch; phpdbg_set_bucket_watchpoint(bucket, &watch); - element = phpdbg_add_watch_element(&watch, element); - phpdbg_watch_parent_ht(element); - return element; + bool added_new; + phpdbg_watch_element *added = phpdbg_add_watch_element(&watch, element, &added_new); + if (added_new) { + phpdbg_watch_parent_ht(added); + } + if (is_new) { + *is_new = added_new; + } + return added; } -phpdbg_watch_element *phpdbg_add_ht_watch_element(zval *zv, phpdbg_watch_element *element) { +phpdbg_watch_element *phpdbg_add_ht_watch_element(zval *zv, phpdbg_watch_element *element, bool *is_new) { phpdbg_watchpoint_t watch; HashTable *ht = HT_FROM_ZVP(zv); + if (is_new) { + *is_new = false; + } + if (!ht) { return NULL; } element->flags |= Z_TYPE_P(zv) == IS_ARRAY ? PHPDBG_WATCH_ARRAY : PHPDBG_WATCH_OBJECT; phpdbg_set_ht_watchpoint(ht, &watch); - return phpdbg_add_watch_element(&watch, element); + return phpdbg_add_watch_element(&watch, element, is_new); } bool phpdbg_is_recursively_watched(void *ptr, phpdbg_watch_element *element) { @@ -588,8 +632,15 @@ void phpdbg_add_recursive_watch_from_ht(phpdbg_watch_element *element, zend_long child->parent = element; child->child = NULL; child->parent_container = HT_WATCH_HT(element->watch); - zend_hash_add_ptr(&element->child_container, child->str, child); - phpdbg_add_bucket_watch_element((Bucket *) zv, child); + if (phpdbg_bucket_watch_element_collides(zv, child)) { + phpdbg_free_watch_element(child); + return; + } + bool added_new; + phpdbg_add_bucket_watch_element((Bucket *) zv, child, &added_new); + if (added_new) { + zend_hash_add_ptr(&element->child_container, child->str, child); + } } void phpdbg_recurse_watch_element(phpdbg_watch_element *element) { @@ -627,8 +678,13 @@ void phpdbg_recurse_watch_element(phpdbg_watch_element *element) { child->child = NULL; element->child = child; } + if (phpdbg_ht_watch_element_collides(zv, child)) { + phpdbg_free_watch_element(child); + element->child = NULL; + return; + } zend_hash_init(&child->child_container, 8, NULL, NULL, 0); - phpdbg_add_ht_watch_element(zv, child); + phpdbg_add_ht_watch_element(zv, child, NULL); } else if (zend_hash_num_elements(&element->child_container) == 0) { zend_string *str; zend_long idx; @@ -727,7 +783,10 @@ bool phpdbg_try_re_adding_watch_element(zval *parent, phpdbg_watch_element *elem phpdbg_print_watch_diff(WATCH_ON_HASHTABLE, element->str, oldPtr, htPtr); } - phpdbg_add_ht_watch_element(parent, element); + if ((element->flags & PHPDBG_WATCH_RECURSIVE) && phpdbg_ht_watch_element_collides(parent, element)) { + return false; + } + element = phpdbg_add_ht_watch_element(parent, element, NULL); } else if ((zv = zend_symtable_find(ht, element->name_in_parent))) { if (element->flags & PHPDBG_WATCH_IMPLICIT) { zval *next = zv; @@ -746,9 +805,11 @@ bool phpdbg_try_re_adding_watch_element(zval *parent, phpdbg_watch_element *elem phpdbg_print_watch_diff(WATCH_ON_ZVAL, element->str, &element->backup.zv, zv); } + if ((element->flags & PHPDBG_WATCH_RECURSIVE) && phpdbg_bucket_watch_element_collides(zv, element)) { + return false; + } element->parent_container = ht; - phpdbg_add_bucket_watch_element((Bucket *) zv, element); - phpdbg_watch_parent_ht(element); + element = phpdbg_add_bucket_watch_element((Bucket *) zv, element, NULL); } else { return false; } @@ -1010,7 +1071,8 @@ void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch) { zend_string *name = NULL; void *comparePtr; - if (watch->type == WATCH_ON_HASHTABLE) { + if (watch->type == WATCH_ON_HASHTABLE + && phpdbg_check_watch_diff(WATCH_ON_HASHTABLE, (char *) &watch->backup.ht + HT_WATCH_OFFSET, watch->addr.ptr)) { phpdbg_watch_element *element; zend_string *str; zend_long idx; @@ -1248,49 +1310,80 @@ void phpdbg_list_watchpoints(void) { } ZEND_HASH_FOREACH_END(); } -static int phpdbg_create_simple_watchpoint(zval *zv, phpdbg_watch_element *element) { - element->flags = PHPDBG_WATCH_SIMPLE; - phpdbg_add_bucket_watch_element((Bucket *) zv, element); - return SUCCESS; +typedef enum { + PHPDBG_WATCH_ADD_OK, + PHPDBG_WATCH_ADD_FAILED, + PHPDBG_WATCH_ADD_DUPLICATE, +} phpdbg_watch_add_result; + +static phpdbg_watch_add_result phpdbg_create_simple_watchpoint(zval *zv, phpdbg_watch_element **element) { + phpdbg_watch_element *added; + (*element)->flags = PHPDBG_WATCH_SIMPLE; + bool added_new; + added = phpdbg_add_bucket_watch_element((Bucket *) zv, *element, &added_new); + if (!added_new) { + *element = added; + return PHPDBG_WATCH_ADD_DUPLICATE; + } + return PHPDBG_WATCH_ADD_OK; } -static int phpdbg_create_array_watchpoint(zval *zv, phpdbg_watch_element *element) { - phpdbg_watch_element *new; +static phpdbg_watch_add_result phpdbg_create_array_watchpoint(zval *zv, phpdbg_watch_element **element_ptr) { + phpdbg_watch_element *element = *element_ptr; + phpdbg_watch_element *new, *added; zend_string *str; zval *orig_zv = zv; ZVAL_DEREF(zv); if (Z_TYPE_P(zv) != IS_ARRAY && Z_TYPE_P(zv) != IS_OBJECT) { - return FAILURE; + return PHPDBG_WATCH_ADD_FAILED; } - new = ecalloc(1, sizeof(phpdbg_watch_element)); - str = strpprintf(0, "%.*s[]", (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str)); zend_string_release(element->str); element->str = str; element->flags = PHPDBG_WATCH_IMPLICIT; - phpdbg_add_bucket_watch_element((Bucket *) orig_zv, element); - element->child = new; + bool added_new; + added = phpdbg_add_bucket_watch_element((Bucket *) orig_zv, element, &added_new); + if (!added_new) { + *element_ptr = added; + return PHPDBG_WATCH_ADD_DUPLICATE; + } + element = added; + new = ecalloc(1, sizeof(phpdbg_watch_element)); new->flags = PHPDBG_WATCH_SIMPLE; new->str = zend_string_copy(str); new->parent = element; - phpdbg_add_ht_watch_element(zv, new); - return SUCCESS; + added = phpdbg_add_ht_watch_element(zv, new, &added_new); + if (added != NULL && !added_new) { + phpdbg_clean_watch_element(element); + phpdbg_free_watch_element(element); + *element_ptr = added; + return PHPDBG_WATCH_ADD_DUPLICATE; + } + element->child = new; + *element_ptr = element; + return PHPDBG_WATCH_ADD_OK; } -static int phpdbg_create_recursive_watchpoint(zval *zv, phpdbg_watch_element *element) { - element->flags = PHPDBG_WATCH_RECURSIVE | PHPDBG_WATCH_RECURSIVE_ROOT; - element->child = NULL; - phpdbg_add_bucket_watch_element((Bucket *) zv, element); - return SUCCESS; +static phpdbg_watch_add_result phpdbg_create_recursive_watchpoint(zval *zv, phpdbg_watch_element **element) { + phpdbg_watch_element *added; + (*element)->flags = PHPDBG_WATCH_RECURSIVE | PHPDBG_WATCH_RECURSIVE_ROOT; + (*element)->child = NULL; + bool added_new; + added = phpdbg_add_bucket_watch_element((Bucket *) zv, *element, &added_new); + if (!added_new) { + *element = added; + return PHPDBG_WATCH_ADD_DUPLICATE; + } + return PHPDBG_WATCH_ADD_OK; } -typedef struct { int (*callback)(zval *zv, phpdbg_watch_element *); zend_string *str; } phpdbg_watch_parse_struct; +typedef struct { phpdbg_watch_add_result (*callback)(zval *zv, phpdbg_watch_element **); zend_string *str; } phpdbg_watch_parse_struct; static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key, size_t keylen, HashTable *parent, zval *zv, phpdbg_watch_parse_struct *info) { - int ret; + int ret = SUCCESS; phpdbg_watch_element *element = ecalloc(1, sizeof(phpdbg_watch_element)); element->str = zend_string_init(name, namelen, 0); element->name_in_parent = zend_string_init(key, keylen, 0); @@ -1298,13 +1391,25 @@ static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key element->parent = PHPDBG_G(watch_tmp); element->child = NULL; - ret = info->callback(zv, element); + phpdbg_watch_add_result result = info->callback(zv, &element); efree(name); efree(key); - if (ret != SUCCESS) { + if (result == PHPDBG_WATCH_ADD_FAILED) { phpdbg_remove_watch_element(element); + ret = FAILURE; + } else if (result == PHPDBG_WATCH_ADD_DUPLICATE) { + phpdbg_notice("%.*s is already being watched", (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str)); + while (PHPDBG_G(watch_tmp) && PHPDBG_G(watch_tmp)->child == NULL) { + phpdbg_watch_element *parent = PHPDBG_G(watch_tmp)->parent; + phpdbg_clean_watch_element(PHPDBG_G(watch_tmp)); + phpdbg_free_watch_element(PHPDBG_G(watch_tmp)); + if (parent) { + parent->child = NULL; + } + PHPDBG_G(watch_tmp) = parent; + } } else { if (PHPDBG_G(watch_tmp)) { PHPDBG_G(watch_tmp)->child = element; @@ -1346,7 +1451,7 @@ static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, s element->name_in_parent = zend_string_init(key, keylen, 0); element->parent_container = parent; element->parent = PHPDBG_G(watch_tmp); - element = phpdbg_add_bucket_watch_element((Bucket *) zv, element); + element = phpdbg_add_bucket_watch_element((Bucket *) zv, element, NULL); efree(name); efree(key); @@ -1359,7 +1464,7 @@ static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, s return SUCCESS; } -static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(zval *, phpdbg_watch_element *)) { +static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, phpdbg_watch_add_result (*callback)(zval *, phpdbg_watch_element **)) { zend_class_entry *scope = zend_get_executed_scope(); phpdbg_watch_parse_struct info; int ret; @@ -1529,6 +1634,37 @@ void phpdbg_destroy_watchpoints(void) { free(PHPDBG_G(watchlist_mem_backup)); } +void phpdbg_release_watch_elements(void) { + phpdbg_watch_element *element; + uint32_t guard; + + ZEND_HASH_MAP_FOREACH_PTR(&PHPDBG_G(watch_recreation), element) { + phpdbg_automatic_dequeue_free(element); + } ZEND_HASH_FOREACH_END(); + zend_hash_clean(&PHPDBG_G(watch_recreation)); + + guard = zend_hash_num_elements(&PHPDBG_G(watch_elements)) + 1; + while (zend_hash_num_elements(&PHPDBG_G(watch_elements)) > 0 && guard-- > 0) { + ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watch_elements), element) { + phpdbg_remove_watch_element(element); + break; + } ZEND_HASH_FOREACH_END(); + } + zend_hash_clean(&PHPDBG_G(watch_recreation)); + + phpdbg_btree_clean(&PHPDBG_G(watchpoint_tree)); + phpdbg_btree_clean(&PHPDBG_G(watch_HashTables)); + + zend_hash_destroy(&PHPDBG_G(watch_elements)); + zend_hash_init(&PHPDBG_G(watch_elements), 8, NULL, NULL, 0); + zend_hash_destroy(&PHPDBG_G(watch_recreation)); + zend_hash_init(&PHPDBG_G(watch_recreation), 8, NULL, NULL, 0); + zend_hash_destroy(&PHPDBG_G(watch_free)); + zend_hash_init(&PHPDBG_G(watch_free), 8, NULL, NULL, 0); + zend_hash_destroy(&PHPDBG_G(watch_collisions)); + zend_hash_init(&PHPDBG_G(watch_collisions), 8, NULL, NULL, 0); +} + void phpdbg_purge_watchpoint_tree(void) { phpdbg_btree_position pos; phpdbg_btree_result *res; diff --git a/sapi/phpdbg/phpdbg_watch.h b/sapi/phpdbg/phpdbg_watch.h index 380140fd9f1f..e87dd77f0543 100644 --- a/sapi/phpdbg/phpdbg_watch.h +++ b/sapi/phpdbg/phpdbg_watch.h @@ -110,6 +110,7 @@ typedef struct { void phpdbg_setup_watchpoints(void); void phpdbg_destroy_watchpoints(void); +void phpdbg_release_watch_elements(void); void phpdbg_purge_watchpoint_tree(void); #ifndef _WIN32 diff --git a/sapi/phpdbg/tests/gh22480.phpt b/sapi/phpdbg/tests/gh22480.phpt new file mode 100644 index 000000000000..16559a75f27a --- /dev/null +++ b/sapi/phpdbg/tests/gh22480.phpt @@ -0,0 +1,34 @@ +--TEST-- +GH-22480 (Use-after-free when re-watching an already-watched variable) +--PHPDBG-- +b 6 +r +w a $a +w a $a +c + +q +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at %s:6] +prompt> [Breakpoint #0 at %s:6, hits: 1] +>00006: $a[0] = 2; + 00007: + 00008: $a = [0 => 3, 1 => 4]; +prompt> [Added watchpoint #0 for $a[]] +prompt> [$a[] is already being watched] +prompt> [Breaking on watchpoint $a[]] +1 elements were added to the array +>00009: +prompt> [$a[] has been removed, removing watchpoint] +[Script ended normally] +prompt> +--FILE-- + 3, 1 => 4]; diff --git a/sapi/phpdbg/tests/gh22480_2.phpt b/sapi/phpdbg/tests/gh22480_2.phpt new file mode 100644 index 000000000000..aa6e13dbf8d5 --- /dev/null +++ b/sapi/phpdbg/tests/gh22480_2.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-22480 (Use-after-free re-watching a sub-path of a recursive watchpoint) +--INI-- +opcache.optimization_level=0 +--PHPDBG-- +b 5 +r +w r $a +w $a[x] +c +q +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at %s:5] +prompt> [Breakpoint #0 at %s:5, hits: 1] +>00005: $a['x'] = 2; + 00006: +prompt> [Added watchpoint #0 for $a[]] +prompt> [$a[x] is already being watched] +prompt> [Breaking on watchpoint $a] +Old value%s +New value: Array ([x] => 2) +>00006: +prompt> [$a has been removed, removing watchpoint recursively] +--FILE-- + 1]; + +$a['x'] = 2; diff --git a/sapi/phpdbg/tests/watch_006.phpt b/sapi/phpdbg/tests/watch_006.phpt index c5f53a37510c..bf38b8eff1fb 100644 --- a/sapi/phpdbg/tests/watch_006.phpt +++ b/sapi/phpdbg/tests/watch_006.phpt @@ -1,12 +1,5 @@ --TEST-- Test multiple watch elements pointing to the same watchpoint ---SKIPIF-- - --PHPDBG-- b 4 r From df773093effb5f0479634b254753b6d42c645c35 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 12 Jul 2026 23:47:06 +0100 Subject: [PATCH 188/211] GH-22617: avoid null byte truncation of persistent stream keys Abstract unix domain socket addresses begin with a null byte, but the persistent stream list is keyed by a NUL-terminated string. The key was truncated at that null byte, so distinct abstract sockets collapsed onto the same persistent resource in p(f)sockopen() and stream_socket_client(). Escape null bytes (and backslashes, to stay unambiguous) when building the persistent hash key so the full address is preserved. The escaping helper lives in ext/standard rather than the public streams header, since only p(f)sockopen() and stream_socket_client() need it. Fix #22617 close GH-22704 --- NEWS | 4 +++ ext/standard/fsock.c | 6 ++-- ext/standard/streamsfuncs.c | 5 ++- ext/standard/tests/streams/gh22617.phpt | 45 +++++++++++++++++++++++++ main/streams/php_streams_int.h | 4 +++ main/streams/streams.c | 21 ++++++++++++ 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 ext/standard/tests/streams/gh22617.phpt diff --git a/NEWS b/NEWS index 9bcb68e52712..d4f7af6f7692 100644 --- a/NEWS +++ b/NEWS @@ -94,6 +94,10 @@ PHP NEWS . Fixed bug GH-22678 (Use-after-free in array_multisort() when the comparator mutates the array being sorted). (azchin, iliaal) +- Streams: + . Fixed bug GH-22617 (persistent stream keys truncated at null bytes, causing + distinct abstract unix domain sockets to share a resource). (David Carlier) + - Zip: . Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex() could crash after overwriting an entry and resetting its inherited diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 2b9e00a57554..c8e6ed08e4c0 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -22,6 +22,7 @@ #include #include "php_network.h" #include "file.h" +#include "streams/php_streams_int.h" static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, const char *host, size_t host_len, zend_long port) @@ -84,8 +85,9 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, - host_len, port); + zend_string *escaped = php_stream_escape_persistent_key(host, host_len); + spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, ZSTR_VAL(escaped), port); + zend_string_release_ex(escaped, false); } if (port > 0) { diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 1929075b60b7..391a72b5d9e0 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -23,6 +23,7 @@ #include "streamsfuncs.h" #include "php_network.h" #include "php_string.h" +#include "streams/php_streams_int.h" #ifdef HAVE_UNISTD_H #include #endif @@ -130,7 +131,9 @@ PHP_FUNCTION(stream_socket_client) context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_STREAM_CLIENT_PERSISTENT) { - spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(host)); + zend_string *escaped = php_stream_escape_persistent_key(ZSTR_VAL(host), ZSTR_LEN(host)); + spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(escaped)); + zend_string_release_ex(escaped, false); } /* prepare the timeout value for use */ diff --git a/ext/standard/tests/streams/gh22617.phpt b/ext/standard/tests/streams/gh22617.phpt new file mode 100644 index 000000000000..83dbd832e652 --- /dev/null +++ b/ext/standard/tests/streams/gh22617.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-22617: Persistent abstract unix domain sockets resolved to wrong resource +--CREDITS-- +Roysten +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(17) "persistent stream" +bool(true) +string(17) "persistent stream" +bool(true) diff --git a/main/streams/php_streams_int.h b/main/streams/php_streams_int.h index 7580088fba31..f9dda9a52ff7 100644 --- a/main/streams/php_streams_int.h +++ b/main/streams/php_streams_int.h @@ -62,3 +62,7 @@ * any other function that expects standard modes and you allow non-standard * ones. result should be a char[5]. */ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result); + +/* Escapes NUL and backslash bytes so a host containing them cannot truncate or + * collide the persistent stream hash key. */ +zend_string *php_stream_escape_persistent_key(const char *host, size_t hostlen); diff --git a/main/streams/streams.c b/main/streams/streams.c index 6feb8b7c01b5..368de1a64774 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -140,6 +140,27 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * /* }}} */ +zend_string *php_stream_escape_persistent_key(const char *host, size_t hostlen) +{ + zend_string *escaped = zend_string_safe_alloc(hostlen, 2, 0, 0); + char *ptr = ZSTR_VAL(escaped); + for (size_t i = 0; i < hostlen; i++) { + if (host[i] == '\0') { + *ptr++ = '\\'; + *ptr++ = '0'; + } else if (host[i] == '\\') { + *ptr++ = '\\'; + *ptr++ = '\\'; + } else { + *ptr++ = host[i]; + } + } + *ptr = '\0'; + ZSTR_LEN(escaped) = ptr - ZSTR_VAL(escaped); + + return escaped; +} + static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper) { if (!FG(wrapper_errors)) { From 8e4daeff7a399847abb78cce89522e90f82cabc0 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 07:34:19 -0400 Subject: [PATCH 189/211] Fix GH-22668: odbc heap over-read on oversized column value ext/odbc bound a displaysize+1 buffer per column but built the result string from the driver-reported vallen. A conforming driver truncates an over-long value into the buffer yet reports the full length, so the fetch over-read past the allocation and returned adjacent heap bytes to userland. Store the bound capacity in odbc_result_value and clamp the string length to it at the three bound-buffer fetch sites (odbc_fetch_array/object, odbc_fetch_into, odbc_result); the SQLGetData long-column paths stay bounded by longreadlen. Fixes GH-22668 Closes GH-22670 --- NEWS | 4 ++++ ext/odbc/php_odbc.c | 21 ++++++++++++++++++--- ext/odbc/php_odbc_includes.h | 1 + ext/odbc/tests/config.inc | 2 ++ ext/odbc/tests/gh22668.phpt | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 ext/odbc/tests/gh22668.phpt diff --git a/NEWS b/NEWS index d4f7af6f7692..a9ad00c2be65 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,10 @@ PHP NEWS . Fixed IntlChar methods leaving stale global error state after successful calls. (Xuyang Zhang) +- ODBC: + . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the + driver-reported display size). (iliaal) + - OpenSSL: . Fixed timeout for supplemental read at end of a blocking stream in SSL stream wrapper. (ilutov) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 692841be10e1..16617078d3c1 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -680,6 +680,7 @@ void odbc_bindcols(odbc_result *result) for(i = 0; i < result->numcols; i++) { charextraalloc = 0; + result->values[i].value_max_len = 0; colfieldid = SQL_COLUMN_DISPLAY_SIZE; rc = PHP_ODBC_SQLCOLATTRIBUTE(result->stmt, (SQLUSMALLINT)(i+1), PHP_ODBC_SQL_DESC_NAME, @@ -706,6 +707,7 @@ void odbc_bindcols(odbc_result *result) #ifdef HAVE_ADABAS case SQL_TIMESTAMP: result->values[i].value = (char *)emalloc(27); + result->values[i].value_max_len = 26; SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value, 27, &result->values[i].vallen); break; @@ -775,6 +777,7 @@ void odbc_bindcols(odbc_result *result) displaysize *= 4; } result->values[i].value = (char *)emalloc(displaysize + 1); + result->values[i].value_max_len = displaysize; rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value, displaysize + 1, &result->values[i].vallen); break; @@ -1492,7 +1495,11 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) ZVAL_FALSE(&tmp); break; } - ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen); + SQLLEN str_len = result->values[i].vallen; + if (str_len > result->values[i].value_max_len) { + str_len = result->values[i].value_max_len; + } + ZVAL_STRINGL(&tmp, result->values[i].value, str_len); break; } @@ -1660,7 +1667,11 @@ PHP_FUNCTION(odbc_fetch_into) ZVAL_FALSE(&tmp); break; } - ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen); + SQLLEN str_len = result->values[i].vallen; + if (str_len > result->values[i].value_max_len) { + str_len = result->values[i].value_max_len; + } + ZVAL_STRINGL(&tmp, result->values[i].value, str_len); break; } zend_hash_index_update(Z_ARRVAL_P(pv_res_arr), i, &tmp); @@ -1910,7 +1921,11 @@ PHP_FUNCTION(odbc_result) php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", field_ind + 1); RETURN_FALSE; } else { - RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen); + SQLLEN str_len = result->values[field_ind].vallen; + if (str_len > result->values[field_ind].value_max_len) { + str_len = result->values[field_ind].value_max_len; + } + RETURN_STRINGL(result->values[field_ind].value, str_len); } break; } diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h index 59404ecd266b..1c267c9e6b42 100644 --- a/ext/odbc/php_odbc_includes.h +++ b/ext/odbc/php_odbc_includes.h @@ -205,6 +205,7 @@ typedef struct odbc_result_value { char name[256]; char *value; SQLLEN vallen; + SQLLEN value_max_len; SQLLEN coltype; } odbc_result_value; diff --git a/ext/odbc/tests/config.inc b/ext/odbc/tests/config.inc index 8d74feeb6ffb..a0f24f301558 100644 --- a/ext/odbc/tests/config.inc +++ b/ext/odbc/tests/config.inc @@ -1,5 +1,7 @@ +--FILE-- + +--EXPECT-- +clamped to buffer: bool(true) +only value bytes: bool(true) From a7539ff8344c7290b3e0e275e2b095a572d84b28 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 08:21:24 -0400 Subject: [PATCH 190/211] Fix GH-22666: pdo_odbc heap overflow on oversized output param odbc_stmt_param_hook() sizes an output-param buffer from the declared maxlen but then copies the runtime value into it using the value's own length, and at fetch-back hands ZVAL_STRINGL the driver-reported length. Both trust a length that can exceed the buffer: the input copy overflows the heap (controlled write), and on a truncated OUTPUT value the driver reports the full length via the shared indicator, so the readback over-reads. Record the bound capacity in a new outbuflen field and clamp the copy and the readback to it. LOB output is rejected at bind, so those copy sites stay unreachable. Fixes GH-22666 Closes GH-22674 --- NEWS | 2 ++ ext/pdo_odbc/odbc_stmt.c | 11 +++++++++++ ext/pdo_odbc/php_pdo_odbc_int.h | 1 + ext/pdo_odbc/tests/gh22666.phpt | 30 ++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 ext/pdo_odbc/tests/gh22666.phpt diff --git a/NEWS b/NEWS index a9ad00c2be65..76be38bcaa70 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,8 @@ PHP NEWS carries no credentials). (iliaal) . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the driver-reported display size). (iliaal) + . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is + longer than the declared maxlen). (iliaal) - Phar: . Fixed inconsistent handling of the magic ".phar" directory. Paths such as diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 5b9104895854..8940143da09c 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -360,6 +360,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p param->driver_data = P; P->len = 0; /* is re-populated each EXEC_PRE */ + P->outbuflen = 0; P->outbuf = NULL; P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype); @@ -384,6 +385,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p P->len *= 2; } P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); + P->outbuflen = P->len; } } @@ -481,10 +483,16 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p case PDO_ODBC_CONV_FAIL: case PDO_ODBC_CONV_NOT_REQUIRED: P->len = Z_STRLEN_P(parameter); + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len); break; case PDO_ODBC_CONV_OK: P->len = ulen; + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } memcpy(P->outbuf, S->convbuf, P->len); break; } @@ -506,6 +514,9 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p zval_ptr_dtor(parameter); if (P->len >= 0) { + if (P->len > P->outbuflen) { + P->len = P->outbuflen; + } ZVAL_STRINGL(parameter, P->outbuf, P->len); switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { case PDO_ODBC_CONV_FAIL: diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h index 473d70ff7076..ea93c58b2b60 100644 --- a/ext/pdo_odbc/php_pdo_odbc_int.h +++ b/ext/pdo_odbc/php_pdo_odbc_int.h @@ -153,6 +153,7 @@ typedef struct { typedef struct { SQLLEN len; + SQLLEN outbuflen; SQLSMALLINT paramtype; char *outbuf; unsigned is_unicode:1; diff --git a/ext/pdo_odbc/tests/gh22666.phpt b/ext/pdo_odbc/tests/gh22666.phpt new file mode 100644 index 000000000000..b6ba2265b9be --- /dev/null +++ b/ext/pdo_odbc/tests/gh22666.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-22666 (Heap buffer overflow when an output param value exceeds its maxlen) +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +prepare('SELECT ?'); +$stmt->bindParam(1, $value, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 4); +$stmt->execute(); + +echo "bounded to maxlen: "; var_dump($value); +?> +--EXPECT-- +bounded to maxlen: string(4) "AAAA" From 345b88deebf59574023020f5c1feebabfe0767f3 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 10 Jul 2026 08:32:15 -0400 Subject: [PATCH 191/211] Fix GH-22665: pdo_odbc OOB write on oversized diagnostic length pdo_odbc_error() writes the NUL terminator at einfo->last_err_msg[errmsgsize], where errmsgsize is the diagnostic length SQLGetDiagRec reports. On a truncated message (SQL_SUCCESS_WITH_INFO) the driver reports the full untruncated length, which can reach or exceed the 512-byte buffer, so the terminator lands out of bounds in the adjacent last_error field. Clamp the index to sizeof(last_err_msg) - 1; the size_t cast also folds a negative driver-reported length into the same guard. Fixes GH-22665 Closes GH-22675 --- NEWS | 2 ++ ext/pdo_odbc/odbc_driver.c | 2 ++ ext/pdo_odbc/tests/gh22665.phpt | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 ext/pdo_odbc/tests/gh22665.phpt diff --git a/NEWS b/NEWS index 76be38bcaa70..70dd9bce1c48 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,8 @@ PHP NEWS driver-reported display size). (iliaal) . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is longer than the declared maxlen). (iliaal) + . Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a + diagnostic message length beyond the error buffer). (iliaal) - Phar: . Fixed inconsistent handling of the magic ".phar" directory. Paths such as diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index 2cdac30223c0..3bb028ed6314 100644 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -90,6 +90,8 @@ void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { errmsgsize = 0; + } else if ((size_t) errmsgsize >= sizeof(einfo->last_err_msg)) { + errmsgsize = sizeof(einfo->last_err_msg) - 1; } einfo->last_err_msg[errmsgsize] = '\0'; diff --git a/ext/pdo_odbc/tests/gh22665.phpt b/ext/pdo_odbc/tests/gh22665.phpt new file mode 100644 index 000000000000..d6edc62288e8 --- /dev/null +++ b/ext/pdo_odbc/tests/gh22665.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-22665 (OOB write in pdo_odbc_error when the driver reports a long message) +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- + PDO::ERRMODE_SILENT, +]); + +// A failing query with a long identifier makes the driver report a diagnostic +// message length >= the fixed last_err_msg buffer. The terminator write must +// stay inside the buffer. +$pdo->query('SELECT * FROM "' . str_repeat('A', 4096) . '"'); +$info = $pdo->errorInfo(); + +echo "sqlstate: ", $info[0], "\n"; +echo "done\n"; +?> +--EXPECT-- +sqlstate: HY000 +done From 665bab296553f8d8c224bb9a9518062f1ca819b7 Mon Sep 17 00:00:00 2001 From: Volker Dusch Date: Wed, 15 Jul 2026 02:18:14 +0200 Subject: [PATCH 192/211] PHP-8.5 is now for PHP 8.5.10-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 7e138fcc2bb1..878cf9d5d12e 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.5.9 +?? ??? ????, PHP 8.5.10 + + +30 Jul 2026, PHP 8.5.9 - Core: . Fixed bug GH-22290 (AST pretty printing does not correctly handle strings diff --git a/Zend/zend.h b/Zend/zend.h index 40b3d7825a72..1ae16ee0d6a0 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.5.9-dev" +#define ZEND_VERSION "4.5.10-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 3ac956d908de..b8e19cc167c7 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.5.9-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.5.10-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 2b4f5a437201..f3eae43fad45 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 5 -#define PHP_RELEASE_VERSION 9 +#define PHP_RELEASE_VERSION 10 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.5.9-dev" -#define PHP_VERSION_ID 80509 +#define PHP_VERSION "8.5.10-dev" +#define PHP_VERSION_ID 80510 From 923814caf1a316fcdf9f64c04345049c36364e8f Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Wed, 15 Jul 2026 11:49:58 +0900 Subject: [PATCH 193/211] PHP-8.4 is now for PHP 8.4.25-dev --- NEWS | 3 +++ Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 70dd9bce1c48..96bd6d1e9222 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 + +30 Jul 2026, PHP 8.4.24 + - Calendar: . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with INT_MAX year). (arshidkv12) diff --git a/Zend/zend.h b/Zend/zend.h index ded79b992d61..d0e46881d989 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.24-dev" +#define ZEND_VERSION "4.4.25-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 07f83f84951d..33608b4ae94e 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.24-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.25-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 73a7d5c29935..f6516cc08b0b 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 24 +#define PHP_RELEASE_VERSION 25 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.24-dev" -#define PHP_VERSION_ID 80424 +#define PHP_VERSION "8.4.25-dev" +#define PHP_VERSION_ID 80425 From a6f85c3bfd000f8eea2c9b0d03d0af081580a800 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 15 Jul 2026 14:28:09 +0200 Subject: [PATCH 194/211] Re-enable JIT for ZTS builds on Apple Silicon For __APPLE__ && __aarch64__ && ZTS, this moves the JIT buffer out of the OPcache shared memory and creates a dedicated mapping. Background: OPcache JIT is currently disabled for ZTS builds on Apple Silicon. The JIT buffer normally lives at the end of OPcache's MAP_SHARED | MAP_ANON allocation. In ZTS, one thread may generate code while another executes existing code, so the buffer must remain executable while it is writable. Apple Silicon rejects RWX anonymous mappings with EPERM. Apple's supported solution is MAP_JIT with pthread_jit_write_protect_np(), which provides per-thread write protection, but macOS rejects MAP_JIT | MAP_SHARED with EINVAL. The existing combined OPcache/JIT mapping won't work on Apple Silicon. Fixes GH-13400 Closes GH-22712 --- NEWS | 3 ++ UPGRADING | 3 ++ ext/opcache/ZendAccelerator.c | 25 ++++++---- ext/opcache/config.m4 | 7 ++- ext/opcache/jit/zend_jit.c | 73 ++++++++++++++++++------------ ext/opcache/jit/zend_jit.h | 7 +++ ext/opcache/tests/jit/gh13400.phpt | 55 ++++++++++++++++++++++ 7 files changed, 131 insertions(+), 42 deletions(-) create mode 100644 ext/opcache/tests/jit/gh13400.phpt diff --git a/NEWS b/NEWS index 6b1e857a8708..5bb78b98c4e7 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the driver-reported display size). (iliaal) +- Opcache: + . Re-enable JIT for ZTS builds on Apple Silicon. (realFlowControl) + - PDO_ODBC: . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the driver-reported display size). (iliaal) diff --git a/UPGRADING b/UPGRADING index 57a1f6a0ebad..26e50b215f77 100644 --- a/UPGRADING +++ b/UPGRADING @@ -486,6 +486,9 @@ PHP 8.6 UPGRADE NOTES - mysqli . Added new constant MYSQLI_OPT_COMPRESS. +- Opcache + . JIT is now supported for ZTS builds on Apple Silicon. + ======================================== 10. New Global Constants ======================================== diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index cedc21c376a4..ac6d6787a6d6 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3259,8 +3259,9 @@ static zend_result accel_post_startup(void) file_cache_only = ZCG(accel_directives).file_cache_only; if (!file_cache_only) { size_t shm_size = ZCG(accel_directives).memory_consumption; -#ifdef HAVE_JIT size_t jit_size = 0; +#ifdef HAVE_JIT + size_t jit_buffer_size = 0; bool reattached = false; if (JIT_G(enabled) && JIT_G(buffer_size) @@ -3272,15 +3273,16 @@ static zend_result accel_post_startup(void) zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size."); abort(); } - jit_size = JIT_G(buffer_size); - jit_size = ZEND_MM_ALIGNED_SIZE_EX(jit_size, page_size); + jit_buffer_size = JIT_G(buffer_size); + jit_buffer_size = ZEND_MM_ALIGNED_SIZE_EX(jit_buffer_size, page_size); +# ifndef ZEND_JIT_USE_APPLE_MAP_JIT + jit_size = jit_buffer_size; shm_size += jit_size; +# endif } +#endif switch (zend_shared_alloc_startup(shm_size, jit_size)) { -#else - switch (zend_shared_alloc_startup(shm_size, 0)) { -#endif case ALLOC_SUCCESS: if (zend_accel_init_shm() == FAILURE) { accel_startup_ok = false; @@ -3334,10 +3336,15 @@ static zend_result accel_post_startup(void) if (JIT_G(buffer_size) == 0) { JIT_G(enabled) = false; JIT_G(on) = false; - } else if (!ZSMMG(reserved)) { - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!"); } else { - zend_jit_startup(ZSMMG(reserved), jit_size, reattached); +# ifdef ZEND_JIT_USE_APPLE_MAP_JIT + zend_jit_startup(NULL, jit_buffer_size, reattached); +# else + if (!ZSMMG(reserved)) { + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!"); + } + zend_jit_startup(ZSMMG(reserved), jit_buffer_size, reattached); +# endif zend_jit_startup_ok = true; } } diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 3798499a4511..2a1ac4e332fc 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -31,10 +31,9 @@ AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ PHP_OPCACHE_JIT=no ]) - if test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"; then - AC_MSG_WARN([JIT not supported on Apple Silicon with ZTS]) - PHP_OPCACHE_JIT=no - fi + AS_IF([test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"], + [AC_CHECK_FUNC([pthread_jit_write_protect_np], [], + [AC_MSG_ERROR([OPcache JIT on Apple Silicon with ZTS requires pthread_jit_write_protect_np()])])]) ]) AS_VAR_IF([PHP_OPCACHE_JIT], [yes], [ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fbbfab6b243c..6f550f0cf36c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -40,8 +40,10 @@ #include "jit/zend_jit_internal.h" -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT +#include #include +#include #endif #ifdef ZTS @@ -77,9 +79,6 @@ int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT]; const zend_op *zend_jit_halt_op = NULL; const zend_op *zend_jit_interrupt_op = NULL; -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP -static int zend_write_protect = 1; -#endif static void *dasm_buf = NULL; static void *dasm_end = NULL; @@ -3517,17 +3516,14 @@ int zend_jit_script(zend_script *script) void zend_jit_unprotect(void) { -#ifdef HAVE_MPROTECT +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT + pthread_jit_write_protect_np(0); +#elif defined(HAVE_MPROTECT) if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { int opts = PROT_READ | PROT_WRITE; -#ifdef ZTS -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP - if (zend_write_protect) { - pthread_jit_write_protect_np(0); - } -#endif +# ifdef ZTS opts |= PROT_EXEC; -#endif +# endif if (mprotect(dasm_buf, dasm_size, opts) != 0) { fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); } @@ -3535,11 +3531,11 @@ void zend_jit_unprotect(void) #elif defined(_WIN32) if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { DWORD old, new; -#ifdef ZTS +# ifdef ZTS new = PAGE_EXECUTE_READWRITE; -#else +# else new = PAGE_READWRITE; -#endif +# endif if (!VirtualProtect(dasm_buf, dasm_size, new, &old)) { DWORD err = GetLastError(); char *msg = php_win32_error_to_msg(err); @@ -3552,13 +3548,10 @@ void zend_jit_unprotect(void) void zend_jit_protect(void) { -#ifdef HAVE_MPROTECT +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT + pthread_jit_write_protect_np(1); +#elif defined(HAVE_MPROTECT) if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP - if (zend_write_protect) { - pthread_jit_write_protect_np(1); - } -#endif if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) { fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); } @@ -3780,20 +3773,36 @@ void zend_jit_startup(void *buf, size_t size, bool reattached) zend_jit_interrupt_op = zend_get_interrupt_op(); zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME); -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP - zend_write_protect = pthread_jit_write_protect_supported_np(); +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT + buf = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); + if (buf == MAP_FAILED) { + int error = errno; + zend_accel_error_noreturn(ACCEL_LOG_FATAL, + "Unable to allocate %zu bytes for JIT buffer using MAP_JIT: %s (%d)", + size, strerror(error), error); + } + if (minherit(buf, size, VM_INHERIT_SHARE) != 0) { + int error = errno; + munmap(buf, size); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, + "Unable to share JIT buffer across fork using minherit(): %s (%d)", + strerror(error), error); + } + if (!pthread_jit_write_protect_supported_np()) { + munmap(buf, size); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, + "Apple Silicon ZTS JIT requires pthread_jit_write_protect_np() support"); + } #endif dasm_buf = buf; dasm_size = size; dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2); -#ifdef HAVE_MPROTECT -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP - if (zend_write_protect) { - pthread_jit_write_protect_np(1); - } -#endif +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT + pthread_jit_write_protect_np(1); +#elif defined(HAVE_MPROTECT) if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); @@ -3874,6 +3883,12 @@ void zend_jit_shutdown(void) zend_jit_trace_free_caches(&jit_globals); #endif +#ifdef ZEND_JIT_USE_APPLE_MAP_JIT + if (dasm_buf != NULL) { + munmap(dasm_buf, dasm_size); + } +#endif + /* Reset global pointers to prevent use-after-free in `zend_jit_status()` * after gracefully restarting Apache with mod_php, see: * https://github.com/php/php-src/pull/19212 */ diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index 2671ddd23e2d..c4080d86bc6d 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -17,6 +17,13 @@ #ifndef HAVE_JIT_H #define HAVE_JIT_H +#if defined(__APPLE__) && defined(__aarch64__) && defined(ZTS) +# ifndef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP +# error "Apple Silicon ZTS JIT requires pthread_jit_write_protect_np()" +# endif +# define ZEND_JIT_USE_APPLE_MAP_JIT 1 +#endif + #if defined(__x86_64__) || defined(i386) || defined(ZEND_WIN32) # define ZEND_JIT_TARGET_X86 1 # define ZEND_JIT_TARGET_ARM64 0 diff --git a/ext/opcache/tests/jit/gh13400.phpt b/ext/opcache/tests/jit/gh13400.phpt new file mode 100644 index 000000000000..db3d7b755761 --- /dev/null +++ b/ext/opcache/tests/jit/gh13400.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-13400: JIT code generated after fork is visible to the parent +--DESCRIPTION-- +OPcache metadata and generated JIT code are shared by forked workers, so the +JIT mapping and its allocation pointer must remain shared after fork. This test +records the available JIT space before forking and makes only the child call a +function often enough to generate code. The parent then verifies that it sees +the reduced free space and can execute the generated function. + +Without shared inheritance, a private JIT mapping becomes copy-on-write in the +child. The parent's free-space check would print bool(false), while shared +OPcache metadata could point the parent at code bytes that exist only in the +child and cause a crash instead of printing int(42). A startup-only test would +not detect this failure in fork-based SAPIs such as FPM or Apache. +--EXTENSIONS-- +opcache +pcntl +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=tracing +opcache.jit_buffer_size=16M +opcache.jit_hot_func=1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +int(42) From e2ea940fc95393452b2d8d52ea268368bfb43afe Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Wed, 15 Jul 2026 20:40:19 +0800 Subject: [PATCH 195/211] ext/gmp: Fix GMP operator RHS overflow for GMP values (#22656) ```php string(1) "1" } object(GMP)#2 (1) { ["num"]=> string(1) "2" } ``` This PR fix the overflow by rejecting large gmp objects in these calculation (throws ValueError) --- NEWS | 4 ++ ext/gmp/gmp.c | 46 ++++++++++++------- .../tests/gmp_operator_rhs_gmp_overflow.phpt | 33 +++++++++++++ 3 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt diff --git a/NEWS b/NEWS index 5bb78b98c4e7..cb10b3be0b6d 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0alpha3 +- GMP: + . Fixed GMP power and shift operators to reject GMP right operands outside + the unsigned long range instead of silently truncating them. (Weilin Du) + - ODBC: . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the driver-reported display size). (iliaal) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 59209c4e1be3..b05706e48297 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -327,8 +327,17 @@ static zend_result binop_operator_helper(gmp_binary_op_t gmp_op, zval *return_va typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong); +static void gmp_shift_operator_range_error(uint8_t opcode) { + zend_throw_error( + zend_ce_value_error, "%s must be between 0 and %lu", + opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX + ); +} + static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) { zend_long shift = 0; + gmp_ulong shift_ui = 0; + bool have_shift_ui = false; if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { if (UNEXPECTED(!IS_GMP(op2))) { @@ -349,31 +358,36 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val goto typeof_op_failure; } } else { - // TODO We shouldn't cast the GMP object to int here - shift = zval_get_long(op2); + mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2); + if (!mpz_fits_ulong_p(gmpnum_shift)) { + gmp_shift_operator_range_error(opcode); + return FAILURE; + } + shift_ui = (gmp_ulong) mpz_get_ui(gmpnum_shift); + have_shift_ui = true; } } else { shift = Z_LVAL_P(op2); } - if (shift < 0 || shift > ULONG_MAX) { - zend_throw_error( - zend_ce_value_error, "%s must be between 0 and %lu", - opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX - ); - return FAILURE; - } else { - mpz_ptr gmpnum_op, gmpnum_result; - - if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) { - goto typeof_op_failure; + if (!have_shift_ui) { + if (shift < 0 || shift > ULONG_MAX) { + gmp_shift_operator_range_error(opcode); + return FAILURE; } + shift_ui = (gmp_ulong) shift; + } - INIT_GMP_RETVAL(gmpnum_result); - op(gmpnum_result, gmpnum_op, (gmp_ulong) shift); - return SUCCESS; + mpz_ptr gmpnum_op, gmpnum_result; + + if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) { + goto typeof_op_failure; } + INIT_GMP_RETVAL(gmpnum_result); + op(gmpnum_result, gmpnum_op, shift_ui); + return SUCCESS; + typeof_op_failure: ; /* Returning FAILURE without throwing an exception would emit the * Unsupported operand types: GMP OP TypeOfOp2 diff --git a/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt b/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt new file mode 100644 index 000000000000..e8b6139ace0f --- /dev/null +++ b/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt @@ -0,0 +1,33 @@ +--TEST-- +GMP operator right operand rejects values outside the unsigned long range +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), PHP_EOL; +} + +try { + var_dump(gmp_init(2) << $too_large); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(gmp_init(2) >> $too_large); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +echo "Done\n"; +?> +--EXPECTF-- +Exponent must be between 0 and %d +Shift must be between 0 and %d +Shift must be between 0 and %d +Done From 7f4960657c184f1e280bc4e5b6f38e112c83f1ea Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 15 Jul 2026 16:50:46 +0100 Subject: [PATCH 196/211] standard/file.c: use normal docref function for E_WARNINGs in popen() (#22746) Now that arguments in warnings can be configured to always appear it is no longer necessary to use `php_error_docref2()`. --- ext/standard/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/file.c b/ext/standard/file.c index 0bb00355744f..fd6f578e4ef0 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -822,7 +822,7 @@ PHP_FUNCTION(popen) fp = VCWD_POPEN(command, posix_mode); if (!fp) { - php_error_docref2(NULL, command, posix_mode, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); efree(posix_mode); RETURN_FALSE; } @@ -831,7 +831,7 @@ PHP_FUNCTION(popen) stream = php_stream_fopen_from_pipe(fp, mode); if (stream == NULL) { - php_error_docref2(NULL, command, mode, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); RETVAL_FALSE; } else { php_stream_to_zval(stream, return_value); From 19860880d234da95362d0f41d185d81cc722f529 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 15 Jul 2026 16:51:03 +0100 Subject: [PATCH 197/211] exif: change usage of php_error_docref1() to php_error_docref() (#22747) As arguments to functions are now handled globally via an INI setting. --- ext/exif/exif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 31030e12fbf1..810ce30f38d0 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -4938,7 +4938,7 @@ PHP_FUNCTION(exif_read_data) exif_discard_imageinfo(&ImageInfo); #ifdef EXIF_DEBUG - php_error_docref1(NULL, (Z_TYPE_P(stream) == IS_RESOURCE ? "" : Z_STRVAL_P(stream)), E_NOTICE, "Done"); + php_error_docref(NULL, E_NOTICE, "Done"); #endif } /* }}} */ @@ -5026,7 +5026,7 @@ PHP_FUNCTION(exif_thumbnail) exif_discard_imageinfo(&ImageInfo); #ifdef EXIF_DEBUG - php_error_docref1(NULL, (Z_TYPE_P(stream) == IS_RESOURCE ? "" : Z_STRVAL_P(stream)), E_NOTICE, "Done"); + php_error_docref(NULL, E_NOTICE, "Done"); #endif } /* }}} */ From 345b2a640f7c59e34d209a45ef9a4657a47fa0a2 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 16 Jul 2026 01:48:55 +0800 Subject: [PATCH 198/211] ext/exif: Fix EXIF warning format specifiers for size_t values (#22750) Fix EXIF warning format specifiers for size_t values Several EXIF warning messages printed `size_t` values using `%d` or `%X`, which does not match the argument type passed through varargs. Use `%zu` / `%zX` for size and offset values to avoid undefined behavior and make the diagnostics portable across platforms. No behavior change is intended apart from the corrected warning formatting. --- ext/exif/exif.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 810ce30f38d0..62dca4f5db43 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -2215,7 +2215,7 @@ static void exif_iif_add_value(image_info_type *image_info, int section_index, c switch (format) { case TAG_FMT_STRING: if (length > value_len) { - exif_error_docref("exif_iif_add_value" EXIFERR_CC, image_info, E_WARNING, "length > value_len: %d > %zu", length, value_len); + exif_error_docref("exif_iif_add_value" EXIFERR_CC, image_info, E_WARNING, "length > value_len: %zu > %zu", length, value_len); value = NULL; } if (value) { @@ -2245,7 +2245,7 @@ static void exif_iif_add_value(image_info_type *image_info, int section_index, c ZEND_FALLTHROUGH; case TAG_FMT_UNDEFINED: if (length > value_len) { - exif_error_docref("exif_iif_add_value" EXIFERR_CC, image_info, E_WARNING, "length > value_len: %d > %zu", length, value_len); + exif_error_docref("exif_iif_add_value" EXIFERR_CC, image_info, E_WARNING, "length > value_len: %zu > %zu", length, value_len); value = NULL; } if (value) { @@ -3304,7 +3304,7 @@ static bool exif_process_IFD_TAG_impl(image_info_type *ImageInfo, char *dir_entr * relative to the start of the TIFF header in APP1 section. */ // TODO: Shouldn't we also be taking "displacement" into account here? if (byte_count > ImageInfo->FileSize || offset_val>ImageInfo->FileSize-byte_count || (ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_II && ImageInfo->FileType!=IMAGE_FILETYPE_TIFF_MM && ImageInfo->FileType!=IMAGE_FILETYPE_JPEG)) { - exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal pointer offset(x%04X + x%04X = x%04X > x%04X)", tag, exif_get_tagname_debug(tag, tag_table), offset_val, byte_count, offset_val+byte_count, ImageInfo->FileSize); + exif_error_docref("exif_read_data#error_ifd" EXIFERR_CC, ImageInfo, E_WARNING, "Process tag(x%04X=%s): Illegal pointer offset(x%04zX + x%04zX = x%04zX > x%04zX)", tag, exif_get_tagname_debug(tag, tag_table), offset_val, byte_count, offset_val+byte_count, ImageInfo->FileSize); return false; } if (byte_count>sizeof(cbuf)) { @@ -3326,7 +3326,7 @@ static bool exif_process_IFD_TAG_impl(image_info_type *ImageInfo, char *dir_entr size_t fgot = php_stream_tell(ImageInfo->infile); if (fgot!=displacement+offset_val) { EFREE_IF(outside); - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x%08X", fgot, displacement+offset_val); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08zX != 0x%08zX", fgot, displacement+offset_val); return false; } fgot = exif_read_from_stream_file_looped(ImageInfo->infile, value_ptr, byte_count); @@ -3866,7 +3866,7 @@ static bool exif_scan_JPEG_header(image_info_type *ImageInfo) got = exif_read_from_stream_file_looped(ImageInfo->infile, (char*)(Data+2), itemlen-2); /* Read the whole section. */ if (got != itemlen-2) { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error reading from file: got=x%04X(=%d) != itemlen-2=x%04X(=%d)", got, got, itemlen-2, itemlen-2); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error reading from file: got=x%04zX(=%zu) != itemlen-2=x%04zX(=%zu)", got, got, itemlen-2, itemlen-2); return false; } @@ -4150,7 +4150,7 @@ static bool exif_process_IFD_in_TIFF_impl(image_info_type *ImageInfo, size_t dir if (ImageInfo->FileSize >= ImageInfo->file.list[sn].size && ImageInfo->FileSize - ImageInfo->file.list[sn].size >= dir_offset) { if (ifd_size > dir_size) { if (ImageInfo->FileSize < ifd_size || dir_offset > ImageInfo->FileSize - ifd_size) { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD(x%04X + x%04X)", ImageInfo->FileSize, dir_offset, ifd_size); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04zX) less than size of IFD(x%04zX + x%04zX)", ImageInfo->FileSize, dir_offset, ifd_size); return false; } if (exif_file_sections_realloc(ImageInfo, sn, ifd_size)) { @@ -4266,15 +4266,15 @@ static bool exif_process_IFD_in_TIFF_impl(image_info_type *ImageInfo, size_t dir } return true; } else { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD(x%04X)", ImageInfo->FileSize, dir_offset+ImageInfo->file.list[sn].size); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04zX) less than size of IFD(x%04zX)", ImageInfo->FileSize, dir_offset+ImageInfo->file.list[sn].size); return false; } } else { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than size of IFD dir(x%04X)", ImageInfo->FileSize, dir_offset+dir_size); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04zX) less than size of IFD dir(x%04zX)", ImageInfo->FileSize, dir_offset+dir_size); return false; } } else { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04X) less than start of IFD dir(x%04X)", ImageInfo->FileSize, dir_offset+2); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Error in TIFF: filesize(x%04zX) less than start of IFD dir(x%04zX)", ImageInfo->FileSize, dir_offset+2); return false; } } @@ -4507,7 +4507,7 @@ static bool exif_scan_FILE_header(image_info_type *ImageInfo) ImageInfo->FileType = IMAGE_FILETYPE_UNKNOWN; if (UNEXPECTED(ImageInfo->FileSize < 2)) { - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File too small (%d)", ImageInfo->FileSize); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File too small (%zu)", ImageInfo->FileSize); return false; } From 0f78dbaab2c183c210d223fd1b153c0bf8525298 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 15 Jul 2026 19:33:48 +0100 Subject: [PATCH 199/211] streams: use built-in argument handling for warnings instead of 2 param functions (#22749) Since all warnings can now display the arguments of the call, the stream layer can just piggyback on-top of it instead of the additional complexity of handling it. This is only a first step as there is still the 1 param variant that needs to be converted. --- ext/standard/tests/file/rename_variation.phpt | 2 +- .../tests/file/rename_variation12.phpt | 9 ++++--- .../tests/file/rename_variation13.phpt | 25 +++++++++++-------- .../tests/file/rename_variation5.phpt | 8 +++--- .../tests/file/rename_variation8.phpt | 4 +-- main/streams/php_stream_errors.h | 15 ----------- main/streams/plain_wrapper.c | 18 ++++++------- main/streams/stream_errors.c | 23 ----------------- 8 files changed, 35 insertions(+), 69 deletions(-) diff --git a/ext/standard/tests/file/rename_variation.phpt b/ext/standard/tests/file/rename_variation.phpt index b86c5c80973d..88ad26080f4a 100644 --- a/ext/standard/tests/file/rename_variation.phpt +++ b/ext/standard/tests/file/rename_variation.phpt @@ -55,7 +55,7 @@ bool(false) bool(true) -- Iteration 2 -- -Warning: rename(%s,%s): Not a directory in %s on line %d +Warning: rename(): Not a directory in %s on line %d bool(false) bool(false) bool(false) diff --git a/ext/standard/tests/file/rename_variation12.phpt b/ext/standard/tests/file/rename_variation12.phpt index 8675b17aab5c..954c12427232 100644 --- a/ext/standard/tests/file/rename_variation12.phpt +++ b/ext/standard/tests/file/rename_variation12.phpt @@ -2,6 +2,9 @@ Test rename() function : variation - various relative, absolute paths --CREDITS-- Dave Kelsey +--INI-- +zend.exception_string_param_max_len=1000000 +error_include_args=On --SKIPIF-- +--INI-- +zend.exception_string_param_max_len=1000000 +error_include_args=On --SKIPIF-- Date: Wed, 15 Jul 2026 14:37:30 -0400 Subject: [PATCH 200/211] Run oversized bz2 resource tests in isolation to avoid OOM (#22744) gh20620 and bzdecompress_input_too_large each allocate ~4GB under RUN_RESOURCE_HEAVY_TESTS; two in parallel exhaust a memory-constrained runner and the OOM killer aborts the whole run. Mark both --CONFLICTS-- all so run-tests executes them in isolation, matching ext/tidy/tests/parsing_file_too_large.phpt. --- ext/bz2/tests/bzdecompress_input_too_large.phpt | 2 ++ ext/bz2/tests/gh20620.phpt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ext/bz2/tests/bzdecompress_input_too_large.phpt b/ext/bz2/tests/bzdecompress_input_too_large.phpt index 88c93d366c54..9774c6dbbfd6 100644 --- a/ext/bz2/tests/bzdecompress_input_too_large.phpt +++ b/ext/bz2/tests/bzdecompress_input_too_large.phpt @@ -10,6 +10,8 @@ if (!getenv('RUN_RESOURCE_HEAVY_TESTS')) die('skip resource-heavy test'); if (getenv('SKIP_SLOW_TESTS')) die('skip slow test'); if (PHP_INT_SIZE != 8) die('skip 64-bit only'); ?> +--CONFLICTS-- +all --FILE-- +--CONFLICTS-- +all --INI-- memory_limit=-1 --FILE-- From 2d86f8cf489ea5410edd19d301c1aefe330599ab Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 7 Jul 2026 20:32:58 +0200 Subject: [PATCH 201/211] Fix leak on double DatePeriod::__construct() call Closes GH-22643 --- NEWS | 2 ++ ext/date/php_date.c | 20 ++++++++++++++++++- .../DatePeriod_double_constructor_call.phpt | 14 +++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 ext/date/tests/DatePeriod_double_constructor_call.phpt diff --git a/NEWS b/NEWS index 12ae8c0fb23d..e9e5b76cea69 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.33 +- Date: + . Fixed leak on double DatePeriod::__construct() call. (ilutov) 02 Jul 2026, PHP 8.2.32 diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 9108a7c246a8..c71cef2cf155 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -4720,6 +4720,23 @@ static bool date_period_initialize(timelib_time **st, timelib_time **et, timelib return retval; } /* }}} */ +static void date_period_reset(php_period_obj *period_obj) +{ + if (period_obj->start) { + timelib_time_dtor(period_obj->start); + } + if (period_obj->current) { + timelib_time_dtor(period_obj->current); + } + if (period_obj->end) { + timelib_time_dtor(period_obj->end); + } + if (period_obj->interval) { + timelib_rel_time_dtor(period_obj->interval); + } + memset(period_obj, 0, XtOffsetOf(php_period_obj, std)); +} + /* {{{ Creates new DatePeriod object. */ PHP_METHOD(DatePeriod, __construct) { @@ -4741,7 +4758,7 @@ PHP_METHOD(DatePeriod, __construct) } dpobj = Z_PHPPERIOD_P(ZEND_THIS); - dpobj->current = NULL; + date_period_reset(dpobj); if (isostr) { if (!date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len)) { @@ -4780,6 +4797,7 @@ PHP_METHOD(DatePeriod, __construct) if (end) { DATE_CHECK_INITIALIZED(Z_PHPDATE_P(end)->time, DateTimeInterface); } + DATE_CHECK_INITIALIZED(Z_PHPINTERVAL_P(interval)->initialized, Z_OBJCE_P(interval)); /* init */ php_interval_obj *intobj = Z_PHPINTERVAL_P(interval); diff --git a/ext/date/tests/DatePeriod_double_constructor_call.phpt b/ext/date/tests/DatePeriod_double_constructor_call.phpt new file mode 100644 index 000000000000..551d2727282d --- /dev/null +++ b/ext/date/tests/DatePeriod_double_constructor_call.phpt @@ -0,0 +1,14 @@ +--TEST-- +Double DatePeriod::__construct() call +--FILE-- +__construct($start, $interval, 1); + +?> +===DONE=== +--EXPECT-- +===DONE=== From 070f8b21861f37c23f6d910f9f797d939ead3949 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 15 Jul 2026 20:53:05 +0200 Subject: [PATCH 202/211] Adjust XtOffsetOf() to offsetof() for master --- ext/date/php_date.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index ba660650e256..0e24e4450641 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -5174,7 +5174,7 @@ static void date_period_reset(php_period_obj *period_obj) if (period_obj->interval) { timelib_rel_time_dtor(period_obj->interval); } - memset(period_obj, 0, XtOffsetOf(php_period_obj, std)); + memset(period_obj, 0, offsetof(php_period_obj, std)); } /* {{{ Creates new DatePeriod object. */ From bdcf9ceee383a9d2f4624b90b463279e3a7b1368 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 14 Jul 2026 19:23:53 -0400 Subject: [PATCH 203/211] Fix pdo_odbc output-buffer leak and stale-binding out-of-bounds read SQLBindParameter recorded deferred pointers (P->outbuf, &P->len, and the bound-parameter struct) into the ODBC statement at PDO_PARAM_EVT_ALLOC, and P->outbuf was never freed. Both outlive the bound-parameter struct, which PDO destroys mid-statement on execute() with an array or a rebind: the buffer leaked and the stale binding made the next SQLExecute read freed memory. Track output buffers on the statement and free them in the destructor, and defer binding to execute time, resetting and rebinding the current parameters when the set changed. Closes GH-22735 --- ext/pdo_odbc/odbc_stmt.c | 79 ++++++++++++++++++---- ext/pdo_odbc/php_pdo_odbc_int.h | 8 ++- ext/pdo_odbc/tests/bound_param_rebind.phpt | 28 ++++++++ ext/pdo_odbc/tests/output_param_leak.phpt | 29 ++++++++ 4 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 ext/pdo_odbc/tests/bound_param_rebind.phpt create mode 100644 ext/pdo_odbc/tests/output_param_leak.phpt diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 8940143da09c..8786f2563e52 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -133,6 +133,11 @@ static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) } } +static void odbc_free_out_buffer(zval *el) +{ + efree(Z_PTR_P(el)); +} + static int odbc_stmt_dtor(pdo_stmt_t *stmt) { pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; @@ -153,11 +158,41 @@ static int odbc_stmt_dtor(pdo_stmt_t *stmt) if (S->convbuf) { efree(S->convbuf); } + if (S->out_buffers) { + zend_hash_destroy(S->out_buffers); + FREE_HASHTABLE(S->out_buffers); + } efree(S); return 1; } +static bool odbc_bind_param(pdo_stmt_t *stmt, struct pdo_bound_param_data *param) +{ + pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; + pdo_odbc_param *P = (pdo_odbc_param*)param->driver_data; + RETCODE rc; + + if (!P) { + return true; + } + + rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, + P->paramtype, P->ctype, P->sqltype, P->precision, P->scale, + P->paramtype == SQL_PARAM_INPUT ? + (SQLPOINTER)param : + P->outbuf, + P->outbuf ? P->outbuflen : 0, + &P->len + ); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { + return true; + } + pdo_odbc_stmt_error("SQLBindParameter"); + return false; +} + static int odbc_stmt_execute(pdo_stmt_t *stmt) { RETCODE rc, rc1; @@ -169,6 +204,24 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt) SQLCloseCursor(S->stmt); } + if (S->params_dirty) { + rc = SQLFreeStmt(S->stmt, SQL_RESET_PARAMS); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + pdo_odbc_stmt_error("SQLFreeStmt: SQL_RESET_PARAMS"); + return 0; + } + if (stmt->bound_params) { + struct pdo_bound_param_data *param; + + ZEND_HASH_FOREACH_PTR(stmt->bound_params, param) { + if (!odbc_bind_param(stmt, param)) { + return 0; + } + } ZEND_HASH_FOREACH_END(); + } + S->params_dirty = false; + } + rc = SQLExecute(S->stmt); while (rc == SQL_NEED_DATA) { @@ -312,6 +365,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p if (P) { efree(P); } + S->params_dirty = true; break; case PDO_PARAM_EVT_ALLOC: @@ -386,6 +440,11 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p } P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1)); P->outbuflen = P->len; + if (!S->out_buffers) { + ALLOC_HASHTABLE(S->out_buffers); + zend_hash_init(S->out_buffers, HT_MIN_SIZE, NULL, odbc_free_out_buffer, false); + } + zend_hash_next_index_insert_ptr(S->out_buffers, P->outbuf); } } @@ -394,20 +453,12 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p return 0; } - rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1, - P->paramtype, ctype, sqltype, precision, scale, - P->paramtype == SQL_PARAM_INPUT ? - (SQLPOINTER)param : - P->outbuf, - P->len, - &P->len - ); - - if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { - return 1; - } - pdo_odbc_stmt_error("SQLBindParameter"); - return 0; + P->sqltype = sqltype; + P->ctype = ctype; + P->precision = precision; + P->scale = scale; + S->params_dirty = true; + return 1; } case PDO_PARAM_EVT_EXEC_PRE: diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h index ea93c58b2b60..c25fa5e36eae 100644 --- a/ext/pdo_odbc/php_pdo_odbc_int.h +++ b/ext/pdo_odbc/php_pdo_odbc_int.h @@ -145,16 +145,22 @@ typedef struct { pdo_odbc_errinfo einfo; char *convbuf; zend_ulong convbufsize; + HashTable *out_buffers; unsigned going_long:1; unsigned assume_utf8:1; + unsigned params_dirty:1; signed col_count:16; - unsigned _spare:14; + unsigned _spare:13; } pdo_odbc_stmt; typedef struct { SQLLEN len; SQLLEN outbuflen; SQLSMALLINT paramtype; + SQLSMALLINT sqltype; + SQLSMALLINT ctype; + SQLSMALLINT scale; + SQLULEN precision; char *outbuf; unsigned is_unicode:1; unsigned _spare:31; diff --git a/ext/pdo_odbc/tests/bound_param_rebind.phpt b/ext/pdo_odbc/tests/bound_param_rebind.phpt new file mode 100644 index 000000000000..1ae9651f3a7d --- /dev/null +++ b/ext/pdo_odbc/tests/bound_param_rebind.phpt @@ -0,0 +1,28 @@ +--TEST-- +pdo_odbc: a bound parameter does not leave a stale ODBC binding on re-execute +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +$stmt = $pdo->prepare('SELECT ? AS v'); +$v = 'bound'; +$stmt->bindParam(1, $v, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 256); +$stmt->execute(['from_array']); + +var_dump($stmt->fetch(PDO::FETCH_ASSOC)['v']); +?> +--EXPECT-- +string(10) "from_array" diff --git a/ext/pdo_odbc/tests/output_param_leak.phpt b/ext/pdo_odbc/tests/output_param_leak.phpt new file mode 100644 index 000000000000..f6d4aad5a8c6 --- /dev/null +++ b/ext/pdo_odbc/tests/output_param_leak.phpt @@ -0,0 +1,29 @@ +--TEST-- +pdo_odbc: bound output parameter buffer is released (no leak) +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +$stmt = $pdo->prepare('SELECT ? AS v'); +$var = 'seed'; +$stmt->bindParam(1, $var, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 256); +$stmt->execute(); +$row = $stmt->fetch(PDO::FETCH_ASSOC); + +var_dump($row['v']); +?> +--EXPECT-- +string(4) "seed" From cc8abaf9f109a83c741b9fe5297f1c094c775811 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 15 Jul 2026 16:20:48 -0400 Subject: [PATCH 204/211] Fix use-after-free serializing an array grown by an element's hook (#22714) The IS_ARRAY case of php_var_serialize_intern() walked the array's HashTable without holding a reference across php_var_serialize_nested_data(), which recurses into user hooks (__serialize, __sleep, Serializable::serialize). A hook that grows the same array through a by-reference alias reallocs the backing store mid-walk, so the iterator reads freed memory. Hold a ref across the walk, as the object path and var_dump/var_export already do, so the append separates a copy instead of reallocating in place. --- .../serialize_array_ref_reentrancy.phpt | 21 +++++++++++++++++++ ext/standard/var.c | 8 +++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt diff --git a/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt b/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt new file mode 100644 index 000000000000..490fa4f6c3b0 --- /dev/null +++ b/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt @@ -0,0 +1,21 @@ +--TEST-- +serialize(): a by-reference __serialize() that grows the array being walked must not free it +--FILE-- +ref[] = 'x' . $i; + } + return ['d' => 1]; + } +} +$g = new G(); +$inner = [$g, 'tail']; +$g->ref = &$inner; +$top = [&$inner]; +var_dump(serialize($top)); +?> +--EXPECT-- +string(59) "a:1:{i:0;a:2:{i:0;O:1:"G":1:{s:1:"d";i:1;}i:1;s:4:"tail";}}" diff --git a/ext/standard/var.c b/ext/standard/var.c index 3137a270f661..d79b8941634d 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -1303,13 +1303,17 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ zend_release_properties(myht); return; } - case IS_ARRAY: + case IS_ARRAY: { smart_str_appendl(buf, "a:", 2); myht = Z_ARRVAL_P(struc); + bool rcn = !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1); + GC_TRY_ADDREF(myht); php_var_serialize_nested_data( buf, struc, myht, zend_array_count(myht), /* incomplete_class */ false, var_hash, - !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1)); + rcn); + GC_TRY_DTOR_NO_REF(myht); return; + } case IS_REFERENCE: struc = Z_REFVAL_P(struc); goto again; From 6441f89857b3528e6b71a5f9ce6fa1ade3e67d7e Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 9 Apr 2026 15:02:50 -0400 Subject: [PATCH 205/211] Fix GH-21691: OPcache CFG optimizer drops QM_ASSIGN feeding JMPZ/JMPZ_EX with IS_VAR The CFG optimizer (pass 5) substituted a QM_ASSIGN's source operand into its consumer without checking the source's operand type. When ASSIGN_REF produces IS_VAR and a QM_ASSIGN converts it to IS_TMP_VAR before JMPZ/JMPNZ or JMPZ_EX/JMPNZ_EX, eliminating the QM_ASSIGN leaves an IS_VAR operand on a consumer whose handler is declared CONST|TMP|CV, producing "Invalid opcode 43/4/0" (or 46/4/0 for the _EX variants). Guard both substitution sites with src->op1_type != IS_VAR, folded into the enclosing condition (per dstogov's review) so the BOOL_NOT branch is defensively covered as well. Test exercises both JMPZ and JMPZ_EX paths. Closes GH-21691 Closes GH-21696 --- Zend/Optimizer/block_pass.c | 4 ++-- ext/opcache/tests/gh21691.phpt | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 ext/opcache/tests/gh21691.phpt diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index 02c28ead33e1..3775f8165c3e 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -648,7 +648,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } else if (opline->op1_type == IS_TMP_VAR && !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { src = VAR_SOURCE(opline->op1); - if (src) { + if (src && (src->op1_type != IS_VAR)) { if (src->opcode == ZEND_BOOL_NOT) { VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); @@ -692,7 +692,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) || opline->result.var == opline->op1.var)) { src = VAR_SOURCE(opline->op1); - if (src) { + if (src && (src->op1_type != IS_VAR)) { if (src->opcode == ZEND_BOOL || src->opcode == ZEND_QM_ASSIGN) { VAR_SOURCE(opline->op1) = NULL; diff --git a/ext/opcache/tests/gh21691.phpt b/ext/opcache/tests/gh21691.phpt new file mode 100644 index 000000000000..3926b76c7a12 --- /dev/null +++ b/ext/opcache/tests/gh21691.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-21691 (OPcache CFG optimizer breaks reference returns with JMPZ/JMPZ_EX) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +NULL From fa47ae16a1778523e9ef3e8ca89a2ead6427b131 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 15 Jul 2026 14:51:11 +0200 Subject: [PATCH 206/211] CI: Enable JIT testing on Apple Silicon ZTS 8.6+ Related: GH-22712 Co-authored-by: Florian Engelhardt --- .github/matrix.php | 14 +++++++++++++- .github/workflows/test-suite.yml | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/matrix.php b/.github/matrix.php index 7970442d7050..eefb27e5ba53 100644 --- a/.github/matrix.php +++ b/.github/matrix.php @@ -127,7 +127,19 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re $test_arm = version_compare($php_version, '8.4', '>='); $jobs['MACOS']['matrix'] = $all_variations ? ['arch' => $test_arm ? ['X64', 'ARM64'] : ['X64'], 'debug' => [true, false], 'zts' => [true, false]] - : ['include' => [['arch' => $test_arm ? 'ARM64' : 'X64', 'debug' => true, 'zts' => false]]]; + : ['include' => [['arch' => $test_arm ? 'ARM64' : 'X64', 'debug' => true, 'zts' => false, 'jit' => true]]]; + if ($all_variations) { + // Set the jit variable on X64 jobs + $jobs['MACOS']['matrix']['include'][] = ['arch' => 'X64', 'jit' => true]; + if ($test_arm) { + // Set the jit variable on ARM64 NTS jobs + $jobs['MACOS']['matrix']['include'][] = ['arch' => 'ARM64', 'zts' => false, 'jit' => true]; + // Set the jit variable on ARM64 ZTS jobs on 8.6+ + if (version_compare($php_version, '8.6', '>=')) { + $jobs['MACOS']['matrix']['include'][] = ['arch' => 'ARM64', 'zts' => true, 'jit' => true]; + } + } + } $jobs['MACOS']['config']['arm64_version'] = version_compare($php_version, '8.4', '>=') ? '15' : '14'; } if ($all_jobs || $test_msan) { diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index e269ed336408..b51dfbd957c5 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -345,18 +345,18 @@ jobs: if: ${{ inputs.all_variations }} uses: ./.github/actions/test-macos - name: Test Tracing JIT - if: ${{ matrix.arch == 'X64' || !matrix.zts }} + if: ${{ matrix.jit }} uses: ./.github/actions/test-macos with: enableOpcache: true jitType: tracing - name: Test OpCache - if: ${{ inputs.all_variations || (matrix.arch == 'ARM64' && matrix.zts) }} + if: ${{ inputs.all_variations }} uses: ./.github/actions/test-macos with: enableOpcache: true - name: Test Function JIT - if: ${{ inputs.all_variations && (matrix.arch == 'X64' || !matrix.zts) }} + if: ${{ inputs.all_variations && matrix.jit }} uses: ./.github/actions/test-macos with: enableOpcache: true From d826d4948324d50a6ae36b4fb8606061b866e54c Mon Sep 17 00:00:00 2001 From: Pierre Joye Date: Thu, 16 Jul 2026 21:46:18 +0700 Subject: [PATCH 207/211] Sync gd 2 4 (#22532) 1st part of syncing bundled LibGd to ext/gd/libgd # Updated/Fixed - Color Match improvement using Lab* + Delta-E and actual % as input - Fix and improve freetype rendering and merge both php's libgd + libgd "new encoding" - Fix RAMQ rendering - All codecs with conformance tests (upstream, C) - Perceptual difference (upstream C, will be exposed so php's tests can rely on it - libgd codecs include gif, bmp, webp and jxl full support including animation. Tiff read/write multipages. all BMP format supported but OS/2. Many of these additions or changes work already with existing php's apis. 2D vector APIs and all new Codecs or Gd\* APIs will be a separe PR+RFC. See https://wiki.php.net/rfc/ext-gd-2.4 --- ext/gd/config.m4 | 134 +- ext/gd/config.w32 | 105 +- ext/gd/gd.c | 53 +- ext/gd/libgd/.clang-format | 22 + ext/gd/libgd/bmp.h | 124 +- ext/gd/libgd/entities.h | 76 + ext/gd/libgd/ftraster/gd_ft_math.c | 456 + ext/gd/libgd/ftraster/gd_ft_math.h | 391 + ext/gd/libgd/ftraster/gd_ft_raster.c | 1723 + ext/gd/libgd/ftraster/gd_ft_raster.h | 579 + ext/gd/libgd/ftraster/gd_ft_stroker.c | 1892 + ext/gd/libgd/ftraster/gd_ft_stroker.h | 320 + ext/gd/libgd/ftraster/gd_ft_types.h | 146 + ext/gd/libgd/gd.c | 7273 ++-- ext/gd/libgd/gd.h | 2543 +- ext/gd/libgd/gd2copypal.c | 81 +- ext/gd/libgd/gd2time.c | 87 +- ext/gd/libgd/gd2topng.c | 57 +- ext/gd/libgd/gd_array.c | 136 + ext/gd/libgd/gd_array.h | 57 + ext/gd/libgd/gd_avif.c | 1010 +- ext/gd/libgd/gd_bmp.c | 3033 +- ext/gd/libgd/gd_color.c | 38 + ext/gd/libgd/gd_color.h | 14 + ext/gd/libgd/gd_color_map.c | 793 + ext/gd/libgd/gd_color_map.h | 31 + ext/gd/libgd/gd_color_match.c | 103 +- ext/gd/libgd/gd_compositor.c | 339 + ext/gd/libgd/gd_compositor.h | 23 + ext/gd/libgd/gd_crop.c | 514 +- ext/gd/libgd/gd_draw.c | 483 + ext/gd/libgd/gd_draw_blend.c | 788 + ext/gd/libgd/gd_draw_blend.h | 7 + ext/gd/libgd/gd_errors.h | 4 +- ext/gd/libgd/gd_filename.c | 278 + ext/gd/libgd/gd_filter.c | 1545 +- ext/gd/libgd/gd_fixed.h | 32 + ext/gd/libgd/gd_gd.c | 574 +- ext/gd/libgd/gd_gd2.c | 1742 +- ext/gd/libgd/gd_gif_in.c | 1926 +- ext/gd/libgd/gd_gif_out.c | 1798 +- ext/gd/libgd/gd_gradient.c | 318 + ext/gd/libgd/gd_gradient.h | 43 + ext/gd/libgd/gd_heif.c | 621 + ext/gd/libgd/gd_intern.h | 187 +- ext/gd/libgd/gd_interpolation.c | 3715 +- ext/gd/libgd/gd_io.c | 304 +- ext/gd/libgd/gd_io.h | 22 +- ext/gd/libgd/gd_io_dp.c | 583 +- ext/gd/libgd/gd_io_file.c | 150 +- ext/gd/libgd/gd_io_ss.c | 183 +- ext/gd/libgd/gd_jpeg.c | 1931 +- ext/gd/libgd/gd_jxl.c | 1507 + ext/gd/libgd/gd_matrix.c | 255 +- ext/gd/libgd/gd_metadata.c | 324 + ext/gd/libgd/gd_nnquant.c | 633 + ext/gd/libgd/gd_nnquant.h | 15 + ext/gd/libgd/gd_path.c | 726 + ext/gd/libgd/gd_path.h | 43 + ext/gd/libgd/gd_path_arc.c | 250 + ext/gd/libgd/gd_path_arc.h | 13 + ext/gd/libgd/gd_path_dash.c | 124 + ext/gd/libgd/gd_path_dash.h | 30 + ext/gd/libgd/gd_path_matrix.c | 222 + ext/gd/libgd/gd_path_matrix.h | 10 + ext/gd/libgd/gd_path_outline.h | 19 + ext/gd/libgd/gd_path_stroke.c | 185 + ext/gd/libgd/gd_perceptual_diff.c | 149 + ext/gd/libgd/gd_png.c | 2351 +- ext/gd/libgd/gd_qoi.c | 372 + ext/gd/libgd/gd_qoi.h | 629 + ext/gd/libgd/gd_readimage.c | 289 + ext/gd/libgd/gd_rotate.c | 598 +- ext/gd/libgd/gd_security.c | 77 +- ext/gd/libgd/gd_span_rle.c | 466 + ext/gd/libgd/gd_span_rle.h | 35 + ext/gd/libgd/gd_ss.c | 57 +- ext/gd/libgd/gd_surface.c | 135 + ext/gd/libgd/gd_surface.h | 6 + ext/gd/libgd/gd_tga.c | 804 +- ext/gd/libgd/gd_tga.h | 79 +- ext/gd/libgd/gd_tiff.c | 2422 ++ ext/gd/libgd/gd_topal.c | 2547 +- ext/gd/libgd/gd_transform.c | 164 +- ext/gd/libgd/gd_uhdr.c | 1321 + ext/gd/libgd/gd_vector2d.h | 631 + ext/gd/libgd/gd_vector2d_private.h | 134 + ext/gd/libgd/gd_version.c | 34 + ext/gd/libgd/gd_wbmp.c | 427 +- ext/gd/libgd/gd_webp.c | 1479 +- ext/gd/libgd/gd_xbm.c | 498 +- ext/gd/libgd/gdcache.c | 250 +- ext/gd/libgd/gdcache.h | 42 +- ext/gd/libgd/gddemo.c | 193 +- ext/gd/libgd/gdfontg.c | 39458 ++++++++++++++++++--- ext/gd/libgd/gdfontg.h | 21 +- ext/gd/libgd/gdfontl.c | 37921 +++++++++++++++++--- ext/gd/libgd/gdfontl.h | 23 +- ext/gd/libgd/gdfontmb.c | 27682 +++++++++++++-- ext/gd/libgd/gdfontmb.h | 19 +- ext/gd/libgd/gdfonts.c | 39 +- ext/gd/libgd/gdfonts.h | 19 +- ext/gd/libgd/gdfontt.c | 40 +- ext/gd/libgd/gdfontt.h | 21 +- ext/gd/libgd/gdft.c | 2710 +- ext/gd/libgd/gdfx.c | 472 + ext/gd/libgd/gdfx.h | 21 + ext/gd/libgd/gdhelpers.c | 112 +- ext/gd/libgd/gdhelpers.h | 49 +- ext/gd/libgd/gdkanji.c | 819 +- ext/gd/libgd/gdparttopng.c | 71 +- ext/gd/libgd/gdtables.c | 784 +- ext/gd/libgd/gdtest.c | 596 +- ext/gd/libgd/gdtestft.c | 130 +- ext/gd/libgd/gdtopng.c | 55 +- ext/gd/libgd/gdxpm.c | 361 +- ext/gd/libgd/jisx0208.h | 2025 +- ext/gd/libgd/pngtogd.c | 55 +- ext/gd/libgd/pngtogd2.c | 65 +- ext/gd/libgd/testac.c | 183 +- ext/gd/libgd/wbmp.c | 485 +- ext/gd/libgd/wbmp.h | 60 +- ext/gd/libgd/webpng.c | 440 +- ext/gd/tests/bug43073.phpt | 28 +- ext/gd/tests/bug48732-mb.phpt | 5 +- ext/gd/tests/bug48732.phpt | 5 +- ext/gd/tests/bug48801-mb.phpt | 8 +- ext/gd/tests/bug48801.phpt | 8 +- ext/gd/tests/bug53504.phpt | 42 +- ext/gd/tests/bug65148.phpt | 84 +- ext/gd/tests/bug73272.png | Bin 741 -> 749 bytes ext/gd/tests/bug73291.phpt | 8 +- ext/gd/tests/createfromwbmp2_extern.phpt | 4 - ext/gd/tests/gd223.png | Bin 9721 -> 11321 bytes ext/gd/tests/imagepng_filters.phpt | 59 + main/php_compat.h | 6 + 136 files changed, 142450 insertions(+), 32475 deletions(-) create mode 100644 ext/gd/libgd/.clang-format create mode 100644 ext/gd/libgd/entities.h create mode 100644 ext/gd/libgd/ftraster/gd_ft_math.c create mode 100644 ext/gd/libgd/ftraster/gd_ft_math.h create mode 100644 ext/gd/libgd/ftraster/gd_ft_raster.c create mode 100644 ext/gd/libgd/ftraster/gd_ft_raster.h create mode 100644 ext/gd/libgd/ftraster/gd_ft_stroker.c create mode 100644 ext/gd/libgd/ftraster/gd_ft_stroker.h create mode 100644 ext/gd/libgd/ftraster/gd_ft_types.h create mode 100644 ext/gd/libgd/gd_array.c create mode 100644 ext/gd/libgd/gd_array.h create mode 100644 ext/gd/libgd/gd_color.c create mode 100644 ext/gd/libgd/gd_color.h create mode 100644 ext/gd/libgd/gd_color_map.c create mode 100644 ext/gd/libgd/gd_color_map.h create mode 100644 ext/gd/libgd/gd_compositor.c create mode 100644 ext/gd/libgd/gd_compositor.h create mode 100644 ext/gd/libgd/gd_draw.c create mode 100644 ext/gd/libgd/gd_draw_blend.c create mode 100644 ext/gd/libgd/gd_draw_blend.h create mode 100644 ext/gd/libgd/gd_filename.c create mode 100644 ext/gd/libgd/gd_fixed.h create mode 100644 ext/gd/libgd/gd_gradient.c create mode 100644 ext/gd/libgd/gd_gradient.h create mode 100644 ext/gd/libgd/gd_heif.c create mode 100644 ext/gd/libgd/gd_jxl.c create mode 100644 ext/gd/libgd/gd_metadata.c create mode 100644 ext/gd/libgd/gd_nnquant.c create mode 100644 ext/gd/libgd/gd_nnquant.h create mode 100644 ext/gd/libgd/gd_path.c create mode 100644 ext/gd/libgd/gd_path.h create mode 100644 ext/gd/libgd/gd_path_arc.c create mode 100644 ext/gd/libgd/gd_path_arc.h create mode 100644 ext/gd/libgd/gd_path_dash.c create mode 100644 ext/gd/libgd/gd_path_dash.h create mode 100644 ext/gd/libgd/gd_path_matrix.c create mode 100644 ext/gd/libgd/gd_path_matrix.h create mode 100644 ext/gd/libgd/gd_path_outline.h create mode 100644 ext/gd/libgd/gd_path_stroke.c create mode 100644 ext/gd/libgd/gd_perceptual_diff.c create mode 100644 ext/gd/libgd/gd_qoi.c create mode 100644 ext/gd/libgd/gd_qoi.h create mode 100644 ext/gd/libgd/gd_readimage.c create mode 100644 ext/gd/libgd/gd_span_rle.c create mode 100644 ext/gd/libgd/gd_span_rle.h create mode 100644 ext/gd/libgd/gd_surface.c create mode 100644 ext/gd/libgd/gd_surface.h create mode 100644 ext/gd/libgd/gd_tiff.c create mode 100644 ext/gd/libgd/gd_uhdr.c create mode 100644 ext/gd/libgd/gd_vector2d.h create mode 100644 ext/gd/libgd/gd_vector2d_private.h create mode 100644 ext/gd/libgd/gd_version.c create mode 100644 ext/gd/libgd/gdfx.c create mode 100644 ext/gd/libgd/gdfx.h create mode 100644 ext/gd/tests/imagepng_filters.phpt diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index a997238b7f75..b32735bc4a69 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -20,6 +20,13 @@ PHP_ARG_WITH([avif], [no], [no]) +PHP_ARG_WITH([uhdr], + [for libuhdr], + [AS_HELP_STRING([--with-uhdr], + [GD: Enable UltraHDR support (only for bundled libgd)])], + [no], + [no]) + PHP_ARG_WITH([webp], [for libwebp], [AS_HELP_STRING([--with-webp], @@ -55,6 +62,27 @@ PHP_ARG_ENABLE([gd-jis-conv], [no], [no]) +PHP_ARG_WITH([heif], + [for libheif], + [AS_HELP_STRING([--with-heif], + [GD: Enable HEIF support (only for bundled libgd)])], + [no], + [no]) + +PHP_ARG_WITH([imagequant], + [for libimagequant], + [AS_HELP_STRING([--with-imagequant], + [GD: Enable libimagequant support (only for bundled libgd)])], + [no], + [no]) + +PHP_ARG_WITH([tiff], + [for libtiff], + [AS_HELP_STRING([--with-tiff], + [GD: Enable TIFF support (only for bundled libgd)])], + [no], + [no]) + dnl dnl Checks for the configure options dnl @@ -70,7 +98,7 @@ AC_DEFUN([PHP_GD_PNG],[ AC_DEFUN([PHP_GD_AVIF], [ AS_VAR_IF([PHP_AVIF], [no],, [ - PKG_CHECK_MODULES([AVIF], [libavif >= 0.8.2]) + PKG_CHECK_MODULES([AVIF], [libavif >= 1.0.0]) PHP_EVAL_LIBLINE([$AVIF_LIBS], [GD_SHARED_LIBADD]) PHP_EVAL_INCLINE([$AVIF_CFLAGS]) AC_DEFINE([HAVE_LIBAVIF], [1], @@ -80,9 +108,39 @@ AC_DEFUN([PHP_GD_AVIF], [ ]) ]) +AC_DEFUN([PHP_GD_HEIF], [ + AS_VAR_IF([PHP_HEIF], [no],, [ + PKG_CHECK_MODULES([HEIF], [libheif >= 1.7.0]) + PHP_EVAL_LIBLINE([$HEIF_LIBS], [GD_SHARED_LIBADD]) + PHP_EVAL_INCLINE([$HEIF_CFLAGS]) + AC_DEFINE([HAVE_LIBHEIF], [1], + [Define to 1 if you have the libheif library.]) + AC_DEFINE([HAVE_GD_HEIF], [1], + [Define to 1 if gd extension has HEIF support.]) + ]) +]) + +AC_DEFUN([PHP_GD_UHDR], [ + AS_VAR_IF([PHP_UHDR], [no],, [ + PKG_CHECK_MODULES([UHDR], [libuhdr >= 1.4.0]) + PHP_EVAL_LIBLINE([$UHDR_LIBS], [GD_SHARED_LIBADD]) + PHP_EVAL_INCLINE([$UHDR_CFLAGS]) + + PKG_CHECK_VAR([UHDR_WRITE_XMP], [libuhdr], [UHDR_WRITE_XMP]) + AS_VAR_IF([UHDR_WRITE_XMP], [], [], + [AC_MSG_ERROR([libuhdr was compiled with UHDR_WRITE_XMP enabled, but this extension requires it to be OFF.])]) + + AC_DEFINE([HAVE_LIBUHDR], [1], + [Define to 1 if you have the libuhdr library.]) + AC_DEFINE([HAVE_GD_UHDR], [1], + [Define to 1 if gd extension has UltraHDR support.]) + ]) +]) + AC_DEFUN([PHP_GD_WEBP],[ AS_VAR_IF([PHP_WEBP], [no],, [ - PKG_CHECK_MODULES([WEBP], [libwebp >= 0.2.0]) + PKG_CHECK_MODULES([WEBP], + [libwebp >= 0.2.0 libwebpdemux libwebpmux]) PHP_EVAL_LIBLINE([$WEBP_LIBS], [GD_SHARED_LIBADD]) PHP_EVAL_INCLINE([$WEBP_CFLAGS]) AC_DEFINE([HAVE_LIBWEBP], [1], @@ -110,6 +168,7 @@ AC_DEFUN([PHP_GD_XPM],[ PHP_EVAL_LIBLINE([$XPM_LIBS], [GD_SHARED_LIBADD]) PHP_EVAL_INCLINE([$XPM_CFLAGS]) AC_DEFINE([HAVE_XPM], [1], [Define to 1 if you have the xpm library.]) + AC_DEFINE([HAVE_LIBXPM], [1], [Define to 1 if you have the xpm library.]) AC_DEFINE([HAVE_GD_XPM], [1], [Define to 1 if gd extension has XPM support.]) ]) @@ -136,6 +195,35 @@ AC_DEFUN([PHP_GD_JISX0208],[ ]) ]) +AC_DEFUN([PHP_GD_IMAGEQUANT], [ + AS_VAR_IF([PHP_IMAGEQUANT], [no],, [ + AC_CHECK_HEADER([libimagequant.h], [], + [AC_MSG_ERROR([libimagequant header not found])]) + PHP_CHECK_LIBRARY([imagequant], [liq_attr_create], + [ + PHP_ADD_LIBRARY([imagequant], [], [GD_SHARED_LIBADD]) + AC_DEFINE([HAVE_LIBIMAGEQUANT], [1], + [Define to 1 if you have the libimagequant library.]) + AC_DEFINE([HAVE_GD_IMAGEQUANT], [1], + [Define to 1 if gd extension has libimagequant support.]) + ], + [AC_MSG_ERROR([libimagequant library not found])], + [-limagequant]) + ]) +]) + +AC_DEFUN([PHP_GD_TIFF], [ + AS_VAR_IF([PHP_TIFF], [no],, [ + PKG_CHECK_MODULES([TIFF], [libtiff-4]) + PHP_EVAL_LIBLINE([$TIFF_LIBS], [GD_SHARED_LIBADD]) + PHP_EVAL_INCLINE([$TIFF_CFLAGS]) + AC_DEFINE([HAVE_LIBTIFF], [1], + [Define to 1 if you have the libtiff library.]) + AC_DEFINE([HAVE_GD_TIFF], [1], + [Define to 1 if gd extension has TIFF support.]) + ]) +]) + dnl dnl PHP_GD_CHECK_FORMAT(format, [action-if-found]) dnl @@ -194,6 +282,7 @@ AS_VAR_POPDEF([php_var]) AC_DEFUN([PHP_GD_CHECK_VERSION],[ PHP_GD_CHECK_FORMAT([Png], [AC_DEFINE([HAVE_GD_PNG], [1])]) PHP_GD_CHECK_FORMAT([Avif], [AC_DEFINE([HAVE_GD_AVIF], [1])]) + PHP_GD_CHECK_FORMAT([Heif], [AC_DEFINE([HAVE_GD_HEIF], [1])]) PHP_GD_CHECK_FORMAT([Webp], [AC_DEFINE([HAVE_GD_WEBP], [1])]) PHP_GD_CHECK_FORMAT([Jpeg], [AC_DEFINE([HAVE_GD_JPG], [1])]) PHP_GD_CHECK_FORMAT([Xpm], [AC_DEFINE([HAVE_GD_XPM], [1])]) @@ -206,6 +295,11 @@ AC_DEFUN([PHP_GD_CHECK_VERSION],[ [Define to 1 if GD library has the 'gdVersionString' function.])], [], [$GD_SHARED_LIBADD]) + PHP_CHECK_LIBRARY([gd], [gdPngGetVersionString], + [AC_DEFINE([HAVE_GD_PNG_GET_VERSION_STRING], [1], + [Define to 1 if GD library has the 'gdPngGetVersionString' function.])], + [], + [$GD_SHARED_LIBADD]) PHP_CHECK_LIBRARY([gd], [gdImageGetInterpolationMethod], [AC_DEFINE([HAVE_GD_GET_INTERPOLATION], [1], [Define to 1 if GD library has the 'gdImageGetInterpolationMethod' @@ -237,6 +331,7 @@ if test "$PHP_GD" != "no"; then libgd/gd_io.c libgd/gd_jpeg.c libgd/gd_matrix.c + libgd/gd_metadata.c libgd/gd_png.c libgd/gd_rotate.c libgd/gd_security.c @@ -260,6 +355,33 @@ if test "$PHP_GD" != "no"; then libgd/gdtables.c libgd/gdxpm.c libgd/wbmp.c + libgd/gd_qoi.c + libgd/gd_jxl.c + libgd/gd_color_map.c + libgd/gd_heif.c + libgd/gd_uhdr.c + libgd/gd_nnquant.c + libgd/gd_color.c + libgd/gd_tiff.c + libgd/gd_readimage.c + libgd/gd_filename.c + libgd/ftraster/gd_ft_math.c + libgd/ftraster/gd_ft_raster.c + libgd/ftraster/gd_ft_stroker.c + libgd/gd_array.c + libgd/gd_span_rle.c + libgd/gd_surface.c + libgd/gd_version.c + libgd/gd_compositor.c + libgd/gd_gradient.c + libgd/gd_path.c + libgd/gd_path_arc.c + libgd/gd_path_dash.c + libgd/gd_path_matrix.c + libgd/gd_path_stroke.c + libgd/gd_draw.c + libgd/gd_draw_blend.c + libgd/gd_perceptual_diff.c "]) AC_DEFINE([HAVE_GD_BUNDLED], [1], @@ -269,21 +391,29 @@ if test "$PHP_GD" != "no"; then [Define to 1 if GD library has the 'gdImageGetInterpolationMethod' function.]) + AC_DEFINE([HAVE_GD_PNG_GET_VERSION_STRING], [1], + [Define to 1 if GD library has the 'gdPngGetVersionString' function.]) + dnl Various checks for GD features PHP_SETUP_ZLIB([GD_SHARED_LIBADD]) PHP_GD_PNG PHP_GD_AVIF + PHP_GD_HEIF + PHP_GD_UHDR PHP_GD_WEBP PHP_GD_JPEG PHP_GD_XPM PHP_GD_FREETYPE2 PHP_GD_JISX0208 + PHP_GD_IMAGEQUANT + PHP_GD_TIFF PHP_NEW_EXTENSION([gd], [gd.c $extra_sources], [$ext_shared],, [-Wno-strict-prototypes -I@ext_srcdir@/libgd]) PHP_ADD_BUILD_DIR([$ext_builddir/libgd]) + PHP_ADD_BUILD_DIR([$ext_builddir/libgd/ftraster]) PHP_INSTALL_HEADERS([ext/gd], [php_gd.h libgd/]) diff --git a/ext/gd/config.w32 b/ext/gd/config.w32 index 506bb05cf2e0..ce1dc1119777 100644 --- a/ext/gd/config.w32 +++ b/ext/gd/config.w32 @@ -3,6 +3,11 @@ ARG_WITH("gd", "Bundled GD support", "yes,shared"); ARG_WITH("libwebp", "webp support", "yes"); ARG_WITH("libavif", "avif support", "yes"); +ARG_WITH("heif", "HEIF support", "yes"); +ARG_WITH("uhdr", "UltraHDR support", "yes"); +ARG_WITH("jxl", "JPEG XL support", "yes"); +ARG_WITH("imagequant", "libimagequant support", "no"); +ARG_WITH("tiff", "TIFF support", "no"); if (PHP_GD != "no") { if ( @@ -25,12 +30,17 @@ if (PHP_GD != "no") { CHECK_HEADER("xpm.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\X11") ) { AC_DEFINE('HAVE_XPM', 1, "Define to 1 if you have the xpm library."); + AC_DEFINE('HAVE_LIBXPM', 1, "Define to 1 if you have the xpm library."); AC_DEFINE('HAVE_GD_XPM', 1, "Define to 1 if gd extension has XPM support."); } if (PHP_LIBWEBP != "no") { if ((CHECK_LIB("libwebp_a.lib", "gd", PHP_GD) || CHECK_LIB("libwebp.lib", "gd", PHP_GD)) && + (CHECK_LIB("libwebpdemux_a.lib", "gd", PHP_GD) || CHECK_LIB("libwebpdemux.lib", "gd", PHP_GD)) && + (CHECK_LIB("libwebpmux_a.lib", "gd", PHP_GD) || CHECK_LIB("libwebpmux.lib", "gd", PHP_GD)) && CHECK_HEADER("decode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp") && - CHECK_HEADER("encode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp")) { + CHECK_HEADER("demux.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp") && + CHECK_HEADER("encode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp") && + CHECK_HEADER("mux.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\webp")) { AC_DEFINE("HAVE_LIBWEBP", 1, "Define to 1 if you have the libwebp library."); AC_DEFINE("HAVE_GD_WEBP", 1, "Define to 1 if gd extension has WebP support."); } else { @@ -49,18 +59,107 @@ if (PHP_GD != "no") { WARNING("libavif not enabled; libraries and headers not found"); } } + + if (PHP_HEIF != "no") { + var heif_location = CHECK_LIB("heif_a.lib;heif.lib", "gd", PHP_GD); + if (!CHECK_HEADER("libheif/heif.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) { + heif_location = false; + } + if (heif_location && heif_location.match(/heif_a\.lib$/)) { + if (!(CHECK_LIB("libde265_a.lib", "gd", PHP_GD) && + CHECK_LIB("x265_a.lib", "gd", PHP_GD) && + CHECK_LIB("aom_a.lib", "gd", PHP_GD) && + CHECK_LIB("dav1d_a.lib", "gd", PHP_GD))) { + WARNING("static libheif not enabled; codec libraries not found"); + heif_location = false; + } else { + ADD_FLAG("CFLAGS_GD", "/D LIBHEIF_STATIC_BUILD"); + } + } + if (heif_location) { + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBHEIF /D HAVE_GD_HEIF"); + } else { + WARNING("libheif not enabled; library and headers not found"); + } + } + + // requires UHDR_WRITE_XMP=Off, for the windows build team, when adding/updating libuhdr + if (PHP_UHDR != "no") { + var uhdr_location = CHECK_LIB("uhdr_a.lib;uhdr.lib", "gd", PHP_GD); + if (uhdr_location && + CHECK_HEADER("ultrahdr_api.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) { + if (uhdr_location.match(/uhdr\.lib$/)) { + ADD_FLAG("CFLAGS_GD", "/D UHDR_USING_SHARED_LIBRARY"); + } + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBUHDR /D HAVE_GD_UHDR"); + } else { + WARNING("libuhdr not enabled; library and headers not found"); + } + } + + if (PHP_JXL != "no") { + var jxl_location = CHECK_LIB("jxl_a.lib;jxl.lib", "gd", PHP_GD); + if (jxl_location && jxl_location.match(/jxl_a\.lib$/)) { + if (!(CHECK_LIB("jxl_cms_a.lib", "gd", PHP_GD) && + CHECK_LIB("hwy_a.lib", "gd", PHP_GD) && + CHECK_LIB("brotlienc.lib;brotlienc-static.lib", "gd", PHP_GD) && + CHECK_LIB("brotlidec.lib;brotlidec-static.lib", "gd", PHP_GD) && + CHECK_LIB("brotlicommon.lib;brotlicommon-static.lib", "gd", PHP_GD))) { + WARNING("static libjxl not enabled; dependency libraries not found"); + jxl_location = false; + } else { + ADD_FLAG("CFLAGS_GD", "/D JXL_STATIC_DEFINE /D JXL_CMS_STATIC_DEFINE"); + } + } + if (jxl_location && + CHECK_HEADER("jxl/decode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include") && + CHECK_HEADER("jxl/encode.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) { + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBJXL /D HAVE_GD_JXL"); + } else { + WARNING("libjxl not enabled; library and headers not found"); + } + } + + if (PHP_IMAGEQUANT != "no") { + if (CHECK_LIB("imagequant.lib", "gd", PHP_GD) && + CHECK_HEADER("libimagequant.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) { + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBIMAGEQUANT /D HAVE_GD_IMAGEQUANT"); + } else { + WARNING("libimagequant not enabled; library and headers not found"); + } + } + + if (PHP_TIFF != "no") { + if (CHECK_LIB("tiff_a.lib;tiff.lib", "gd", PHP_GD) && + CHECK_LIB("liblzma_a.lib;liblzma.lib", "gd", PHP_GD) && + CHECK_LIB("libzstd_a.lib;libzstd.lib", "gd", PHP_GD) && + CHECK_LIB("libwebp_a.lib;libwebp.lib", "gd", PHP_GD) && + CHECK_HEADER("tiff.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include")) { + ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBTIFF /D HAVE_GD_TIFF"); + } else { + WARNING("libtiff not enabled; library and headers not found"); + } + } + CHECK_LIB("User32.lib", "gd", PHP_GD); CHECK_LIB("Gdi32.lib", "gd", PHP_GD); + CHECK_LIB("Advapi32.lib", "gd", PHP_GD); EXTENSION("gd", "gd.c", null, "-Iext/gd/libgd"); ADD_SOURCES("ext/gd/libgd", "gd2copypal.c gd.c \ gdcache.c gdfontg.c gdfontl.c gdfontmb.c gdfonts.c gdfontt.c \ gdft.c gd_gd2.c gd_gd.c gd_gif_in.c gd_gif_out.c gdhelpers.c gd_io.c gd_io_dp.c \ - gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \ + gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_metadata.c gd_png.c gd_ss.c \ gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c gd_xbm.c gd_security.c gd_transform.c \ gd_filter.c gd_rotate.c gd_color_match.c gd_webp.c gd_avif.c \ - gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c gd_tga.c", "gd"); + gd_crop.c gd_interpolation.c gd_matrix.c gd_bmp.c gd_tga.c gd_qoi.c gd_jxl.c gd_color_map.c \ + gd_heif.c gd_uhdr.c gd_tiff.c gd_nnquant.c gd_color.c gd_readimage.c gd_filename.c \ + gd_array.c gd_span_rle.c \ + gd_surface.c gd_version.c gd_compositor.c gd_gradient.c gd_path.c gd_path_arc.c gd_path_dash.c \ + gd_path_matrix.c gd_path_stroke.c gd_draw.c gd_draw_blend.c gd_perceptual_diff.c", "gd"); + ADD_SOURCES("ext/gd/libgd/ftraster", "gd_ft_math.c gd_ft_raster.c gd_ft_stroker.c", "gd"); AC_DEFINE('HAVE_GD_BUNDLED', 1, "Define to 1 if gd extension uses GD library bundled in PHP."); + AC_DEFINE('HAVE_GD_PNG_GET_VERSION_STRING', 1, "Define to 1 if GD library has the 'gdPngGetVersionString' function."); AC_DEFINE('HAVE_GD_PNG', 1, "Define to 1 if gd extension has PNG support."); AC_DEFINE('HAVE_LIBPNG', 1, "Define to 1 if you have the libpng library."); AC_DEFINE('HAVE_LIBJPEG', 1, "Define to 1 if you have the libjpeg library."); diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 70a379dbe9be..11363bfa4271 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -299,6 +299,15 @@ PHP_INI_END() /* {{{ php_gd_error_method */ void php_gd_error_method(int type, const char *format, va_list args) { + /* Keep PHP's historical PNG warning text while bundled gd_png.c stays + * identical to upstream libgd. */ + if (strcmp(format, "gd-png: fatal libpng error: %s\n") == 0) { + format = "gd-png: fatal libpng error: %s"; + } else if (strncmp(format, "gd-png error: setjmp returns error condition", + sizeof("gd-png error: setjmp returns error condition") - 1) == 0) { + format = "gd-png error: setjmp returns error condition"; + } + switch (type) { #ifndef PHP_WIN32 case GD_DEBUG: @@ -358,7 +367,7 @@ PHP_RSHUTDOWN_FUNCTION(gd) /* }}} */ #ifdef HAVE_GD_BUNDLED -#define PHP_GD_VERSION_STRING "bundled (2.1.0 compatible)" +#define PHP_GD_VERSION_STRING "bundled (2.4.0 compatible)" #else # define PHP_GD_VERSION_STRING GD_VERSION_STRING #endif @@ -413,7 +422,7 @@ PHP_MINFO_FUNCTION(gd) #ifdef HAVE_GD_PNG php_info_print_table_row(2, "PNG Support", "enabled"); -#ifdef HAVE_GD_BUNDLED +#ifdef HAVE_GD_PNG_GET_VERSION_STRING php_info_print_table_row(2, "libPNG Version", gdPngGetVersionString()); #endif #endif @@ -723,6 +732,10 @@ PHP_FUNCTION(imagetruecolortopalette) RETURN_THROWS(); } + /* Preserve PHP's historical palette conversion behavior regardless of + * whether bundled libgd was built with libimagequant support. */ + gdImageTrueColorToPaletteSetMethod(im, GD_QUANT_JQUANT, 0); + if (gdImageTrueColorToPalette(im, dither, (int)ncolors)) { RETURN_TRUE; } else { @@ -1880,6 +1893,8 @@ PHP_FUNCTION(imagegif) gdImagePtr im; gdIOCtx *ctx; zval *to_zval = NULL; + int quantization_method; + int quantization_speed; if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|z!", &imgind, gd_image_ce, &to_zval) == FAILURE) { RETURN_THROWS(); @@ -1892,7 +1907,18 @@ PHP_FUNCTION(imagegif) RETURN_FALSE; } + quantization_method = im->paletteQuantizationMethod; + quantization_speed = im->paletteQuantizationSpeed; + if (im->trueColor) { + /* GIF conversion historically used JQUANT in PHP. Keep output stable + * when bundled libgd has a build-dependent default such as LIQ. */ + gdImageTrueColorToPaletteSetMethod(im, GD_QUANT_JQUANT, 0); + } gdImageGifCtx(im, ctx); + if (im->trueColor) { + gdImageTrueColorToPaletteSetMethod(im, quantization_method, + quantization_speed); + } ctx->gd_free(ctx); @@ -1928,7 +1954,28 @@ PHP_FUNCTION(imagepng) } #ifdef HAVE_GD_BUNDLED - gdImagePngCtxEx(im, ctx, (int) quality, (int) basefilter); + { + gdPngWriteOptions options; + unsigned int filters = GD_PNG_FILTER_NONE; + unsigned int unknown_filters; + + gdPngWriteOptionsInit(&options); + options.compression_level = (int) quality; + if (basefilter >= 0) { + unsigned long php_filters = (unsigned long) basefilter; + if (php_filters & 0x08) filters |= GD_PNG_FILTER_NONE; + if (php_filters & 0x10) filters |= GD_PNG_FILTER_SUB; + if (php_filters & 0x20) filters |= GD_PNG_FILTER_UP; + if (php_filters & 0x40) filters |= GD_PNG_FILTER_AVERAGE; + if (php_filters & 0x80) filters |= GD_PNG_FILTER_PAETH; + unknown_filters = (unsigned int) (php_filters & ~0xf8UL); + if (unknown_filters != 0) { + filters |= 1U << 31; + } + } + options.filters = filters; + (void) gdImagePngCtxWithOptions(im, ctx, &options); + } #else gdImagePngCtxEx(im, ctx, (int) quality); #endif diff --git a/ext/gd/libgd/.clang-format b/ext/gd/libgd/.clang-format new file mode 100644 index 000000000000..ce71740440b3 --- /dev/null +++ b/ext/gd/libgd/.clang-format @@ -0,0 +1,22 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +ColumnLimit: 100 +PointerAlignment: Right +SpaceBeforeParens: ControlStatements + +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true + AfterControlStatement: false + AfterEnum: false + AfterStruct: false + AfterUnion: false + AfterNamespace: false + AfterExternBlock: false + BeforeElse: false + BeforeCatch: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false \ No newline at end of file diff --git a/ext/gd/libgd/bmp.h b/ext/gd/libgd/bmp.h index cecde0383f53..6864a670e7e0 100644 --- a/ext/gd/libgd/bmp.h +++ b/ext/gd/libgd/bmp.h @@ -1,32 +1,34 @@ -/* $Id$ */ +#ifndef BMP_H +#define BMP_H 1 + #ifdef __cplusplus extern "C" { #endif - /* - gd_bmp.c +/* + gd_bmp.c - Bitmap format support for libgd + Bitmap format support for libgd - * Written 2007, Scott MacVicar - --------------------------------------------------------------------------- + * Written 2007, Scott MacVicar + --------------------------------------------------------------------------- - Todo: + Todo: - RLE4, RLE8 and Bitfield encoding - Add full support for Windows v4 and Windows v5 header formats + RLE4, RLE8 and Bitfield encoding + Add full support for Windows v4 and Windows v5 header formats - ---------------------------------------------------------------------------- - */ - -#ifndef BMP_H -#define BMP_H 1 + ---------------------------------------------------------------------------- + */ #define BMP_PALETTE_3 1 #define BMP_PALETTE_4 2 #define BMP_WINDOWS_V3 40 +#define BMP_WINDOWS_V2 52 +#define BMP_WINDOWS_V3_ALPHA 56 #define BMP_OS2_V1 12 +#define BMP_OS2_V2_SHORT 16 #define BMP_OS2_V2 64 #define BMP_WINDOWS_V4 108 #define BMP_WINDOWS_V5 124 @@ -37,6 +39,7 @@ extern "C" { #define BMP_BI_BITFIELDS 3 #define BMP_BI_JPEG 4 #define BMP_BI_PNG 5 +#define BMP_BI_ALPHABITFIELDS 6 #define BMP_RLE_COMMAND 0 #define BMP_RLE_ENDOFLINE 0 @@ -46,67 +49,78 @@ extern "C" { #define BMP_RLE_TYPE_RAW 0 #define BMP_RLE_TYPE_RLE 1 - /* BMP header. */ - typedef struct { - /* 16 bit - header identifying the type */ - signed short int magic; +/* BMP header. */ +typedef struct { + /* 16 bit - header identifying the type */ + signed short int magic; + + /* 32bit - size of the file */ + int size; - /* 32bit - size of the file */ - int size; + /* 16bit - these two are in the spec but "reserved" */ + signed short int reserved1; + signed short int reserved2; - /* 16bit - these two are in the spec but "reserved" */ - signed short int reserved1; - signed short int reserved2; + /* 32 bit - offset of the bitmap header from data in bytes */ + signed int off; - /* 32 bit - offset of the bitmap header from data in bytes */ - signed int off; +} bmp_hdr_t; - } bmp_hdr_t; +/* BMP info. */ +typedef struct { + /* 16bit - Type, ie Windows or OS/2 for the palette info */ + signed short int type; + /* 32bit - The length of the bitmap information header in bytes. */ + signed int len; - /* BMP info. */ - typedef struct { - /* 16bit - Type, ie Windows or OS/2 for the palette info */ - signed short int type; - /* 32bit - The length of the bitmap information header in bytes. */ - signed int len; + /* 32bit - The width of the bitmap in pixels. */ + signed int width; - /* 32bit - The width of the bitmap in pixels. */ - signed int width; + /* 32bit - The height of the bitmap in pixels. */ + signed int height; - /* 32bit - The height of the bitmap in pixels. */ - signed int height; + /* 8 bit - The bitmap data is specified in top-down order. */ + signed char topdown; - /* 8 bit - The bitmap data is specified in top-down order. */ - signed char topdown; + /* 16 bit - The number of planes. This must be set to a value of one. */ + signed short int numplanes; - /* 16 bit - The number of planes. This must be set to a value of one. */ - signed short int numplanes; + /* 16 bit - The number of bits per pixel. */ + signed short int depth; - /* 16 bit - The number of bits per pixel. */ - signed short int depth; + /* 32bit - The type of compression used. */ + signed int enctype; - /* 32bit - The type of compression used. */ - signed int enctype; + /* 32bit - The size of the image in bytes. */ + signed int size; - /* 32bit - The size of the image in bytes. */ - signed int size; + /* 32bit - The horizontal resolution in pixels/metre. */ + signed int hres; - /* 32bit - The horizontal resolution in pixels/metre. */ - signed int hres; + /* 32bit - The vertical resolution in pixels/metre. */ + signed int vres; - /* 32bit - The vertical resolution in pixels/metre. */ - signed int vres; + /* 32bit - The number of color indices used by the bitmap. */ + signed int numcolors; - /* 32bit - The number of color indices used by the bitmap. */ - signed int numcolors; + /* 32bit - The number of color indices important for displaying the bitmap. + */ + signed int mincolors; - /* 32bit - The number of color indices important for displaying the bitmap. */ - signed int mincolors; + /* 32bit - Color masks for BI_BITFIELDS and v4/v5 headers. */ + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + unsigned int alpha_mask; - } bmp_info_t; +} bmp_info_t; #endif #ifdef __cplusplus } #endif + +#ifdef __cplusplus +} +#endif diff --git a/ext/gd/libgd/entities.h b/ext/gd/libgd/entities.h new file mode 100644 index 000000000000..37be6c85a8da --- /dev/null +++ b/ext/gd/libgd/entities.h @@ -0,0 +1,76 @@ +/* + * Generated file - do not edit directly. + * + * This file was generated from: + * http://www.w3.org/TR/REC-html40/sgml/entities.html + * by means of the script: + * entities.tcl + */ + +#ifdef __cplusplus +extern "C" { +#endif + +static struct entities_s { + char *name; + int value; +} entities[] = { + {"AElig", 198}, {"Aacute", 193}, {"Acirc", 194}, {"Agrave", 192}, {"Alpha", 913}, + {"Aring", 197}, {"Atilde", 195}, {"Auml", 196}, {"Beta", 914}, {"Ccedil", 199}, + {"Chi", 935}, {"Dagger", 8225}, {"Delta", 916}, {"ETH", 208}, {"Eacute", 201}, + {"Ecirc", 202}, {"Egrave", 200}, {"Epsilon", 917}, {"Eta", 919}, {"Euml", 203}, + {"Gamma", 915}, {"Iacute", 205}, {"Icirc", 206}, {"Igrave", 204}, {"Iota", 921}, + {"Iuml", 207}, {"Kappa", 922}, {"Lambda", 923}, {"Mu", 924}, {"Ntilde", 209}, + {"Nu", 925}, {"OElig", 338}, {"Oacute", 211}, {"Ocirc", 212}, {"Ograve", 210}, + {"Omega", 937}, {"Omicron", 927}, {"Oslash", 216}, {"Otilde", 213}, {"Ouml", 214}, + {"Phi", 934}, {"Pi", 928}, {"Prime", 8243}, {"Psi", 936}, {"Rho", 929}, + {"Scaron", 352}, {"Sigma", 931}, {"THORN", 222}, {"Tau", 932}, {"Theta", 920}, + {"Uacute", 218}, {"Ucirc", 219}, {"Ugrave", 217}, {"Upsilon", 933}, {"Uuml", 220}, + {"Xi", 926}, {"Yacute", 221}, {"Yuml", 376}, {"Zeta", 918}, {"aacute", 225}, + {"acirc", 226}, {"acute", 180}, {"aelig", 230}, {"agrave", 224}, {"alefsym", 8501}, + {"alpha", 945}, {"amp", 38}, {"and", 8743}, {"ang", 8736}, {"aring", 229}, + {"asymp", 8776}, {"atilde", 227}, {"auml", 228}, {"bdquo", 8222}, {"beta", 946}, + {"brvbar", 166}, {"bull", 8226}, {"cap", 8745}, {"ccedil", 231}, {"cedil", 184}, + {"cent", 162}, {"chi", 967}, {"circ", 710}, {"clubs", 9827}, {"cong", 8773}, + {"copy", 169}, {"crarr", 8629}, {"cup", 8746}, {"curren", 164}, {"dArr", 8659}, + {"dagger", 8224}, {"darr", 8595}, {"deg", 176}, {"delta", 948}, {"diams", 9830}, + {"divide", 247}, {"eacute", 233}, {"ecirc", 234}, {"egrave", 232}, {"empty", 8709}, + {"emsp", 8195}, {"ensp", 8194}, {"epsilon", 949}, {"equiv", 8801}, {"eta", 951}, + {"eth", 240}, {"euml", 235}, {"euro", 8364}, {"exist", 8707}, {"fnof", 402}, + {"forall", 8704}, {"frac12", 189}, {"frac14", 188}, {"frac34", 190}, {"frasl", 8260}, + {"gamma", 947}, {"ge", 8805}, {"gt", 62}, {"hArr", 8660}, {"harr", 8596}, + {"hearts", 9829}, {"hellip", 8230}, {"iacute", 237}, {"icirc", 238}, {"iexcl", 161}, + {"igrave", 236}, {"image", 8465}, {"infin", 8734}, {"int", 8747}, {"iota", 953}, + {"iquest", 191}, {"isin", 8712}, {"iuml", 239}, {"kappa", 954}, {"lArr", 8656}, + {"lambda", 955}, {"lang", 9001}, {"laquo", 171}, {"larr", 8592}, {"lceil", 8968}, + {"ldquo", 8220}, {"le", 8804}, {"lfloor", 8970}, {"lowast", 8727}, {"loz", 9674}, + {"lrm", 8206}, {"lsaquo", 8249}, {"lsquo", 8216}, {"lt", 60}, {"macr", 175}, + {"mdash", 8212}, {"micro", 181}, {"middot", 183}, {"minus", 8722}, {"mu", 956}, + {"nabla", 8711}, {"nbsp", 160}, {"ndash", 8211}, {"ne", 8800}, {"ni", 8715}, + {"not", 172}, {"notin", 8713}, {"nsub", 8836}, {"ntilde", 241}, {"nu", 957}, + {"oacute", 243}, {"ocirc", 244}, {"oelig", 339}, {"ograve", 242}, {"oline", 8254}, + {"omega", 969}, {"omicron", 959}, {"oplus", 8853}, {"or", 8744}, {"ordf", 170}, + {"ordm", 186}, {"oslash", 248}, {"otilde", 245}, {"otimes", 8855}, {"ouml", 246}, + {"para", 182}, {"part", 8706}, {"permil", 8240}, {"perp", 8869}, {"phi", 966}, + {"pi", 960}, {"piv", 982}, {"plusmn", 177}, {"pound", 163}, {"prime", 8242}, + {"prod", 8719}, {"prop", 8733}, {"psi", 968}, {"quot", 34}, {"rArr", 8658}, + {"radic", 8730}, {"rang", 9002}, {"raquo", 187}, {"rarr", 8594}, {"rceil", 8969}, + {"rdquo", 8221}, {"real", 8476}, {"reg", 174}, {"rfloor", 8971}, {"rho", 961}, + {"rlm", 8207}, {"rsaquo", 8250}, {"rsquo", 8217}, {"sbquo", 8218}, {"scaron", 353}, + {"sdot", 8901}, {"sect", 167}, {"shy", 173}, {"sigma", 963}, {"sigmaf", 962}, + {"sim", 8764}, {"spades", 9824}, {"sub", 8834}, {"sube", 8838}, {"sum", 8721}, + {"sup", 8835}, {"sup1", 185}, {"sup2", 178}, {"sup3", 179}, {"supe", 8839}, + {"szlig", 223}, {"tau", 964}, {"there4", 8756}, {"theta", 952}, {"thetasym", 977}, + {"thinsp", 8201}, {"thorn", 254}, {"tilde", 732}, {"times", 215}, {"trade", 8482}, + {"uArr", 8657}, {"uacute", 250}, {"uarr", 8593}, {"ucirc", 251}, {"ugrave", 249}, + {"uml", 168}, {"upsih", 978}, {"upsilon", 965}, {"uuml", 252}, {"weierp", 8472}, + {"xi", 958}, {"yacute", 253}, {"yen", 165}, {"yuml", 255}, {"zeta", 950}, + {"zwj", 8205}, {"zwnj", 8204}, +}; + +#define ENTITY_NAME_LENGTH_MAX 8 +#define NR_OF_ENTITIES 252 + +#ifdef __cplusplus +} +#endif diff --git a/ext/gd/libgd/ftraster/gd_ft_math.c b/ext/gd/libgd/ftraster/gd_ft_math.c new file mode 100644 index 000000000000..95cd4e5d7ac1 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_math.c @@ -0,0 +1,456 @@ +/***************************************************************************/ +/* */ +/* fttrigon.c */ +/* */ +/* FreeType trigonometric functions (body). */ +/* */ +/* Copyright 2001-2005, 2012-2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "gd_ft_math.h" +#include + +#if defined(_MSC_VER) +#include +static unsigned int __inline clz(unsigned int x) +{ + unsigned long r = 0; + if (x != 0) { + _BitScanReverse(&r, x); + } + return r; +} +#define GD_FT_MSB(x) (clz(x)) +#elif defined(__GNUC__) +#define GD_FT_MSB(x) (31 - __builtin_clz(x)) +#else +static unsigned int __inline clz(unsigned int x) +{ + int c = 31; + x &= ~x + 1; + if (n & 0x0000FFFF) + c -= 16; + if (n & 0x00FF00FF) + c -= 8; + if (n & 0x0F0F0F0F) + c -= 4; + if (n & 0x33333333) + c -= 2; + if (n & 0x55555555) + c -= 1; + return c; +} +#define GD_FT_MSB(x) (clz(x)) +#endif + +#define GD_FT_PAD_FLOOR(x, n) ((x) & ~((n) - 1)) +#define GD_FT_PAD_ROUND(x, n) GD_FT_PAD_FLOOR((x) + ((n) / 2), n) +#define GD_FT_PAD_CEIL(x, n) GD_FT_PAD_FLOOR((x) + ((n) - 1), n) + +#define GD_FT_BEGIN_STMNT do { +#define GD_FT_END_STMNT \ + } \ + while (0) +/* transfer sign leaving a positive number */ +#define GD_FT_MOVE_SIGN(x, s) \ + GD_FT_BEGIN_STMNT \ + if (x < 0) { \ + x = -x; \ + s = -s; \ + } \ + GD_FT_END_STMNT + +GD_FT_Long GD_FT_MulFix(GD_FT_Long a, GD_FT_Long b) +{ + GD_FT_Int s = 1; + GD_FT_Long c; + + GD_FT_MOVE_SIGN(a, s); + GD_FT_MOVE_SIGN(b, s); + + c = (GD_FT_Long)(((GD_FT_Int64)a * b + 0x8000L) >> 16); + + return (s > 0) ? c : -c; +} + +GD_FT_Long GD_FT_MulDiv(GD_FT_Long a, GD_FT_Long b, GD_FT_Long c) +{ + GD_FT_Int s = 1; + GD_FT_Long d; + + GD_FT_MOVE_SIGN(a, s); + GD_FT_MOVE_SIGN(b, s); + GD_FT_MOVE_SIGN(c, s); + + d = (GD_FT_Long)(c > 0 ? ((GD_FT_Int64)a * b + (c >> 1)) / c : 0x7FFFFFFFL); + + return (s > 0) ? d : -d; +} + +GD_FT_Long GD_FT_DivFix(GD_FT_Long a, GD_FT_Long b) +{ + GD_FT_Int s = 1; + GD_FT_Long q; + + GD_FT_MOVE_SIGN(a, s); + GD_FT_MOVE_SIGN(b, s); + + q = (GD_FT_Long)(b > 0 ? (((GD_FT_UInt64)a << 16) + (b >> 1)) / b : 0x7FFFFFFFL); + + return (s < 0 ? -q : q); +} + +/*************************************************************************/ +/* */ +/* This is a fixed-point CORDIC implementation of trigonometric */ +/* functions as well as transformations between Cartesian and polar */ +/* coordinates. The angles are represented as 16.16 fixed-point values */ +/* in degrees, i.e., the angular resolution is 2^-16 degrees. Note that */ +/* only vectors longer than 2^16*180/pi (or at least 22 bits) on a */ +/* discrete Cartesian grid can have the same or better angular */ +/* resolution. Therefore, to maintain this precision, some functions */ +/* require an interim upscaling of the vectors, whereas others operate */ +/* with 24-bit long vectors directly. */ +/* */ +/*************************************************************************/ + +/* the Cordic shrink factor 0.858785336480436 * 2^32 */ +#define GD_FT_TRIG_SCALE 0xDBD95B16UL + +/* the highest bit in overflow-safe vector components, */ +/* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */ +#define GD_FT_TRIG_SAFE_MSB 29 + +/* this table was generated for GD_FT_PI = 180L << 16, i.e. degrees */ +#define GD_FT_TRIG_MAX_ITERS 23 + +static const GD_FT_Fixed ft_trig_arctan_table[] = { + 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, 14668L, 7334L, 3667L, 1833L, + 917L, 458L, 229L, 115L, 57L, 29L, 14L, 7L, 4L, 2L, 1L}; + +/* multiply a given value by the CORDIC shrink factor */ +static GD_FT_Fixed ft_trig_downscale(GD_FT_Fixed val) +{ + GD_FT_Fixed s; + GD_FT_Int64 v; + + s = val; + val = GD_FT_ABS(val); + + v = (val * (GD_FT_Int64)GD_FT_TRIG_SCALE) + 0x100000000UL; + val = (GD_FT_Fixed)(v >> 32); + + return (s >= 0) ? val : -val; +} + +/* undefined and never called for zero vector */ +static GD_FT_Int ft_trig_prenorm(GD_FT_Vector *vec) +{ + GD_FT_Pos x, y; + GD_FT_Int shift; + + x = vec->x; + y = vec->y; + + shift = GD_FT_MSB(GD_FT_ABS(x) | GD_FT_ABS(y)); + + if (shift <= GD_FT_TRIG_SAFE_MSB) { + shift = GD_FT_TRIG_SAFE_MSB - shift; + vec->x = (GD_FT_Pos)((GD_FT_ULong)x << shift); + vec->y = (GD_FT_Pos)((GD_FT_ULong)y << shift); + } else { + shift -= GD_FT_TRIG_SAFE_MSB; + vec->x = x >> shift; + vec->y = y >> shift; + shift = -shift; + } + + return shift; +} + +static void ft_trig_pseudo_rotate(GD_FT_Vector *vec, GD_FT_Angle theta) +{ + GD_FT_Int i; + GD_FT_Fixed x, y, xtemp, b; + const GD_FT_Fixed *arctanptr; + + x = vec->x; + y = vec->y; + + /* Rotate inside [-PI/4,PI/4] sector */ + while (theta < -GD_FT_ANGLE_PI4) { + xtemp = y; + y = -x; + x = xtemp; + theta += GD_FT_ANGLE_PI2; + } + + while (theta > GD_FT_ANGLE_PI4) { + xtemp = -y; + y = x; + x = xtemp; + theta -= GD_FT_ANGLE_PI2; + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for (i = 1, b = 1; i < GD_FT_TRIG_MAX_ITERS; b <<= 1, i++) { + GD_FT_Fixed v1 = ((y + b) >> i); + GD_FT_Fixed v2 = ((x + b) >> i); + if (theta < 0) { + xtemp = x + v1; + y = y - v2; + x = xtemp; + theta += *arctanptr++; + } else { + xtemp = x - v1; + y = y + v2; + x = xtemp; + theta -= *arctanptr++; + } + } + + vec->x = x; + vec->y = y; +} + +static void ft_trig_pseudo_polarize(GD_FT_Vector *vec) +{ + GD_FT_Angle theta; + GD_FT_Int i; + GD_FT_Fixed x, y, xtemp, b; + const GD_FT_Fixed *arctanptr; + + x = vec->x; + y = vec->y; + + /* Get the vector into [-PI/4,PI/4] sector */ + if (y > x) { + if (y > -x) { + theta = GD_FT_ANGLE_PI2; + xtemp = y; + y = -x; + x = xtemp; + } else { + theta = y > 0 ? GD_FT_ANGLE_PI : -GD_FT_ANGLE_PI; + x = -x; + y = -y; + } + } else { + if (y < -x) { + theta = -GD_FT_ANGLE_PI2; + xtemp = -y; + y = x; + x = xtemp; + } else { + theta = 0; + } + } + + arctanptr = ft_trig_arctan_table; + + /* Pseudorotations, with right shifts */ + for (i = 1, b = 1; i < GD_FT_TRIG_MAX_ITERS; b <<= 1, i++) { + GD_FT_Fixed v1 = ((y + b) >> i); + GD_FT_Fixed v2 = ((x + b) >> i); + if (y > 0) { + xtemp = x + v1; + y = y - v2; + x = xtemp; + theta += *arctanptr++; + } else { + xtemp = x - v1; + y = y + v2; + x = xtemp; + theta -= *arctanptr++; + } + } + + /* round theta */ + if (theta >= 0) + theta = GD_FT_PAD_ROUND(theta, 32); + else + theta = -GD_FT_PAD_ROUND(-theta, 32); + + vec->x = x; + vec->y = theta; +} + +/* documentation is in fttrigon.h */ + +GD_FT_Fixed GD_FT_Cos(GD_FT_Angle angle) +{ + GD_FT_Vector v; + + v.x = GD_FT_TRIG_SCALE >> 8; + v.y = 0; + ft_trig_pseudo_rotate(&v, angle); + + return (v.x + 0x80L) >> 8; +} + +/* documentation is in fttrigon.h */ + +GD_FT_Fixed GD_FT_Sin(GD_FT_Angle angle) { return GD_FT_Cos(GD_FT_ANGLE_PI2 - angle); } + +/* documentation is in fttrigon.h */ + +GD_FT_Fixed GD_FT_Tan(GD_FT_Angle angle) +{ + GD_FT_Vector v; + + v.x = GD_FT_TRIG_SCALE >> 8; + v.y = 0; + ft_trig_pseudo_rotate(&v, angle); + + return GD_FT_DivFix(v.y, v.x); +} + +/* documentation is in fttrigon.h */ + +GD_FT_Angle GD_FT_Atan2(GD_FT_Fixed dx, GD_FT_Fixed dy) +{ + GD_FT_Vector v; + + if (dx == 0 && dy == 0) + return 0; + + v.x = dx; + v.y = dy; + ft_trig_prenorm(&v); + ft_trig_pseudo_polarize(&v); + + return v.y; +} + +/* documentation is in fttrigon.h */ + +void GD_FT_Vector_Unit(GD_FT_Vector *vec, GD_FT_Angle angle) +{ + vec->x = GD_FT_TRIG_SCALE >> 8; + vec->y = 0; + ft_trig_pseudo_rotate(vec, angle); + vec->x = (vec->x + 0x80L) >> 8; + vec->y = (vec->y + 0x80L) >> 8; +} + +/* these macros return 0 for positive numbers, + and -1 for negative ones */ +#define GD_FT_SIGN_LONG(x) ((x) >> (GD_FT_SIZEOF_LONG * 8 - 1)) +#define GD_FT_SIGN_INT(x) ((x) >> (GD_FT_SIZEOF_INT * 8 - 1)) +#define GD_FT_SIGN_INT32(x) ((x) >> 31) +#define GD_FT_SIGN_INT16(x) ((x) >> 15) + +/* documentation is in fttrigon.h */ + +void GD_FT_Vector_Rotate(GD_FT_Vector *vec, GD_FT_Angle angle) +{ + GD_FT_Int shift; + GD_FT_Vector v; + + v.x = vec->x; + v.y = vec->y; + + if (angle && (v.x != 0 || v.y != 0)) { + shift = ft_trig_prenorm(&v); + ft_trig_pseudo_rotate(&v, angle); + v.x = ft_trig_downscale(v.x); + v.y = ft_trig_downscale(v.y); + + if (shift > 0) { + GD_FT_Int32 half = (GD_FT_Int32)1L << (shift - 1); + + vec->x = (v.x + half + GD_FT_SIGN_LONG(v.x)) >> shift; + vec->y = (v.y + half + GD_FT_SIGN_LONG(v.y)) >> shift; + } else { + shift = -shift; + vec->x = (GD_FT_Pos)((GD_FT_ULong)v.x << shift); + vec->y = (GD_FT_Pos)((GD_FT_ULong)v.y << shift); + } + } +} + +/* documentation is in fttrigon.h */ + +GD_FT_Fixed GD_FT_Vector_Length(GD_FT_Vector *vec) +{ + GD_FT_Int shift; + GD_FT_Vector v; + + v = *vec; + + /* handle trivial cases */ + if (v.x == 0) { + return GD_FT_ABS(v.y); + } else if (v.y == 0) { + return GD_FT_ABS(v.x); + } + + /* general case */ + shift = ft_trig_prenorm(&v); + ft_trig_pseudo_polarize(&v); + + v.x = ft_trig_downscale(v.x); + + if (shift > 0) + return (v.x + (1 << (shift - 1))) >> shift; + + return (GD_FT_Fixed)((GD_FT_UInt32)v.x << -shift); +} + +/* documentation is in fttrigon.h */ + +void GD_FT_Vector_Polarize(GD_FT_Vector *vec, GD_FT_Fixed *length, GD_FT_Angle *angle) +{ + GD_FT_Int shift; + GD_FT_Vector v; + + v = *vec; + + if (v.x == 0 && v.y == 0) + return; + + shift = ft_trig_prenorm(&v); + ft_trig_pseudo_polarize(&v); + + v.x = ft_trig_downscale(v.x); + + *length = (shift >= 0) ? (v.x >> shift) : (GD_FT_Fixed)((GD_FT_UInt32)v.x << -shift); + *angle = v.y; +} + +/* documentation is in fttrigon.h */ + +void GD_FT_Vector_From_Polar(GD_FT_Vector *vec, GD_FT_Fixed length, GD_FT_Angle angle) +{ + vec->x = length; + vec->y = 0; + + GD_FT_Vector_Rotate(vec, angle); +} + +/* documentation is in fttrigon.h */ + +GD_FT_Angle GD_FT_Angle_Diff(GD_FT_Angle angle1, GD_FT_Angle angle2) +{ + GD_FT_Angle delta = angle2 - angle1; + + while (delta <= -GD_FT_ANGLE_PI) + delta += GD_FT_ANGLE_2PI; + + while (delta > GD_FT_ANGLE_PI) + delta -= GD_FT_ANGLE_2PI; + + return delta; +} + +/* END */ diff --git a/ext/gd/libgd/ftraster/gd_ft_math.h b/ext/gd/libgd/ftraster/gd_ft_math.h new file mode 100644 index 000000000000..da332c1ff407 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_math.h @@ -0,0 +1,391 @@ +#ifndef GD_FT_MATH_H +#define GD_FT_MATH_H + +/***************************************************************************/ +/* */ +/* fttrigon.h */ +/* */ +/* FreeType trigonometric functions (specification). */ +/* */ +/* Copyright 2001, 2003, 2005, 2007, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "gd_ft_types.h" + +/*************************************************************************/ +/* */ +/* The min and max functions missing in C. As usual, be careful not to */ +/* write things like GD_FT_MIN( a++, b++ ) to avoid side effects. */ +/* */ +#define GD_FT_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GD_FT_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define GD_FT_ABS(a) ((a) < 0 ? -(a) : (a)) + +/* + * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' + * algorithm. We use alpha = 1, beta = 3/8, giving us results with a + * largest error less than 7% compared to the exact value. + */ +#define GD_FT_HYPOT(x, y) \ + (x = GD_FT_ABS(x), y = GD_FT_ABS(y), x > y ? x + (3 * y >> 3) : y + (3 * x >> 3)) + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_MulFix */ +/* */ +/* */ +/* A very simple function used to perform the computation */ +/* `(a*b)/0x10000' with maximum accuracy. Most of the time this is */ +/* used to multiply a given value by a 16.16 fixed-point factor. */ +/* */ +/* */ +/* a :: The first multiplier. */ +/* b :: The second multiplier. Use a 16.16 factor here whenever */ +/* possible (see note below). */ +/* */ +/* */ +/* The result of `(a*b)/0x10000'. */ +/* */ +/* */ +/* This function has been optimized for the case where the absolute */ +/* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ +/* As this happens mainly when scaling from notional units to */ +/* fractional pixels in FreeType, it resulted in noticeable speed */ +/* improvements between versions 2.x and 1.x. */ +/* */ +/* As a conclusion, always try to place a 16.16 factor as the */ +/* _second_ argument of this function; this can make a great */ +/* difference. */ +/* */ +GD_FT_Long GD_FT_MulFix(GD_FT_Long a, GD_FT_Long b); + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_MulDiv */ +/* */ +/* */ +/* A very simple function used to perform the computation `(a*b)/c' */ +/* with maximum accuracy (it uses a 64-bit intermediate integer */ +/* whenever necessary). */ +/* */ +/* This function isn't necessarily as fast as some processor specific */ +/* operations, but is at least completely portable. */ +/* */ +/* */ +/* a :: The first multiplier. */ +/* b :: The second multiplier. */ +/* c :: The divisor. */ +/* */ +/* */ +/* The result of `(a*b)/c'. This function never traps when trying to */ +/* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ +/* on the signs of `a' and `b'. */ +/* */ +GD_FT_Long GD_FT_MulDiv(GD_FT_Long a, GD_FT_Long b, GD_FT_Long c); + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_DivFix */ +/* */ +/* */ +/* A very simple function used to perform the computation */ +/* `(a*0x10000)/b' with maximum accuracy. Most of the time, this is */ +/* used to divide a given value by a 16.16 fixed-point factor. */ +/* */ +/* */ +/* a :: The numerator. */ +/* b :: The denominator. Use a 16.16 factor here. */ +/* */ +/* */ +/* The result of `(a*0x10000)/b'. */ +/* */ +GD_FT_Long GD_FT_DivFix(GD_FT_Long a, GD_FT_Long b); + +/*************************************************************************/ +/* */ +/*
*/ +/* computations */ +/* */ +/*************************************************************************/ + +/************************************************************************* + * + * @type: + * GD_FT_Angle + * + * @description: + * This type is used to model angle values in FreeType. Note that the + * angle is a 16.16 fixed-point value expressed in degrees. + * + */ +typedef GD_FT_Fixed GD_FT_Angle; + +/************************************************************************* + * + * @macro: + * GD_FT_ANGLE_PI + * + * @description: + * The angle pi expressed in @GD_FT_Angle units. + * + */ +#define GD_FT_ANGLE_PI (180L << 16) + +/************************************************************************* + * + * @macro: + * GD_FT_ANGLE_2PI + * + * @description: + * The angle 2*pi expressed in @GD_FT_Angle units. + * + */ +#define GD_FT_ANGLE_2PI (GD_FT_ANGLE_PI * 2) + +/************************************************************************* + * + * @macro: + * GD_FT_ANGLE_PI2 + * + * @description: + * The angle pi/2 expressed in @GD_FT_Angle units. + * + */ +#define GD_FT_ANGLE_PI2 (GD_FT_ANGLE_PI / 2) + +/************************************************************************* + * + * @macro: + * GD_FT_ANGLE_PI4 + * + * @description: + * The angle pi/4 expressed in @GD_FT_Angle units. + * + */ +#define GD_FT_ANGLE_PI4 (GD_FT_ANGLE_PI / 4) + +/************************************************************************* + * + * @function: + * GD_FT_Sin + * + * @description: + * Return the sinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The sinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @GD_FT_Vector_Unit. + * + */ +GD_FT_Fixed GD_FT_Sin(GD_FT_Angle angle); + +/************************************************************************* + * + * @function: + * GD_FT_Cos + * + * @description: + * Return the cosinus of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The cosinus value. + * + * @note: + * If you need both the sinus and cosinus for a given angle, use the + * function @GD_FT_Vector_Unit. + * + */ +GD_FT_Fixed GD_FT_Cos(GD_FT_Angle angle); + +/************************************************************************* + * + * @function: + * GD_FT_Tan + * + * @description: + * Return the tangent of a given angle in fixed-point format. + * + * @input: + * angle :: + * The input angle. + * + * @return: + * The tangent value. + * + */ +GD_FT_Fixed GD_FT_Tan(GD_FT_Angle angle); + +/************************************************************************* + * + * @function: + * GD_FT_Atan2 + * + * @description: + * Return the arc-tangent corresponding to a given vector (x,y) in + * the 2d plane. + * + * @input: + * x :: + * The horizontal vector coordinate. + * + * y :: + * The vertical vector coordinate. + * + * @return: + * The arc-tangent value (i.e. angle). + * + */ +GD_FT_Angle GD_FT_Atan2(GD_FT_Fixed x, GD_FT_Fixed y); + +/************************************************************************* + * + * @function: + * GD_FT_Angle_Diff + * + * @description: + * Return the difference between two angles. The result is always + * constrained to the ]-PI..PI] interval. + * + * @input: + * angle1 :: + * First angle. + * + * angle2 :: + * Second angle. + * + * @return: + * Constrained value of `value2-value1'. + * + */ +GD_FT_Angle GD_FT_Angle_Diff(GD_FT_Angle angle1, GD_FT_Angle angle2); + +/************************************************************************* + * + * @function: + * GD_FT_Vector_Unit + * + * @description: + * Return the unit vector corresponding to a given angle. After the + * call, the value of `vec.x' will be `sin(angle)', and the value of + * `vec.y' will be `cos(angle)'. + * + * This function is useful to retrieve both the sinus and cosinus of a + * given angle quickly. + * + * @output: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ +void GD_FT_Vector_Unit(GD_FT_Vector *vec, GD_FT_Angle angle); + +/************************************************************************* + * + * @function: + * GD_FT_Vector_Rotate + * + * @description: + * Rotate a vector by a given angle. + * + * @inout: + * vec :: + * The address of target vector. + * + * @input: + * angle :: + * The input angle. + * + */ +void GD_FT_Vector_Rotate(GD_FT_Vector *vec, GD_FT_Angle angle); + +/************************************************************************* + * + * @function: + * GD_FT_Vector_Length + * + * @description: + * Return the length of a given vector. + * + * @input: + * vec :: + * The address of target vector. + * + * @return: + * The vector length, expressed in the same units that the original + * vector coordinates. + * + */ +GD_FT_Fixed GD_FT_Vector_Length(GD_FT_Vector *vec); + +/************************************************************************* + * + * @function: + * GD_FT_Vector_Polarize + * + * @description: + * Compute both the length and angle of a given vector. + * + * @input: + * vec :: + * The address of source vector. + * + * @output: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ +void GD_FT_Vector_Polarize(GD_FT_Vector *vec, GD_FT_Fixed *length, GD_FT_Angle *angle); + +/************************************************************************* + * + * @function: + * GD_FT_Vector_From_Polar + * + * @description: + * Compute vector coordinates from a length and angle. + * + * @output: + * vec :: + * The address of source vector. + * + * @input: + * length :: + * The vector length. + * + * angle :: + * The vector angle. + * + */ +void GD_FT_Vector_From_Polar(GD_FT_Vector *vec, GD_FT_Fixed length, GD_FT_Angle angle); + +#endif // GD_FT_MATH_H diff --git a/ext/gd/libgd/ftraster/gd_ft_raster.c b/ext/gd/libgd/ftraster/gd_ft_raster.c new file mode 100644 index 000000000000..6891f7da2c32 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_raster.c @@ -0,0 +1,1723 @@ +/**************************************************************************** + * + * ftgrays.c + * + * A new `perfect' anti-aliasing renderer (body). + * + * Copyright (C) 2000-2020 by + * David Turner, Robert Wilhelm, and Werner Lemberg. + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + */ + +/*************************************************************************/ +/* */ +/* This is a new anti-aliasing scan-converter for FreeType 2. The */ +/* algorithm used here is _very_ different from the one in the standard */ +/* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ +/* coverage of the outline on each pixel cell. */ +/* */ +/* It is based on ideas that I initially found in Raph Levien's */ +/* excellent LibArt graphics library (see http://www.levien.com/libart */ +/* for more information, though the web pages do not tell anything */ +/* about the renderer; you'll have to dive into the source code to */ +/* understand how it works). */ +/* */ +/* Note, however, that this is a _very_ different implementation */ +/* compared to Raph's. Coverage information is stored in a very */ +/* different way, and I don't use sorted vector paths. Also, it doesn't */ +/* use floating point values. */ +/* */ +/* This renderer has the following advantages: */ +/* */ +/* - It doesn't need an intermediate bitmap. Instead, one can supply a */ +/* callback function that will be called by the renderer to draw gray */ +/* spans on any target surface. You can thus do direct composition on */ +/* any kind of bitmap, provided that you give the renderer the right */ +/* callback. */ +/* */ +/* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ +/* each pixel cell. */ +/* */ +/* - It performs a single pass on the outline (the `standard' FT2 */ +/* renderer makes two passes). */ +/* */ +/* - It can easily be modified to render to _any_ number of gray levels */ +/* cheaply. */ +/* */ +/* - For small (< 20) pixel sizes, it is faster than the standard */ +/* renderer. */ +/* */ +/*************************************************************************/ + +#include "gd_ft_raster.h" +#include "gd_ft_math.h" +#include "gd_path_matrix.h" +#include "gd_vector2d_private.h" + +/* Auxiliary macros for token concatenation. */ +#define GD_FT_ERR_XCAT(x, y) x##y +#define GD_FT_ERR_CAT(x, y) GD_FT_ERR_XCAT(x, y) + +#define GD_FT_BEGIN_STMNT do { +#define GD_FT_END_STMNT \ + } \ + while (0) + +#include +#include +#include +#include +#define GD_FT_UINT_MAX UINT_MAX +#define GD_FT_INT_MAX INT_MAX +#define GD_FT_ULONG_MAX ULONG_MAX +#define GD_FT_CHAR_BIT CHAR_BIT + +#define ft_memset memset + +#define ft_setjmp setjmp +#define ft_longjmp longjmp +#define ft_jmp_buf jmp_buf + +typedef ptrdiff_t GD_FT_PtrDist; + +#define ErrRaster_Invalid_Mode -2 +#define ErrRaster_Invalid_Outline -1 +#define ErrRaster_Invalid_Argument -3 +#define ErrRaster_Memory_Overflow -4 + +#define GD_FT_BEGIN_HEADER +#define GD_FT_END_HEADER + +/* This macro is used to indicate that a function parameter is unused. */ +/* Its purpose is simply to reduce compiler warnings. Note also that */ +/* simply defining it as `(void)x' doesn't avoid warnings with certain */ +/* ANSI compilers (e.g. LCC). */ +#define GD_FT_UNUSED(x) (x) = (x) + +#define GD_FT_THROW(e) GD_FT_ERR_CAT(ErrRaster_, e) + +/* The size in bytes of the render pool used by the scan-line converter */ +/* to do all of its work. */ +#define GD_FT_RENDER_POOL_SIZE 16384L + +typedef int (*GD_FT_Outline_MoveToFunc)(const GD_FT_Vector *to, void *user); + +#define GD_FT_Outline_MoveTo_Func GD_FT_Outline_MoveToFunc + +typedef int (*GD_FT_Outline_LineToFunc)(const GD_FT_Vector *to, void *user); + +#define GD_FT_Outline_LineTo_Func GD_FT_Outline_LineToFunc + +typedef int (*GD_FT_Outline_ConicToFunc)(const GD_FT_Vector *control, const GD_FT_Vector *to, + void *user); + +#define GD_FT_Outline_ConicTo_Func GD_FT_Outline_ConicToFunc + +typedef int (*GD_FT_Outline_CubicToFunc)(const GD_FT_Vector *control1, const GD_FT_Vector *control2, + const GD_FT_Vector *to, void *user); + +#define GD_FT_Outline_CubicTo_Func GD_FT_Outline_CubicToFunc + +typedef struct GD_FT_Outline_Funcs_ { + GD_FT_Outline_MoveToFunc move_to; + GD_FT_Outline_LineToFunc line_to; + GD_FT_Outline_ConicToFunc conic_to; + GD_FT_Outline_CubicToFunc cubic_to; + + int shift; + GD_FT_Pos delta; + +} GD_FT_Outline_Funcs; + +#define GD_FT_DEFINE_OUTLINE_FUNCS(class_, move_to_, line_to_, conic_to_, cubic_to_, shift_, \ + delta_) \ + static const GD_FT_Outline_Funcs class_ = {move_to_, line_to_, conic_to_, \ + cubic_to_, shift_, delta_}; + +#define GD_FT_DEFINE_RASTER_FUNCS(class_, raster_new_, raster_reset_, raster_render_, \ + raster_done_) \ + const GD_FT_Raster_Funcs class_ = {raster_new_, raster_reset_, raster_render_, raster_done_}; + +#ifndef GD_FT_MEM_SET +#define GD_FT_MEM_SET(d, s, c) ft_memset(d, s, c) +#endif + +#ifndef GD_FT_MEM_ZERO +#define GD_FT_MEM_ZERO(dest, count) GD_FT_MEM_SET(dest, 0, count) +#endif + +/* as usual, for the speed hungry :-) */ + +#undef RAS_ARG +#undef RAS_ARG_ +#undef RAS_VAR +#undef RAS_VAR_ + +#ifndef GD_FT_STATIC_RASTER + +#define RAS_ARG gray_PWorker worker +#define RAS_ARG_ gray_PWorker worker, + +#define RAS_VAR worker +#define RAS_VAR_ worker, + +#else /* GD_FT_STATIC_RASTER */ + +#define RAS_ARG /* empty */ +#define RAS_ARG_ /* empty */ +#define RAS_VAR /* empty */ +#define RAS_VAR_ /* empty */ + +#endif /* GD_FT_STATIC_RASTER */ + +/* must be at least 6 bits! */ +#define PIXEL_BITS 8 + +#undef FLOOR +#undef CEILING +#undef TRUNC +#undef SCALED + +#define ONE_PIXEL (1L << PIXEL_BITS) +#define PIXEL_MASK (-1L << PIXEL_BITS) +#define TRUNC(x) ((TCoord)((x) >> PIXEL_BITS)) +#define SUBPIXELS(x) ((TPos)(x) * ONE_PIXEL) +#define FLOOR(x) ((x) & -ONE_PIXEL) +#define CEILING(x) (((x) + ONE_PIXEL - 1) & -ONE_PIXEL) +#define ROUND(x) (((x) + ONE_PIXEL / 2) & -ONE_PIXEL) + +#if PIXEL_BITS >= 6 +#define UPSCALE(x) ((x) * (1L << (PIXEL_BITS - 6))) +#define DOWNSCALE(x) ((x) >> (PIXEL_BITS - 6)) +#else +#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS)) +#define DOWNSCALE(x) ((x) * (1L << (6 - PIXEL_BITS))) +#endif + +/* Compute `dividend / divisor' and return both its quotient and */ +/* remainder, cast to a specific type. This macro also ensures that */ +/* the remainder is always positive. */ +#define GD_FT_DIV_MOD(type, dividend, divisor, quotient, remainder) \ + GD_FT_BEGIN_STMNT(quotient) = (type)((dividend) / (divisor)); \ + (remainder) = (type)((dividend) % (divisor)); \ + if ((remainder) < 0) { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + GD_FT_END_STMNT + +#ifdef __arm__ +/* Work around a bug specific to GCC which make the compiler fail to */ +/* optimize a division and modulo operation on the same parameters */ +/* into a single call to `__aeabi_idivmod'. See */ +/* */ +/* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */ +#undef GD_FT_DIV_MOD +#define GD_FT_DIV_MOD(type, dividend, divisor, quotient, remainder) \ + GD_FT_BEGIN_STMNT(quotient) = (type)((dividend) / (divisor)); \ + (remainder) = (type)((dividend) - (quotient) * (divisor)); \ + if ((remainder) < 0) { \ + (quotient)--; \ + (remainder) += (type)(divisor); \ + } \ + GD_FT_END_STMNT +#endif /* __arm__ */ + +/* These macros speed up repetitive divisions by replacing them */ +/* with multiplications and right shifts. */ +#define GD_FT_UDIVPREP(b) long b##_r = (long)(GD_FT_ULONG_MAX >> PIXEL_BITS) / (b) +#define GD_FT_UDIV(a, b) \ + (((unsigned long)(a) * (unsigned long)(b##_r)) >> (sizeof(long) * GD_FT_CHAR_BIT - PIXEL_BITS)) + +/*************************************************************************/ +/* */ +/* TYPE DEFINITIONS */ +/* */ + +/* don't change the following types to GD_FT_Int or GD_FT_Pos, since we might */ +/* need to define them to "float" or "double" when experimenting with */ +/* new algorithms */ + +typedef long TCoord; /* integer scanline/pixel coordinate */ +typedef long TPos; /* sub-pixel coordinate */ + +/* determine the type used to store cell areas. This normally takes at */ +/* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ +/* `long' instead of `int', otherwise bad things happen */ + +#if PIXEL_BITS <= 7 + +typedef int TArea; + +#else /* PIXEL_BITS >= 8 */ + +/* approximately determine the size of integers using an ANSI-C header */ +#if GD_FT_UINT_MAX == 0xFFFFU +typedef long TArea; +#else +typedef int TArea; +#endif + +#endif /* PIXEL_BITS >= 8 */ + +/* maximum number of gray spans in a call to the span callback */ +#define GD_FT_MAX_GRAY_SPANS 256 + +typedef struct TCell_ *PCell; + +typedef struct TCell_ { + TPos x; /* same with gray_TWorker.ex */ + TCoord cover; /* same with gray_TWorker.cover */ + TArea area; + PCell next; + +} TCell; + +#if defined(_MSC_VER) /* Visual C++ (and Intel C++) */ +/* We disable the warning `structure was padded due to */ +/* __declspec(align())' in order to compile cleanly with */ +/* the maximum level of warnings. */ +#pragma warning(push) +#pragma warning(disable : 4324) +#endif /* _MSC_VER */ + +typedef struct gray_TWorker_ { + TCoord ex, ey; + TPos min_ex, max_ex; + TPos min_ey, max_ey; + TPos count_ex, count_ey; + + TArea area; + TCoord cover; + int invalid; + + PCell cells; + GD_FT_PtrDist max_cells; + GD_FT_PtrDist num_cells; + + TPos x, y; + + GD_FT_Vector bez_stack[32 * 3 + 1]; + int lev_stack[32]; + + GD_FT_Outline outline; + GD_FT_BBox clip_box; + + int bound_left; + int bound_top; + int bound_right; + int bound_bottom; + + GD_FT_Span gray_spans[GD_FT_MAX_GRAY_SPANS]; + int num_gray_spans; + + GD_FT_Raster_Span_Func render_span; + void *render_span_data; + + int band_size; + int band_shoot; + + ft_jmp_buf jump_buffer; + + void *buffer; + long buffer_size; + + PCell *ycells; + TPos ycount; + + /* Source abstraction for direct path rendering */ + struct gray_TSource_ *source; +} gray_TWorker, *gray_PWorker; + +typedef struct gray_TSource_ { + int (*get_cbox)(void *source, GD_FT_BBox *box); + int (*decompose)(void *source, void *worker); + void *source; +} gray_TSource; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#ifndef GD_FT_STATIC_RASTER +#define ras (*worker) +#else +static gray_TWorker ras; +#endif + +typedef struct gray_TRaster_ { + void *memory; + +} gray_TRaster, *gray_PRaster; + +/*************************************************************************/ +/* */ +/* Initialize the cells table. */ +/* */ +static void gray_init_cells(RAS_ARG_ void *buffer, long byte_size) +{ + ras.buffer = buffer; + ras.buffer_size = byte_size; + + ras.ycells = (PCell *)buffer; + ras.cells = NULL; + ras.max_cells = 0; + ras.num_cells = 0; + ras.area = 0; + ras.cover = 0; + ras.invalid = 1; + + ras.bound_left = INT_MAX; + ras.bound_top = INT_MAX; + ras.bound_right = INT_MIN; + ras.bound_bottom = INT_MIN; +} + +/*************************************************************************/ +/* */ +/* Compute the outline bounding box. */ +/* */ +static void gray_compute_cbox(RAS_ARG) +{ + if (ras.source && ras.source->get_cbox) { + GD_FT_BBox box; + int error = ras.source->get_cbox(ras.source->source, &box); + if (!error) { + ras.min_ex = box.xMin >> 6; + ras.min_ey = box.yMin >> 6; + ras.max_ex = (box.xMax + 63) >> 6; + ras.max_ey = (box.yMax + 63) >> 6; + return; + } + } + + /* Fallback to outline */ + GD_FT_Outline *outline = &ras.outline; + GD_FT_Vector *vec = outline->points; + GD_FT_Vector *limit = vec + outline->n_points; + + if (outline->n_points <= 0) { + ras.min_ex = ras.max_ex = 0; + ras.min_ey = ras.max_ey = 0; + return; + } + + ras.min_ex = ras.max_ex = vec->x; + ras.min_ey = ras.max_ey = vec->y; + + vec++; + + for (; vec < limit; vec++) { + TPos x = vec->x; + TPos y = vec->y; + + if (x < ras.min_ex) + ras.min_ex = x; + if (x > ras.max_ex) + ras.max_ex = x; + if (y < ras.min_ey) + ras.min_ey = y; + if (y > ras.max_ey) + ras.max_ey = y; + } + + /* truncate the bounding box to integer pixels */ + ras.min_ex = ras.min_ex >> 6; + ras.min_ey = ras.min_ey >> 6; + ras.max_ex = (ras.max_ex + 63) >> 6; + ras.max_ey = (ras.max_ey + 63) >> 6; +} + +/*************************************************************************/ +/* */ +/* Record the current cell in the table. */ +/* */ +static PCell gray_find_cell(RAS_ARG) +{ + PCell *pcell, cell; + TPos x = ras.ex; + + if (x > ras.count_ex) + x = ras.count_ex; + + pcell = &ras.ycells[ras.ey]; + for (;;) { + cell = *pcell; + if (cell == NULL || cell->x > x) + break; + + if (cell->x == x) + goto Exit; + + pcell = &cell->next; + } + + if (ras.num_cells >= ras.max_cells) + ft_longjmp(ras.jump_buffer, 1); + + cell = ras.cells + ras.num_cells++; + cell->x = x; + cell->area = 0; + cell->cover = 0; + + cell->next = *pcell; + *pcell = cell; + +Exit: + return cell; +} + +static void gray_record_cell(RAS_ARG) +{ + if (ras.area | ras.cover) { + PCell cell = gray_find_cell(RAS_VAR); + + cell->area += ras.area; + cell->cover += ras.cover; + } +} + +/*************************************************************************/ +/* */ +/* Set the current cell to a new position. */ +/* */ +static void gray_set_cell(RAS_ARG_ TCoord ex, TCoord ey) +{ + /* Move the cell pointer to a new position. We set the `invalid' */ + /* flag to indicate that the cell isn't part of those we're interested */ + /* in during the render phase. This means that: */ + /* */ + /* . the new vertical position must be within min_ey..max_ey-1. */ + /* . the new horizontal position must be strictly less than max_ex */ + /* */ + /* Note that if a cell is to the left of the clipping region, it is */ + /* actually set to the (min_ex-1) horizontal position. */ + + /* All cells that are on the left of the clipping region go to the */ + /* min_ex - 1 horizontal position. */ + ey -= ras.min_ey; + + if (ex > ras.max_ex) + ex = ras.max_ex; + + ex -= ras.min_ex; + if (ex < 0) + ex = -1; + + /* are we moving to a different cell ? */ + if (ex != ras.ex || ey != ras.ey) { + /* record the current one if it is valid */ + if (!ras.invalid) + gray_record_cell(RAS_VAR); + + ras.area = 0; + ras.cover = 0; + ras.ex = ex; + ras.ey = ey; + } + + ras.invalid = ((unsigned)ey >= (unsigned)ras.count_ey || ex >= ras.count_ex); +} + +/*************************************************************************/ +/* */ +/* Start a new contour at a given cell. */ +/* */ +static void gray_start_cell(RAS_ARG_ TCoord ex, TCoord ey) +{ + if (ex > ras.max_ex) + ex = (TCoord)(ras.max_ex); + + if (ex < ras.min_ex) + ex = (TCoord)(ras.min_ex - 1); + + ras.area = 0; + ras.cover = 0; + ras.ex = ex - ras.min_ex; + ras.ey = ey - ras.min_ey; + ras.invalid = 0; + + gray_set_cell(RAS_VAR_ ex, ey); +} + +/*************************************************************************/ +/* */ +/* Render a straight line across multiple cells in any direction. */ +/* */ +static void gray_render_line(RAS_ARG_ TPos to_x, TPos to_y) +{ + TPos dx, dy, fx1, fy1, fx2, fy2; + TCoord ex1, ex2, ey1, ey2; + + ex1 = TRUNC(ras.x); + ex2 = TRUNC(to_x); + ey1 = TRUNC(ras.y); + ey2 = TRUNC(to_y); + + /* perform vertical clipping */ + if ((ey1 >= ras.max_ey && ey2 >= ras.max_ey) || (ey1 < ras.min_ey && ey2 < ras.min_ey)) + goto End; + + dx = to_x - ras.x; + dy = to_y - ras.y; + + fx1 = ras.x - SUBPIXELS(ex1); + fy1 = ras.y - SUBPIXELS(ey1); + + if (ex1 == ex2 && ey1 == ey2) /* inside one cell */ + ; + else if (dy == 0) /* ex1 != ex2 */ /* any horizontal line */ + { + ex1 = ex2; + gray_set_cell(RAS_VAR_ ex1, ey1); + } else if (dx == 0) { + if (dy > 0) /* vertical line up */ + do { + fy2 = ONE_PIXEL; + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * fx1 * 2; + fy1 = 0; + ey1++; + gray_set_cell(RAS_VAR_ ex1, ey1); + } while (ey1 != ey2); + else /* vertical line down */ + do { + fy2 = 0; + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * fx1 * 2; + fy1 = ONE_PIXEL; + ey1--; + gray_set_cell(RAS_VAR_ ex1, ey1); + } while (ey1 != ey2); + } else /* any other line */ + { + TArea prod = dx * fy1 - dy * fx1; + GD_FT_UDIVPREP(dx); + GD_FT_UDIVPREP(dy); + + /* The fundamental value `prod' determines which side and the */ + /* exact coordinate where the line exits current cell. It is */ + /* also easily updated when moving from one cell to the next. */ + do { + if (prod <= 0 && prod - dx * ONE_PIXEL > 0) /* left */ + { + fx2 = 0; + fy2 = (TPos)GD_FT_UDIV(-prod, -dx); + prod -= dy * ONE_PIXEL; + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * (fx1 + fx2); + fx1 = ONE_PIXEL; + fy1 = fy2; + ex1--; + } else if (prod - dx * ONE_PIXEL <= 0 && + prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0) /* up */ + { + prod -= dx * ONE_PIXEL; + fx2 = (TPos)GD_FT_UDIV(-prod, dy); + fy2 = ONE_PIXEL; + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * (fx1 + fx2); + fx1 = fx2; + fy1 = 0; + ey1++; + } else if (prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && + prod + dy * ONE_PIXEL >= 0) /* right */ + { + prod += dy * ONE_PIXEL; + fx2 = ONE_PIXEL; + fy2 = (TPos)GD_FT_UDIV(prod, dx); + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * (fx1 + fx2); + fx1 = 0; + fy1 = fy2; + ex1++; + } else /* ( prod + dy * ONE_PIXEL < 0 && + prod > 0 ) down */ + { + fx2 = (TPos)GD_FT_UDIV(prod, -dy); + fy2 = 0; + prod += dx * ONE_PIXEL; + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * (fx1 + fx2); + fx1 = fx2; + fy1 = ONE_PIXEL; + ey1--; + } + + gray_set_cell(RAS_VAR_ ex1, ey1); + } while (ex1 != ex2 || ey1 != ey2); + } + + fx2 = to_x - SUBPIXELS(ex2); + fy2 = to_y - SUBPIXELS(ey2); + + ras.cover += (fy2 - fy1); + ras.area += (fy2 - fy1) * (fx1 + fx2); + +End: + ras.x = to_x; + ras.y = to_y; +} + +static void gray_split_conic(GD_FT_Vector *base) +{ + TPos a, b; + + base[4].x = base[2].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + base[3].x = b >> 1; + base[2].x = (a + b) >> 2; + base[1].x = a >> 1; + + base[4].y = base[2].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + base[3].y = b >> 1; + base[2].y = (a + b) >> 2; + base[1].y = a >> 1; +} + +static void gray_render_conic(RAS_ARG_ const GD_FT_Vector *control, const GD_FT_Vector *to) +{ + TPos dx, dy; + TPos min, max, y; + int top, level; + int *levels; + GD_FT_Vector *arc; + + levels = ras.lev_stack; + + arc = ras.bez_stack; + arc[0].x = UPSCALE(to->x); + arc[0].y = UPSCALE(to->y); + arc[1].x = UPSCALE(control->x); + arc[1].y = UPSCALE(control->y); + arc[2].x = ras.x; + arc[2].y = ras.y; + top = 0; + + dx = GD_FT_ABS(arc[2].x + arc[0].x - 2 * arc[1].x); + dy = GD_FT_ABS(arc[2].y + arc[0].y - 2 * arc[1].y); + if (dx < dy) + dx = dy; + + if (dx < ONE_PIXEL / 4) + goto Draw; + + /* short-cut the arc that crosses the current band */ + min = max = arc[0].y; + + y = arc[1].y; + if (y < min) + min = y; + if (y > max) + max = y; + + y = arc[2].y; + if (y < min) + min = y; + if (y > max) + max = y; + + if (TRUNC(min) >= ras.max_ey || TRUNC(max) < ras.min_ey) + goto Draw; + + level = 0; + do { + dx >>= 2; + level++; + } while (dx > ONE_PIXEL / 4); + + levels[0] = level; + + do { + level = levels[top]; + if (level > 0) { + gray_split_conic(arc); + arc += 2; + top++; + levels[top] = levels[top - 1] = level - 1; + continue; + } + + Draw: + gray_render_line(RAS_VAR_ arc[0].x, arc[0].y); + top--; + arc -= 2; + + } while (top >= 0); +} + +static void gray_split_cubic(GD_FT_Vector *base) +{ + TPos a, b, c; + + base[6].x = base[3].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + c = base[2].x + base[3].x; + base[5].x = c >> 1; + c += b; + base[4].x = c >> 2; + base[1].x = a >> 1; + a += b; + base[2].x = a >> 2; + base[3].x = (a + c) >> 3; + + base[6].y = base[3].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + c = base[2].y + base[3].y; + base[5].y = c >> 1; + c += b; + base[4].y = c >> 2; + base[1].y = a >> 1; + a += b; + base[2].y = a >> 2; + base[3].y = (a + c) >> 3; +} + +static void gray_render_cubic(RAS_ARG_ const GD_FT_Vector *control1, const GD_FT_Vector *control2, + const GD_FT_Vector *to) +{ + GD_FT_Vector *arc = ras.bez_stack; + + arc[0].x = UPSCALE(to->x); + arc[0].y = UPSCALE(to->y); + arc[1].x = UPSCALE(control2->x); + arc[1].y = UPSCALE(control2->y); + arc[2].x = UPSCALE(control1->x); + arc[2].y = UPSCALE(control1->y); + arc[3].x = ras.x; + arc[3].y = ras.y; + + /* short-cut the arc that crosses the current band */ + if ((TRUNC(arc[0].y) >= ras.max_ey && TRUNC(arc[1].y) >= ras.max_ey && + TRUNC(arc[2].y) >= ras.max_ey && TRUNC(arc[3].y) >= ras.max_ey) || + (TRUNC(arc[0].y) < ras.min_ey && TRUNC(arc[1].y) < ras.min_ey && + TRUNC(arc[2].y) < ras.min_ey && TRUNC(arc[3].y) < ras.min_ey)) { + ras.x = arc[0].x; + ras.y = arc[0].y; + return; + } + + for (;;) { + /* with each split, control points quickly converge towards */ + /* chord trisection points and the vanishing distances below */ + /* indicate when the segment is flat enough to draw */ + if (GD_FT_ABS(2 * arc[0].x - 3 * arc[1].x + arc[3].x) > ONE_PIXEL / 2 || + GD_FT_ABS(2 * arc[0].y - 3 * arc[1].y + arc[3].y) > ONE_PIXEL / 2 || + GD_FT_ABS(arc[0].x - 3 * arc[2].x + 2 * arc[3].x) > ONE_PIXEL / 2 || + GD_FT_ABS(arc[0].y - 3 * arc[2].y + 2 * arc[3].y) > ONE_PIXEL / 2) + goto Split; + + gray_render_line(RAS_VAR_ arc[0].x, arc[0].y); + + if (arc == ras.bez_stack) + return; + + arc -= 3; + continue; + + Split: + gray_split_cubic(arc); + arc += 3; + } +} + +static int gray_move_to(const GD_FT_Vector *to, gray_PWorker worker) +{ + TPos x, y; + + /* record current cell, if any */ + if (!ras.invalid) + gray_record_cell(RAS_VAR); + + /* start to a new position */ + x = UPSCALE(to->x); + y = UPSCALE(to->y); + + gray_start_cell(RAS_VAR_ TRUNC(x), TRUNC(y)); + + worker->x = x; + worker->y = y; + return 0; +} + +static int gray_line_to(const GD_FT_Vector *to, gray_PWorker worker) +{ + gray_render_line(RAS_VAR_ UPSCALE(to->x), UPSCALE(to->y)); + return 0; +} + +static int gray_conic_to(const GD_FT_Vector *control, const GD_FT_Vector *to, gray_PWorker worker) +{ + gray_render_conic(RAS_VAR_ control, to); + return 0; +} + +static int gray_cubic_to(const GD_FT_Vector *control1, const GD_FT_Vector *control2, + const GD_FT_Vector *to, gray_PWorker worker) +{ + gray_render_cubic(RAS_VAR_ control1, control2, to); + return 0; +} + +static void gray_hline(RAS_ARG_ TCoord x, TCoord y, TPos area, TCoord acount) +{ + int coverage; + + /* compute the coverage line's coverage, depending on the */ + /* outline fill rule */ + /* */ + /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ + /* */ + coverage = (int)(area >> (PIXEL_BITS * 2 + 1 - 8)); + /* use range 0..256 */ + if (coverage < 0) + coverage = -coverage; + + if (ras.outline.flags & GD_FT_OUTLINE_EVEN_ODD_FILL) { + coverage &= 511; + + if (coverage > 256) + coverage = 512 - coverage; + else if (coverage == 256) + coverage = 255; + } else { + /* normal non-zero winding rule */ + if (coverage >= 256) + coverage = 255; + } + + y += (TCoord)ras.min_ey; + x += (TCoord)ras.min_ex; + + /* GD_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ + if (x >= 32767) + x = 32767; + + /* GD_FT_Span.y is an integer, so limit our coordinates appropriately */ + if (y >= GD_FT_INT_MAX) + y = GD_FT_INT_MAX; + + if (coverage) { + GD_FT_Span *span; + int count; + + // update bounding box. + if (x < ras.bound_left) + ras.bound_left = x; + if (y < ras.bound_top) + ras.bound_top = y; + if (y > ras.bound_bottom) + ras.bound_bottom = y; + if (x + acount > ras.bound_right) + ras.bound_right = x + acount; + + /* see whether we can add this span to the current list */ + count = ras.num_gray_spans; + span = ras.gray_spans + count - 1; + if (count > 0 && span->y == y && (int)span->x + span->len == (int)x && + span->coverage == coverage) { + span->len = (unsigned short)(span->len + acount); + return; + } + + if (count >= GD_FT_MAX_GRAY_SPANS) { + if (ras.render_span && count > 0) + ras.render_span(count, ras.gray_spans, ras.render_span_data); + +#ifdef DEBUG_GRAYS + + if (1) { + int n; + + fprintf(stderr, "count = %3d ", count); + span = ras.gray_spans; + for (n = 0; n < count; n++, span++) + fprintf(stderr, "[%d , %d..%d] : %d ", span->y, span->x, + span->x + span->len - 1, span->coverage); + fprintf(stderr, "\n"); + } + +#endif /* DEBUG_GRAYS */ + + ras.num_gray_spans = 0; + + span = ras.gray_spans; + } else + span++; + + /* add a gray span to the current list */ + span->x = (short)x; + span->y = (short)y; + span->len = (unsigned short)acount; + span->coverage = (unsigned char)coverage; + + ras.num_gray_spans++; + } +} + +static void gray_sweep(RAS_ARG) +{ + int yindex; + + if (ras.num_cells == 0) + return; + + ras.num_gray_spans = 0; + + for (yindex = 0; yindex < ras.ycount; yindex++) { + PCell cell = ras.ycells[yindex]; + TCoord cover = 0; + TCoord x = 0; + + for (; cell != NULL; cell = cell->next) { + TPos area; + + if (cell->x > x && cover != 0) + gray_hline(RAS_VAR_ x, yindex, cover * (ONE_PIXEL * 2), cell->x - x); + + cover += cell->cover; + area = cover * (ONE_PIXEL * 2) - cell->area; + + if (area != 0 && cell->x >= 0) + gray_hline(RAS_VAR_ cell->x, yindex, area, 1); + + x = cell->x + 1; + } + + if (cover != 0) + gray_hline(RAS_VAR_ x, yindex, cover * (ONE_PIXEL * 2), ras.count_ex - x); + } + + if (ras.render_span && ras.num_gray_spans > 0) + ras.render_span(ras.num_gray_spans, ras.gray_spans, ras.render_span_data); +} + +/*************************************************************************/ +/* */ +/* The following function should only compile in stand-alone mode, */ +/* i.e., when building this component without the rest of FreeType. */ +/* */ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Outline_Decompose */ +/* */ +/* */ +/* Walk over an outline's structure to decompose it into individual */ +/* segments and Bézier arcs. This function is also able to emit */ +/* `move to' and `close to' operations to indicate the start and end */ +/* of new contours in the outline. */ +/* */ +/* */ +/* outline :: A pointer to the source target. */ +/* */ +/* func_interface :: A table of `emitters', i.e., function pointers */ +/* called during decomposition to indicate path */ +/* operations. */ +/* */ +/* */ +/* user :: A typeless pointer which is passed to each */ +/* emitter during the decomposition. It can be */ +/* used to store the state during the */ +/* decomposition. */ +/* */ +/* */ +/* Error code. 0 means success. */ +/* */ +static int GD_FT_Outline_Decompose(const GD_FT_Outline *outline, + const GD_FT_Outline_Funcs *func_interface, void *user) +{ +#undef SCALED +#define SCALED(x) (((TPos)(x) * (1L << shift)) - delta) + + GD_FT_Vector v_last; + GD_FT_Vector v_control; + GD_FT_Vector v_start; + + GD_FT_Vector *point; + GD_FT_Vector *limit; + char *tags; + + int error; + + int n; /* index of contour in outline */ + int first; /* index of first point in contour */ + char tag; /* current point's state */ + + int shift; + TPos delta; + + if (!outline || !func_interface) + return GD_FT_THROW(Invalid_Argument); + + shift = func_interface->shift; + delta = func_interface->delta; + first = 0; + + for (n = 0; n < outline->n_contours; n++) { + int last; /* index of last point in contour */ + + last = outline->contours[n]; + if (last < 0) + goto Invalid_Outline; + limit = outline->points + last; + + v_start = outline->points[first]; + v_start.x = SCALED(v_start.x); + v_start.y = SCALED(v_start.y); + + v_last = outline->points[last]; + v_last.x = SCALED(v_last.x); + v_last.y = SCALED(v_last.y); + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = GD_FT_CURVE_TAG(tags[0]); + + /* A contour cannot start with a cubic control point! */ + if (tag == GD_FT_CURVE_TAG_CUBIC) + goto Invalid_Outline; + + /* check first point to determine origin */ + if (tag == GD_FT_CURVE_TAG_CONIC) { + /* first point is conic control. Yes, this happens. */ + if (GD_FT_CURVE_TAG(outline->tags[last]) == GD_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; + } + point--; + tags--; + } + + error = func_interface->move_to(&v_start, user); + if (error) + goto Exit; + + while (point < limit) { + point++; + tags++; + + tag = GD_FT_CURVE_TAG(tags[0]); + switch (tag) { + case GD_FT_CURVE_TAG_ON: /* emit a single line_to */ + { + GD_FT_Vector vec; + + vec.x = SCALED(point->x); + vec.y = SCALED(point->y); + + error = func_interface->line_to(&vec, user); + if (error) + goto Exit; + continue; + } + + case GD_FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = SCALED(point->x); + v_control.y = SCALED(point->y); + + Do_Conic: + if (point < limit) { + GD_FT_Vector vec; + GD_FT_Vector v_middle; + + point++; + tags++; + tag = GD_FT_CURVE_TAG(tags[0]); + + vec.x = SCALED(point->x); + vec.y = SCALED(point->y); + + if (tag == GD_FT_CURVE_TAG_ON) { + error = func_interface->conic_to(&v_control, &vec, user); + if (error) + goto Exit; + continue; + } + + if (tag != GD_FT_CURVE_TAG_CONIC) + goto Invalid_Outline; + + v_middle.x = (v_control.x + vec.x) / 2; + v_middle.y = (v_control.y + vec.y) / 2; + + error = func_interface->conic_to(&v_control, &v_middle, user); + if (error) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + error = func_interface->conic_to(&v_control, &v_start, user); + goto Close; + + default: /* GD_FT_CURVE_TAG_CUBIC */ + { + GD_FT_Vector vec1, vec2; + + if (point + 1 > limit || GD_FT_CURVE_TAG(tags[1]) != GD_FT_CURVE_TAG_CUBIC) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1.x = SCALED(point[-2].x); + vec1.y = SCALED(point[-2].y); + + vec2.x = SCALED(point[-1].x); + vec2.y = SCALED(point[-1].y); + + if (point <= limit) { + GD_FT_Vector vec; + + vec.x = SCALED(point->x); + vec.y = SCALED(point->y); + + error = func_interface->cubic_to(&vec1, &vec2, &vec, user); + if (error) + goto Exit; + continue; + } + + error = func_interface->cubic_to(&vec1, &vec2, &v_start, user); + goto Close; + } + } + } + + /* close the contour with a line segment */ + error = func_interface->line_to(&v_start, user); + + Close: + if (error) + goto Exit; + + first = last + 1; + } + + return 0; + +Exit: + return error; + +Invalid_Outline: + return GD_FT_THROW(Invalid_Outline); +} + +typedef struct gray_TBand_ { + TPos min, max; + +} gray_TBand; + +GD_FT_DEFINE_OUTLINE_FUNCS(func_interface, (GD_FT_Outline_MoveTo_Func)gray_move_to, + (GD_FT_Outline_LineTo_Func)gray_line_to, + (GD_FT_Outline_ConicTo_Func)gray_conic_to, + (GD_FT_Outline_CubicTo_Func)gray_cubic_to, 0, 0) + +static int gray_convert_glyph_inner(RAS_ARG) +{ + volatile int error = 0; + + if (ft_setjmp(ras.jump_buffer) == 0) { + if (ras.source && ras.source->decompose) { + error = ras.source->decompose(ras.source->source, &ras); + } else { + error = GD_FT_Outline_Decompose(&ras.outline, &func_interface, &ras); + } + if (!ras.invalid) + gray_record_cell(RAS_VAR); + } else + error = GD_FT_THROW(Memory_Overflow); + + return error; +} + +static int gray_convert_glyph(RAS_ARG) +{ + gray_TBand bands[40]; + gray_TBand *volatile band; + int volatile n, num_bands; + TPos volatile min, max, max_y; + GD_FT_BBox *clip; + + /* Set up state in the raster object */ + gray_compute_cbox(RAS_VAR); + + /* clip to target bitmap, exit if nothing to do */ + clip = &ras.clip_box; + + if (ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax || ras.max_ey <= clip->yMin || + ras.min_ey >= clip->yMax) + return 0; + + if (ras.min_ex < clip->xMin) + ras.min_ex = clip->xMin; + if (ras.min_ey < clip->yMin) + ras.min_ey = clip->yMin; + + if (ras.max_ex > clip->xMax) + ras.max_ex = clip->xMax; + if (ras.max_ey > clip->yMax) + ras.max_ey = clip->yMax; + + ras.count_ex = ras.max_ex - ras.min_ex; + ras.count_ey = ras.max_ey - ras.min_ey; + + /* set up vertical bands */ + num_bands = (int)((ras.max_ey - ras.min_ey) / ras.band_size); + if (num_bands == 0) + num_bands = 1; + if (num_bands >= 39) + num_bands = 39; + + ras.band_shoot = 0; + + min = ras.min_ey; + max_y = ras.max_ey; + + for (n = 0; n < num_bands; n++, min = max) { + max = min + ras.band_size; + if (n == num_bands - 1 || max > max_y) + max = max_y; + + bands[0].min = min; + bands[0].max = max; + band = bands; + + while (band >= bands) { + TPos bottom, top, middle; + int error; + + { + PCell cells_max; + int yindex; + long cell_start, cell_end, cell_mod; + + ras.ycells = (PCell *)ras.buffer; + ras.ycount = band->max - band->min; + + cell_start = sizeof(PCell) * ras.ycount; + cell_mod = cell_start % sizeof(TCell); + if (cell_mod > 0) + cell_start += sizeof(TCell) - cell_mod; + + cell_end = ras.buffer_size; + cell_end -= cell_end % sizeof(TCell); + + cells_max = (PCell)((char *)ras.buffer + cell_end); + ras.cells = (PCell)((char *)ras.buffer + cell_start); + if (ras.cells >= cells_max) + goto ReduceBands; + + ras.max_cells = cells_max - ras.cells; + if (ras.max_cells < 2) + goto ReduceBands; + + for (yindex = 0; yindex < ras.ycount; yindex++) + ras.ycells[yindex] = NULL; + } + + ras.num_cells = 0; + ras.invalid = 1; + ras.min_ey = band->min; + ras.max_ey = band->max; + ras.count_ey = band->max - band->min; + + error = gray_convert_glyph_inner(RAS_VAR); + + if (!error) { + gray_sweep(RAS_VAR); + band--; + continue; + } else if (error != ErrRaster_Memory_Overflow) + return 1; + + ReduceBands: + /* render pool overflow; we will reduce the render band by half */ + bottom = band->min; + top = band->max; + middle = bottom + ((top - bottom) >> 1); + + /* This is too complex for a single scanline; there must */ + /* be some problems. */ + if (middle == bottom) { + return 1; + } + + if (bottom - top >= ras.band_size) + ras.band_shoot++; + + band[1].min = bottom; + band[1].max = middle; + band[0].min = middle; + band[0].max = top; + band++; + } + } + + if (ras.band_shoot > 8 && ras.band_size > 16) + ras.band_size = ras.band_size / 2; + + return 0; +} + +static int gray_raster_render(gray_PRaster raster, const GD_FT_Raster_Params *params) +{ + GD_FT_UNUSED(raster); + const GD_FT_Outline *outline = (const GD_FT_Outline *)params->source; + + gray_TWorker worker[1]; + + TCell buffer[GD_FT_RENDER_POOL_SIZE / sizeof(TCell)]; + long buffer_size = sizeof(buffer); + int band_size = (int)(buffer_size / (long)(sizeof(TCell) * 8)); + + if (!outline) + return GD_FT_THROW(Invalid_Outline); + + /* return immediately if the outline is empty */ + if (outline->n_points == 0 || outline->n_contours <= 0) + return 0; + + if (!outline->contours || !outline->points) + return GD_FT_THROW(Invalid_Outline); + + if (outline->n_points != outline->contours[outline->n_contours - 1] + 1) + return GD_FT_THROW(Invalid_Outline); + + if (params->flags & GD_FT_RASTER_FLAG_CLIP) + ras.clip_box = params->clip_box; + else { + ras.clip_box.xMin = -32768L; + ras.clip_box.yMin = -32768L; + ras.clip_box.xMax = 32767L; + ras.clip_box.yMax = 32767L; + } + + gray_init_cells(RAS_VAR_ buffer, buffer_size); + + ras.outline = *outline; + ras.source = NULL; + ras.num_cells = 0; + ras.invalid = 1; + ras.band_size = band_size; + ras.num_gray_spans = 0; + + ras.render_span = (GD_FT_Raster_Span_Func)params->gray_spans; + ras.render_span_data = params->user; + + gray_convert_glyph(RAS_VAR); + if (ras.bound_right > ras.bound_left && ras.bound_bottom > ras.bound_top) { + params->bbox_cb(ras.bound_left, ras.bound_top, ras.bound_right - ras.bound_left, + ras.bound_bottom - ras.bound_top + 1, params->user); + } + return 1; +} + +/**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ +/**** a static object. *****/ + +static int gray_raster_new(GD_FT_Raster *araster) +{ + static gray_TRaster the_raster; + + *araster = (GD_FT_Raster)&the_raster; + GD_FT_MEM_ZERO(&the_raster, sizeof(the_raster)); + + return 0; +} + +static void gray_raster_done(GD_FT_Raster raster) +{ + /* nothing */ + GD_FT_UNUSED(raster); +} + +static void gray_raster_reset(GD_FT_Raster raster, char *pool_base, long pool_size) +{ + GD_FT_UNUSED(raster); + GD_FT_UNUSED(pool_base); + GD_FT_UNUSED(pool_size); +} + +GD_FT_DEFINE_RASTER_FUNCS(gd_ft_grays_raster, + + (GD_FT_Raster_New_Func)gray_raster_new, + (GD_FT_Raster_Reset_Func)gray_raster_reset, + (GD_FT_Raster_Render_Func)gray_raster_render, + (GD_FT_Raster_Done_Func)gray_raster_done) + +typedef struct gdPathRasterSource_ { + gdPathPtr path; + gdPathMatrixPtr matrix; +} gdPathRasterSource; + +static void gdpath_include_point(GD_FT_BBox *box, int *first, const gdPointF *point) +{ + TPos x = (TPos)(point->x * 64.0); + TPos y = (TPos)(point->y * 64.0); + + if (*first) { + box->xMin = box->xMax = x; + box->yMin = box->yMax = y; + *first = 0; + return; + } + + if (x < box->xMin) + box->xMin = x; + if (x > box->xMax) + box->xMax = x; + if (y < box->yMin) + box->yMin = y; + if (y > box->yMax) + box->yMax = y; +} + +static GD_FT_Vector gdpath_vector(const gdPointF *point) +{ + GD_FT_Vector vector = {(GD_FT_Pos)(point->x * 64.0), (GD_FT_Pos)(point->y * 64.0)}; + return vector; +} + +static int gdpath_get_cbox(void *source, GD_FT_BBox *box) +{ + const gdPathRasterSource *path_source = source; + gdPathPtr path = path_source->path; + gdPathMatrixPtr matrix = path_source->matrix; + unsigned int numElements = gdArrayNumElements(&path->elements); + unsigned int pointsIndex = 0; + int first = 1; + gdPointF p[3]; + + for (unsigned int i = 0; i < numElements; i++) { + gdPathOpsPtr element = (gdPathOpsPtr)gdArrayIndex(&path->elements, i); + gdPointFPtr point = gdArrayIndex(&path->points, pointsIndex); + + switch (*element) { + case gdPathOpsMoveTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + gdpath_include_point(box, &first, &p[0]); + pointsIndex += 1; + break; + case gdPathOpsLineTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + gdpath_include_point(box, &first, &p[0]); + pointsIndex += 1; + break; + case gdPathOpsCubicTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + point = gdArrayIndex(&path->points, pointsIndex + 2); + gdPathMatrixMapPoint(matrix, point, &p[2]); + gdpath_include_point(box, &first, &p[0]); + gdpath_include_point(box, &first, &p[1]); + gdpath_include_point(box, &first, &p[2]); + pointsIndex += 3; + break; + case gdPathOpsQuadTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + gdpath_include_point(box, &first, &p[0]); + gdpath_include_point(box, &first, &p[1]); + pointsIndex += 2; + break; + case gdPathOpsClose: + pointsIndex += 1; + break; + } + } + + if (first) { + return -1; + } + + return 0; +} + +static int gdpath_decompose(void *source, void *worker_data) +{ + const gdPathRasterSource *path_source = source; + gdPathPtr path = path_source->path; + gdPathMatrixPtr matrix = path_source->matrix; + gray_PWorker worker = worker_data; + unsigned int pointsIndex = 0; + gdPointF p[3]; + GD_FT_Vector contour_start = {0, 0}; + int contour_open = 0; + + for (unsigned int i = 0; i < gdArrayNumElements(&path->elements); i++) { + gdPathOpsPtr element = (gdPathOpsPtr)gdArrayIndex(&path->elements, i); + gdPointFPtr point = gdArrayIndex(&path->points, pointsIndex); + + switch (*element) { + case gdPathOpsMoveTo: + if (contour_open) + gray_line_to(&contour_start, worker); + gdPathMatrixMapPoint(matrix, point, &p[0]); + { + GD_FT_Vector v = gdpath_vector(&p[0]); + gray_move_to(&v, worker); + contour_start = v; + contour_open = 1; + } + pointsIndex += 1; + break; + case gdPathOpsLineTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + { + GD_FT_Vector v = gdpath_vector(&p[0]); + if (contour_open) { + gray_line_to(&v, worker); + } else { + gray_move_to(&v, worker); + contour_start = v; + contour_open = 1; + } + } + pointsIndex += 1; + break; + case gdPathOpsCubicTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + point = gdArrayIndex(&path->points, pointsIndex + 2); + gdPathMatrixMapPoint(matrix, point, &p[2]); + { + GD_FT_Vector v1 = gdpath_vector(&p[0]); + GD_FT_Vector v2 = gdpath_vector(&p[1]); + GD_FT_Vector v3 = gdpath_vector(&p[2]); + if (contour_open) { + gray_cubic_to(&v1, &v2, &v3, worker); + } else { + gray_move_to(&v3, worker); + contour_start = v3; + contour_open = 1; + } + } + pointsIndex += 3; + break; + case gdPathOpsQuadTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + { + GD_FT_Vector v1 = gdpath_vector(&p[0]); + GD_FT_Vector v2 = gdpath_vector(&p[1]); + if (contour_open) { + gray_conic_to(&v1, &v2, worker); + } else { + gray_move_to(&v2, worker); + contour_start = v2; + contour_open = 1; + } + } + pointsIndex += 2; + break; + case gdPathOpsClose: + if (contour_open) { + gdPathMatrixMapPoint(matrix, point, &p[0]); + { + GD_FT_Vector v = gdpath_vector(&p[0]); + gray_line_to(&v, worker); + } + contour_open = 0; + } + pointsIndex += 1; + break; + } + } + + if (contour_open) + gray_line_to(&contour_start, worker); + return 0; +} + +/* END */ + +GD_FT_Error gd_ft_raster_render_path(const gdPathPtr path, gdPathMatrixPtr matrix, + GD_FT_Raster_Params *params, int outline_flags) +{ + if (!path || !params) + return GD_FT_THROW(Invalid_Argument); + +#ifndef GD_FT_STATIC_RASTER + gray_TWorker worker_storage; + gray_PWorker worker = &worker_storage; +#else + gray_TWorker save_ras = ras; +#endif + + gdPathRasterSource path_source = {path, matrix}; + gray_TSource source = {gdpath_get_cbox, gdpath_decompose, &path_source}; + ras.source = &source; + + params->flags |= GD_FT_RASTER_FLAG_DIRECT | GD_FT_RASTER_FLAG_AA; + if (params->flags & GD_FT_RASTER_FLAG_CLIP) { + ras.clip_box = params->clip_box; + } else { + ras.clip_box.xMin = -32768L; + ras.clip_box.yMin = -32768L; + ras.clip_box.xMax = 32767L; + ras.clip_box.yMax = 32767L; + } + + TCell buffer[GD_FT_RENDER_POOL_SIZE / sizeof(TCell)]; + long buffer_size = sizeof(buffer); + int band_size = (int)(buffer_size / (long)(sizeof(TCell) * 8)); + + gray_init_cells(RAS_VAR_ buffer, buffer_size); + ras.outline.n_contours = 0; + ras.outline.n_points = 0; + ras.outline.flags = outline_flags; + ras.num_cells = 0; + ras.invalid = 1; + ras.band_size = band_size; + ras.num_gray_spans = 0; + ras.render_span = (GD_FT_Raster_Span_Func)params->gray_spans; + ras.render_span_data = params->user; + + int error = gray_convert_glyph(RAS_VAR); + int bound_left = ras.bound_left; + int bound_top = ras.bound_top; + int bound_right = ras.bound_right; + int bound_bottom = ras.bound_bottom; + +#ifdef GD_FT_STATIC_RASTER + ras = save_ras; +#endif + + if (!error && params->bbox_cb && bound_right > bound_left && bound_bottom > bound_top) { + params->bbox_cb(bound_left, bound_top, bound_right - bound_left, + bound_bottom - bound_top + 1, params->user); + } + + if (error) + return error; + return gdArrayNumElements(&path->elements) > 0 ? 1 : 0; +} diff --git a/ext/gd/libgd/ftraster/gd_ft_raster.h b/ext/gd/libgd/ftraster/gd_ft_raster.h new file mode 100644 index 000000000000..96e974419752 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_raster.h @@ -0,0 +1,579 @@ +#ifndef GD_FT_IMG_H +#define GD_FT_IMG_H +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2010, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +/*************************************************************************/ +/* */ +/* Note: A `raster' is simply a scan-line converter, used to render */ +/* GD_FT_Outlines into GD_FT_Bitmaps. */ +/* */ +/*************************************************************************/ + +#include "gd_ft_types.h" + +typedef struct gdPathStruct *gdPathPtr; +typedef struct gdPathMatrixStruct *gdPathMatrixPtr; + +/*************************************************************************/ +/* */ +/* */ +/* FT_BBox */ +/* */ +/* */ +/* A structure used to hold an outline's bounding box, i.e., the */ +/* coordinates of its extrema in the horizontal and vertical */ +/* directions. */ +/* */ +/* */ +/* xMin :: The horizontal minimum (left-most). */ +/* */ +/* yMin :: The vertical minimum (bottom-most). */ +/* */ +/* xMax :: The horizontal maximum (right-most). */ +/* */ +/* yMax :: The vertical maximum (top-most). */ +/* */ +/* */ +/* The bounding box is specified with the coordinates of the lower */ +/* left and the upper right corner. In PostScript, those values are */ +/* often called (llx,lly) and (urx,ury), respectively. */ +/* */ +/* If `yMin' is negative, this value gives the glyph's descender. */ +/* Otherwise, the glyph doesn't descend below the baseline. */ +/* Similarly, if `ymax' is positive, this value gives the glyph's */ +/* ascender. */ +/* */ +/* `xMin' gives the horizontal distance from the glyph's origin to */ +/* the left edge of the glyph's bounding box. If `xMin' is negative, */ +/* the glyph extends to the left of the origin. */ +/* */ +typedef struct GD_FT_BBox_ { + GD_FT_Pos xMin, yMin; + GD_FT_Pos xMax, yMax; + +} GD_FT_BBox; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Outline */ +/* */ +/* */ +/* This structure is used to describe an outline to the scan-line */ +/* converter. */ +/* */ +/* */ +/* n_contours :: The number of contours in the outline. */ +/* */ +/* n_points :: The number of points in the outline. */ +/* */ +/* points :: A pointer to an array of `n_points' @GD_FT_Vector */ +/* elements, giving the outline's point coordinates. */ +/* */ +/* tags :: A pointer to an array of `n_points' chars, giving */ +/* each outline point's type. */ +/* */ +/* If bit~0 is unset, the point is `off' the curve, */ +/* i.e., a Bézier control point, while it is `on' if */ +/* set. */ +/* */ +/* Bit~1 is meaningful for `off' points only. If set, */ +/* it indicates a third-order Bézier arc control point; */ +/* and a second-order control point if unset. */ +/* */ +/* If bit~2 is set, bits 5-7 contain the drop-out mode */ +/* (as defined in the OpenType specification; the value */ +/* is the same as the argument to the SCANMODE */ +/* instruction). */ +/* */ +/* Bits 3 and~4 are reserved for internal purposes. */ +/* */ +/* contours :: An array of `n_contours' shorts, giving the end */ +/* point of each contour within the outline. For */ +/* example, the first contour is defined by the points */ +/* `0' to `contours[0]', the second one is defined by */ +/* the points `contours[0]+1' to `contours[1]', etc. */ +/* */ +/* flags :: A set of bit flags used to characterize the outline */ +/* and give hints to the scan-converter and hinter on */ +/* how to convert/grid-fit it. See @GD_FT_OUTLINE_FLAGS.*/ +/* */ +typedef struct GD_FT_Outline_ { + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + GD_FT_Vector *points; /* the outline's points */ + char *tags; /* the points flags */ + short *contours; /* the contour end points */ + char *contours_flag; /* the contour open flags */ + + int flags; /* outline masks */ + +} GD_FT_Outline; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_OUTLINE_FLAGS */ +/* */ +/* */ +/* A list of bit-field constants use for the flags in an outline's */ +/* `flags' field. */ +/* */ +/* */ +/* GD_FT_OUTLINE_NONE :: */ +/* Value~0 is reserved. */ +/* */ +/* GD_FT_OUTLINE_OWNER :: */ +/* If set, this flag indicates that the outline's field arrays */ +/* (i.e., `points', `flags', and `contours') are `owned' by the */ +/* outline object, and should thus be freed when it is destroyed. */ +/* */ +/* GD_FT_OUTLINE_EVEN_ODD_FILL :: */ +/* By default, outlines are filled using the non-zero winding rule. */ +/* If set to 1, the outline will be filled using the even-odd fill */ +/* rule (only works with the smooth rasterizer). */ +/* */ +/* GD_FT_OUTLINE_REVERSE_FILL :: */ +/* By default, outside contours of an outline are oriented in */ +/* clock-wise direction, as defined in the TrueType specification. */ +/* This flag is set if the outline uses the opposite direction */ +/* (typically for Type~1 fonts). This flag is ignored by the scan */ +/* converter. */ +/* */ +/* */ +/* */ +/* There exists a second mechanism to pass the drop-out mode to the */ +/* B/W rasterizer; see the `tags' field in @GD_FT_Outline. */ +/* */ +/* Please refer to the description of the `SCANTYPE' instruction in */ +/* the OpenType specification (in file `ttinst1.doc') how simple */ +/* drop-outs, smart drop-outs, and stubs are defined. */ +/* */ +#define GD_FT_OUTLINE_NONE 0x0 +#define GD_FT_OUTLINE_OWNER 0x1 +#define GD_FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define GD_FT_OUTLINE_REVERSE_FILL 0x4 + +/* */ + +#define GD_FT_CURVE_TAG(flag) (flag & 3) + +#define GD_FT_CURVE_TAG_ON 1 +#define GD_FT_CURVE_TAG_CONIC 0 +#define GD_FT_CURVE_TAG_CUBIC 2 + +#define GD_FT_Curve_Tag_On GD_FT_CURVE_TAG_ON +#define GD_FT_Curve_Tag_Conic GD_FT_CURVE_TAG_CONIC +#define GD_FT_Curve_Tag_Cubic GD_FT_CURVE_TAG_CUBIC + +/*************************************************************************/ +/* */ +/* A raster is a scan converter, in charge of rendering an outline into */ +/* a a bitmap. This section contains the public API for rasters. */ +/* */ +/* Note that in FreeType 2, all rasters are now encapsulated within */ +/* specific modules called `renderers'. See `ftrender.h' for more */ +/* details on renderers. */ +/* */ +/*************************************************************************/ + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster */ +/* */ +/* */ +/* A handle (pointer) to a raster object. Each object can be used */ +/* independently to convert an outline into a bitmap or pixmap. */ +/* */ +typedef struct GD_FT_RasterRec_ *GD_FT_Raster; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Span */ +/* */ +/* */ +/* A structure used to model a single span of gray (or black) pixels */ +/* when rendering a monochrome or anti-aliased bitmap. */ +/* */ +/* */ +/* x :: The span's horizontal start position. */ +/* */ +/* len :: The span's length in pixels. */ +/* */ +/* coverage :: The span color/coverage, ranging from 0 (background) */ +/* to 255 (foreground). Only used for anti-aliased */ +/* rendering. */ +/* */ +/* */ +/* This structure is used by the span drawing callback type named */ +/* @GD_FT_SpanFunc that takes the y~coordinate of the span as a */ +/* parameter. */ +/* */ +/* The coverage value is always between 0 and 255. If you want less */ +/* gray values, the callback function has to reduce them. */ +/* */ +typedef struct GD_FT_Span_ { + short x; + short y; + unsigned short len; + unsigned char coverage; + +} GD_FT_Span; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_SpanFunc */ +/* */ +/* */ +/* A function used as a call-back by the anti-aliased renderer in */ +/* order to let client applications draw themselves the gray pixel */ +/* spans on each scan line. */ +/* */ +/* */ +/* y :: The scanline's y~coordinate. */ +/* */ +/* count :: The number of spans to draw on this scanline. */ +/* */ +/* spans :: A table of `count' spans to draw on the scanline. */ +/* */ +/* user :: User-supplied data that is passed to the callback. */ +/* */ +/* */ +/* This callback allows client applications to directly render the */ +/* gray spans of the anti-aliased bitmap to any kind of surfaces. */ +/* */ +/* This can be used to write anti-aliased outlines directly to a */ +/* given background bitmap, and even perform translucency. */ +/* */ +/* Note that the `count' field cannot be greater than a fixed value */ +/* defined by the `GD_FT_MAX_GRAY_SPANS' configuration macro in */ +/* `ftoption.h'. By default, this value is set to~32, which means */ +/* that if there are more than 32~spans on a given scanline, the */ +/* callback is called several times with the same `y' parameter in */ +/* order to draw all callbacks. */ +/* */ +/* Otherwise, the callback is only called once per scan-line, and */ +/* only for those scanlines that do have `gray' pixels on them. */ +/* */ +typedef void (*GD_FT_SpanFunc)(int count, const GD_FT_Span *spans, void *user); + +typedef void (*GD_FT_BboxFunc)(int x, int y, int w, int h, void *user); + +#define GD_FT_Raster_Span_Func GD_FT_SpanFunc + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_RASTER_FLAG_XXX */ +/* */ +/* */ +/* A list of bit flag constants as used in the `flags' field of a */ +/* @GD_FT_Raster_Params structure. */ +/* */ +/* */ +/* GD_FT_RASTER_FLAG_DEFAULT :: This value is 0. */ +/* */ +/* GD_FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ +/* anti-aliased glyph image should be */ +/* generated. Otherwise, it will be */ +/* monochrome (1-bit). */ +/* */ +/* GD_FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ +/* rendering. In this mode, client */ +/* applications must provide their own span */ +/* callback. This lets them directly */ +/* draw or compose over an existing bitmap. */ +/* If this bit is not set, the target */ +/* pixmap's buffer _must_ be zeroed before */ +/* rendering. */ +/* */ +/* Note that for now, direct rendering is */ +/* only possible with anti-aliased glyphs. */ +/* */ +/* GD_FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ +/* rendering mode. If set, the output will */ +/* be clipped to a box specified in the */ +/* `clip_box' field of the */ +/* @GD_FT_Raster_Params structure. */ +/* */ +/* Note that by default, the glyph bitmap */ +/* is clipped to the target pixmap, except */ +/* in direct rendering mode where all spans */ +/* are generated if no clipping box is set. */ +/* */ +#define GD_FT_RASTER_FLAG_DEFAULT 0x0 +#define GD_FT_RASTER_FLAG_AA 0x1 +#define GD_FT_RASTER_FLAG_DIRECT 0x2 +#define GD_FT_RASTER_FLAG_CLIP 0x4 + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_Params */ +/* */ +/* */ +/* A structure to hold the arguments used by a raster's render */ +/* function. */ +/* */ +/* */ +/* target :: The target bitmap. */ +/* */ +/* source :: A pointer to the source glyph image (e.g., an */ +/* @GD_FT_Outline). */ +/* */ +/* flags :: The rendering flags. */ +/* */ +/* gray_spans :: The gray span drawing callback. */ +/* */ +/* black_spans :: The black span drawing callback. UNIMPLEMENTED! */ +/* */ +/* bit_test :: The bit test callback. UNIMPLEMENTED! */ +/* */ +/* bit_set :: The bit set callback. UNIMPLEMENTED! */ +/* */ +/* user :: User-supplied data that is passed to each drawing */ +/* callback. */ +/* */ +/* clip_box :: An optional clipping box. It is only used in */ +/* direct rendering mode. Note that coordinates here */ +/* should be expressed in _integer_ pixels (and not in */ +/* 26.6 fixed-point units). */ +/* */ +/* */ +/* An anti-aliased glyph bitmap is drawn if the @GD_FT_RASTER_FLAG_AA */ +/* bit flag is set in the `flags' field, otherwise a monochrome */ +/* bitmap is generated. */ +/* */ +/* If the @GD_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ +/* raster will call the `gray_spans' callback to draw gray pixel */ +/* spans, in the case of an aa glyph bitmap, it will call */ +/* `black_spans', and `bit_test' and `bit_set' in the case of a */ +/* monochrome bitmap. This allows direct composition over a */ +/* pre-existing bitmap through user-provided callbacks to perform the */ +/* span drawing/composition. */ +/* */ +/* Note that the `bit_test' and `bit_set' callbacks are required when */ +/* rendering a monochrome bitmap, as they are crucial to implement */ +/* correct drop-out control as defined in the TrueType specification. */ +/* */ +typedef struct GD_FT_Raster_Params_ { + const void *source; + int flags; + GD_FT_SpanFunc gray_spans; + GD_FT_BboxFunc bbox_cb; + void *user; + GD_FT_BBox clip_box; + +} GD_FT_Raster_Params; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Outline_Check */ +/* */ +/* */ +/* Check the contents of an outline descriptor. */ +/* */ +/* */ +/* outline :: A handle to a source outline. */ +/* */ +/* */ +/* FreeType error code. 0~means success. */ +/* */ +GD_FT_Error GD_FT_Outline_Check(GD_FT_Outline *outline); + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Outline_Get_CBox */ +/* */ +/* */ +/* Return an outline's `control box'. The control box encloses all */ +/* the outline's points, including Bézier control points. Though it */ +/* coincides with the exact bounding box for most glyphs, it can be */ +/* slightly larger in some situations (like when rotating an outline */ +/* that contains Bézier outside arcs). */ +/* */ +/* Computing the control box is very fast, while getting the bounding */ +/* box can take much more time as it needs to walk over all segments */ +/* and arcs in the outline. To get the latter, you can use the */ +/* `ftbbox' component, which is dedicated to this single task. */ +/* */ +/* */ +/* outline :: A pointer to the source outline descriptor. */ +/* */ +/* */ +/* acbox :: The outline's control box. */ +/* */ +/* */ +/* See @GD_FT_Glyph_Get_CBox for a discussion of tricky fonts. */ +/* */ +void GD_FT_Outline_Get_CBox(const GD_FT_Outline *outline, GD_FT_BBox *acbox); + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_NewFunc */ +/* */ +/* */ +/* A function used to create a new raster object. */ +/* */ +/* */ +/* memory :: A handle to the memory allocator. */ +/* */ +/* */ +/* raster :: A handle to the new raster object. */ +/* */ +/* */ +/* Error code. 0~means success. */ +/* */ +/* */ +/* The `memory' parameter is a typeless pointer in order to avoid */ +/* un-wanted dependencies on the rest of the FreeType code. In */ +/* practice, it is an @GD_FT_Memory object, i.e., a handle to the */ +/* standard FreeType memory allocator. However, this field can be */ +/* completely ignored by a given raster implementation. */ +/* */ +typedef int (*GD_FT_Raster_NewFunc)(GD_FT_Raster *raster); + +#define GD_FT_Raster_New_Func GD_FT_Raster_NewFunc + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_DoneFunc */ +/* */ +/* */ +/* A function used to destroy a given raster object. */ +/* */ +/* */ +/* raster :: A handle to the raster object. */ +/* */ +typedef void (*GD_FT_Raster_DoneFunc)(GD_FT_Raster raster); + +#define GD_FT_Raster_Done_Func GD_FT_Raster_DoneFunc + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_ResetFunc */ +/* */ +/* */ +/* FreeType provides an area of memory called the `render pool', */ +/* available to all registered rasters. This pool can be freely used */ +/* during a given scan-conversion but is shared by all rasters. Its */ +/* content is thus transient. */ +/* */ +/* This function is called each time the render pool changes, or just */ +/* after a new raster object is created. */ +/* */ +/* */ +/* raster :: A handle to the new raster object. */ +/* */ +/* pool_base :: The address in memory of the render pool. */ +/* */ +/* pool_size :: The size in bytes of the render pool. */ +/* */ +/* */ +/* Rasters can ignore the render pool and rely on dynamic memory */ +/* allocation if they want to (a handle to the memory allocator is */ +/* passed to the raster constructor). However, this is not */ +/* recommended for efficiency purposes. */ +/* */ +typedef void (*GD_FT_Raster_ResetFunc)(GD_FT_Raster raster, unsigned char *pool_base, + unsigned long pool_size); + +#define GD_FT_Raster_Reset_Func GD_FT_Raster_ResetFunc + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_RenderFunc */ +/* */ +/* */ +/* Invoke a given raster to scan-convert a given glyph image into a */ +/* target bitmap. */ +/* */ +/* */ +/* raster :: A handle to the raster object. */ +/* */ +/* params :: A pointer to an @GD_FT_Raster_Params structure used to */ +/* store the rendering parameters. */ +/* */ +/* */ +/* Error code. 0~means success. */ +/* */ +/* */ +/* The exact format of the source image depends on the raster's glyph */ +/* format defined in its @GD_FT_Raster_Funcs structure. It can be an */ +/* @GD_FT_Outline or anything else in order to support a large array of */ +/* glyph formats. */ +/* */ +/* Note also that the render function can fail and return a */ +/* `GD_FT_Err_Unimplemented_Feature' error code if the raster used does */ +/* not support direct composition. */ +/* */ +/* XXX: For now, the standard raster doesn't support direct */ +/* composition but this should change for the final release (see */ +/* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */ +/* for examples of distinct implementations that support direct */ +/* composition). */ +/* */ +typedef int (*GD_FT_Raster_RenderFunc)(GD_FT_Raster raster, const GD_FT_Raster_Params *params); + +#define GD_FT_Raster_Render_Func GD_FT_Raster_RenderFunc + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Raster_Funcs */ +/* */ +/* */ +/* A structure used to describe a given raster class to the library. */ +/* */ +/* */ +/* glyph_format :: The supported glyph format for this raster. */ +/* */ +/* raster_new :: The raster constructor. */ +/* */ +/* raster_reset :: Used to reset the render pool within the raster. */ +/* */ +/* raster_render :: A function to render a glyph into a given bitmap. */ +/* */ +/* raster_done :: The raster destructor. */ +/* */ +typedef struct GD_FT_Raster_Funcs_ { + GD_FT_Raster_NewFunc raster_new; + GD_FT_Raster_ResetFunc raster_reset; + GD_FT_Raster_RenderFunc raster_render; + GD_FT_Raster_DoneFunc raster_done; + +} GD_FT_Raster_Funcs; + +extern const GD_FT_Raster_Funcs gd_ft_grays_raster; + +/* Direct path rendering without FT_Outline intermediate */ +GD_FT_Error gd_ft_raster_render_path(const gdPathPtr path, gdPathMatrixPtr matrix, + GD_FT_Raster_Params *params, int outline_flags); + +#endif // GD_FT_IMG_H diff --git a/ext/gd/libgd/ftraster/gd_ft_stroker.c b/ext/gd/libgd/ftraster/gd_ft_stroker.c new file mode 100644 index 000000000000..ca77586c34f5 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_stroker.c @@ -0,0 +1,1892 @@ + +/***************************************************************************/ +/* */ +/* ftstroke.c */ +/* */ +/* FreeType path stroker (body). */ +/* */ +/* Copyright 2002-2006, 2008-2011, 2013 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "gd_ft_stroker.h" +#include "gd_ft_math.h" +#include +#include +#include + +/*************************************************************************/ +/*************************************************************************/ +/***** *****/ +/***** BEZIER COMPUTATIONS *****/ +/***** *****/ +/*************************************************************************/ +/*************************************************************************/ + +#define GD_FT_SMALL_CONIC_THRESHOLD (GD_FT_ANGLE_PI / 6) +#define GD_FT_SMALL_CUBIC_THRESHOLD (GD_FT_ANGLE_PI / 8) + +#define GD_FT_EPSILON 2 + +#define GD_FT_IS_SMALL(x) ((x) > -GD_FT_EPSILON && (x) < GD_FT_EPSILON) + +static inline GD_FT_Pos ft_pos_abs(GD_FT_Pos x) { return x >= 0 ? x : -x; } + +static void ft_conic_split(GD_FT_Vector *base) +{ + GD_FT_Pos a, b; + + base[4].x = base[2].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + base[3].x = b >> 1; + base[2].x = (a + b) >> 2; + base[1].x = a >> 1; + + base[4].y = base[2].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + base[3].y = b >> 1; + base[2].y = (a + b) >> 2; + base[1].y = a >> 1; +} + +static GD_FT_Bool ft_conic_is_small_enough(GD_FT_Vector *base, GD_FT_Angle *angle_in, + GD_FT_Angle *angle_out) +{ + GD_FT_Vector d1, d2; + GD_FT_Angle theta; + GD_FT_Int close1, close2; + + d1.x = base[1].x - base[2].x; + d1.y = base[1].y - base[2].y; + d2.x = base[0].x - base[1].x; + d2.y = base[0].y - base[1].y; + + close1 = GD_FT_IS_SMALL(d1.x) && GD_FT_IS_SMALL(d1.y); + close2 = GD_FT_IS_SMALL(d2.x) && GD_FT_IS_SMALL(d2.y); + + if (close1) { + if (close2) { + /* basically a point; */ + /* do nothing to retain original direction */ + } else { + *angle_in = *angle_out = GD_FT_Atan2(d2.x, d2.y); + } + } else /* !close1 */ + { + if (close2) { + *angle_in = *angle_out = GD_FT_Atan2(d1.x, d1.y); + } else { + *angle_in = GD_FT_Atan2(d1.x, d1.y); + *angle_out = GD_FT_Atan2(d2.x, d2.y); + } + } + + theta = ft_pos_abs(GD_FT_Angle_Diff(*angle_in, *angle_out)); + + return GD_FT_BOOL(theta < GD_FT_SMALL_CONIC_THRESHOLD); +} + +static void ft_cubic_split(GD_FT_Vector *base) +{ + GD_FT_Pos a, b, c; + + base[6].x = base[3].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + c = base[2].x + base[3].x; + base[5].x = c >> 1; + c += b; + base[4].x = c >> 2; + base[1].x = a >> 1; + a += b; + base[2].x = a >> 2; + base[3].x = (a + c) >> 3; + + base[6].y = base[3].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + c = base[2].y + base[3].y; + base[5].y = c >> 1; + c += b; + base[4].y = c >> 2; + base[1].y = a >> 1; + a += b; + base[2].y = a >> 2; + base[3].y = (a + c) >> 3; +} + +/* Return the average of `angle1' and `angle2'. */ +/* This gives correct result even if `angle1' and `angle2' */ +/* have opposite signs. */ +static GD_FT_Angle ft_angle_mean(GD_FT_Angle angle1, GD_FT_Angle angle2) +{ + return angle1 + GD_FT_Angle_Diff(angle1, angle2) / 2; +} + +static GD_FT_Bool ft_cubic_is_small_enough(GD_FT_Vector *base, GD_FT_Angle *angle_in, + GD_FT_Angle *angle_mid, GD_FT_Angle *angle_out) +{ + GD_FT_Vector d1, d2, d3; + GD_FT_Angle theta1, theta2; + GD_FT_Int close1, close2, close3; + + d1.x = base[2].x - base[3].x; + d1.y = base[2].y - base[3].y; + d2.x = base[1].x - base[2].x; + d2.y = base[1].y - base[2].y; + d3.x = base[0].x - base[1].x; + d3.y = base[0].y - base[1].y; + + close1 = GD_FT_IS_SMALL(d1.x) && GD_FT_IS_SMALL(d1.y); + close2 = GD_FT_IS_SMALL(d2.x) && GD_FT_IS_SMALL(d2.y); + close3 = GD_FT_IS_SMALL(d3.x) && GD_FT_IS_SMALL(d3.y); + + if (close1) { + if (close2) { + if (close3) { + /* basically a point; */ + /* do nothing to retain original direction */ + } else /* !close3 */ + { + *angle_in = *angle_mid = *angle_out = GD_FT_Atan2(d3.x, d3.y); + } + } else /* !close2 */ + { + if (close3) { + *angle_in = *angle_mid = *angle_out = GD_FT_Atan2(d2.x, d2.y); + } else /* !close3 */ + { + *angle_in = *angle_mid = GD_FT_Atan2(d2.x, d2.y); + *angle_out = GD_FT_Atan2(d3.x, d3.y); + } + } + } else /* !close1 */ + { + if (close2) { + if (close3) { + *angle_in = *angle_mid = *angle_out = GD_FT_Atan2(d1.x, d1.y); + } else /* !close3 */ + { + *angle_in = GD_FT_Atan2(d1.x, d1.y); + *angle_out = GD_FT_Atan2(d3.x, d3.y); + *angle_mid = ft_angle_mean(*angle_in, *angle_out); + } + } else /* !close2 */ + { + if (close3) { + *angle_in = GD_FT_Atan2(d1.x, d1.y); + *angle_mid = *angle_out = GD_FT_Atan2(d2.x, d2.y); + } else /* !close3 */ + { + *angle_in = GD_FT_Atan2(d1.x, d1.y); + *angle_mid = GD_FT_Atan2(d2.x, d2.y); + *angle_out = GD_FT_Atan2(d3.x, d3.y); + } + } + } + + theta1 = ft_pos_abs(GD_FT_Angle_Diff(*angle_in, *angle_mid)); + theta2 = ft_pos_abs(GD_FT_Angle_Diff(*angle_mid, *angle_out)); + + return GD_FT_BOOL(theta1 < GD_FT_SMALL_CUBIC_THRESHOLD && theta2 < GD_FT_SMALL_CUBIC_THRESHOLD); +} + +/*************************************************************************/ +/*************************************************************************/ +/***** *****/ +/***** STROKE BORDERS *****/ +/***** *****/ +/*************************************************************************/ +/*************************************************************************/ + +typedef enum GD_FT_StrokeTags_ { + GD_FT_STROKE_TAG_ON = 1, /* on-curve point */ + GD_FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ + GD_FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ + GD_FT_STROKE_TAG_END = 8 /* sub-path end */ + +} GD_FT_StrokeTags; + +#define GD_FT_STROKE_TAG_BEGIN_END (GD_FT_STROKE_TAG_BEGIN | GD_FT_STROKE_TAG_END) + +typedef struct GD_FT_StrokeBorderRec_ { + GD_FT_UInt num_points; + GD_FT_UInt max_points; + GD_FT_Vector *points; + GD_FT_Byte *tags; + GD_FT_Bool movable; /* TRUE for ends of lineto borders */ + GD_FT_Int start; /* index of current sub-path start point */ + GD_FT_Bool valid; + +} GD_FT_StrokeBorderRec, *GD_FT_StrokeBorder; + +GD_FT_Error GD_FT_Outline_Check(GD_FT_Outline *outline) +{ + if (outline) { + GD_FT_Int n_points = outline->n_points; + GD_FT_Int n_contours = outline->n_contours; + GD_FT_Int end0, end; + GD_FT_Int n; + + /* empty glyph? */ + if (n_points == 0 && n_contours == 0) + return 0; + + /* check point and contour counts */ + if (n_points <= 0 || n_contours <= 0) + goto Bad; + + end0 = end = -1; + for (n = 0; n < n_contours; n++) { + end = outline->contours[n]; + + /* note that we don't accept empty contours */ + if (end <= end0 || end >= n_points) + goto Bad; + + end0 = end; + } + + if (end != n_points - 1) + goto Bad; + + /* XXX: check the tags array */ + return 0; + } + +Bad: + return -1; // GD_FT_THROW( Invalid_Argument ); +} + +void GD_FT_Outline_Get_CBox(const GD_FT_Outline *outline, GD_FT_BBox *acbox) +{ + GD_FT_Pos xMin, yMin, xMax, yMax; + + if (outline && acbox) { + if (outline->n_points == 0) { + xMin = 0; + yMin = 0; + xMax = 0; + yMax = 0; + } else { + GD_FT_Vector *vec = outline->points; + GD_FT_Vector *limit = vec + outline->n_points; + + xMin = xMax = vec->x; + yMin = yMax = vec->y; + vec++; + + for (; vec < limit; vec++) { + GD_FT_Pos x, y; + + x = vec->x; + if (x < xMin) + xMin = x; + if (x > xMax) + xMax = x; + + y = vec->y; + if (y < yMin) + yMin = y; + if (y > yMax) + yMax = y; + } + } + acbox->xMin = xMin; + acbox->xMax = xMax; + acbox->yMin = yMin; + acbox->yMax = yMax; + } +} + +static GD_FT_Error ft_stroke_border_grow(GD_FT_StrokeBorder border, GD_FT_UInt new_points) +{ + GD_FT_UInt old_max = border->max_points; + GD_FT_UInt new_max = border->num_points + new_points; + GD_FT_Error error = 0; + + if (new_max > old_max) { + GD_FT_UInt cur_max = old_max; + + while (cur_max < new_max) + cur_max += (cur_max >> 1) + 16; + + border->points = (GD_FT_Vector *)realloc(border->points, cur_max * sizeof(GD_FT_Vector)); + border->tags = (GD_FT_Byte *)realloc(border->tags, cur_max * sizeof(GD_FT_Byte)); + + if (!border->points || !border->tags) + goto Exit; + + border->max_points = cur_max; + } + +Exit: + return error; +} + +static void ft_stroke_border_close(GD_FT_StrokeBorder border, GD_FT_Bool reverse) +{ + GD_FT_UInt start = border->start; + GD_FT_UInt count = border->num_points; + + assert(border->start >= 0); + + /* don't record empty paths! */ + if (count <= start + 1U) + border->num_points = start; + else { + /* copy the last point to the start of this sub-path, since */ + /* it contains the `adjusted' starting coordinates */ + border->num_points = --count; + border->points[start] = border->points[count]; + + if (reverse) { + /* reverse the points */ + { + GD_FT_Vector *vec1 = border->points + start + 1; + GD_FT_Vector *vec2 = border->points + count - 1; + + for (; vec1 < vec2; vec1++, vec2--) { + GD_FT_Vector tmp; + + tmp = *vec1; + *vec1 = *vec2; + *vec2 = tmp; + } + } + + /* then the tags */ + { + GD_FT_Byte *tag1 = border->tags + start + 1; + GD_FT_Byte *tag2 = border->tags + count - 1; + + for (; tag1 < tag2; tag1++, tag2--) { + GD_FT_Byte tmp; + + tmp = *tag1; + *tag1 = *tag2; + *tag2 = tmp; + } + } + } + + border->tags[start] |= GD_FT_STROKE_TAG_BEGIN; + border->tags[count - 1] |= GD_FT_STROKE_TAG_END; + } + + border->start = -1; + border->movable = FALSE; +} + +static GD_FT_Error ft_stroke_border_lineto(GD_FT_StrokeBorder border, GD_FT_Vector *to, + GD_FT_Bool movable) +{ + GD_FT_Error error = 0; + + assert(border->start >= 0); + + if (border->movable) { + /* move last point */ + border->points[border->num_points - 1] = *to; + } else { + /* don't add zero-length lineto */ + if (border->num_points > 0 && + GD_FT_IS_SMALL(border->points[border->num_points - 1].x - to->x) && + GD_FT_IS_SMALL(border->points[border->num_points - 1].y - to->y)) + return error; + + /* add one point */ + error = ft_stroke_border_grow(border, 1); + if (!error) { + GD_FT_Vector *vec = border->points + border->num_points; + GD_FT_Byte *tag = border->tags + border->num_points; + + vec[0] = *to; + tag[0] = GD_FT_STROKE_TAG_ON; + + border->num_points += 1; + } + } + border->movable = movable; + return error; +} + +static GD_FT_Error ft_stroke_border_conicto(GD_FT_StrokeBorder border, GD_FT_Vector *control, + GD_FT_Vector *to) +{ + GD_FT_Error error; + + assert(border->start >= 0); + + error = ft_stroke_border_grow(border, 2); + if (!error) { + GD_FT_Vector *vec = border->points + border->num_points; + GD_FT_Byte *tag = border->tags + border->num_points; + + vec[0] = *control; + vec[1] = *to; + + tag[0] = 0; + tag[1] = GD_FT_STROKE_TAG_ON; + + border->num_points += 2; + } + + border->movable = FALSE; + + return error; +} + +static GD_FT_Error ft_stroke_border_cubicto(GD_FT_StrokeBorder border, GD_FT_Vector *control1, + GD_FT_Vector *control2, GD_FT_Vector *to) +{ + GD_FT_Error error; + + assert(border->start >= 0); + + error = ft_stroke_border_grow(border, 3); + if (!error) { + GD_FT_Vector *vec = border->points + border->num_points; + GD_FT_Byte *tag = border->tags + border->num_points; + + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + + tag[0] = GD_FT_STROKE_TAG_CUBIC; + tag[1] = GD_FT_STROKE_TAG_CUBIC; + tag[2] = GD_FT_STROKE_TAG_ON; + + border->num_points += 3; + } + + border->movable = FALSE; + + return error; +} + +#define GD_FT_ARC_CUBIC_ANGLE (GD_FT_ANGLE_PI / 2) + +static GD_FT_Error ft_stroke_border_arcto(GD_FT_StrokeBorder border, GD_FT_Vector *center, + GD_FT_Fixed radius, GD_FT_Angle angle_start, + GD_FT_Angle angle_diff) +{ + GD_FT_Fixed coef; + GD_FT_Vector a0, a1, a2, a3; + GD_FT_Int i, arcs = 1; + GD_FT_Error error = 0; + + /* number of cubic arcs to draw */ + while (angle_diff > GD_FT_ARC_CUBIC_ANGLE * arcs || -angle_diff > GD_FT_ARC_CUBIC_ANGLE * arcs) + arcs++; + + /* control tangents */ + coef = GD_FT_Tan(angle_diff / (4 * arcs)); + coef += coef / 3; + + /* compute start and first control point */ + GD_FT_Vector_From_Polar(&a0, radius, angle_start); + a1.x = GD_FT_MulFix(-a0.y, coef); + a1.y = GD_FT_MulFix(a0.x, coef); + + a0.x += center->x; + a0.y += center->y; + a1.x += a0.x; + a1.y += a0.y; + + for (i = 1; i <= arcs; i++) { + /* compute end and second control point */ + GD_FT_Vector_From_Polar(&a3, radius, angle_start + i * angle_diff / arcs); + a2.x = GD_FT_MulFix(a3.y, coef); + a2.y = GD_FT_MulFix(-a3.x, coef); + + a3.x += center->x; + a3.y += center->y; + a2.x += a3.x; + a2.y += a3.y; + + /* add cubic arc */ + error = ft_stroke_border_cubicto(border, &a1, &a2, &a3); + if (error) + break; + + /* a0 = a3; */ + a1.x = a3.x - a2.x + a3.x; + a1.y = a3.y - a2.y + a3.y; + } + + return error; +} + +static GD_FT_Error ft_stroke_border_moveto(GD_FT_StrokeBorder border, GD_FT_Vector *to) +{ + /* close current open path if any ? */ + if (border->start >= 0) + ft_stroke_border_close(border, FALSE); + + border->start = border->num_points; + border->movable = FALSE; + + return ft_stroke_border_lineto(border, to, FALSE); +} + +static void ft_stroke_border_init(GD_FT_StrokeBorder border) +{ + border->points = NULL; + border->tags = NULL; + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + border->valid = FALSE; +} + +static void ft_stroke_border_reset(GD_FT_StrokeBorder border) +{ + border->num_points = 0; + border->start = -1; + border->valid = FALSE; +} + +static void ft_stroke_border_done(GD_FT_StrokeBorder border) +{ + free(border->points); + free(border->tags); + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + border->valid = FALSE; +} + +static GD_FT_Error ft_stroke_border_get_counts(GD_FT_StrokeBorder border, GD_FT_UInt *anum_points, + GD_FT_UInt *anum_contours) +{ + GD_FT_Error error = 0; + GD_FT_UInt num_points = 0; + GD_FT_UInt num_contours = 0; + + GD_FT_UInt count = border->num_points; + GD_FT_Vector *point = border->points; + GD_FT_Byte *tags = border->tags; + GD_FT_Int in_contour = 0; + + for (; count > 0; count--, num_points++, point++, tags++) { + if (tags[0] & GD_FT_STROKE_TAG_BEGIN) { + if (in_contour != 0) + goto Fail; + + in_contour = 1; + } else if (in_contour == 0) + goto Fail; + + if (tags[0] & GD_FT_STROKE_TAG_END) { + in_contour = 0; + num_contours++; + } + } + + if (in_contour != 0) + goto Fail; + + border->valid = TRUE; + +Exit: + *anum_points = num_points; + *anum_contours = num_contours; + return error; + +Fail: + num_points = 0; + num_contours = 0; + goto Exit; +} + +static void ft_stroke_border_export(GD_FT_StrokeBorder border, GD_FT_Outline *outline) +{ + /* copy point locations */ + memcpy(outline->points + outline->n_points, border->points, + border->num_points * sizeof(GD_FT_Vector)); + + /* copy tags */ + { + GD_FT_UInt count = border->num_points; + GD_FT_Byte *read = border->tags; + GD_FT_Byte *write = (GD_FT_Byte *)outline->tags + outline->n_points; + + for (; count > 0; count--, read++, write++) { + if (*read & GD_FT_STROKE_TAG_ON) + *write = GD_FT_CURVE_TAG_ON; + else if (*read & GD_FT_STROKE_TAG_CUBIC) + *write = GD_FT_CURVE_TAG_CUBIC; + else + *write = GD_FT_CURVE_TAG_CONIC; + } + } + + /* copy contours */ + { + GD_FT_UInt count = border->num_points; + GD_FT_Byte *tags = border->tags; + GD_FT_Short *write = outline->contours + outline->n_contours; + GD_FT_Short idx = (GD_FT_Short)outline->n_points; + + for (; count > 0; count--, tags++, idx++) { + if (*tags & GD_FT_STROKE_TAG_END) { + *write++ = idx; + outline->n_contours++; + } + } + } + + outline->n_points = (short)(outline->n_points + border->num_points); + + assert(GD_FT_Outline_Check(outline) == 0); +} + +/*************************************************************************/ +/*************************************************************************/ +/***** *****/ +/***** STROKER *****/ +/***** *****/ +/*************************************************************************/ +/*************************************************************************/ + +#define GD_FT_SIDE_TO_ROTATE(s) (GD_FT_ANGLE_PI2 - (s) * GD_FT_ANGLE_PI) + +typedef struct GD_FT_StrokerRec_ { + GD_FT_Angle angle_in; /* direction into curr join */ + GD_FT_Angle angle_out; /* direction out of join */ + GD_FT_Vector center; /* current position */ + GD_FT_Fixed line_length; /* length of last lineto */ + GD_FT_Bool first_point; /* is this the start? */ + GD_FT_Bool subpath_open; /* is the subpath open? */ + GD_FT_Angle subpath_angle; /* subpath start direction */ + GD_FT_Vector subpath_start; /* subpath start position */ + GD_FT_Fixed subpath_line_length; /* subpath start lineto len */ + GD_FT_Bool handle_wide_strokes; /* use wide strokes logic? */ + + GD_FT_Stroker_LineCap line_cap; + GD_FT_Stroker_LineJoin line_join; + GD_FT_Stroker_LineJoin line_join_saved; + GD_FT_Fixed miter_limit; + GD_FT_Fixed radius; + + GD_FT_StrokeBorderRec borders[2]; +} GD_FT_StrokerRec; + +/* documentation is in ftstroke.h */ +GD_FT_Error GD_FT_Stroker_New(GD_FT_Stroker *astroker) +{ + GD_FT_Error error = 0; /* assigned in SW_FT_NEW */ + GD_FT_Stroker stroker = NULL; + + stroker = (GD_FT_StrokerRec *)calloc(1, sizeof(GD_FT_StrokerRec)); + if (stroker) { + ft_stroke_border_init(&stroker->borders[0]); + ft_stroke_border_init(&stroker->borders[1]); + } + + *astroker = stroker; + + return error; +} + +void GD_FT_Stroker_Rewind(GD_FT_Stroker stroker) +{ + if (stroker) { + ft_stroke_border_reset(&stroker->borders[0]); + ft_stroke_border_reset(&stroker->borders[1]); + } +} + +/* documentation is in ftstroke.h */ + +void GD_FT_Stroker_Set(GD_FT_Stroker stroker, GD_FT_Fixed radius, GD_FT_Stroker_LineCap line_cap, + GD_FT_Stroker_LineJoin line_join, GD_FT_Fixed miter_limit) +{ + stroker->radius = radius; + stroker->line_cap = line_cap; + stroker->line_join = line_join; + stroker->miter_limit = miter_limit; + + /* ensure miter limit has sensible value */ + if (stroker->miter_limit < 0x10000) + stroker->miter_limit = 0x10000; + + /* save line join style: */ + /* line join style can be temporarily changed when stroking curves */ + stroker->line_join_saved = line_join; + + GD_FT_Stroker_Rewind(stroker); +} + +/* documentation is in ftstroke.h */ + +void GD_FT_Stroker_Done(GD_FT_Stroker stroker) +{ + if (stroker) { + ft_stroke_border_done(&stroker->borders[0]); + ft_stroke_border_done(&stroker->borders[1]); + + free(stroker); + } +} + +/* create a circular arc at a corner or cap */ +static GD_FT_Error ft_stroker_arcto(GD_FT_Stroker stroker, GD_FT_Int side) +{ + GD_FT_Angle total, rotate; + GD_FT_Fixed radius = stroker->radius; + GD_FT_Error error = 0; + GD_FT_StrokeBorder border = stroker->borders + side; + + rotate = GD_FT_SIDE_TO_ROTATE(side); + + total = GD_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); + if (total == GD_FT_ANGLE_PI) + total = -rotate * 2; + + error = + ft_stroke_border_arcto(border, &stroker->center, radius, stroker->angle_in + rotate, total); + border->movable = FALSE; + return error; +} + +/* add a cap at the end of an opened path */ +static GD_FT_Error ft_stroker_cap(GD_FT_Stroker stroker, GD_FT_Angle angle, GD_FT_Int side) +{ + GD_FT_Error error = 0; + + if (stroker->line_cap == GD_FT_STROKER_LINECAP_ROUND) { + /* add a round cap */ + stroker->angle_in = angle; + stroker->angle_out = angle + GD_FT_ANGLE_PI; + + error = ft_stroker_arcto(stroker, side); + } else { + /* add a square or butt cap */ + GD_FT_Vector middle, delta; + GD_FT_Fixed radius = stroker->radius; + GD_FT_StrokeBorder border = stroker->borders + side; + + /* compute middle point and first angle point */ + GD_FT_Vector_From_Polar(&middle, radius, angle); + delta.x = side ? middle.y : -middle.y; + delta.y = side ? -middle.x : middle.x; + + if (stroker->line_cap == GD_FT_STROKER_LINECAP_SQUARE) { + middle.x += stroker->center.x; + middle.y += stroker->center.y; + } else /* GD_FT_STROKER_LINECAP_BUTT */ + { + middle.x = stroker->center.x; + middle.y = stroker->center.y; + } + + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + + /* compute second angle point */ + delta.x = middle.x - delta.x + middle.x; + delta.y = middle.y - delta.y + middle.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + } + +Exit: + return error; +} + +/* process an inside corner, i.e. compute intersection */ +static GD_FT_Error ft_stroker_inside(GD_FT_Stroker stroker, GD_FT_Int side, GD_FT_Fixed line_length) +{ + GD_FT_StrokeBorder border = stroker->borders + side; + GD_FT_Angle phi, theta, rotate; + GD_FT_Fixed length; + GD_FT_Vector sigma, delta; + GD_FT_Error error = 0; + GD_FT_Bool intersect; /* use intersection of lines? */ + + rotate = GD_FT_SIDE_TO_ROTATE(side); + + theta = GD_FT_Angle_Diff(stroker->angle_in, stroker->angle_out) / 2; + + /* Only intersect borders if between two lineto's and both */ + /* lines are long enough (line_length is zero for curves). */ + if (!border->movable || line_length == 0 || theta > 0x59C000 || theta < -0x59C000) + intersect = FALSE; + else { + /* compute minimum required length of lines */ + GD_FT_Fixed min_length; + + GD_FT_Vector_Unit(&sigma, theta); + min_length = ft_pos_abs(GD_FT_MulDiv(stroker->radius, sigma.y, sigma.x)); + + intersect = GD_FT_BOOL(min_length && stroker->line_length >= min_length && + line_length >= min_length); + } + + if (!intersect) { + GD_FT_Vector_From_Polar(&delta, stroker->radius, stroker->angle_out + rotate); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + border->movable = FALSE; + } else { + /* compute median angle */ + phi = stroker->angle_in + theta + rotate; + + length = GD_FT_DivFix(stroker->radius, sigma.x); + + GD_FT_Vector_From_Polar(&delta, length, phi); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + } + + error = ft_stroke_border_lineto(border, &delta, FALSE); + + return error; +} + +/* process an outside corner, i.e. compute bevel/miter/round */ +static GD_FT_Error ft_stroker_outside(GD_FT_Stroker stroker, GD_FT_Int side, + GD_FT_Fixed line_length) +{ + GD_FT_StrokeBorder border = stroker->borders + side; + GD_FT_Error error; + GD_FT_Angle rotate; + + if (stroker->line_join == GD_FT_STROKER_LINEJOIN_ROUND) + error = ft_stroker_arcto(stroker, side); + else { + /* this is a mitered (pointed) or beveled (truncated) corner */ + GD_FT_Fixed radius = stroker->radius; + GD_FT_Vector sigma; + GD_FT_Angle theta = 0, phi = 0; + GD_FT_Bool bevel, fixed_bevel; + + rotate = GD_FT_SIDE_TO_ROTATE(side); + + bevel = GD_FT_BOOL(stroker->line_join == GD_FT_STROKER_LINEJOIN_BEVEL); + + fixed_bevel = GD_FT_BOOL(stroker->line_join != GD_FT_STROKER_LINEJOIN_MITER_VARIABLE); + + /* check miter limit first */ + if (!bevel) { + theta = GD_FT_Angle_Diff(stroker->angle_in, stroker->angle_out) / 2; + + if (theta == GD_FT_ANGLE_PI2) + theta = -rotate; + + phi = stroker->angle_in + theta + rotate; + + GD_FT_Vector_From_Polar(&sigma, stroker->miter_limit, theta); + + /* is miter limit exceeded? */ + if (sigma.x < 0x10000L) { + /* don't create variable bevels for very small deviations; */ + /* FT_Sin(x) = 0 for x <= 57 */ + if (fixed_bevel || ft_pos_abs(theta) > 57) + bevel = TRUE; + } + } + + if (bevel) /* this is a bevel (broken angle) */ + { + if (fixed_bevel) { + /* the outer corners are simply joined together */ + GD_FT_Vector delta; + + /* add bevel */ + GD_FT_Vector_From_Polar(&delta, radius, stroker->angle_out + rotate); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + border->movable = FALSE; + error = ft_stroke_border_lineto(border, &delta, FALSE); + } else /* variable bevel or clipped miter */ + { + /* the miter is truncated */ + GD_FT_Vector middle, delta; + GD_FT_Fixed coef; + + /* compute middle point and first angle point */ + GD_FT_Vector_From_Polar(&middle, GD_FT_MulFix(radius, stroker->miter_limit), phi); + + coef = GD_FT_DivFix(0x10000L - sigma.x, sigma.y); + delta.x = GD_FT_MulFix(middle.y, coef); + delta.y = GD_FT_MulFix(-middle.x, coef); + + middle.x += stroker->center.x; + middle.y += stroker->center.y; + delta.x += middle.x; + delta.y += middle.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + + /* compute second angle point */ + delta.x = middle.x - delta.x + middle.x; + delta.y = middle.y - delta.y + middle.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + + /* finally, add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if (line_length == 0) { + GD_FT_Vector_From_Polar(&delta, radius, stroker->angle_out + rotate); + + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + } + } + } else /* this is a miter (intersection) */ + { + GD_FT_Fixed length; + GD_FT_Vector delta; + + length = GD_FT_MulDiv(stroker->radius, stroker->miter_limit, sigma.x); + + GD_FT_Vector_From_Polar(&delta, length, phi); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + + /* now add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if (line_length == 0) { + GD_FT_Vector_From_Polar(&delta, stroker->radius, stroker->angle_out + rotate); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto(border, &delta, FALSE); + } + } + } + +Exit: + return error; +} + +static GD_FT_Error ft_stroker_process_corner(GD_FT_Stroker stroker, GD_FT_Fixed line_length) +{ + GD_FT_Error error = 0; + GD_FT_Angle turn; + GD_FT_Int inside_side; + + turn = GD_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); + + /* no specific corner processing is required if the turn is 0 */ + if (turn == 0) + goto Exit; + + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if (turn < 0) + inside_side = 1; + + /* process the inside side */ + error = ft_stroker_inside(stroker, inside_side, line_length); + if (error) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside(stroker, 1 - inside_side, line_length); + +Exit: + return error; +} + +/* add two points to the left and right borders corresponding to the */ +/* start of the subpath */ +static GD_FT_Error ft_stroker_subpath_start(GD_FT_Stroker stroker, GD_FT_Angle start_angle, + GD_FT_Fixed line_length) +{ + GD_FT_Vector delta; + GD_FT_Vector point; + GD_FT_Error error; + GD_FT_StrokeBorder border; + + GD_FT_Vector_From_Polar(&delta, stroker->radius, start_angle + GD_FT_ANGLE_PI2); + + point.x = stroker->center.x + delta.x; + point.y = stroker->center.y + delta.y; + + border = stroker->borders; + error = ft_stroke_border_moveto(border, &point); + if (error) + goto Exit; + + point.x = stroker->center.x - delta.x; + point.y = stroker->center.y - delta.y; + + border++; + error = ft_stroke_border_moveto(border, &point); + + /* save angle, position, and line length for last join */ + /* (line_length is zero for curves) */ + stroker->subpath_angle = start_angle; + stroker->first_point = FALSE; + stroker->subpath_line_length = line_length; + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_LineTo(GD_FT_Stroker stroker, GD_FT_Vector *to) +{ + GD_FT_Error error = 0; + GD_FT_StrokeBorder border; + GD_FT_Vector delta; + GD_FT_Angle angle; + GD_FT_Int side; + GD_FT_Fixed line_length; + + delta.x = to->x - stroker->center.x; + delta.y = to->y - stroker->center.y; + + /* a zero-length lineto is a no-op; avoid creating a spurious corner */ + if (delta.x == 0 && delta.y == 0) + goto Exit; + + /* compute length of line */ + line_length = GD_FT_Vector_Length(&delta); + + angle = GD_FT_Atan2(delta.x, delta.y); + GD_FT_Vector_From_Polar(&delta, stroker->radius, angle + GD_FT_ANGLE_PI2); + + /* process corner if necessary */ + if (stroker->first_point) { + /* This is the first segment of a subpath. We need to */ + /* add a point to each border at their respective starting */ + /* point locations. */ + error = ft_stroker_subpath_start(stroker, angle, line_length); + if (error) + goto Exit; + } else { + /* process the current corner */ + stroker->angle_out = angle; + error = ft_stroker_process_corner(stroker, line_length); + if (error) + goto Exit; + } + + /* now add a line segment to both the `inside' and `outside' paths */ + for (border = stroker->borders, side = 1; side >= 0; side--, border++) { + GD_FT_Vector point; + + point.x = to->x + delta.x; + point.y = to->y + delta.y; + + /* the ends of lineto borders are movable */ + error = ft_stroke_border_lineto(border, &point, TRUE); + if (error) + goto Exit; + + delta.x = -delta.x; + delta.y = -delta.y; + } + + stroker->angle_in = angle; + stroker->center = *to; + stroker->line_length = line_length; + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_ConicTo(GD_FT_Stroker stroker, GD_FT_Vector *control, GD_FT_Vector *to) +{ + GD_FT_Error error = 0; + GD_FT_Vector bez_stack[34]; + GD_FT_Vector *arc; + GD_FT_Vector *limit = bez_stack + 30; + GD_FT_Bool first_arc = TRUE; + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if (GD_FT_IS_SMALL(stroker->center.x - control->x) && + GD_FT_IS_SMALL(stroker->center.y - control->y) && GD_FT_IS_SMALL(control->x - to->x) && + GD_FT_IS_SMALL(control->y - to->y)) { + stroker->center = *to; + goto Exit; + } + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control; + arc[2] = stroker->center; + + while (arc >= bez_stack) { + GD_FT_Angle angle_in, angle_out; + + /* initialize with current direction */ + angle_in = angle_out = stroker->angle_in; + + if (arc < limit && !ft_conic_is_small_enough(arc, &angle_in, &angle_out)) { + if (stroker->first_point) + stroker->angle_in = angle_in; + + ft_conic_split(arc); + arc += 2; + continue; + } + + if (first_arc) { + first_arc = FALSE; + + /* process corner if necessary */ + if (stroker->first_point) + error = ft_stroker_subpath_start(stroker, angle_in, 0); + else { + stroker->angle_out = angle_in; + error = ft_stroker_process_corner(stroker, 0); + } + } else if (ft_pos_abs(GD_FT_Angle_Diff(stroker->angle_in, angle_in)) > + GD_FT_SMALL_CONIC_THRESHOLD / 4) { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[2]; + stroker->angle_out = angle_in; + stroker->line_join = GD_FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner(stroker, 0); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if (error) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + GD_FT_Vector ctrl, end; + GD_FT_Angle theta, phi, rotate, alpha0 = 0; + GD_FT_Fixed length; + GD_FT_StrokeBorder border; + GD_FT_Int side; + + theta = GD_FT_Angle_Diff(angle_in, angle_out) / 2; + phi = angle_in + theta; + length = GD_FT_DivFix(stroker->radius, GD_FT_Cos(theta)); + + /* compute direction of original arc */ + if (stroker->handle_wide_strokes) + alpha0 = GD_FT_Atan2(arc[0].x - arc[2].x, arc[0].y - arc[2].y); + + for (border = stroker->borders, side = 0; side <= 1; side++, border++) { + rotate = GD_FT_SIDE_TO_ROTATE(side); + + /* compute control point */ + GD_FT_Vector_From_Polar(&ctrl, length, phi + rotate); + ctrl.x += arc[1].x; + ctrl.y += arc[1].y; + + /* compute end point */ + GD_FT_Vector_From_Polar(&end, stroker->radius, angle_out + rotate); + end.x += arc[0].x; + end.y += arc[0].y; + + if (stroker->handle_wide_strokes) { + GD_FT_Vector start; + GD_FT_Angle alpha1; + + /* determine whether the border radius is greater than the + */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = GD_FT_Atan2(end.x - start.x, end.y - start.y); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if (ft_pos_abs(GD_FT_Angle_Diff(alpha0, alpha1)) > GD_FT_ANGLE_PI / 2) { + GD_FT_Angle beta, gamma; + GD_FT_Vector bvec, delta; + GD_FT_Fixed blen, sinA, sinB, alen; + + /* use the sine rule to find the intersection point */ + beta = GD_FT_Atan2(arc[2].x - start.x, arc[2].y - start.y); + gamma = GD_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y); + + bvec.x = end.x - start.x; + bvec.y = end.y - start.y; + + blen = GD_FT_Vector_Length(&bvec); + + sinA = ft_pos_abs(GD_FT_Sin(alpha1 - gamma)); + sinB = ft_pos_abs(GD_FT_Sin(beta - gamma)); + + alen = GD_FT_MulDiv(blen, sinA, sinB); + + GD_FT_Vector_From_Polar(&delta, alen, beta); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + border->movable = FALSE; + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + error = ft_stroke_border_lineto(border, &end, FALSE); + if (error) + goto Exit; + error = ft_stroke_border_conicto(border, &ctrl, &start); + if (error) + goto Exit; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto(border, &end, FALSE); + if (error) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_conicto(border, &ctrl, &end); + if (error) + goto Exit; + } + } + + arc -= 2; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_CubicTo(GD_FT_Stroker stroker, GD_FT_Vector *control1, + GD_FT_Vector *control2, GD_FT_Vector *to) +{ + GD_FT_Error error = 0; + GD_FT_Vector bez_stack[37]; + GD_FT_Vector *arc; + GD_FT_Vector *limit = bez_stack + 32; + GD_FT_Bool first_arc = TRUE; + + /* if all control points are coincident, this is a no-op; */ + /* avoid creating a spurious corner */ + if (GD_FT_IS_SMALL(stroker->center.x - control1->x) && + GD_FT_IS_SMALL(stroker->center.y - control1->y) && + GD_FT_IS_SMALL(control1->x - control2->x) && GD_FT_IS_SMALL(control1->y - control2->y) && + GD_FT_IS_SMALL(control2->x - to->x) && GD_FT_IS_SMALL(control2->y - to->y)) { + stroker->center = *to; + goto Exit; + } + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control2; + arc[2] = *control1; + arc[3] = stroker->center; + + while (arc >= bez_stack) { + GD_FT_Angle angle_in, angle_mid, angle_out; + + /* initialize with current direction */ + angle_in = angle_out = angle_mid = stroker->angle_in; + + if (arc < limit && !ft_cubic_is_small_enough(arc, &angle_in, &angle_mid, &angle_out)) { + if (stroker->first_point) + stroker->angle_in = angle_in; + + ft_cubic_split(arc); + arc += 3; + continue; + } + + if (first_arc) { + first_arc = FALSE; + + /* process corner if necessary */ + if (stroker->first_point) + error = ft_stroker_subpath_start(stroker, angle_in, 0); + else { + stroker->angle_out = angle_in; + error = ft_stroker_process_corner(stroker, 0); + } + } else if (ft_pos_abs(GD_FT_Angle_Diff(stroker->angle_in, angle_in)) > + GD_FT_SMALL_CUBIC_THRESHOLD / 4) { + /* if the deviation from one arc to the next is too great, */ + /* add a round corner */ + stroker->center = arc[3]; + stroker->angle_out = angle_in; + stroker->line_join = GD_FT_STROKER_LINEJOIN_ROUND; + + error = ft_stroker_process_corner(stroker, 0); + + /* reinstate line join style */ + stroker->line_join = stroker->line_join_saved; + } + + if (error) + goto Exit; + + /* the arc's angle is small enough; we can add it directly to each */ + /* border */ + { + GD_FT_Vector ctrl1, ctrl2, end; + GD_FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; + GD_FT_Fixed length1, length2; + GD_FT_StrokeBorder border; + GD_FT_Int side; + + theta1 = GD_FT_Angle_Diff(angle_in, angle_mid) / 2; + theta2 = GD_FT_Angle_Diff(angle_mid, angle_out) / 2; + phi1 = ft_angle_mean(angle_in, angle_mid); + phi2 = ft_angle_mean(angle_mid, angle_out); + length1 = GD_FT_DivFix(stroker->radius, GD_FT_Cos(theta1)); + length2 = GD_FT_DivFix(stroker->radius, GD_FT_Cos(theta2)); + + /* compute direction of original arc */ + if (stroker->handle_wide_strokes) + alpha0 = GD_FT_Atan2(arc[0].x - arc[3].x, arc[0].y - arc[3].y); + + for (border = stroker->borders, side = 0; side <= 1; side++, border++) { + rotate = GD_FT_SIDE_TO_ROTATE(side); + + /* compute control points */ + GD_FT_Vector_From_Polar(&ctrl1, length1, phi1 + rotate); + ctrl1.x += arc[2].x; + ctrl1.y += arc[2].y; + + GD_FT_Vector_From_Polar(&ctrl2, length2, phi2 + rotate); + ctrl2.x += arc[1].x; + ctrl2.y += arc[1].y; + + /* compute end point */ + GD_FT_Vector_From_Polar(&end, stroker->radius, angle_out + rotate); + end.x += arc[0].x; + end.y += arc[0].y; + + if (stroker->handle_wide_strokes) { + GD_FT_Vector start; + GD_FT_Angle alpha1; + + /* determine whether the border radius is greater than the + */ + /* radius of curvature of the original arc */ + start = border->points[border->num_points - 1]; + + alpha1 = GD_FT_Atan2(end.x - start.x, end.y - start.y); + + /* is the direction of the border arc opposite to */ + /* that of the original arc? */ + if (ft_pos_abs(GD_FT_Angle_Diff(alpha0, alpha1)) > GD_FT_ANGLE_PI / 2) { + GD_FT_Angle beta, gamma; + GD_FT_Vector bvec, delta; + GD_FT_Fixed blen, sinA, sinB, alen; + + /* use the sine rule to find the intersection point */ + beta = GD_FT_Atan2(arc[3].x - start.x, arc[3].y - start.y); + gamma = GD_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y); + + bvec.x = end.x - start.x; + bvec.y = end.y - start.y; + + blen = GD_FT_Vector_Length(&bvec); + + sinA = ft_pos_abs(GD_FT_Sin(alpha1 - gamma)); + sinB = ft_pos_abs(GD_FT_Sin(beta - gamma)); + + alen = GD_FT_MulDiv(blen, sinA, sinB); + + GD_FT_Vector_From_Polar(&delta, alen, beta); + delta.x += start.x; + delta.y += start.y; + + /* circumnavigate the negative sector backwards */ + border->movable = FALSE; + error = ft_stroke_border_lineto(border, &delta, FALSE); + if (error) + goto Exit; + error = ft_stroke_border_lineto(border, &end, FALSE); + if (error) + goto Exit; + error = ft_stroke_border_cubicto(border, &ctrl2, &ctrl1, &start); + if (error) + goto Exit; + /* and then move to the endpoint */ + error = ft_stroke_border_lineto(border, &end, FALSE); + if (error) + goto Exit; + + continue; + } + + /* else fall through */ + } + + /* simply add an arc */ + error = ft_stroke_border_cubicto(border, &ctrl1, &ctrl2, &end); + if (error) + goto Exit; + } + } + + arc -= 3; + + stroker->angle_in = angle_out; + } + + stroker->center = *to; + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_BeginSubPath(GD_FT_Stroker stroker, GD_FT_Vector *to, GD_FT_Bool open) +{ + /* We cannot process the first point, because there is not enough */ + /* information regarding its corner/cap. The latter will be processed */ + /* in the `GD_FT_Stroker_EndSubPath' routine. */ + /* */ + stroker->first_point = TRUE; + stroker->center = *to; + stroker->subpath_open = open; + + /* Determine if we need to check whether the border radius is greater */ + /* than the radius of curvature of a curve, to handle this case */ + /* specially. This is only required if bevel joins or butt caps may */ + /* be created, because round & miter joins and round & square caps */ + /* cover the negative sector created with wide strokes. */ + stroker->handle_wide_strokes = + GD_FT_BOOL(stroker->line_join != GD_FT_STROKER_LINEJOIN_ROUND || + (stroker->subpath_open && stroker->line_cap == GD_FT_STROKER_LINECAP_BUTT)); + + /* record the subpath start point for each border */ + stroker->subpath_start = *to; + + stroker->angle_in = 0; + + return 0; +} + +static GD_FT_Error ft_stroker_add_reverse_left(GD_FT_Stroker stroker, GD_FT_Bool open) +{ + GD_FT_StrokeBorder right = stroker->borders + 0; + GD_FT_StrokeBorder left = stroker->borders + 1; + GD_FT_Int new_points; + GD_FT_Error error = 0; + + assert(left->start >= 0); + + new_points = left->num_points - left->start; + if (new_points > 0) { + error = ft_stroke_border_grow(right, (GD_FT_UInt)new_points); + if (error) + goto Exit; + + { + GD_FT_Vector *dst_point = right->points + right->num_points; + GD_FT_Byte *dst_tag = right->tags + right->num_points; + GD_FT_Vector *src_point = left->points + left->num_points - 1; + GD_FT_Byte *src_tag = left->tags + left->num_points - 1; + + while (src_point >= left->points + left->start) { + *dst_point = *src_point; + *dst_tag = *src_tag; + + if (open) + dst_tag[0] &= ~GD_FT_STROKE_TAG_BEGIN_END; + else { + GD_FT_Byte ttag = (GD_FT_Byte)(dst_tag[0] & GD_FT_STROKE_TAG_BEGIN_END); + + /* switch begin/end tags if necessary */ + if (ttag == GD_FT_STROKE_TAG_BEGIN || ttag == GD_FT_STROKE_TAG_END) + dst_tag[0] ^= GD_FT_STROKE_TAG_BEGIN_END; + } + + src_point--; + src_tag--; + dst_point++; + dst_tag++; + } + } + + left->num_points = left->start; + right->num_points += new_points; + + right->movable = FALSE; + left->movable = FALSE; + } + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +/* there's a lot of magic in this function! */ +GD_FT_Error GD_FT_Stroker_EndSubPath(GD_FT_Stroker stroker) +{ + GD_FT_Error error = 0; + + if (stroker->subpath_open) { + GD_FT_StrokeBorder right = stroker->borders; + + /* All right, this is an opened path, we need to add a cap between */ + /* right & left, add the reverse of left, then add a final cap */ + /* between left & right. */ + error = ft_stroker_cap(stroker, stroker->angle_in, 0); + if (error) + goto Exit; + + /* add reversed points from `left' to `right' */ + error = ft_stroker_add_reverse_left(stroker, TRUE); + if (error) + goto Exit; + + /* now add the final cap */ + stroker->center = stroker->subpath_start; + error = ft_stroker_cap(stroker, stroker->subpath_angle + GD_FT_ANGLE_PI, 0); + if (error) + goto Exit; + + /* Now end the right subpath accordingly. The left one is */ + /* rewind and doesn't need further processing. */ + ft_stroke_border_close(right, FALSE); + } else { + GD_FT_Angle turn; + GD_FT_Int inside_side; + + /* close the path if needed */ + if (stroker->center.x != stroker->subpath_start.x || + stroker->center.y != stroker->subpath_start.y) { + error = GD_FT_Stroker_LineTo(stroker, &stroker->subpath_start); + if (error) + goto Exit; + } + + /* process the corner */ + stroker->angle_out = stroker->subpath_angle; + turn = GD_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); + + /* no specific corner processing is required if the turn is 0 */ + if (turn != 0) { + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if (turn < 0) + inside_side = 1; + + error = ft_stroker_inside(stroker, inside_side, stroker->subpath_line_length); + if (error) + goto Exit; + + /* process the outside side */ + error = ft_stroker_outside(stroker, 1 - inside_side, stroker->subpath_line_length); + if (error) + goto Exit; + } + + /* then end our two subpaths */ + ft_stroke_border_close(stroker->borders + 0, FALSE); + ft_stroke_border_close(stroker->borders + 1, TRUE); + } + +Exit: + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_GetBorderCounts(GD_FT_Stroker stroker, GD_FT_StrokerBorder border, + GD_FT_UInt *anum_points, GD_FT_UInt *anum_contours) +{ + GD_FT_UInt num_points = 0, num_contours = 0; + GD_FT_Error error; + + if (!stroker || border > 1) { + error = -1; // GD_FT_THROW( Invalid_Argument ); + goto Exit; + } + + error = ft_stroke_border_get_counts(stroker->borders + border, &num_points, &num_contours); +Exit: + if (anum_points) + *anum_points = num_points; + + if (anum_contours) + *anum_contours = num_contours; + + return error; +} + +/* documentation is in ftstroke.h */ + +GD_FT_Error GD_FT_Stroker_GetCounts(GD_FT_Stroker stroker, GD_FT_UInt *anum_points, + GD_FT_UInt *anum_contours) +{ + GD_FT_UInt count1, count2, num_points = 0; + GD_FT_UInt count3, count4, num_contours = 0; + GD_FT_Error error; + + error = ft_stroke_border_get_counts(stroker->borders + 0, &count1, &count2); + if (error) + goto Exit; + + error = ft_stroke_border_get_counts(stroker->borders + 1, &count3, &count4); + if (error) + goto Exit; + + num_points = count1 + count3; + num_contours = count2 + count4; + +Exit: + *anum_points = num_points; + *anum_contours = num_contours; + return error; +} + +/* documentation is in ftstroke.h */ + +void GD_FT_Stroker_ExportBorder(GD_FT_Stroker stroker, GD_FT_StrokerBorder border, + GD_FT_Outline *outline) +{ + if (border == GD_FT_STROKER_BORDER_LEFT || border == GD_FT_STROKER_BORDER_RIGHT) { + GD_FT_StrokeBorder sborder = &stroker->borders[border]; + + if (sborder->valid) + ft_stroke_border_export(sborder, outline); + } +} + +/* documentation is in ftstroke.h */ + +void GD_FT_Stroker_Export(GD_FT_Stroker stroker, GD_FT_Outline *outline) +{ + GD_FT_Stroker_ExportBorder(stroker, GD_FT_STROKER_BORDER_LEFT, outline); + GD_FT_Stroker_ExportBorder(stroker, GD_FT_STROKER_BORDER_RIGHT, outline); +} + +/* documentation is in ftstroke.h */ + +/* + * The following is very similar to GD_FT_Outline_Decompose, except + * that we do support opened paths, and do not scale the outline. + */ +GD_FT_Error GD_FT_Stroker_ParseOutline(GD_FT_Stroker stroker, const GD_FT_Outline *outline) +{ + GD_FT_Vector v_last; + GD_FT_Vector v_control; + GD_FT_Vector v_start; + + GD_FT_Vector *point; + GD_FT_Vector *limit; + char *tags; + + GD_FT_Error error; + + GD_FT_Int n; /* index of contour in outline */ + GD_FT_UInt first; /* index of first point in contour */ + GD_FT_Int tag; /* current point's state */ + + if (!outline || !stroker) + return -1; // GD_FT_THROW( Invalid_Argument ); + + GD_FT_Stroker_Rewind(stroker); + + first = 0; + + for (n = 0; n < outline->n_contours; n++) { + GD_FT_UInt last; /* index of last point in contour */ + + last = outline->contours[n]; + limit = outline->points + last; + + /* skip empty points; we don't stroke these */ + if (last <= first) { + first = last + 1; + continue; + } + + v_start = outline->points[first]; + v_last = outline->points[last]; + + v_control = v_start; + + point = outline->points + first; + tags = outline->tags + first; + tag = GD_FT_CURVE_TAG(tags[0]); + + /* A contour cannot start with a cubic control point! */ + if (tag == GD_FT_CURVE_TAG_CUBIC) + goto Invalid_Outline; + + /* check first point to determine origin */ + if (tag == GD_FT_CURVE_TAG_CONIC) { + /* First point is conic control. Yes, this happens. */ + if (GD_FT_CURVE_TAG(outline->tags[last]) == GD_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 */ + v_start.x = (v_start.x + v_last.x) / 2; + v_start.y = (v_start.y + v_last.y) / 2; + } + point--; + tags--; + } + + error = GD_FT_Stroker_BeginSubPath(stroker, &v_start, outline->contours_flag[n]); + if (error) + goto Exit; + + while (point < limit) { + point++; + tags++; + + tag = GD_FT_CURVE_TAG(tags[0]); + switch (tag) { + case GD_FT_CURVE_TAG_ON: /* emit a single line_to */ + { + GD_FT_Vector vec; + + vec.x = point->x; + vec.y = point->y; + + error = GD_FT_Stroker_LineTo(stroker, &vec); + if (error) + goto Exit; + continue; + } + + case GD_FT_CURVE_TAG_CONIC: /* consume conic arcs */ + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if (point < limit) { + GD_FT_Vector vec; + GD_FT_Vector v_middle; + + point++; + tags++; + tag = GD_FT_CURVE_TAG(tags[0]); + + vec = point[0]; + + if (tag == GD_FT_CURVE_TAG_ON) { + error = GD_FT_Stroker_ConicTo(stroker, &v_control, &vec); + if (error) + goto Exit; + continue; + } + + if (tag != GD_FT_CURVE_TAG_CONIC) + goto Invalid_Outline; + + v_middle.x = (v_control.x + vec.x) / 2; + v_middle.y = (v_control.y + vec.y) / 2; + + error = GD_FT_Stroker_ConicTo(stroker, &v_control, &v_middle); + if (error) + goto Exit; + + v_control = vec; + goto Do_Conic; + } + + error = GD_FT_Stroker_ConicTo(stroker, &v_control, &v_start); + goto Close; + + default: /* GD_FT_CURVE_TAG_CUBIC */ + { + GD_FT_Vector vec1, vec2; + + if (point + 1 > limit || GD_FT_CURVE_TAG(tags[1]) != GD_FT_CURVE_TAG_CUBIC) + goto Invalid_Outline; + + point += 2; + tags += 2; + + vec1 = point[-2]; + vec2 = point[-1]; + + if (point <= limit) { + GD_FT_Vector vec; + + vec = point[0]; + + error = GD_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &vec); + if (error) + goto Exit; + continue; + } + + error = GD_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &v_start); + goto Close; + } + } + } + + Close: + if (error) + goto Exit; + + /* don't try to end the path if no segments have been generated */ + if (!stroker->first_point) { + error = GD_FT_Stroker_EndSubPath(stroker); + if (error) + goto Exit; + } + + first = last + 1; + } + + return 0; + +Exit: + return error; + +Invalid_Outline: + return -2; // GD_FT_THROW( Invalid_Outline ); +} + +/* END */ diff --git a/ext/gd/libgd/ftraster/gd_ft_stroker.h b/ext/gd/libgd/ftraster/gd_ft_stroker.h new file mode 100644 index 000000000000..2af3c872ac96 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_stroker.h @@ -0,0 +1,320 @@ +#ifndef GD_FT_STROKER_H +#define GD_FT_STROKER_H +/***************************************************************************/ +/* */ +/* ftstroke.h */ +/* */ +/* FreeType path stroker (specification). */ +/* */ +/* Copyright 2002-2006, 2008, 2009, 2011-2012 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include "gd_ft_raster.h" + +/************************************************************** + * + * @type: + * GD_FT_Stroker + * + * @description: + * Opaque handler to a path stroker object. + */ +typedef struct GD_FT_StrokerRec_ *GD_FT_Stroker; + +/************************************************************** + * + * @enum: + * GD_FT_Stroker_LineJoin + * + * @description: + * These values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * GD_FT_STROKER_LINEJOIN_ROUND :: + * Used to render rounded line joins. Circular arcs are used + * to join two lines smoothly. + * + * GD_FT_STROKER_LINEJOIN_BEVEL :: + * Used to render beveled line joins. The outer corner of + * the joined lines is filled by enclosing the triangular + * region of the corner with a straight line between the + * outer corners of each stroke. + * + * GD_FT_STROKER_LINEJOIN_MITER_FIXED :: + * Used to render mitered line joins, with fixed bevels if the + * miter limit is exceeded. The outer edges of the strokes + * for the two segments are extended until they meet at an + * angle. If the segments meet at too sharp an angle (such + * that the miter would extend from the intersection of the + * segments a distance greater than the product of the miter + * limit value and the border radius), then a bevel join (see + * above) is used instead. This prevents long spikes being + * created. GD_FT_STROKER_LINEJOIN_MITER_FIXED generates a miter + * line join as used in PostScript and PDF. + * + * GD_FT_STROKER_LINEJOIN_MITER_VARIABLE :: + * GD_FT_STROKER_LINEJOIN_MITER :: + * Used to render mitered line joins, with variable bevels if + * the miter limit is exceeded. The intersection of the + * strokes is clipped at a line perpendicular to the bisector + * of the angle between the strokes, at the distance from the + * intersection of the segments equal to the product of the + * miter limit value and the border radius. This prevents + * long spikes being created. + * GD_FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line + * join as used in XPS. GD_FT_STROKER_LINEJOIN_MITER is an alias + * for GD_FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for + * backwards compatibility. + */ +typedef enum GD_FT_Stroker_LineJoin_ { + GD_FT_STROKER_LINEJOIN_ROUND = 0, + GD_FT_STROKER_LINEJOIN_BEVEL = 1, + GD_FT_STROKER_LINEJOIN_MITER_VARIABLE = 2, + GD_FT_STROKER_LINEJOIN_MITER = GD_FT_STROKER_LINEJOIN_MITER_VARIABLE, + GD_FT_STROKER_LINEJOIN_MITER_FIXED = 3 + +} GD_FT_Stroker_LineJoin; + +/************************************************************** + * + * @enum: + * GD_FT_Stroker_LineCap + * + * @description: + * These values determine how the end of opened sub-paths are + * rendered in a stroke. + * + * @values: + * GD_FT_STROKER_LINECAP_BUTT :: + * The end of lines is rendered as a full stop on the last + * point itself. + * + * GD_FT_STROKER_LINECAP_ROUND :: + * The end of lines is rendered as a half-circle around the + * last point. + * + * GD_FT_STROKER_LINECAP_SQUARE :: + * The end of lines is rendered as a square around the + * last point. + */ +typedef enum GD_FT_Stroker_LineCap_ { + GD_FT_STROKER_LINECAP_BUTT = 0, + GD_FT_STROKER_LINECAP_ROUND, + GD_FT_STROKER_LINECAP_SQUARE + +} GD_FT_Stroker_LineCap; + +/************************************************************** + * + * @enum: + * GD_FT_StrokerBorder + * + * @description: + * These values are used to select a given stroke border + * in @GD_FT_Stroker_GetBorderCounts and @GD_FT_Stroker_ExportBorder. + * + * @values: + * GD_FT_STROKER_BORDER_LEFT :: + * Select the left border, relative to the drawing direction. + * + * GD_FT_STROKER_BORDER_RIGHT :: + * Select the right border, relative to the drawing direction. + * + * @note: + * Applications are generally interested in the `inside' and `outside' + * borders. However, there is no direct mapping between these and the + * `left' and `right' ones, since this really depends on the glyph's + * drawing orientation, which varies between font formats. + * + * You can however use @GD_FT_Outline_GetInsideBorder and + * @GD_FT_Outline_GetOutsideBorder to get these. + */ +typedef enum GD_FT_StrokerBorder_ { + GD_FT_STROKER_BORDER_LEFT = 0, + GD_FT_STROKER_BORDER_RIGHT + +} GD_FT_StrokerBorder; + +/************************************************************** + * + * @function: + * GD_FT_Stroker_New + * + * @description: + * Create a new stroker object. + * + * @input: + * library :: + * FreeType library handle. + * + * @output: + * astroker :: + * A new stroker object handle. NULL in case of error. + * + * @return: + * FreeType error code. 0~means success. + */ +GD_FT_Error GD_FT_Stroker_New(GD_FT_Stroker *astroker); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_Set + * + * @description: + * Reset a stroker object's attributes. + * + * @input: + * stroker :: + * The target stroker handle. + * + * radius :: + * The border radius. + * + * line_cap :: + * The line cap style. + * + * line_join :: + * The line join style. + * + * miter_limit :: + * The miter limit for the GD_FT_STROKER_LINEJOIN_MITER_FIXED and + * GD_FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles, + * expressed as 16.16 fixed-point value. + * + * @note: + * The radius is expressed in the same units as the outline + * coordinates. + */ +void GD_FT_Stroker_Set(GD_FT_Stroker stroker, GD_FT_Fixed radius, GD_FT_Stroker_LineCap line_cap, + GD_FT_Stroker_LineJoin line_join, GD_FT_Fixed miter_limit); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_ParseOutline + * + * @description: + * A convenience function used to parse a whole outline with + * the stroker. The resulting outline(s) can be retrieved + * later by functions like @GD_FT_Stroker_GetCounts and @GD_FT_Stroker_Export. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The source outline. + * + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * If `opened' is~0 (the default), the outline is treated as a closed + * path, and the stroker generates two distinct `border' outlines. + * + * + * This function calls @GD_FT_Stroker_Rewind automatically. + */ +GD_FT_Error GD_FT_Stroker_ParseOutline(GD_FT_Stroker stroker, const GD_FT_Outline *outline); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_GetCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export all points/borders from the stroked + * outline/path. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0~means success. + */ +GD_FT_Error GD_FT_Stroker_GetCounts(GD_FT_Stroker stroker, GD_FT_UInt *anum_points, + GD_FT_UInt *anum_contours); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_ExportBorder + * + * @description: + * Export a single border of a stroked outline. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border to export (LEFT or RIGHT). + * + * outline :: + * The target outline handle. + * + * @note: + * Call this after GD_FT_Stroker_GetCounts to get individual borders. + * The outline must be pre-allocated with sufficient space. + */ +void GD_FT_Stroker_ExportBorder(GD_FT_Stroker stroker, GD_FT_StrokerBorder border, + GD_FT_Outline *outline); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_Export + * + * @description: + * Call this function after @GD_FT_Stroker_GetBorderCounts to + * export all borders to your own @GD_FT_Outline structure. + * + * Note that this function appends the border points and + * contours to your outline, but does not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The target outline handle. + */ +void GD_FT_Stroker_Export(GD_FT_Stroker stroker, GD_FT_Outline *outline); + +/************************************************************** + * + * @function: + * GD_FT_Stroker_Done + * + * @description: + * Destroy a stroker object. + * + * @input: + * stroker :: + * A stroker handle. Can be NULL. + */ +void GD_FT_Stroker_Done(GD_FT_Stroker stroker); + +#endif // GD_FT_STROKER_H diff --git a/ext/gd/libgd/ftraster/gd_ft_types.h b/ext/gd/libgd/ftraster/gd_ft_types.h new file mode 100644 index 000000000000..4e30d2b38694 --- /dev/null +++ b/ext/gd/libgd/ftraster/gd_ft_types.h @@ -0,0 +1,146 @@ +#ifndef GD_FT_TYPES_H +#define GD_FT_TYPES_H + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Fixed */ +/* */ +/* */ +/* This type is used to store 16.16 fixed-point values, like scaling */ +/* values or matrix coefficients. */ +/* */ +typedef signed long GD_FT_Fixed; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Int */ +/* */ +/* */ +/* A typedef for the int type. */ +/* */ +typedef signed int GD_FT_Int; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_UInt */ +/* */ +/* */ +/* A typedef for the unsigned int type. */ +/* */ +typedef unsigned int GD_FT_UInt; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Long */ +/* */ +/* */ +/* A typedef for signed long. */ +/* */ +typedef signed long GD_FT_Long; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_ULong */ +/* */ +/* */ +/* A typedef for unsigned long. */ +/* */ +typedef unsigned long GD_FT_ULong; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Short */ +/* */ +/* */ +/* A typedef for signed short. */ +/* */ +typedef signed short GD_FT_Short; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Byte */ +/* */ +/* */ +/* A simple typedef for the _unsigned_ char type. */ +/* */ +typedef unsigned char GD_FT_Byte; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Bool */ +/* */ +/* */ +/* A typedef of unsigned char, used for simple booleans. As usual, */ +/* values 1 and~0 represent true and false, respectively. */ +/* */ +typedef unsigned char GD_FT_Bool; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Error */ +/* */ +/* */ +/* The FreeType error code type. A value of~0 is always interpreted */ +/* as a successful operation. */ +/* */ +typedef int GD_FT_Error; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Pos */ +/* */ +/* */ +/* The type GD_FT_Pos is used to store vectorial coordinates. Depending */ +/* on the context, these can represent distances in integer font */ +/* units, or 16.16, or 26.6 fixed-point pixel coordinates. */ +/* */ +typedef signed long GD_FT_Pos; + +/*************************************************************************/ +/* */ +/* */ +/* GD_FT_Vector */ +/* */ +/* */ +/* A simple structure used to store a 2D vector; coordinates are of */ +/* the GD_FT_Pos type. */ +/* */ +/* */ +/* x :: The horizontal coordinate. */ +/* y :: The vertical coordinate. */ +/* */ +typedef struct GD_FT_Vector_ { + GD_FT_Pos x; + GD_FT_Pos y; + +} GD_FT_Vector; + +typedef long long int GD_FT_Int64; +typedef unsigned long long int GD_FT_UInt64; + +typedef signed int GD_FT_Int32; +typedef unsigned int GD_FT_UInt32; + +#define GD_FT_BOOL(x) ((GD_FT_Bool)(x)) + +#define GD_FT_SIZEOF_LONG 4 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#endif // GD_FT_TYPES_H diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 47c4dbc0d770..ccdf95a0a2ac 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -1,3008 +1,3994 @@ -#include -#include -#include #include "gd.h" -#include "gdhelpers.h" +#include "gd_color.h" #include "gd_errors.h" +#include "gd_intern.h" +#include "gdhelpers.h" +#include +#include +#include +#include +#include + +#if ENABLE_CORRECTED_LEGACY_COMPOSITING +#include "gd_compositor.h" +#endif #include "php.h" +/* 2.0.12: this now checks the clipping rectangle */ +#define gdImageBoundsSafeMacro(im, x, y) \ + (!((((y) < (im)->cy1) || ((y) > (im)->cy2)) || (((x) < (im)->cx1) || ((x) > (im)->cx2)))) -#ifdef _OSD_POSIX /* BS2000 uses the EBCDIC char set instead of ASCII */ +#ifdef _OSD_POSIX /* BS2000 uses the EBCDIC char set instead of ASCII */ #define CHARSET_EBCDIC -#define __attribute__(any) /*nothing */ +#define __attribute__(any) /*nothing */ #endif /*_OSD_POSIX*/ #ifndef CHARSET_EBCDIC -#define ASC(ch) ch +#define ASC(ch) ch #else /*CHARSET_EBCDIC */ #define ASC(ch) gd_toascii[(unsigned char)ch] -static const unsigned char gd_toascii[256] = -{ -/*00 */ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, - 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................ */ -/*10 */ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, - 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................ */ -/*20 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................ */ -/*30 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, - 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................ */ -/*40 */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, - 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+| */ -/*50 */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, - 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);. */ -/*60 */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, - 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, -/*-/........^,%_>?*/ -/*70 */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, - 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'=" */ -/*80 */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi...... */ -/*90 */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, - 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr...... */ -/*a0 */ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz...... */ -/*b0 */ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, - 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\].. */ -/*c0 */ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI...... */ -/*d0 */ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, - 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR...... */ -/*e0 */ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ...... */ -/*f0 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~ */ +static const unsigned char gd_toascii[256] = { + /*00 */ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, /*................ */ + /*10 */ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, + 0x1e, 0x1f, /*................ */ + /*20 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, + 0x06, 0x07, /*................ */ + /*30 */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, + 0x9e, 0x1a, /*................ */ + /*40 */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, + 0x2b, 0x7c, /* .........`.<(+| */ + /*50 */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, + 0x3b, 0x9f, /*&.........!$*);. */ + /*60 */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, + 0x3e, 0x3f, + /*-/........^,%_>?*/ + /*70 */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, + 0x3d, 0x22, /*..........:#@'=" */ + /*80 */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, + 0xfe, 0xb1, /*.abcdefghi...... */ + /*90 */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, + 0xc6, 0xa4, /*.jklmnopqr...... */ + /*a0 */ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, + 0xde, 0xae, /*..stuvwxyz...... */ + /*b0 */ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, + 0xb4, 0xd7, /*...........[\].. */ + /*c0 */ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, + 0xf3, 0xf5, /*.ABCDEFGHI...... */ + /*d0 */ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, + 0xfa, 0xff, /*.JKLMNOPQR...... */ + /*e0 */ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, + 0xd3, 0xd5, /*..STUVWXYZ...... */ + /*f0 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, + 0xda, 0x7e /*0123456789.{.}.~ */ }; #endif /*CHARSET_EBCDIC */ - /* 2.0.10: cast instead of floor() yields 35% performance improvement. Thanks to John Buckman. */ -#define floor_cast(exp) ((long) exp) - -extern int gdCosT[]; -extern int gdSinT[]; +#define floor_cast(exp) ((long)exp) +extern const int gdCosT[]; +extern const int gdSinT[]; /** * Group: Error Handling */ -void gd_stderr_error(int priority, const char *format, va_list args) -{ - switch (priority) { - case GD_ERROR: - fputs("GD Error: ", stderr); - break; - case GD_WARNING: - fputs("GD Warning: ", stderr); - break; - case GD_NOTICE: - fputs("GD Notice: ", stderr); - break; +static void gd_stderr_error(int priority, const char *format, va_list args) +{ + switch (priority) { + case GD_ERROR: + fputs("GD Error: ", stderr); + break; + case GD_WARNING: + fputs("GD Warning: ", stderr); + break; + case GD_NOTICE: + fputs("GD Notice: ", stderr); + break; #ifndef PHP_WIN32 - case GD_INFO: - fputs("GD Info: ", stderr); - break; - case GD_DEBUG: - fputs("GD Debug: ", stderr); - break; + case GD_INFO: + fputs("GD Info: ", stderr); + break; + case GD_DEBUG: + fputs("GD Debug: ", stderr); + break; #endif - } - vfprintf(stderr, format, args); - fflush(stderr); + } + vfprintf(stderr, format, args); + fflush(stderr); } static gdErrorMethod gd_error_method = gd_stderr_error; static void _gd_error_ex(int priority, const char *format, va_list args) { - if (gd_error_method) { - gd_error_method(priority, format, args); - } + if (gd_error_method) { + gd_error_method(priority, format, args); + } } void gd_error(const char *format, ...) { - va_list args; + va_list args; - va_start(args, format); - _gd_error_ex(GD_WARNING, format, args); - va_end(args); + va_start(args, format); + _gd_error_ex(GD_WARNING, format, args); + va_end(args); } void gd_error_ex(int priority, const char *format, ...) { - va_list args; + va_list args; - va_start(args, format); - _gd_error_ex(priority, format, args); - va_end(args); + va_start(args, format); + _gd_error_ex(priority, format, args); + va_end(args); } /* - Function: gdSetErrorMethod + Function: gdSetErrorMethod */ -void gdSetErrorMethod(gdErrorMethod error_method) -{ - gd_error_method = error_method; -} +BGD_DECLARE(void) gdSetErrorMethod(gdErrorMethod error_method) { gd_error_method = error_method; } /* - Function: gdClearErrorMethod + Function: gdClearErrorMethod */ -void gdClearErrorMethod(void) -{ - gd_error_method = gd_stderr_error; -} +BGD_DECLARE(void) gdClearErrorMethod(void) { gd_error_method = gd_stderr_error; } static void gdImageBrushApply(gdImagePtr im, int x, int y); static void gdImageTileApply(gdImagePtr im, int x, int y); static int gdAlphaOverlayColor(int src, int dst, int max); -int gdImageGetTrueColorPixel(gdImagePtr im, int x, int y); - -gdImagePtr gdImageCreate (int sx, int sy) -{ - int i; - gdImagePtr im; - - if (overflow2(sx, sy)) { - return NULL; - } - if (overflow2(sizeof(unsigned char *), sy)) { - return NULL; - } - if (overflow2(sizeof(unsigned char), sx)) { - return NULL; - } - - im = (gdImage *) gdCalloc(1, sizeof(gdImage)); - - /* Row-major ever since gd 1.3 */ - im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy); - im->polyInts = 0; - im->polyAllocated = 0; - im->brush = 0; - im->tile = 0; - im->style = 0; - for (i = 0; i < sy; i++) { - /* Row-major ever since gd 1.3 */ - im->pixels[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char)); - } - im->sx = sx; - im->sy = sy; - im->colorsTotal = 0; - im->transparent = (-1); - im->interlace = 0; - im->thick = 1; - im->AA = 0; - for (i = 0; i < gdMaxColors; i++) { - im->open[i] = 1; - im->red[i] = 0; - im->green[i] = 0; - im->blue[i] = 0; - } - im->trueColor = 0; - im->tpixels = 0; - im->cx1 = 0; - im->cy1 = 0; - im->cx2 = im->sx - 1; - im->cy2 = im->sy - 1; - im->res_x = GD_RESOLUTION; - im->res_y = GD_RESOLUTION; - im->interpolation = NULL; - im->interpolation_id = GD_BILINEAR_FIXED; - return im; -} - -gdImagePtr gdImageCreateTrueColor (int sx, int sy) -{ - int i; - gdImagePtr im; - - if (overflow2(sx, sy)) { - return NULL; - } - if (overflow2(sizeof(int *), sy)) { - return NULL; - } - if (overflow2(sizeof(int), sx)) { - return NULL; - } - - im = (gdImage *) gdMalloc(sizeof(gdImage)); - memset(im, 0, sizeof(gdImage)); - im->tpixels = (int **) gdMalloc(sizeof(int *) * sy); - im->polyInts = 0; - im->polyAllocated = 0; - im->brush = 0; - im->tile = 0; - im->style = 0; - for (i = 0; i < sy; i++) { - im->tpixels[i] = (int *) gdCalloc(sx, sizeof(int)); - } - im->sx = sx; - im->sy = sy; - im->transparent = (-1); - im->interlace = 0; - im->trueColor = 1; - /* 2.0.2: alpha blending is now on by default, and saving of alpha is - * off by default. This allows font antialiasing to work as expected - * on the first try in JPEGs -- quite important -- and also allows - * for smaller PNGs when saving of alpha channel is not really - * desired, which it usually isn't! - */ - im->saveAlphaFlag = 0; - im->alphaBlendingFlag = 1; - im->thick = 1; - im->AA = 0; - im->cx1 = 0; - im->cy1 = 0; - im->cx2 = im->sx - 1; - im->cy2 = im->sy - 1; - im->res_x = GD_RESOLUTION; - im->res_y = GD_RESOLUTION; - im->interpolation = NULL; - im->interpolation_id = GD_BILINEAR_FIXED; - return im; -} - -void gdImageDestroy (gdImagePtr im) -{ - int i; - if (im->pixels) { - for (i = 0; i < im->sy; i++) { - gdFree(im->pixels[i]); - } - gdFree(im->pixels); - } - if (im->tpixels) { - for (i = 0; i < im->sy; i++) { - gdFree(im->tpixels[i]); - } - gdFree(im->tpixels); - } - if (im->polyInts) { - gdFree(im->polyInts); - } - if (im->style) { - gdFree(im->style); - } - gdFree(im); -} - -int gdImageColorClosest (gdImagePtr im, int r, int g, int b) -{ - return gdImageColorClosestAlpha (im, r, g, b, gdAlphaOpaque); -} - -int gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a) -{ - int i; - long rd, gd, bd, ad; - int ct = (-1); - int first = 1; - long mindist = 0; - - if (im->trueColor) { - return gdTrueColorAlpha(r, g, b, a); - } - for (i = 0; i < im->colorsTotal; i++) { - long dist; - if (im->open[i]) { - continue; - } - rd = im->red[i] - r; - gd = im->green[i] - g; - bd = im->blue[i] - b; - /* gd 2.02: whoops, was - b (thanks to David Marwood) */ - ad = im->alpha[i] - a; - dist = rd * rd + gd * gd + bd * bd + ad * ad; - if (first || (dist < mindist)) { - mindist = dist; - ct = i; - first = 0; - } - } - return ct; -} - -/* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article - * on colour conversion to/from RBG and HWB colour systems. - * It has been modified to return the converted value as a * parameter. - */ -#define RETURN_HWB(h, w, b) {HWB->H = h; HWB->W = w; HWB->B = b; return HWB;} -#define RETURN_RGB(r, g, b) {RGB->R = r; RGB->G = g; RGB->B = b; return RGB;} -#define HWB_UNDEFINED -1 -#define SETUP_RGB(s, r, g, b) {s.R = r/255.0f; s.G = g/255.0f; s.B = b/255.0f;} - -#ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -#endif -#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c))) -#ifndef MAX -#define MAX(a,b) ((a)<(b)?(b):(a)) -#endif -#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c))) +BGD_DECLARE(int) gdImageGetTrueColorPixel(gdImagePtr im, int x, int y); +/** + * Group: Creation and Destruction + */ /* - * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure - * red always maps to 6 in this implementation. Therefore UNDEFINED can be - * defined as 0 in situations where only unsigned numbers are desired. - */ -typedef struct -{ - float R, G, B; -} -RGBType; -typedef struct -{ - float H, W, B; -} -HWBType; + Function: gdImageCreate -static HWBType * RGB_to_HWB (RGBType RGB, HWBType * HWB) -{ - /* - * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is - * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B. - */ + gdImageCreate is called to create palette-based images, with no + more than 256 colors. The image must eventually be destroyed using + gdImageDestroy(). - float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f; - int i; + Parameters: - w = MIN3 (R, G, B); - v = MAX3 (R, G, B); - b = 1 - v; - if (v == w) { - RETURN_HWB(HWB_UNDEFINED, w, b); - } - f = (R == w) ? G - B : ((G == w) ? B - R : R - G); - i = (R == w) ? 3 : ((G == w) ? 5 : 1); + sx - The image width. + sy - The image height. - RETURN_HWB(i - f / (v - w), w, b); -} + Returns: -static float HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2) -{ - RGBType RGB1, RGB2; - HWBType HWB1, HWB2; - float diff; + A pointer to the new image or NULL if an error occurred. - SETUP_RGB(RGB1, r1, g1, b1); - SETUP_RGB(RGB2, r2, g2, b2); + Example: + (start code) - RGB_to_HWB(RGB1, &HWB1); - RGB_to_HWB(RGB2, &HWB2); + gdImagePtr im; + im = gdImageCreate(64, 64); + // ... Use the image ... + gdImageDestroy(im); - /* - * I made this bit up; it seems to produce OK results, and it is certainly - * more visually correct than the current RGB metric. (PJW) - */ + (end code) - if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED)) { - diff = 0.0f; /* Undefined hues always match... */ - } else { - diff = fabsf(HWB1.H - HWB2.H); - if (diff > 3.0f) { - diff = 6.0f - diff; /* Remember, it's a colour circle */ - } - } + See Also: - diff = diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B); + - return diff; -} + */ +BGD_DECLARE(gdImagePtr) gdImageCreate(int sx, int sy) +{ + int i; + gdImagePtr im; + if (overflow2(sx, sy)) { + return NULL; + } -#if 0 -/* - * This is not actually used, but is here for completeness, in case someone wants to - * use the HWB stuff for anything else... - */ -static RGBType * HWB_to_RGB (HWBType HWB, RGBType * RGB) -{ - /* - * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1]. - * RGB are each returned on [0, 1]. - */ - - float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f; - int i; - - v = 1 - b; - if (h == HWB_UNDEFINED) { - RETURN_RGB(v, v, v); - } - i = floor(h); - f = h - i; - if (i & 1) { - f = 1 - f; /* if i is odd */ - } - n = w + f * (v - w); /* linear interpolation between w and v */ - switch (i) { - case 6: - case 0: - RETURN_RGB(v, n, w); - case 1: - RETURN_RGB(n, v, w); - case 2: - RETURN_RGB(w, v, n); - case 3: - RETURN_RGB(w, n, v); - case 4: - RETURN_RGB(n, w, v); - case 5: - RETURN_RGB(v, w, n); - } - - return RGB; -} -#endif + if (overflow2(sizeof(unsigned char *), sy)) { + return NULL; + } + if (overflow2(sizeof(unsigned char), sx)) { + return NULL; + } -int gdImageColorClosestHWB (gdImagePtr im, int r, int g, int b) -{ - int i; - /* long rd, gd, bd; */ - int ct = (-1); - int first = 1; - float mindist = 0; - if (im->trueColor) { - return gdTrueColor(r, g, b); - } - for (i = 0; i < im->colorsTotal; i++) { - float dist; - if (im->open[i]) { - continue; - } - dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b); - if (first || (dist < mindist)) { - mindist = dist; - ct = i; - first = 0; - } - } - return ct; -} - -int gdImageColorExact (gdImagePtr im, int r, int g, int b) -{ - return gdImageColorExactAlpha (im, r, g, b, gdAlphaOpaque); -} - -int gdImageColorExactAlpha (gdImagePtr im, int r, int g, int b, int a) -{ - int i; - if (im->trueColor) { - return gdTrueColorAlpha(r, g, b, a); - } - for (i = 0; i < im->colorsTotal; i++) { - if (im->open[i]) { - continue; - } - if ((im->red[i] == r) && (im->green[i] == g) && (im->blue[i] == b) && (im->alpha[i] == a)) { - return i; - } - } - return -1; -} - -int gdImageColorAllocate (gdImagePtr im, int r, int g, int b) -{ - return gdImageColorAllocateAlpha (im, r, g, b, gdAlphaOpaque); -} - -int gdImageColorAllocateAlpha (gdImagePtr im, int r, int g, int b, int a) -{ - int i; - int ct = (-1); - if (im->trueColor) { - return gdTrueColorAlpha(r, g, b, a); - } - for (i = 0; i < im->colorsTotal; i++) { - if (im->open[i]) { - ct = i; - break; - } - } - if (ct == (-1)) { - ct = im->colorsTotal; - if (ct == gdMaxColors) { - return -1; - } - im->colorsTotal++; - } - im->red[ct] = r; - im->green[ct] = g; - im->blue[ct] = b; - im->alpha[ct] = a; - im->open[ct] = 0; - - return ct; -} + im = (gdImage *)gdCalloc(1, sizeof(gdImage)); + if (!im) { + return NULL; + } -/* - * gdImageColorResolve is an alternative for the code fragment: - * - * if ((color=gdImageColorExact(im,R,G,B)) < 0) - * if ((color=gdImageColorAllocate(im,R,G,B)) < 0) - * color=gdImageColorClosest(im,R,G,B); - * - * in a single function. Its advantage is that it is guaranteed to - * return a color index in one search over the color table. - */ + /* Row-major ever since gd 1.3 */ + im->pixels = (unsigned char **)gdMalloc(sizeof(unsigned char *) * sy); + if (!im->pixels) { + gdFree(im); + return NULL; + } -int gdImageColorResolve (gdImagePtr im, int r, int g, int b) -{ - return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque); -} - -int gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a) -{ - int c; - int ct = -1; - int op = -1; - long rd, gd, bd, ad, dist; - long mindist = 4 * 255 * 255; /* init to max poss dist */ - if (im->trueColor) - { - return gdTrueColorAlpha (r, g, b, a); - } - - for (c = 0; c < im->colorsTotal; c++) - { - if (im->open[c]) - { - op = c; /* Save open slot */ - continue; /* Color not in use */ - } - if (c == im->transparent) - { - /* don't ever resolve to the color that has - * been designated as the transparent color */ - continue; - } - rd = (long) (im->red[c] - r); - gd = (long) (im->green[c] - g); - bd = (long) (im->blue[c] - b); - ad = (long) (im->alpha[c] - a); - dist = rd * rd + gd * gd + bd * bd + ad * ad; - if (dist < mindist) - { - if (dist == 0) - { - return c; /* Return exact match color */ - } - mindist = dist; - ct = c; - } - } - /* no exact match. We now know closest, but first try to allocate exact */ - if (op == -1) - { - op = im->colorsTotal; - if (op == gdMaxColors) - { /* No room for more colors */ - return ct; /* Return closest available color */ - } - im->colorsTotal++; - } - im->red[op] = r; - im->green[op] = g; - im->blue[op] = b; - im->alpha[op] = a; - im->open[op] = 0; - return op; /* Return newly allocated color */ -} - -void gdImageColorDeallocate (gdImagePtr im, int color) -{ - if (im->trueColor) { - return; - } - /* Mark it open. */ - im->open[color] = 1; -} - -void gdImageColorTransparent (gdImagePtr im, int color) -{ - if (color < 0) { - return; - } - if (!im->trueColor) { - if((color >= im->colorsTotal)) { - return; - } - /* Make the old transparent color opaque again */ - if (im->transparent != -1) { - im->alpha[im->transparent] = gdAlphaOpaque; - } - im->alpha[color] = gdAlphaTransparent; - } - im->transparent = color; -} - -void gdImagePaletteCopy (gdImagePtr to, gdImagePtr from) -{ - int i; - int x, y, p; - int xlate[256]; - if (to->trueColor || from->trueColor) { - return; - } - - for (i = 0; i < 256; i++) { - xlate[i] = -1; - } - - for (y = 0; y < to->sy; y++) { - for (x = 0; x < to->sx; x++) { - p = gdImageGetPixel(to, x, y); - if (xlate[p] == -1) { - /* This ought to use HWB, but we don't have an alpha-aware version of that yet. */ - xlate[p] = gdImageColorClosestAlpha (from, to->red[p], to->green[p], to->blue[p], to->alpha[p]); - } - gdImageSetPixel(to, x, y, xlate[p]); - } - } + im->polyInts = 0; + im->polyAllocated = 0; + im->brush = 0; + im->tile = 0; + im->style = 0; + for (i = 0; (i < sy); i++) { + /* Row-major ever since gd 1.3 */ + im->pixels[i] = (unsigned char *)gdCalloc(sx, sizeof(unsigned char)); + if (!im->pixels[i]) { + for (--i; i >= 0; i--) { + gdFree(im->pixels[i]); + } + gdFree(im->pixels); + gdFree(im); + return NULL; + } + } + im->sx = sx; + im->sy = sy; + im->colorsTotal = 0; + im->transparent = (-1); + im->interlace = 0; + im->thick = 1; + im->AA = 0; + for (i = 0; (i < gdMaxColors); i++) { + im->open[i] = 1; + im->red[i] = 0; + im->green[i] = 0; + im->blue[i] = 0; + }; + im->trueColor = 0; + im->tpixels = 0; + im->cx1 = 0; + im->cy1 = 0; + im->cx2 = im->sx - 1; + im->cy2 = im->sy - 1; + im->res_x = GD_RESOLUTION; + im->res_y = GD_RESOLUTION; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; + return im; +} - for (i = 0; i < from->colorsTotal; i++) { - to->red[i] = from->red[i]; - to->blue[i] = from->blue[i]; - to->green[i] = from->green[i]; - to->alpha[i] = from->alpha[i]; - to->open[i] = 0; - } +/* + Function: gdImageCreateTrueColor - for (i = from->colorsTotal; i < to->colorsTotal; i++) { - to->open[i] = 1; - } + is called to create truecolor images, + with an essentially unlimited number of colors. Invoke + with the x and y dimensions of the + desired image. returns a + to the new image, or NULL if unable to allocate the image. The + image must eventually be destroyed using (). - to->colorsTotal = from->colorsTotal; -} + Truecolor images are always filled with black at creation + time. There is no concept of a "background" color index. -/* 2.0.10: before the drawing routines, some code to clip points that are - * outside the drawing window. Nick Atty (nick@canalplan.org.uk) - * - * This is the Sutherland Hodgman Algorithm, as implemented by - * Duvanenko, Robbins and Gyurcsik - SH(DRG) for short. See Dr Dobb's - * Journal, January 1996, pp107-110 and 116-117 - * - * Given the end points of a line, and a bounding rectangle (which we - * know to be from (0,0) to (SX,SY)), adjust the endpoints to be on - * the edges of the rectangle if the line should be drawn at all, - * otherwise return a failure code - */ + Parameters: -/* this does "one-dimensional" clipping: note that the second time it - * is called, all the x parameters refer to height and the y to width - * - the comments ignore this (if you can understand it when it's - * looking at the X parameters, it should become clear what happens on - * the second call!) The code is simplified from that in the article, - * as we know that gd images always start at (0,0) - */ -/* 2.0.26, TBB: we now have to respect a clipping rectangle, it won't - necessarily start at 0. */ - -static int clip_1d(int *x0, int *y0, int *x1, int *y1, int mindim, int maxdim) { - double m; /* gradient of line */ - - if (*x0 < mindim) { /* start of line is left of window */ - if(*x1 < mindim) { /* as is the end, so the line never cuts the window */ - return 0; - } - m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */ - /* adjust x0 to be on the left boundary (ie to be zero), and y0 to match */ - *y0 -= (int)(m * (*x0 - mindim)); - *x0 = mindim; - /* now, perhaps, adjust the far end of the line as well */ - if (*x1 > maxdim) { - *y1 += (int)(m * (maxdim - *x1)); - *x1 = maxdim; - } - return 1; - } - if (*x0 > maxdim) { /* start of line is right of window - complement of above */ - if (*x1 > maxdim) { /* as is the end, so the line misses the window */ - return 0; - } - m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */ - *y0 += (int)(m * (maxdim - *x0)); /* adjust so point is on the right boundary */ - *x0 = maxdim; - /* now, perhaps, adjust the end of the line */ - if (*x1 < mindim) { - *y1 -= (int)(m * (*x1 - mindim)); - *x1 = mindim; - } - return 1; - } - /* the final case - the start of the line is inside the window */ - if (*x1 > maxdim) { /* other end is outside to the right */ - m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */ - *y1 += (int)(m * (maxdim - *x1)); - *x1 = maxdim; - return 1; - } - if (*x1 < mindim) { /* other end is outside to the left */ - m = (*y1 - *y0)/(double)(*x1 - *x0); /* calculate the slope of the line */ - *y1 -= (int)(m * (*x1 - mindim)); - *x1 = mindim; - return 1; - } - /* only get here if both points are inside the window */ - return 1; -} - -void gdImageSetPixel (gdImagePtr im, int x, int y, int color) -{ - int p; - switch (color) { - case gdStyled: - if (!im->style) { - /* Refuse to draw if no style is set. */ - return; - } else { - p = im->style[im->stylePos++]; - } - if (p != gdTransparent) { - gdImageSetPixel(im, x, y, p); - } - im->stylePos = im->stylePos % im->styleLength; - break; - case gdStyledBrushed: - if (!im->style) { - /* Refuse to draw if no style is set. */ - return; - } - p = im->style[im->stylePos++]; - if (p != gdTransparent && p != 0) { - gdImageSetPixel(im, x, y, gdBrushed); - } - im->stylePos = im->stylePos % im->styleLength; - break; - case gdBrushed: - gdImageBrushApply(im, x, y); - break; - case gdTiled: - gdImageTileApply(im, x, y); - break; - case gdAntiAliased: - /* This shouldn't happen (2.0.26) because we just call - gdImageAALine now, but do something sane. */ - gdImageSetPixel(im, x, y, im->AA_color); - break; - default: - if (gdImageBoundsSafe(im, x, y)) { - if (im->trueColor) { - switch (im->alphaBlendingFlag) { - default: - case gdEffectReplace: - im->tpixels[y][x] = color; - break; - case gdEffectAlphaBlend: - case gdEffectNormal: - im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color); - break; - case gdEffectOverlay : - im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color); - break; - case gdEffectMultiply : - im->tpixels[y][x] = gdLayerMultiply(im->tpixels[y][x], color); - break; - } - } else { - im->pixels[y][x] = color; - } - } - break; - } -} - -int gdImageGetTrueColorPixel (gdImagePtr im, int x, int y) -{ - int p = gdImageGetPixel(im, x, y); - - if (!im->trueColor) { - return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p], (im->transparent == p) ? gdAlphaTransparent : im->alpha[p]); - } else { - return p; - } -} - -static void gdImageBrushApply (gdImagePtr im, int x, int y) -{ - int lx, ly; - int hy, hx; - int x1, y1, x2, y2; - int srcx, srcy; - - if (!im->brush) { - return; - } - - hy = gdImageSY(im->brush) / 2; - y1 = y - hy; - y2 = y1 + gdImageSY(im->brush); - hx = gdImageSX(im->brush) / 2; - x1 = x - hx; - x2 = x1 + gdImageSX(im->brush); - srcy = 0; - - if (im->trueColor) { - if (im->brush->trueColor) { - for (ly = y1; ly < y2; ly++) { - srcx = 0; - for (lx = x1; (lx < x2); lx++) { - int p; - p = gdImageGetTrueColorPixel(im->brush, srcx, srcy); - /* 2.0.9, Thomas Winzig: apply simple full transparency */ - if (p != gdImageGetTransparent(im->brush)) { - gdImageSetPixel(im, lx, ly, p); - } - srcx++; - } - srcy++; - } - } else { - /* 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger for pointing out the issue) */ - for (ly = y1; ly < y2; ly++) { - srcx = 0; - for (lx = x1; lx < x2; lx++) { - int p, tc; - p = gdImageGetPixel(im->brush, srcx, srcy); - tc = gdImageGetTrueColorPixel(im->brush, srcx, srcy); - /* 2.0.9, Thomas Winzig: apply simple full transparency */ - if (p != gdImageGetTransparent(im->brush)) { - gdImageSetPixel(im, lx, ly, tc); - } - srcx++; - } - srcy++; - } - } - } else { - for (ly = y1; ly < y2; ly++) { - srcx = 0; - for (lx = x1; lx < x2; lx++) { - int p; - p = gdImageGetPixel(im->brush, srcx, srcy); - /* Allow for non-square brushes! */ - if (p != gdImageGetTransparent(im->brush)) { - /* Truecolor brush. Very slow on a palette destination. */ - if (im->brush->trueColor) { - gdImageSetPixel(im, lx, ly, gdImageColorResolveAlpha(im, gdTrueColorGetRed(p), - gdTrueColorGetGreen(p), - gdTrueColorGetBlue(p), - gdTrueColorGetAlpha(p))); - } else { - gdImageSetPixel(im, lx, ly, im->brushColorMap[p]); - } - } - srcx++; - } - srcy++; - } - } -} - -static void gdImageTileApply (gdImagePtr im, int x, int y) -{ - gdImagePtr tile = im->tile; - int srcx, srcy; - int p; - if (!tile) { - return; - } - srcx = x % gdImageSX(tile); - srcy = y % gdImageSY(tile); - if (im->trueColor) { - p = gdImageGetPixel(tile, srcx, srcy); - if (p != gdImageGetTransparent (tile)) { - if (!tile->trueColor) { - p = gdTrueColorAlpha(tile->red[p], tile->green[p], tile->blue[p], tile->alpha[p]); - } - gdImageSetPixel(im, x, y, p); - } - } else { - p = gdImageGetPixel(tile, srcx, srcy); - /* Allow for transparency */ - if (p != gdImageGetTransparent(tile)) { - if (tile->trueColor) { - /* Truecolor tile. Very slow on a palette destination. */ - gdImageSetPixel(im, x, y, gdImageColorResolveAlpha(im, - gdTrueColorGetRed(p), - gdTrueColorGetGreen(p), - gdTrueColorGetBlue(p), - gdTrueColorGetAlpha(p))); - } else { - gdImageSetPixel(im, x, y, im->tileColorMap[p]); - } - } - } -} - - -static int gdImageTileGet (gdImagePtr im, int x, int y) -{ - int srcx, srcy; - int tileColor,p; - if (!im->tile) { - return -1; - } - srcx = x % gdImageSX(im->tile); - srcy = y % gdImageSY(im->tile); - p = gdImageGetPixel(im->tile, srcx, srcy); - - if (p == im->tile->transparent) { - tileColor = im->transparent; - } else if (im->trueColor) { - if (im->tile->trueColor) { - tileColor = p; - } else { - tileColor = gdTrueColorAlpha( gdImageRed(im->tile,p), gdImageGreen(im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p)); - } - } else { - if (im->tile->trueColor) { - tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed (p), gdTrueColorGetGreen (p), gdTrueColorGetBlue (p), gdTrueColorGetAlpha (p)); - } else { - tileColor = p; - tileColor = gdImageColorResolveAlpha(im, gdImageRed (im->tile,p), gdImageGreen (im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p)); - } - } - return tileColor; -} - - -int gdImageGetPixel (gdImagePtr im, int x, int y) -{ - if (gdImageBoundsSafe(im, x, y)) { - if (im->trueColor) { - return im->tpixels[y][x]; - } else { - return im->pixels[y][x]; - } - } else { - return 0; - } -} - -void gdImageAABlend (gdImagePtr im) -{ - (void)im; -} - -static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color); - -gdImagePtr gdImageClone (gdImagePtr src) { - gdImagePtr dst; - register int i, x; - - if (src->trueColor) { - dst = gdImageCreateTrueColor(src->sx , src->sy); - } else { - dst = gdImageCreate(src->sx , src->sy); - } - - if (dst == NULL) { - return NULL; - } - - if (src->trueColor == 0) { - dst->colorsTotal = src->colorsTotal; - for (i = 0; i < gdMaxColors; i++) { - dst->red[i] = src->red[i]; - dst->green[i] = src->green[i]; - dst->blue[i] = src->blue[i]; - dst->alpha[i] = src->alpha[i]; - dst->open[i] = src->open[i]; - } - for (i = 0; i < src->sy; i++) { - for (x = 0; x < src->sx; x++) { - dst->pixels[i][x] = src->pixels[i][x]; - } - } - } else { - for (i = 0; i < src->sy; i++) { - for (x = 0; x < src->sx; x++) { - dst->tpixels[i][x] = src->tpixels[i][x]; - } - } - } + sx - The image width. + sy - The image height. - dst->interlace = src->interlace; + Returns: - dst->alphaBlendingFlag = src->alphaBlendingFlag; - dst->saveAlphaFlag = src->saveAlphaFlag; - dst->AA = src->AA; - dst->AA_color = src->AA_color; - dst->AA_dont_blend = src->AA_dont_blend; + A pointer to the new image or NULL if an error occurred. - dst->cx1 = src->cx1; - dst->cy1 = src->cy1; - dst->cx2 = src->cx2; - dst->cy2 = src->cy2; + Example: + (start code) - dst->res_x = src->res_x; - dst->res_y = src->res_y; + gdImagePtr im; + im = gdImageCreateTrueColor(64, 64); + // ... Use the image ... + gdImageDestroy(im); - dst->interpolation_id = src->interpolation_id; - dst->interpolation = src->interpolation; + (end code) - if (src->brush) { - dst->brush = gdImageClone(src->brush); - } + See Also: - if (src->tile) { - dst->tile = gdImageClone(src->tile); - } + - if (src->style) { - gdImageSetStyle(dst, src->style, src->styleLength); - dst->stylePos = src->stylePos; - } +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateTrueColor(int sx, int sy) +{ + int i; + gdImagePtr im; - for (i = 0; i < gdMaxColors; i++) { - dst->brushColorMap[i] = src->brushColorMap[i]; - dst->tileColorMap[i] = src->tileColorMap[i]; - } + if (overflow2(sx, sy)) { + return NULL; + } + if (overflow2(sizeof(int *), sy)) { + return NULL; + } + if (overflow2(sizeof(int), sx)) { + return NULL; + } - if (src->polyAllocated > 0 && overflow2(sizeof(int), src->polyAllocated) == 0) { - dst->polyInts = gdMalloc (sizeof (int) * src->polyAllocated); - dst->polyAllocated = src->polyAllocated; - for (i = 0; i < src->polyAllocated; i++) { - dst->polyInts[i] = src->polyInts[i]; - } - } + im = (gdImage *)gdMalloc(sizeof(gdImage)); + if (!im) { + return 0; + } + memset(im, 0, sizeof(gdImage)); - return dst; + im->tpixels = (int **)gdMalloc(sizeof(int *) * sy); + if (!im->tpixels) { + gdFree(im); + return 0; + } + im->polyInts = 0; + im->polyAllocated = 0; + im->brush = 0; + im->tile = 0; + im->style = 0; + for (i = 0; (i < sy); i++) { + im->tpixels[i] = (int *)gdCalloc(sx, sizeof(int)); + if (!im->tpixels[i]) { + /* 2.0.34 */ + i--; + while (i >= 0) { + gdFree(im->tpixels[i]); + i--; + } + gdFree(im->tpixels); + gdFree(im); + return 0; + } + } + im->sx = sx; + im->sy = sy; + im->transparent = (-1); + im->interlace = 0; + im->trueColor = 1; + /* 2.0.2: alpha blending is now on by default, and saving of alpha is + off by default. This allows font antialiasing to work as expected + on the first try in JPEGs -- quite important -- and also allows + for smaller PNGs when saving of alpha channel is not really + desired, which it usually isn't! */ + im->saveAlphaFlag = 0; + im->alphaBlendingFlag = 1; + im->thick = 1; + im->AA = 0; + im->cx1 = 0; + im->cy1 = 0; + im->cx2 = im->sx - 1; + im->cy2 = im->sy - 1; + im->res_x = GD_RESOLUTION; + im->res_y = GD_RESOLUTION; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; + return im; } -static void gdImageHLine(gdImagePtr im, int y, int x1, int x2, int col) -{ - if (im->thick > 1) { - int thickhalf = im->thick >> 1; - _gdImageFilledHRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col); - } else { - if (x2 < x1) { - int t = x2; - x2 = x1; - x1 = t; - } +/* + Function: gdImageDestroy - for (;x1 <= x2; x1++) { - gdImageSetPixel(im, x1, y, col); - } - } - return; -} + is used to free the memory associated with an + image. It is important to invoke before exiting + your program or assigning a new image to a variable. -static void gdImageVLine(gdImagePtr im, int x, int y1, int y2, int col) -{ - if (im->thick > 1) { - int thickhalf = im->thick >> 1; - gdImageFilledRectangle(im, x - thickhalf, y1, x + im->thick - thickhalf - 1, y2, col); - } else { - if (y2 < y1) { - int t = y1; - y1 = y2; - y2 = t; - } - - for (;y1 <= y2; y1++) { - gdImageSetPixel(im, x, y1, col); - } - } - return; -} - -/* Bresenham as presented in Foley & Van Dam */ -void gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) -{ - int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; - int wid; - int w, wstart; - int thick = im->thick; - - if (color == gdAntiAliased) { - /* - gdAntiAliased passed as color: use the much faster, much cheaper - and equally attractive gdImageAALine implementation. That - clips too, so don't clip twice. - */ - gdImageAALine(im, x1, y1, x2, y2, im->AA_color); - return; - } - /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no - points need to be drawn. 2.0.26, TBB: clip to edges of clipping - rectangle. We were getting away with this because gdImageSetPixel - is used for actual drawing, but this is still more efficient and opens - the way to skip per-pixel bounds checking in the future. */ - - if (clip_1d (&x1, &y1, &x2, &y2, im->cx1, im->cx2) == 0) - return; - if (clip_1d (&y1, &x1, &y2, &x2, im->cy1, im->cy2) == 0) - return; - - dx = abs (x2 - x1); - dy = abs (y2 - y1); - - if (dx == 0) { - gdImageVLine(im, x1, y1, y2, color); - return; - } else if (dy == 0) { - gdImageHLine(im, y1, x1, x2, color); - return; - } - - if (dy <= dx) { - /* More-or-less horizontal. use wid for vertical stroke */ - /* Doug Claar: watch out for NaN in atan2 (2.0.5) */ - if ((dx == 0) && (dy == 0)) { - wid = 1; - } else { - /* 2.0.12: Michael Schwartz: divide rather than multiply; -TBB: but watch out for /0! */ - double ac = cos (atan2 (dy, dx)); - if (ac != 0) { - wid = thick / ac; - } else { - wid = 1; - } - if (wid == 0) { - wid = 1; - } - } - d = 2 * dy - dx; - incr1 = 2 * dy; - incr2 = 2 * (dy - dx); - if (x1 > x2) { - x = x2; - y = y2; - ydirflag = (-1); - xend = x1; - } else { - x = x1; - y = y1; - ydirflag = 1; - xend = x2; - } - - /* Set up line thickness */ - wstart = y - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel(im, x, w, color); - } - - if (((y2 - y1) * ydirflag) > 0) { - while (x < xend) { - x++; - if (d < 0) { - d += incr1; - } else { - y++; - d += incr2; - } - wstart = y - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel (im, x, w, color); - } - } - } else { - while (x < xend) { - x++; - if (d < 0) { - d += incr1; - } else { - y--; - d += incr2; - } - wstart = y - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel (im, x, w, color); - } - } - } - } else { - /* More-or-less vertical. use wid for horizontal stroke */ - /* 2.0.12: Michael Schwartz: divide rather than multiply; - TBB: but watch out for /0! */ - double as = sin (atan2 (dy, dx)); - if (as != 0) { - wid = thick / as; - } else { - wid = 1; - } - if (wid == 0) { - wid = 1; - } - - d = 2 * dx - dy; - incr1 = 2 * dx; - incr2 = 2 * (dx - dy); - if (y1 > y2) { - y = y2; - x = x2; - yend = y1; - xdirflag = (-1); - } else { - y = y1; - x = x1; - yend = y2; - xdirflag = 1; - } - - /* Set up line thickness */ - wstart = x - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel (im, w, y, color); - } - - if (((x2 - x1) * xdirflag) > 0) { - while (y < yend) { - y++; - if (d < 0) { - d += incr1; - } else { - x++; - d += incr2; - } - wstart = x - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel (im, w, y, color); - } - } - } else { - while (y < yend) { - y++; - if (d < 0) { - d += incr1; - } else { - x--; - d += incr2; - } - wstart = x - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel (im, w, y, color); - } - } - } - } -} + Parameters: + im - Pointer to the gdImage to delete. -/* - * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org) - * */ -#define BLEND_COLOR(a, nc, c, cc) \ -nc = (cc) + (((((c) - (cc)) * (a)) + ((((c) - (cc)) * (a)) >> 8) + 0x80) >> 8); + Returns: -inline static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t) -{ - int dr,dg,db,p,r,g,b; - dr = gdTrueColorGetRed(color); - dg = gdTrueColorGetGreen(color); - db = gdTrueColorGetBlue(color); + Nothing. - p = gdImageGetPixel(im,x,y); - r = gdTrueColorGetRed(p); - g = gdTrueColorGetGreen(p); - b = gdTrueColorGetBlue(p); + Example: + (start code) - BLEND_COLOR(t, dr, r, dr); - BLEND_COLOR(t, dg, g, dg); - BLEND_COLOR(t, db, b, db); - im->tpixels[y][x]=gdTrueColorAlpha(dr, dg, db, gdAlphaOpaque); -} + gdImagePtr im; + im = gdImageCreate(10, 10); + // ... Use the image ... + // Now destroy it + gdImageDestroy(im); -/* - * Added on 2003/12 by Pierre-Alain Joye (pajoye@pearfr.org) - **/ -void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int col) -{ - /* keep them as 32bits */ - long x, y, inc, frac; - long dx, dy,tmp; - - if (!im->trueColor) { - /* TBB: don't crash when the image is of the wrong type */ - gdImageLine(im, x1, y1, x2, y2, col); - return; - } - - /* TBB: use the clipping rectangle */ - if (clip_1d (&x1, &y1, &x2, &y2, im->cx1, im->cx2) == 0) - return; - if (clip_1d (&y1, &x1, &y2, &x2, im->cy1, im->cy2) == 0) - return; - - dx = x2 - x1; - dy = y2 - y1; - - if (dx == 0 && dy == 0) { - return; - } - if (abs((int)dx) > abs((int)dy)) { - if (dx < 0) { - tmp = x1; - x1 = x2; - x2 = tmp; - tmp = y1; - y1 = y2; - y2 = tmp; - dx = x2 - x1; - dy = y2 - y1; - } - y = y1; - inc = (dy * 65536) / dx; - frac = 0; - for (x = x1; x <= x2; x++) { - gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF); - if (y + 1 < im->sy) { - gdImageSetAAPixelColor(im, x, y + 1, col, (~frac >> 8) & 0xFF); - } - frac += inc; - if (frac >= 65536) { - frac -= 65536; - y++; - } else if (frac < 0) { - frac += 65536; - y--; - } - } - } else { - if (dy < 0) { - tmp = x1; - x1 = x2; - x2 = tmp; - tmp = y1; - y1 = y2; - y2 = tmp; - dx = x2 - x1; - dy = y2 - y1; - } - x = x1; - inc = (dx * 65536) / dy; - frac = 0; - for (y = y1; y <= y2; y++) { - gdImageSetAAPixelColor(im, x, y, col, (frac >> 8) & 0xFF); - if (x + 1 < im->sx) { - gdImageSetAAPixelColor(im, x + 1, y, col, (~frac >> 8) & 0xFF); - } - frac += inc; - if (frac >= 65536) { - frac -= 65536; - x++; - } else if (frac < 0) { - frac += 65536; - x--; - } - } - } -} - -static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert); - -void gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) -{ - int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; - int dashStep = 0; - int on = 1; - int wid; - int vert; - int thick = im->thick; - - dx = abs(x2 - x1); - dy = abs(y2 - y1); - if (dy <= dx) { - /* More-or-less horizontal. use wid for vertical stroke */ - /* 2.0.12: Michael Schwartz: divide rather than multiply; - TBB: but watch out for /0! */ - double as = sin(atan2(dy, dx)); - if (as != 0) { - wid = thick / as; - } else { - wid = 1; - } - vert = 1; - - d = 2 * dy - dx; - incr1 = 2 * dy; - incr2 = 2 * (dy - dx); - if (x1 > x2) { - x = x2; - y = y2; - ydirflag = (-1); - xend = x1; - } else { - x = x1; - y = y1; - ydirflag = 1; - xend = x2; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - if (((y2 - y1) * ydirflag) > 0) { - while (x < xend) { - x++; - if (d < 0) { - d += incr1; - } else { - y++; - d += incr2; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - } - } else { - while (x < xend) { - x++; - if (d < 0) { - d += incr1; - } else { - y--; - d += incr2; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - } - } - } else { - /* 2.0.12: Michael Schwartz: divide rather than multiply; - TBB: but watch out for /0! */ - double as = sin (atan2 (dy, dx)); - if (as != 0) { - wid = thick / as; - } else { - wid = 1; - } - vert = 0; - - d = 2 * dx - dy; - incr1 = 2 * dx; - incr2 = 2 * (dx - dy); - if (y1 > y2) { - y = y2; - x = x2; - yend = y1; - xdirflag = (-1); - } else { - y = y1; - x = x1; - yend = y2; - xdirflag = 1; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - if (((x2 - x1) * xdirflag) > 0) { - while (y < yend) { - y++; - if (d < 0) { - d += incr1; - } else { - x++; - d += incr2; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - } - } else { - while (y < yend) { - y++; - if (d < 0) { - d += incr1; - } else { - x--; - d += incr2; - } - dashedSet(im, x, y, color, &on, &dashStep, wid, vert); - } - } - } -} - -static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert) -{ - int dashStep = *dashStepP; - int on = *onP; - int w, wstart; - - dashStep++; - if (dashStep == gdDashSize) { - dashStep = 0; - on = !on; - } - if (on) { - if (vert) { - wstart = y - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel(im, x, w, color); - } - } else { - wstart = x - wid / 2; - for (w = wstart; w < wstart + wid; w++) { - gdImageSetPixel(im, w, y, color); - } - } - } - *dashStepP = dashStep; - *onP = on; -} + (end code) + +*/ -void gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) +BGD_DECLARE(void) gdImageDestroy(gdImagePtr im) { - int cx, cy; - int px, py; - int fline; - const int xuppper = (x > INT_MAX - f->w) ? INT_MAX : x + f->w; - const int yuppper = (y > INT_MAX - f->h) ? INT_MAX : y + f->h; - cx = 0; - cy = 0; -#ifdef CHARSET_EBCDIC - c = ASC (c); -#endif /*CHARSET_EBCDIC */ - if ((c < f->offset) || (c >= (f->offset + f->nchars))) { - return; - } - fline = (c - f->offset) * f->h * f->w; - for (py = y; py < yuppper; py++) { - for (px = x; px < xuppper; px++) { - if (f->data[fline + cy * f->w + cx]) { - gdImageSetPixel(im, px, py, color); - } - cx++; - } - cx = 0; - cy++; - } -} - -void gdImageCharUp (gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) -{ - int cx, cy; - int px, py; - int fline; - const int xuppper = (x > INT_MAX - f->h) ? INT_MAX : x + f->h; - const int ylower = (y < INT_MIN + f->w) ? INT_MIN : y - f->w; - cx = 0; - cy = 0; -#ifdef CHARSET_EBCDIC - c = ASC (c); -#endif /*CHARSET_EBCDIC */ - if ((c < f->offset) || (c >= (f->offset + f->nchars))) { - return; - } - fline = (c - f->offset) * f->h * f->w; - for (py = y; py > ylower; py--) { - for (px = x; px < xuppper; px++) { - if (f->data[fline + cy * f->w + cx]) { - gdImageSetPixel(im, px, py, color); - } - cy++; - } - cy = 0; - cx++; - } + int i; + if (im->pixels) { + for (i = 0; (i < im->sy); i++) { + gdFree(im->pixels[i]); + } + gdFree(im->pixels); + } + if (im->tpixels) { + for (i = 0; (i < im->sy); i++) { + gdFree(im->tpixels[i]); + } + gdFree(im->tpixels); + } + if (im->polyInts) { + gdFree(im->polyInts); + } + if (im->style) { + gdFree(im->style); + } + gdFree(im); } -void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color) +/** + * Group: Color + */ + +/** + * Function: gdImageColorClosest + * + * Gets the closest color of the image + * + * This is a simplified variant of where the alpha + * channel is always opaque. + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * + * Returns: + * The closest color already available in the palette for palette images; + * the color value of the given components for truecolor images. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImageColorClosest(gdImagePtr im, int r, int g, int b) { - int i; - int l; - l = strlen ((char *) s); - for (i = 0; (i < l); i++) { - gdImageChar(im, f, x, y, s[i], color); - x += f->w; - } + return gdImageColorClosestAlpha(im, r, g, b, gdAlphaOpaque); } -void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color) +/** + * Function: gdImageColorClosestAlpha + * + * Gets the closest color of the image + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * a - The value of the alpha component. + * + * Returns: + * The closest color already available in the palette for palette images; + * the color value of the given components for truecolor images. + * + * See also: + * - + */ +BGD_DECLARE(int) +gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a) { - int i; - int l; - l = strlen ((char *) s); - for (i = 0; (i < l); i++) { - gdImageCharUp(im, f, x, y, s[i], color); - y -= f->w; - } + int i; + long rd, gd, bd, ad; + int ct = (-1); + int first = 1; + long mindist = 0; + + if (im->trueColor) { + return gdTrueColorAlpha(r, g, b, a); + } + for (i = 0; (i < (im->colorsTotal)); i++) { + long dist; + if (im->open[i]) { + continue; + } + rd = (im->red[i] - r); + gd = (im->green[i] - g); + bd = (im->blue[i] - b); + /* gd 2.02: whoops, was - b (thanks to David Marwood) */ + /* gd 2.16: was blue rather than alpha! Geez! Thanks to + Artur Jakub Jerzak */ + ad = (im->alpha[i] - a); + dist = rd * rd + gd * gd + bd * bd + ad * ad; + if (first || (dist < mindist)) { + mindist = dist; + ct = i; + first = 0; + } + } + return ct; } -static int strlen16 (unsigned short *s); +/* This code is taken from + * http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article on colour + * conversion to/from RBG and HWB colour systems. It has been modified to return + * the converted value as a * parameter. + */ +#define RETURN_HWB(h, w, b) \ + { \ + HWB->H = h; \ + HWB->W = w; \ + HWB->B = b; \ + return HWB; \ + } +#define RETURN_RGB(r, g, b) \ + { \ + RGB->R = r; \ + RGB->G = g; \ + RGB->B = b; \ + return RGB; \ + } +#define HWB_UNDEFINED -1 +#define SETUP_RGB(s, r, g, b) \ + { \ + s.R = r / 255.0; \ + s.G = g / 255.0; \ + s.B = b / 255.0; \ + } -void gdImageString16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color) +/* + * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. + * Pure red always maps to 6 in this implementation. Therefore UNDEFINED can be + * defined as 0 in situations where only unsigned numbers are desired. + */ +typedef struct { + float R, G, B; +} RGBType; +typedef struct { + float H, W, B; +} HWBType; + +static HWBType *RGB_to_HWB(RGBType RGB, HWBType *HWB) { - int i; - int l; - l = strlen16(s); - for (i = 0; (i < l); i++) { - gdImageChar(im, f, x, y, s[i], color); - x += f->w; - } + + /* + * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is + * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B. + */ + + float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f; + int i; + + w = MIN3(R, G, B); + v = MAX3(R, G, B); + b = 1 - v; + if (v == w) + RETURN_HWB(HWB_UNDEFINED, w, b); + f = (R == w) ? G - B : ((G == w) ? B - R : R - G); + i = (R == w) ? 3 : ((G == w) ? 5 : 1); + + RETURN_HWB(i - f / (v - w), w, b); } -void gdImageStringUp16 (gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color) +static float HWB_Diff(int r1, int g1, int b1, int r2, int g2, int b2) { - int i; - int l; - l = strlen16(s); - for (i = 0; i < l; i++) { - gdImageCharUp(im, f, x, y, s[i], color); - y -= f->w; - } + RGBType RGB1, RGB2; + HWBType HWB1, HWB2; + float diff; + + SETUP_RGB(RGB1, r1, g1, b1); + SETUP_RGB(RGB2, r2, g2, b2); + + RGB_to_HWB(RGB1, &HWB1); + RGB_to_HWB(RGB2, &HWB2); + + /* + * I made this bit up; it seems to produce OK results, and it is certainly + * more visually correct than the current RGB metric. (PJW) + */ + + if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED)) { + diff = 0; /* Undefined hues always match... */ + } else { + diff = fabs(HWB1.H - HWB2.H); + if (diff > 3) { + diff = 6 - diff; /* Remember, it's a colour circle */ + } + } + + diff = + diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B); + + return diff; } -static int strlen16 (unsigned short *s) +/* + Function: gdImageColorClosestHWB +*/ +BGD_DECLARE(int) gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b) { - int len = 0; - while (*s) { - s++; - len++; - } - return len; + int i; + /* long rd, gd, bd; */ + int ct = (-1); + int first = 1; + float mindist = 0; + if (im->trueColor) { + return gdTrueColor(r, g, b); + } + for (i = 0; (i < (im->colorsTotal)); i++) { + float dist; + if (im->open[i]) { + continue; + } + dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b); + if (first || (dist < mindist)) { + mindist = dist; + ct = i; + first = 0; + } + } + return ct; } -#ifndef HAVE_LSQRT -/* If you don't have a nice square root function for longs, you can use - ** this hack +/** + * Function: gdImageColorExact + * + * Gets the exact color of the image + * + * This is a simplified variant of where the alpha + * channel is always opaque. + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * + * Returns: + * The exact color already available in the palette for palette images; if + * there is no exact color, -1 is returned. + * For truecolor images the color value of the given components is returned. + * + * See also: + * - */ -long lsqrt (long n) +BGD_DECLARE(int) gdImageColorExact(gdImagePtr im, int r, int g, int b) { - long result = (long) sqrt ((double) n); - return result; + return gdImageColorExactAlpha(im, r, g, b, gdAlphaOpaque); } -#endif -/* s and e are integers modulo 360 (degrees), with 0 degrees - being the rightmost extreme and degrees changing clockwise. - cx and cy are the center in pixels; w and h are the horizontal - and vertical diameter in pixels. */ +/** + * Function: gdImageColorExactAlpha + * + * Gets the exact color of the image + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * a - The value of the alpha component. + * + * Returns: + * The exact color already available in the palette for palette images; if + * there is no exact color, -1 is returned. + * For truecolor images the color value of the given components is returned. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) +gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a) +{ + int i; + if (im->trueColor) { + return gdTrueColorAlpha(r, g, b, a); + } + for (i = 0; (i < (im->colorsTotal)); i++) { + if (im->open[i]) { + continue; + } + if ((im->red[i] == r) && (im->green[i] == g) && (im->blue[i] == b) && (im->alpha[i] == a)) { + return i; + } + } + return -1; +} -void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color) -{ - gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill); -} - -void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style) -{ - gdPoint pts[363]; - int i, pti; - int lx = 0, ly = 0; - int fx = 0, fy = 0; - int startx = -1, starty = -1, endx = -1, endy = -1; - - if ((s % 360) == (e % 360)) { - s = 0; e = 360; - } else { - if (s > 360) { - s = s % 360; - } - - if (e > 360) { - e = e % 360; - } - - while (s < 0) { - s += 360; - } - - while (e < s) { - e += 360; - } - if (s == e) { - s = 0; e = 360; - } - } - - for (i = s, pti = 1; i <= e; i++, pti++) { - int x, y; - x = endx = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx; - y = endy = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy; - if (i != s) { - if (!(style & gdChord)) { - if (style & gdNoFill) { - gdImageLine(im, lx, ly, x, y, color); - } else { - if (y == ly) { - pti--; /* don't add this point */ - if (((i > 270 || i < 90) && x > lx) || ((i > 90 && i < 270) && x < lx)) { - /* replace the old x coord, if increasing on the - right side or decreasing on the left side */ - pts[pti].x = x; - } - } else { - pts[pti].x = x; - pts[pti].y = y; - } - } - } - } else { - fx = x; - fy = y; - if (!(style & (gdChord | gdNoFill))) { - pts[0].x = cx; - pts[0].y = cy; - pts[pti].x = startx = x; - pts[pti].y = starty = y; - } - } - lx = x; - ly = y; - } - if (style & gdChord) { - if (style & gdNoFill) { - if (style & gdEdged) { - gdImageLine(im, cx, cy, lx, ly, color); - gdImageLine(im, cx, cy, fx, fy, color); - } - gdImageLine(im, fx, fy, lx, ly, color); - } else { - pts[0].x = fx; - pts[0].y = fy; - pts[1].x = lx; - pts[1].y = ly; - pts[2].x = cx; - pts[2].y = cy; - gdImageFilledPolygon(im, pts, 3, color); - } - } else { - if (style & gdNoFill) { - if (style & gdEdged) { - gdImageLine(im, cx, cy, lx, ly, color); - gdImageLine(im, cx, cy, fx, fy, color); - } - } else { - if (e - s < 360) { - if (pts[1].x != startx && pts[1].y == starty) { - /* start point has been removed due to y-coord fix => insert it */ - for (i = pti; i > 1; i--) { - pts[i].x = pts[i-1].x; - pts[i].y = pts[i-1].y; - } - pts[1].x = startx; - pts[1].y = starty; - pti++; - } - if (pts[pti-1].x != endx && pts[pti-1].y == endy) { - /* end point has been removed due to y-coord fix => insert it */ - pts[pti].x = endx; - pts[pti].y = endy; - pti++; - } - } - pts[pti].x = cx; - pts[pti].y = cy; - gdImageFilledPolygon(im, pts, pti+1, color); - } - } +/** + * Function: gdImageColorAllocate + * + * Allocates a color + * + * This is a simplified variant of where the alpha + * channel is always opaque. + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * + * Returns: + * The color value. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImageColorAllocate(gdImagePtr im, int r, int g, int b) +{ + return gdImageColorAllocateAlpha(im, r, g, b, gdAlphaOpaque); } /** - * Integer Ellipse functions (gdImageEllipse and gdImageFilledEllipse) - * Function added by Pierre-Alain Joye 02/08/2003 (paj@pearfr.org) - * See the ellipse function simplification for the equation - * as well as the midpoint algorithm. + * Function: gdImageColorAllocateAlpha + * + * Allocates a color + * + * This is typically used for palette images, but can be used for truecolor + * images as well. + * + * Parameters: + * im - The image. + * r - The value of the red component. + * g - The value of the green component. + * b - The value of the blue component. + * + * Returns: + * The color value. + * + * See also: + * - */ +BGD_DECLARE(int) +gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a) +{ + int i; + int ct = (-1); + if (im->trueColor) { + return gdTrueColorAlpha(r, g, b, a); + } + for (i = 0; (i < (im->colorsTotal)); i++) { + if (im->open[i]) { + ct = i; + break; + } + } + if (ct == (-1)) { + ct = im->colorsTotal; + if (ct == gdMaxColors) { + return -1; + } + im->colorsTotal++; + } + im->red[ct] = r; + im->green[ct] = g; + im->blue[ct] = b; + im->alpha[ct] = a; + im->open[ct] = 0; -void gdImageEllipse(gdImagePtr im, int mx, int my, int w, int h, int c) -{ - int x=0,mx1=0,mx2=0,my1=0,my2=0; - int64_t aq,bq,dx,dy,r,rx,ry,a,b; - - a=w>>1; - b=h>>1; - if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { - return; - } - gdImageSetPixel(im,mx+a, my, c); - gdImageSetPixel(im,mx-a, my, c); - mx1 = mx-a;my1 = my; - mx2 = mx+a;my2 = my; - - aq = a * a; - bq = b * b; - dx = aq << 1; - dy = bq << 1; - r = a * bq; - rx = r << 1; - ry = 0; - x = a; - while (x > 0){ - if (r > 0) { - my1++;my2--; - ry +=dx; - r -=ry; - } - if (r <= 0){ - x--; - mx1++;mx2--; - rx -=dy; - r +=rx; - } - gdImageSetPixel(im,mx1, my1, c); - gdImageSetPixel(im,mx1, my2, c); - gdImageSetPixel(im,mx2, my1, c); - gdImageSetPixel(im,mx2, my2, c); - } -} - -void gdImageFilledEllipse (gdImagePtr im, int mx, int my, int w, int h, int c) -{ - int x=0,mx1=0,mx2=0,my1=0,my2=0; - int64_t aq,bq,dx,dy,r,rx,ry,a,b; - int i; - int old_y2; - - a=w>>1; - b=h>>1; - if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { - return; - } - for (x = mx-a; x <= mx+a; x++) { - gdImageSetPixel(im, x, my, c); - } - - mx1 = mx-a;my1 = my; - mx2 = mx+a;my2 = my; - - aq = a * a; - bq = b * b; - dx = aq << 1; - dy = bq << 1; - r = a * bq; - rx = r << 1; - ry = 0; - x = a; - old_y2=-2; - while (x > 0){ - if (r > 0) { - my1++;my2--; - ry +=dx; - r -=ry; - } - if (r <= 0){ - x--; - mx1++;mx2--; - rx -=dy; - r +=rx; - } - if(old_y2!=my2){ - for(i=mx1;i<=mx2;i++){ - gdImageSetPixel(im,i,my1,c); - gdImageSetPixel(im,i,my2,c); - } - } - old_y2 = my2; - } -} - -void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color) -{ - int lastBorder; - /* Seek left */ - int leftLimit = -1, rightLimit; - int i, restoreAlphaBlending = 0; - - if (border < 0 || color < 0) { - /* Refuse to fill to a non-solid border */ - return; - } - - if (!im->trueColor) { - if ((color > (im->colorsTotal - 1)) || (border > (im->colorsTotal - 1)) || (color < 0)) { - return; - } - } - - restoreAlphaBlending = im->alphaBlendingFlag; - im->alphaBlendingFlag = 0; - - if (x >= im->sx) { - x = im->sx - 1; - } else if (x < 0) { - x = 0; - } - if (y >= im->sy) { - y = im->sy - 1; - } else if (y < 0) { - y = 0; - } - - for (i = x; i >= 0; i--) { - if (gdImageGetPixel(im, i, y) == border) { - break; - } - gdImageSetPixel(im, i, y, color); - leftLimit = i; - } - if (leftLimit == -1) { - im->alphaBlendingFlag = restoreAlphaBlending; - return; - } - /* Seek right */ - rightLimit = x; - for (i = (x + 1); i < im->sx; i++) { - if (gdImageGetPixel(im, i, y) == border) { - break; - } - gdImageSetPixel(im, i, y, color); - rightLimit = i; - } - /* Look at lines above and below and start paints */ - /* Above */ - if (y > 0) { - lastBorder = 1; - for (i = leftLimit; i <= rightLimit; i++) { - int c = gdImageGetPixel(im, i, y - 1); - if (lastBorder) { - if ((c != border) && (c != color)) { - gdImageFillToBorder(im, i, y - 1, border, color); - lastBorder = 0; - } - } else if ((c == border) || (c == color)) { - lastBorder = 1; - } - } - } - - /* Below */ - if (y < ((im->sy) - 1)) { - lastBorder = 1; - for (i = leftLimit; i <= rightLimit; i++) { - int c = gdImageGetPixel(im, i, y + 1); - - if (lastBorder) { - if ((c != border) && (c != color)) { - gdImageFillToBorder(im, i, y + 1, border, color); - lastBorder = 0; - } - } else if ((c == border) || (c == color)) { - lastBorder = 1; - } - } - } - im->alphaBlendingFlag = restoreAlphaBlending; + return ct; } /* - * set the pixel at (x,y) and its 4-connected neighbors - * with the same pixel value to the new pixel value nc (new color). - * A 4-connected neighbor: pixel above, below, left, or right of a pixel. - * ideas from comp.graphics discussions. - * For tiled fill, the use of a flag buffer is mandatory. As the tile image can - * contain the same color as the color to fill. To do not bloat normal filling - * code I added a 2nd private function. + Function: gdImageColorResolve + + gdImageColorResolve is an alternative for the code fragment + (start code) + if ((color=gdImageColorExact(im,R,G,B)) < 0) + if ((color=gdImageColorAllocate(im,R,G,B)) < 0) + color=gdImageColorClosest(im,R,G,B); + (end code) + in a single function. Its advantage is that it is guaranteed to + return a color index in one search over the color table. */ -/* horizontal segment of scan line y */ -struct seg {int y, xl, xr, dy;}; - -/* max depth of stack */ -#define FILL_MAX ((int)(im->sy*im->sx)/4) -#define FILL_PUSH(Y, XL, XR, DY) \ - if (sp=0 && Y+(DY)y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} - -#define FILL_POP(Y, XL, XR, DY) \ - {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} - -static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc); - -void gdImageFill(gdImagePtr im, int x, int y, int nc) -{ - int l, x1, x2, dy; - int oc; /* old pixel value */ - int wx2,wy2; - - int alphablending_bak; - - /* stack of filled segments */ - /* struct seg stack[FILL_MAX],*sp = stack; */ - struct seg *stack = NULL; - struct seg *sp; - - if (!im->trueColor && nc > (im->colorsTotal -1)) { - return; - } - - alphablending_bak = im->alphaBlendingFlag; - im->alphaBlendingFlag = 0; - - if (nc==gdTiled){ - _gdImageFillTiled(im,x,y,nc); - im->alphaBlendingFlag = alphablending_bak; - return; - } - - wx2=im->sx;wy2=im->sy; - oc = gdImageGetPixel(im, x, y); - if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) { - im->alphaBlendingFlag = alphablending_bak; - return; - } - - /* Do not use the 4 neighbors implementation with - * small images - */ - if (im->sx < 4) { - int ix = x, iy = y, c; - do { - do { - c = gdImageGetPixel(im, ix, iy); - if (c != oc) { - goto done; - } - gdImageSetPixel(im, ix, iy, nc); - } while(ix++ < (im->sx -1)); - ix = x; - } while(iy++ < (im->sy -1)); - goto done; - } - - if(overflow2(im->sy, im->sx)) { - return; - } - - if(overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { - return; - } - - stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1); - sp = stack; - - /* required! */ - FILL_PUSH(y,x,x,1); - /* seed segment (popped 1st) */ - FILL_PUSH(y+1, x, x, -1); - while (sp>stack) { - FILL_POP(y, x1, x2, dy); - - for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) { - gdImageSetPixel(im,x, y, nc); - } - if (x>=x1) { - goto skip; - } - l = x+1; - - /* leak on left? */ - if (lx2+1) { - FILL_PUSH(y, x2+1, x-1, -dy); - } -skip: - for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++); - - l = x; - } while (x<=x2); - } - - efree(stack); - -done: - im->alphaBlendingFlag = alphablending_bak; +BGD_DECLARE(int) gdImageColorResolve(gdImagePtr im, int r, int g, int b) +{ + return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque); } -static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc) +/* + Function: gdImageColorResolveAlpha +*/ +BGD_DECLARE(int) gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a) { - int l, x1, x2, dy; - int oc; /* old pixel value */ - int wx2,wy2; - /* stack of filled segments */ - struct seg *stack; - struct seg *sp; - char *pts; - - if (!im->tile) { - return; - } - - wx2=im->sx;wy2=im->sy; - - nc = gdImageTileGet(im,x,y); - - if(overflow2(im->sy, im->sx)) { - return; - } - - if(overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { - return; - } - - pts = (char *) ecalloc(im->sy * im->sx, sizeof(char)); - - stack = (struct seg *)safe_emalloc(sizeof(struct seg), ((int)(im->sy*im->sx)/4), 1); - sp = stack; - - oc = gdImageGetPixel(im, x, y); - - /* required! */ - FILL_PUSH(y,x,x,1); - /* seed segment (popped 1st) */ - FILL_PUSH(y+1, x, x, -1); - while (sp>stack) { - FILL_POP(y, x1, x2, dy); - for (x=x1; x>=0 && (!pts[y + x*wy2] && gdImageGetPixel(im,x,y)==oc); x--) { - nc = gdImageTileGet(im,x,y); - pts[y + x*wy2] = 1; - gdImageSetPixel(im,x, y, nc); - } - if (x>=x1) { - goto skip; - } - l = x+1; - - /* leak on left? */ - if (lx2+1) { - FILL_PUSH(y, x2+1, x-1, -dy); - } -skip: - for(x++; x<=x2 && (pts[y + x*wy2] || gdImageGetPixel(im,x, y)!=oc); x++); - l = x; - } while (x<=x2); - } + int c; + int ct = -1; + int op = -1; + long rd, gd, bd, ad, dist; + long mindist = 4 * 255 * 255; /* init to max poss dist */ + if (im->trueColor) { + return gdTrueColorAlpha(r, g, b, a); + } - efree(pts); - efree(stack); + for (c = 0; c < im->colorsTotal; c++) { + if (im->open[c]) { + op = c; /* Save open slot */ + continue; /* Color not in use */ + } + if (c == im->transparent) { + /* don't ever resolve to the color that has + * been designated as the transparent color */ + continue; + } + rd = (long)(im->red[c] - r); + gd = (long)(im->green[c] - g); + bd = (long)(im->blue[c] - b); + ad = (long)(im->alpha[c] - a); + dist = rd * rd + gd * gd + bd * bd + ad * ad; + if (dist < mindist) { + if (dist == 0) { + return c; /* Return exact match color */ + } + mindist = dist; + ct = c; + } + } + /* no exact match. We now know closest, but first try to allocate exact */ + if (op == -1) { + op = im->colorsTotal; + if (op == gdMaxColors) { + /* No room for more colors */ + return ct; /* Return closest available color */ + } + im->colorsTotal++; + } + im->red[op] = r; + im->green[op] = g; + im->blue[op] = b; + im->alpha[op] = a; + im->open[op] = 0; + return op; /* Return newly allocated color */ } - - -void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) +/** + * Function: gdImageColorDeallocate + * + * Removes a palette entry + * + * This is a no-op for truecolor images. + * The function does not alter the image data nor the transparent color or any + * other places where this color index could have been referenced. + * The index is marked as open and will be used too for any subsequent + * or calls. Other lower + * index may be open as well, the fist open index found will be used. + * + * Parameters: + * im - The image. + * color - The palette index. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) gdImageColorDeallocate(gdImagePtr im, int color) { - int thick = im->thick; - int t; - - if (x1 == x2 && y1 == y2 && thick == 1) { - gdImageSetPixel(im, x1, y1, color); - return; - } + if (im->trueColor || (color >= gdMaxColors) || (color < 0)) { + return; + } + /* Mark it open. */ + im->open[color] = 1; +} - if (y2 < y1) { - t=y1; - y1 = y2; - y2 = t; - } +/** + * Function: gdImageColorTransparent + * + * Sets the transparent color of the image + * + * Parameter: + * im - The image. + * color - The color. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageColorTransparent(gdImagePtr im, int color) +{ + // Reset ::transparent + if (color == -1) { + im->transparent = -1; + return; + } + if (color < -1) { + return; + } - if (x2 < x1) { - t = x1; - x1 = x2; - x2 = t; - } + if (im->trueColor) { + im->transparent = color; + return; + } - if (thick > 1) { - int cx, cy, x1ul, y1ul, x2lr, y2lr; - int half = thick >> 1; + // Palette Image + if (color >= gdMaxColors) { + return; + } + if (im->transparent != -1) { + im->alpha[im->transparent] = gdAlphaOpaque; + } + im->alpha[color] = gdAlphaTransparent; + im->transparent = color; +} - x1ul = x1 - half; - y1ul = y1 - half; +/* + Function: gdImagePaletteCopy +*/ +BGD_DECLARE(void) gdImagePaletteCopy(gdImagePtr to, gdImagePtr from) +{ + int i; + int x, y, p; + int xlate[256]; + if (to->trueColor || from->trueColor) { + return; + } + for (i = 0; i < 256; i++) { + xlate[i] = -1; + }; + + for (y = 0; y < (to->sy); y++) { + for (x = 0; x < (to->sx); x++) { + /* Optimization: no gdImageGetPixel */ + p = to->pixels[y][x]; + if (xlate[p] == -1) { + /* This ought to use HWB, but we don't have an alpha-aware + version of that yet. */ + xlate[p] = gdImageColorClosestAlpha(from, to->red[p], to->green[p], to->blue[p], + to->alpha[p]); + /*printf("Mapping %d (%d, %d, %d, %d) to %d (%d, %d, %d, %d)\n", + */ + /* p, to->red[p], to->green[p], to->blue[p], to->alpha[p], + */ + /* xlate[p], from->red[xlate[p]], from->green[xlate[p]], + * from->blue[xlate[p]], from->alpha[xlate[p]]); */ + }; + /* Optimization: no gdImageSetPixel */ + to->pixels[y][x] = xlate[p]; + }; + }; + + for (i = 0; (i < (from->colorsTotal)); i++) { + to->red[i] = from->red[i]; + to->blue[i] = from->blue[i]; + to->green[i] = from->green[i]; + to->alpha[i] = from->alpha[i]; + to->open[i] = 0; + }; + + for (i = from->colorsTotal; (i < to->colorsTotal); i++) { + to->open[i] = 1; + }; + + to->colorsTotal = from->colorsTotal; +} - x2lr = x2 + half; - y2lr = y2 + half; +/* + Function: gdImageColorReplace +*/ +BGD_DECLARE(int) gdImageColorReplace(gdImagePtr im, int src, int dst) +{ + register int x, y; + int n = 0; - cy = y1ul + thick; - while (cy-- > y1ul) { - cx = x1ul - 1; - while (cx++ < x2lr) { - gdImageSetPixel(im, cx, cy, color); - } - } + if (src == dst) { + return 0; + } - cy = y2lr - thick; - while (cy++ < y2lr) { - cx = x1ul - 1; - while (cx++ < x2lr) { - gdImageSetPixel(im, cx, cy, color); - } - } +#define REPLACING_LOOP(pixel) \ + do { \ + for (y = im->cy1; y <= im->cy2; y++) { \ + for (x = im->cx1; x <= im->cx2; x++) { \ + if (pixel(im, x, y) == src) { \ + gdImageSetPixel(im, x, y, dst); \ + n++; \ + } \ + } \ + } \ + } while (0) + + if (im->trueColor) { + REPLACING_LOOP(gdImageTrueColorPixel); + } else { + REPLACING_LOOP(gdImagePalettePixel); + } - cy = y1ul + thick - 1; - while (cy++ < y2lr -thick) { - cx = x1ul - 1; - while (cx++ < x1ul + thick) { - gdImageSetPixel(im, cx, cy, color); - } - } +#undef REPLACING_LOOP - cy = y1ul + thick - 1; - while (cy++ < y2lr -thick) { - cx = x2lr - thick - 1; - while (cx++ < x2lr) { - gdImageSetPixel(im, cx, cy, color); - } - } - - return; - } else { - if (x1 == x2 || y1 == y2) { - gdImageLine(im, x1, y1, x2, y2, color); - } else { - gdImageLine(im, x1, y1, x2, y1, color); - gdImageLine(im, x1, y2, x2, y2, color); - gdImageLine(im, x1, y1 + 1, x1, y2 - 1, color); - gdImageLine(im, x2, y1 + 1, x2, y2 - 1, color); - } - } + return n; +} + +/* + Function: gdImageColorReplaceThreshold + Note: threshold semantics changed in versions >=2.3.4 — the value now scales + linearly with perceptual color distance. Callers using threshold values + tuned against the old behavior should apply new_t = sqrt(old_t / 100) * 100 + to approximate the previous behavior. This is due to a bug fix in the color + distance calculation, which previously did not take the square root + of the sum of squares, and thus returned a value that was the square + of the actual perceptual color distance. + The new behavior is more intuitive and consistent with common color distance + metrics +*/ +BGD_DECLARE(int) +gdImageColorReplaceThreshold(gdImagePtr im, int src, int dst, float threshold) +{ + register int x, y; + int n = 0; + + if (src == dst) { + return 0; + } + +#define REPLACING_LOOP(pixel) \ + do { \ + for (y = im->cy1; y <= im->cy2; y++) { \ + for (x = im->cx1; x <= im->cx2; x++) { \ + if (gdColorMatch(im, src, pixel(im, x, y), threshold)) { \ + gdImageSetPixel(im, x, y, dst); \ + n++; \ + } \ + } \ + } \ + } while (0) + + if (im->trueColor) { + REPLACING_LOOP(gdImageTrueColorPixel); + } else { + REPLACING_LOOP(gdImagePalettePixel); + } + +#undef REPLACING_LOOP + + return n; +} + +static int colorCmp(const void *x, const void *y) +{ + int a = *(int const *)x; + int b = *(int const *)y; + return (a > b) - (a < b); +} + +/* + Function: gdImageColorReplaceArray +*/ +BGD_DECLARE(int) +gdImageColorReplaceArray(gdImagePtr im, int len, int *src, int *dst) +{ + register int x, y; + int c, *d, *base; + int i, n = 0; + + if (len <= 0 || src == dst) { + return 0; + } + if (len == 1) { + return gdImageColorReplace(im, src[0], dst[0]); + } + if (overflow2(len, sizeof(int) << 1)) { + return -1; + } + base = (int *)gdMalloc(len * (sizeof(int) << 1)); + if (!base) { + return -1; + } + for (i = 0; i < len; i++) { + base[(i << 1)] = src[i]; + base[(i << 1) + 1] = dst[i]; + } + qsort(base, len, sizeof(int) << 1, colorCmp); + +#define REPLACING_LOOP(pixel) \ + do { \ + for (y = im->cy1; y <= im->cy2; y++) { \ + for (x = im->cx1; x <= im->cx2; x++) { \ + c = pixel(im, x, y); \ + if ((d = (int *)bsearch(&c, base, len, sizeof(int) << 1, colorCmp))) { \ + gdImageSetPixel(im, x, y, d[1]); \ + n++; \ + } \ + } \ + } \ + } while (0) + + if (im->trueColor) { + REPLACING_LOOP(gdImageTrueColorPixel); + } else { + REPLACING_LOOP(gdImagePalettePixel); + } + +#undef REPLACING_LOOP + + gdFree(base); + return n; +} + +/* + Function: gdImageColorReplaceCallback +*/ +BGD_DECLARE(int) +gdImageColorReplaceCallback(gdImagePtr im, gdCallbackImageColor callback) +{ + int c, d, n = 0; + + if (!callback) { + return 0; + } + if (im->trueColor) { + register int x, y; + + for (y = im->cy1; y <= im->cy2; y++) { + for (x = im->cx1; x <= im->cx2; x++) { + c = gdImageTrueColorPixel(im, x, y); + if ((d = callback(im, c)) != c) { + gdImageSetPixel(im, x, y, d); + n++; + } + } + } + } else { /* palette */ + int *sarr, *darr; + int k, len = 0; + + sarr = (int *)gdCalloc(im->colorsTotal, sizeof(int)); + if (!sarr) { + return -1; + } + for (c = 0; c < im->colorsTotal; c++) { + if (!im->open[c]) { + sarr[len++] = c; + } + } + darr = (int *)gdCalloc(len, sizeof(int)); + if (!darr) { + gdFree(sarr); + return -1; + } + for (k = 0; k < len; k++) { + darr[k] = callback(im, sarr[k]); + } + n = gdImageColorReplaceArray(im, k, sarr, darr); + gdFree(darr); + gdFree(sarr); + } + return n; +} + +/* 2.0.10: before the drawing routines, some code to clip points that are + * outside the drawing window. Nick Atty (nick@canalplan.org.uk) + * + * This is the Sutherland Hodgman Algorithm, as implemented by + * Duvanenko, Robbins and Gyurcsik - SH(DRG) for short. See Dr Dobb's + * Journal, January 1996, pp107-110 and 116-117 + * + * Given the end points of a line, and a bounding rectangle (which we + * know to be from (0,0) to (SX,SY)), adjust the endpoints to be on + * the edges of the rectangle if the line should be drawn at all, + * otherwise return a failure code */ + +/* this does "one-dimensional" clipping: note that the second time it + is called, all the x parameters refer to height and the y to width + - the comments ignore this (if you can understand it when it's + looking at the X parameters, it should become clear what happens on + the second call!) The code is simplified from that in the article, + as we know that gd images always start at (0,0) */ + +/* 2.0.26, TBB: we now have to respect a clipping rectangle, it won't + necessarily start at 0. */ + +static int clip_1d(int *x0, int *y0, int *x1, int *y1, int mindim, int maxdim) +{ + double m; /* gradient of line */ + if (*x0 < mindim) { + /* start of line is left of window */ + if (*x1 < mindim) /* as is the end, so the line never cuts the window */ + return 0; + m = (*y1 - *y0) / (double)(*x1 - *x0); /* calculate the slope of the line */ + /* adjust x0 to be on the left boundary (ie to be zero), and y0 to match + */ + *y0 -= (int)(m * (*x0 - mindim)); + *x0 = mindim; + /* now, perhaps, adjust the far end of the line as well */ + if (*x1 > maxdim) { + *y1 += (int)(m * (maxdim - *x1)); + *x1 = maxdim; + } + return 1; + } + if (*x0 > maxdim) { + /* start of line is right of window - + complement of above */ + if (*x1 > maxdim) /* as is the end, so the line misses the window */ + return 0; + m = (*y1 - *y0) / (double)(*x1 - *x0); /* calculate the slope of the line */ + *y0 += (int)(m * (maxdim - *x0)); /* adjust so point is on the right + boundary */ + *x0 = maxdim; + /* now, perhaps, adjust the end of the line */ + if (*x1 < mindim) { + *y1 -= (int)(m * (*x1 - mindim)); + *x1 = mindim; + } + return 1; + } + /* the final case - the start of the line is inside the window */ + if (*x1 > maxdim) { + /* other end is outside to the right */ + m = (*y1 - *y0) / (double)(*x1 - *x0); /* calculate the slope of the line */ + *y1 += (int)(m * (maxdim - *x1)); + *x1 = maxdim; + return 1; + } + if (*x1 < mindim) { + /* other end is outside to the left */ + m = (*y1 - *y0) / (double)(*x1 - *x0); /* calculate the slope of the line */ + *y1 -= (int)(m * (*x1 - mindim)); + *x1 = mindim; + return 1; + } + /* only get here if both points are inside the window */ + return 1; +} + +/** + * Group: Pixels + */ + +/* + Function: gdImageSetPixel +*/ +BGD_DECLARE(void) gdImageSetPixel(gdImagePtr im, int x, int y, int color) +{ + int p; + switch (color) { + case gdStyled: + if (!im->style) { + /* Refuse to draw if no style is set. */ + return; + } else { + p = im->style[im->stylePos++]; + } + if (p != (gdTransparent)) { + gdImageSetPixel(im, x, y, p); + } + im->stylePos = im->stylePos % im->styleLength; + break; + case gdStyledBrushed: + if (!im->style) { + /* Refuse to draw if no style is set. */ + return; + } + p = im->style[im->stylePos++]; + if ((p != gdTransparent) && (p != 0)) { + gdImageSetPixel(im, x, y, gdBrushed); + } + im->stylePos = im->stylePos % im->styleLength; + break; + case gdBrushed: + gdImageBrushApply(im, x, y); + break; + case gdTiled: + gdImageTileApply(im, x, y); + break; + case gdAntiAliased: + /* This shouldn't happen (2.0.26) because we just call + gdImageAALine now, but do something sane. */ + gdImageSetPixel(im, x, y, im->AA_color); + break; + default: + if (gdImageBoundsSafeMacro(im, x, y)) { + if (im->trueColor) { + switch (im->alphaBlendingFlag) { + default: + case gdEffectReplace: + im->tpixels[y][x] = color; + break; + case gdEffectAlphaBlend: + case gdEffectNormal: + im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color); + break; + case gdEffectOverlay: + im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color); + break; + case gdEffectMultiply: + im->tpixels[y][x] = gdLayerMultiply(im->tpixels[y][x], color); + break; + } + } else { + im->pixels[y][x] = color; + } + } + break; + } +} + +static void gdImageBrushApply(gdImagePtr im, int x, int y) +{ + int lx, ly; + int hy; + int hx; + int x1, y1, x2, y2; + int srcx, srcy; + + if (!im->brush) { + return; + } + hy = gdImageSY(im->brush) / 2; + y1 = y - hy; + y2 = y1 + gdImageSY(im->brush); + hx = gdImageSX(im->brush) / 2; + x1 = x - hx; + x2 = x1 + gdImageSX(im->brush); + srcy = 0; + if (im->trueColor) { + if (im->brush->trueColor) { + for (ly = y1; (ly < y2); ly++) { + srcx = 0; + for (lx = x1; (lx < x2); lx++) { + int p; + p = gdImageGetTrueColorPixel(im->brush, srcx, srcy); + /* 2.0.9, Thomas Winzig: apply simple full transparency */ + if (p != gdImageGetTransparent(im->brush)) { + gdImageSetPixel(im, lx, ly, p); + } + srcx++; + } + srcy++; + } + } else { + /* 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger for pointing out + * the issue) */ + for (ly = y1; (ly < y2); ly++) { + srcx = 0; + for (lx = x1; (lx < x2); lx++) { + int p, tc; + p = gdImageGetPixel(im->brush, srcx, srcy); + tc = gdImageGetTrueColorPixel(im->brush, srcx, srcy); + /* 2.0.9, Thomas Winzig: apply simple full transparency */ + if (p != gdImageGetTransparent(im->brush)) { + gdImageSetPixel(im, lx, ly, tc); + } + srcx++; + } + srcy++; + } + } + } else { + for (ly = y1; (ly < y2); ly++) { + srcx = 0; + for (lx = x1; (lx < x2); lx++) { + int p; + p = gdImageGetPixel(im->brush, srcx, srcy); + /* Allow for non-square brushes! */ + if (p != gdImageGetTransparent(im->brush)) { + if (im->brush->trueColor) { + gdImageSetPixel(im, lx, ly, + gdImageColorResolveAlpha( + im, gdTrueColorGetRed(p), gdTrueColorGetGreen(p), + gdTrueColorGetBlue(p), gdTrueColorGetAlpha(p))); + } else { + gdImageSetPixel(im, lx, ly, im->brushColorMap[p]); + } + } + srcx++; + } + srcy++; + } + } +} + +static void gdImageTileApply(gdImagePtr im, int x, int y) +{ + gdImagePtr tile = im->tile; + int srcx, srcy; + int p; + if (!tile) { + return; + } + srcx = x % gdImageSX(tile); + srcy = y % gdImageSY(tile); + if (im->trueColor) { + p = gdImageGetPixel(tile, srcx, srcy); + if (p != gdImageGetTransparent(tile)) { + if (!tile->trueColor) { + p = gdTrueColorAlpha(tile->red[p], tile->green[p], tile->blue[p], tile->alpha[p]); + } + gdImageSetPixel(im, x, y, p); + } + } else { + p = gdImageGetPixel(tile, srcx, srcy); + /* Allow for transparency */ + if (p != gdImageGetTransparent(tile)) { + if (tile->trueColor) { + /* Truecolor tile. Very slow on a palette destination. */ + gdImageSetPixel( + im, x, y, + gdImageColorResolveAlpha(im, gdTrueColorGetRed(p), gdTrueColorGetGreen(p), + gdTrueColorGetBlue(p), gdTrueColorGetAlpha(p))); + } else { + gdImageSetPixel(im, x, y, im->tileColorMap[p]); + } + } + } +} + +/** + * Function: gdImageGetPixel + * + * Gets a pixel color as stored in the image. + * + * Parameters: + * im - The image. + * x - The x-coordinate. + * y - The y-coordinate. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(int) gdImageGetPixel(gdImagePtr im, int x, int y) +{ + if (gdImageBoundsSafeMacro(im, x, y)) { + if (im->trueColor) { + return im->tpixels[y][x]; + } else { + return im->pixels[y][x]; + } + } else { + return 0; + } +} + +/** + * Function: gdImageGetTrueColorPixel + * + * Gets a pixel color always as truecolor value. + * + * Parameters: + * im - The image. + * x - The x-coordinate. + * y - The y-coordinate. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) gdImageGetTrueColorPixel(gdImagePtr im, int x, int y) +{ + int p = gdImageGetPixel(im, x, y); + if (!im->trueColor) { + return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p], + (im->transparent == p) ? gdAlphaTransparent : im->alpha[p]); + } else { + return p; + } +} + +/** + * Group: Primitives + */ + +/* + Function: gdImageAABlend + + NO-OP, kept for library compatibility. +*/ +BGD_DECLARE(void) gdImageAABlend(gdImagePtr im) { (void)im; } +static void _gdImageFilledHRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color); + +static void gdImageHLine(gdImagePtr im, int y, int x1, int x2, int col) +{ + if (im->thick > 1) { + int thickhalf = im->thick >> 1; + _gdImageFilledHRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col); + } else { + if (x2 < x1) { + int t = x2; + x2 = x1; + x1 = t; + } + + for (; x1 <= x2; x1++) { + gdImageSetPixel(im, x1, y, col); + } + } + return; +} + +static void gdImageVLine(gdImagePtr im, int x, int y1, int y2, int col) +{ + if (im->thick > 1) { + int thickhalf = im->thick >> 1; + gdImageFilledRectangle(im, x - thickhalf, y1, x + im->thick - thickhalf - 1, y2, col); + } else { + if (y2 < y1) { + int t = y1; + y1 = y2; + y2 = t; + } + + for (; y1 <= y2; y1++) { + gdImageSetPixel(im, x, y1, col); + } + } + return; +} + +/* + Function: gdImageLine + + Bresenham as presented in Foley & Van Dam. +*/ +BGD_DECLARE(void) gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; + int wid; + int w, wstart; + int thick = im->thick; + + if (color == gdAntiAliased) { + /* + gdAntiAliased passed as color: use the much faster, much cheaper + and equally attractive gdImageAALine implementation. That + clips too, so don't clip twice. + */ + gdImageAALine(im, x1, y1, x2, y2, im->AA_color); + return; + } + /* 2.0.10: Nick Atty: clip to edges of drawing rectangle, return if no + points need to be drawn. 2.0.26, TBB: clip to edges of clipping + rectangle. We were getting away with this because gdImageSetPixel + is used for actual drawing, but this is still more efficient and opens + the way to skip per-pixel bounds checking in the future. */ + + if (clip_1d(&x1, &y1, &x2, &y2, im->cx1, im->cx2) == 0) + return; + if (clip_1d(&y1, &x1, &y2, &x2, im->cy1, im->cy2) == 0) + return; + + dx = abs(x2 - x1); + dy = abs(y2 - y1); + + if (dx == 0) { + gdImageVLine(im, x1, y1, y2, color); + return; + } else if (dy == 0) { + gdImageHLine(im, y1, x1, x2, color); + return; + } + + if (dy <= dx) { + /* More-or-less horizontal. use wid for vertical stroke */ + /* Doug Claar: watch out for NaN in atan2 (2.0.5) */ + if ((dx == 0) && (dy == 0)) { + wid = 1; + } else { + /* 2.0.12: Michael Schwartz: divide rather than multiply; +TBB: but watch out for /0! */ + double ac = cos(atan2(dy, dx)); + if (ac != 0) { + wid = thick / ac; + } else { + wid = 1; + } + if (wid == 0) { + wid = 1; + } + } + d = 2 * dy - dx; + incr1 = 2 * dy; + incr2 = 2 * (dy - dx); + if (x1 > x2) { + x = x2; + y = y2; + ydirflag = (-1); + xend = x1; + } else { + x = x1; + y = y1; + ydirflag = 1; + xend = x2; + } + + /* Set up line thickness */ + wstart = y - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, x, w, color); + } + + if (((y2 - y1) * ydirflag) > 0) { + while (x < xend) { + x++; + if (d < 0) { + d += incr1; + } else { + y++; + d += incr2; + } + wstart = y - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, x, w, color); + } + } + } else { + while (x < xend) { + x++; + if (d < 0) { + d += incr1; + } else { + y--; + d += incr2; + } + wstart = y - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, x, w, color); + } + } + } + } else { + /* More-or-less vertical. use wid for horizontal stroke */ + /* 2.0.12: Michael Schwartz: divide rather than multiply; + TBB: but watch out for /0! */ + double as = sin(atan2(dy, dx)); + if (as != 0) { + wid = thick / as; + } else { + wid = 1; + } + if (wid == 0) { + wid = 1; + } + + d = 2 * dx - dy; + incr1 = 2 * dx; + incr2 = 2 * (dx - dy); + if (y1 > y2) { + y = y2; + x = x2; + yend = y1; + xdirflag = (-1); + } else { + y = y1; + x = x1; + yend = y2; + xdirflag = 1; + } + + /* Set up line thickness */ + wstart = x - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, w, y, color); + } + + if (((x2 - x1) * xdirflag) > 0) { + while (y < yend) { + y++; + if (d < 0) { + d += incr1; + } else { + x++; + d += incr2; + } + wstart = x - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, w, y, color); + } + } + } else { + while (y < yend) { + y++; + if (d < 0) { + d += incr1; + } else { + x--; + d += incr2; + } + wstart = x - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, w, y, color); + } + } + } + } +} + +static void dashedSet(gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, + int vert); + +/* + Function: gdImageDashedLine +*/ +BGD_DECLARE(void) gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; + int dashStep = 0; + int on = 1; + int wid; + int vert; + int thick = im->thick; + + dx = abs(x2 - x1); + dy = abs(y2 - y1); + if (dy <= dx) { + /* More-or-less horizontal. use wid for vertical stroke */ + /* 2.0.12: Michael Schwartz: divide rather than multiply; + TBB: but watch out for /0! */ + double as = sin(atan2(dy, dx)); + if (as != 0) { + wid = thick / as; + } else { + wid = 1; + } + vert = 1; + + d = 2 * dy - dx; + incr1 = 2 * dy; + incr2 = 2 * (dy - dx); + if (x1 > x2) { + x = x2; + y = y2; + ydirflag = (-1); + xend = x1; + } else { + x = x1; + y = y1; + ydirflag = 1; + xend = x2; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + if (((y2 - y1) * ydirflag) > 0) { + while (x < xend) { + x++; + if (d < 0) { + d += incr1; + } else { + y++; + d += incr2; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + } + } else { + while (x < xend) { + x++; + if (d < 0) { + d += incr1; + } else { + y--; + d += incr2; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + } + } + } else { + /* 2.0.12: Michael Schwartz: divide rather than multiply; + TBB: but watch out for /0! */ + double as = sin(atan2(dy, dx)); + if (as != 0) { + wid = thick / as; + } else { + wid = 1; + } + vert = 0; + + d = 2 * dx - dy; + incr1 = 2 * dx; + incr2 = 2 * (dx - dy); + if (y1 > y2) { + y = y2; + x = x2; + yend = y1; + xdirflag = (-1); + } else { + y = y1; + x = x1; + yend = y2; + xdirflag = 1; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + if (((x2 - x1) * xdirflag) > 0) { + while (y < yend) { + y++; + if (d < 0) { + d += incr1; + } else { + x++; + d += incr2; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + } + } else { + while (y < yend) { + y++; + if (d < 0) { + d += incr1; + } else { + x--; + d += incr2; + } + dashedSet(im, x, y, color, &on, &dashStep, wid, vert); + } + } + } +} + +static void dashedSet(gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, + int vert) +{ + int dashStep = *dashStepP; + int on = *onP; + int w, wstart; + + dashStep++; + if (dashStep == gdDashSize) { + dashStep = 0; + on = !on; + } + if (on) { + if (vert) { + wstart = y - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, x, w, color); + } + } else { + wstart = x - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetPixel(im, w, y, color); + } + } + } + *dashStepP = dashStep; + *onP = on; +} + +/** + * Function: gdImageChar + * + * Draws a single character. + * + * Parameters: + * im - The image to draw onto. + * f - The raster font. + * x - The x coordinate of the upper left pixel. + * y - The y coordinate of the upper left pixel. + * c - The character. + * color - The color. + * + * Variants: + * - + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) +{ + int cx, cy; + int px, py; + int fline; + const int xuppper = (x > INT_MAX - f->w) ? INT_MAX : x + f->w; + const int yuppper = (y > INT_MAX - f->h) ? INT_MAX : y + f->h; + cx = 0; + cy = 0; +#ifdef CHARSET_EBCDIC + c = ASC(c); +#endif /*CHARSET_EBCDIC */ + if ((c < f->offset) || (c >= (f->offset + f->nchars))) { + return; + } + fline = (c - f->offset) * f->h * f->w; + for (py = y; py < yuppper; py++) { + for (px = x; px < xuppper; px++) { + if (f->data[fline + cy * f->w + cx]) { + gdImageSetPixel(im, px, py, color); + } + cx++; + } + cx = 0; + cy++; + } +} + +/** + * Function: gdImageCharUp + */ +BGD_DECLARE(void) gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) +{ + int cx, cy; + int px, py; + int fline; + const int xuppper = (x > INT_MAX - f->h) ? INT_MAX : x + f->h; + const int ylower = (y < INT_MIN + f->w) ? INT_MIN : y - f->w; + cx = 0; + cy = 0; +#ifdef CHARSET_EBCDIC + c = ASC(c); +#endif /*CHARSET_EBCDIC */ + if ((c < f->offset) || (c >= (f->offset + f->nchars))) { + return; + } + fline = (c - f->offset) * f->h * f->w; + for (py = y; py > ylower; py--) { + for (px = x; px < xuppper; px++) { + if (f->data[fline + cy * f->w + cx]) { + gdImageSetPixel(im, px, py, color); + } + cy++; + } + cy = 0; + cx++; + } +} + +/** + * Function: gdImageString + * + * Draws a character string. + * + * Parameters: + * im - The image to draw onto. + * f - The raster font. + * x - The x coordinate of the upper left pixel. + * y - The y coordinate of the upper left pixel. + * c - The character string. + * color - The color. + * + * Variants: + * - + * - + * - + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color) +{ + int i; + int l; + l = strlen((char *)s); + for (i = 0; (i < l); i++) { + gdImageChar(im, f, x, y, s[i], color); + x += f->w; + } +} + +/** + * Function: gdImageStringUp + */ +BGD_DECLARE(void) +gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color) +{ + int i; + int l; + l = strlen((char *)s); + for (i = 0; (i < l); i++) { + gdImageCharUp(im, f, x, y, s[i], color); + y -= f->w; + } +} + +static int strlen16(unsigned short *s); + +/** + * Function: gdImageString16 + */ +BGD_DECLARE(void) +gdImageString16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color) +{ + int i; + int l; + l = strlen16(s); + for (i = 0; (i < l); i++) { + gdImageChar(im, f, x, y, s[i], color); + x += f->w; + } +} + +/** + * Function: gdImageStringUp16 + */ +BGD_DECLARE(void) +gdImageStringUp16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color) +{ + int i; + int l; + l = strlen16(s); + for (i = 0; (i < l); i++) { + gdImageCharUp(im, f, x, y, s[i], color); + y -= f->w; + } +} + +static int strlen16(unsigned short *s) +{ + int len = 0; + while (*s) { + s++; + len++; + } + return len; +} + +#ifndef HAVE_LSQRT +/* If you don't have a nice square root function for longs, you can use + ** this hack + */ +long lsqrt(long n) +{ + long result = (long)sqrt((double)n); + return result; +} +#endif + +/* s and e are integers modulo 360 (degrees), with 0 degrees + being the rightmost extreme and degrees changing clockwise. + cx and cy are the center in pixels; w and h are the horizontal + and vertical diameter in pixels. */ + +/* + Function: gdImageArc +*/ +BGD_DECLARE(void) +gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color) +{ + gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill); +} + +/* + Function: gdImageFilledArc +*/ +BGD_DECLARE(void) +gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style) +{ + gdPoint pts[363]; + int i, pti; + int lx = 0, ly = 0; + int fx = 0, fy = 0; + int startx = -1, starty = -1, endx = -1, endy = -1; + + if ((s % 360) == (e % 360)) { + s = 0; + e = 360; + } else { + if (s > 360) { + s = s % 360; + } + + if (e > 360) { + e = e % 360; + } + + while (s < 0) { + s += 360; + } + + while (e < s) { + e += 360; + } + if (s == e) { + s = 0; + e = 360; + } + } + + for (i = s, pti = 1; (i <= e); i++, pti++) { + int x, y; + x = endx = ((long)gdCosT[i % 360] * (long)w / (2 * 1024)) + cx; + y = endy = ((long)gdSinT[i % 360] * (long)h / (2 * 1024)) + cy; + if (i != s) { + if (!(style & gdChord)) { + if (style & gdNoFill) { + gdImageLine(im, lx, ly, x, y, color); + } else { + if (y == ly) { + pti--; /* don't add this point */ + if (((i > 270 || i < 90) && x > lx) || ((i > 90 && i < 270) && x < lx)) { + /* replace the old x coord, if increasing on the + right side or decreasing on the left side */ + pts[pti].x = x; + } + } else { + pts[pti].x = x; + pts[pti].y = y; + } + } + } + } else { + fx = x; + fy = y; + if (!(style & (gdChord | gdNoFill))) { + pts[0].x = cx; + pts[0].y = cy; + pts[pti].x = startx = x; + pts[pti].y = starty = y; + } + } + lx = x; + ly = y; + } + if (style & gdChord) { + if (style & gdNoFill) { + if (style & gdEdged) { + gdImageLine(im, cx, cy, lx, ly, color); + gdImageLine(im, cx, cy, fx, fy, color); + } + gdImageLine(im, fx, fy, lx, ly, color); + } else { + pts[0].x = fx; + pts[0].y = fy; + pts[1].x = lx; + pts[1].y = ly; + pts[2].x = cx; + pts[2].y = cy; + gdImageFilledPolygon(im, pts, 3, color); + } + } else { + if (style & gdNoFill) { + if (style & gdEdged) { + gdImageLine(im, cx, cy, lx, ly, color); + gdImageLine(im, cx, cy, fx, fy, color); + } + } else { + if (e - s < 360) { + if (pts[1].x != startx && pts[1].y == starty) { + /* start point has been removed due to y-coord fix => insert it */ + for (i = pti; i > 1; i--) { + pts[i].x = pts[i - 1].x; + pts[i].y = pts[i - 1].y; + } + pts[1].x = startx; + pts[1].y = starty; + pti++; + } + if (pts[pti - 1].x != endx && pts[pti - 1].y == endy) { + /* end point has been removed due to y-coord fix => insert it */ + pts[pti].x = endx; + pts[pti].y = endy; + pti++; + } + } + pts[pti].x = cx; + pts[pti].y = cy; + gdImageFilledPolygon(im, pts, pti + 1, color); + } + } } -static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) +/* + * Function: gdImageEllipse + * + * Draw an ellipse, stroke only. + * + * Note: + * This function does not support . GD 3.0 supports + * actual 2D vectors operation, you may rely on it if you need better 2D drawing + * operations. + * + * Parameters: + * im - The destination image. + * src - The source image. + * mx - x-coordinate of the center. + * my - y-coordinate of the center. + * w - The ellipse width. + * h - The ellipse height. + * c - The color of the ellipse. A color identifier created with one of the + * image color allocate functions. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageEllipse(gdImagePtr im, int mx, int my, int w, int h, int c) +{ + int x = 0, mx1 = 0, mx2 = 0, my1 = 0, my2 = 0; + int64_t aq, bq, dx, dy, r, rx, ry, a, b; + + a = w >> 1; + b = h >> 1; + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } + gdImageSetPixel(im, mx + a, my, c); + gdImageSetPixel(im, mx - a, my, c); + mx1 = mx - a; + my1 = my; + mx2 = mx + a; + my2 = my; + + aq = a * a; + bq = b * b; + dx = aq << 1; + dy = bq << 1; + r = a * bq; + rx = r << 1; + ry = 0; + x = a; + while (x > 0) { + if (r > 0) { + my1++; + my2--; + ry += dx; + r -= ry; + } + if (r <= 0) { + x--; + mx1++; + mx2--; + rx -= dy; + r += rx; + } + gdImageSetPixel(im, mx1, my1, c); + gdImageSetPixel(im, mx1, my2, c); + gdImageSetPixel(im, mx2, my1, c); + gdImageSetPixel(im, mx2, my2, c); + } +} + +/* + Function: gdImageFilledEllipse +*/ +BGD_DECLARE(void) +gdImageFilledEllipse(gdImagePtr im, int mx, int my, int w, int h, int c) +{ + int x = 0, mx1 = 0, mx2 = 0, my1 = 0, my2 = 0; + int64_t aq, bq, dx, dy, r, rx, ry, a, b; + int i; + int old_y2; + + a = w >> 1; + b = h >> 1; + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } + for (x = mx - a; x <= mx + a; x++) { + gdImageSetPixel(im, x, my, c); + } + + mx1 = mx - a; + my1 = my; + mx2 = mx + a; + my2 = my; + + aq = a * a; + bq = b * b; + dx = aq << 1; + dy = bq << 1; + r = a * bq; + rx = r << 1; + ry = 0; + x = a; + old_y2 = -2; + while (x > 0) { + if (r > 0) { + my1++; + my2--; + ry += dx; + r -= ry; + } + if (r <= 0) { + x--; + mx1++; + mx2--; + rx -= dy; + r += rx; + } + if (old_y2 != my2) { + for (i = mx1; i <= mx2; i++) { + gdImageSetPixel(im, i, my1, c); + gdImageSetPixel(im, i, my2, c); + } + } + old_y2 = my2; + } +} + +/* + Function: gdImageFillToBorder +*/ +BGD_DECLARE(void) gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color) +{ + int lastBorder; + /* Seek left */ + int leftLimit, rightLimit; + int i; + int restoreAlphaBlending = 0; + + if (border < 0 || color < 0) { + /* Refuse to fill to a non-solid border */ + return; + } + + if (!im->trueColor) { + if ((color > (im->colorsTotal - 1)) || (border > (im->colorsTotal - 1)) || (color < 0)) { + return; + } + } + + leftLimit = (-1); + + restoreAlphaBlending = im->alphaBlendingFlag; + im->alphaBlendingFlag = 0; + + if (x >= im->sx) { + x = im->sx - 1; + } else if (x < 0) { + x = 0; + } + if (y >= im->sy) { + y = im->sy - 1; + } else if (y < 0) { + y = 0; + } + + for (i = x; (i >= 0); i--) { + if (gdImageGetPixel(im, i, y) == border) { + break; + } + gdImageSetPixel(im, i, y, color); + leftLimit = i; + } + if (leftLimit == (-1)) { + im->alphaBlendingFlag = restoreAlphaBlending; + return; + } + /* Seek right */ + rightLimit = x; + for (i = (x + 1); (i < im->sx); i++) { + if (gdImageGetPixel(im, i, y) == border) { + break; + } + gdImageSetPixel(im, i, y, color); + rightLimit = i; + } + /* Look at lines above and below and start paints */ + /* Above */ + if (y > 0) { + lastBorder = 1; + for (i = leftLimit; (i <= rightLimit); i++) { + int c = gdImageGetPixel(im, i, y - 1); + if (lastBorder) { + if ((c != border) && (c != color)) { + gdImageFillToBorder(im, i, y - 1, border, color); + lastBorder = 0; + } + } else if ((c == border) || (c == color)) { + lastBorder = 1; + } + } + } + /* Below */ + if (y < ((im->sy) - 1)) { + lastBorder = 1; + for (i = leftLimit; (i <= rightLimit); i++) { + int c = gdImageGetPixel(im, i, y + 1); + if (lastBorder) { + if ((c != border) && (c != color)) { + gdImageFillToBorder(im, i, y + 1, border, color); + lastBorder = 0; + } + } else if ((c == border) || (c == color)) { + lastBorder = 1; + } + } + } + im->alphaBlendingFlag = restoreAlphaBlending; +} + +/* + * set the pixel at (x,y) and its 4-connected neighbors + * with the same pixel value to the new pixel value nc (new color). + * A 4-connected neighbor: pixel above, below, left, or right of a pixel. + * ideas from comp.graphics discussions. + * For tiled fill, the use of a flag buffer is mandatory. As the tile image can + * contain the same color as the color to fill. To do not bloat normal filling + * code I added a 2nd private function. + */ + +static int gdImageTileGet(gdImagePtr im, int x, int y) +{ + int srcx, srcy; + int tileColor, p; + if (!im->tile) { + return -1; + } + srcx = x % gdImageSX(im->tile); + srcy = y % gdImageSY(im->tile); + p = gdImageGetPixel(im->tile, srcx, srcy); + if (p == im->tile->transparent) { + tileColor = im->transparent; + } else if (im->trueColor) { + if (im->tile->trueColor) { + tileColor = p; + } else { + tileColor = gdTrueColorAlpha(gdImageRed(im->tile, p), gdImageGreen(im->tile, p), + gdImageBlue(im->tile, p), gdImageAlpha(im->tile, p)); + } + } else { + if (im->tile->trueColor) { + tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed(p), gdTrueColorGetGreen(p), + gdTrueColorGetBlue(p), gdTrueColorGetAlpha(p)); + } else { + tileColor = + gdImageColorResolveAlpha(im, gdImageRed(im->tile, p), gdImageGreen(im->tile, p), + gdImageBlue(im->tile, p), gdImageAlpha(im->tile, p)); + } + } + return tileColor; +} + +/* horizontal segment of scan line y */ +struct seg { + int y, xl, xr, dy; +}; + +/* max depth of stack */ +#define FILL_MAX ((int)(im->sy * im->sx) / 4) +#define FILL_PUSH(Y, XL, XR, DY) \ + if (sp < stack + FILL_MAX && Y + (DY) >= 0 && Y + (DY) < wy2) { \ + sp->y = Y; \ + sp->xl = XL; \ + sp->xr = XR; \ + sp->dy = DY; \ + sp++; \ + } + +#define FILL_POP(Y, XL, XR, DY) \ + { \ + sp--; \ + Y = sp->y + (DY = sp->dy); \ + XL = sp->xl; \ + XR = sp->xr; \ + } + +static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc); + +/* + Function: gdImageFill +*/ +BGD_DECLARE(void) gdImageFill(gdImagePtr im, int x, int y, int nc) +{ + int l, x1, x2, dy; + int oc; /* old pixel value */ + int wx2, wy2; + + int alphablending_bak; + + /* stack of filled segments */ + /* struct seg stack[FILL_MAX],*sp = stack; */ + struct seg *stack = NULL; + struct seg *sp; + + if (!im->trueColor && nc > (im->colorsTotal - 1)) { + return; + } + + alphablending_bak = im->alphaBlendingFlag; + im->alphaBlendingFlag = 0; + + if (nc == gdTiled) { + _gdImageFillTiled(im, x, y, nc); + im->alphaBlendingFlag = alphablending_bak; + return; + } + + wx2 = im->sx; + wy2 = im->sy; + oc = gdImageGetPixel(im, x, y); + if (oc == nc || x < 0 || x > wx2 || y < 0 || y > wy2) { + im->alphaBlendingFlag = alphablending_bak; + return; + } + + /* Do not use the 4 neighbors implementation with + * small images + */ + if (im->sx < 4) { + int ix = x, iy = y, c; + do { + do { + c = gdImageGetPixel(im, ix, iy); + if (c != oc) { + goto done; + } + gdImageSetPixel(im, ix, iy, nc); + } while (ix++ < (im->sx - 1)); + ix = x; + } while (iy++ < (im->sy - 1)); + goto done; + } + + if (overflow2(im->sy, im->sx)) { + return; + } + + if (overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { + return; + } + + stack = (struct seg *)gdMalloc(sizeof(struct seg) * ((int)(im->sy * im->sx) / 4)); + if (!stack) { + return; + } + sp = stack; + + /* required! */ + FILL_PUSH(y, x, x, 1); + /* seed segment (popped 1st) */ + FILL_PUSH(y + 1, x, x, -1); + while (sp > stack) { + FILL_POP(y, x1, x2, dy); + + for (x = x1; x >= 0 && gdImageGetPixel(im, x, y) == oc; x--) { + gdImageSetPixel(im, x, y, nc); + } + if (x >= x1) { + goto skip; + } + l = x + 1; + + /* leak on left? */ + if (l < x1) { + FILL_PUSH(y, l, x1 - 1, -dy); + } + x = x1 + 1; + do { + for (; x <= wx2 && gdImageGetPixel(im, x, y) == oc; x++) { + gdImageSetPixel(im, x, y, nc); + } + FILL_PUSH(y, l, x - 1, dy); + /* leak on right? */ + if (x > x2 + 1) { + FILL_PUSH(y, x2 + 1, x - 1, -dy); + } + skip: + for (x++; x <= x2 && (gdImageGetPixel(im, x, y) != oc); x++) + ; + + l = x; + } while (x <= x2); + } + + gdFree(stack); + +done: + im->alphaBlendingFlag = alphablending_bak; +} + +static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc) +{ + int l, x1, x2, dy; + int oc; /* old pixel value */ + int wx2, wy2; + /* stack of filled segments */ + struct seg *stack; + struct seg *sp; + char *pts; + + if (!im->tile) { + return; + } + + wx2 = im->sx; + wy2 = im->sy; + oc = gdImageGetPixel(im, x, y); + if (oc == nc || x < 0 || x > wx2 || y < 0 || y > wy2) { + return; + } + + if (overflow2(im->sy, im->sx)) { + return; + } + + if (overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { + return; + } + + pts = (char *)gdCalloc(im->sy * im->sx, sizeof(char)); + if (!pts) { + return; + } + + stack = (struct seg *)gdMalloc(sizeof(struct seg) * ((int)(im->sy * im->sx) / 4)); + if (!stack) { + gdFree(pts); + return; + } + sp = stack; + + oc = gdImageGetPixel(im, x, y); + + /* required! */ + FILL_PUSH(y, x, x, 1); + /* seed segment (popped 1st) */ + FILL_PUSH(y + 1, x, x, -1); + while (sp > stack) { + FILL_POP(y, x1, x2, dy); + for (x = x1; x >= 0 && (!pts[y + x * wy2] && gdImageGetPixel(im, x, y) == oc); x--) { + nc = gdImageTileGet(im, x, y); + pts[y + x * wy2] = 1; + gdImageSetPixel(im, x, y, nc); + } + if (x >= x1) { + goto skip; + } + l = x + 1; + + /* leak on left? */ + if (l < x1) { + FILL_PUSH(y, l, x1 - 1, -dy); + } + x = x1 + 1; + do { + for (; x < wx2 && (!pts[y + x * wy2] && gdImageGetPixel(im, x, y) == oc); x++) { + if (pts[y + x * wy2]) { + /* we should never be here */ + break; + } + nc = gdImageTileGet(im, x, y); + pts[y + x * wy2] = 1; + gdImageSetPixel(im, x, y, nc); + } + FILL_PUSH(y, l, x - 1, dy); + /* leak on right? */ + if (x > x2 + 1) { + FILL_PUSH(y, x2 + 1, x - 1, -dy); + } + skip: + for (x++; x <= x2 && (pts[y + x * wy2] || gdImageGetPixel(im, x, y) != oc); x++) + ; + l = x; + } while (x <= x2); + } + + gdFree(pts); + gdFree(stack); +} + +/** + * Function: gdImageRectangle + * + * Draws a rectangle. + * + * Parameters: + * im - The image. + * x1 - The x-coordinate of one of the corners. + * y1 - The y-coordinate of one of the corners. + * x2 - The x-coordinate of another corner. + * y2 - The y-coordinate of another corner. + * color - The color. + * + * See also: + * - + */ +BGD_DECLARE(void) +gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + int thick = im->thick; + + if (x1 == x2 && y1 == y2 && thick == 1) { + gdImageSetPixel(im, x1, y1, color); + return; + } + + if (y2 < y1) { + int t = y1; + y1 = y2; + y2 = t; + } + + if (x2 < x1) { + int t = x1; + x1 = x2; + x2 = t; + } + + if (thick > 1) { + int cx, cy, x1ul, y1ul, x2lr, y2lr; + int half = thick >> 1; + + x1ul = x1 - half; + y1ul = y1 - half; + + x2lr = x2 + half; + y2lr = y2 + half; + + cy = y1ul + thick; + while (cy-- > y1ul) { + cx = x1ul - 1; + while (cx++ < x2lr) { + gdImageSetPixel(im, cx, cy, color); + } + } + + cy = y2lr - thick; + while (cy++ < y2lr) { + cx = x1ul - 1; + while (cx++ < x2lr) { + gdImageSetPixel(im, cx, cy, color); + } + } + + cy = y1ul + thick - 1; + while (cy++ < y2lr - thick) { + cx = x1ul - 1; + while (cx++ < x1ul + thick) { + gdImageSetPixel(im, cx, cy, color); + } + } + + cy = y1ul + thick - 1; + while (cy++ < y2lr - thick) { + cx = x2lr - thick - 1; + while (cx++ < x2lr) { + gdImageSetPixel(im, cx, cy, color); + } + } + + return; + } else { + if (x1 == x2 || y1 == y2) { + gdImageLine(im, x1, y1, x2, y2, color); + } else { + gdImageLine(im, x1, y1, x2, y1, color); + gdImageLine(im, x1, y2, x2, y2, color); + gdImageLine(im, x1, y1 + 1, x1, y2 - 1, color); + gdImageLine(im, x2, y1 + 1, x2, y2 - 1, color); + } + } +} + +static void _gdImageFilledHRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + int x, y; + + if (x1 == x2 && y1 == y2) { + gdImageSetPixel(im, x1, y1, color); + return; + } + + if (x1 > x2) { + x = x1; + x1 = x2; + x2 = x; + } + + if (y1 > y2) { + y = y1; + y1 = y2; + y2 = y; + } + + if (x1 < 0) { + x1 = 0; + } + + if (x2 >= gdImageSX(im)) { + x2 = gdImageSX(im) - 1; + } + + if (y1 < 0) { + y1 = 0; + } + + if (y2 >= gdImageSY(im)) { + y2 = gdImageSY(im) - 1; + } + + for (x = x1; (x <= x2); x++) { + for (y = y1; (y <= y2); y++) { + gdImageSetPixel(im, x, y, color); + } + } +} + +static void _gdImageFilledVRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + int x, y; + + if (x1 == x2 && y1 == y2) { + gdImageSetPixel(im, x1, y1, color); + return; + } + + if (x1 > x2) { + x = x1; + x1 = x2; + x2 = x; + } + + if (y1 > y2) { + y = y1; + y1 = y2; + y2 = y; + } + + if (x1 < 0) { + x1 = 0; + } + + if (x2 >= gdImageSX(im)) { + x2 = gdImageSX(im) - 1; + } + + if (y1 < 0) { + y1 = 0; + } + + if (y2 >= gdImageSY(im)) { + y2 = gdImageSY(im) - 1; + } + + for (y = y1; (y <= y2); y++) { + for (x = x1; (x <= x2); x++) { + gdImageSetPixel(im, x, y, color); + } + } +} + +/* + Function: gdImageFilledRectangle +*/ +BGD_DECLARE(void) gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) +{ + _gdImageFilledVRectangle(im, x1, y1, x2, y2, color); +} + +/** + * Group: Cloning and Copying + */ + +/** + * Function: gdImageClone + * + * Clones an image + * + * Creates an exact duplicate of the given image. + * + * Parameters: + * src - The source image. + * + * Returns: + * The cloned image on success, NULL on failure. + */ +BGD_DECLARE(gdImagePtr) gdImageClone(gdImagePtr src) +{ + gdImagePtr dst; + register int i, x; + + if (src->trueColor) { + dst = gdImageCreateTrueColor(src->sx, src->sy); + } else { + dst = gdImageCreate(src->sx, src->sy); + } + + if (dst == NULL) { + return NULL; + } + + if (src->trueColor == 0) { + dst->colorsTotal = src->colorsTotal; + for (i = 0; i < gdMaxColors; i++) { + dst->red[i] = src->red[i]; + dst->green[i] = src->green[i]; + dst->blue[i] = src->blue[i]; + dst->alpha[i] = src->alpha[i]; + dst->open[i] = src->open[i]; + } + for (i = 0; i < src->sy; i++) { + for (x = 0; x < src->sx; x++) { + dst->pixels[i][x] = src->pixels[i][x]; + } + } + } else { + for (i = 0; i < src->sy; i++) { + for (x = 0; x < src->sx; x++) { + dst->tpixels[i][x] = src->tpixels[i][x]; + } + } + } + + dst->interlace = src->interlace; + + dst->alphaBlendingFlag = src->alphaBlendingFlag; + dst->saveAlphaFlag = src->saveAlphaFlag; + dst->AA = src->AA; + dst->AA_color = src->AA_color; + dst->AA_dont_blend = src->AA_dont_blend; + + dst->cx1 = src->cx1; + dst->cy1 = src->cy1; + dst->cx2 = src->cx2; + dst->cy2 = src->cy2; + + dst->res_x = src->res_x; + dst->res_y = src->res_y; + + dst->paletteQuantizationMethod = src->paletteQuantizationMethod; + dst->paletteQuantizationSpeed = src->paletteQuantizationSpeed; + dst->paletteQuantizationMinQuality = src->paletteQuantizationMinQuality; + dst->paletteQuantizationMaxQuality = src->paletteQuantizationMaxQuality; + + dst->interpolation_id = src->interpolation_id; + dst->interpolation = src->interpolation; + + if (src->brush) { + dst->brush = gdImageClone(src->brush); + } + + if (src->tile) { + dst->tile = gdImageClone(src->tile); + } + + if (src->style) { + gdImageSetStyle(dst, src->style, src->styleLength); + dst->stylePos = src->stylePos; + } + + for (i = 0; i < gdMaxColors; i++) { + dst->brushColorMap[i] = src->brushColorMap[i]; + dst->tileColorMap[i] = src->tileColorMap[i]; + } + + if (src->polyAllocated > 0 && overflow2(sizeof(int), src->polyAllocated) == 0) { + dst->polyInts = gdMalloc(sizeof(int) * src->polyAllocated); + dst->polyAllocated = src->polyAllocated; + for (i = 0; i < src->polyAllocated; i++) { + dst->polyInts[i] = src->polyInts[i]; + } + } + + return dst; +} + +/** + * Function: gdImageCopy + * + * Copy an area of an image to another image + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the upper left corner to copy to. + * dstY - The y-coordinate of the upper left corner to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * w - The width of the area to copy. + * h - The height of the area to copy. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h) +{ + int c; + int x, y; + int tox, toy; + int i; + int colorMap[gdMaxColors]; + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } + if (dst->trueColor) { + /* 2.0: much easier when the destination is truecolor. */ + /* 2.0.10: needs a transparent-index check that is still valid if + * the source is not truecolor. Thanks to Frank Warmerdam. + */ + + if (src->trueColor) { + for (y = 0; (y < h); y++) { + for (x = 0; (x < w); x++) { + int c = gdImageGetTrueColorPixel(src, srcX + x, srcY + y); + if (c != src->transparent) { + gdImageSetPixel(dst, dstX + x, dstY + y, c); + } + } + } + } else { + /* source is palette based */ + for (y = 0; (y < h); y++) { + for (x = 0; (x < w); x++) { + int c = gdImageGetPixel(src, srcX + x, srcY + y); + if (c != src->transparent) { + gdImageSetPixel(dst, dstX + x, dstY + y, + gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], + src->alpha[c])); + } + } + } + } + return; + } + + /* Palette based to palette based */ + for (i = 0; i < gdMaxColors; i++) { + colorMap[i] = (-1); + } + toy = dstY; + for (y = srcY; (y < (srcY + h)); y++) { + tox = dstX; + for (x = srcX; (x < (srcX + w)); x++) { + int nc; + int mapTo; + c = gdImageGetPixel(src, x, y); + /* Added 7/24/95: support transparent copies */ + if (gdImageGetTransparent(src) == c) { + tox++; + continue; + } + /* Have we established a mapping for this color? */ + if (src->trueColor) { + /* 2.05: remap to the palette available in the destination image. This is slow and + * works badly, but it beats crashing! Thanks to Padhrig McCarthy. + */ + mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), + gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c)); + } else if (colorMap[c] == (-1)) { + /* If it's the same image, mapping is trivial */ + if (dst == src) { + nc = c; + } else { + /* Get best match possible. This function never returns error. */ + nc = gdImageColorResolveAlpha(dst, src->red[c], src->green[c], src->blue[c], + src->alpha[c]); + } + colorMap[c] = nc; + mapTo = colorMap[c]; + } else { + mapTo = colorMap[c]; + } + gdImageSetPixel(dst, tox, toy, mapTo); + tox++; + } + toy++; + } +} + +/** + * Function: gdImageCopyMerge + * + * Copy an area of an image to another image ignoring alpha + * + * The source area will be copied to the destination are by merging the pixels. + * + * Note: + * This function is a substitute for real alpha channel operations, + * so it doesn't pay attention to the alpha channel. + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the upper left corner to copy to. + * dstY - The y-coordinate of the upper left corner to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * w - The width of the area to copy. + * h - The height of the area to copy. + * pct - The percentage in range 0..100. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, + int h, int pct) +{ + int c, dc; + int x, y; + int tox, toy; + int ncR, ncG, ncB; + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } + toy = dstY; + for (y = srcY; (y < (srcY + h)); y++) { + tox = dstX; + for (x = srcX; (x < (srcX + w)); x++) { + int nc; + c = gdImageGetPixel(src, x, y); + /* Added 7/24/95: support transparent copies */ + if (gdImageGetTransparent(src) == c) { + tox++; + continue; + } + /* If it's the same image, mapping is trivial */ + if (dst == src) { + nc = c; + } else { + dc = gdImageGetPixel(dst, tox, toy); + + ncR = gdImageRed(src, c) * (pct / 100.0) + + gdImageRed(dst, dc) * ((100 - pct) / 100.0); + ncG = gdImageGreen(src, c) * (pct / 100.0) + + gdImageGreen(dst, dc) * ((100 - pct) / 100.0); + ncB = gdImageBlue(src, c) * (pct / 100.0) + + gdImageBlue(dst, dc) * ((100 - pct) / 100.0); + + /* Find a reasonable color */ + nc = gdImageColorResolve(dst, ncR, ncG, ncB); + } + gdImageSetPixel(dst, tox, toy, nc); + tox++; + } + toy++; + } +} + +/** + * Function: gdImageCopyMergeGray + * + * Copy an area of an image to another image ignoring alpha + * + * The source area will be copied to the grayscaled destination area by merging + * the pixels. + * + * Note: + * This function is a substitute for real alpha channel operations, + * so it doesn't pay attention to the alpha channel. + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the upper left corner to copy to. + * dstY - The y-coordinate of the upper left corner to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * w - The width of the area to copy. + * h - The height of the area to copy. + * pct - The percentage of the source color intensity in range 0..100. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, + int h, int pct) +{ + int c, dc; + int x, y; + int tox, toy; + int ncR, ncG, ncB; + float g; + + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } + + toy = dstY; + for (y = srcY; (y < (srcY + h)); y++) { + tox = dstX; + for (x = srcX; (x < (srcX + w)); x++) { + int nc; + c = gdImageGetPixel(src, x, y); + /* Added 7/24/95: support transparent copies */ + if (gdImageGetTransparent(src) == c) { + tox++; + continue; + } + /* + * If it's the same image, mapping is NOT trivial since we + * merge with greyscale target, but if pct is 100, the grey + * value is not used, so it becomes trivial. pjw 2.0.12. + */ + if (dst == src && pct == 100) { + nc = c; + } else { + dc = gdImageGetPixel(dst, tox, toy); + g = 0.29900 * gdImageRed(dst, dc) + 0.58700 * gdImageGreen(dst, dc) + + 0.11400 * gdImageBlue(dst, dc); + + ncR = gdImageRed(src, c) * (pct / 100.0) + g * ((100 - pct) / 100.0); + ncG = gdImageGreen(src, c) * (pct / 100.0) + g * ((100 - pct) / 100.0); + ncB = gdImageBlue(src, c) * (pct / 100.0) + g * ((100 - pct) / 100.0); + + /* First look for an exact match */ + nc = gdImageColorExact(dst, ncR, ncG, ncB); + if (nc == (-1)) { + /* No, so try to allocate it */ + nc = gdImageColorAllocate(dst, ncR, ncG, ncB); + /* If we're out of colors, go for the closest color */ + if (nc == (-1)) { + nc = gdImageColorClosest(dst, ncR, ncG, ncB); + } + } + } + gdImageSetPixel(dst, tox, toy, nc); + tox++; + } + toy++; + } +} + +/** + * Function: gdImageCopyResized + * + * Copy a resized area from an image to another image + * + * If the source and destination area differ in size, the area will be resized + * using nearest-neighbor interpolation. + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the upper left corner to copy to. + * dstY - The y-coordinate of the upper left corner to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * dstW - The width of the area to copy to. + * dstH - The height of the area to copy to. + * srcW - The width of the area to copy from. + * srcH - The height of the area to copy from. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, + int dstH, int srcW, int srcH) { - int x, y; + int c; + int x, y; + int tox, toy; + int ydest; + int i; + int colorMap[gdMaxColors]; + /* Stretch vectors */ + int *stx; + int *sty; + /* We only need to use floating point to determine the correct + stretch vector for one line's worth. */ + if (overflow2(sizeof(int), srcW)) { + return; + } + if (overflow2(sizeof(int), srcH)) { + return; + } + if (!gdImageClipCopyResized(dst, &dstX, &dstY, &dstW, &dstH, &srcX, &srcY, &srcW, &srcH)) { + return; + } + stx = (int *)gdMalloc(sizeof(int) * srcW); + if (!stx) { + return; + } - if (x1 == x2 && y1 == y2) { - gdImageSetPixel(im, x1, y1, color); - return; - } + sty = (int *)gdMalloc(sizeof(int) * srcH); + if (!sty) { + gdFree(stx); + return; + } - if (x1 > x2) { - x = x1; - x1 = x2; - x2 = x; - } - - if (y1 > y2) { - y = y1; - y1 = y2; - y2 = y; - } - - if (x1 < 0) { - x1 = 0; - } - - if (x2 >= gdImageSX(im)) { - x2 = gdImageSX(im) - 1; - } - - if (y1 < 0) { - y1 = 0; - } - - if (y2 >= gdImageSY(im)) { - y2 = gdImageSY(im) - 1; - } - - for (x = x1; (x <= x2); x++) { - for (y = y1; (y <= y2); y++) { - gdImageSetPixel (im, x, y, color); - } - } -} - -static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) -{ - int x, y; - - if (x1 == x2 && y1 == y2) { - gdImageSetPixel(im, x1, y1, color); - return; - } - - if (x1 > x2) { - x = x1; - x1 = x2; - x2 = x; - } - - if (y1 > y2) { - y = y1; - y1 = y2; - y2 = y; - } - - if (x1 < 0) { - x1 = 0; - } - - if (x2 >= gdImageSX(im)) { - x2 = gdImageSX(im) - 1; - } - - if (y1 < 0) { - y1 = 0; - } - - if (y2 >= gdImageSY(im)) { - y2 = gdImageSY(im) - 1; - } - - for (y = y1; (y <= y2); y++) { - for (x = x1; (x <= x2); x++) { - gdImageSetPixel (im, x, y, color); - } - } -} - -void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color) -{ - _gdImageFilledVRectangle(im, x1, y1, x2, y2, color); -} + /* Fixed by Mao Morimoto 2.0.16 */ + for (i = 0; (i < srcW); i++) { + stx[i] = dstW * (i + 1) / srcW - dstW * i / srcW; + } + for (i = 0; (i < srcH); i++) { + sty[i] = dstH * (i + 1) / srcH - dstH * i / srcH; + } + for (i = 0; (i < gdMaxColors); i++) { + colorMap[i] = (-1); + } + toy = dstY; + for (y = srcY; (y < (srcY + srcH)); y++) { + for (ydest = 0; (ydest < sty[y - srcY]); ydest++) { + tox = dstX; + for (x = srcX; (x < (srcX + srcW)); x++) { + int nc = 0; + int mapTo; + if (!stx[x - srcX]) { + continue; + } + if (dst->trueColor) { + if (!src->trueColor) { + int tmp = gdImageGetPixel(src, x, y); + mapTo = gdImageGetTrueColorPixel(src, x, y); + if (gdImageGetTransparent(src) == tmp) { + /* 2.0.21, TK: not tox++ */ + tox += stx[x - srcX]; + continue; + } + } else { + /* TK: old code follows */ + mapTo = gdImageGetTrueColorPixel(src, x, y); + /* Added 7/24/95: support transparent copies */ + if (gdImageGetTransparent(src) == mapTo) { + /* 2.0.21, TK: not tox++ */ + tox += stx[x - srcX]; + continue; + } + } + } else { + c = gdImageGetPixel(src, x, y); + /* Added 7/24/95: support transparent copies */ + if (gdImageGetTransparent(src) == c) { + tox += stx[x - srcX]; + continue; + } + if (src->trueColor) { + /* Remap to the palette available in the destination image. This is slow and + * works badly. */ + mapTo = gdImageColorResolveAlpha( + dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), + gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c)); + } else { + /* Have we established a mapping for this color? */ + if (colorMap[c] == (-1)) { + /* If it's the same image, mapping is trivial */ + if (dst == src) { + nc = c; + } else { + /* Find or create the best match */ + nc = gdImageColorResolveAlpha( + dst, gdImageRed(src, c), gdImageGreen(src, c), + gdImageBlue(src, c), gdImageAlpha(src, c)); + } + colorMap[c] = nc; + } + mapTo = colorMap[c]; + } + } + for (i = 0; (i < stx[x - srcX]); i++) { + gdImageSetPixel(dst, tox, toy, mapTo); + tox++; + } + } + toy++; + } + } + gdFree(stx); + gdFree(sty); +} -void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h) +/** + * Function: gdImageCopyRotated + * + * Copy a rotated area from an image to another image + * + * The area is counter-clockwise rotated using nearest-neighbor interpolation. + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the center of the area to copy to. + * dstY - The y-coordinate of the center of the area to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * srcW - The width of the area to copy from. + * srcH - The height of the area to copy from. + * angle - The angle in degrees. + * + * See also: + * - + */ +BGD_DECLARE(void) +gdImageCopyRotated(gdImagePtr dst, gdImagePtr src, double dstX, double dstY, int srcX, int srcY, + int srcWidth, int srcHeight, int angle) { - int c; - int x, y; - int tox, toy; - int i; - int colorMap[gdMaxColors]; + double dx, dy; + double radius = sqrt(srcWidth * srcWidth + srcHeight * srcHeight); + double aCos = cos(angle * .0174532925); + double aSin = sin(angle * .0174532925); + double scX = srcX + ((double)srcWidth) / 2; + double scY = srcY + ((double)srcHeight) / 2; + int cmap[gdMaxColors]; + int i; + const double safe_max = (double)INT_MAX / 2.0; + const double safe_min = (double)INT_MIN / 2.0; + double dyMin, dyMax, dxMin, dxMax; + + /* + 2.0.34: transparency preservation. The transparentness of + the transparent color is more important than its hue. + */ + if (src->transparent != -1) { + if (dst->transparent == -1) { + dst->transparent = src->transparent; + } + } - if (dst->trueColor) { - /* 2.0: much easier when the destination is truecolor. */ - /* 2.0.10: needs a transparent-index check that is still valid if - * the source is not truecolor. Thanks to Frank Warmerdam. - */ - - if (src->trueColor) { - for (y = 0; (y < h); y++) { - for (x = 0; (x < w); x++) { - int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y); - if (c != src->transparent) { - gdImageSetPixel (dst, dstX + x, dstY + y, c); - } - } - } - } else { - /* source is palette based */ - for (y = 0; (y < h); y++) { - for (x = 0; (x < w); x++) { - int c = gdImageGetPixel (src, srcX + x, srcY + y); - if (c != src->transparent) { - gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c])); - } - } - } - } - return; - } - - /* Palette based to palette based */ - for (i = 0; i < gdMaxColors; i++) { - colorMap[i] = (-1); - } - toy = dstY; - for (y = srcY; y < (srcY + h); y++) { - tox = dstX; - for (x = srcX; x < (srcX + w); x++) { - int nc; - int mapTo; - c = gdImageGetPixel (src, x, y); - /* Added 7/24/95: support transparent copies */ - if (gdImageGetTransparent (src) == c) { - tox++; - continue; - } - /* Have we established a mapping for this color? */ - if (src->trueColor) { - /* 2.05: remap to the palette available in the destination image. This is slow and - * works badly, but it beats crashing! Thanks to Padhrig McCarthy. - */ - mapTo = gdImageColorResolveAlpha (dst, gdTrueColorGetRed (c), gdTrueColorGetGreen (c), gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c)); - } else if (colorMap[c] == (-1)) { - /* If it's the same image, mapping is trivial */ - if (dst == src) { - nc = c; - } else { - /* Get best match possible. This function never returns error. */ - nc = gdImageColorResolveAlpha (dst, src->red[c], src->green[c], src->blue[c], src->alpha[c]); - } - colorMap[c] = nc; - mapTo = colorMap[c]; - } else { - mapTo = colorMap[c]; - } - gdImageSetPixel (dst, tox, toy, mapTo); - tox++; - } - toy++; - } -} - -/* This function is a substitute for real alpha channel operations, - so it doesn't pay attention to the alpha channel. */ -void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct) -{ - int c, dc; - int x, y; - int tox, toy; - int ncR, ncG, ncB; - toy = dstY; - - for (y = srcY; y < (srcY + h); y++) { - tox = dstX; - for (x = srcX; x < (srcX + w); x++) { - int nc; - c = gdImageGetPixel(src, x, y); - /* Added 7/24/95: support transparent copies */ - if (gdImageGetTransparent(src) == c) { - tox++; - continue; - } - /* If it's the same image, mapping is trivial */ - if (dst == src) { - nc = c; - } else { - dc = gdImageGetPixel(dst, tox, toy); - - ncR = (int)(gdImageRed (src, c) * (pct / 100.0) + gdImageRed (dst, dc) * ((100 - pct) / 100.0)); - ncG = (int)(gdImageGreen (src, c) * (pct / 100.0) + gdImageGreen (dst, dc) * ((100 - pct) / 100.0)); - ncB = (int)(gdImageBlue (src, c) * (pct / 100.0) + gdImageBlue (dst, dc) * ((100 - pct) / 100.0)); - - /* Find a reasonable color */ - nc = gdImageColorResolve (dst, ncR, ncG, ncB); - } - gdImageSetPixel (dst, tox, toy, nc); - tox++; - } - toy++; - } -} - -/* This function is a substitute for real alpha channel operations, - so it doesn't pay attention to the alpha channel. */ -void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct) -{ - int c, dc; - int x, y; - int tox, toy; - int ncR, ncG, ncB; - float g; - toy = dstY; - - for (y = srcY; (y < (srcY + h)); y++) { - tox = dstX; - for (x = srcX; (x < (srcX + w)); x++) { - int nc; - c = gdImageGetPixel (src, x, y); - /* Added 7/24/95: support transparent copies */ - if (gdImageGetTransparent(src) == c) { - tox++; - continue; - } + for (i = 0; (i < gdMaxColors); i++) { + cmap[i] = (-1); + } - /* - * If it's the same image, mapping is NOT trivial since we - * merge with greyscale target, but if pct is 100, the grey - * value is not used, so it becomes trivial. pjw 2.0.12. - */ - if (dst == src && pct == 100) { - nc = c; - } else { - dc = gdImageGetPixel(dst, tox, toy); - g = (0.29900f * gdImageRed(dst, dc)) + (0.58700f * gdImageGreen(dst, dc)) + (0.11400f * gdImageBlue(dst, dc)); - - ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0)); - ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0)); - ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + g * ((100 - pct) / 100.0)); - - - /* First look for an exact match */ - nc = gdImageColorExact(dst, ncR, ncG, ncB); - if (nc == (-1)) { - /* No, so try to allocate it */ - nc = gdImageColorAllocate(dst, ncR, ncG, ncB); - /* If we're out of colors, go for the closest color */ - if (nc == (-1)) { - nc = gdImageColorClosest(dst, ncR, ncG, ncB); - } - } - } - gdImageSetPixel(dst, tox, toy, nc); - tox++; - } - toy++; - } -} - -void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH) -{ - int c; - int x, y; - int tox, toy; - int ydest; - int i; - int colorMap[gdMaxColors]; - /* Stretch vectors */ - int *stx, *sty; - - if (overflow2(sizeof(int), srcW)) { - return; - } - if (overflow2(sizeof(int), srcH)) { - return; - } - - stx = (int *) gdMalloc (sizeof (int) * srcW); - sty = (int *) gdMalloc (sizeof (int) * srcH); - - /* Fixed by Mao Morimoto 2.0.16 */ - for (i = 0; (i < srcW); i++) { - stx[i] = dstW * (i+1) / srcW - dstW * i / srcW ; - } - for (i = 0; (i < srcH); i++) { - sty[i] = dstH * (i+1) / srcH - dstH * i / srcH ; - } - for (i = 0; (i < gdMaxColors); i++) { - colorMap[i] = (-1); - } - toy = dstY; - for (y = srcY; (y < (srcY + srcH)); y++) { - for (ydest = 0; (ydest < sty[y - srcY]); ydest++) { - tox = dstX; - for (x = srcX; (x < (srcX + srcW)); x++) { - int nc = 0; - int mapTo; - if (!stx[x - srcX]) { - continue; - } - if (dst->trueColor) { - /* 2.0.9: Thorben Kundinger: Maybe the source image is not a truecolor image */ - if (!src->trueColor) { - int tmp = gdImageGetPixel (src, x, y); - mapTo = gdImageGetTrueColorPixel (src, x, y); - if (gdImageGetTransparent (src) == tmp) { - /* 2.0.21, TK: not tox++ */ - tox += stx[x - srcX]; - continue; - } - } else { - /* TK: old code follows */ - mapTo = gdImageGetTrueColorPixel (src, x, y); - /* Added 7/24/95: support transparent copies */ - if (gdImageGetTransparent (src) == mapTo) { - /* 2.0.21, TK: not tox++ */ - tox += stx[x - srcX]; - continue; - } - } - } else { - c = gdImageGetPixel (src, x, y); - /* Added 7/24/95: support transparent copies */ - if (gdImageGetTransparent (src) == c) { - tox += stx[x - srcX]; - continue; - } - if (src->trueColor) { - /* Remap to the palette available in the destination image. This is slow and works badly. */ - mapTo = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c), - gdTrueColorGetGreen(c), - gdTrueColorGetBlue(c), - gdTrueColorGetAlpha (c)); - } else { - /* Have we established a mapping for this color? */ - if (colorMap[c] == (-1)) { - /* If it's the same image, mapping is trivial */ - if (dst == src) { - nc = c; - } else { - /* Find or create the best match */ - /* 2.0.5: can't use gdTrueColorGetRed, etc with palette */ - nc = gdImageColorResolveAlpha(dst, gdImageRed(src, c), - gdImageGreen(src, c), - gdImageBlue(src, c), - gdImageAlpha(src, c)); - } - colorMap[c] = nc; - } - mapTo = colorMap[c]; - } - } - for (i = 0; (i < stx[x - srcX]); i++) { - gdImageSetPixel (dst, tox, toy, mapTo); - tox++; - } - } - toy++; - } - } - gdFree (stx); - gdFree (sty); + dyMin = dstY - radius; + dyMax = dstY + radius; + dxMin = dstX - radius; + dxMax = dstX + radius; + + /* clamp loop bounds so sx/sy casts are always safe */ + if (dxMin < safe_min + radius + scX) + dxMin = safe_min + radius + scX; + if (dxMax > safe_max - radius - scX) + dxMax = safe_max - radius - scX; + if (dyMin < safe_min + radius + scY) + dyMin = safe_min + radius + scY; + if (dyMax > safe_max - radius - scY) + dyMax = safe_max - radius - scY; + + for (dy = dyMin; dy <= dyMax; dy++) { + for (dx = dxMin; dx <= dxMax; dx++) { + // for (dy = dstY - radius; (dy <= dstY + radius); dy++) { + // for (dx = dstX - radius; (dx <= dstX + radius); dx++) { + double sxd = (dx - dstX) * aCos - (dy - dstY) * aSin; + double syd = (dy - dstY) * aCos + (dx - dstX) * aSin; + int sx = sxd + scX; + int sy = syd + scY; + if ((sx >= srcX) && (sx < srcX + srcWidth) && (sy >= srcY) && (sy < srcY + srcHeight)) { + int c = gdImageGetPixel(src, sx, sy); + /* 2.0.34: transparency wins */ + if (c == src->transparent) { + gdImageSetPixel(dst, dx, dy, dst->transparent); + } else if (!src->trueColor) { + /* Use a table to avoid an expensive + lookup on every single pixel */ + if (cmap[c] == -1) { + cmap[c] = + gdImageColorResolveAlpha(dst, gdImageRed(src, c), gdImageGreen(src, c), + gdImageBlue(src, c), gdImageAlpha(src, c)); + } + gdImageSetPixel(dst, dx, dy, cmap[c]); + } else { + gdImageSetPixel( + dst, dx, dy, + gdImageColorResolveAlpha(dst, gdImageRed(src, c), gdImageGreen(src, c), + gdImageBlue(src, c), gdImageAlpha(src, c))); + } + } + } + } } /* When gd 1.x was first created, floating point was to be avoided. These days it is often faster than table lookups or integer arithmetic. The routine below is shamelessly, gloriously floating point. TBB */ - -void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH) -{ - int x, y; - double sy1, sy2, sx1, sx2; - - if (!dst->trueColor) { - gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); - return; - } - for (y = dstY; (y < dstY + dstH); y++) { - sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH; - sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH; - for (x = dstX; (x < dstX + dstW); x++) { - double sx, sy; - double spixels = 0; - double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; - double alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0; - sx1 = ((double) x - (double) dstX) * (double) srcW / dstW; - sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW; - sy = sy1; - do { - double yportion; - if (floor_cast(sy) == floor_cast(sy1)) { - yportion = 1.0f - (sy - floor_cast(sy)); - if (yportion > sy2 - sy1) { - yportion = sy2 - sy1; - } - sy = floor_cast(sy); - } else if (sy == floorf(sy2)) { - yportion = sy2 - floor_cast(sy2); - } else { - yportion = 1.0f; - } - sx = sx1; - do { - double xportion; - double pcontribution; - int p; - if (floorf(sx) == floor_cast(sx1)) { - xportion = 1.0f - (sx - floor_cast(sx)); - if (xportion > sx2 - sx1) { - xportion = sx2 - sx1; - } - sx = floor_cast(sx); - } else if (sx == floorf(sx2)) { - xportion = sx2 - floor_cast(sx2); - } else { - xportion = 1.0f; - } - pcontribution = xportion * yportion; - p = gdImageGetTrueColorPixel(src, (int) sx + srcX, (int) sy + srcY); - - alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution; - red += gdTrueColorGetRed (p) * alpha_factor; - green += gdTrueColorGetGreen (p) * alpha_factor; - blue += gdTrueColorGetBlue (p) * alpha_factor; - alpha += gdTrueColorGetAlpha (p) * pcontribution; - alpha_sum += alpha_factor; - contrib_sum += pcontribution; - spixels += xportion * yportion; - sx += 1.0f; - } - while (sx < sx2); - - sy += 1.0f; - } - - while (sy < sy2); - - if (spixels != 0.0f) { - red /= spixels; - green /= spixels; - blue /= spixels; - alpha /= spixels; - } - if ( alpha_sum != 0.0f) { - if( contrib_sum != 0.0f) { - alpha_sum /= contrib_sum; - } - red /= alpha_sum; - green /= alpha_sum; - blue /= alpha_sum; - } - /* Round up closest next channel value and clamp to max channel value */ - red = red >= 255.5 ? 255 : red+0.5; - blue = blue >= 255.5 ? 255 : blue+0.5; - green = green >= 255.5 ? 255 : green+0.5; - alpha = alpha >= gdAlphaMax+0.5 ? gdAlphaMax : alpha+0.5; - gdImageSetPixel(dst, x, y, gdTrueColorAlpha ((int)red, (int)green, (int)blue, (int)alpha)); - } - } +#define floor2(exp) ((long)exp) +/** + * Function: gdImageCopyResampled + * + * Copy a resampled area from an image to another image + * + * If the source and destination area differ in size, the area will be resized + * using bilinear interpolation for truecolor images, and nearest-neighbor + * interpolation for palette images. + * + * Parameters: + * dst - The destination image. + * src - The source image. + * dstX - The x-coordinate of the upper left corner to copy to. + * dstY - The y-coordinate of the upper left corner to copy to. + * srcX - The x-coordinate of the upper left corner to copy from. + * srcY - The y-coordinate of the upper left corner to copy from. + * dstW - The width of the area to copy to. + * dstH - The height of the area to copy to. + * srcW - The width of the area to copy from. + * srcH - The height of the area to copy from. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageCopyResampled(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, + int dstW, int dstH, int srcW, int srcH) +{ + int x, y; + if (!dst->trueColor) { + gdImageCopyResized(dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); + return; + } + if (!gdImageClipCopyResized(dst, &dstX, &dstY, &dstW, &dstH, &srcX, &srcY, &srcW, &srcH)) { + return; + } + for (y = dstY; (y < dstY + dstH); y++) { + for (x = dstX; (x < dstX + dstW); x++) { + float sy1, sy2, sx1, sx2; + float sx, sy; + float spixels = 0.0; + float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; + float alpha_factor, alpha_sum = 0.0, contrib_sum = 0.0; + sy1 = ((float)(y - dstY)) * (float)srcH / (float)dstH; + sy2 = ((float)(y + 1 - dstY)) * (float)srcH / (float)dstH; + sy = sy1; + do { + float yportion; + if (floorf(sy) == floorf(sy1)) { + yportion = 1.0 - (sy - floorf(sy)); + if (yportion > sy2 - sy1) { + yportion = sy2 - sy1; + } + sy = floorf(sy); + } else if (sy == floorf(sy2)) { + yportion = sy2 - floorf(sy2); + } else { + yportion = 1.0; + } + sx1 = ((float)(x - dstX)) * (float)srcW / dstW; + sx2 = ((float)(x + 1 - dstX)) * (float)srcW / dstW; + sx = sx1; + do { + float xportion; + float pcontribution; + int p; + if (floorf(sx) == floorf(sx1)) { + xportion = 1.0 - (sx - floorf(sx)); + if (xportion > sx2 - sx1) { + xportion = sx2 - sx1; + } + sx = floorf(sx); + } else if (sx == floorf(sx2)) { + xportion = sx2 - floorf(sx2); + } else { + xportion = 1.0; + } + pcontribution = xportion * yportion; + p = gdImageGetTrueColorPixel(src, (int)sx + srcX, (int)sy + srcY); + + alpha_factor = ((gdAlphaMax - gdTrueColorGetAlpha(p))) * pcontribution; + red += gdTrueColorGetRed(p) * alpha_factor; + green += gdTrueColorGetGreen(p) * alpha_factor; + blue += gdTrueColorGetBlue(p) * alpha_factor; + alpha += gdTrueColorGetAlpha(p) * pcontribution; + alpha_sum += alpha_factor; + contrib_sum += pcontribution; + spixels += xportion * yportion; + sx += 1.0; + } while (sx < sx2); + sy += 1.0f; + } while (sy < sy2); + + if (spixels != 0.0) { + red /= spixels; + green /= spixels; + blue /= spixels; + alpha /= spixels; + } + if (alpha_sum != 0.0) { + if (contrib_sum != 0.0) { + alpha_sum /= contrib_sum; + } + red /= alpha_sum; + green /= alpha_sum; + blue /= alpha_sum; + } + /* Round up closest next channel value and clamp to max channel value */ + red = red >= 255.5 ? 255 : red + 0.5; + blue = blue >= 255.5 ? 255 : blue + 0.5; + green = green >= 255.5 ? 255 : green + 0.5; + alpha = alpha >= gdAlphaMax + 0.5 ? gdAlphaMax : alpha + 0.5; + gdImageSetPixel(dst, x, y, + gdTrueColorAlpha((int)red, (int)green, (int)blue, (int)alpha)); + } + } } -void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c) -{ - if (n <= 0) { - return; - } +/** + * Group: Polygons + */ +/** + * Function: gdImagePolygon + * + * Draws a closed polygon + * + * Parameters: + * im - The image. + * p - The vertices as array of s. + * n - The number of vertices. + * c - The color. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c) +{ + if (n <= 0) { + return; + } - gdImageLine (im, p->x, p->y, p[n - 1].x, p[n - 1].y, c); - gdImageOpenPolygon (im, p, n, c); + gdImageLine(im, p->x, p->y, p[n - 1].x, p[n - 1].y, c); + gdImageOpenPolygon(im, p, n, c); } -void gdImageOpenPolygon (gdImagePtr im, gdPointPtr p, int n, int c) +/** + * Function: gdImageOpenPolygon + * + * Draws an open polygon + * + * Parameters: + * im - The image. + * p - The vertices as array of s. + * n - The number of vertices. + * c - The color + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageOpenPolygon(gdImagePtr im, gdPointPtr p, int n, int c) { - int i; - int lx, ly; + int i; + int lx, ly; - if (n <= 0) { - return; - } + if (n <= 0) { + return; + } - lx = p->x; - ly = p->y; - for (i = 1; i < n; i++) { - p++; - gdImageLine(im, lx, ly, p->x, p->y, c); - lx = p->x; - ly = p->y; - } + lx = p->x; + ly = p->y; + for (i = 1; (i < n); i++) { + p++; + gdImageLine(im, lx, ly, p->x, p->y, c); + lx = p->x; + ly = p->y; + } } /* THANKS to Kirsten Schulz for the polygon fixes! */ -/* The intersection finding technique of this code could be improved - * by remembering the previous intertersection, and by using the slope. - * That could help to adjust intersections to produce a nice - * interior_extrema. - */ +/* The intersection finding technique of this code could be improved */ +/* by remembering the previous intersection, and by using the slope. */ +/* That could help to adjust intersections to produce a nice */ +/* interior_extrema. */ -void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c) -{ - int i; - int j; - int index; - int y; - int miny, maxy, pmaxy; - int x1, y1; - int x2, y2; - int ind1, ind2; - int ints; - int fill_color; - - if (n <= 0) { - return; - } - - if (overflow2(sizeof(int), n)) { - return; - } - - if (c == gdAntiAliased) { - fill_color = im->AA_color; - } else { - fill_color = c; - } - - if (!im->polyAllocated) { - im->polyInts = (int *) gdMalloc(sizeof(int) * n); - im->polyAllocated = n; - } - if (im->polyAllocated < n) { - while (im->polyAllocated < n) { - im->polyAllocated *= 2; - } - if (overflow2(sizeof(int), im->polyAllocated)) { - return; - } - im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated); - } - miny = p[0].y; - maxy = p[0].y; - for (i = 1; i < n; i++) { - if (p[i].y < miny) { - miny = p[i].y; - } - if (p[i].y > maxy) { - maxy = p[i].y; - } - } - /* necessary special case: horizontal line */ - if (n > 1 && miny == maxy) { - x1 = x2 = p[0].x; - for (i = 1; (i < n); i++) { - if (p[i].x < x1) { - x1 = p[i].x; - } else if (p[i].x > x2) { - x2 = p[i].x; - } - } - gdImageLine(im, x1, miny, x2, miny, c); - return; - } - pmaxy = maxy; - /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */ - /* 2.0.26: clipping rectangle is even better */ - if (miny < im->cy1) { - miny = im->cy1; - } - if (maxy > im->cy2) { - maxy = im->cy2; - } - /* Fix in 1.3: count a vertex only once */ - for (y = miny; y <= maxy; y++) { - /*1.4 int interLast = 0; */ - /* int dirLast = 0; */ - /* int interFirst = 1; */ - ints = 0; - for (i = 0; i < n; i++) { - if (!i) { - ind1 = n - 1; - ind2 = 0; - } else { - ind1 = i - 1; - ind2 = i; - } - y1 = p[ind1].y; - y2 = p[ind2].y; - if (y1 < y2) { - x1 = p[ind1].x; - x2 = p[ind2].x; - } else if (y1 > y2) { - y2 = p[ind1].y; - y1 = p[ind2].y; - x2 = p[ind1].x; - x1 = p[ind2].x; - } else { - continue; - } - /* Do the following math as float intermediately, and round to ensure - * that Polygon and FilledPolygon for the same set of points have the - * same footprint. - */ - if (y >= y1 && y < y2) { - im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1; - } else if (y == pmaxy && y == y2) { - im->polyInts[ints++] = x2; - } - } - /* - 2.0.26: polygons pretty much always have less than 100 points, - and most of the time they have considerably less. For such trivial - cases, insertion sort is a good choice. Also a good choice for - future implementations that may wish to indirect through a table. - */ - for (i = 1; (i < ints); i++) { - index = im->polyInts[i]; - j = i; - while ((j > 0) && (im->polyInts[j - 1] > index)) { - im->polyInts[j] = im->polyInts[j - 1]; - j--; - } - im->polyInts[j] = index; - } - for (i = 0; i < ints - 1; i += 2) { - gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color); - } - } +/** + * Function: gdImageFilledPolygon + * + * Draws a filled polygon + * + * The polygon is filled using the even-odd fillrule what can leave unfilled + * regions inside of self-intersecting polygons. This behavior might change in + * a future version. + * + * Parameters: + * im - The image. + * p - The vertices as array of s. + * n - The number of vertices. + * c - The color + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c) +{ + int i; + int j; + int index; + int y; + int miny, maxy, pmaxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + int fill_color; + + if (n <= 0) { + return; + } - /* If we are drawing this AA, then redraw the border with AA lines. */ - if (c == gdAntiAliased) { - gdImagePolygon(im, p, n, c); - } + if (c == gdAntiAliased) { + fill_color = im->AA_color; + } else { + fill_color = c; + } + if (!im->polyAllocated) { + if (overflow2(sizeof(int), n)) { + return; + } + im->polyInts = (int *)gdMalloc(sizeof(int) * n); + if (!im->polyInts) { + return; + } + im->polyAllocated = n; + } + if (im->polyAllocated < n) { + while (im->polyAllocated < n) { + im->polyAllocated *= 2; + } + if (overflow2(sizeof(int), im->polyAllocated)) { + return; + } + im->polyInts = (int *)gdReallocEx(im->polyInts, sizeof(int) * im->polyAllocated); + if (!im->polyInts) { + return; + } + } + miny = p[0].y; + maxy = p[0].y; + for (i = 1; (i < n); i++) { + if (p[i].y < miny) { + miny = p[i].y; + } + if (p[i].y > maxy) { + maxy = p[i].y; + } + } + /* necessary special case: horizontal line */ + if (n > 1 && miny == maxy) { + x1 = x2 = p[0].x; + for (i = 1; (i < n); i++) { + if (p[i].x < x1) { + x1 = p[i].x; + } else if (p[i].x > x2) { + x2 = p[i].x; + } + } + gdImageLine(im, x1, miny, x2, miny, c); + return; + } + pmaxy = maxy; + /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */ + /* 2.0.26: clipping rectangle is even better */ + if (miny < im->cy1) { + miny = im->cy1; + } + if (maxy > im->cy2) { + maxy = im->cy2; + } + /* Fix in 1.3: count a vertex only once */ + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = p[ind1].y; + y2 = p[ind2].y; + if (y1 < y2) { + x1 = p[ind1].x; + x2 = p[ind2].x; + } else if (y1 > y2) { + y2 = p[ind1].y; + y1 = p[ind2].y; + x2 = p[ind1].x; + x1 = p[ind2].x; + } else { + continue; + } + + /* Do the following math as float intermediately, and round to + * ensure that Polygon and FilledPolygon for the same set of points + * have the same footprint. */ + + if ((y >= y1) && (y < y2)) { + im->polyInts[ints++] = + (int)((float)((y - y1) * (x2 - x1)) / (float)(y2 - y1) + 0.5 + x1); + } else if ((y == pmaxy) && (y == y2)) { + im->polyInts[ints++] = x2; + } + } + /* + 2.0.26: polygons pretty much always have less than 100 points, + and most of the time they have considerably less. For such trivial + cases, insertion sort is a good choice. Also a good choice for + future implementations that may wish to indirect through a table. + */ + for (i = 1; (i < ints); i++) { + index = im->polyInts[i]; + j = i; + while ((j > 0) && (im->polyInts[j - 1] > index)) { + im->polyInts[j] = im->polyInts[j - 1]; + j--; + } + im->polyInts[j] = index; + } + for (i = 0; (i < (ints - 1)); i += 2) { + gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color); + } + } + /* If we are drawing this AA, then redraw the border with AA lines. */ + if (c == gdAntiAliased) { + gdImagePolygon(im, p, n, c); + } } -void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels) -{ - if (overflow2(sizeof (int), noOfPixels)) { - return; - } - if (im->style) { - gdFree(im->style); - } - im->style = (int *) gdMalloc(sizeof(int) * noOfPixels); - memcpy(im->style, style, sizeof(int) * noOfPixels); - im->styleLength = noOfPixels; - im->stylePos = 0; -} +/** + * Group: other + */ -void gdImageSetThickness (gdImagePtr im, int thickness) +static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t); + +/** + * Function: gdImageSetStyle + * + * Sets the style for following drawing operations + * + * Parameters: + * im - The image. + * style - An array of color values. + * noOfPixel - The number of color values. + */ +BGD_DECLARE(void) gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels) { - im->thick = thickness; + if (overflow2(sizeof(int), noOfPixels)) { + return; + } + if (im->style) { + gdFree(im->style); + } + im->style = (int *)gdMalloc(sizeof(int) * noOfPixels); + if (!im->style) { + return; + } + memcpy(im->style, style, sizeof(int) * noOfPixels); + im->styleLength = noOfPixels; + im->stylePos = 0; } -void gdImageSetBrush (gdImagePtr im, gdImagePtr brush) +/** + * Function: gdImageSetThickness + * + * Sets the thickness for following drawing operations + * + * Parameters: + * im - The image. + * thickness - The thickness in pixels. + */ +BGD_DECLARE(void) gdImageSetThickness(gdImagePtr im, int thickness) { im->thick = thickness; } + +/** + * Function: gdImageSetBrush + * + * Sets the brush for following drawing operations + * + * Parameters: + * im - The image. + * brush - The brush image. + */ +BGD_DECLARE(void) gdImageSetBrush(gdImagePtr im, gdImagePtr brush) { - int i; - im->brush = brush; - if (!im->trueColor && !im->brush->trueColor) { - for (i = 0; i < gdImageColorsTotal(brush); i++) { - int index; - index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), gdImageBlue(brush, i), gdImageAlpha(brush, i)); - im->brushColorMap[i] = index; - } - } + int i; + im->brush = brush; + if ((!im->trueColor) && (!im->brush->trueColor)) { + for (i = 0; (i < gdImageColorsTotal(brush)); i++) { + int index; + index = gdImageColorResolveAlpha(im, gdImageRed(brush, i), gdImageGreen(brush, i), + gdImageBlue(brush, i), gdImageAlpha(brush, i)); + im->brushColorMap[i] = index; + } + } } -void gdImageSetTile (gdImagePtr im, gdImagePtr tile) +/* + Function: gdImageSetTile +*/ +BGD_DECLARE(void) gdImageSetTile(gdImagePtr im, gdImagePtr tile) { - int i; - im->tile = tile; - if (!im->trueColor && !im->tile->trueColor) { - for (i = 0; i < gdImageColorsTotal(tile); i++) { - int index; - index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), gdImageBlue(tile, i), gdImageAlpha(tile, i)); - im->tileColorMap[i] = index; - } - } + int i; + im->tile = tile; + if ((!im->trueColor) && (!im->tile->trueColor)) { + for (i = 0; (i < gdImageColorsTotal(tile)); i++) { + int index; + index = gdImageColorResolveAlpha(im, gdImageRed(tile, i), gdImageGreen(tile, i), + gdImageBlue(tile, i), gdImageAlpha(tile, i)); + im->tileColorMap[i] = index; + } + } } -void gdImageSetAntiAliased (gdImagePtr im, int c) +/** + * Function: gdImageSetAntiAliased + * + * Set the color for subsequent anti-aliased drawing + * + * If is passed as color to drawing operations that support + * anti-aliased drawing (such as and ), the actual + * color to be used can be set with this function. + * + * Example: draw an anti-aliased blue line: + * | gdImageSetAntiAliased(im, gdTrueColorAlpha(0, 0, gdBlueMax, + * gdAlphaOpaque)); | gdImageLine(im, 10,10, 20,20, gdAntiAliased); + * + * Parameters: + * im - The image. + * c - The color. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageSetAntiAliased(gdImagePtr im, int c) { - im->AA = 1; - im->AA_color = c; - im->AA_dont_blend = -1; + im->AA = 1; + im->AA_color = c; + im->AA_dont_blend = -1; } -void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend) +/** + * Function: gdImageSetAntiAliasedDontBlend + * + * Set the color and "dont_blend" color for subsequent anti-aliased drawing + * + * This extended variant of allows to also specify a + * (background) color that will not be blended in anti-aliased drawing + * operations. + * + * Parameters: + * im - The image. + * c - The color. + * dont_blend - Whether to blend. + */ +BGD_DECLARE(void) gdImageSetAntiAliasedDontBlend(gdImagePtr im, int c, int dont_blend) { - im->AA = 1; - im->AA_color = c; - im->AA_dont_blend = dont_blend; + im->AA = 1; + im->AA_color = c; + im->AA_dont_blend = dont_blend; } - -void gdImageInterlace (gdImagePtr im, int interlaceArg) +/** + * Function: gdImageInterlace + * + * Sets whether an image is interlaced + * + * This is relevant only when saving the image in a format that supports + * interlacing. + * + * Parameters: + * im - The image. + * interlaceArg - Whether the image is interlaced. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageInterlace(gdImagePtr im, int interlaceArg) { - im->interlace = interlaceArg; + im->interlace = interlaceArg; } -int gdImageCompare (gdImagePtr im1, gdImagePtr im2) +/** + * Function: gdImageCompare + * + * Compare two images + * + * Parameters: + * im1 - An image. + * im2 - Another image. + * + * Returns: + * A bitmask of flags where each set flag signals + * which attributes of the images are different. + */ +BGD_DECLARE(int) gdImageCompare(gdImagePtr im1, gdImagePtr im2) { - int x, y; - int p1, p2; - int cmpStatus = 0; - int sx, sy; - - if (im1->interlace != im2->interlace) { - cmpStatus |= GD_CMP_INTERLACE; - } + int x, y; + int p1, p2; + int cmpStatus = 0; + int sx, sy; - if (im1->transparent != im2->transparent) { - cmpStatus |= GD_CMP_TRANSPARENT; - } + if (im1->interlace != im2->interlace) { + cmpStatus |= GD_CMP_INTERLACE; + } - if (im1->trueColor != im2->trueColor) { - cmpStatus |= GD_CMP_TRUECOLOR; - } + if (im1->transparent != im2->transparent) { + cmpStatus |= GD_CMP_TRANSPARENT; + } - sx = im1->sx; - if (im1->sx != im2->sx) { - cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE; - if (im2->sx < im1->sx) { - sx = im2->sx; - } - } + if (im1->trueColor != im2->trueColor) { + cmpStatus |= GD_CMP_TRUECOLOR; + } - sy = im1->sy; - if (im1->sy != im2->sy) { - cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE; - if (im2->sy < im1->sy) { - sy = im2->sy; - } - } + sx = im1->sx; + if (im1->sx != im2->sx) { + cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE; + if (im2->sx < im1->sx) { + sx = im2->sx; + } + } - if (im1->colorsTotal != im2->colorsTotal) { - cmpStatus |= GD_CMP_NUM_COLORS; - } + sy = im1->sy; + if (im1->sy != im2->sy) { + cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE; + if (im2->sy < im1->sy) { + sy = im2->sy; + } + } - for (y = 0; y < sy; y++) { - for (x = 0; x < sx; x++) { - p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y); - p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y); + if (im1->colorsTotal != im2->colorsTotal) { + cmpStatus |= GD_CMP_NUM_COLORS; + } - if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) { - cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; - break; - } - if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) { - cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; - break; - } - if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) { - cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; - break; - } + for (y = 0; (y < sy); y++) { + for (x = 0; (x < sx); x++) { + p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y); + p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y); + if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) { + cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; + break; + } + if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) { + cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; + break; + } + if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) { + cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE; + break; + } #if 0 /* Soon we'll add alpha channel to palettes */ if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) { @@ -3010,241 +3996,532 @@ int gdImageCompare (gdImagePtr im1, gdImagePtr im2) break; } #endif - } - if (cmpStatus & GD_CMP_COLOR) { - break; - } - } + } + if (cmpStatus & GD_CMP_COLOR) { + break; + }; + } - return cmpStatus; + return cmpStatus; } -int gdAlphaBlend (int dst, int src) { +/* Thanks to Frank Warmerdam for this superior implementation + of gdAlphaBlend(), which merges alpha in the + destination color much better. */ + +/** + * Function: gdAlphaBlend + * + * Blend two colors + * + * Parameters: + * dst - The color to blend onto. + * src - The color to blend. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(int) gdAlphaBlend(int dst, int src) +{ +#if ENABLE_CORRECTED_LEGACY_COMPOSITING + return gdCompositePixelToGd(gdCompositePixel(GD_OP_OVER, gdCompositePixelFromGd(src), + gdCompositePixelFromGd(dst), 1.0f)); +#else int src_alpha = gdTrueColorGetAlpha(src); int dst_alpha, alpha, red, green, blue; int src_weight, dst_weight, tot_weight; -/* -------------------------------------------------------------------- */ -/* Simple cases we want to handle fast. */ -/* -------------------------------------------------------------------- */ - if( src_alpha == gdAlphaOpaque ) + /* -------------------------------------------------------------------- */ + /* Simple cases we want to handle fast. */ + /* -------------------------------------------------------------------- */ + if (src_alpha == gdAlphaOpaque) return src; dst_alpha = gdTrueColorGetAlpha(dst); - if( src_alpha == gdAlphaTransparent ) + if (src_alpha == gdAlphaTransparent) return dst; - if( dst_alpha == gdAlphaTransparent ) + if (dst_alpha == gdAlphaTransparent) return src; -/* -------------------------------------------------------------------- */ -/* What will the source and destination alphas be? Note that */ -/* the destination weighting is substantially reduced as the */ -/* overlay becomes quite opaque. */ -/* -------------------------------------------------------------------- */ + /* -------------------------------------------------------------------- */ + /* What will the source and destination alphas be? Note that */ + /* the destination weighting is substantially reduced as the */ + /* overlay becomes quite opaque. */ + /* -------------------------------------------------------------------- */ src_weight = gdAlphaTransparent - src_alpha; dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax; tot_weight = src_weight + dst_weight; -/* -------------------------------------------------------------------- */ -/* What red, green and blue result values will we use? */ -/* -------------------------------------------------------------------- */ + /* -------------------------------------------------------------------- */ + /* What red, green and blue result values will we use? */ + /* -------------------------------------------------------------------- */ alpha = src_alpha * dst_alpha / gdAlphaMax; - red = (gdTrueColorGetRed(src) * src_weight - + gdTrueColorGetRed(dst) * dst_weight) / tot_weight; - green = (gdTrueColorGetGreen(src) * src_weight - + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight; - blue = (gdTrueColorGetBlue(src) * src_weight - + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight; + red = (gdTrueColorGetRed(src) * src_weight + gdTrueColorGetRed(dst) * dst_weight) / tot_weight; + green = (gdTrueColorGetGreen(src) * src_weight + gdTrueColorGetGreen(dst) * dst_weight) / + tot_weight; + blue = + (gdTrueColorGetBlue(src) * src_weight + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight; -/* -------------------------------------------------------------------- */ -/* Return merged result. */ -/* -------------------------------------------------------------------- */ + /* -------------------------------------------------------------------- */ + /* Return merged result. */ + /* -------------------------------------------------------------------- */ return ((alpha << 24) + (red << 16) + (green << 8) + blue); +#endif +} + +#if !ENABLE_CORRECTED_LEGACY_COMPOSITING +static int gdAlphaOverlayColor(int src, int dst, int max); +#endif + +/** + * Function: gdLayerOverlay + * + * Overlay two colors + * + * Parameters: + * dst - The color to overlay onto. + * src - The color to overlay. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(int) gdLayerOverlay(int dst, int src) +{ +#if ENABLE_CORRECTED_LEGACY_COMPOSITING + return gdCompositePixelToGd(gdCompositePixel(GD_OP_OVERLAY, gdCompositePixelFromGd(src), + gdCompositePixelFromGd(dst), 1.0f)); +#else + int a1, a2; + a1 = gdAlphaMax - gdTrueColorGetAlpha(dst); + a2 = gdAlphaMax - gdTrueColorGetAlpha(src); + return ( + ((gdAlphaMax - a1 * a2 / gdAlphaMax) << 24) + + (gdAlphaOverlayColor(gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax) << 16) + + (gdAlphaOverlayColor(gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax) << 8) + + (gdAlphaOverlayColor(gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax))); +#endif +} + +/* Apply 'overlay' effect - background pixels are colourised by the foreground + * colour */ +#if !ENABLE_CORRECTED_LEGACY_COMPOSITING +static int gdAlphaOverlayColor(int src, int dst, int max) +{ + dst = dst << 1; + if (dst > max) { + /* in the "light" zone */ + return dst + (src << 1) - (dst * src / max) - max; + } else { + /* in the "dark" zone */ + return dst * src / max; + } +} +#endif + +/** + * Function: gdLayerMultiply + * + * Overlay two colors with multiply effect + * + * Parameters: + * dst - The color to overlay onto. + * src - The color to overlay. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(int) gdLayerMultiply(int dst, int src) +{ +#if ENABLE_CORRECTED_LEGACY_COMPOSITING + return gdCompositePixelToGd(gdCompositePixel(GD_OP_MULTIPLY, gdCompositePixelFromGd(src), + gdCompositePixelFromGd(dst), 1.0f)); +#else + int a1, a2, r1, r2, g1, g2, b1, b2; + a1 = gdAlphaMax - gdTrueColorGetAlpha(src); + a2 = gdAlphaMax - gdTrueColorGetAlpha(dst); + + r1 = gdRedMax - (a1 * (gdRedMax - gdTrueColorGetRed(src))) / gdAlphaMax; + r2 = gdRedMax - (a2 * (gdRedMax - gdTrueColorGetRed(dst))) / gdAlphaMax; + g1 = gdGreenMax - (a1 * (gdGreenMax - gdTrueColorGetGreen(src))) / gdAlphaMax; + g2 = gdGreenMax - (a2 * (gdGreenMax - gdTrueColorGetGreen(dst))) / gdAlphaMax; + b1 = gdBlueMax - (a1 * (gdBlueMax - gdTrueColorGetBlue(src))) / gdAlphaMax; + b2 = gdBlueMax - (a2 * (gdBlueMax - gdTrueColorGetBlue(dst))) / gdAlphaMax; + + a1 = gdAlphaMax - a1; + a2 = gdAlphaMax - a2; + return (((a1 * a2 / gdAlphaMax) << 24) + ((r1 * r2 / gdRedMax) << 16) + + ((g1 * g2 / gdGreenMax) << 8) + ((b1 * b2 / gdBlueMax))); +#endif +} + +/** + * Function: gdImageAlphaBlending + * + * Set the effect for subsequent drawing operations + * + * Note that the effect is used for truecolor images only. + * + * Parameters: + * im - The image. + * alphaBlendingArg - The effect. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg) +{ + im->alphaBlendingFlag = alphaBlendingArg; +} + +/** + * Function: gdImageSaveAlpha + * + * Sets the save alpha flag + * + * The save alpha flag specifies whether the alpha channel of the pixels should + * be saved. This is supported only for image formats that support full alpha + * transparency, e.g. PNG. + */ +BGD_DECLARE(void) gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg) +{ + im->saveAlphaFlag = saveAlphaArg; +} +/** + * Function: gdImageSetClip + * + * Sets the clipping rectangle + * + * The clipping rectangle restricts the drawing area for following drawing + * operations. + * + * Parameters: + * im - The image. + * x1 - The x-coordinate of the upper left corner. + * y1 - The y-coordinate of the upper left corner. + * x2 - The x-coordinate of the lower right corner. + * y2 - The y-coordinate of the lower right corner. + * + * See also: + * - + */ +BGD_DECLARE(void) +gdImageSetClip(gdImagePtr im, int x1, int y1, int x2, int y2) +{ + if (x1 < 0) { + x1 = 0; + } + if (x1 >= im->sx) { + x1 = im->sx - 1; + } + if (x2 < 0) { + x2 = 0; + } + if (x2 >= im->sx) { + x2 = im->sx - 1; + } + if (y1 < 0) { + y1 = 0; + } + if (y1 >= im->sy) { + y1 = im->sy - 1; + } + if (y2 < 0) { + y2 = 0; + } + if (y2 >= im->sy) { + y2 = im->sy - 1; + } + im->cx1 = x1; + im->cy1 = y1; + im->cx2 = x2; + im->cy2 = y2; } -void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg) -{ - im->alphaBlendingFlag = alphaBlendingArg; +/** + * Function: gdImageGetClip + * + * Gets the current clipping rectangle + * + * Parameters: + * im - The image. + * x1P - (out) The x-coordinate of the upper left corner. + * y1P - (out) The y-coordinate of the upper left corner. + * x2P - (out) The x-coordinate of the lower right corner. + * y2P - (out) The y-coordinate of the lower right corner. + * + * See also: + * - + */ +BGD_DECLARE(void) gdImageGetClip(gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P) +{ + *x1P = im->cx1; + *y1P = im->cy1; + *x2P = im->cx2; + *y2P = im->cy2; } -void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg) +/** + * Function: gdImageSetResolution + * + * Sets the resolution of an image. + * + * Parameters: + * im - The image. + * res_x - The horizontal resolution in DPI. + * res_y - The vertical resolution in DPI. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y) { - im->saveAlphaFlag = saveAlphaArg; + if (res_x > 0) + im->res_x = res_x; + if (res_y > 0) + im->res_y = res_y; } -int gdLayerOverlay (int dst, int src) +#define BLEND_COLOR(a, nc, c, cc) \ + nc = (cc) + (((((c) - (cc)) * (a)) + ((((c) - (cc)) * (a)) >> 8) + 0x80) >> 8); + +static void gdImageSetAAPixelColor(gdImagePtr im, int x, int y, int color, int t) { - int a1, a2; - a1 = gdAlphaMax - gdTrueColorGetAlpha(dst); - a2 = gdAlphaMax - gdTrueColorGetAlpha(src); - return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) + - (gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) + - (gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) + - (gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax )) - ); + int dr, dg, db, p, r, g, b; + + /* 2.0.34: watch out for out of range calls */ + if (!gdImageBoundsSafeMacro(im, x, y)) { + return; + } + p = gdImageGetPixel(im, x, y); + /* TBB: we have to implement the dont_blend stuff to provide + the full feature set of the old implementation */ + if ((p == color) || ((p == im->AA_dont_blend) && (t != 0x00))) { + return; + } + dr = gdTrueColorGetRed(color); + dg = gdTrueColorGetGreen(color); + db = gdTrueColorGetBlue(color); + + r = gdTrueColorGetRed(p); + g = gdTrueColorGetGreen(p); + b = gdTrueColorGetBlue(p); + + BLEND_COLOR(t, dr, r, dr); + BLEND_COLOR(t, dg, g, dg); + BLEND_COLOR(t, db, b, db); + im->tpixels[y][x] = gdTrueColorAlpha(dr, dg, db, gdAlphaOpaque); } -static int gdAlphaOverlayColor (int src, int dst, int max ) +BGD_DECLARE(void) gdImageAALine(gdImagePtr im, int x1, int y1, int x2, int y2, int col) { - /* this function implements the algorithm - * - * for dst[rgb] < 0.5, - * c[rgb] = 2.src[rgb].dst[rgb] - * and for dst[rgb] > 0.5, - * c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1 - * - */ + /* keep them as 32bits */ + long x, y, inc, frac; + long dx, dy, tmp; + int w, wid, wstart; + int thick = im->thick; + + if (!im->trueColor) { + /* TBB: don't crash when the image is of the wrong type */ + gdImageLine(im, x1, y1, x2, y2, col); + return; + } + + /* TBB: use the clipping rectangle */ + if (clip_1d(&x1, &y1, &x2, &y2, im->cx1, im->cx2) == 0) + return; + if (clip_1d(&y1, &x1, &y2, &x2, im->cy1, im->cy2) == 0) + return; + + dx = x2 - x1; + dy = y2 - y1; + + if (dx == 0 && dy == 0) { + /* TBB: allow setting points */ + gdImageSetPixel(im, x1, y1, col); + return; + } else { + double ag; + /* Cast the long to an int to avoid compiler warnings about truncation. + * This isn't a problem as computed dy/dx values came from ints above. + */ + ag = fabs(abs((int)dy) < abs((int)dx) ? cos(atan2(dy, dx)) : sin(atan2(dy, dx))); + if (ag != 0) { + wid = thick / ag; + } else { + wid = 1; + } + if (wid == 0) { + wid = 1; + } + } - dst = dst << 1; - if( dst > max ) { - /* in the "light" zone */ - return dst + (src << 1) - (dst * src / max) - max; - } else { - /* in the "dark" zone */ - return dst * src / max; - } + /* Axis aligned lines */ + if (dx == 0) { + gdImageVLine(im, x1, y1, y2, col); + return; + } else if (dy == 0) { + gdImageHLine(im, y1, x1, x2, col); + return; + } + + if (abs((int)dx) > abs((int)dy)) { + if (dx < 0) { + tmp = x1; + x1 = x2; + x2 = tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + dx = x2 - x1; + dy = y2 - y1; + } + y = y1; + inc = (dy * 65536) / dx; + frac = 0; + /* TBB: set the last pixel for consistency (<=) */ + for (x = x1; x <= x2; x++) { + wstart = y - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetAAPixelColor(im, x, w, col, (frac >> 8) & 0xFF); + gdImageSetAAPixelColor(im, x, w + 1, col, (~frac >> 8) & 0xFF); + } + frac += inc; + if (frac >= 65536) { + frac -= 65536; + y++; + } else if (frac < 0) { + frac += 65536; + y--; + } + } + } else { + if (dy < 0) { + tmp = x1; + x1 = x2; + x2 = tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + dx = x2 - x1; + dy = y2 - y1; + } + x = x1; + inc = (dx * 65536) / dy; + frac = 0; + /* TBB: set the last pixel for consistency (<=) */ + for (y = y1; y <= y2; y++) { + wstart = x - wid / 2; + for (w = wstart; w < wstart + wid; w++) { + gdImageSetAAPixelColor(im, w, y, col, (frac >> 8) & 0xFF); + gdImageSetAAPixelColor(im, w + 1, y, col, (~frac >> 8) & 0xFF); + } + frac += inc; + if (frac >= 65536) { + frac -= 65536; + x++; + } else if (frac < 0) { + frac += 65536; + x--; + } + } + } } -int gdLayerMultiply (int dst, int src) -{ - int a1, a2, r1, r2, g1, g2, b1, b2; - a1 = gdAlphaMax - gdTrueColorGetAlpha(src); - a2 = gdAlphaMax - gdTrueColorGetAlpha(dst); - - r1 = gdRedMax - (a1 * (gdRedMax - gdTrueColorGetRed(src))) / gdAlphaMax; - r2 = gdRedMax - (a2 * (gdRedMax - gdTrueColorGetRed(dst))) / gdAlphaMax; - g1 = gdGreenMax - (a1 * (gdGreenMax - gdTrueColorGetGreen(src))) / gdAlphaMax; - g2 = gdGreenMax - (a2 * (gdGreenMax - gdTrueColorGetGreen(dst))) / gdAlphaMax; - b1 = gdBlueMax - (a1 * (gdBlueMax - gdTrueColorGetBlue(src))) / gdAlphaMax; - b2 = gdBlueMax - (a2 * (gdBlueMax - gdTrueColorGetBlue(dst))) / gdAlphaMax ; - - a1 = gdAlphaMax - a1; - a2 = gdAlphaMax - a2; - return ( ((a1*a2/gdAlphaMax) << 24) + - ((r1*r2/gdRedMax) << 16) + - ((g1*g2/gdGreenMax) << 8) + - ((b1*b2/gdBlueMax)) - ); -} - -void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2) -{ - if (x1 < 0) { - x1 = 0; - } - if (x1 >= im->sx) { - x1 = im->sx - 1; - } - if (x2 < 0) { - x2 = 0; - } - if (x2 >= im->sx) { - x2 = im->sx - 1; - } - if (y1 < 0) { - y1 = 0; - } - if (y1 >= im->sy) { - y1 = im->sy - 1; - } - if (y2 < 0) { - y2 = 0; - } - if (y2 >= im->sy) { - y2 = im->sy - 1; - } - im->cx1 = x1; - im->cy1 = y1; - im->cx2 = x2; - im->cy2 = y2; -} - -void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P) -{ - *x1P = im->cx1; - *y1P = im->cy1; - *x2P = im->cx2; - *y2P = im->cy2; -} - -void gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y) -{ - if (res_x > 0) im->res_x = res_x; - if (res_y > 0) im->res_y = res_y; -} - -/* convert a palette image to true color */ -int gdImagePaletteToTrueColor(gdImagePtr src) -{ - unsigned int y; - unsigned int yy; - - if (src == NULL) { - return 0; - } - - if (src->trueColor == 1) { - return 1; - } else { - unsigned int x; - const unsigned int sy = gdImageSY(src); - const unsigned int sx = gdImageSX(src); - - // Note: do not revert back to gdMalloc() below ; reason here, - // due to a bug with a certain memory_limit INI value treshold, - // imagepalettetotruecolor crashes with even unrelated ZendMM allocations. - // See GH-17772 for a use case. - src->tpixels = (int **) gdCalloc(sy, sizeof(int *)); - if (src->tpixels == NULL) { - return 0; - } - - for (y = 0; y < sy; y++) { - const unsigned char *src_row = src->pixels[y]; - int * dst_row; - - /* no need to calloc it, we overwrite all pxl anyway */ - src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int)); - if (src->tpixels[y] == NULL) { - goto clean_on_error; - } +/** + * Function: gdImagePaletteToTrueColor + * + * Convert a palette image to true color + * + * Parameters: + * src - The image. + * + * Returns: + * Non-zero if the conversion succeeded, zero otherwise. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImagePaletteToTrueColor(gdImagePtr src) +{ + unsigned int y; + unsigned int yy; - dst_row = src->tpixels[y]; - for (x = 0; x < sx; x++) { - const unsigned char c = *(src_row + x); - if (c == src->transparent) { - *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127); - } else { - *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); - } - } - } - } - - /* free old palette buffer (y is sy) */ - for (yy = 0; yy < y; yy++) { - gdFree(src->pixels[yy]); - } - gdFree(src->pixels); - src->trueColor = 1; - src->pixels = NULL; - src->alphaBlendingFlag = 0; - src->saveAlphaFlag = 1; - - if (src->transparent >= 0) { - const unsigned char c = src->transparent; - src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); - } - - return 1; + if (src == NULL) { + return 0; + } + + if (src->trueColor == 1) { + return 1; + } else { + unsigned int x; + const unsigned int sy = gdImageSY(src); + const unsigned int sx = gdImageSX(src); + + // Note: do not revert back to gdMalloc() below ; reason here, + // due to a bug with a certain memory_limit INI value treshold, + // imagepalettetotruecolor crashes with even unrelated ZendMM allocations. + // See GH-17772 for a use case. + src->tpixels = (int **)gdCalloc(sy, sizeof(int *)); + if (src->tpixels == NULL) { + return 0; + } + + for (y = 0; y < sy; y++) { + const unsigned char *src_row = src->pixels[y]; + int *dst_row; + + /* no need to calloc it, we overwrite all pxl anyway */ + src->tpixels[y] = (int *)gdMalloc(sx * sizeof(int)); + if (src->tpixels[y] == NULL) { + goto clean_on_error; + } + + dst_row = src->tpixels[y]; + for (x = 0; x < sx; x++) { + const unsigned char c = *(src_row + x); + if (c == src->transparent) { + *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127); + } else { + *(dst_row + x) = + gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); + } + } + } + } + + /* free old palette buffer (y is sy) */ + for (yy = 0; yy < y; yy++) { + gdFree(src->pixels[yy]); + } + gdFree(src->pixels); + src->trueColor = 1; + src->pixels = NULL; + src->alphaBlendingFlag = 0; + src->saveAlphaFlag = 1; + + if (src->transparent >= 0) { + const unsigned char c = src->transparent; + src->transparent = + gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); + } + + return 1; clean_on_error: - /* free new true color buffer (y is not allocated, have failed) */ - for (yy = 0; yy < y; yy++) { - gdFree(src->tpixels[yy]); - } - gdFree(src->tpixels); - return 0; + /* free new true color buffer (y is not allocated, have failed) */ + for (yy = 0; yy < y; yy++) { + gdFree(src->tpixels[yy]); + } + gdFree(src->tpixels); + return 0; } diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 5325a6370925..dc8728fcaec1 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -12,10 +12,25 @@ extern "C" { #include "php_compat.h" #define GD_MAJOR_VERSION 2 -#define GD_MINOR_VERSION 0 -#define GD_RELEASE_VERSION 35 +#define GD_MINOR_VERSION 4 +#define GD_RELEASE_VERSION 0 #define GD_EXTRA_VERSION "" -#define GD_VERSION_STRING "2.0.35" + +/* Bundled libgd has no separate symbol visibility requirements. */ +#ifndef BGD_EXPORT_DATA_PROT +#define BGD_EXPORT_DATA_PROT +#endif +#ifndef BGD_STDCALL +#define BGD_STDCALL +#endif +#ifndef BGD_DECLARE +#define BGD_DECLARE(rt) rt +#endif +#ifndef ARG_NOT_USED +#define ARG_NOT_USED(arg) (void)(arg) +#endif + +#define GD_VERSION_STRING "2.4.0" /* gd.h: declarations file for the graphic-draw module. * Permission to use, copy, modify, and distribute this software and its @@ -29,38 +44,36 @@ extern "C" { * documentation. */ /* stdio is needed for file I/O. */ -#include #include "gd_io.h" - -/* va_list needed in gdErrorMethod */ #include +#include /* The maximum number of palette entries in palette-based images. - In the wonderful new world of gd 2.0, you can of course have - many more colors when using truecolor mode. */ + In the wonderful new world of gd 2.0, you can of course have + many more colors when using truecolor mode. */ #define gdMaxColors 256 /* Image type. See functions below; you will not need to change - the elements directly. Use the provided macros to - access sx, sy, the color table, and colorsTotal for - read-only purposes. */ + the elements directly. Use the provided macros to + access sx, sy, the color table, and colorsTotal for + read-only purposes. */ /* If 'truecolor' is set true, the image is truecolor; - pixels are represented by integers, which - must be 32 bits wide or more. + pixels are represented by integers, which + must be 32 bits wide or more. - True colors are repsented as follows: + True colors are represented as follows: - ARGB + ARGB - Where 'A' (alpha channel) occupies only the - LOWER 7 BITS of the MSB. This very small - loss of alpha channel resolution allows gd 2.x - to keep backwards compatibility by allowing - signed integers to be used to represent colors, - and negative numbers to represent special cases, - just as in gd 1.x. */ + Where 'A' (alpha channel) occupies only the + LOWER 7 BITS of the MSB. This very small + loss of alpha channel resolution allows gd 2.x + to keep backwards compatibility by allowing + signed integers to be used to represent colors, + and negative numbers to represent special cases, + just as in gd 1.x. */ #define gdAlphaMax 127 #define gdAlphaOpaque 0 @@ -68,10 +81,85 @@ extern "C" { #define gdRedMax 255 #define gdGreenMax 255 #define gdBlueMax 255 + +/** + * Group: Color Decomposition + */ + +/** + * Macro: gdTrueColorGetAlpha + * + * Gets the alpha channel value + * + * Parameters: + * c - The color + * + * See also: + * - + */ #define gdTrueColorGetAlpha(c) (((c) & 0x7F000000) >> 24) + +/** + * Macro: gdTrueColorGetRed + * + * Gets the red channel value + * + * Parameters: + * c - The color + * + * See also: + * - + */ #define gdTrueColorGetRed(c) (((c) & 0xFF0000) >> 16) + +/** + * Macro: gdTrueColorGetGreen + * + * Gets the green channel value + * + * Parameters: + * c - The color + * + * See also: + * - + */ #define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8) + +/** + * Macro: gdTrueColorGetBlue + * + * Gets the blue channel value + * + * Parameters: + * c - The color + * + * See also: + * - + */ #define gdTrueColorGetBlue(c) ((c) & 0x0000FF) + +/** + * Group: Effects + * + * The layering effect + * + * When pixels are drawn the new colors are "mixed" with the background + * depending on the effect. + * + * Note that the effect does not apply to palette images, where pixels + * are always replaced. + * + * Modes: + * gdEffectReplace - replace pixels + * gdEffectAlphaBlend - blend pixels, see + * gdEffectNormal - default mode; same as gdEffectAlphaBlend + * gdEffectOverlay - overlay pixels, see + * gdEffectMultiply - overlay pixels with multiply effect, see + * + * + * See also: + * - + */ #define gdEffectReplace 0 #define gdEffectAlphaBlend 1 #define gdEffectNormal 2 @@ -82,157 +170,250 @@ extern "C" { #define GD_FALSE 0 #define GD_EPSILON 1e-6 +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* This function accepts truecolor pixel values only. The - source color is composited with the destination color - based on the alpha channel value of the source color. - The resulting color is opaque. */ + source color is composited with the destination color + based on the alpha channel value of the source color. + The resulting color is opaque. */ -int gdAlphaBlend(int dest, int src); -int gdLayerOverlay(int dst, int src); -int gdLayerMultiply(int dest, int src); +BGD_DECLARE(int) gdAlphaBlend(int dest, int src); +BGD_DECLARE(int) gdLayerOverlay(int dest, int src); +BGD_DECLARE(int) gdLayerMultiply(int dest, int src); /** * Group: Transform * * Constants: gdInterpolationMethod - - * GD_BELL - Bell + * + * GD_BELL - Bell * GD_BESSEL - Bessel - * GD_BILINEAR_FIXED - fixed point bilinear + * GD_BILINEAR_FIXED - compatibility alias for bilinear * GD_BICUBIC - Bicubic - * GD_BICUBIC_FIXED - fixed point bicubic integer + * GD_BICUBIC_FIXED - compatibility alias for bicubic * GD_BLACKMAN - Blackman - * GD_BOX - Box + * GD_BOX - Box * GD_BSPLINE - BSpline * GD_CATMULLROM - Catmullrom * GD_GAUSSIAN - Gaussian - * GD_GENERALIZED_CUBIC - Generalized cubic + * GD_GENERALIZED_CUBIC - Generalized cubic * GD_HERMITE - Hermite * GD_HAMMING - Hamming * GD_HANNING - Hannig * GD_MITCHELL - Mitchell - * GD_NEAREST_NEIGHBOUR - Nearest neighbour interpolation + * GD_NEAREST_NEIGHBOUR - Nearest neighbour interpolation * GD_POWER - Power * GD_QUADRATIC - Quadratic - * GD_SINC - Sinc + * GD_SINC - Sinc * GD_TRIANGLE - Triangle * GD_WEIGHTED4 - 4 pixels weighted bilinear interpolation + * GD_LINEAR - bilinear interpolation * * See also: - * - **/ + * - + */ typedef enum { - GD_DEFAULT = 0, - GD_BELL, - GD_BESSEL, - GD_BILINEAR_FIXED, - GD_BICUBIC, - GD_BICUBIC_FIXED, - GD_BLACKMAN, - GD_BOX, - GD_BSPLINE, - GD_CATMULLROM, - GD_GAUSSIAN, - GD_GENERALIZED_CUBIC, - GD_HERMITE, - GD_HAMMING, - GD_HANNING, - GD_MITCHELL, - GD_NEAREST_NEIGHBOUR, - GD_POWER, - GD_QUADRATIC, - GD_SINC, - GD_TRIANGLE, - GD_WEIGHTED4, - GD_LINEAR, - GD_METHOD_COUNT = 22 + GD_DEFAULT = 0, + GD_BELL, + GD_BESSEL, + GD_BILINEAR_FIXED, + GD_BICUBIC, + GD_BICUBIC_FIXED, + GD_BLACKMAN, + GD_BOX, + GD_BSPLINE, + GD_CATMULLROM, + GD_GAUSSIAN, + GD_GENERALIZED_CUBIC, + GD_HERMITE, + GD_HAMMING, + GD_HANNING, + GD_MITCHELL, + GD_NEAREST_NEIGHBOUR, + GD_POWER, + GD_QUADRATIC, + GD_SINC, + GD_TRIANGLE, + GD_WEIGHTED4, + GD_LINEAR, + GD_LANCZOS3, + GD_LANCZOS8, + GD_BLACKMAN_BESSEL, + GD_BLACKMAN_SINC, + GD_QUADRATIC_BSPLINE, + GD_CUBIC_SPLINE, + GD_COSINE, + GD_WELSH, + GD_METHOD_COUNT = 30 } gdInterpolationMethod; -/* define struct with name and func ptr and add it to gdImageStruct gdInterpolationMethod interpolation; */ +/* define struct with name and func ptr and add it to gdImageStruct + * gdInterpolationMethod interpolation; */ /* Interpolation function ptr */ -typedef double (* interpolation_method )(double); +typedef double (*interpolation_method)(double, double); + +/* + Group: Types + + typedef: gdImage + + typedef: gdImagePtr + The data structure in which gd stores images. , + and the various image file-loading functions + return a pointer to this type, and the other functions expect to + receive a pointer to this type as their first argument. + + *gdImagePtr* is a pointer to *gdImage*. + + See also: + + + (Previous versions of this library encouraged directly manipulating + the contents of the struct but we are attempting to move away from + this practice so the fields are no longer documented here. If you + need to poke at the internals of this struct, feel free to look at + *gd.h*.) +*/ typedef struct gdImageStruct { - /* Palette-based image pixels */ - unsigned char ** pixels; - int sx; - int sy; - /* These are valid in palette images only. See also - 'alpha', which appears later in the structure to - preserve binary backwards compatibility */ - int colorsTotal; - int red[gdMaxColors]; - int green[gdMaxColors]; - int blue[gdMaxColors]; - int open[gdMaxColors]; - /* For backwards compatibility, this is set to the - first palette entry with 100% transparency, - and is also set and reset by the - gdImageColorTransparent function. Newer - applications can allocate palette entries - with any desired level of transparency; however, - bear in mind that many viewers, notably - many web browsers, fail to implement - full alpha channel for PNG and provide - support for full opacity or transparency only. */ - int transparent; - int *polyInts; - int polyAllocated; - struct gdImageStruct *brush; - struct gdImageStruct *tile; - int brushColorMap[gdMaxColors]; - int tileColorMap[gdMaxColors]; - int styleLength; - int stylePos; - int *style; - int interlace; - /* New in 2.0: thickness of line. Initialized to 1. */ - int thick; - /* New in 2.0: alpha channel for palettes. Note that only - Macintosh Internet Explorer and (possibly) Netscape 6 - really support multiple levels of transparency in - palettes, to my knowledge, as of 2/15/01. Most - common browsers will display 100% opaque and - 100% transparent correctly, and do something - unpredictable and/or undesirable for levels - in between. TBB */ - int alpha[gdMaxColors]; - /* Truecolor flag and pixels. New 2.0 fields appear here at the - end to minimize breakage of existing object code. */ - int trueColor; - int ** tpixels; - /* Should alpha channel be copied, or applied, each time a - pixel is drawn? This applies to truecolor images only. - No attempt is made to alpha-blend in palette images, - even if semitransparent palette entries exist. - To do that, build your image as a truecolor image, - then quantize down to 8 bits. */ - int alphaBlendingFlag; - /* Should the alpha channel of the image be saved? This affects - PNG at the moment; other future formats may also - have that capability. JPEG doesn't. */ - int saveAlphaFlag; - - /* 2.0.12: anti-aliased globals. 2.0.26: just a few vestiges after - switching to the fast, memory-cheap implementation from PHP-gd. */ - int AA; - int AA_color; - int AA_dont_blend; - - /* 2.0.12: simple clipping rectangle. These values must be checked for safety when set; please use gdImageSetClip */ - int cx1; - int cy1; - int cx2; - int cy2; - unsigned int res_x; - unsigned int res_y; - gdInterpolationMethod interpolation_id; - interpolation_method interpolation; + /* Palette-based image pixels */ + unsigned char **pixels; + int sx; + int sy; + /* These are valid in palette images only. See also + 'alpha', which appears later in the structure to + preserve binary backwards compatibility */ + int colorsTotal; + int red[gdMaxColors]; + int green[gdMaxColors]; + int blue[gdMaxColors]; + int open[gdMaxColors]; + /* For backwards compatibility, this is set to the + first palette entry with 100% transparency, + and is also set and reset by the + gdImageColorTransparent function. Newer + applications can allocate palette entries + with any desired level of transparency; however, + bear in mind that many viewers, notably + many web browsers, fail to implement + full alpha channel for PNG and provide + support for full opacity or transparency only. */ + int transparent; + int *polyInts; + int polyAllocated; + struct gdImageStruct *brush; + struct gdImageStruct *tile; + int brushColorMap[gdMaxColors]; + int tileColorMap[gdMaxColors]; + int styleLength; + int stylePos; + int *style; + int interlace; + /* New in 2.0: thickness of line. Initialized to 1. */ + int thick; + /* New in 2.0: alpha channel for palettes. Note that only + Macintosh Internet Explorer and (possibly) Netscape 6 + really support multiple levels of transparency in + palettes, to my knowledge, as of 2/15/01. Most + common browsers will display 100% opaque and + 100% transparent correctly, and do something + unpredictable and/or undesirable for levels + in between. TBB */ + int alpha[gdMaxColors]; + /* Truecolor flag and pixels. New 2.0 fields appear here at the + end to minimize breakage of existing object code. */ + int trueColor; + int **tpixels; + /* Should alpha channel be copied, or applied, each time a + pixel is drawn? This applies to truecolor images only. + No attempt is made to alpha-blend in palette images, + even if semitransparent palette entries exist. + To do that, build your image as a truecolor image, + then quantize down to 8 bits. */ + int alphaBlendingFlag; + /* Should the alpha channel of the image be saved? This affects + PNG at the moment; other future formats may also + have that capability. JPEG doesn't. */ + int saveAlphaFlag; + + /* There should NEVER BE ACCESSOR MACROS FOR ITEMS BELOW HERE, so this + part of the structure can be safely changed in new releases. */ + + /* 2.0.12: anti-aliased globals. 2.0.26: just a few vestiges after + switching to the fast, memory-cheap implementation from PHP-gd. */ + int AA; + int AA_color; + int AA_dont_blend; + + /* 2.0.12: simple clipping rectangle. These values + must be checked for safety when set; please use + gdImageSetClip */ + int cx1; + int cy1; + int cx2; + int cy2; + + /* 2.1.0: allows to specify resolution in dpi */ + unsigned int res_x; + unsigned int res_y; + + /* Selects quantization method, see gdImageTrueColorToPaletteSetMethod() and + * gdPaletteQuantizationMethod enum. */ + int paletteQuantizationMethod; + /* speed/quality trade-off. 1 = best quality, 10 = best speed. 0 = + method-specific default. Applicable to GD_QUANT_LIQ and + GD_QUANT_NEUQUANT. */ + int paletteQuantizationSpeed; + /* Image will remain true-color if conversion to palette cannot achieve + given quality. Value from 1 to 100, 1 = ugly, 100 = perfect. Applicable + to GD_QUANT_LIQ.*/ + int paletteQuantizationMinQuality; + /* Image will use minimum number of palette colors needed to achieve given + quality. Must be higher than paletteQuantizationMinQuality Value from 1 + to 100, 1 = ugly, 100 = perfect. Applicable to GD_QUANT_LIQ.*/ + int paletteQuantizationMaxQuality; + gdInterpolationMethod interpolation_id; + interpolation_method interpolation; } gdImage; -typedef gdImage * gdImagePtr; +typedef gdImage *gdImagePtr; + +typedef struct gdImageMetadata gdImageMetadata; + +#define GD_META_OK 0 +#define GD_META_ERR_FORMAT -1 +#define GD_META_ERR_PARSE -2 +#define GD_META_ERR_NOMEM -3 +#define GD_META_ERR_LIMIT -4 +#define GD_META_ERR_UNSUPPORTED -5 +#define GD_META_ERR_INVALID -6 + +#define GD_METADATA_DEFAULT_MAX_PROFILE_SIZE ((size_t)64 * 1024 * 1024) +#define GD_METADATA_DEFAULT_MAX_TOTAL_SIZE ((size_t)256 * 1024 * 1024) + +BGD_DECLARE(gdImageMetadata *) gdImageMetadataCreate(void); +BGD_DECLARE(void) gdImageMetadataFree(gdImageMetadata *metadata); +BGD_DECLARE(void) gdImageMetadataReset(gdImageMetadata *metadata); +BGD_DECLARE(int) +gdImageMetadataSetLimits(gdImageMetadata *metadata, size_t max_profile_size, size_t max_total_size); +BGD_DECLARE(void) +gdImageMetadataGetLimits(const gdImageMetadata *metadata, size_t *max_profile_size, + size_t *max_total_size); +BGD_DECLARE(int) +gdImageMetadataSetProfile(gdImageMetadata *metadata, const char *key, const unsigned char *data, + size_t size); +BGD_DECLARE(const unsigned char *) +gdImageMetadataGetProfile(const gdImageMetadata *metadata, const char *key, size_t *size); +BGD_DECLARE(int) +gdImageMetadataRemoveProfile(gdImageMetadata *metadata, const char *key); +BGD_DECLARE(size_t) +gdImageMetadataGetProfileCount(const gdImageMetadata *metadata); +BGD_DECLARE(int) +gdImageMetadataGetProfileAt(const gdImageMetadata *metadata, size_t index, const char **key, + const unsigned char **data, size_t *size); /* Point type for use in polygon drawing. */ @@ -251,64 +432,104 @@ typedef gdImage * gdImagePtr; * See also: * , , **/ -typedef struct -{ - double x, y; -} -gdPointF, *gdPointFPtr; +typedef struct { + double x, y; +} gdPointF, *gdPointFPtr; + +/* + Group: Types + + typedef: gdFont + + typedef: gdFontPtr + + A font structure, containing the bitmaps of all characters in a + font. Used to declare the characteristics of a font. Text-output + functions expect these as their second argument, following the + argument. and both + return one. + + You can provide your own font data by providing such a structure and + the associated pixel array. You can determine the width and height + of a single character in a font by examining the w and h members of + the structure. If you will not be creating your own fonts, you will + not need to concern yourself with the rest of the components of this + structure. + + Please see the files gdfontl.c and gdfontl.h for an example of + the proper declaration of this structure. + + > typedef struct { + > // # of characters in font + > int nchars; + > // First character is numbered... (usually 32 = space) + > int offset; + > // Character width and height + > int w; + > int h; + > // Font data; array of characters, one row after another. + > // Easily included in code, also easily loaded from + > // data files. + > char *data; + > } gdFont; + + gdFontPtr is a pointer to gdFont. +*/ typedef struct { - /* # of characters in font */ - int nchars; - /* First character is numbered... (usually 32 = space) */ - int offset; - /* Character width and height */ - int w; - int h; - /* Font data; array of characters, one row after another. - Easily included in code, also easily loaded from - data files. */ - char *data; + /* # of characters in font */ + int nchars; + /* First character is numbered... (usually 32 = space) */ + int offset; + /* Character width and height */ + int w; + int h; + /* Font data; array of characters, one row after another. + Easily included in code, also easily loaded from + data files. */ + char *data; } gdFont; /* Text functions take these. */ typedef gdFont *gdFontPtr; -typedef void(*gdErrorMethod)(int, const char *, va_list); +typedef void (*gdErrorMethod)(int, const char *, va_list); + +BGD_DECLARE(void) gdSetErrorMethod(gdErrorMethod); +BGD_DECLARE(void) gdClearErrorMethod(void); -void gdSetErrorMethod(gdErrorMethod); -void gdClearErrorMethod(void); +/* For backwards compatibility only. Use gdImageSetStyle() + for MUCH more flexible line drawing. Also see + gdImageSetBrush(). */ +#define gdDashSize 4 /** - * Group: Types - * - * typedef: gdRect - * Defines a rectilinear region. + * Group: Colors * - * x - left position - * y - right position - * width - Rectangle width - * height - Rectangle height + * Colors are always of type int which is supposed to be at least 32 bit large. * - * typedef: gdRectPtr - * Pointer to a + * Kinds of colors: + * true colors - ARGB values where the alpha channel is stored as most + * significant, and the blue channel as least significant + * byte. Note that the alpha channel only uses the 7 least + * significant bits. + * Don't rely on the internal representation, though, and + * use to compose a truecolor value, and + * , , + * and to access + * the respective channels. + * palette indexes - The index of a color palette entry (0-255). + * special colors - As listed in the following section. * - * See also: - * - **/ -typedef struct -{ - int x, y; - int width, height; -} -gdRect, *gdRectPtr; - -/* For backwards compatibility only. Use gdImageSetStyle() - for MUCH more flexible line drawing. Also see - gdImageSetBrush(). */ -#define gdDashSize 4 - -/* Special colors. */ + * Constants: Special Colors + * gdStyled - use the current style, see + * gdBrushed - use the current brush, see + * gdStyledBrushed - use the current style and brush + * gdTiled - use the current tile, see + * gdTransparent - indicate transparency, what is not the same as the + * transparent color index; used for lines only + * gdAntiAliased - draw anti aliased + */ #define gdStyled (-2) #define gdBrushed (-3) @@ -316,7 +537,7 @@ gdRect, *gdRectPtr; #define gdTiled (-5) /* NOT the same as the transparent color index. - This is used in line styles only. */ + This is used in line styles only. */ #define gdTransparent (-6) #define gdAntiAliased (-7) @@ -324,287 +545,768 @@ gdRect, *gdRectPtr; /* Functions to manipulate images. */ /* Creates a palette-based image (up to 256 colors). */ -gdImagePtr gdImageCreate(int sx, int sy); +BGD_DECLARE(gdImagePtr) gdImageCreate(int sx, int sy); /* An alternate name for the above (2.0). */ #define gdImageCreatePalette gdImageCreate /* Creates a truecolor image (millions of colors). */ -gdImagePtr gdImageCreateTrueColor(int sx, int sy); +BGD_DECLARE(gdImagePtr) gdImageCreateTrueColor(int sx, int sy); /* Creates an image from various file types. These functions - return a palette or truecolor image based on the - nature of the file being loaded. Truecolor PNG - stays truecolor; palette PNG stays palette-based; - JPEG is always truecolor. */ -gdImagePtr gdImageCreateFromPng(FILE *fd); -gdImagePtr gdImageCreateFromPngCtx(gdIOCtxPtr in); -gdImagePtr gdImageCreateFromWBMP(FILE *inFile); -gdImagePtr gdImageCreateFromWBMPCtx(gdIOCtx *infile); -gdImagePtr gdImageCreateFromJpeg(FILE *infile); -gdImagePtr gdImageCreateFromJpegEx(FILE *infile, int ignore_warning); -gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile); -gdImagePtr gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning); -gdImagePtr gdImageCreateFromJpegPtr (int size, void *data); -gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning); -gdImagePtr gdImageCreateFromWebp(FILE *fd); -gdImagePtr gdImageCreateFromWebpCtx(gdIOCtxPtr in); -gdImagePtr gdImageCreateFromWebpPtr (int size, void *data); - -gdImagePtr gdImageCreateFromAvif(FILE *infile); -gdImagePtr gdImageCreateFromAvifPtr(int size, void *data); -gdImagePtr gdImageCreateFromAvifCtx(gdIOCtx *infile); - -gdImagePtr gdImageCreateFromTga( FILE * fp ); -gdImagePtr gdImageCreateFromTgaCtx(gdIOCtx* ctx); -gdImagePtr gdImageCreateFromTgaPtr(int size, void *data); - -gdImagePtr gdImageCreateFromBmp (FILE * inFile); -gdImagePtr gdImageCreateFromBmpPtr (int size, void *data); -gdImagePtr gdImageCreateFromBmpCtx (gdIOCtxPtr infile); - -const char * gdPngGetVersionString(void); - -const char * gdJpegGetVersionString(void); - -/* A custom data source. */ -/* The source function must return -1 on error, otherwise the number - of bytes fetched. 0 is EOF, not an error! */ -/* context will be passed to your source function. */ + return a palette or truecolor image based on the + nature of the file being loaded. Truecolor PNG + stays truecolor; palette PNG stays palette-based; + JPEG is always truecolor. */ +/* PNG */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPng(FILE *fd); +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngCtx(gdIOCtxPtr in); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngCtxWithMetadata(gdIOCtxPtr in, gdImageMetadata *metadata); +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngPtrWithMetadata(int size, void *data, gdImageMetadata *metadata); +BGD_DECLARE(void) gdImagePng(gdImagePtr im, FILE *out); +BGD_DECLARE(void) gdImagePngCtx(gdImagePtr im, gdIOCtxPtr out); +/* 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all, + 1 is FASTEST but produces larger files, 9 provides the best + compression (smallest files) but takes a long time to compress, and + -1 selects the default compiled into the zlib library. */ +BGD_DECLARE(void) gdImagePngEx(gdImagePtr im, FILE *out, int level); +BGD_DECLARE(void) gdImagePngCtxEx(gdImagePtr im, gdIOCtxPtr out, int level); +BGD_DECLARE(void) +gdImagePngCtxWithMetadata(gdImagePtr im, gdIOCtxPtr out, const gdImageMetadata *metadata); +BGD_DECLARE(void) +gdImagePngCtxExWithMetadata(gdImagePtr im, gdIOCtxPtr out, int level, + const gdImageMetadata *metadata); + +/* Best to free this memory with gdFree(), not free() */ +BGD_DECLARE(void *) gdImagePngPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) gdImagePngPtrEx(gdImagePtr im, int *size, int level); +BGD_DECLARE(void *) +gdImagePngPtrWithMetadata(gdImagePtr im, int *size, const gdImageMetadata *metadata); +BGD_DECLARE(void *) +gdImagePngPtrExWithMetadata(gdImagePtr im, int *size, int level, const gdImageMetadata *metadata); +BGD_DECLARE(int) +gdImageMetadataInjectPng(void **data, int *size, const gdImageMetadata *metadata); + +#define GD_PNG_FILTER_AUTO 0U +#define GD_PNG_FILTER_NONE (1U << 0) +#define GD_PNG_FILTER_SUB (1U << 1) +#define GD_PNG_FILTER_UP (1U << 2) +#define GD_PNG_FILTER_AVERAGE (1U << 3) +#define GD_PNG_FILTER_PAETH (1U << 4) +#define GD_PNG_FILTER_ALL \ + (GD_PNG_FILTER_NONE | GD_PNG_FILTER_SUB | GD_PNG_FILTER_UP | GD_PNG_FILTER_AVERAGE | \ + GD_PNG_FILTER_PAETH) + +enum { + GD_PNG_COMPRESSION_STRATEGY_DEFAULT = 0, + GD_PNG_COMPRESSION_STRATEGY_FILTERED, + GD_PNG_COMPRESSION_STRATEGY_HUFFMAN_ONLY, + GD_PNG_COMPRESSION_STRATEGY_RLE, + GD_PNG_COMPRESSION_STRATEGY_FIXED +}; typedef struct { - int (*source) (void *context, char *buffer, int len); - void *context; -} gdSource, *gdSourcePtr; + size_t struct_size; + int compression_level; + unsigned int filters; + int compression_strategy; + const gdImageMetadata *metadata; +} gdPngWriteOptions; + +BGD_DECLARE(void) gdPngWriteOptionsInit(gdPngWriteOptions *options); +BGD_DECLARE(int) gdImagePngWithOptions(gdImagePtr im, FILE *out, const gdPngWriteOptions *options); +BGD_DECLARE(int) +gdImagePngCtxWithOptions(gdImagePtr im, gdIOCtxPtr out, const gdPngWriteOptions *options); +BGD_DECLARE(void *) +gdImagePngPtrWithOptions(gdImagePtr im, int *size, const gdPngWriteOptions *options); +BGD_DECLARE(const char *) gdPngGetVersionString(void); + +/* QOI */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoi(FILE *fd); +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiCtx(gdIOCtxPtr in); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromQoiCtxWithMetadata(gdIOCtxPtr in, gdImageMetadata *metadata); +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromQoiPtrWithMetadata(int size, void *data, gdImageMetadata *metadata); +BGD_DECLARE(void *) gdImageQoiPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) gdImageQoiPtrEx(gdImagePtr im, int *size, int colorspace); +BGD_DECLARE(void *) +gdImageQoiPtrWithMetadata(gdImagePtr im, int *size, const gdImageMetadata *metadata); +BGD_DECLARE(void *) +gdImageQoiPtrExWithMetadata(gdImagePtr im, int *size, int colorspace, + const gdImageMetadata *metadata); +BGD_DECLARE(int) gdImageMetadataInjectQoi(void **data, int *size, const gdImageMetadata *metadata); + +BGD_DECLARE(void) gdImageQoi(gdImagePtr im, FILE *out); +BGD_DECLARE(void) gdImageQoiCtx(gdImagePtr im, gdIOCtxPtr out); +BGD_DECLARE(void) +gdImageQoiCtxWithMetadata(gdImagePtr im, gdIOCtxPtr out, const gdImageMetadata *metadata); +enum { GD_QOI_SRGB = 0, GD_QOI_LINEAR = 1 }; + +BGD_DECLARE(void) gdImageQoi(gdImagePtr im, FILE *out); +BGD_DECLARE(void) gdImageQoiCtx(gdImagePtr im, gdIOCtxPtr out); +BGD_DECLARE(void) +gdImageQoiCtxWithMetadata(gdImagePtr im, gdIOCtxPtr out, const gdImageMetadata *metadata); +BGD_DECLARE(void) gdImageQoiEx(gdImagePtr im, FILE *out, int colorspace); +BGD_DECLARE(void) +gdImageQoiCtxEx(gdImagePtr im, gdIOCtxPtr out, int colorspace); +BGD_DECLARE(void) +gdImageQoiCtxExWithMetadata(gdImagePtr im, gdIOCtxPtr out, int colorspace, + const gdImageMetadata *metadata); +/* GIF */ +/* These read the first frame only */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGif(FILE *fd); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGifCtx(gdIOCtxPtr in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGifPtr(int size, void *data); +BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out); +BGD_DECLARE(void) gdImageGif(gdImagePtr im, FILE *out); +/* Best to free this memory with gdFree(), not free() */ +BGD_DECLARE(void *) gdImageGifPtr(gdImagePtr im, int *size); -gdImagePtr gdImageCreateFromPngSource(gdSourcePtr in); +/* Anims, frames or compositions */ +typedef struct gdGifReadStruct *gdGifReadPtr; -gdImagePtr gdImageCreateFromGd(FILE *in); -gdImagePtr gdImageCreateFromGdCtx(gdIOCtxPtr in); +typedef struct { + int width; + int height; + int backgroundIndex; + int globalColorTable; + int loopCount; +} gdGifInfo; -gdImagePtr gdImageCreateFromGd2(FILE *in); -gdImagePtr gdImageCreateFromGd2Ctx(gdIOCtxPtr in); +typedef struct { + int frameIndex; + int x; + int y; + int width; + int height; + int delay; + int disposal; + int transparentIndex; + int localColorTable; + int interlace; +} gdGifFrameInfo; + +BGD_DECLARE(int) gdGifIsAnimated(FILE *fd); +BGD_DECLARE(int) gdGifIsAnimatedCtx(gdIOCtxPtr in); +BGD_DECLARE(int) gdGifIsAnimatedPtr(int size, void *data); +BGD_DECLARE(gdGifReadPtr) gdGifReadOpen(FILE *fd); +BGD_DECLARE(gdGifReadPtr) gdGifReadOpenCtx(gdIOCtxPtr in); +BGD_DECLARE(gdGifReadPtr) gdGifReadOpenPtr(int size, void *data); +BGD_DECLARE(void) gdGifReadClose(gdGifReadPtr gif); +BGD_DECLARE(int) gdGifReadGetInfo(gdGifReadPtr gif, gdGifInfo *info); +BGD_DECLARE(int) +gdGifReadNextFrame(gdGifReadPtr gif, gdGifFrameInfo *info, gdImagePtr *frame); +BGD_DECLARE(int) +gdGifReadNextImage(gdGifReadPtr gif, gdGifFrameInfo *info, gdImagePtr *image); +BGD_DECLARE(gdImagePtr) gdGifReadCloneImage(gdGifReadPtr gif); -gdImagePtr gdImageCreateFromGd2Part(FILE *in, int srcx, int srcy, int w, int h); -gdImagePtr gdImageCreateFromGd2PartCtx(gdIOCtxPtr in, int srcx, int srcy, int w, int h); +/** + * Group: GifAnim + * + * Legal values for Disposal. gdDisposalNone is always used by + * the built-in optimizer if previm is passed. + * + * Constants: gdImageGifAnim + * + * gdDisposalUnknown - Not recommended + * gdDisposalNone - Preserve previous frame + * gdDisposalRestoreBackground - First allocated color of palette + * gdDisposalRestorePrevious - Restore to before start of frame + * + * See also: + * - + */ +enum { gdDisposalUnknown, gdDisposalNone, gdDisposalRestoreBackground, gdDisposalRestorePrevious }; + +BGD_DECLARE(void) +gdImageGifAnimBegin(gdImagePtr im, FILE *outFile, int GlobalCM, int Loops); +BGD_DECLARE(void) +gdImageGifAnimAdd(gdImagePtr im, FILE *outFile, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm); +BGD_DECLARE(void) gdImageGifAnimEnd(FILE *outFile); +BGD_DECLARE(void) +gdImageGifAnimBeginCtx(gdImagePtr im, gdIOCtxPtr out, int GlobalCM, int Loops); +BGD_DECLARE(void) +gdImageGifAnimAddCtx(gdImagePtr im, gdIOCtxPtr out, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm); +BGD_DECLARE(void) gdImageGifAnimEndCtx(gdIOCtxPtr out); +BGD_DECLARE(void *) +gdImageGifAnimBeginPtr(gdImagePtr im, int *size, int GlobalCM, int Loops); +BGD_DECLARE(void *) +gdImageGifAnimAddPtr(gdImagePtr im, int *size, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm); +BGD_DECLARE(void *) gdImageGifAnimEndPtr(int *size); + +/* WBMP */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMP(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMPCtx(gdIOCtxPtr infile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMPPtr(int size, void *data); + +/* JPEG */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpeg(FILE *infile); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegEx(FILE *infile, int ignore_warning); +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegCtx(gdIOCtxPtr infile); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxEx(gdIOCtxPtr infile, int ignore_warning); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxWithMetadata(gdIOCtxPtr infile, gdImageMetadata *metadata); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxExWithMetadata(gdIOCtxPtr infile, int ignore_warning, + gdImageMetadata *metadata); +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrEx(int size, void *data, int ignore_warning); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrWithMetadata(int size, void *data, gdImageMetadata *metadata); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrExWithMetadata(int size, void *data, int ignore_warning, + gdImageMetadata *metadata); +BGD_DECLARE(const char *) gdJpegGetVersionString(); + +/* WEBP */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebp(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpCtx(gdIOCtxPtr infile); + +typedef struct gdWebpReadStruct *gdWebpReadPtr; +typedef struct gdWebpWriteStruct *gdWebpWritePtr; -gdImagePtr gdImageCreateFromXbm(FILE *fd); -void gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOCtx * out); +typedef struct { + int width; + int height; + int frameCount; + int loopCount; + int backgroundColor; + int formatFlags; +} gdWebpInfo; -gdImagePtr gdImageCreateFromXpm (char *filename); +typedef struct { + int frameIndex; + int x; + int y; + int width; + int height; + int duration; + int timestamp; + int dispose; + int blend; + int hasAlpha; + int complete; +} gdWebpFrameInfo; -void gdImageDestroy(gdImagePtr im); +typedef struct { + int canvasWidth; + int canvasHeight; + int loopCount; + int backgroundColor; + int quality; + int lossless; + int method; + int minimizeSize; + int kmin; + int kmax; + int allowMixed; +} gdWebpWriteOptions; + +enum { gdWebpDisposeNone, gdWebpDisposeBackground }; +enum { gdWebpBlendAlpha, gdWebpBlendNone }; + +BGD_DECLARE(int) gdWebpIsAnimated(FILE *fd); +BGD_DECLARE(int) gdWebpIsAnimatedCtx(gdIOCtxPtr in); +BGD_DECLARE(int) gdWebpIsAnimatedPtr(int size, void *data); +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpen(FILE *fd); +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenCtx(gdIOCtxPtr in); +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenPtr(int size, void *data); +BGD_DECLARE(void) gdWebpReadClose(gdWebpReadPtr webp); +BGD_DECLARE(int) gdWebpReadGetInfo(gdWebpReadPtr webp, gdWebpInfo *info); +BGD_DECLARE(int) +gdWebpReadNextFrame(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *frame); +BGD_DECLARE(int) +gdWebpReadNextImage(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *image); +BGD_DECLARE(gdImagePtr) gdWebpReadCloneImage(gdWebpReadPtr webp); +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpen(FILE *outFile, const gdWebpWriteOptions *options); +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenCtx(gdIOCtxPtr out, const gdWebpWriteOptions *options); +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenPtr(const gdWebpWriteOptions *options); +BGD_DECLARE(int) +gdWebpWriteAddImage(gdWebpWritePtr webp, gdImagePtr image, int durationMs); +BGD_DECLARE(void) gdWebpWriteClose(gdWebpWritePtr webp); +BGD_DECLARE(void *) gdWebpWritePtrFinish(gdWebpWritePtr webp, int *size); + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxl(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlCtx(gdIOCtxPtr infile); + +BGD_DECLARE(void) gdImageJxl(gdImagePtr im, FILE *outFile); +BGD_DECLARE(void) +gdImageJxlEx(gdImagePtr im, FILE *outFile, int lossless, float distance, int effort); +BGD_DECLARE(void *) gdImageJxlPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) +gdImageJxlPtrEx(gdImagePtr im, int *size, int lossless, float distance, int effort); +BGD_DECLARE(void) gdImageJxlCtx(gdImagePtr im, gdIOCtxPtr outfile); +BGD_DECLARE(void) +gdImageJxlCtxEx(gdImagePtr im, gdIOCtxPtr outfile, int lossless, float distance, int effort); + +/* Animation API */ +typedef struct gdJxlAnimReader *gdJxlAnimReaderPtr; +typedef struct gdJxlAnim *gdJxlAnimPtr; -/* Replaces or blends with the background depending on the - most recent call to gdImageAlphaBlending and the - alpha channel value of 'color'; default is to overwrite. - Tiling and line styling are also implemented - here. All other gd drawing functions pass through this call, - allowing for many useful effects. */ +typedef struct { + int delay_ms; + int x_offset; + int y_offset; + int width; + int height; + int blend_mode; + int is_last; +} gdJxlFrameInfo; -void gdImageSetPixel(gdImagePtr im, int x, int y, int color); +#define GD_JXL_BLEND_REPLACE 0 +#define GD_JXL_BLEND_ADD 1 +#define GD_JXL_BLEND_BLEND 2 +#define GD_JXL_BLEND_MULADD 3 +#define GD_JXL_BLEND_MUL 4 -int gdImageGetTrueColorPixel (gdImagePtr im, int x, int y); -int gdImageGetPixel(gdImagePtr im, int x, int y); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreate(FILE *inFile); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreatePtr(int size, void *data); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateCtx(gdIOCtxPtr inCtx); -void gdImageAABlend(gdImagePtr im); +BGD_DECLARE(gdImagePtr) gdJxlReadNextImage(gdJxlAnimReaderPtr reader, int *delay_ms); -void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); -void gdImageAALine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateRaw(FILE *inFile); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateRawPtr(int size, void *data); +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateRawCtx(gdIOCtxPtr inCtx); -/* For backwards compatibility only. Use gdImageSetStyle() - for much more flexible line drawing. */ -void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); -/* Corners specified (not width and height). Upper left first, lower right - second. */ -void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color); -/* Solid bar. Upper left corner first, lower right corner second. */ -void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color); -void gdImageSetClip(gdImagePtr im, int x1, int y1, int x2, int y2); -void gdImageGetClip(gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P); -void gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y); -void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color); -void gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color); -void gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color); -void gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color); -void gdImageString16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color); -void gdImageStringUp16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color); +BGD_DECLARE(gdImagePtr) gdJxlReadNextFrame(gdJxlAnimReaderPtr reader, gdJxlFrameInfo *info); -/* - * The following functions are required to be called prior to the - * use of any sort of threads in a module load / shutdown function - * respectively. - */ -void gdFontCacheMutexSetup(void); -void gdFontCacheMutexShutdown(void); +BGD_DECLARE(void) gdImageJxlAnimReaderDestroy(gdJxlAnimReaderPtr reader); -/* 2.0.16: for thread-safe use of gdImageStringFT and friends, - * call this before allowing any thread to call gdImageStringFT. - * Otherwise it is invoked by the first thread to invoke - * gdImageStringFT, with a very small but real risk of a race condition. - * Return 0 on success, nonzero on failure to initialize freetype. +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBegin(FILE *outFile, int width, int height, int lossless, float distance, int effort); + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginCtx(gdIOCtxPtr outCtx, int width, int height, int lossless, float distance, + int effort); + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginPtr(int width, int height, int lossless, float distance, int effort); + +BGD_DECLARE(int) gdImageJxlAnimAddFrame(gdJxlAnimPtr anim, gdImagePtr im, int delay_ms); + +BGD_DECLARE(int) gdImageJxlAnimEnd(gdJxlAnimPtr anim); +BGD_DECLARE(void *) gdImageJxlAnimEndPtr(gdJxlAnimPtr anim, int *size); + +/* HEIF */ + +/** + * Group: HEIF Coding Format + * + * Values that select the HEIF coding format. + * + * Constants: gdHeifCodec + * + * GD_HEIF_CODEC_UNKNOWN + * GD_HEIF_CODEC_HEVC + * GD_HEIF_CODEC_AV1 + * + * See also: + * - */ -int gdFontCacheSetup(void); +typedef enum { + GD_HEIF_CODEC_UNKNOWN = 0, + GD_HEIF_CODEC_HEVC, + GD_HEIF_CODEC_AV1 = 4, +} gdHeifCodec; -/* Optional: clean up after application is done using fonts in gdImageStringFT(). */ -void gdFontCacheShutdown(void); +/** + * Group: HEIF Chroma Subsampling + * + * Values that select the HEIF chroma subsampling. + * + * Constants: gdHeifCompression + * + * GD_HEIF_CHROMA_420 + * GD_HEIF_CHROMA_422 + * GD_HEIF_CHROMA_444 + * + * See also: + * - + */ +typedef const char *gdHeifChroma; + +#define GD_HEIF_CHROMA_420 "420" +#define GD_HEIF_CHROMA_422 "422" +#define GD_HEIF_CHROMA_444 "444" + +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtxPtr infile); + +BGD_DECLARE(void) +gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma); +BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile); +BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) +gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma); +BGD_DECLARE(void) +gdImageHeifCtx(gdImagePtr im, gdIOCtxPtr outfile, int quality, gdHeifCodec codec, + gdHeifChroma chroma); + +/* AVIF */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvif(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifCtx(gdIOCtxPtr infile); + +BGD_DECLARE(void) gdImageAvif(gdImagePtr im, FILE *outFile); +BGD_DECLARE(void) +gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed); +BGD_DECLARE(void *) gdImageAvifPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) +gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed); +BGD_DECLARE(void) +gdImageAvifCtx(gdImagePtr im, gdIOCtxPtr outfile, int quality, int speed); + +/* TIFF */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtxPtr infile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data); + +typedef struct gdTiffReadStruct *gdTiffReadPtr; -/* Calls gdImageStringFT. Provided for backwards compatibility only. */ -char *gdImageStringTTF(gdImage *im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string); +typedef struct { + int width; + int height; + int pageCount; + int bitsPerSample; + int samplesPerPixel; + int compression; + int photometric; + float xResolution; + float yResolution; + int resolutionUnit; +} gdTiffInfo; -/* FreeType 2 text output */ -char *gdImageStringFT(gdImage *im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string); +typedef struct { + int pageIndex; + int width; + int height; + int bitsPerSample; + int samplesPerPixel; + int compression; + int photometric; + int planar; + int hasAlpha; + int isTiled; + float xResolution; + float yResolution; + int resolutionUnit; +} gdTiffPageInfo; + +BGD_DECLARE(int) gdTiffIsMultiPage(FILE *fd); +BGD_DECLARE(int) gdTiffIsMultiPageCtx(gdIOCtxPtr in); +BGD_DECLARE(int) gdTiffIsMultiPagePtr(int size, void *data); +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpen(FILE *fd); +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenCtx(gdIOCtxPtr in); +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenPtr(int size, void *data); +BGD_DECLARE(void) gdTiffReadClose(gdTiffReadPtr tiff); +BGD_DECLARE(int) gdTiffReadGetInfo(gdTiffReadPtr tiff, gdTiffInfo *info); +BGD_DECLARE(int) +gdTiffReadNextImage(gdTiffReadPtr tiff, gdTiffPageInfo *info, gdImagePtr *image); +BGD_DECLARE(gdImagePtr) gdTiffReadCloneImage(gdTiffReadPtr tiff); + +/* TIFF Write API */ +#define GD_TIFF_RGB 1 +#define GD_TIFF_RGBA 2 +#define GD_TIFF_GRAY 3 +#define GD_TIFF_BILEVEL 4 + +#define GD_TIFF_RESUNIT_NONE 1 +#define GD_TIFF_RESUNIT_INCH 2 +#define GD_TIFF_RESUNIT_CENTIMETER 3 + +#define GD_TIFF_ALPHA_UNASSOCIATED 1 +#define GD_TIFF_ALPHA_ASSOCIATED 2 typedef struct { - double linespacing; /* fine tune line spacing for '\n' */ - int flags; /* Logical OR of gdFTEX_ values */ - int charmap; /* TBB: 2.0.12: may be gdFTEX_Unicode, - gdFTEX_Shift_JIS, gdFTEX_Big5 or gdFTEX_MacRoman; - when not specified, maps are searched - for in the above order. */ - int hdpi; - int vdpi; -} - gdFTStringExtra, *gdFTStringExtraPtr; + int bitDepth; + int colorspace; + int compression; + int jpegQuality; + int minIsWhite; + int resolutionUnit; + float xResolution; + float yResolution; + int alphaType; +} gdTiffWriteOptions; + +typedef struct gdTiffWriteStruct *gdTiffWritePtr; + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpen(FILE *outFile, const gdTiffWriteOptions *options); +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenCtx(gdIOCtxPtr out, const gdTiffWriteOptions *options); +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenPtr(const gdTiffWriteOptions *options); +BGD_DECLARE(int) gdTiffWriteAddImage(gdTiffWritePtr write, gdImagePtr image); +BGD_DECLARE(void) gdTiffWriteClose(gdTiffWritePtr write); +BGD_DECLARE(void *) gdTiffWritePtrFinish(gdTiffWritePtr write, int *size); + +BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile); +BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size); +BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtxPtr out); + +/* TGA */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromTga(FILE *fp); +BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaCtx(gdIOCtxPtr ctx); +BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaPtr(int size, void *data); + +/* BMP */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmp(FILE *inFile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpPtr(int size, void *data); +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpCtx(gdIOCtxPtr infile); + +/* UltraHDR */ -#define gdFTEX_LINESPACE 1 -#define gdFTEX_CHARMAP 2 -#define gdFTEX_RESOLUTION 4 +/** + * Group: UltraHDR + * + * UltraHDR (gain map) APIs are separate from . The UltraHDR handle + * type is opaque and cannot be passed to existing functions. + */ -/* These are NOT flags; set one in 'charmap' if you set the gdFTEX_CHARMAP bit in 'flags'. */ -#define gdFTEX_Unicode 0 -#define gdFTEX_Shift_JIS 1 -#define gdFTEX_Big5 2 -#define gdFTEX_MacRoman 3 +/** + * Constants: gdUhdrStatus + * + * Return status values used by UltraHDR APIs. + * + * GD_UHDR_SUCCESS - operation succeeded + * GD_UHDR_NOT_AVAILABLE - libgd was built without UltraHDR support + * GD_UHDR_E_INVALID - invalid argument or state + * GD_UHDR_E_UNSUPPORTED - unsupported format or operation + * GD_UHDR_E_ENCODE - encode failure + * GD_UHDR_E_DECODE - decode failure + */ +#define GD_UHDR_SUCCESS 0 +#define GD_UHDR_NOT_AVAILABLE -1 +#define GD_UHDR_E_INVALID -2 +#define GD_UHDR_E_UNSUPPORTED -3 +#define GD_UHDR_E_ENCODE -4 +#define GD_UHDR_E_DECODE -5 -/* FreeType 2 text output with fine tuning */ -char * -gdImageStringFTEx(gdImage * im, int *brect, int fg, char * fontlist, - double ptsize, double angle, int x, int y, char * string, - gdFTStringExtraPtr strex); +/** + * Constants: gdUhdrMirrorAxis + * + * Mirror axis values used by . + * + * GD_UHDR_MIRROR_HORIZONTAL + * GD_UHDR_MIRROR_VERTICAL + */ +#define GD_UHDR_MIRROR_HORIZONTAL 0 +#define GD_UHDR_MIRROR_VERTICAL 1 +/** + * Enum: gdUhdrFormat + * + * UltraHDR container format selector. + * + * GD_UHDR_FORMAT_JPEG - UltraHDR JPEG (currently supported) + * GD_UHDR_FORMAT_WEBP - reserved for future support + * GD_UHDR_FORMAT_HEIF - reserved for future support + */ +typedef enum { + GD_UHDR_FORMAT_JPEG = 0, + GD_UHDR_FORMAT_WEBP = 1, + GD_UHDR_FORMAT_HEIF = 2 +} gdUhdrFormat; -/* Point type for use in polygon drawing. */ +/** + * Typedef: gdUhdrImage + * + * Opaque UltraHDR image handle. + */ +typedef struct gdUhdrImageStruct gdUhdrImage; + +/** + * Typedef: gdUhdrImagePtr + * + * Pointer to . + */ +typedef gdUhdrImage *gdUhdrImagePtr; + +/** + * Typedef: gdUhdrError + * + * Structured error details for UltraHDR APIs. + * + * Fields: + * code - libgd UltraHDR status code (GD_UHDR_*) + * provider_code - underlying provider error code, if any + * message - optional human-readable detail string + */ typedef struct { - int x, y; -} gdPoint, *gdPointPtr; + int code; + int provider_code; + char message[128]; +} gdUhdrError; -void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c); -void gdImageOpenPolygon(gdImagePtr im, gdPointPtr p, int n, int c); -void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c); +/** + * Typedef: gdUhdrErrorPtr + * + * Pointer to . + */ +typedef gdUhdrError *gdUhdrErrorPtr; -/* These functions still work with truecolor images, - for which they never return error. */ -int gdImageColorAllocate(gdImagePtr im, int r, int g, int b); -/* gd 2.0: palette entries with non-opaque transparency are permitted. */ -int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a); -/* Assumes opaque is the preferred alpha channel value */ -int gdImageColorClosest(gdImagePtr im, int r, int g, int b); -/* Closest match taking all four parameters into account. - A slightly different color with the same transparency - beats the exact same color with radically different - transparency */ -int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a); -/* An alternate method */ -int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b); -/* Returns exact, 100% opaque matches only */ -int gdImageColorExact(gdImagePtr im, int r, int g, int b); -/* Returns an exact match only, including alpha */ -int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a); -/* Opaque only */ -int gdImageColorResolve(gdImagePtr im, int r, int g, int b); -/* Based on gdImageColorExactAlpha and gdImageColorClosestAlpha */ -int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a); +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromFile(const char *filename, int format, gdUhdrErrorPtr err); +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromCtx(gdIOCtxPtr ctx, int format, gdUhdrErrorPtr err); +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromPtr(int size, void *data, int format, gdUhdrErrorPtr err); +BGD_DECLARE(void) gdUhdrImageDestroy(gdUhdrImagePtr im); -/* A simpler way to obtain an opaque truecolor value for drawing on a - truecolor image. Not for use with palette images! */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromFile(const char *filename); +BGD_DECLARE(gdImagePtr) gdImageReadFile(const char *filename); +BGD_DECLARE(gdImagePtr) gdImageReadCtx(gdIOCtxPtr ctx); -#define gdTrueColor(r, g, b) (((r) << 16) + \ - ((g) << 8) + \ - (b)) +/* + Group: Types -/* Returns a truecolor value with an alpha channel component. - gdAlphaMax (127, **NOT 255**) is transparent, 0 is completely - opaque. */ + typedef: gdSource -#define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + \ - ((r) << 16) + \ - ((g) << 8) + \ - (b)) + typedef: gdSourcePtr -void gdImageColorDeallocate(gdImagePtr im, int color); + *Note:* This interface is *obsolete* and kept only for + *compatibility. Use instead. -/* Converts a truecolor image to a palette-based image, - using a high-quality two-pass quantization routine - which attempts to preserve alpha channel information - as well as R/G/B color information when creating - a palette. If ditherFlag is set, the image will be - dithered to approximate colors better, at the expense - of some obvious "speckling." colorsWanted can be - anything up to 256. If the original source image - includes photographic information or anything that - came out of a JPEG, 256 is strongly recommended. - - Better yet, don't use this function -- write real - truecolor PNGs and JPEGs. The disk space gain of - conversion to palette is not great (for small images - it can be negative) and the quality loss is ugly. */ + Represents a source from which a PNG can be read. Programmers who + do not wish to read PNGs from a file can provide their own + alternate input mechanism, using the + function. See the documentation of that function for an example of + the proper use of this type. -gdImagePtr gdImageCreatePaletteFromTrueColor (gdImagePtr im, int ditherFlag, int colorsWanted); + > typedef struct { + > int (*source) (void *context, char *buffer, int len); + > void *context; + > } gdSource, *gdSourcePtr; -int gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted); -int gdImagePaletteToTrueColor(gdImagePtr src); + The source function must return -1 on error, otherwise the number + of bytes fetched. 0 is EOF, not an error! -/* An attempt at getting the results of gdImageTrueColorToPalette - to look a bit more like the original (im1 is the original - and im2 is the palette version */ -int gdImageColorMatch(gdImagePtr im1, gdImagePtr im2); + 'context' will be passed to your source function. -/* Specifies a color index (if a palette image) or an - RGB color (if a truecolor image) which should be - considered 100% transparent. FOR TRUECOLOR IMAGES, - THIS IS IGNORED IF AN ALPHA CHANNEL IS BEING - SAVED. Use gdImageSaveAlpha(im, 0); to - turn off the saving of a full alpha channel in - a truecolor image. Note that gdImageColorTransparent - is usually compatible with older browsers that - do not understand full alpha channels well. TBB */ -void gdImageColorTransparent(gdImagePtr im, int color); - -void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src); -void gdImagePng(gdImagePtr im, FILE *out); -void gdImagePngCtx(gdImagePtr im, gdIOCtx *out); -void gdImageGif(gdImagePtr im, FILE *out); -void gdImageGifCtx(gdImagePtr im, gdIOCtx *out); - -void * gdImageBmpPtr(gdImagePtr im, int *size, int compression); -void gdImageBmp(gdImagePtr im, FILE *outFile, int compression); -void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression); +*/ +typedef struct { + int (*source)(void *context, char *buffer, int len); + void *context; +} gdSource, *gdSourcePtr; -/* 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all, - * 1 is FASTEST but produces larger files, 9 provides the best - * compression (smallest files) but takes a long time to compress, and - * -1 selects the default compiled into the zlib library. - */ -void gdImagePngEx(gdImagePtr im, FILE * out, int level, int basefilter); -void gdImagePngCtxEx(gdImagePtr im, gdIOCtx * out, int level, int basefilter); +/* Deprecated in favor of gdImageCreateFromPngCtx */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngSource(gdSourcePtr in); +/* for completeness with Sink 2.x APIs, will be removed in 3.0 with all Sink APIs */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiSource(gdSourcePtr in); -void gdImageWBMP(gdImagePtr image, int fg, FILE *out); -void gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGifSource(gdSourcePtr in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd(FILE *in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGdCtx(gdIOCtxPtr in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGdPtr(int size, void *data); +/* Best to free this memory with gdFree(), not free() */ +BGD_DECLARE(void *) gdImageGdPtr(gdImagePtr im, int *size); +BGD_DECLARE(void) gdImageGd(gdImagePtr im, FILE *out); + +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2(FILE *in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx(gdIOCtxPtr in); +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ptr(int size, void *data); + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromGd2Part(FILE *in, int srcx, int srcy, int w, int h); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromGd2PartCtx(gdIOCtxPtr in, int srcx, int srcy, int w, int h); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromGd2PartPtr(int size, void *data, int srcx, int srcy, int w, int h); + +BGD_DECLARE(gdImagePtr) gdImageCreateFromXbm(FILE *in); +BGD_DECLARE(void) +gdImageXbmCtx(gdImagePtr image, char *file_name, int fg, gdIOCtxPtr out); + +/* NOTE: filename, not FILE */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromXpm(char *filename); + +BGD_DECLARE(void *) gdImageBmpPtr(gdImagePtr im, int *size, int compression); +BGD_DECLARE(void) gdImageBmp(gdImagePtr im, FILE *outFile, int compression); +BGD_DECLARE(void) gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression); + +#define GD_BMP_COMPRESS_NONE 0 +#define GD_BMP_COMPRESS_RLE8 1 +#define GD_BMP_COMPRESS_RLE4 2 + +#define GD_BMP_FLAG_NONE 0 +#define GD_BMP_FLAG_FORCE_V4HDR (1 << 0) +#define GD_BMP_FLAG_QUANTIZE (1 << 1) +#define GD_BMP_FLAG_RGB555 (1 << 2) + +BGD_DECLARE(void *) +gdImageBmpPtrEx(gdImagePtr im, int *size, int bpp, int compression, int flags); +BGD_DECLARE(void) +gdImageBmpEx(gdImagePtr im, FILE *outFile, int bpp, int compression, int flags); +BGD_DECLARE(void) +gdImageBmpCtxEx(gdImagePtr im, gdIOCtxPtr out, int bpp, int compression, int flags); + +BGD_DECLARE(void) gdImageWBMP(gdImagePtr image, int fg, FILE *out); +BGD_DECLARE(void) gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtxPtr out); + +BGD_DECLARE(int) gdUhdrIsAvailable(void); +BGD_DECLARE(int) gdUhdrImageWidth(gdUhdrImagePtr im); +BGD_DECLARE(int) gdUhdrImageHeight(gdUhdrImagePtr im); +BGD_DECLARE(int) gdUhdrImageHasGainMap(gdUhdrImagePtr im); +BGD_DECLARE(int) +gdUhdrImageResize(gdUhdrImagePtr im, int width, int height, gdUhdrErrorPtr err); +BGD_DECLARE(int) +gdUhdrImageCrop(gdUhdrImagePtr im, int left, int top, int width, int height, gdUhdrErrorPtr err); +BGD_DECLARE(int) +gdUhdrImageRotate(gdUhdrImagePtr im, int degrees, gdUhdrErrorPtr err); +BGD_DECLARE(int) +gdUhdrImageMirror(gdUhdrImagePtr im, int axis, gdUhdrErrorPtr err); +BGD_DECLARE(int) +gdUhdrImageFile(gdUhdrImagePtr im, const char *filename, int format, int quality, + gdUhdrErrorPtr err); +BGD_DECLARE(int) +gdUhdrImageCtx(gdUhdrImagePtr im, gdIOCtxPtr ctx, int format, int quality, gdUhdrErrorPtr err); +BGD_DECLARE(void *) +gdUhdrImageWritePtr(gdUhdrImagePtr im, int *size, int format, int quality, gdUhdrErrorPtr err); +BGD_DECLARE(gdImagePtr) +gdUhdrImageGetSdr(gdUhdrImagePtr im, gdUhdrErrorPtr err); + + +BGD_DECLARE(int) gdImageFile(gdImagePtr im, const char *filename); +BGD_DECLARE(int) gdSupportsFileType(const char *filename, int writing); /* Guaranteed to correctly free memory returned - by the gdImage*Ptr functions */ + by the gdImage*Ptr functions */ void gdFree(void *m); /* Best to free this memory with gdFree(), not free() */ -void *gdImageWBMPPtr(gdImagePtr im, int *size, int fg); +BGD_DECLARE(void *) gdImageWBMPPtr(gdImagePtr im, int *size, int fg); /* 100 is highest quality (there is always a little loss with JPEG). 0 is lowest. 10 is about the lowest useful setting. */ -void gdImageJpeg(gdImagePtr im, FILE *out, int quality); -void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality); +BGD_DECLARE(void) gdImageJpeg(gdImagePtr im, FILE *out, int quality); +BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtxPtr out, int quality); +BGD_DECLARE(void) +gdImageJpegCtxWithMetadata(gdImagePtr im, gdIOCtxPtr out, int quality, + const gdImageMetadata *metadata); + +/* Best to free this memory with gdFree(), not free() */ +BGD_DECLARE(void *) gdImageJpegPtr(gdImagePtr im, int *size, int quality); +BGD_DECLARE(void *) +gdImageJpegPtrWithMetadata(gdImagePtr im, int *size, int quality, const gdImageMetadata *metadata); /** * Group: WebP @@ -615,309 +1317,908 @@ void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality); * , the image will be written in the lossless WebP format. * * See also: - * - + * - */ #define gdWebpLossless 101 -void gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality); +BGD_DECLARE(void) gdImageWebpEx(gdImagePtr im, FILE *outFile, int quantization); +BGD_DECLARE(void) gdImageWebp(gdImagePtr im, FILE *outFile); +BGD_DECLARE(void *) gdImageWebpPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) +gdImageWebpPtrEx(gdImagePtr im, int *size, int quantization); +BGD_DECLARE(void) +gdImageWebpCtx(gdImagePtr im, gdIOCtxPtr outfile, int quantization); + + +/* + Group: Types + + typedef: gdSink + + typedef: gdSinkPtr + + *Note:* This interface is *obsolete* and kept only for + *compatibility*. Use instead. + + Represents a "sink" (destination) to which a PNG can be + written. Programmers who do not wish to write PNGs to a file can + provide their own alternate output mechanism, using the + function. See the documentation of that + function for an example of the proper use of this type. + + > typedef struct { + > int (*sink) (void *context, char *buffer, int len); + > void *context; + > } gdSink, *gdSinkPtr; + + The _sink_ function must return -1 on error, otherwise the number of + bytes written, which must be equal to len. + + _context_ will be passed to your sink function. + +*/ -/* Best to free this memory with gdFree(), not free() */ -void *gdImageJpegPtr(gdImagePtr im, int *size, int quality); - -gdImagePtr gdImageCreateFromGif(FILE *fd); -gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr in); -gdImagePtr gdImageCreateFromGifSource(gdSourcePtr in); - -void gdImageAvif(gdImagePtr im, FILE *outfile); -void gdImageAvifEx(gdImagePtr im, FILE *outfile, int quality, int speed); -void *gdImageAvifPtr(gdImagePtr im, int *size); -void *gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed); -void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed); - -/* A custom data sink. For backwards compatibility. Use - gdIOCtx instead. */ -/* The sink function must return -1 on error, otherwise the number - of bytes written, which must be equal to len. */ -/* context will be passed to your sink function. */ typedef struct { - int (*sink) (void *context, const char *buffer, int len); - void *context; + int (*sink)(void *context, const char *buffer, int len); + void *context; } gdSink, *gdSinkPtr; -void gdImagePngToSink(gdImagePtr im, gdSinkPtr out); +BGD_DECLARE(void) gdImagePngToSink(gdImagePtr im, gdSinkPtr out); +BGD_DECLARE(void) gdImageQoiToSink(gdImagePtr im, gdSinkPtr out); -void gdImageGd(gdImagePtr im, FILE *out); -void gdImageGd2(gdImagePtr im, FILE *out, int cs, int fmt); +BGD_DECLARE(void) gdImageGd2(gdImagePtr im, FILE *out, int cs, int fmt); /* Best to free this memory with gdFree(), not free() */ -void* gdImagePngPtr(gdImagePtr im, int *size); +BGD_DECLARE(void *) gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size); -/* Best to free this memory with gdFree(), not free() */ -void* gdImageGdPtr(gdImagePtr im, int *size); -void *gdImagePngPtrEx(gdImagePtr im, int *size, int level, int basefilter); +BGD_DECLARE(void) gdImageDestroy(gdImagePtr im); -/* Best to free this memory with gdFree(), not free() */ -void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size); +/* These functions still work with truecolor images, + for which they never return error. */ +BGD_DECLARE(int) gdImageColorAllocate(gdImagePtr im, int r, int g, int b); +/* gd 2.0: palette entries with non-opaque transparency are permitted. */ +BGD_DECLARE(int) +gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a); +/* Assumes opaque is the preferred alpha channel value */ +BGD_DECLARE(int) gdImageColorClosest(gdImagePtr im, int r, int g, int b); +/* Closest match taking all four parameters into account. + A slightly different color with the same transparency + beats the exact same color with radically different + transparency */ +BGD_DECLARE(int) +gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a); +/* An alternate method */ +BGD_DECLARE(int) gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b); +/* Returns exact, 100% opaque matches only */ +BGD_DECLARE(int) gdImageColorExact(gdImagePtr im, int r, int g, int b); +/* Returns an exact match only, including alpha */ +BGD_DECLARE(int) +gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a); +/* Opaque only */ +BGD_DECLARE(int) gdImageColorResolve(gdImagePtr im, int r, int g, int b); +/* Based on gdImageColorExactAlpha and gdImageColorClosestAlpha */ +BGD_DECLARE(int) +gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a); + +/* A simpler way to obtain an opaque truecolor value for drawing on a + truecolor image. Not for use with palette images! */ -void gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int c); +#define gdTrueColor(r, g, b) (((r) << 16) + ((g) << 8) + (b)) + +/** + * Group: Color Composition + * + * Macro: gdTrueColorAlpha + * + * Compose a truecolor value from its components + * + * Parameters: + * r - The red channel (0-255) + * g - The green channel (0-255) + * b - The blue channel (0-255) + * a - The alpha channel (0-127, where 127 is fully transparent, and 0 is + * completely opaque). + * + * See also: + * - + * - + * - + * - + * - + */ +#define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + ((r) << 16) + ((g) << 8) + (b)) + +BGD_DECLARE(void) gdImageColorDeallocate(gdImagePtr im, int color); + +/* Converts a truecolor image to a palette-based image, + using a high-quality two-pass quantization routine + which attempts to preserve alpha channel information + as well as R/G/B color information when creating + a palette. If ditherFlag is set, the image will be + dithered to approximate colors better, at the expense + of some obvious "speckling." colorsWanted can be + anything up to 256. If the original source image + includes photographic information or anything that + came out of a JPEG, 256 is strongly recommended. + + Better yet, don't use these function -- write real + truecolor PNGs and JPEGs. The disk space gain of + conversion to palette is not great (for small images + it can be negative) and the quality loss is ugly. + + DIFFERENCES: gdImageCreatePaletteFromTrueColor creates and + returns a new image. gdImageTrueColorToPalette modifies + an existing image, and the truecolor pixels are discarded. + + gdImageTrueColorToPalette() returns TRUE on success, FALSE on failure. +*/ +BGD_DECLARE(gdImagePtr) +gdImageCreatePaletteFromTrueColor(gdImagePtr im, int ditherFlag, int colorsWanted); + +BGD_DECLARE(int) +gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted); + +BGD_DECLARE(int) gdImagePaletteToTrueColor(gdImagePtr src); + +/* An attempt at getting the results of gdImageTrueColorToPalette to + * look a bit more like the original (im1 is the original and im2 is + * the palette version */ + +BGD_DECLARE(int) gdImageColorMatch(gdImagePtr im1, gdImagePtr im2); + +/* Selects quantization method used for subsequent gdImageTrueColorToPalette + calls. See gdPaletteQuantizationMethod enum (e.g. GD_QUANT_NEUQUANT, + GD_QUANT_LIQ). Speed is from 1 (highest quality) to 10 (fastest). Speed 0 + selects method-specific default (recommended). + + Returns FALSE if the given method is invalid or not available. +*/ +BGD_DECLARE(int) +gdImageTrueColorToPaletteSetMethod(gdImagePtr im, int method, int speed); + +/* + Chooses quality range that subsequent call to gdImageTrueColorToPalette will + aim for. Min and max quality is in range 1-100 (1 = ugly, 100 = perfect). Max + must be higher than min. If palette cannot represent image with at least + min_quality, then image will remain true-color. If palette can represent image + with quality better than max_quality, then lower number of colors will be + used. This function has effect only when GD_QUANT_LIQ method has been selected + and the source image is true-color. +*/ +BGD_DECLARE(void) +gdImageTrueColorToPaletteSetQuality(gdImagePtr im, int min_quality, int max_quality); + +/* Specifies a color index (if a palette image) or an + RGB color (if a truecolor image) which should be + considered 100% transparent. FOR TRUECOLOR IMAGES, + THIS IS IGNORED IF AN ALPHA CHANNEL IS BEING + SAVED. Use gdImageSaveAlpha(im, 0); to + turn off the saving of a full alpha channel in + a truecolor image. Note that gdImageColorTransparent + is usually compatible with older browsers that + do not understand full alpha channels well. TBB */ +BGD_DECLARE(void) gdImageColorTransparent(gdImagePtr im, int color); + +BGD_DECLARE(void) gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src); + +typedef int (*gdCallbackImageColor)(gdImagePtr im, int src); + +BGD_DECLARE(int) gdImageColorReplace(gdImagePtr im, int src, int dst); +BGD_DECLARE(int) +gdImageColorReplaceThreshold(gdImagePtr im, int src, int dst, float threshold); +BGD_DECLARE(int) +gdImageColorReplaceArray(gdImagePtr im, int len, int *src, int *dst); +BGD_DECLARE(int) +gdImageColorReplaceCallback(gdImagePtr im, gdCallbackImageColor callback); + +/* Replaces or blends with the background depending on the + most recent call to gdImageAlphaBlending and the + alpha channel value of 'color'; default is to overwrite. + Tiling and line styling are also implemented + here. All other gd drawing functions pass through this call, + allowing for many useful effects. + Overlay and multiply effects are used when gdImageAlphaBlending + is passed gdEffectOverlay and gdEffectMultiply */ + +BGD_DECLARE(void) gdImageSetPixel(gdImagePtr im, int x, int y, int color); + +BGD_DECLARE(int) gdImageGetPixel(gdImagePtr im, int x, int y); +BGD_DECLARE(int) gdImageGetTrueColorPixel(gdImagePtr im, int x, int y); + +BGD_DECLARE(void) gdImageAABlend(gdImagePtr im); + +BGD_DECLARE(void) gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); +BGD_DECLARE(void) gdImageAALine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); + +/* For backwards compatibility only. Use gdImageSetStyle() + for much more flexible line drawing. */ +BGD_DECLARE(void) gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color); +/* Corners specified (not width and height). Upper left first, lower right + second. */ +BGD_DECLARE(void) gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color); +/* Solid bar. Upper left corner first, lower right corner second. */ +BGD_DECLARE(void) gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color); +BGD_DECLARE(void) gdImageSetClip(gdImagePtr im, int x1, int y1, int x2, int y2); +BGD_DECLARE(void) gdImageGetClip(gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P); +BGD_DECLARE(void) +gdImageSetResolution(gdImagePtr im, const unsigned int res_x, const unsigned int res_y); +BGD_DECLARE(void) gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color); +BGD_DECLARE(void) gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color); +BGD_DECLARE(void) +gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color); +BGD_DECLARE(void) +gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color); +BGD_DECLARE(void) +gdImageString16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color); +BGD_DECLARE(void) +gdImageStringUp16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color); + +/* + * The following functions are required to be called prior to the + * use of any sort of threads in a module load / shutdown function + * respectively. + */ +void gdFontCacheMutexSetup(void); +void gdFontCacheMutexShutdown(void); + +/* 2.0.16: for thread-safe use of gdImageStringFT and friends, + * call this before allowing any thread to call gdImageStringFT. + * Otherwise it is invoked by the first thread to invoke + * gdImageStringFT, with a very small but real risk of a race condition. + * Return 0 on success, nonzero on failure to initialize freetype. + */ +int gdFontCacheSetup(void); + +/* Optional: clean up after application is done using fonts in gdImageStringFT(). */ +void gdFontCacheShutdown(void); + +BGD_DECLARE(void) gdFreeFontCache(void); + +/* Calls gdImageStringFT. Provided for backwards compatibility only. */ +BGD_DECLARE(char *) +gdImageStringTTF(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string); + +/* FreeType 2 text output */ +BGD_DECLARE(char *) +gdImageStringFT(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string); + +/* + Group: Types + + typedef: gdFTStringExtra + + typedef: gdFTStringExtraPtr + + A structure and associated pointer type used to pass additional + parameters to the function. See + for the structure definition. + + Thanks to Wez Furlong. +*/ + +/* 2.0.5: provides an extensible way to pass additional parameters. + Thanks to Wez Furlong, sorry for the delay. */ +typedef struct { + int flags; /* Logical OR of gdFTEX_ values */ + double linespacing; /* fine tune line spacing for '\n' */ + int charmap; /* gdFTEX_Unicode, gdFTEX_Shift_JIS, gdFTEX_Big5, + or gdFTEX_Adobe_Custom/gdFTEX_MacRoman */ + int hdpi; /* if (flags & gdFTEX_RESOLUTION) */ + int vdpi; /* if (flags & gdFTEX_RESOLUTION) */ + char *xshow; /* gdMalloc'ed result if gdFTEX_XSHOW is set */ + char *fontpath; /* gdMalloc'ed result if gdFTEX_RETURNFONTPATHNAME is set */ +} gdFTStringExtra, *gdFTStringExtraPtr; + +#define gdFTEX_LINESPACE 1 +#define gdFTEX_CHARMAP 2 +#define gdFTEX_RESOLUTION 4 +#define gdFTEX_DISABLE_KERNING 8 +#define gdFTEX_XSHOW 16 +#define gdFTEX_FONTPATHNAME 32 +#define gdFTEX_FONTCONFIG 64 +#define gdFTEX_RETURNFONTPATHNAME 128 + +BGD_DECLARE(int) gdFTUseFontConfig(int flag); + +/* These are NOT flags; set one in 'charmap' if you set the + gdFTEX_CHARMAP bit in 'flags'. */ +#define gdFTEX_Unicode 0 +#define gdFTEX_Shift_JIS 1 +#define gdFTEX_Big5 2 +#define gdFTEX_Adobe_Custom 3 +#define gdFTEX_MacRoman gdFTEX_Adobe_Custom + +BGD_DECLARE(char *) +gdImageStringFTEx(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string, gdFTStringExtraPtr strex); + +/* + Group: Types + + typedef: gdPoint + + typedef: gdPointPtr + + Represents a point in the coordinate space of the image; used by + , and + for polygon drawing. + + > typedef struct { + > int x, y; + > } gdPoint, *gdPointPtr; + +*/ +typedef struct { + int x, y; +} gdPoint, *gdPointPtr; + +/** + * Typedef: gdRect + * + * A rectangle in the coordinate space of the image + * + * Members: + * x - The x-coordinate of the upper left corner. + * y - The y-coordinate of the upper left corner. + * width - The width. + * height - The height. + * + * Typedef: gdRectPtr + * + * A pointer to a + */ +typedef struct { + int x, y; + int width, height; +} gdRect, *gdRectPtr; + +BGD_DECLARE(void) gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c); +BGD_DECLARE(void) gdImageOpenPolygon(gdImagePtr im, gdPointPtr p, int n, int c); +BGD_DECLARE(void) +gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c); + +BGD_DECLARE(void) +gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style); +BGD_DECLARE(void) +gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color); +BGD_DECLARE(void) +gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color); +BGD_DECLARE(void) +gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color); /* Style is a bitwise OR ( | operator ) of these. - gdArc and gdChord are mutually exclusive; - gdChord just connects the starting and ending - angles with a straight line, while gdArc produces - a rounded edge. gdPie is a synonym for gdArc. - gdNoFill indicates that the arc or chord should be - outlined, not filled. gdEdged, used together with - gdNoFill, indicates that the beginning and ending - angles should be connected to the center; this is - a good way to outline (rather than fill) a - 'pie slice'. */ -#define gdArc 0 -#define gdPie gdArc + gdArc and gdChord are mutually exclusive; + gdChord just connects the starting and ending + angles with a straight line, while gdArc produces + a rounded edge. gdPie is a synonym for gdArc. + gdNoFill indicates that the arc or chord should be + outlined, not filled. gdEdged, used together with + gdNoFill, indicates that the beginning and ending + angles should be connected to the center; this is + a good way to outline (rather than fill) a + 'pie slice'. */ +#define gdArc 0 +#define gdPie gdArc #define gdChord 1 #define gdNoFill 2 #define gdEdged 4 -void gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style); -void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color); -void gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color); -void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color); -void gdImageFill(gdImagePtr im, int x, int y, int color); -void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h); -void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, - int srcX, int srcY, int w, int h, int pct); -void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, - int srcX, int srcY, int w, int h, int pct); +BGD_DECLARE(void) +gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color); +BGD_DECLARE(void) gdImageFill(gdImagePtr im, int x, int y, int color); +BGD_DECLARE(void) +gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h); +BGD_DECLARE(void) +gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, + int h, int pct); +BGD_DECLARE(void) +gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, + int h, int pct); /* Stretches or shrinks to fit, as needed. Does NOT attempt - to average the entire set of source pixels that scale down onto the - destination pixel. */ -void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH); + to average the entire set of source pixels that scale down onto the + destination pixel. */ +BGD_DECLARE(void) +gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, + int dstH, int srcW, int srcH); /* gd 2.0: stretches or shrinks to fit, as needed. When called with a - truecolor destination image, this function averages the - entire set of source pixels that scale down onto the - destination pixel, taking into account what portion of the - destination pixel each source pixel represents. This is a - floating point operation, but this is not a performance issue - on modern hardware, except for some embedded devices. If the - destination is a palette image, gdImageCopyResized is - substituted automatically. */ -void gdImageCopyResampled(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH); - -gdImagePtr gdImageClone(gdImagePtr src); - -void gdImageSetBrush(gdImagePtr im, gdImagePtr brush); -void gdImageSetTile(gdImagePtr im, gdImagePtr tile); -void gdImageSetAntiAliased(gdImagePtr im, int c); -void gdImageSetAntiAliasedDontBlend(gdImagePtr im, int c, int dont_blend); -void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels); + truecolor destination image, this function averages the + entire set of source pixels that scale down onto the + destination pixel, taking into account what portion of the + destination pixel each source pixel represents. This is a + floating point operation, but this is not a performance issue + on modern hardware, except for some embedded devices. If the + destination is a palette image, gdImageCopyResized is + substituted automatically. */ +BGD_DECLARE(void) +gdImageCopyResampled(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, + int dstW, int dstH, int srcW, int srcH); + +/* Source is a rectangle, with its upper left corner at + srcX and srcY. Destination is the *center* of + the rotated copy. Angle is in degrees, same as + gdImageArc. Floating point destination center + coordinates allow accurate rotation of + objects of odd-numbered width or height. */ +BGD_DECLARE(void) +gdImageCopyRotated(gdImagePtr dst, gdImagePtr src, double dstX, double dstY, int srcX, int srcY, + int srcWidth, int srcHeight, int angle); + +BGD_DECLARE(gdImagePtr) gdImageClone(gdImagePtr src); + +BGD_DECLARE(void) gdImageSetBrush(gdImagePtr im, gdImagePtr brush); +BGD_DECLARE(void) gdImageSetTile(gdImagePtr im, gdImagePtr tile); +BGD_DECLARE(void) gdImageSetAntiAliased(gdImagePtr im, int c); +BGD_DECLARE(void) gdImageSetAntiAliasedDontBlend(gdImagePtr im, int c, int dont_blend); +BGD_DECLARE(void) gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels); + /* Line thickness (defaults to 1). Affects lines, ellipses, - rectangles, polygons and so forth. */ -void gdImageSetThickness(gdImagePtr im, int thickness); + rectangles, polygons and so forth. */ +BGD_DECLARE(void) gdImageSetThickness(gdImagePtr im, int thickness); /* On or off (1 or 0) for all three of these. */ -void gdImageInterlace(gdImagePtr im, int interlaceArg); -void gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg); -void gdImageAntialias(gdImagePtr im, int antialias); -void gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg); - -enum gdPixelateMode { - GD_PIXELATE_UPPERLEFT, - GD_PIXELATE_AVERAGE +BGD_DECLARE(void) gdImageInterlace(gdImagePtr im, int interlaceArg); +BGD_DECLARE(void) gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg); +BGD_DECLARE(void) gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg); + +/** + * Group: Color Quantization + * + * Enum: gdPaletteQuantizationMethod + * + * Constants: + * GD_QUANT_DEFAULT - GD_QUANT_LIQ if libimagequant is available, + * GD_QUANT_JQUANT otherwise. + * GD_QUANT_JQUANT - libjpeg's old median cut. Fast, but only uses 16-bit + * color. + * GD_QUANT_NEUQUANT - NeuQuant - approximation using Kohonen neural network. + * GD_QUANT_LIQ - A combination of algorithms used in libimagequant + * aiming for the highest quality at cost of speed. + * + * Note that GD_QUANT_JQUANT does not retain the alpha channel, and + * GD_QUANT_NEUQUANT does not support dithering. + * + * See also: + * - + */ +enum gdPaletteQuantizationMethod { + GD_QUANT_DEFAULT = 0, + GD_QUANT_JQUANT = 1, + GD_QUANT_NEUQUANT = 2, + GD_QUANT_LIQ = 3 }; +BGD_DECLARE(gdImagePtr) +gdImageNeuQuant(gdImagePtr im, const int max_color, int sample_factor); + +/* filters section + * + * Negate the imag src, white becomes black, + * The red, green, and blue intensities of an image are negated. + * White becomes black, yellow becomes blue, etc. + */ + +enum gdPixelateMode { GD_PIXELATE_UPPERLEFT, GD_PIXELATE_AVERAGE }; -int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode); +BGD_DECLARE(int) +gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode); typedef struct { - int sub; - int plus; - unsigned int num_colors; - int *colors; - unsigned int seed; + int sub; + int plus; + unsigned int num_colors; + int *colors; + unsigned int seed; } gdScatter, *gdScatterPtr; -int gdImageScatter(gdImagePtr im, int sub, int plus); -int gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors); -int gdImageScatterEx(gdImagePtr im, gdScatterPtr s); +BGD_DECLARE(int) gdImageScatter(gdImagePtr im, int sub, int plus); +BGD_DECLARE(int) +gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors); +BGD_DECLARE(int) gdImageScatterEx(gdImagePtr im, gdScatterPtr s); +BGD_DECLARE(int) gdImageSmooth(gdImagePtr im, float weight); +BGD_DECLARE(int) gdImageMeanRemoval(gdImagePtr im); +BGD_DECLARE(int) gdImageEmboss(gdImagePtr im); +BGD_DECLARE(int) gdImageGaussianBlur(gdImagePtr im); +BGD_DECLARE(int) gdImageEdgeDetectQuick(gdImagePtr src); +BGD_DECLARE(int) gdImageSelectiveBlur(gdImagePtr src); +BGD_DECLARE(int) +gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, float offset); +BGD_DECLARE(int) +gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha); +BGD_DECLARE(int) gdImageContrast(gdImagePtr src, double contrast); +BGD_DECLARE(int) gdImageBrightness(gdImagePtr src, int brightness); +BGD_DECLARE(int) gdImageGrayScale(gdImagePtr src); +BGD_DECLARE(int) gdImageNegate(gdImagePtr src); + +BGD_DECLARE(gdImagePtr) +gdImageCopyGaussianBlurred(gdImagePtr src, int radius, double sigma); -/* Macros to access information about images. */ - -/* Returns nonzero if the image is a truecolor image, - zero for a palette image. */ +/** + * Group: Accessor Macros + */ +/** + * Macro: gdImageTrueColor + * + * Whether an image is a truecolor image. + * + * Parameters: + * im - The image. + * + * Returns: + * Non-zero if the image is a truecolor image, zero for palette images. + */ #define gdImageTrueColor(im) ((im)->trueColor) +/** + * Macro: gdImageSX + * + * Gets the width (in pixels) of an image. + * + * Parameters: + * im - The image. + */ #define gdImageSX(im) ((im)->sx) -#define gdImageSY(im) ((im)->sy) -#define gdImageColorsTotal(im) ((im)->colorsTotal) -#define gdImageRed(im, c) ((im)->trueColor ? gdTrueColorGetRed(c) : \ - (im)->red[(c)]) -#define gdImageGreen(im, c) ((im)->trueColor ? gdTrueColorGetGreen(c) : \ - (im)->green[(c)]) -#define gdImageBlue(im, c) ((im)->trueColor ? gdTrueColorGetBlue(c) : \ - (im)->blue[(c)]) -#define gdImageAlpha(im, c) ((im)->trueColor ? gdTrueColorGetAlpha(c) : \ - (im)->alpha[(c)]) -#define gdImageGetTransparent(im) ((im)->transparent) -#define gdImageGetInterlaced(im) ((im)->interlace) -/* These macros provide direct access to pixels in - palette-based and truecolor images, respectively. - If you use these macros, you must perform your own - bounds checking. Use of the macro for the correct type - of image is also your responsibility. */ -#define gdImagePalettePixel(im, x, y) (im)->pixels[(y)][(x)] -#define gdImageTrueColorPixel(im, x, y) (im)->tpixels[(y)][(x)] -#define gdImageResolutionX(im) (im)->res_x -#define gdImageResolutionY(im) (im)->res_y - -/* I/O Support routines. */ +/** + * Macro: gdImageSY + * + * Gets the height (in pixels) of an image. + * + * Parameters: + * im - The image. + */ +#define gdImageSY(im) ((im)->sy) -gdIOCtx* gdNewFileCtx(FILE*); -gdIOCtx* gdNewDynamicCtx(int, void*); -gdIOCtx *gdNewDynamicCtxEx(int size, void *data, int freeFlag); -gdIOCtx* gdNewSSCtx(gdSourcePtr in, gdSinkPtr out); -void* gdDPExtractData(struct gdIOCtx* ctx, int *size); +/** + * Macro: gdImageColorsTotal + * + * Gets the number of colors in the palette. + * + * This macro is only valid for palette images. + * + * Parameters: + * im - The image + */ +#define gdImageColorsTotal(im) ((im)->colorsTotal) -#define GD2_CHUNKSIZE 128 -#define GD2_CHUNKSIZE_MIN 64 -#define GD2_CHUNKSIZE_MAX 4096 +/** + * Macro: gdImageRed + * + * Gets the red component value of a given color. + * + * Parameters: + * im - The image. + * c - The color. + */ +#define gdImageRed(im, c) ((im)->trueColor ? gdTrueColorGetRed(c) : (im)->red[(c)]) -#define GD2_VERS 2 -#define GD2_ID "gd2" -#define GD2_FMT_RAW 1 -#define GD2_FMT_COMPRESSED 2 +/** + * Macro: gdImageGreen + * + * Gets the green component value of a given color. + * + * Parameters: + * im - The image. + * c - The color. + */ +#define gdImageGreen(im, c) ((im)->trueColor ? gdTrueColorGetGreen(c) : (im)->green[(c)]) +/** + * Macro: gdImageBlue + * + * Gets the blue component value of a given color. + * + * Parameters: + * im - The image. + * c - The color. + */ +#define gdImageBlue(im, c) ((im)->trueColor ? gdTrueColorGetBlue(c) : (im)->blue[(c)]) -/* filters section +/** + * Macro: gdImageAlpha * - * Negate the imag src, white becomes black, - * The red, green, and blue intensities of an image are negated. - * White becomes black, yellow becomes blue, etc. + * Gets the alpha component value of a given color. + * + * Parameters: + * im - The image. + * c - The color. */ -int gdImageNegate(gdImagePtr src); +#define gdImageAlpha(im, c) ((im)->trueColor ? gdTrueColorGetAlpha(c) : (im)->alpha[(c)]) -/* Convert the image src to a grayscale image */ -int gdImageGrayScale(gdImagePtr src); +/** + * Macro: gdImageGetTransparent + * + * Gets the transparent color of the image. + * + * Parameters: + * im - The image. + * + * See also: + * - + */ +#define gdImageGetTransparent(im) ((im)->transparent) -/* Set the brightness level for the image src */ -int gdImageBrightness(gdImagePtr src, int brightness); +/** + * Macro: gdImageGetInterlaced + * + * Whether an image is interlaced. + * + * Parameters: + * im - The image. + * + * Returns: + * Non-zero for interlaced images, zero otherwise. + * + * See also: + * - + */ +#define gdImageGetInterlaced(im) ((im)->interlace) -/* Set the contrast level for the image */ -int gdImageContrast(gdImagePtr src, double contrast); +/** + * Macro: gdImagePalettePixel + * + * Gets the color of a pixel. + * + * Calling this macro is only valid for palette images. + * No bounds checking is done for the coordinates. + * + * Parameters: + * im - The image. + * x - The x-coordinate. + * y - The y-coordinate. + * + * See also: + * - + * - + */ +#define gdImagePalettePixel(im, x, y) (im)->pixels[(y)][(x)] -/* Simply adds or subtracts respectively red, green or blue to a pixel */ -int gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha); +/** + * Macro: gdImageTrueColorPixel + * + * Gets the color of a pixel. + * + * Calling this macro is only valid for truecolor images. + * No bounds checking is done for the coordinates. + * + * Parameters: + * im - The image. + * x - The x-coordinate. + * y - The y-coordinate. + * + * See also: + * - + * - + */ +#define gdImageTrueColorPixel(im, x, y) (im)->tpixels[(y)][(x)] -/* Image convolution by a 3x3 custom matrix */ -int gdImageConvolution(gdImagePtr src, float ft[3][3], float filter_div, float offset); +/** + * Macro: gdImageResolutionX + * + * Gets the horizontal resolution in DPI. + * + * Parameters: + * im - The image. + * + * See also: + * - + * - + */ +#define gdImageResolutionX(im) (im)->res_x -int gdImageEdgeDetectQuick(gdImagePtr src); +/** + * Macro: gdImageResolutionY + * + * Gets the vertical resolution in DPI. + * + * Parameters: + * im - The image. + * + * See also: + * - + * - + */ +#define gdImageResolutionY(im) (im)->res_y -int gdImageGaussianBlur(gdImagePtr im); +/* I/O Support routines. */ -int gdImageSelectiveBlur( gdImagePtr src); +BGD_DECLARE(gdIOCtxPtr) gdNewFileCtx(FILE *); +/* If data is null, size is ignored and an initial data buffer is + allocated automatically. NOTE: this function assumes gd has the right + to free or reallocate "data" at will! Also note that gd will free + "data" when the IO context is freed. If data is not null, it must point + to memory allocated with gdMalloc, or by a call to gdImage[something]Ptr. + If not, see gdNewDynamicCtxEx for an alternative. */ +BGD_DECLARE(gdIOCtxPtr) gdNewDynamicCtx(int size, void *data); +/* 2.0.21: if freeFlag is nonzero, gd will free and/or reallocate "data" as + needed as described above. If freeFlag is zero, gd will never free + or reallocate "data", which means that the context should only be used + for *reading* an image from a memory buffer, or writing an image to a + memory buffer which is already large enough. If the memory buffer is + not large enough and an image write is attempted, the write operation + will fail. Those wishing to write an image to a buffer in memory have + a much simpler alternative in the gdImage[something]Ptr functions. */ +BGD_DECLARE(gdIOCtxPtr) gdNewDynamicCtxEx(int size, void *data, int freeFlag); +BGD_DECLARE(gdIOCtxPtr) gdNewSSCtx(gdSourcePtr in, gdSinkPtr out); +BGD_DECLARE(void *) gdDPExtractData(gdIOCtxPtr ctx, int *size); + +#define GD2_CHUNKSIZE 128 +#define GD2_CHUNKSIZE_MIN 64 +#define GD2_CHUNKSIZE_MAX 4096 + +#define GD2_VERS 2 +#define GD2_ID "gd2" +#define GD2_FMT_RAW 1 +#define GD2_FMT_COMPRESSED 2 -int gdImageEmboss(gdImagePtr im); +/* Image comparison definitions */ +BGD_DECLARE(int) gdImageCompare(gdImagePtr im1, gdImagePtr im2); -int gdImageMeanRemoval(gdImagePtr im); +typedef enum { + GD_IMAGE_DIFF_NONE, + GD_IMAGE_DIFF_OVERLAY, + GD_IMAGE_DIFF_MASK +} gdImageDiffMode; -int gdImageSmooth(gdImagePtr im, float weight); +typedef struct { + gdImageDiffMode mode; + int highlight_color; +} gdImagePerceptualDiffOptions; -/* Image comparison definitions */ -int gdImageCompare(gdImagePtr im1, gdImagePtr im2); +typedef struct { + unsigned int pixels_changed; + /* Largest normalized perceptual distance, in the range 0.0 to 1.0. */ + double maximum_delta; +} gdImagePerceptualDiffResult; -void gdImageFlipHorizontal(gdImagePtr im); -void gdImageFlipVertical(gdImagePtr im); -void gdImageFlipBoth(gdImagePtr im); +/* + * Compare two equally sized images using a perceptual YIQ distance. + * + * A NULL options pointer selects an overlay with opaque red highlights. A + * non-NULL diff_image receives a newly allocated truecolor image for overlay + * and mask modes; the caller owns it and must call gdImageDestroy(). Passing + * NULL for diff_image computes statistics only. The result is always reset, + * including on failure. + * + * Returns 1 on success, or 0 for invalid arguments or allocation failure. + */ +BGD_DECLARE(int) +gdImagePerceptualDiff(gdImagePtr image1, gdImagePtr image2, double threshold, + const gdImagePerceptualDiffOptions *options, + gdImagePtr *diff_image, + gdImagePerceptualDiffResult *result); -#define GD_FLIP_HORIZONTAL 1 -#define GD_FLIP_VERTICAL 2 -#define GD_FLIP_BOTH 3 +BGD_DECLARE(void) gdImageFlipHorizontal(gdImagePtr im); +BGD_DECLARE(void) gdImageFlipVertical(gdImagePtr im); +BGD_DECLARE(void) gdImageFlipBoth(gdImagePtr im); /** * Group: Crop * * Constants: gdCropMode - * GD_CROP_DEFAULT - Default crop mode (4 corners or background) + * GD_CROP_DEFAULT - Same as GD_CROP_TRANSPARENT * GD_CROP_TRANSPARENT - Crop using the transparent color * GD_CROP_BLACK - Crop black borders * GD_CROP_WHITE - Crop white borders * GD_CROP_SIDES - Crop using colors of the 4 corners * * See also: - * + * - **/ enum gdCropMode { - GD_CROP_DEFAULT = 0, - GD_CROP_TRANSPARENT, - GD_CROP_BLACK, - GD_CROP_WHITE, - GD_CROP_SIDES, - GD_CROP_THRESHOLD + GD_CROP_DEFAULT = 0, + GD_CROP_TRANSPARENT, + GD_CROP_BLACK, + GD_CROP_WHITE, + GD_CROP_SIDES, + GD_CROP_THRESHOLD }; -gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop); -gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode); -gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold); +BGD_DECLARE(gdImagePtr) gdImageCrop(gdImagePtr src, const gdRect *crop); +BGD_DECLARE(gdImagePtr) gdImageCropAuto(gdImagePtr im, const unsigned int mode); +BGD_DECLARE(gdImagePtr) +gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold); -int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id); -gdInterpolationMethod gdImageGetInterpolationMethod(gdImagePtr im); +BGD_DECLARE(int) +gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id); +BGD_DECLARE(gdInterpolationMethod) gdImageGetInterpolationMethod(gdImagePtr im); -gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height); +BGD_DECLARE(gdImagePtr) +gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height); -gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor); +BGD_DECLARE(gdImagePtr) +gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor); typedef enum { - GD_AFFINE_TRANSLATE = 0, - GD_AFFINE_SCALE, - GD_AFFINE_ROTATE, - GD_AFFINE_SHEAR_HORIZONTAL, - GD_AFFINE_SHEAR_VERTICAL, + GD_AFFINE_TRANSLATE = 0, + GD_AFFINE_SCALE, + GD_AFFINE_ROTATE, + GD_AFFINE_SHEAR_HORIZONTAL, + GD_AFFINE_SHEAR_VERTICAL } gdAffineStandardMatrix; -int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, const double affine[6]); -int gdAffineInvert (double dst[6], const double src[6]); -int gdAffineFlip (double dst_affine[6], const double src_affine[6], const int flip_h, const int flip_v); -int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]); - -int gdAffineIdentity (double dst[6]); -int gdAffineScale (double dst[6], const double scale_x, const double scale_y); -int gdAffineRotate (double dst[6], const double angle); -int gdAffineShearHorizontal (double dst[6], const double angle); -int gdAffineShearVertical(double dst[6], const double angle); -int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y); -double gdAffineExpansion (const double src[6]); -int gdAffineRectilinear (const double src[6]); -int gdAffineEqual (const double matrix1[6], const double matrix2[6]); -int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]); -int gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, gdRectPtr src_region, const double affine[6]); +BGD_DECLARE(int) +gdAffineApplyToPointF(gdPointFPtr dst, const gdPointFPtr src, const double affine[6]); +BGD_DECLARE(int) gdAffineInvert(double dst[6], const double src[6]); +BGD_DECLARE(int) +gdAffineFlip(double dst_affine[6], const double src_affine[6], const int flip_h, const int flip_v); +BGD_DECLARE(int) +gdAffineConcat(double dst[6], const double m1[6], const double m2[6]); + +BGD_DECLARE(int) gdAffineIdentity(double dst[6]); +BGD_DECLARE(int) +gdAffineScale(double dst[6], const double scale_x, const double scale_y); +BGD_DECLARE(int) gdAffineRotate(double dst[6], const double angle); +BGD_DECLARE(int) gdAffineShearHorizontal(double dst[6], const double angle); +BGD_DECLARE(int) gdAffineShearVertical(double dst[6], const double angle); +BGD_DECLARE(int) +gdAffineTranslate(double dst[6], const double offset_x, const double offset_y); +BGD_DECLARE(double) gdAffineExpansion(const double src[6]); +BGD_DECLARE(int) gdAffineRectilinear(const double src[6]); +BGD_DECLARE(int) +gdAffineEqual(const double matrix1[6], const double matrix2[6]); +BGD_DECLARE(int) +gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, + const double affine[6]); +BGD_DECLARE(int) +gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, + gdRectPtr src_region, const double affine[6]); /* gdTransformAffineCopy(gdImagePtr dst, int x0, int y0, int x1, int y1, - const gdImagePtr src, int src_width, int src_height, - const double affine[6]); + const gdImagePtr src, int src_width, int src_height, + const double affine[6]); */ -int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox); - +BGD_DECLARE(int) +gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox); -#define GD_CMP_IMAGE 1 /* Actual image IS different */ -#define GD_CMP_NUM_COLORS 2 /* Number of Colours in palette differ */ -#define GD_CMP_COLOR 4 /* Image colours differ */ -#define GD_CMP_SIZE_X 8 /* Image width differs */ -#define GD_CMP_SIZE_Y 16 /* Image heights differ */ -#define GD_CMP_TRANSPARENT 32 /* Transparent colour */ -#define GD_CMP_BACKGROUND 64 /* Background colour */ -#define GD_CMP_INTERLACE 128 /* Interlaced setting */ -#define GD_CMP_TRUECOLOR 256 /* Truecolor vs palette differs */ +/** + * Group: Image Comparison + * + * Constants: + * GD_CMP_IMAGE - Actual image IS different + * GD_CMP_NUM_COLORS - Number of colors in pallette differ + * GD_CMP_COLOR - Image colors differ + * GD_CMP_SIZE_X - Image width differs + * GD_CMP_SIZE_Y - Image heights differ + * GD_CMP_TRANSPARENT - Transparent color differs + * GD_CMP_BACKGROUND - Background color differs + * GD_CMP_INTERLACE - Interlaced setting differs + * GD_CMP_TRUECOLOR - Truecolor vs palette differs + * + * See also: + * - + */ +#define GD_CMP_IMAGE 1 +#define GD_CMP_NUM_COLORS 2 +#define GD_CMP_COLOR 4 +#define GD_CMP_SIZE_X 8 +#define GD_CMP_SIZE_Y 16 +#define GD_CMP_TRANSPARENT 32 +#define GD_CMP_BACKGROUND 64 +#define GD_CMP_INTERLACE 128 +#define GD_CMP_TRUECOLOR 256 /* resolution affects ttf font rendering, particularly hinting */ -#define GD_RESOLUTION 96 /* pixels per inch */ +#define GD_RESOLUTION 96 /* pixels per inch */ + +/* Version information functions */ +BGD_DECLARE(int) gdMajorVersion(void); +BGD_DECLARE(int) gdMinorVersion(void); +BGD_DECLARE(int) gdReleaseVersion(void); +BGD_DECLARE(const char *) gdExtraVersion(void); +BGD_DECLARE(const char *) gdVersionString(void); + +/* newfangled special effects */ +#include "gdfx.h" #ifdef __cplusplus } #endif /* 2.0.12: this now checks the clipping rectangle */ -#define gdImageBoundsSafe(im, x, y) (!((((y) < (im)->cy1) || ((y) > (im)->cy2)) || (((x) < (im)->cx1) || ((x) > (im)->cx2)))) +#define gdImageBoundsSafe(im, x, y) \ + (!((((y) < (im)->cy1) || ((y) > (im)->cy2)) || (((x) < (im)->cx1) || ((x) > (im)->cx2)))) #endif /* GD_H */ diff --git a/ext/gd/libgd/gd2copypal.c b/ext/gd/libgd/gd2copypal.c index ae9b559f17b2..16a441ddb8a7 100644 --- a/ext/gd/libgd/gd2copypal.c +++ b/ext/gd/libgd/gd2copypal.c @@ -1,6 +1,6 @@ -#include #include "gd.h" +#include #include /* A short program which converts a .png file into a .gd file, for @@ -8,58 +8,51 @@ basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - gdImagePtr pal; - FILE *in, *out; - if (argc != 3) - { - fprintf (stderr, "Usage: gd2copypal palettefile.gd2 filename.gd2\n"); - exit (1); + gdImagePtr im; + gdImagePtr pal; + FILE *in, *out; + if (argc != 3) { + fprintf(stderr, "Usage: gd2copypal palettefile.gd2 filename.gd2\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Palette file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Palette file does not exist!\n"); + exit(1); } - pal = gdImageCreateFromGd2 (in); - fclose (in); - if (!pal) - { - fprintf (stderr, "Palette is not in GD2 format!\n"); - exit (1); + pal = gdImageCreateFromGd2(in); + fclose(in); + if (!pal) { + fprintf(stderr, "Palette is not in GD2 format!\n"); + exit(1); } - in = fopen (argv[2], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[2], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromGd2 (in); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in GD2 format!\n"); - exit (1); + im = gdImageCreateFromGd2(in); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in GD2 format!\n"); + exit(1); } - gdImagePaletteCopy (im, pal); + gdImagePaletteCopy(im, pal); - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } - gdImageGd2 (im, out, 128, 2); - fclose (out); - gdImageDestroy (pal); - gdImageDestroy (im); + gdImageGd2(im, out, 128, 2); + fclose(out); + gdImageDestroy(pal); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/gd2time.c b/ext/gd/libgd/gd2time.c index 18e6c0c9e4c2..63bbf29b020e 100644 --- a/ext/gd/libgd/gd2time.c +++ b/ext/gd/libgd/gd2time.c @@ -1,60 +1,55 @@ -#include -#include /* for atoi */ -#include /* For time */ #include "gd.h" +#include +#include /* for atoi */ +#include /* For time */ /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in; - int x, y, w, h; - int c; - int i; - int t0; - - if (argc != 7) - { - fprintf (stderr, "Usage: gd2time filename.gd count x y w h\n"); - exit (1); + gdImagePtr im; + FILE *in; + int x, y, w, h; + int c; + int i; + int t0; + + if (argc != 7) { + fprintf(stderr, "Usage: gd2time filename.gd count x y w h\n"); + exit(1); } - c = atoi (argv[2]); - x = atoi (argv[3]); - y = atoi (argv[4]); - w = atoi (argv[5]); - h = atoi (argv[6]); - - printf ("Extracting %d times from (%d, %d), size is %dx%d\n", c, x, y, w, h); - - t0 = time (0); - for (i = 0; i < c; i++) - { - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); - } - - im = gdImageCreateFromGd2Part (in, x, y, w, h); - fclose (in); - - if (!im) - { - fprintf (stderr, "Error reading source file!\n"); - exit (1); - } - gdImageDestroy (im); + c = atoi(argv[2]); + x = atoi(argv[3]); + y = atoi(argv[4]); + w = atoi(argv[5]); + h = atoi(argv[6]); + + printf("Extracting %d times from (%d, %d), size is %dx%d\n", c, x, y, w, h); + + t0 = time(0); + for (i = 0; i < c; i++) { + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); + } + + im = gdImageCreateFromGd2Part(in, x, y, w, h); + fclose(in); + + if (!im) { + fprintf(stderr, "Error reading source file!\n"); + exit(1); + } + gdImageDestroy(im); }; - t0 = time (0) - t0; - printf ("%d seconds to extract (& destroy) %d times\n", t0, c); + t0 = time(0) - t0; + printf("%d seconds to extract (& destroy) %d times\n", t0, c); - return 0; + return 0; } diff --git a/ext/gd/libgd/gd2topng.c b/ext/gd/libgd/gd2topng.c index 6db5eb30037b..05954b658b42 100644 --- a/ext/gd/libgd/gd2topng.c +++ b/ext/gd/libgd/gd2topng.c @@ -1,49 +1,44 @@ -#include #include "gd.h" +#include /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in, *out; - if (argc != 3) - { - fprintf (stderr, "Usage: gd2topng filename.gd2 filename.png\n"); - exit (1); + gdImagePtr im; + FILE *in, *out; + if (argc != 3) { + fprintf(stderr, "Usage: gd2topng filename.gd2 filename.png\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromGd2 (in); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in GD2 format!\n"); - exit (1); + im = gdImageCreateFromGd2(in); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in GD2 format!\n"); + exit(1); } - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } #ifdef HAVE_LIBPNG - gdImagePng (im, out); + gdImagePng(im, out); #else - fprintf(stderr, "No PNG library support available.\n"); + fprintf(stderr, "No PNG library support available.\n"); #endif - fclose (out); - gdImageDestroy (im); + fclose(out); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/gd_array.c b/ext/gd/libgd/gd_array.c new file mode 100644 index 000000000000..3471a5631d71 --- /dev/null +++ b/ext/gd/libgd/gd_array.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd.h" +#include "gd_array.h" +#include "gd_color.h" +#include "gd_errors.h" +#include "gdhelpers.h" + +void gdArrayInit(gdArrayPtr array, unsigned int element_size) +{ + array->size = 0; + array->cnt_elements = 0; + array->element_size = element_size; + array->elements = NULL; +} + +void gdArrayDestroy(gdArrayPtr array) { gdFree(array->elements); } + +void gdArrayTruncate(gdArrayPtr array, unsigned int cnt_elements) +{ + if (cnt_elements < array->cnt_elements) + array->cnt_elements = cnt_elements; +} + +void *gdArrayIndex(gdArrayPtr array, unsigned int index) +{ + if (index == 0 && array->cnt_elements == 0) + return NULL; + + return (unsigned char *)array->elements + index * array->element_size; +} + +const void *gdArrayIndexConst(gdArrayPtr array, unsigned int index) +{ + if (index == 0 && array->cnt_elements == 0) + return NULL; + + if (index >= array->cnt_elements) + return NULL; + + return (const unsigned char *)array->elements + index * array->element_size; +} + +int gdArrayReallocBy(gdArrayPtr array, unsigned int additional) +{ + char *new_elements; + unsigned int old_size = array->size; + unsigned int required_size = array->cnt_elements + additional; + unsigned int new_size; + + /* check for integer overflow */ + if (required_size > INT_MAX || required_size < array->cnt_elements) + return 0; + + if (required_size <= old_size) + return 1; + + if (old_size == 0) + new_size = 1; + else + new_size = old_size * 2; + + while (new_size <= required_size) { + if (new_size > (unsigned int)INT_MAX / 2) { + new_size = required_size; + break; + } + new_size *= 2; + } + + array->size = new_size; + + if (array->size > (unsigned int)INT_MAX || array->element_size > (unsigned int)INT_MAX || + overflow2((int)array->size, (int)array->element_size)) { + array->size = old_size; + return 0; + } + new_elements = gdRealloc(array->elements, (size_t)array->size * array->element_size); + + if (new_elements == NULL) { + array->size = old_size; + return 0; + } + + array->elements = new_elements; + + return 1; +} + +int gdArrayAppend(gdArrayPtr array, const void *element) +{ + return gdArrayAppendMultiple(array, element, 1); +} + +int gdArrayAppendMultiple(gdArrayPtr array, const void *elements, unsigned int cnt_elements) +{ + int status; + void *dest; + + status = gdArrayAlloc(array, cnt_elements, &dest); + if (!status) + return status; + memcpy(dest, elements, (size_t)cnt_elements * array->element_size); + + return 1; +} + +int gdArrayAlloc(gdArrayPtr array, unsigned int cnt_elements, void **elements) +{ + int status; + + status = gdArrayReallocBy(array, cnt_elements); + if (!status) + return status; + + *elements = + (unsigned char *)array->elements + (size_t)array->cnt_elements * array->element_size; + + array->cnt_elements += cnt_elements; + + return 1; +} + +unsigned int gdArrayNumElements(const gdArrayPtr array) { return array->cnt_elements; } + +unsigned int gdArraySize(const gdArrayPtr array) { return array->size; } + +void *gdArrayGetData(const gdArrayPtr array) { return array->elements; } diff --git a/ext/gd/libgd/gd_array.h b/ext/gd/libgd/gd_array.h new file mode 100644 index 000000000000..d9408637cbc4 --- /dev/null +++ b/ext/gd/libgd/gd_array.h @@ -0,0 +1,57 @@ +#ifndef GD_ARRAY_H +#define GD_ARRAY_H 1 + +typedef struct gdArrayStruct { + unsigned int size; + unsigned int cnt_elements; + unsigned int element_size; + void *elements; +} gdArray; +typedef gdArray *gdArrayPtr; + +/* +C++ Vect like for C. +element_size is f.e. sizeof(gdPath) equivalent to +gdPathPtr path; +or +gdPathPtr path[12]; for a fixed length + +Each function takes a pointers to the array. +*/ + +/* Initialize a gdArray, gdArrayarray allocation and freed is the caller's responsability */ +void gdArrayInit(gdArrayPtr array, unsigned int element_size); + +/* Destroy the internal data of a gdArray, gdArrayarray has to be freed by the caller */ +void gdArrayDestroy(gdArrayPtr array); + +/* Growth the internal data storage to be able to store the additional requested amount (of + * elements) */ +int gdArrayReallocBy(gdArrayPtr array, unsigned int additional); + +/* Reduce the elements to cnt_elements (storage will remain the same until more are +requested) */ +void gdArrayTruncate(gdArrayPtr array, unsigned int cnt_elements); + +/* Append an element at the end of the array */ +int gdArrayAppend(gdArrayPtr array, const void *element); + +/* Append cnt_elements elements to the array. + *elements is the actual gdSpanPtr[12] for 12 elements */ +int gdArrayAppendMultiple(gdArrayPtr array, const void *elements, unsigned int cnt_elements); + +/* Returns the elements at the given index */ +void *gdArrayIndex(gdArrayPtr array, unsigned int index); + +/* Same as gdArrayIndex but immutable pointer */ +const void *gdArrayIndexConst(gdArrayPtr array, unsigned int index); + +/* Return the current storage size. Storage size is the available space, not the actual +amount of elements stored. Use gdArrayNumElements to know the actual stored elements count */ +unsigned int gdArraySize(const gdArrayPtr array); +unsigned int gdArrayNumElements(const gdArrayPtr array); + +/* Private for internal usage in gdArray */ +int gdArrayAlloc(gdArrayPtr array, unsigned int cnt_elements, void **elements); +void *gdArrayGetData(const gdArrayPtr array); +#endif /* GD_ARRAY_H */ diff --git a/ext/gd/libgd/gd_avif.c b/ext/gd/libgd/gd_avif.c index 9c1ffdc34bc6..fa876b072b24 100644 --- a/ext/gd/libgd/gd_avif.c +++ b/ext/gd/libgd/gd_avif.c @@ -1,33 +1,43 @@ +/** + * File: AVIF IO + * + * Read and write AVIF images using libavif + * (https://github.com/AOMediaCodec/libavif) . Currently, the only ICC profile + * we support is sRGB. Since that's what web browsers use, it's sufficient for + * now. + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include +#include #include #include #include -#include -#include #include "gd.h" #include "gd_errors.h" -#include "gdhelpers.h" #include "gd_intern.h" +#include "gdhelpers.h" #ifdef HAVE_LIBAVIF #include /* - Define defaults for encoding images: - CHROMA_SUBSAMPLING_DEFAULT: 4:2:0 is commonly used for Chroma subsampling. - CHROMA_SUBAMPLING_HIGH_QUALITY: Use 4:4:4, or no subsampling, when a sufficient high quality is requested. - SUBAMPLING_HIGH_QUALITY_THRESHOLD: At or above this value, use CHROMA_SUBAMPLING_HIGH_QUALITY - QUANTIZER_DEFAULT: - We need more testing to really know what quantizer settings are optimal, - but teams at Google have been using maximum=30 as a starting point. - QUALITY_DEFAULT: following gd conventions, -1 indicates the default. - SPEED_DEFAULT: - AVIF_SPEED_DEFAULT is simply the default encoding speed of the AV1 codec. - This could be as slow as 0. So we use 6, which is currently considered to be a fine default. + Define defaults for encoding images: + CHROMA_SUBSAMPLING_DEFAULT: 4:2:0 is commonly used for Chroma + subsampling. CHROMA_SUBAMPLING_HIGH_QUALITY: Use 4:4:4, or no subsampling, + when a sufficient high quality is requested. + SUBAMPLING_HIGH_QUALITY_THRESHOLD: At or above this value, use + CHROMA_SUBAMPLING_HIGH_QUALITY QUANTIZER_DEFAULT: We need more testing to + really know what quantizer settings are optimal, but teams at Google have + been using maximum=30 as a starting point. QUALITY_DEFAULT: following gd + conventions, -1 indicates the default. SPEED_DEFAULT: AVIF_SPEED_DEFAULT is + simply the default encoding speed of the AV1 codec. This could be as slow as + 0. So we use 6, which is currently considered to be a fine default. + */ #define CHROMA_SUBSAMPLING_DEFAULT AVIF_PIXEL_FORMAT_YUV420 @@ -37,15 +47,18 @@ #define QUALITY_DEFAULT -1 #define SPEED_DEFAULT 6 -// This initial size for the gdIOCtx is standard among GD image conversion functions. +// This initial size for the gdIOCtx is standard among GD image conversion +// functions. #define NEW_DYNAMIC_CTX_SIZE 2048 // Our quality param ranges from 0 to 100. -// To calculate quality, we convert from AVIF's quantizer scale, which runs from 63 to 0. +// To calculate quality, we convert from AVIF's quantizer scale, which runs from +// 63 to 0. #define MAX_QUALITY 100 -// These constants are for computing the number of tiles and threads to use during encoding. -// Maximum threads are from libavif/contrib/gkd-pixbuf/loader.c. +// These constants are for computing the number of tiles and threads to use +// during encoding. Maximum threads are from +// libavif/contrib/gkd-pixbuf/loader.c. #define MIN_TILE_AREA (512 * 512) #define MAX_TILES 8 #define MAX_THREADS 64 @@ -53,604 +66,723 @@ /*** Macros ***/ /* - From gd_png.c: - convert the 7-bit alpha channel to an 8-bit alpha channel. - We do a little bit-flipping magic, repeating the MSB - as the LSB, to ensure that 0 maps to 0 and - 127 maps to 255. We also have to invert to match - PNG's convention in which 255 is opaque. + From gd_png.c: + convert the 7-bit alpha channel to an 8-bit alpha channel. + We do a little bit-flipping magic, repeating the MSB + as the LSB, to ensure that 0 maps to 0 and + 127 maps to 255. We also have to invert to match + PNG's convention in which 255 is opaque. */ -#define alpha7BitTo8Bit(alpha7Bit) \ - (alpha7Bit == 127 ? \ - 0 : \ - 255 - ((alpha7Bit << 1) + (alpha7Bit >> 6))) +#define alpha7BitTo8Bit(alpha7Bit) \ + (alpha7Bit == 127 ? 0 : 255 - ((alpha7Bit << 1) + (alpha7Bit >> 6))) #define alpha8BitTo7Bit(alpha8Bit) (gdAlphaMax - (alpha8Bit >> 1)) - /*** Helper functions ***/ /* Convert the quality param we expose to the quantity params used by libavif. - The *Quantizer* params values can range from 0 to 63, with 0 = highest quality and 63 = worst. - We make the scale 0-100, and we reverse this, so that 0 = worst quality and 100 = highest. + The *Quantizer* params values can range from 0 to 63, with 0 = highest + quality and 63 = worst. We make the scale 0-100, and we reverse this, so that + 0 = worst quality and 100 = highest. - Values below 0 are set to 0, and values below MAX_QUALITY are set to MAX_QUALITY. + Values below 0 are set to 0, and values below MAX_QUALITY are set to + MAX_QUALITY. */ -static int quality2Quantizer(int quality) { - int clampedQuality = CLAMP(quality, 0, MAX_QUALITY); +static int quality2Quantizer(int quality) +{ + int clampedQuality = CLAMP(quality, 0, MAX_QUALITY); - float scaleFactor = (float) AVIF_QUANTIZER_WORST_QUALITY / (float) MAX_QUALITY; + float scaleFactor = (float)AVIF_QUANTIZER_WORST_QUALITY / (float)MAX_QUALITY; - return round(scaleFactor * (MAX_QUALITY - clampedQuality)); + return round(scaleFactor * (MAX_QUALITY - clampedQuality)); } /* - As of February 2021, this algorithm reflects the latest research on how many tiles - and threads to include for a given image size. - This is subject to change as research continues. + As of February 2021, this algorithm reflects the latest research on how + many tiles and threads to include for a given image size. This is subject to + change as research continues. - Returns false if there was an error, true if all was well. + Returns false if there was an error, true if all was well. */ -static avifBool setEncoderTilesAndThreads(avifEncoder *encoder, avifRGBImage *rgb) { - int imageArea, tiles, tilesLog2, encoderTiles; +static avifBool setEncoderTilesAndThreads(avifEncoder *encoder, avifRGBImage *rgb) +{ + int imageArea, tiles, tilesLog2, encoderTiles; - // _gdImageAvifCtx(), the calling function, checks this operation for overflow - imageArea = rgb->width * rgb->height; + // _gdImageAvifCtx(), the calling function, checks this operation for + // overflow + imageArea = rgb->width * rgb->height; - tiles = (int) ceil((double) imageArea / MIN_TILE_AREA); - tiles = MIN(tiles, MAX_TILES); - tiles = MIN(tiles, MAX_THREADS); + tiles = (int)ceil((double)imageArea / MIN_TILE_AREA); + tiles = MIN(tiles, MAX_TILES); + tiles = MIN(tiles, MAX_THREADS); - // The number of tiles in any dimension will always be a power of 2. We can only specify log(2)tiles. + // The number of tiles in any dimension will always be a power of 2. We can + // only specify log(2)tiles. - tilesLog2 = floor(log2(tiles)); + tilesLog2 = floor(log2(tiles)); - // If the image's width is greater than the height, use more tile columns - // than tile rows to make the tile size close to a square. + // If the image's width is greater than the height, use more tile columns + // than tile rows to make the tile size close to a square. - if (rgb->width >= rgb->height) { - encoder->tileRowsLog2 = tilesLog2 / 2; - encoder->tileColsLog2 = tilesLog2 - encoder->tileRowsLog2; - } else { - encoder->tileColsLog2 = tilesLog2 / 2; - encoder->tileRowsLog2 = tilesLog2 - encoder->tileColsLog2; - } + if (rgb->width >= rgb->height) { + encoder->tileRowsLog2 = tilesLog2 / 2; + encoder->tileColsLog2 = tilesLog2 - encoder->tileRowsLog2; + } else { + encoder->tileColsLog2 = tilesLog2 / 2; + encoder->tileRowsLog2 = tilesLog2 - encoder->tileColsLog2; + } - // It's good to have one thread per tile. - encoderTiles = (1 << encoder->tileRowsLog2) * (1 << encoder->tileColsLog2); - encoder->maxThreads = encoderTiles; + // It's good to have one thread per tile. + encoderTiles = (1 << encoder->tileRowsLog2) * (1 << encoder->tileColsLog2); + encoder->maxThreads = encoderTiles; - return AVIF_TRUE; + return AVIF_TRUE; } /* - We can handle AVIF images whose color profile is sRGB, or whose color profile isn't set. + We can handle AVIF images whose color profile is sRGB, or whose color + profile isn't set. */ -static avifBool isAvifSrgbImage(avifImage *avifIm) { - return - (avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_BT709 || - avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) && - (avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_SRGB || - avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) - ; +static avifBool isAvifSrgbImage(avifImage *avifIm) +{ + return (avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_BT709 || + avifIm->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) && + (avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_SRGB || + avifIm->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED); } /* - Check the result from an Avif function to see if it's an error. - If so, decode the error and output it, and return true. - Otherwise, return false. + Check the result from an Avif function to see if it's an error. + If so, decode the error and output it, and return true. + Otherwise, return false. */ -static avifBool isAvifError(avifResult result, const char *msg) { - if (result != AVIF_RESULT_OK) { - gd_error("avif error - %s: %s\n", msg, avifResultToString(result)); - return AVIF_TRUE; - } +static avifBool isAvifError(avifResult result, const char *msg) +{ + if (result != AVIF_RESULT_OK) { + gd_error("avif error - %s: %s\n", msg, avifResultToString(result)); + return AVIF_TRUE; + } - return AVIF_FALSE; + return AVIF_FALSE; } - typedef struct avifIOCtxReader { - avifIO io; // this must be the first member for easy casting to avifIO* - avifROData rodata; + avifIO io; // this must be the first member for easy casting to avifIO* + avifROData rodata; } avifIOCtxReader; /* - implements the avifIOReadFunc interface by calling the relevant functions - in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead(). - We don't know whether we're reading from a file or from memory. We don't have to know, - since we rely on the helper functions in the gdIOCtx. - We assume we've stashed the gdIOCtx in io->data, as we do in createAvifIOFromCtx(). - - We ignore readFlags, just as the avifIO*ReaderRead() functions do. - - If there's a problem, this returns an avifResult error. - If things go well, return AVIF_RESULT_OK. - Of course these AVIF codes shouldn't be returned by any top-level GD function. + implements the avifIOReadFunc interface by calling the + relevant functions in the gdIOCtx. Our logic is inspired by + avifIOMemoryReaderRead() and avifIOFileReaderRead(). We don't know whether + we're reading from a file or from memory. We don't have to know, since we + rely on the helper functions in the gdIOCtx. We assume we've stashed the + gdIOCtx in io->data, as we do in createAvifIOFromCtx(). + + We ignore readFlags, just as the avifIO*ReaderRead() functions do. + + If there's a problem, this returns an avifResult error. + If things go well, return AVIF_RESULT_OK. + Of course these AVIF codes shouldn't be returned by any top-level GD + function. */ -static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, avifROData *out) +static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, + avifROData *out) { - gdIOCtx *ctx = (gdIOCtx *) io->data; - avifIOCtxReader *reader = (avifIOCtxReader *) io; - - // readFlags is unsupported - if (readFlags != 0) { - return AVIF_RESULT_IO_ERROR; - } - - // TODO: if we set sizeHint, this will be more efficient. - - if (offset > INT_MAX || size > INT_MAX) - return AVIF_RESULT_IO_ERROR; - - // Try to seek offset bytes forward. If we pass the end of the buffer, throw an error. - if (!ctx->seek(ctx, (int) offset)) - return AVIF_RESULT_IO_ERROR; - - if (size > reader->rodata.size) { - reader->rodata.data = gdRealloc((void *) reader->rodata.data, size); - reader->rodata.size = size; - } - if (!reader->rodata.data) { - gd_error("avif error - couldn't allocate memory"); - return AVIF_RESULT_UNKNOWN_ERROR; - } - - // Read the number of bytes requested. - // If getBuf() returns a negative value, that means there was an error. - int charsRead = ctx->getBuf(ctx, (void *) reader->rodata.data, (int) size); - if (charsRead < 0) { - return AVIF_RESULT_IO_ERROR; - } - - out->data = reader->rodata.data; - out->size = charsRead; - return AVIF_RESULT_OK; + gdIOCtx *ctx = (gdIOCtx *)io->data; + avifIOCtxReader *reader = (avifIOCtxReader *)io; + + // readFlags is unsupported + if (readFlags != 0) { + return AVIF_RESULT_IO_ERROR; + } + + // TODO: if we set sizeHint, this will be more efficient. + + if (offset > INT_MAX || size > INT_MAX) + return AVIF_RESULT_IO_ERROR; + + // Try to seek offset bytes forward. If we pass the end of the buffer, throw + // an error. + if (!ctx->seek(ctx, (int)offset)) + return AVIF_RESULT_IO_ERROR; + + if (size > reader->rodata.size) { + reader->rodata.data = gdRealloc((void *)reader->rodata.data, size); + reader->rodata.size = size; + } + if (!reader->rodata.data) { + gd_error("avif error - couldn't allocate memory"); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + // Read the number of bytes requested. + // If getBuf() returns a negative value, that means there was an error. + int charsRead = ctx->getBuf(ctx, (void *)reader->rodata.data, (int)size); + if (charsRead < 0) { + return AVIF_RESULT_IO_ERROR; + } + + out->data = reader->rodata.data; + out->size = charsRead; + return AVIF_RESULT_OK; } // avif.h says this is optional, but it seemed easy to implement. -static void destroyAvifIO(struct avifIO *io) { - avifIOCtxReader *reader = (avifIOCtxReader *) io; - if (reader->rodata.data != NULL) { - gdFree((void *) reader->rodata.data); - } - gdFree(reader); +static void destroyAvifIO(struct avifIO *io) +{ + avifIOCtxReader *reader = (avifIOCtxReader *)io; + if (reader->rodata.data != NULL) { + gdFree((void *)reader->rodata.data); + } + gdFree(reader); } /* Set up an avifIO object. - The functions in the gdIOCtx struct may point either to a file or a memory buffer. - To us, that's immaterial. - Our task is simply to assign avifIO functions to the proper functions from gdIOCtx. - The destroy function needs to destroy the avifIO object and anything else it uses. + The functions in the gdIOCtx struct may point either to a file or a memory + buffer. To us, that's immaterial. Our task is simply to assign avifIO + functions to the proper functions from gdIOCtx. The destroy function needs to + destroy the avifIO object and anything else it uses. - Returns NULL if memory for the object can't be allocated. + Returns NULL if memory for the object can't be allocated. */ // TODO: can we get sizeHint somehow? -static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) { - struct avifIOCtxReader *reader; - - reader = gdMalloc(sizeof(*reader)); - if (reader == NULL) - return NULL; - - // TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary? - reader->io.persistent = AVIF_FALSE; - reader->io.read = readFromCtx; - reader->io.write = NULL; // this function is currently unused; see avif.h - reader->io.destroy = destroyAvifIO; - reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx. - reader->io.data = ctx; - reader->rodata.data = NULL; - reader->rodata.size = 0; - - return (avifIO *) reader; +static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) +{ + struct avifIOCtxReader *reader; + + reader = gdMalloc(sizeof(*reader)); + if (reader == NULL) + return NULL; + + // TODO: setting persistent=FALSE is safe, but it's less efficient. Is it + // necessary? + reader->io.persistent = AVIF_FALSE; + reader->io.read = readFromCtx; + reader->io.write = NULL; // this function is currently unused; see avif.h + reader->io.destroy = destroyAvifIO; + reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx. + reader->io.data = ctx; + reader->rodata.data = NULL; + reader->rodata.size = 0; + + return (avifIO *)reader; } - /*** Decoding functions ***/ /* - Function: gdImageCreateFromAvif + Function: gdImageCreateFromAvif - is called to load truecolor images from - AVIF format files. Invoke with an - already opened pointer to a file containing the desired - image. returns a to the new - truecolor image, or NULL if unable to load the image (most often - because the file is corrupt or does not contain a AVIF - image). does not close the file. + is called to load truecolor images from + AVIF format files. Invoke with an + already opened pointer to a file containing the desired + image. returns a to the new + truecolor image, or NULL if unable to load the image (most often + because the file is corrupt or does not contain a AVIF + image). does not close the file. - This function creates a gdIOCtx struct from the file pointer it's passed. - And then it relies on to do the real decoding work. - If the file contains an image sequence, we simply read the first one, discarding the rest. + This function creates a gdIOCtx struct from the file pointer it's + passed. And then it relies on to do the real + decoding work. If the file contains an image sequence, we simply read the + first one, discarding the rest. - Variants: + Variants: - creates an image from AVIF data - already in memory. + creates an image from AVIF data + already in memory. - reads data from the function - pointers in a structure. + reads data from the function + pointers in a structure. - Parameters: + Parameters: - infile - pointer to the input file + infile - pointer to the input file - Returns: + Returns: - A pointer to the new truecolor image. This will need to be - destroyed with once it is no longer needed. + A pointer to the new truecolor image. This will need to be + destroyed with once it is no longer needed. - On error, returns 0. + On error, returns 0. */ -gdImagePtr gdImageCreateFromAvif(FILE *infile) +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvif(FILE *infile) { - gdImagePtr im; - gdIOCtx *ctx = gdNewFileCtx(infile); + gdImagePtr im; + gdIOCtx *ctx = gdNewFileCtx(infile); - if (!ctx) - return NULL; + if (!ctx) + return NULL; - im = gdImageCreateFromAvifCtx(ctx); - ctx->gd_free(ctx); + im = gdImageCreateFromAvifCtx(ctx); + ctx->gd_free(ctx); - return im; + return im; } /* - Function: gdImageCreateFromAvifPtr + Function: gdImageCreateFromAvifPtr - See . + See . - Parameters: + Parameters: - size - size of Avif data in bytes. - data - pointer to Avif data. + size - size of Avif data in bytes. + data - pointer to Avif data. */ -gdImagePtr gdImageCreateFromAvifPtr(int size, void *data) +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifPtr(int size, void *data) { - gdImagePtr im; - gdIOCtx *ctx = gdNewDynamicCtxEx(size, data, 0); + gdImagePtr im; + gdIOCtx *ctx = gdNewDynamicCtxEx(size, data, 0); - if (!ctx) - return 0; + if (!ctx) + return 0; - im = gdImageCreateFromAvifCtx(ctx); - ctx->gd_free(ctx); + im = gdImageCreateFromAvifCtx(ctx); + ctx->gd_free(ctx); - return im; + return im; } /* - Function: gdImageCreateFromAvifCtx + Function: gdImageCreateFromAvifCtx - See . + See . - Additional details: the AVIF library comes with functions to create an IO object from - a file and from a memory pointer. Of course, it doesn't have a way to create an IO object - from a gdIOCtx. So, here, we use our own helper function, . + Additional details: the AVIF library comes with functions to create an + IO object from a file and from a memory pointer. Of course, it doesn't have a + way to create an IO object from a gdIOCtx. So, here, we use our own helper + function, . - Otherwise, we create the image by calling AVIF library functions in order: - * avifDecoderCreate(), to create the decoder - * avifDecoderSetIO(), to tell libavif how to read from our data structure - * avifDecoderParse(), to parse the image - * avifDecoderNextImage(), to read the first image from the decoder - * avifRGBImageSetDefaults(), to create the avifRGBImage - * avifRGBImageAllocatePixels(), to allocate memory for the pixels - * avifImageYUVToRGB(), to convert YUV to RGB + Otherwise, we create the image by calling AVIF library functions in + order: + * avifDecoderCreate(), to create the decoder + * avifDecoderSetIO(), to tell libavif how to read from our data + structure + * avifDecoderParse(), to parse the image + * avifDecoderNextImage(), to read the first image from the decoder + * avifRGBImageSetDefaults(), to create the avifRGBImage + * avifRGBImageAllocatePixels(), to allocate memory for the pixels + * avifImageYUVToRGB(), to convert YUV to RGB - Finally, we create a new gd image and copy over the pixel data. + Finally, we create a new gd image and copy over the pixel data. - Parameters: + Parameters: - ctx - a gdIOCtx struct + ctx - a gdIOCtx struct */ -gdImagePtr gdImageCreateFromAvifCtx (gdIOCtx *ctx) +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifCtx(gdIOCtx *ctx) { - uint32_t x, y; - gdImage *im = NULL; - avifResult result; - avifIO *io; - avifDecoder *decoder; - avifRGBImage rgb; + uint32_t x, y; + gdImage *im = NULL; + avifResult result; + avifIO *io; + avifDecoder *decoder; + avifRGBImage rgb; - // this lets us know that memory hasn't been allocated yet for the pixels - rgb.pixels = NULL; + // this lets us know that memory hasn't been allocated yet for the pixels + rgb.pixels = NULL; - decoder = avifDecoderCreate(); + decoder = avifDecoderCreate(); - // Check if libavif version is >= 0.9.1 - // If so, allow the PixelInformationProperty ('pixi') to be missing in AV1 image - // items. libheif v1.11.0 or older does not add the 'pixi' item property to - // AV1 image items. (This issue has been corrected in libheif v1.12.0.) + // Check if libavif version is >= 0.9.1. + // If so, allow the PixelInformationProperty ('pixi') to be missing in AV1 + // image items. libheif v1.11.0 or older does not add the 'pixi' item + // property to AV1 image items. (This issue has been corrected in libheif + // v1.12.0.) #if AVIF_VERSION >= 90100 - decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; + decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; #endif - io = createAvifIOFromCtx(ctx); - if (!io) { - gd_error("avif error - Could not allocate memory"); - goto cleanup; - } + io = createAvifIOFromCtx(ctx); + if (!io) { + gd_error("avif error - Could not allocate memory"); + goto cleanup; + } - avifDecoderSetIO(decoder, io); + avifDecoderSetIO(decoder, io); - result = avifDecoderParse(decoder); - if (isAvifError(result, "Could not parse image")) - goto cleanup; + result = avifDecoderParse(decoder); + if (isAvifError(result, "Could not parse image")) + goto cleanup; - // Note again that, for an image sequence, we read only the first image, ignoring the rest. - result = avifDecoderNextImage(decoder); - if (isAvifError(result, "Could not decode image")) - goto cleanup; + // Note again that, for an image sequence, we read only the first image, + // ignoring the rest. + result = avifDecoderNextImage(decoder); + if (isAvifError(result, "Could not decode image")) + goto cleanup; - if (!isAvifSrgbImage(decoder->image)) - gd_error_ex(GD_NOTICE, "Image's color profile is not sRGB"); + if (!isAvifSrgbImage(decoder->image)) + gd_error_ex(LOG_NOTICE, "Image's color profile is not sRGB"); - // Set up the avifRGBImage, and convert it from YUV to an 8-bit RGB image. - // (While AVIF image pixel depth can be 8, 10, or 12 bits, GD truecolor images are 8-bit.) - avifRGBImageSetDefaults(&rgb, decoder->image); - rgb.depth = 8; + // Set up the avifRGBImage, and convert it from YUV to an 8-bit RGB image. + // (While AVIF image pixel depth can be 8, 10, or 12 bits, GD truecolor + // images are 8-bit.) + avifRGBImageSetDefaults(&rgb, decoder->image); + rgb.depth = 8; #if AVIF_VERSION >= 1000000 - result = avifRGBImageAllocatePixels(&rgb); - if (isAvifError(result, "Allocating RGB pixels failed")) - goto cleanup; + result = avifRGBImageAllocatePixels(&rgb); + if (isAvifError(result, "Allocating RGB pixels failed")) + goto cleanup; #else - avifRGBImageAllocatePixels(&rgb); + avifRGBImageAllocatePixels(&rgb); #endif - result = avifImageYUVToRGB(decoder->image, &rgb); - if (isAvifError(result, "Conversion from YUV to RGB failed")) - goto cleanup; + result = avifImageYUVToRGB(decoder->image, &rgb); + if (isAvifError(result, "Conversion from YUV to RGB failed")) + goto cleanup; - im = gdImageCreateTrueColor(decoder->image->width, decoder->image->height); - if (!im) { - gd_error("avif error - Could not create GD truecolor image"); - goto cleanup; - } + im = gdImageCreateTrueColor(decoder->image->width, decoder->image->height); + if (!im) { + gd_error("avif error - Could not create GD truecolor image"); + goto cleanup; + } - im->saveAlphaFlag = 1; + im->saveAlphaFlag = 1; - // Read the pixels from the AVIF image and copy them into the GD image. + // Read the pixels from the AVIF image and copy them into the GD image. - uint8_t *p = rgb.pixels; + uint8_t *p = rgb.pixels; - for (y = 0; y < decoder->image->height; y++) { - for (x = 0; x < decoder->image->width; x++) { - uint8_t r = *(p++); - uint8_t g = *(p++); - uint8_t b = *(p++); - uint8_t a = alpha8BitTo7Bit(*(p++)); - im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a); - } - } + for (y = 0; y < decoder->image->height; y++) { + for (x = 0; x < decoder->image->width; x++) { + uint8_t r = *(p++); + uint8_t g = *(p++); + uint8_t b = *(p++); + uint8_t a = alpha8BitTo7Bit(*(p++)); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a); + } + } cleanup: - // if io has been allocated, this frees it - avifDecoderDestroy(decoder); + // if io has been allocated, this frees it + avifDecoderDestroy(decoder); - if (rgb.pixels) - avifRGBImageFreePixels(&rgb); + if (rgb.pixels) + avifRGBImageFreePixels(&rgb); - return im; + return im; } - /*** Encoding functions ***/ /* - Function: gdImageAvifEx + Function: gdImageAvifEx - outputs the specified image to the specified file in - AVIF format. The file must be open for writing. Under MSDOS and - all versions of Windows, it is important to use "wb" as opposed to - simply "w" as the mode when opening the file, and under Unix there - is no penalty for doing so. does not close the file; - your code must do so. + outputs the specified image to the specified file in + AVIF format. The file must be open for writing. Under MSDOS and + all versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. - Variants: + Variants: - writes the image to a file, encoding with the default quality and speed. + writes the image to a file, encoding with the default + quality and speed. - stores the image in RAM. + stores the image in RAM. - stores the image in RAM, encoding with the default quality and speed. + stores the image in RAM, encoding with the default + quality and speed. - stores the image using a struct. + stores the image using a struct. - Parameters: + Parameters: - im - The image to save. - outFile - The FILE pointer to write to. - quality - Compression quality (0-100). 0 is lowest-quality, 100 is highest. - speed - The speed of compression (0-10). 0 is slowest, 10 is fastest. + im - The image to save. + outFile - The FILE pointer to write to. + quality - Compression quality (0-100). 0 is lowest-quality, 100 is + highest. speed - The speed of compression (0-10). 0 is slowest, 10 is + fastest. - Notes on parameters: - quality - If quality = -1, we use a default quality as defined in QUALITY_DEFAULT. - For information on how we convert this quality to libavif's quantity param, see . + Notes on parameters: + quality - If quality = -1, we use a default quality as defined in + QUALITY_DEFAULT. For information on how we convert this quality to libavif's + quantity param, see . - speed - At slower speeds, encoding may be quite slow. Use judiciously. + speed - At slower speeds, encoding may be quite slow. Use judiciously. - Qualities or speeds that are lower than the minimum value get clamped to the minimum value, - and qualities or speeds that are lower than the maximum value get clamped to the maxmum value. - Note that AVIF_SPEED_DEFAULT is -1. If we ever set SPEED_DEFAULT = AVIF_SPEED_DEFAULT, - we'd want to add a conditional to ensure that value doesn't get clamped. + Qualities or speeds that are lower than the minimum value get clamped to + the minimum value, and qualities or speeds that are lower than the maximum + value get clamped to the maximum value. Note that AVIF_SPEED_DEFAULT is -1. + If we ever set SPEED_DEFAULT = AVIF_SPEED_DEFAULT, we'd want to add a + conditional to ensure that value doesn't get clamped. - Returns: + Returns: - * for , , and , nothing. - * for and , a pointer to the image in memory. + * for , , and , nothing. + * for and , a pointer to the image in + memory. */ /* - If we're passed the QUALITY_DEFAULT of -1, set the quantizer params to QUANTIZER_DEFAULT. -*/ -void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed) -{ - avifResult result; - avifRGBImage rgb; - avifRWData avifOutput = AVIF_DATA_EMPTY; - avifBool lossless = quality == 100; - avifEncoder *encoder = NULL; + Private subobject + Function: _gdImageAvifCtx - uint32_t val; - uint8_t *p; - uint32_t x, y; + We need this underscored function because gdImageAvifCtx() can't return + anything. And our functions that operate on a memory buffer need to know + whether the encoding has succeeded. - if (im == NULL) - return; + If we're passed the QUALITY_DEFAULT of -1, set the quantizer params to + QUANTIZER_DEFAULT. - if (!gdImageTrueColor(im)) { - gd_error("avif error - avif doesn't support palette images"); - return; - } - - if (!gdImageSX(im) || !gdImageSY(im)) { - gd_error("avif error - image dimensions must not be zero"); - return; - } - - if (overflow2(gdImageSX(im), gdImageSY(im))) { - gd_error("avif error - image dimensions are too large"); - return; - } - - speed = CLAMP(speed, AVIF_SPEED_SLOWEST, AVIF_SPEED_FASTEST); - - avifPixelFormat subsampling = quality >= HIGH_QUALITY_SUBSAMPLING_THRESHOLD ? - CHROMA_SUBAMPLING_HIGH_QUALITY : CHROMA_SUBSAMPLING_DEFAULT; - - // Create the AVIF image. - // Set the ICC to sRGB, as that's what gd supports right now. - // Note that MATRIX_COEFFICIENTS_IDENTITY enables lossless conversion from RGB to YUV. - - avifImage *avifIm = avifImageCreate(gdImageSX(im), gdImageSY(im), 8, subsampling); -#if AVIF_VERSION >= 1000000 - if (avifIm == NULL) { - gd_error("avif error - Creating image failed\n"); - goto cleanup; - } -#endif - avifIm->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; - avifIm->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; - avifIm->matrixCoefficients = lossless ? AVIF_MATRIX_COEFFICIENTS_IDENTITY : AVIF_MATRIX_COEFFICIENTS_BT709; + This function returns 0 on success, or 1 on failure. +*/ +static avifBool _gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed) +{ + avifResult result; + avifRGBImage rgb = {0}; + avifRWData avifOutput = AVIF_DATA_EMPTY; + avifBool failed = AVIF_FALSE; + avifBool lossless = quality == 100; + avifEncoder *encoder = NULL; + avifImage *avifIm = NULL; + + uint32_t val; + uint8_t *p; + uint32_t x, y; + + if (im == NULL) + return 1; + + if (!gdImageTrueColor(im)) { + gd_error("avif doesn't support palette images"); + return 1; + } + + if (!gdImageSX(im) || !gdImageSY(im)) { + gd_error("image dimensions must not be zero"); + return 1; + } + + if (overflow2(gdImageSX(im), gdImageSY(im))) { + gd_error("image dimensions are too large"); + return 1; + } + + speed = CLAMP(speed, AVIF_SPEED_SLOWEST, AVIF_SPEED_FASTEST); + + avifPixelFormat subsampling = quality >= HIGH_QUALITY_SUBSAMPLING_THRESHOLD + ? CHROMA_SUBAMPLING_HIGH_QUALITY + : CHROMA_SUBSAMPLING_DEFAULT; + + // Create the AVIF image. + // Set the ICC to sRGB, as that's what gd supports right now. + // Note that MATRIX_COEFFICIENTS_IDENTITY enables lossless conversion from + // RGB to YUV. + + avifIm = avifImageCreate(gdImageSX(im), gdImageSY(im), 8, subsampling); + if (avifIm == NULL) { + gd_error("avif error - Creating image failed\n"); + goto cleanup; + } + + avifIm->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; + avifIm->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; + avifIm->matrixCoefficients = + lossless ? AVIF_MATRIX_COEFFICIENTS_IDENTITY : AVIF_MATRIX_COEFFICIENTS_BT709; + + avifRGBImageSetDefaults(&rgb, avifIm); + // this allocates memory, and sets rgb.rowBytes and rgb.pixels. + result = avifRGBImageAllocatePixels(&rgb); + if (isAvifError(result, "Allocating RGB pixels failed")) + goto cleanup; + + // Parse RGB data from the GD image, and copy it into the AVIF RGB image. + // Convert 7-bit GD alpha channel values to 8-bit AVIF values. + + p = rgb.pixels; + for (y = 0; y < rgb.height; y++) { + for (x = 0; x < rgb.width; x++) { + val = im->tpixels[y][x]; + + *(p++) = gdTrueColorGetRed(val); + *(p++) = gdTrueColorGetGreen(val); + *(p++) = gdTrueColorGetBlue(val); + *(p++) = alpha7BitTo8Bit(gdTrueColorGetAlpha(val)); + } + } + + // Convert the RGB image to YUV. + + result = avifImageRGBToYUV(avifIm, &rgb); + failed = isAvifError(result, "Could not convert image to YUV"); + if (failed) + goto cleanup; + + // Encode the image in AVIF format. + + encoder = avifEncoderCreate(); + if (encoder == NULL) { + gd_error("avif error - Creating encoder failed\n"); + goto cleanup; + } + + int quantizerQuality = + quality == QUALITY_DEFAULT ? QUANTIZER_DEFAULT : quality2Quantizer(quality); + + encoder->minQuantizer = quantizerQuality; + encoder->maxQuantizer = quantizerQuality; + encoder->minQuantizerAlpha = quantizerQuality; + encoder->maxQuantizerAlpha = quantizerQuality; + encoder->speed = speed; + + failed = !setEncoderTilesAndThreads(encoder, &rgb); + if (failed) + goto cleanup; + + // TODO: is there a reason to use timeSscales != 1? + result = avifEncoderAddImage(encoder, avifIm, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); + failed = isAvifError(result, "Could not encode image"); + if (failed) + goto cleanup; + + result = avifEncoderFinish(encoder, &avifOutput); + failed = isAvifError(result, "Could not finish encoding"); + if (failed) + goto cleanup; + + // Write the AVIF image bytes to the GD ctx. + + gdPutBuf(avifOutput.data, avifOutput.size, outfile); - avifRGBImageSetDefaults(&rgb, avifIm); - // this allocates memory, and sets rgb.rowBytes and rgb.pixels. -#if AVIF_VERSION >= 1000000 - result = avifRGBImageAllocatePixels(&rgb); - if (isAvifError(result, "Allocating RGB pixels failed")) - goto cleanup; -#else - avifRGBImageAllocatePixels(&rgb); -#endif +cleanup: + if (rgb.pixels) + avifRGBImageFreePixels(&rgb); - // Parse RGB data from the GD image, and copy it into the AVIF RGB image. - // Convert 7-bit GD alpha channel values to 8-bit AVIF values. + if (encoder) + avifEncoderDestroy(encoder); - p = rgb.pixels; - for (y = 0; y < rgb.height; y++) { - for (x = 0; x < rgb.width; x++) { - val = im->tpixels[y][x]; + if (avifOutput.data) + avifRWDataFree(&avifOutput); - *(p++) = gdTrueColorGetRed(val); - *(p++) = gdTrueColorGetGreen(val); - *(p++) = gdTrueColorGetBlue(val); - *(p++) = alpha7BitTo8Bit(gdTrueColorGetAlpha(val)); - } - } + if (avifIm) + avifImageDestroy(avifIm); - // Convert the RGB image to YUV. + return failed; +} - result = avifImageRGBToYUV(avifIm, &rgb); - if (isAvifError(result, "Could not convert image to YUV")) - goto cleanup; +BGD_DECLARE(void) +gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed) +{ + gdIOCtx *out = gdNewFileCtx(outFile); - // Encode the image in AVIF format. + if (out == NULL) + return; - encoder = avifEncoderCreate(); -#if AVIF_VERSION >= 1000000 - if (encoder == NULL) { - gd_error("avif error - Creating encoder failed\n"); - goto cleanup; - } -#endif - int quantizerQuality = quality == QUALITY_DEFAULT ? - QUANTIZER_DEFAULT : quality2Quantizer(quality); + gdImageAvifCtx(im, out, quality, speed); + out->gd_free(out); +} - encoder->minQuantizer = quantizerQuality; - encoder->maxQuantizer = quantizerQuality; - encoder->minQuantizerAlpha = quantizerQuality; - encoder->maxQuantizerAlpha = quantizerQuality; - encoder->speed = speed; +BGD_DECLARE(void) gdImageAvif(gdImagePtr im, FILE *outFile) +{ + gdImageAvifEx(im, outFile, QUALITY_DEFAULT, SPEED_DEFAULT); +} - if (!setEncoderTilesAndThreads(encoder, &rgb)) - goto cleanup; +BGD_DECLARE(void *) +gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(NEW_DYNAMIC_CTX_SIZE, NULL); - //TODO: is there a reason to use timeSscales != 1? - result = avifEncoderAddImage(encoder, avifIm, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); - if (isAvifError(result, "Could not encode image")) - goto cleanup; + if (out == NULL) { + return NULL; + } - result = avifEncoderFinish(encoder, &avifOutput); - if (isAvifError(result, "Could not finish encoding")) - goto cleanup; + if (_gdImageAvifCtx(im, out, quality, speed)) + rv = NULL; + else + rv = gdDPExtractData(out, size); - // Write the AVIF image bytes to the GD ctx. + out->gd_free(out); + return rv; +} - gdPutBuf(avifOutput.data, avifOutput.size, outfile); +BGD_DECLARE(void *) gdImageAvifPtr(gdImagePtr im, int *size) +{ + return gdImageAvifPtrEx(im, size, QUALITY_DEFAULT, AVIF_SPEED_DEFAULT); +} -cleanup: - if (rgb.pixels) - avifRGBImageFreePixels(&rgb); +BGD_DECLARE(void) +gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed) +{ + _gdImageAvifCtx(im, outfile, quality, speed); +} - if (encoder) - avifEncoderDestroy(encoder); +#else /* !HAVE_LIBAVIF */ - if (avifOutput.data) - avifRWDataFree(&avifOutput); +static void *_noAvifError(void) +{ + gd_error("AVIF image support has been disabled\n"); + return NULL; +} - if (avifIm) - avifImageDestroy(avifIm); +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvif(FILE *ctx) +{ + ARG_NOT_USED(ctx); + return _noAvifError(); } -void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed) +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifPtr(int size, void *data) { - gdIOCtx *out = gdNewFileCtx(outFile); + ARG_NOT_USED(size); + ARG_NOT_USED(data); + return _noAvifError(); +} - if (out != NULL) { - gdImageAvifCtx(im, out, quality, speed); - out->gd_free(out); - } +BGD_DECLARE(gdImagePtr) gdImageCreateFromAvifCtx(gdIOCtx *ctx) +{ + ARG_NOT_USED(ctx); + return _noAvifError(); } -void gdImageAvif(gdImagePtr im, FILE *outFile) +BGD_DECLARE(void) +gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed) { - gdImageAvifEx(im, outFile, QUALITY_DEFAULT, SPEED_DEFAULT); + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(quality); + ARG_NOT_USED(speed); + _noAvifError(); } -void * gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed) +BGD_DECLARE(void) +gdImageAvifEx(gdImagePtr im, FILE *outfile, int quality, int speed) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(NEW_DYNAMIC_CTX_SIZE, NULL); + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(quality); + ARG_NOT_USED(speed); + _noAvifError(); +} - if (out == NULL) { - return NULL; - } +BGD_DECLARE(void) gdImageAvif(gdImagePtr im, FILE *outfile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + _noAvifError(); +} - gdImageAvifCtx(im, out, quality, speed); - rv = gdDPExtractData(out, size); +BGD_DECLARE(void *) gdImageAvifPtr(gdImagePtr im, int *size) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); - out->gd_free(out); - return rv; + return _noAvifError(); } -void * gdImageAvifPtr(gdImagePtr im, int *size) +BGD_DECLARE(void *) +gdImageAvifPtrEx(gdImagePtr im, int *size, int quality, int speed) { - return gdImageAvifPtrEx(im, size, QUALITY_DEFAULT, AVIF_SPEED_DEFAULT); + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(quality); + ARG_NOT_USED(speed); + return _noAvifError(); } #endif /* HAVE_LIBAVIF */ diff --git a/ext/gd/libgd/gd_bmp.c b/ext/gd/libgd/gd_bmp.c index c7c9cc9d8c1e..dac69b092c3f 100644 --- a/ext/gd/libgd/gd_bmp.c +++ b/ext/gd/libgd/gd_bmp.c @@ -1,29 +1,48 @@ /* - gd_bmp.c + gd_bmp.c - Bitmap format support for libgd + Bitmap format support for libgd - * Written 2007, Scott MacVicar - --------------------------------------------------------------------------- + * Written 2007, Scott MacVicar + --------------------------------------------------------------------------- - Todo: + Todo: - Bitfield encoding + Bitfield encoding - ---------------------------------------------------------------------------- + ---------------------------------------------------------------------------- */ -/* $Id$ */ + +/** + * File: BMP IO + * + * Read and write BMP images. + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include -#include +#include "bmp.h" #include "gd.h" +#include "gd_errors.h" #include "gdhelpers.h" -#include "bmp.h" +#include +#include +#include +#include +#include +#if defined(__has_builtin) && __has_builtin(__builtin_assume) +#define GD_ASSUME(expr) __builtin_assume(expr) +#elif defined(__GNUC__) +#define GD_ASSUME(expr) \ + do { \ + if (!(expr)) \ + __builtin_unreachable(); \ + } while (0) +#else +#define GD_ASSUME(expr) ((void)(expr)) +#endif static int compress_row(unsigned char *uncompressed_row, int length); static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data); @@ -36,1065 +55,2135 @@ static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info); static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header); static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header); +static int bmp_read_2bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header); static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header); static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header); static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info); -static int _gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression); +typedef struct { + int bpp; + int compression; + int header_ver; + int row_stride; + int bitmap_size; + int info_size; + int palette_size; + int mask_size; + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + unsigned int alpha_mask; +} bmp_write_ctx_t; + +static int _gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int bpp, int compression, int flags); +static int bmp_resolve_write_ctx(gdImagePtr im, int bpp_hint, int compression, int flags, + bmp_write_ctx_t *ctx); +static int bmp_auto_bpp(gdImagePtr im); +static int bmp_has_alpha(gdImagePtr im); +static int bmp_prepare_write_image(gdImagePtr im, int bpp, int flags, gdImagePtr *write_im); +static void bmp_write_file_header(gdIOCtxPtr out, bmp_write_ctx_t *ctx); +static void bmp_write_info_header(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static void bmp_write_palette(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_1bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_4bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_8bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_16bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_24bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_pixels_32bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx); +static int bmp_write_rle4_row(gdIOCtxPtr out, gdImagePtr im, int row); +static int bmp_write_padding(gdIOCtxPtr out, int count); +static int bmp_write_tmpfile_to_ctx(gdIOCtxPtr out, gdIOCtxPtr out_original); + +static int bmp_validate_info(bmp_info_t *info, bmp_hdr_t *hdr); +static int bmp_read_bitfield_masks(gdIOCtxPtr infile, bmp_info_t *info, int read_alpha); +static int bmp_skip_bytes(gdIOCtxPtr infile, int count); +static int bmp_check_palette_index(gdImagePtr im, int index); +static int bmp_row_padding(int width, int depth, int *padding); +static int bmp_image_size(int width, int height, int depth, int *size); +static int bmp_get_mask_shift(unsigned int mask); +static int bmp_get_mask_bits(unsigned int mask); +static int bmp_extract_mask(unsigned int pixel, unsigned int mask); +static unsigned int bmp_pack_mask(int channel_8bit, unsigned int mask); +static int bmp_alpha_to_gd(int alpha); +static int bmp_gd_to_alpha(int gd_alpha); #define BMP_DEBUG(s) static int gdBMPPutWord(gdIOCtx *out, int w) { - /* Byte order is little-endian */ - gdPutC(w & 0xFF, out); - gdPutC((w >> 8) & 0xFF, out); - return 0; + /* Byte order is little-endian */ + gdPutC(w & 0xFF, out); + gdPutC((w >> 8) & 0xFF, out); + return 0; } static int gdBMPPutInt(gdIOCtx *out, int w) { - /* Byte order is little-endian */ - gdPutC(w & 0xFF, out); - gdPutC((w >> 8) & 0xFF, out); - gdPutC((w >> 16) & 0xFF, out); - gdPutC((w >> 24) & 0xFF, out); - return 0; + /* Byte order is little-endian */ + gdPutC(w & 0xFF, out); + gdPutC((w >> 8) & 0xFF, out); + gdPutC((w >> 16) & 0xFF, out); + gdPutC((w >> 24) & 0xFF, out); + return 0; } /* - Function: gdImageBmpPtr + Function: gdImageBmpPtr + + Outputs the given image as BMP data, but using a instead + of a file. See . + + This is the legacy BMP memory API. A zero value writes + uncompressed BMP data. A nonzero value requests legacy + RLE output when the automatically selected BMP bit depth supports RLE. + For explicit bit depth, compression, and conversion control, use + . + + Parameters: + im - the image to save. + size - Output: size in bytes of the result. + compression - whether to apply RLE or not. + + Returns: + + A pointer to memory containing the image data or NULL on error. */ -void * gdImageBmpPtr(gdImagePtr im, int *size, int compression) +BGD_DECLARE(void *) gdImageBmpPtr(gdImagePtr im, int *size, int compression) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - if (out == NULL) return NULL; - if (!_gdImageBmpCtx(im, out, compression)) - rv = gdDPExtractData(out, size); - else - rv = NULL; - out->gd_free(out); - return rv; + return gdImageBmpPtrEx(im, size, 0, compression ? -1 : GD_BMP_COMPRESS_NONE, GD_BMP_FLAG_NONE); } /* - Function: gdImageBmp + Function: gdImageBmpPtrEx + + outputs the given image as BMP data in memory. + See for the meaning of , , and + . + + Parameters: + + im - The image to save. + size - Output: size in bytes of the returned data. + bpp - Requested output bit depth, or 0 for automatic selection. + compression - Requested BMP compression mode. + flags - BMP writer option flags. + + Returns: + + A pointer to memory containing the image data, or NULL on error. + The returned memory must be freed with . +*/ +BGD_DECLARE(void *) +gdImageBmpPtrEx(gdImagePtr im, int *size, int bpp, int compression, int flags) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageBmpCtx(im, out, bpp, compression, flags)) + rv = gdDPExtractData(out, size); + else + rv = NULL; + out->gd_free(out); + return rv; +} + +/* + Function: gdImageBmp + + outputs the specified image to the specified file in + BMP format. The file must be open for writing. Under MSDOS and all + versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + In addition, allows to specify whether RLE compression + should be applied. + + This is the legacy BMP file API. A zero value writes + uncompressed BMP data. A nonzero value requests legacy + RLE output when the automatically selected BMP bit depth supports RLE. + For explicit bit depth, compression, and conversion control, use + . + + Variants: + + write via a instead of a file handle. + + store the image file to memory. + + Parameters: + + im - the image to save. + outFile - the output FILE* object. + compression - whether to apply RLE or not. + + Returns: + nothing */ -void gdImageBmp(gdImagePtr im, FILE *outFile, int compression) +BGD_DECLARE(void) gdImageBmp(gdImagePtr im, FILE *outFile, int compression) { - gdIOCtx *out = gdNewFileCtx(outFile); - if (out == NULL) return; - gdImageBmpCtx(im, out, compression); - out->gd_free(out); + gdImageBmpEx(im, outFile, 0, compression ? -1 : GD_BMP_COMPRESS_NONE, GD_BMP_FLAG_NONE); } /* - Function: gdImageBmpCtx + Function: gdImageBmpEx + + outputs the specified image to the specified file in + BMP format. The file must be open for writing. Under MSDOS and all + versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + This extended BMP writer supports 1, 4, 8, 16, 24, and 32 bits per + pixel. It writes Windows BMP headers only: BITMAPINFOHEADER for most + outputs, BITMAPV4HEADER for 32 bpp alpha output or when + is set. It does not write OS/2 BMP + headers, 2 bpp BMPs, top-down BMPs, custom bit masks, V5 color + profiles, embedded JPEG/PNG BMP data, or non-standard high-depth + bitfield formats. + + Bit depth: + + Pass 0 for automatic bit depth selection. Automatic selection writes + truecolor images with non-opaque pixels as 32 bpp, opaque truecolor + images as 24 bpp, palette images with 2 or fewer colors as 1 bpp, + palette images with 16 or fewer colors as 4 bpp, and other palette + images as 8 bpp. + + Explicit values must be one of 1, 4, 8, 16, 24, or 32. + Indexed output at 1, 4, or 8 bpp requires a palette image with no + more colors than the selected bit depth can store. If the source is + truecolor, explicit indexed output is a lossy conversion and fails + unless is set. With that flag, the writer + clones the image, converts the clone with , + writes the clone, and leaves the caller's image unchanged. + + Compression: + + writes uncompressed BMP pixels. + is valid only for 4 bpp output and writes + BI_RLE4. is valid only for 8 bpp output and + writes BI_RLE8. Any invalid bit depth/compression combination fails + before producing a valid BMP. + + 16 bpp and 32 bpp output use BI_BITFIELDS automatically, regardless + of the requested compression. 16 bpp defaults to RGB565 masks; set + to write RGB555 masks instead. 32 bpp writes + red, green, blue, and alpha masks and stores alpha as BMP opacity + (255 is opaque), converted from gd's alpha representation. + + Palette alpha is not preserved in 1, 4, or 8 bpp BMP output. The + writer stores indexed palettes as B, G, R, reserved byte entries, + and writes the reserved byte as zero. Truecolor images that need + alpha preservation should be written as 32 bpp, either explicitly or + by using automatic bit depth selection on an image with non-opaque + pixels. If a truecolor image is quantized to indexed BMP with + , alpha is not preserved in the indexed BMP. + + Flags: + + selects default behavior. + allows explicit lossy truecolor to indexed + conversion for 1, 4, or 8 bpp output. + selects RGB555 masks for 16 bpp output. + writes a BITMAPV4HEADER even when it is + not otherwise required. + + Variants: + + stores the image in RAM. + + writes using a struct. + + , , and are legacy + wrappers using automatic bit depth selection and no flags. + + Parameters: + + im - The image to save. + outFile - The FILE pointer to write to. + bpp - Requested bit depth, or 0 for automatic selection. + compression - One of , + , or . + flags - A bitwise OR of , + , , + and . + + Returns: + + For and , nothing. + For , a pointer to the image in memory, or NULL + on error. */ -void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression) +BGD_DECLARE(void) +gdImageBmpEx(gdImagePtr im, FILE *outFile, int bpp, int compression, int flags) { - _gdImageBmpCtx(im, out, compression); + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageBmpCtxEx(im, out, bpp, compression, flags); + out->gd_free(out); } -static int _gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression){ - int bitmap_size = 0, info_size, total_size, padding; - int i, row, xpos, pixel; - int error = 0; - unsigned char *uncompressed_row = NULL, *uncompressed_row_start = NULL; - FILE *tmpfile_for_compression = NULL; - gdIOCtxPtr out_original = NULL; - int ret = 1; - - /* No compression if its true colour or we don't support seek */ - if (im->trueColor) { - compression = 0; - } - - if (compression == 1 && !out->seek) { - /* Try to create a temp file where we can seek */ - if ((tmpfile_for_compression = tmpfile()) == NULL) { - compression = 0; - } else { - out_original = out; - if ((out = (gdIOCtxPtr)gdNewFileCtx(tmpfile_for_compression)) == NULL) { - out = out_original; - out_original = NULL; - compression = 0; - } - } - } - - bitmap_size = ((im->sx * (im->trueColor ? 24 : 8)) / 8) * im->sy; - - /* 40 byte Windows v3 header */ - info_size = BMP_WINDOWS_V3; - - /* data for the palette */ - if (!im->trueColor) { - info_size += im->colorsTotal * 4; - if (compression) { - bitmap_size = 0; - } - } - - /* bitmap header + info header + data */ - total_size = 14 + info_size + bitmap_size; - - /* write bmp header info */ - gdPutBuf("BM", 2, out); - gdBMPPutInt(out, total_size); - gdBMPPutWord(out, 0); - gdBMPPutWord(out, 0); - gdBMPPutInt(out, 14 + info_size); - - /* write Windows v3 headers */ - gdBMPPutInt(out, BMP_WINDOWS_V3); /* header size */ - gdBMPPutInt(out, im->sx); /* width */ - gdBMPPutInt(out, im->sy); /* height */ - gdBMPPutWord(out, 1); /* colour planes */ - gdBMPPutWord(out, (im->trueColor ? 24 : 8)); /* bit count */ - gdBMPPutInt(out, (compression ? BMP_BI_RLE8 : BMP_BI_RGB)); /* compression */ - gdBMPPutInt(out, bitmap_size); /* image size */ - gdBMPPutInt(out, 0); /* H resolution */ - gdBMPPutInt(out, 0); /* V ressolution */ - gdBMPPutInt(out, im->colorsTotal); /* colours used */ - gdBMPPutInt(out, 0); /* important colours */ - - /* The line must be divisible by 4, else it's padded with NULLs */ - padding = ((int)(im->trueColor ? 3 : 1) * im->sx) % 4; - if (padding) { - padding = 4 - padding; - } - - /* 8-bit colours */ - if (!im->trueColor) { - for(i = 0; i< im->colorsTotal; ++i) { - Putchar(gdImageBlue(im, i), out); - Putchar(gdImageGreen(im, i), out); - Putchar(gdImageRed(im, i), out); - Putchar(0, out); - } - - if (compression) { - /* Can potentially change this to X + ((X / 128) * 3) */ - uncompressed_row = uncompressed_row_start = (unsigned char *) gdCalloc(gdImageSX(im) * 2, sizeof(char)); - if (!uncompressed_row) { - /* malloc failed */ - goto cleanup; - } - } - - for (row = (im->sy - 1); row >= 0; row--) { - if (compression) { - memset (uncompressed_row, 0, gdImageSX(im)); - } - - for (xpos = 0; xpos < im->sx; xpos++) { - if (compression) { - *uncompressed_row++ = (unsigned char)gdImageGetPixel(im, xpos, row); - } else { - Putchar(gdImageGetPixel(im, xpos, row), out); - } - } - - if (!compression) { - /* Add padding to make sure we have n mod 4 == 0 bytes per row */ - for (xpos = padding; xpos > 0; --xpos) { - Putchar('\0', out); - } - } else { - int compressed_size = 0; - uncompressed_row = uncompressed_row_start; - if ((compressed_size = compress_row(uncompressed_row, gdImageSX(im))) < 0) { - error = 1; - break; - } - bitmap_size += compressed_size; - - - gdPutBuf(uncompressed_row, compressed_size, out); - Putchar(BMP_RLE_COMMAND, out); - Putchar(BMP_RLE_ENDOFLINE, out); - bitmap_size += 2; - } - } - - if (compression && uncompressed_row) { - gdFree(uncompressed_row); - if (error != 0) { - goto cleanup; - } - /* Update filesize based on new values and set compression flag */ - Putchar(BMP_RLE_COMMAND, out); - Putchar(BMP_RLE_ENDOFBITMAP, out); - bitmap_size += 2; - - /* Write new total bitmap size */ - gdSeek(out, 2); - gdBMPPutInt(out, total_size + bitmap_size); - - /* Total length of image data */ - gdSeek(out, 34); - gdBMPPutInt(out, bitmap_size); - } - - } else { - for (row = (im->sy - 1); row >= 0; row--) { - for (xpos = 0; xpos < im->sx; xpos++) { - pixel = gdImageGetPixel(im, xpos, row); - - Putchar(gdTrueColorGetBlue(pixel), out); - Putchar(gdTrueColorGetGreen(pixel), out); - Putchar(gdTrueColorGetRed(pixel), out); - } - - /* Add padding to make sure we have n mod 4 == 0 bytes per row */ - for (xpos = padding; xpos > 0; --xpos) { - Putchar('\0', out); - } - } - } - - - /* If we needed a tmpfile for compression copy it over to out_original */ - if (tmpfile_for_compression) { - unsigned char* copy_buffer = NULL; - int buffer_size = 0; - - gdSeek(out, 0); - copy_buffer = (unsigned char *) gdMalloc(1024 * sizeof(unsigned char)); - if (copy_buffer == NULL) { - goto cleanup; - } - - while ((buffer_size = gdGetBuf(copy_buffer, 1024, out)) != EOF) { - if (buffer_size == 0) { - break; - } - gdPutBuf(copy_buffer , buffer_size, out_original); - } - gdFree(copy_buffer); - - /* Replace the temp with the original which now has data */ - out->gd_free(out); - out = out_original; - out_original = NULL; - } - - ret = 0; +/* + Function: gdImageBmpCtx + + Outputs the given image as BMP data, but using a instead + of a file. See . + + This is the legacy BMP context API. A zero value writes + uncompressed BMP data. A nonzero value requests legacy + RLE output when the automatically selected BMP bit depth supports RLE. + For explicit bit depth, compression, and conversion control, use + . + + Parameters: + im - the image to save. + out - the to write to. + compression - whether to apply RLE or not. +*/ +BGD_DECLARE(void) +gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression) +{ + gdImageBmpCtxEx(im, out, 0, compression ? -1 : GD_BMP_COMPRESS_NONE, GD_BMP_FLAG_NONE); +} + +/* + Function: gdImageBmpCtxEx + + outputs the given image as BMP data using a + structure. See for the meaning of , + , and , including automatic bit depth selection, + RLE restrictions, quantization behavior, bitfield output, and + unsupported BMP variants. + + Parameters: + + im - The image to save. + out - The to write to. + bpp - Requested output bit depth, or 0 for automatic selection. + compression - Requested BMP compression mode. + flags - BMP writer option flags. +*/ +BGD_DECLARE(void) +gdImageBmpCtxEx(gdImagePtr im, gdIOCtxPtr out, int bpp, int compression, int flags) +{ + _gdImageBmpCtx(im, out, bpp, compression, flags); +} + +static int _gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int bpp, int compression, int flags) +{ + bmp_write_ctx_t ctx; + int error = 0; + FILE *tmpfile_for_compression = NULL; + gdIOCtxPtr out_original = NULL; + int ret = 1; + gdImagePtr write_im = im; + + if (im == NULL || out == NULL) { + return 1; + } + + if (gdImageSX(im) <= 0 || gdImageSY(im) <= 0) { + gd_error("image dimensions must be greater than 0"); + return 1; + } + + if (overflow2(gdImageSX(im), gdImageSY(im))) { + gd_error("image dimensions are too large"); + return 1; + } + + if (bmp_prepare_write_image(im, bpp, flags, &write_im)) { + return 1; + } + + if (bmp_resolve_write_ctx(write_im, bpp, compression, flags, &ctx)) { + goto cleanup; + } + + if ((ctx.compression == BMP_BI_RLE8 || ctx.compression == BMP_BI_RLE4) && !out->seek) { + /* Try to create a temp file where we can seek */ + if ((tmpfile_for_compression = tmpfile()) == NULL) { + ctx.compression = BMP_BI_RGB; + if (ctx.bpp == 4 || ctx.bpp == 8) { + ctx.bitmap_size = ctx.row_stride * write_im->sy; + } + } else { + out_original = out; + if ((out = (gdIOCtxPtr)gdNewFileCtx(tmpfile_for_compression)) == NULL) { + out = out_original; + out_original = NULL; + ctx.compression = BMP_BI_RGB; + if (ctx.bpp == 4 || ctx.bpp == 8) { + ctx.bitmap_size = ctx.row_stride * write_im->sy; + } + } + } + } + + bmp_write_file_header(out, &ctx); + bmp_write_info_header(out, write_im, &ctx); + if (ctx.palette_size) { + bmp_write_palette(out, write_im, &ctx); + } + + error = bmp_write_pixels(out, write_im, &ctx); + + if (!error && (ctx.compression == BMP_BI_RLE8 || ctx.compression == BMP_BI_RLE4)) { + gdPutC(BMP_RLE_COMMAND, out); + gdPutC(BMP_RLE_ENDOFBITMAP, out); + ctx.bitmap_size += 2; + + gdSeek(out, 2); + gdBMPPutInt(out, 14 + ctx.info_size + ctx.bitmap_size); + + gdSeek(out, 34); + gdBMPPutInt(out, ctx.bitmap_size); + } + + /* If we needed a tmpfile for compression copy it over to out_original */ + if (!error && tmpfile_for_compression && bmp_write_tmpfile_to_ctx(out, out_original)) { + error = 1; + } + if (tmpfile_for_compression && out_original) { + out->gd_free(out); + out = out_original; + out_original = NULL; + } + + ret = error; cleanup: - if (tmpfile_for_compression) { + if (tmpfile_for_compression) { #ifdef _WIN32 - _rmtmp(); + _rmtmp(); #else - fclose(tmpfile_for_compression); + fclose(tmpfile_for_compression); #endif - tmpfile_for_compression = NULL; - } + tmpfile_for_compression = NULL; + } + + if (out_original) { + out_original->gd_free(out_original); + } + if (write_im != im) { + gdImageDestroy(write_im); + } + return ret; +} + +static int bmp_prepare_write_image(gdImagePtr im, int bpp, int flags, gdImagePtr *write_im) +{ + gdImagePtr clone; + + *write_im = im; + if ((bpp == 1 || bpp == 4 || bpp == 8) && im->trueColor) { + if (!(flags & GD_BMP_FLAG_QUANTIZE)) { + return 1; + } + clone = gdImageClone(im); + if (clone == NULL) { + return 1; + } + if (!gdImageTrueColorToPalette(clone, 0, 1 << bpp)) { + gdImageDestroy(clone); + return 1; + } + *write_im = clone; + } + return 0; +} + +static int bmp_auto_bpp(gdImagePtr im) +{ + if (im->trueColor) { + return bmp_has_alpha(im) ? 32 : 24; + } + if (im->colorsTotal <= 2) { + return 1; + } + if (im->colorsTotal <= 16) { + return 4; + } + return 8; +} + +static int bmp_has_alpha(gdImagePtr im) +{ + int x, y; + + for (y = 0; y < im->sy; y++) { + for (x = 0; x < im->sx; x++) { + if (gdTrueColorGetAlpha(gdImageGetPixel(im, x, y)) != gdAlphaOpaque) { + return 1; + } + } + } + return 0; +} + +static int bmp_resolve_write_ctx(gdImagePtr im, int bpp_hint, int compression, int flags, + bmp_write_ctx_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->bpp = (bpp_hint > 0) ? bpp_hint : bmp_auto_bpp(im); + switch (ctx->bpp) { + case 1: + case 4: + case 8: + case 16: + case 24: + case 32: + break; + default: + return 1; + } + + if ((ctx->bpp == 1 || ctx->bpp == 4 || ctx->bpp == 8) && im->trueColor) { + return 1; + } + if ((ctx->bpp == 1 || ctx->bpp == 4 || ctx->bpp == 8) && im->colorsTotal > (1 << ctx->bpp)) { + return 1; + } + + if (compression == -1) { + if (ctx->bpp == 8) { + ctx->compression = BMP_BI_RLE8; + } else if (ctx->bpp == 4) { + ctx->compression = BMP_BI_RLE4; + } else { + ctx->compression = BMP_BI_RGB; + } + } else { + switch (compression) { + case GD_BMP_COMPRESS_NONE: + ctx->compression = BMP_BI_RGB; + break; + case GD_BMP_COMPRESS_RLE8: + if (ctx->bpp != 8) { + return 1; + } + ctx->compression = BMP_BI_RLE8; + break; + case GD_BMP_COMPRESS_RLE4: + if (ctx->bpp != 4) { + return 1; + } + ctx->compression = BMP_BI_RLE4; + break; + default: + return 1; + } + } + + if (ctx->bpp == 16) { + if (flags & GD_BMP_FLAG_RGB555) { + ctx->red_mask = 0x7C00; + ctx->green_mask = 0x03E0; + ctx->blue_mask = 0x001F; + } else { + ctx->red_mask = 0xF800; + ctx->green_mask = 0x07E0; + ctx->blue_mask = 0x001F; + } + ctx->compression = BMP_BI_BITFIELDS; + } else if (ctx->bpp == 32) { + ctx->red_mask = 0x00FF0000; + ctx->green_mask = 0x0000FF00; + ctx->blue_mask = 0x000000FF; + ctx->alpha_mask = 0xFF000000; + ctx->compression = BMP_BI_BITFIELDS; + } + + ctx->header_ver = ((ctx->bpp == 32 && ctx->alpha_mask) || (flags & GD_BMP_FLAG_FORCE_V4HDR)) + ? BMP_WINDOWS_V4 + : BMP_WINDOWS_V3; + ctx->palette_size = (ctx->bpp <= 8) ? (1 << ctx->bpp) * 4 : 0; + ctx->mask_size = + (ctx->header_ver == BMP_WINDOWS_V3 && ctx->compression == BMP_BI_BITFIELDS) ? 12 : 0; + ctx->info_size = ctx->header_ver + ctx->mask_size + ctx->palette_size; + ctx->row_stride = (((ctx->bpp * im->sx) + 31) / 32) * 4; + if (ctx->compression != BMP_BI_RLE8 && ctx->compression != BMP_BI_RLE4) { + ctx->bitmap_size = ctx->row_stride * im->sy; + } + return 0; +} + +static void bmp_write_file_header(gdIOCtxPtr out, bmp_write_ctx_t *ctx) +{ + gdPutBuf("BM", 2, out); + gdBMPPutInt(out, 14 + ctx->info_size + ctx->bitmap_size); + gdBMPPutWord(out, 0); + gdBMPPutWord(out, 0); + gdBMPPutInt(out, 14 + ctx->info_size); +} + +static void bmp_write_info_header(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int i; + + gdBMPPutInt(out, ctx->header_ver); + gdBMPPutInt(out, im->sx); + gdBMPPutInt(out, im->sy); + gdBMPPutWord(out, 1); + gdBMPPutWord(out, ctx->bpp); + gdBMPPutInt(out, ctx->compression); + gdBMPPutInt(out, ctx->bitmap_size); + gdBMPPutInt(out, 0); + gdBMPPutInt(out, 0); + gdBMPPutInt(out, ctx->palette_size ? (ctx->palette_size / 4) : 0); + gdBMPPutInt(out, 0); + + if (ctx->header_ver == BMP_WINDOWS_V4) { + gdBMPPutInt(out, ctx->red_mask); + gdBMPPutInt(out, ctx->green_mask); + gdBMPPutInt(out, ctx->blue_mask); + gdBMPPutInt(out, ctx->alpha_mask); + gdBMPPutInt(out, 0x73524742); + for (i = 0; i < 9; i++) { + gdBMPPutInt(out, 0); + } + gdBMPPutInt(out, 0); + gdBMPPutInt(out, 0); + gdBMPPutInt(out, 0); + } else if (ctx->mask_size) { + gdBMPPutInt(out, ctx->red_mask); + gdBMPPutInt(out, ctx->green_mask); + gdBMPPutInt(out, ctx->blue_mask); + } +} + +static void bmp_write_palette(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int i, count; + + count = 1 << ctx->bpp; + for (i = 0; i < count; i++) { + if (i < im->colorsTotal) { + gdPutC(gdImageBlue(im, i), out); + gdPutC(gdImageGreen(im, i), out); + gdPutC(gdImageRed(im, i), out); + } else { + gdPutC(0, out); + gdPutC(0, out); + gdPutC(0, out); + } + gdPutC(0, out); + } +} + +static int bmp_write_pixels(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + switch (ctx->bpp) { + case 1: + return bmp_write_pixels_1bit(out, im, ctx); + case 4: + return bmp_write_pixels_4bit(out, im, ctx); + case 8: + return bmp_write_pixels_8bit(out, im, ctx); + case 16: + return bmp_write_pixels_16bit(out, im, ctx); + case 24: + return bmp_write_pixels_24bit(out, im, ctx); + case 32: + return bmp_write_pixels_32bit(out, im, ctx); + default: + return 1; + } +} + +static int bmp_write_padding(gdIOCtxPtr out, int count) +{ + while (count-- > 0) { + gdPutC(0, out); + } + return 0; +} + +static int bmp_write_pixels_1bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos, bit, byte, bytes_written, index; + + for (row = im->sy - 1; row >= 0; row--) { + bytes_written = 0; + for (xpos = 0; xpos < im->sx; xpos += 8) { + byte = 0; + for (bit = 0; bit < 8 && xpos + bit < im->sx; bit++) { + index = gdImageGetPixel(im, xpos + bit, row); + if (index < 0 || index > 1) { + return 1; + } + byte |= (index & 0x01) << (7 - bit); + } + gdPutC(byte, out); + bytes_written++; + } + bmp_write_padding(out, ctx->row_stride - bytes_written); + } + return 0; +} - if (out_original) { - out_original->gd_free(out_original); - } - return ret; +static int bmp_write_pixels_4bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos, high, low, bytes_written; + + if (ctx->compression == BMP_BI_RLE4) { + for (row = im->sy - 1; row >= 0; row--) { + int compressed_size = bmp_write_rle4_row(out, im, row); + if (compressed_size < 0) { + return 1; + } + ctx->bitmap_size += compressed_size; + ctx->bitmap_size += 2; + gdPutC(BMP_RLE_COMMAND, out); + gdPutC(BMP_RLE_ENDOFLINE, out); + } + return 0; + } + + for (row = im->sy - 1; row >= 0; row--) { + bytes_written = 0; + for (xpos = 0; xpos < im->sx; xpos += 2) { + high = gdImageGetPixel(im, xpos, row); + if (high < 0 || high > 15) { + return 1; + } + if (xpos + 1 < im->sx) { + low = gdImageGetPixel(im, xpos + 1, row); + if (low < 0 || low > 15) { + return 1; + } + } else { + low = 0; + } + gdPutC(((high & 0x0f) << 4) | (low & 0x0f), out); + bytes_written++; + } + bmp_write_padding(out, ctx->row_stride - bytes_written); + } + return 0; +} + +static int bmp_write_rle4_row(gdIOCtxPtr out, gdImagePtr im, int row) +{ + int xpos, chunk, i, value, next; + int bytes; + int total = 0; + + for (xpos = 0; xpos < im->sx;) { + chunk = im->sx - xpos; + if (chunk > 255) { + chunk = 255; + } + if (chunk < 3) { + value = gdImageGetPixel(im, xpos, row); + if (value < 0 || value > 15) { + return 1; + } + next = value; + if (chunk == 2) { + next = gdImageGetPixel(im, xpos + 1, row); + if (next < 0 || next > 15) { + return 1; + } + } + gdPutC(chunk, out); + gdPutC(((value & 0x0f) << 4) | (next & 0x0f), out); + total += 2; + xpos += chunk; + continue; + } + gdPutC(BMP_RLE_COMMAND, out); + gdPutC(chunk, out); + total += 2; + bytes = (chunk + 1) / 2; + for (i = 0; i < chunk; i += 2) { + value = gdImageGetPixel(im, xpos + i, row); + if (value < 0 || value > 15) { + return 1; + } + if (i + 1 < chunk) { + next = gdImageGetPixel(im, xpos + i + 1, row); + if (next < 0 || next > 15) { + return 1; + } + } else { + next = 0; + } + gdPutC(((value & 0x0f) << 4) | (next & 0x0f), out); + total++; + } + if (bytes & 1) { + gdPutC(0, out); + total++; + } + xpos += chunk; + } + return total; +} + +static int bmp_write_pixels_8bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos; + unsigned char *uncompressed_row = NULL; + + if (ctx->compression == BMP_BI_RLE8) { + uncompressed_row = (unsigned char *)gdCalloc(gdImageSX(im) * 2, sizeof(char)); + if (!uncompressed_row) { + return 1; + } + for (row = im->sy - 1; row >= 0; row--) { + int compressed_size; + for (xpos = 0; xpos < im->sx; xpos++) { + uncompressed_row[xpos] = (unsigned char)gdImageGetPixel(im, xpos, row); + } + compressed_size = compress_row(uncompressed_row, gdImageSX(im)); + if (compressed_size < 0) { + gdFree(uncompressed_row); + return 1; + } + ctx->bitmap_size += compressed_size; + if (gdPutBuf(uncompressed_row, compressed_size, out) != compressed_size) { + gd_error("gd-bmp write error\n"); + gdFree(uncompressed_row); + return 1; + } + gdPutC(BMP_RLE_COMMAND, out); + gdPutC(BMP_RLE_ENDOFLINE, out); + ctx->bitmap_size += 2; + } + gdFree(uncompressed_row); + return 0; + } + + for (row = im->sy - 1; row >= 0; row--) { + for (xpos = 0; xpos < im->sx; xpos++) { + gdPutC(gdImageGetPixel(im, xpos, row), out); + } + bmp_write_padding(out, ctx->row_stride - im->sx); + } + return 0; +} + +static int bmp_write_pixels_16bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos, pixel, red, green, blue; + unsigned int packed; + + for (row = im->sy - 1; row >= 0; row--) { + for (xpos = 0; xpos < im->sx; xpos++) { + pixel = gdImageGetPixel(im, xpos, row); + if (im->trueColor) { + red = gdTrueColorGetRed(pixel); + green = gdTrueColorGetGreen(pixel); + blue = gdTrueColorGetBlue(pixel); + } else { + red = gdImageRed(im, pixel); + green = gdImageGreen(im, pixel); + blue = gdImageBlue(im, pixel); + } + packed = bmp_pack_mask(red, ctx->red_mask) | bmp_pack_mask(green, ctx->green_mask) | + bmp_pack_mask(blue, ctx->blue_mask); + gdBMPPutWord(out, (int)packed); + } + bmp_write_padding(out, ctx->row_stride - im->sx * 2); + } + return 0; +} + +static int bmp_write_pixels_24bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos, pixel; + + for (row = im->sy - 1; row >= 0; row--) { + for (xpos = 0; xpos < im->sx; xpos++) { + pixel = gdImageGetPixel(im, xpos, row); + if (im->trueColor) { + gdPutC(gdTrueColorGetBlue(pixel), out); + gdPutC(gdTrueColorGetGreen(pixel), out); + gdPutC(gdTrueColorGetRed(pixel), out); + } else { + gdPutC(gdImageBlue(im, pixel), out); + gdPutC(gdImageGreen(im, pixel), out); + gdPutC(gdImageRed(im, pixel), out); + } + } + bmp_write_padding(out, ctx->row_stride - im->sx * 3); + } + return 0; +} + +static int bmp_write_pixels_32bit(gdIOCtxPtr out, gdImagePtr im, bmp_write_ctx_t *ctx) +{ + int row, xpos, pixel, red, green, blue, alpha; + unsigned int packed; + + for (row = im->sy - 1; row >= 0; row--) { + for (xpos = 0; xpos < im->sx; xpos++) { + pixel = gdImageGetPixel(im, xpos, row); + if (im->trueColor) { + red = gdTrueColorGetRed(pixel); + green = gdTrueColorGetGreen(pixel); + blue = gdTrueColorGetBlue(pixel); + alpha = bmp_gd_to_alpha(gdTrueColorGetAlpha(pixel)); + } else { + red = gdImageRed(im, pixel); + green = gdImageGreen(im, pixel); + blue = gdImageBlue(im, pixel); + alpha = bmp_gd_to_alpha(im->alpha[pixel]); + } + packed = bmp_pack_mask(red, ctx->red_mask) | bmp_pack_mask(green, ctx->green_mask) | + bmp_pack_mask(blue, ctx->blue_mask) | bmp_pack_mask(alpha, ctx->alpha_mask); + gdBMPPutInt(out, (int)packed); + } + } + return 0; +} + +static int bmp_write_tmpfile_to_ctx(gdIOCtxPtr out, gdIOCtxPtr out_original) +{ + unsigned char *copy_buffer = NULL; + int buffer_size = 0; + + gdSeek(out, 0); + copy_buffer = (unsigned char *)gdMalloc(1024 * sizeof(unsigned char)); + if (copy_buffer == NULL) { + return 1; + } + + while ((buffer_size = gdGetBuf(copy_buffer, 1024, out)) != EOF) { + if (buffer_size == 0) { + break; + } + if (gdPutBuf(copy_buffer, buffer_size, out_original) != buffer_size) { + gd_error("gd-bmp write error\n"); + gdFree(copy_buffer); + return 1; + } + } + gdFree(copy_buffer); + return 0; } static int compress_row(unsigned char *row, int length) { - int rle_type = 0; - int compressed_length = 0; - int pixel = 0, compressed_run = 0, rle_compression = 0; - unsigned char *uncompressed_row = NULL, *uncompressed_rowp = NULL, *uncompressed_start = NULL; - - uncompressed_row = (unsigned char *) gdMalloc(length); - if (!uncompressed_row) { - return -1; - } - - memcpy(uncompressed_row, row, length); - uncompressed_start = uncompressed_rowp = uncompressed_row; - - for (pixel = 0; pixel < length; pixel++) { - if (compressed_run == 0) { - uncompressed_row = uncompressed_rowp; - compressed_run++; - uncompressed_rowp++; - rle_type = BMP_RLE_TYPE_RAW; - continue; - } - - if (compressed_run == 1) { - /* Compare next byte */ - if (memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) { - rle_type = BMP_RLE_TYPE_RLE; - } - } - - if (rle_type == BMP_RLE_TYPE_RLE) { - if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) != 0) { - /* more than what we can store in a single run or run is over due to non match, force write */ - rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row); - row += rle_compression; - compressed_length += rle_compression; - compressed_run = 0; - pixel--; - } else { - compressed_run++; - uncompressed_rowp++; - } - } else { - if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) { - /* more than what we can store in a single run or run is over due to match, force write */ - rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row); - row += rle_compression; - compressed_length += rle_compression; - compressed_run = 0; - pixel--; - } else { - /* add this pixel to the row */ - compressed_run++; - uncompressed_rowp++; - } - - } - } - - if (compressed_run) { - compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row); - } - - gdFree(uncompressed_start); - - return compressed_length; + int rle_type = 0; + int compressed_length = 0; + int pixel = 0, compressed_run = 0, rle_compression = 0; + unsigned char *uncompressed_row = NULL, *uncompressed_rowp = NULL, *uncompressed_start = NULL; + GD_ASSUME(length > 0); + uncompressed_row = (unsigned char *)gdMalloc(length); + if (!uncompressed_row) { + return -1; + } + + memcpy(uncompressed_row, row, length); + uncompressed_start = uncompressed_rowp = uncompressed_row; + + for (pixel = 0; pixel < length; pixel++) { + if (compressed_run == 0) { + uncompressed_row = uncompressed_rowp; + compressed_run++; + uncompressed_rowp++; + rle_type = BMP_RLE_TYPE_RAW; + continue; + } + + if (compressed_run == 1) { + /* Compare next byte */ + if (memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) { + rle_type = BMP_RLE_TYPE_RLE; + } + } + + if (rle_type == BMP_RLE_TYPE_RLE) { + if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) != 0) { + /* more than what we can store in a single run or run is over + * due to non match, force write */ + rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row); + row += rle_compression; + compressed_length += rle_compression; + compressed_run = 0; + pixel--; + } else { + compressed_run++; + uncompressed_rowp++; + } + } else { + if (compressed_run >= 128 || memcmp(uncompressed_rowp, uncompressed_rowp - 1, 1) == 0) { + /* more than what we can store in a single run or run is over + * due to match, force write */ + rle_compression = build_rle_packet(row, rle_type, compressed_run, uncompressed_row); + row += rle_compression; + compressed_length += rle_compression; + compressed_run = 0; + pixel--; + } else { + /* add this pixel to the row */ + compressed_run++; + uncompressed_rowp++; + } + } + } + + if (compressed_run) { + compressed_length += build_rle_packet(row, rle_type, compressed_run, uncompressed_row); + } + + gdFree(uncompressed_start); + + return compressed_length; } static int build_rle_packet(unsigned char *row, int packet_type, int length, unsigned char *data) { - int compressed_size = 0; - if (length < 1 || length > 128) { - return 0; - } - - /* Bitmap specific cases is that we can't have uncompressed rows of length 1 or 2 */ - if (packet_type == BMP_RLE_TYPE_RAW && length < 3) { - int i = 0; - for (i = 0; i < length; i++) { - compressed_size += 2; - memset(row, 1, 1); - row++; - - memcpy(row, data++, 1); - row++; - } - } else if (packet_type == BMP_RLE_TYPE_RLE) { - compressed_size = 2; - memset(row, length, 1); - row++; - - memcpy(row, data, 1); - row++; - } else { - compressed_size = 2 + length; - memset(row, BMP_RLE_COMMAND, 1); - row++; - - memset(row, length, 1); - row++; - - memcpy(row, data, length); - row += length; - - /* Must be an even number for an uncompressed run */ - if (length % 2) { - memset(row, 0, 1); - row++; - compressed_size++; - } - } - return compressed_size; + int compressed_size = 0; + if (length < 1 || length > 128) { + return 0; + } + + /* Bitmap specific cases is that we can't have uncompressed rows of length 1 + * or 2 */ + if (packet_type == BMP_RLE_TYPE_RAW && length < 3) { + int i = 0; + for (i = 0; i < length; i++) { + compressed_size += 2; + memset(row, 1, 1); + row++; + + memcpy(row, data++, 1); + row++; + } + } else if (packet_type == BMP_RLE_TYPE_RLE) { + compressed_size = 2; + memset(row, length, 1); + row++; + + memcpy(row, data, 1); + row++; + } else { + compressed_size = 2 + length; + memset(row, BMP_RLE_COMMAND, 1); + row++; + + memset(row, length, 1); + row++; + + memcpy(row, data, length); + row += length; + + /* Must be an even number for an uncompressed run */ + if (length % 2) { + memset(row, 0, 1); + row++; + compressed_size++; + } + } + return compressed_size; } /* - Function: gdImageCreateFromBmp + Function: gdImageCreateFromBmp */ -gdImagePtr gdImageCreateFromBmp(FILE * inFile) +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmp(FILE *inFile) { - gdImagePtr im = 0; - gdIOCtx *in = gdNewFileCtx(inFile); - if (in == NULL) return NULL; - im = gdImageCreateFromBmpCtx(in); - in->gd_free(in); - return im; + gdImagePtr im = 0; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromBmpCtx(in); + in->gd_free(in); + return im; } /* - Function: gdImageCreateFromBmpPtr + Function: gdImageCreateFromBmpPtr */ -gdImagePtr gdImageCreateFromBmpPtr(int size, void *data) +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpPtr(int size, void *data) { - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - if (in == NULL) return NULL; - im = gdImageCreateFromBmpCtx(in); - in->gd_free(in); - return im; + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) + return NULL; + im = gdImageCreateFromBmpCtx(in); + in->gd_free(in); + return im; } /* - Function: gdImageCreateFromBmpCtx + Function: gdImageCreateFromBmpCtx */ -gdImagePtr gdImageCreateFromBmpCtx(gdIOCtxPtr infile) +BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpCtx(gdIOCtxPtr infile) { - bmp_hdr_t *hdr; - bmp_info_t *info; - gdImagePtr im = NULL; - int error = 0; - - if (!(hdr= (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) { - return NULL; - } - - if (bmp_read_header(infile, hdr)) { - gdFree(hdr); - return NULL; - } - - if (hdr->magic != 0x4d42) { - gdFree(hdr); - return NULL; - } - - if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) { - gdFree(hdr); - return NULL; - } - - if (bmp_read_info(infile, info)) { - gdFree(hdr); - gdFree(info); - return NULL; - } - - BMP_DEBUG(printf("Numcolours: %d\n", info->numcolors)); - BMP_DEBUG(printf("Width: %d\n", info->width)); - BMP_DEBUG(printf("Height: %d\n", info->height)); - BMP_DEBUG(printf("Planes: %d\n", info->numplanes)); - BMP_DEBUG(printf("Depth: %d\n", info->depth)); - BMP_DEBUG(printf("Offset: %d\n", hdr->off)); - - if (info->depth >= 16) { - im = gdImageCreateTrueColor(info->width, info->height); - } else { - im = gdImageCreate(info->width, info->height); - } - - if (!im) { - gdFree(hdr); - gdFree(info); - return NULL; - } - - switch (info->depth) { - case 1: - BMP_DEBUG(printf("1-bit image\n")); - error = bmp_read_1bit(im, infile, info, hdr); - break; - case 4: - BMP_DEBUG(printf("4-bit image\n")); - error = bmp_read_4bit(im, infile, info, hdr); - break; - case 8: - BMP_DEBUG(printf("8-bit image\n")); - error = bmp_read_8bit(im, infile, info, hdr); - break; - case 16: - case 24: - case 32: - BMP_DEBUG(printf("Direct BMP image\n")); - error = bmp_read_direct(im, infile, info, hdr); - break; - default: - BMP_DEBUG(printf("Unknown bit count\n")); - error = 1; - } - - gdFree(hdr); - gdFree(info); - - if (error) { - gdImageDestroy(im); - return NULL; - } - - return im; + bmp_hdr_t *hdr; + bmp_info_t *info; + gdImagePtr im = NULL; + int error = 0; + + if (!(hdr = (bmp_hdr_t *)gdCalloc(1, sizeof(bmp_hdr_t)))) { + return NULL; + } + + if (bmp_read_header(infile, hdr)) { + gdFree(hdr); + return NULL; + } + + if (hdr->magic != 0x4d42) { + gdFree(hdr); + return NULL; + } + + if (!(info = (bmp_info_t *)gdCalloc(1, sizeof(bmp_info_t)))) { + gdFree(hdr); + return NULL; + } + + if (bmp_read_info(infile, info)) { + gdFree(hdr); + gdFree(info); + return NULL; + } + + if (bmp_validate_info(info, hdr)) { + gdFree(hdr); + gdFree(info); + return NULL; + } + + BMP_DEBUG(printf("Numcolours: %d\n", info->numcolors)); + BMP_DEBUG(printf("Width: %d\n", info->width)); + BMP_DEBUG(printf("Height: %d\n", info->height)); + BMP_DEBUG(printf("Planes: %d\n", info->numplanes)); + BMP_DEBUG(printf("Depth: %d\n", info->depth)); + BMP_DEBUG(printf("Offset: %d\n", hdr->off)); + + if (info->depth >= 16) { + im = gdImageCreateTrueColor(info->width, info->height); + } else { + im = gdImageCreate(info->width, info->height); + } + + if (!im) { + gdFree(hdr); + gdFree(info); + return NULL; + } + + switch (info->depth) { + case 1: + BMP_DEBUG(printf("1-bit image\n")); + error = bmp_read_1bit(im, infile, info, hdr); + break; + case 2: + BMP_DEBUG(printf("2-bit image\n")); + error = bmp_read_2bit(im, infile, info, hdr); + break; + case 4: + BMP_DEBUG(printf("4-bit image\n")); + error = bmp_read_4bit(im, infile, info, hdr); + break; + case 8: + BMP_DEBUG(printf("8-bit image\n")); + error = bmp_read_8bit(im, infile, info, hdr); + break; + case 16: + case 24: + case 32: + BMP_DEBUG(printf("Direct BMP image\n")); + error = bmp_read_direct(im, infile, info, hdr); + break; + default: + BMP_DEBUG(printf("Unknown bit count\n")); + error = 1; + } + + gdFree(hdr); + gdFree(info); + + if (error) { + gdImageDestroy(im); + return NULL; + } + + return im; } static int bmp_read_header(gdIOCtx *infile, bmp_hdr_t *hdr) { - if( - !gdGetWordLSB(&hdr->magic, infile) || - !gdGetIntLSB(&hdr->size, infile) || - !gdGetWordLSB(&hdr->reserved1, infile) || - !gdGetWordLSB(&hdr->reserved2 , infile) || - !gdGetIntLSB(&hdr->off , infile) - ) { - return 1; - } - return 0; + if (!gdGetWordLSB(&hdr->magic, infile) || !gdGetIntLSB(&hdr->size, infile) || + !gdGetWordLSB(&hdr->reserved1, infile) || !gdGetWordLSB(&hdr->reserved2, infile) || + !gdGetIntLSB(&hdr->off, infile)) { + return 1; + } + return 0; } static int bmp_read_info(gdIOCtx *infile, bmp_info_t *info) { - /* read BMP length so we can work out the version */ - if (!gdGetIntLSB(&info->len, infile)) { - return 1; - } - - switch (info->len) { - /* For now treat Windows v4 + v5 as v3 */ - case BMP_WINDOWS_V3: - case BMP_WINDOWS_V4: - case BMP_WINDOWS_V5: - BMP_DEBUG(printf("Reading Windows Header\n")); - if (bmp_read_windows_v3_info(infile, info)) { - return 1; - } - break; - case BMP_OS2_V1: - if (bmp_read_os2_v1_info(infile, info)) { - return 1; - } - break; - case BMP_OS2_V2: - if (bmp_read_os2_v2_info(infile, info)) { - return 1; - } - break; - default: - BMP_DEBUG(printf("Unhandled bitmap\n")); - return 1; - } - return 0; + /* read BMP length so we can work out the version */ + if (!gdGetIntLSB(&info->len, infile)) { + return 1; + } + + switch (info->len) { + /* For now treat Windows v4 + v5 as v3 */ + case BMP_WINDOWS_V3: + case BMP_WINDOWS_V2: + case BMP_WINDOWS_V3_ALPHA: + case BMP_WINDOWS_V4: + case BMP_WINDOWS_V5: + BMP_DEBUG(printf("Reading Windows Header\n")); + if (bmp_read_windows_v3_info(infile, info)) { + return 1; + } + break; + case BMP_OS2_V1: + if (bmp_read_os2_v1_info(infile, info)) { + return 1; + } + break; + case BMP_OS2_V2_SHORT: + case BMP_OS2_V2: + if (bmp_read_os2_v2_info(infile, info)) { + return 1; + } + break; + default: + BMP_DEBUG(printf("Unhandled bitmap\n")); + return 1; + } + return 0; +} + +static int bmp_skip_bytes(gdIOCtxPtr infile, int count) +{ + int i, c; + + for (i = 0; i < count; i++) { + if (!gdGetByte(&c, infile)) { + return 1; + } + } + return 0; +} + +static int bmp_read_bitfield_masks(gdIOCtxPtr infile, bmp_info_t *info, int read_alpha) +{ + int red, green, blue, alpha; + + if (!gdGetIntLSB(&red, infile) || !gdGetIntLSB(&green, infile) || !gdGetIntLSB(&blue, infile)) { + return 1; + } + + info->red_mask = (unsigned int)red; + info->green_mask = (unsigned int)green; + info->blue_mask = (unsigned int)blue; + + if (read_alpha) { + if (!gdGetIntLSB(&alpha, infile)) { + return 1; + } + info->alpha_mask = (unsigned int)alpha; + } + + return 0; +} + +static int bmp_validate_info(bmp_info_t *info, bmp_hdr_t *hdr) +{ + int image_size; + int min_size; + + if (hdr->off < 14 + info->len || hdr->size < 0 || hdr->off < 0) { + return 1; + } + + if (info->numplanes != 1) { + return 1; + } + + if (info->depth != 1 && info->depth != 2 && info->depth != 4 && info->depth != 8 && + info->depth != 16 && info->depth != 24 && info->depth != 32) { + return 1; + } + + if (info->topdown && (info->enctype == BMP_BI_RLE4 || info->enctype == BMP_BI_RLE8)) { + return 1; + } + + if (overflow2(info->width, info->height)) { + return 1; + } + + if (info->hres < 0 || info->vres < 0) { + return 1; + } + if (info->hres > 0 && info->vres > 0 && + (info->hres / info->vres > 1000 || info->vres / info->hres > 1000)) { + return 1; + } + + if (info->enctype == BMP_BI_RGB || info->enctype == BMP_BI_BITFIELDS || + info->enctype == BMP_BI_ALPHABITFIELDS) { + if (bmp_image_size(info->width, info->height, info->depth, &image_size)) { + return 1; + } + if (info->size != 0 && info->size != image_size) { + return 1; + } + if (hdr->size != 0) { + min_size = hdr->off + image_size; + if (min_size < hdr->off) { + return 1; + } + if (hdr->size < min_size && hdr->size != 14 + info->len) { + return 1; + } + if (hdr->size > min_size && hdr->size - min_size > 1024 * 1024) { + return 1; + } + } + } + + return 0; } static int bmp_read_windows_v3_info(gdIOCtxPtr infile, bmp_info_t *info) { - if ( - !gdGetIntLSB(&info->width, infile) || - !gdGetIntLSB(&info->height, infile) || - !gdGetWordLSB(&info->numplanes, infile) || - !gdGetWordLSB(&info->depth, infile) || - !gdGetIntLSB(&info->enctype, infile) || - !gdGetIntLSB(&info->size, infile) || - !gdGetIntLSB(&info->hres, infile) || - !gdGetIntLSB(&info->vres, infile) || - !gdGetIntLSB(&info->numcolors, infile) || - !gdGetIntLSB(&info->mincolors, infile) - ) { - return 1; - } - - if (info->height < 0) { - info->topdown = 1; - info->height = -info->height; - } else { - info->topdown = 0; - } - - info->type = BMP_PALETTE_4; - - if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 || - info->depth <= 0 || info->numcolors < 0 || info->mincolors < 0) { - return 1; - } - - return 0; + int extra = info->len - BMP_WINDOWS_V3; + + if (!gdGetIntLSB(&info->width, infile) || !gdGetIntLSB(&info->height, infile) || + !gdGetWordLSB(&info->numplanes, infile) || !gdGetWordLSB(&info->depth, infile) || + !gdGetIntLSB(&info->enctype, infile) || !gdGetIntLSB(&info->size, infile) || + !gdGetIntLSB(&info->hres, infile) || !gdGetIntLSB(&info->vres, infile) || + !gdGetIntLSB(&info->numcolors, infile) || !gdGetIntLSB(&info->mincolors, infile)) { + return 1; + } + + /* Unlikely, but possible -- largest signed value won't fit in unsigned. */ + if (info->height == 0 || info->height == INT_MIN) + return 1; + if (info->height < 0) { + info->topdown = 1; + info->height = -info->height; + } else { + info->topdown = 0; + } + + info->type = BMP_PALETTE_4; + + if (extra > 0) { + if (extra < 12 || bmp_read_bitfield_masks(infile, info, extra >= 16)) { + return 1; + } + extra -= (extra >= 16) ? 16 : 12; + if (bmp_skip_bytes(infile, extra)) { + return 1; + } + } + + /* Height was checked above. */ + if (info->width <= 0 || info->numplanes <= 0 || info->depth <= 0 || info->numcolors < 0 || + info->mincolors < 0) { + return 1; + } + + return 0; } static int bmp_read_os2_v1_info(gdIOCtxPtr infile, bmp_info_t *info) { - if ( - !gdGetWordLSB((signed short int *)&info->width, infile) || - !gdGetWordLSB((signed short int *)&info->height, infile) || - !gdGetWordLSB(&info->numplanes, infile) || - !gdGetWordLSB(&info->depth, infile) - ) { - return 1; - } - - /* OS2 v1 doesn't support topdown */ - info->topdown = 0; - - info->numcolors = 1 << info->depth; - info->type = BMP_PALETTE_3; - - if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 || - info->depth <= 0 || info->numcolors < 0) { - return 1; - } - - return 0; + if (!gdGetWordLSB((signed short int *)&info->width, infile) || + !gdGetWordLSB((signed short int *)&info->height, infile) || + !gdGetWordLSB(&info->numplanes, infile) || !gdGetWordLSB(&info->depth, infile)) { + return 1; + } + + /* OS2 v1 doesn't support topdown */ + info->topdown = 0; + + /* The spec says the depth can only be a few value values. */ + if (info->depth != 1 && info->depth != 4 && info->depth != 8 && info->depth != 16 && + info->depth != 24) { + return 1; + } + + info->numcolors = (info->depth <= 8) ? (1 << info->depth) : 0; + info->type = BMP_PALETTE_3; + + if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0) { + return 1; + } + + return 0; } static int bmp_read_os2_v2_info(gdIOCtxPtr infile, bmp_info_t *info) { - char useless_bytes[24]; - if ( - !gdGetIntLSB(&info->width, infile) || - !gdGetIntLSB(&info->height, infile) || - !gdGetWordLSB(&info->numplanes, infile) || - !gdGetWordLSB(&info->depth, infile) || - !gdGetIntLSB(&info->enctype, infile) || - !gdGetIntLSB(&info->size, infile) || - !gdGetIntLSB(&info->hres, infile) || - !gdGetIntLSB(&info->vres, infile) || - !gdGetIntLSB(&info->numcolors, infile) || - !gdGetIntLSB(&info->mincolors, infile) - ) { - return 1; - } - - /* Let's seek the next 24 pointless bytes, we don't care too much about it */ - if (!gdGetBuf(useless_bytes, 24, infile)) { - return 1; - } - - if (info->height < 0) { - info->topdown = 1; - info->height = -info->height; - } else { - info->topdown = 0; - } - - info->type = BMP_PALETTE_4; - - if (info->width <= 0 || info->height <= 0 || info->numplanes <= 0 || - info->depth <= 0 || info->numcolors < 0 || info->mincolors < 0) { - return 1; - } - - - return 0; + char useless_bytes[24]; + if (!gdGetIntLSB(&info->width, infile) || !gdGetIntLSB(&info->height, infile) || + !gdGetWordLSB(&info->numplanes, infile) || !gdGetWordLSB(&info->depth, infile)) { + return 1; + } + + if (info->len == BMP_OS2_V2_SHORT) { + info->enctype = BMP_BI_RGB; + info->size = 0; + info->hres = 0; + info->vres = 0; + info->numcolors = (info->depth <= 8) ? (1 << info->depth) : 0; + info->mincolors = 0; + goto done; + } + + if (!gdGetIntLSB(&info->enctype, infile) || !gdGetIntLSB(&info->size, infile) || + !gdGetIntLSB(&info->hres, infile) || !gdGetIntLSB(&info->vres, infile) || + !gdGetIntLSB(&info->numcolors, infile) || !gdGetIntLSB(&info->mincolors, infile)) { + return 1; + } + + /* Lets seek the next 24 pointless bytes, we don't care too much about it */ + if (!gdGetBuf(useless_bytes, 24, infile)) { + return 1; + } + +done: + /* Unlikely, but possible -- largest signed value won't fit in unsigned. */ + if (info->height == 0 || info->height == INT_MIN) + return 1; + if (info->height < 0) { + info->topdown = 1; + info->height = -info->height; + } else { + info->topdown = 0; + } + + info->type = BMP_PALETTE_4; + + /* Height was checked above. */ + if (info->width <= 0 || info->numplanes <= 0 || info->depth <= 0 || info->numcolors < 0 || + info->mincolors < 0) { + return 1; + } + + return 0; } static int bmp_read_direct(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header) { - int ypos = 0, xpos = 0, row = 0; - int padding = 0, alpha = 0, red = 0, green = 0, blue = 0; - signed short int data = 0; - - switch(info->enctype) { - case BMP_BI_RGB: - /* no-op */ - break; - - case BMP_BI_BITFIELDS: - if (info->depth == 24) { - BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n")); - return 1; - } - BMP_DEBUG(printf("Currently no bitfield support\n")); - return 1; - break; - - case BMP_BI_RLE8: - if (info->depth != 8) { - BMP_DEBUG(printf("RLE is only valid for 8-bit images\n")); - return 1; - } - break; - case BMP_BI_RLE4: - if (info->depth != 4) { - BMP_DEBUG(printf("RLE is only valid for 4-bit images\n")); - return 1; - } - break; - case BMP_BI_JPEG: - case BMP_BI_PNG: - default: - BMP_DEBUG(printf("Unsupported BMP compression format\n")); - return 1; - } - - /* There is a chance the data isn't until later, would be weird but it is possible */ - if (gdTell(infile) != header->off) { - /* Should make sure we don't seek past the file size */ - if (!gdSeek(infile, header->off)) { - return 1; - } - } - - /* The line must be divisible by 4, else it's padded with NULLs */ - padding = ((int)(info->depth / 8) * info->width) % 4; - if (padding) { - padding = 4 - padding; - } - - - for (ypos = 0; ypos < info->height; ++ypos) { - if (info->topdown) { - row = ypos; - } else { - row = info->height - ypos - 1; - } - - for (xpos = 0; xpos < info->width; xpos++) { - if (info->depth == 16) { - if (!gdGetWordLSB(&data, infile)) { - return 1; - } - BMP_DEBUG(printf("Data: %X\n", data)); - red = ((data & 0x7C00) >> 10) << 3; - green = ((data & 0x3E0) >> 5) << 3; - blue = (data & 0x1F) << 3; - BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red, green, blue)); - } else if (info->depth == 24) { - if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile)) { - return 1; - } - } else { - if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || !gdGetByte(&red, infile) || !gdGetByte(&alpha, infile)) { - return 1; - } - } - /*alpha = gdAlphaMax - (alpha >> 1);*/ - gdImageSetPixel(im, xpos, row, gdTrueColor(red, green, blue)); - } - for (xpos = padding; xpos > 0; --xpos) { - if (!gdGetByte(&red, infile)) { - return 1; - } - } - } - - return 0; + int ypos = 0, xpos = 0, row = 0; + int padding = 0, red = 0, green = 0, blue = 0; + int alpha = gdAlphaOpaque; + signed short int data16 = 0; + int data32 = 0; + unsigned int mask = 0; + + if (info->depth == 16 && info->enctype == BMP_BI_RGB) { + info->red_mask = 0x7C00; + info->green_mask = 0x03E0; + info->blue_mask = 0x001F; + } else if (info->depth == 32 && info->enctype == BMP_BI_RGB) { + info->red_mask = 0x00FF0000; + info->green_mask = 0x0000FF00; + info->blue_mask = 0x000000FF; + } + + switch (info->enctype) { + case BMP_BI_RGB: + /* no-op */ + break; + + case BMP_BI_BITFIELDS: + if (info->depth == 24) { + BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n")); + return 1; + } + if (info->len == BMP_WINDOWS_V3 && bmp_read_bitfield_masks(infile, info, 0)) { + return 1; + } + break; + case BMP_BI_ALPHABITFIELDS: + if (info->depth != 16 && info->depth != 32) { + return 1; + } + if (info->len == BMP_WINDOWS_V3 && bmp_read_bitfield_masks(infile, info, 1)) { + return 1; + } + break; + + case BMP_BI_RLE8: + if (info->depth != 8) { + BMP_DEBUG(printf("RLE is only valid for 8-bit images\n")); + return 1; + } + break; + case BMP_BI_RLE4: + if (info->depth != 4) { + BMP_DEBUG(printf("RLE is only valid for 4-bit images\n")); + return 1; + } + break; + case BMP_BI_JPEG: + case BMP_BI_PNG: + default: + BMP_DEBUG(printf("Unsupported BMP compression format\n")); + return 1; + } + + if (info->depth == 16 || info->depth == 32) { + mask = info->red_mask | info->green_mask | info->blue_mask; + if (info->red_mask == 0 || info->green_mask == 0 || info->blue_mask == 0) { + return 1; + } + if ((info->red_mask & info->green_mask) || (info->red_mask & info->blue_mask) || + (info->green_mask & info->blue_mask)) { + return 1; + } + if (info->depth == 16 && (mask & 0xFFFF0000U)) { + return 1; + } + } + + /* There is a chance the data isn't until later, would be weird but it is + * possible */ + if (gdTell(infile) != header->off) { + /* Should make sure we don't seek past the file size */ + if (!gdSeek(infile, header->off)) { + return 1; + } + } + + /* The line must be divisible by 4, else its padded with NULLs */ + if (bmp_row_padding(info->width, info->depth, &padding)) { + return 1; + } + + for (ypos = 0; ypos < info->height; ++ypos) { + if (info->topdown) { + row = ypos; + } else { + row = info->height - ypos - 1; + } + + for (xpos = 0; xpos < info->width; xpos++) { + if (info->depth == 16) { + if (!gdGetWordLSB(&data16, infile)) { + return 1; + } + BMP_DEBUG(printf("Data: %X\n", data16)); + red = bmp_extract_mask((unsigned short)data16, info->red_mask); + green = bmp_extract_mask((unsigned short)data16, info->green_mask); + blue = bmp_extract_mask((unsigned short)data16, info->blue_mask); + alpha = info->alpha_mask ? bmp_alpha_to_gd(bmp_extract_mask((unsigned short)data16, + info->alpha_mask)) + : gdAlphaOpaque; + BMP_DEBUG(printf("R: %d, G: %d, B: %d\n", red, green, blue)); + } else if (info->depth == 24) { + if (!gdGetByte(&blue, infile) || !gdGetByte(&green, infile) || + !gdGetByte(&red, infile)) { + return 1; + } + alpha = gdAlphaOpaque; + } else { + if (!gdGetIntLSB(&data32, infile)) { + return 1; + } + red = bmp_extract_mask((unsigned int)data32, info->red_mask); + green = bmp_extract_mask((unsigned int)data32, info->green_mask); + blue = bmp_extract_mask((unsigned int)data32, info->blue_mask); + alpha = + info->alpha_mask + ? bmp_alpha_to_gd(bmp_extract_mask((unsigned int)data32, info->alpha_mask)) + : gdAlphaOpaque; + } + gdImageSetPixel(im, xpos, row, gdTrueColorAlpha(red, green, blue, alpha)); + } + for (xpos = padding; xpos > 0; --xpos) { + if (!gdGetByte(&red, infile)) { + return 1; + } + } + } + + return 0; } static int bmp_read_palette(gdImagePtr im, gdIOCtxPtr infile, int count, int read_four) { - int i; - int r, g, b, z; - - for (i = 0; i < count; i++) { - if ( - !gdGetByte(&b, infile) || - !gdGetByte(&g, infile) || - !gdGetByte(&r, infile) || - (read_four && !gdGetByte(&z, infile)) - ) { - return 1; - } - im->red[i] = r; - im->green[i] = g; - im->blue[i] = b; - im->open[i] = 1; - } - return 0; + int i; + int r, g, b, z; + + for (i = 0; i < count; i++) { + if (!gdGetByte(&b, infile) || !gdGetByte(&g, infile) || !gdGetByte(&r, infile) || + (read_four && !gdGetByte(&z, infile))) { + return 1; + } + im->red[i] = r; + im->green[i] = g; + im->blue[i] = b; + im->open[i] = 1; + } + return 0; +} + +static int bmp_check_palette_index(gdImagePtr im, int index) +{ + return index >= 0 && index < im->colorsTotal; +} + +static int bmp_row_padding(int width, int depth, int *padding) +{ + int bits_per_row; + int bytes_per_row; + + if (overflow2(width, depth)) { + return 1; + } + bits_per_row = width * depth; + if (bits_per_row > INT_MAX - 31) { + return 1; + } + bytes_per_row = ((bits_per_row + 31) / 32) * 4; + *padding = bytes_per_row - ((bits_per_row + 7) / 8); + return 0; +} + +static int bmp_image_size(int width, int height, int depth, int *size) +{ + int bits_per_row; + int bytes_per_row; + + if (overflow2(width, depth)) { + return 1; + } + bits_per_row = width * depth; + if (bits_per_row > INT_MAX - 31) { + return 1; + } + bytes_per_row = ((bits_per_row + 31) / 32) * 4; + if (overflow2(bytes_per_row, height)) { + return 1; + } + *size = bytes_per_row * height; + return 0; +} + +static int bmp_get_mask_shift(unsigned int mask) +{ + int shift = 0; + + if (mask == 0) { + return 0; + } + while ((mask & 1U) == 0) { + mask >>= 1; + shift++; + } + return shift; +} + +static int bmp_get_mask_bits(unsigned int mask) +{ + int bits = 0; + + if (mask == 0) { + return 0; + } + mask >>= bmp_get_mask_shift(mask); + while (mask & 1U) { + bits++; + mask >>= 1; + } + return bits; +} + +static int bmp_extract_mask(unsigned int pixel, unsigned int mask) +{ + unsigned int value; + int bits; + + if (mask == 0) { + return 0; + } + value = (pixel & mask) >> bmp_get_mask_shift(mask); + bits = bmp_get_mask_bits(mask); + if (bits <= 0) { + return 0; + } + if (bits >= 32) { + return (int)(value >> 24); + } + if (bits >= 8) { + return (int)((value * 255U) / ((1U << bits) - 1U)); + } + return (int)((value * 255U + ((1U << bits) - 1U) / 2U) / ((1U << bits) - 1U)); +} + +static unsigned int bmp_pack_mask(int channel_8bit, unsigned int mask) +{ + int bits; + int shift; + unsigned int max_val; + unsigned int value; + + if (mask == 0) { + return 0; + } + bits = bmp_get_mask_bits(mask); + shift = bmp_get_mask_shift(mask); + if (bits <= 0) { + return 0; + } + if (bits >= 32) { + value = (unsigned int)channel_8bit << 24; + } else { + max_val = (1U << bits) - 1U; + value = ((unsigned int)channel_8bit * max_val + 127U) / 255U; + } + return (value << shift) & mask; +} + +static int bmp_alpha_to_gd(int alpha) +{ + if (alpha <= 0) { + return gdAlphaTransparent; + } + if (alpha >= 255) { + return gdAlphaOpaque; + } + return gdAlphaMax - (alpha * gdAlphaMax + 127) / 255; +} + +static int bmp_gd_to_alpha(int gd_alpha) +{ + if (gd_alpha <= gdAlphaOpaque) { + return 255; + } + if (gd_alpha >= gdAlphaTransparent) { + return 0; + } + return (gdAlphaMax - gd_alpha) * 255 / gdAlphaMax; } static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header) { - int ypos = 0, xpos = 0, row = 0, index = 0; - int padding = 0, current_byte = 0, bit = 0; - - if (info->enctype != BMP_BI_RGB) { - return 1; - } - - if (!info->numcolors) { - info->numcolors = 2; - } else if (info->numcolors < 0 || info->numcolors > 2) { - return 1; - } - - if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { - return 1; - } - - im->colorsTotal = info->numcolors; - - /* There is a chance the data isn't until later, would be weird but it is possible */ - if (gdTell(infile) != header->off) { - /* Should make sure we don't seek past the file size */ - if (!gdSeek(infile, header->off)) { - return 1; - } - } - - /* The line must be aligned on a 32 bits word, else it is padded with zeros */ - padding = (info->width + 7) / 8 % 4; - if (padding) { - padding = 4 - padding; - } - - for (ypos = 0; ypos < info->height; ++ypos) { - if (info->topdown) { - row = ypos; - } else { - row = info->height - ypos - 1; - } - - for (xpos = 0; xpos < info->width; xpos += 8) { - /* Bitmaps are always aligned in bytes so we'll never overflow */ - if (!gdGetByte(¤t_byte, infile)) { - return 1; - } - - for (bit = 0; bit < 8; bit++) { - index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00); - if (im->open[index]) { - im->open[index] = 0; - } - gdImageSetPixel(im, xpos + bit, row, index); - /* No need to read anything extra */ - if ((xpos + bit) >= info->width) { - break; - } - } - } - - for (xpos = padding; xpos > 0; --xpos) { - if (!gdGetByte(&index, infile)) { - return 1; - } - } - } - return 0; + int ypos = 0, xpos = 0, row = 0, index = 0; + int padding = 0, current_byte = 0, bit = 0; + + if (info->enctype != BMP_BI_RGB) { + return 1; + } + + if (!info->numcolors) { + info->numcolors = 2; + } else if (info->numcolors < 0 || info->numcolors > 2) { + return 1; + } + + if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { + return 1; + } + + im->colorsTotal = info->numcolors; + + /* There is a chance the data isn't until later, would be weird but it is + * possible */ + if (gdTell(infile) != header->off) { + /* Should make sure we don't seek past the file size */ + if (!gdSeek(infile, header->off)) { + return 1; + } + } + + if (bmp_row_padding(info->width, info->depth, &padding)) { + return 1; + } + + for (ypos = 0; ypos < info->height; ++ypos) { + if (info->topdown) { + row = ypos; + } else { + row = info->height - ypos - 1; + } + + for (xpos = 0; xpos < info->width; xpos += 8) { + /* Bitmaps are always aligned in bytes so we'll never overflow */ + if (!gdGetByte(¤t_byte, infile)) { + return 1; + } + + for (bit = 0; bit < 8; bit++) { + index = ((current_byte & (0x80 >> bit)) != 0 ? 0x01 : 0x00); + /* No need to read anything extra */ + if ((xpos + bit) >= info->width) { + break; + } + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos + bit, row, index); + } + } + + for (xpos = padding; xpos > 0; --xpos) { + if (!gdGetByte(&index, infile)) { + return 1; + } + } + } + return 0; +} + +static int bmp_read_2bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header) +{ + int ypos = 0, xpos = 0, row = 0, index = 0; + int padding = 0, current_byte = 0, shift = 0; + + if (info->enctype != BMP_BI_RGB) { + return 1; + } + + if (!info->numcolors) { + info->numcolors = 4; + } else if (info->numcolors < 0 || info->numcolors > 4) { + return 1; + } + + if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { + return 1; + } + + im->colorsTotal = info->numcolors; + + if (gdTell(infile) != header->off) { + if (!gdSeek(infile, header->off)) { + return 1; + } + } + + if (bmp_row_padding(info->width, info->depth, &padding)) { + return 1; + } + + for (ypos = 0; ypos < info->height; ++ypos) { + if (info->topdown) { + row = ypos; + } else { + row = info->height - ypos - 1; + } + + for (xpos = 0; xpos < info->width; xpos += 4) { + if (!gdGetByte(¤t_byte, infile)) { + return 1; + } + + for (shift = 6; shift >= 0; shift -= 2) { + if (xpos + ((6 - shift) / 2) >= info->width) { + break; + } + index = (current_byte >> shift) & 0x03; + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos + ((6 - shift) / 2), row, index); + } + } + + for (xpos = padding; xpos > 0; --xpos) { + if (!gdGetByte(&index, infile)) { + return 1; + } + } + } + return 0; } static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header) { - int ypos = 0, xpos = 0, row = 0, index = 0; - int padding = 0, current_byte = 0; - - if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4) { - return 1; - } - - if (!info->numcolors) { - info->numcolors = 16; - } else if (info->numcolors < 0 || info->numcolors > 16) { - return 1; - } - - if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { - return 1; - } - - im->colorsTotal = info->numcolors; - - /* There is a chance the data isn't until later, would be weird but it is possible */ - if (gdTell(infile) != header->off) { - /* Should make sure we don't seek past the file size */ - if (!gdSeek(infile, header->off)) { - return 1; - } - } - - /* The line must be divisible by 4, else it's padded with NULLs */ - padding = ((int)ceil(0.5 * info->width)) % 4; - if (padding) { - padding = 4 - padding; - } - - switch (info->enctype) { - case BMP_BI_RGB: - for (ypos = 0; ypos < info->height; ++ypos) { - if (info->topdown) { - row = ypos; - } else { - row = info->height - ypos - 1; - } - - for (xpos = 0; xpos < info->width; xpos += 2) { - if (!gdGetByte(¤t_byte, infile)) { - return 1; - } - - index = (current_byte >> 4) & 0x0f; - if (im->open[index]) { - im->open[index] = 0; - } - gdImageSetPixel(im, xpos, row, index); - - /* This condition may get called often, potential optimsations */ - if (xpos >= info->width) { - break; - } - - index = current_byte & 0x0f; - if (im->open[index]) { - im->open[index] = 0; - } - gdImageSetPixel(im, xpos + 1, row, index); - } - - for (xpos = padding; xpos > 0; --xpos) { - if (!gdGetByte(&index, infile)) { - return 1; - } - } - } - break; - - case BMP_BI_RLE4: - if (bmp_read_rle(im, infile, info)) { - return 1; - } - break; - - default: - return 1; - } - return 0; + int ypos = 0, xpos = 0, row = 0, index = 0; + int padding = 0, current_byte = 0; + + if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4) { + return 1; + } + + if (!info->numcolors) { + info->numcolors = 16; + } else if (info->numcolors < 0 || info->numcolors > 16) { + return 1; + } + + if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { + return 1; + } + + im->colorsTotal = info->numcolors; + + /* There is a chance the data isn't until later, would be weird but it is + * possible */ + if (gdTell(infile) != header->off) { + /* Should make sure we don't seek past the file size */ + if (!gdSeek(infile, header->off)) { + return 1; + } + } + + if (bmp_row_padding(info->width, info->depth, &padding)) { + return 1; + } + + switch (info->enctype) { + case BMP_BI_RGB: + for (ypos = 0; ypos < info->height; ++ypos) { + if (info->topdown) { + row = ypos; + } else { + row = info->height - ypos - 1; + } + + for (xpos = 0; xpos < info->width; xpos += 2) { + if (!gdGetByte(¤t_byte, infile)) { + return 1; + } + + index = (current_byte >> 4) & 0x0f; + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos, row, index); + + /* This condition may get called often, potential optimsations + */ + if (xpos + 1 >= info->width) { + break; + } + + index = current_byte & 0x0f; + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos + 1, row, index); + } + + for (xpos = padding; xpos > 0; --xpos) { + if (!gdGetByte(&index, infile)) { + return 1; + } + } + } + break; + + case BMP_BI_RLE4: + if (bmp_read_rle(im, infile, info)) { + return 1; + } + break; + + default: + return 1; + } + return 0; } static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header) { - int ypos = 0, xpos = 0, row = 0, index = 0; - int padding = 0; - - if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) { - return 1; - } - - if (!info->numcolors) { - info->numcolors = 256; - } else if (info->numcolors < 0 || info->numcolors > 256) { - return 1; - } - - if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { - return 1; - } - - im->colorsTotal = info->numcolors; - - /* There is a chance the data isn't until later, would be weird but it is possible */ - if (gdTell(infile) != header->off) { - /* Should make sure we don't seek past the file size */ - if (!gdSeek(infile, header->off)) { - return 1; - } - } - - /* The line must be divisible by 4, else it's padded with NULLs */ - padding = (1 * info->width) % 4; - if (padding) { - padding = 4 - padding; - } - - switch (info->enctype) { - case BMP_BI_RGB: - for (ypos = 0; ypos < info->height; ++ypos) { - if (info->topdown) { - row = ypos; - } else { - row = info->height - ypos - 1; - } - - for (xpos = 0; xpos < info->width; ++xpos) { - if (!gdGetByte(&index, infile)) { - return 1; - } - - if (im->open[index]) { - im->open[index] = 0; - } - gdImageSetPixel(im, xpos, row, index); - } - /* Could create a new variable, but it isn't really worth it */ - for (xpos = padding; xpos > 0; --xpos) { - if (!gdGetByte(&index, infile)) { - return 1; - } - } - } - break; - - case BMP_BI_RLE8: - if (bmp_read_rle(im, infile, info)) { - return 1; - } - break; - - default: - return 1; - } - return 0; + int ypos = 0, xpos = 0, row = 0, index = 0; + int padding = 0; + int palette_entries = 0; + int palette_entry_size = (info->type == BMP_PALETTE_4) ? 4 : 3; + + if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8) { + return 1; + } + + if (!info->numcolors) { + info->numcolors = 256; + } else if (info->numcolors < 0 || info->numcolors > 1024) { + return 1; + } + + palette_entries = (header->off - (14 + info->len)) / palette_entry_size; + if (palette_entries <= 0) { + return 1; + } + if (info->numcolors > palette_entries) { + info->numcolors = palette_entries; + } + if (info->numcolors > gdMaxColors) { + info->numcolors = gdMaxColors; + } + + if (bmp_read_palette(im, infile, info->numcolors, (info->type == BMP_PALETTE_4))) { + return 1; + } + + im->colorsTotal = info->numcolors; + + /* There is a chance the data isn't until later, would be weird but it is + * possible */ + if (gdTell(infile) != header->off) { + /* Should make sure we don't seek past the file size */ + if (!gdSeek(infile, header->off)) { + return 1; + } + } + + if (bmp_row_padding(info->width, info->depth, &padding)) { + return 1; + } + + switch (info->enctype) { + case BMP_BI_RGB: + for (ypos = 0; ypos < info->height; ++ypos) { + if (info->topdown) { + row = ypos; + } else { + row = info->height - ypos - 1; + } + + for (xpos = 0; xpos < info->width; ++xpos) { + if (!gdGetByte(&index, infile)) { + return 1; + } + + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos, row, index); + } + /* Could create a new variable, but it isn't really worth it */ + for (xpos = padding; xpos > 0; --xpos) { + if (!gdGetByte(&index, infile)) { + return 1; + } + } + } + break; + + case BMP_BI_RLE8: + if (bmp_read_rle(im, infile, info)) { + return 1; + } + break; + + default: + return 1; + } + return 0; } static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info) { - int ypos = 0, xpos = 0, row = 0, index = 0; - int rle_length = 0, rle_data = 0; - int padding = 0; - int i = 0, j = 0; - int pixels_per_byte = 8 / info->depth; - - for (ypos = 0; ypos < info->height && xpos <= info->width;) { - if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) { - return 1; - } - row = info->height - ypos - 1; - - if (rle_length != BMP_RLE_COMMAND) { - if (im->open[rle_data]) { - im->open[rle_data] = 0; - } - - for (i = 0; (i < rle_length) && (xpos < info->width);) { - for (j = 1; (j <= pixels_per_byte) && (xpos < info->width) && (i < rle_length); j++, xpos++, i++) { - index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> (8 - (j * info->depth)); - if (im->open[index]) { - im->open[index] = 0; - } - gdImageSetPixel(im, xpos, row, index); - } - } - } else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) { - /* Uncompressed RLE needs to be even */ - padding = 0; - for (i = 0; (i < rle_data) && (xpos < info->width); i += pixels_per_byte) { - int max_pixels = pixels_per_byte; - - if (!gdGetByte(&index, infile)) { - return 1; - } - padding++; - - if (rle_data - i < max_pixels) { - max_pixels = rle_data - i; - } - - for (j = 1; (j <= max_pixels) && (xpos < info->width); j++, xpos++) { - int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1); - if (im->open[temp]) { - im->open[temp] = 0; - } - gdImageSetPixel(im, xpos, row, temp); - } - } - - /* Make sure the bytes read are even */ - if (padding % 2 && !gdGetByte(&index, infile)) { - return 1; - } - } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) { - /* Next Line */ - xpos = 0; - ypos++; - } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA) { - /* Delta Record, used for bmp files that contain other data*/ - if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) { - return 1; - } - xpos += rle_length; - ypos += rle_data; - } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) { - /* End of bitmap */ - break; - } - } - return 0; + int ypos = 0, xpos = 0, row = 0, index = 0; + int rle_length = 0, rle_data = 0; + int padding = 0; + int i = 0, j = 0; + int pixels_per_byte = 8 / info->depth; + + if (info->topdown) { + return 1; + } + + for (ypos = 0; ypos < info->height && xpos <= info->width;) { + if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) { + return 1; + } + row = info->height - ypos - 1; + if (row < 0 || row >= info->height) { + return 1; + } + + if (rle_length != BMP_RLE_COMMAND) { + for (i = 0; i < rle_length;) { + for (j = 1; (j <= pixels_per_byte) && (i < rle_length); j++, xpos++, i++) { + index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> + (8 - (j * info->depth)); + if (xpos >= info->width) { + return 1; + } + if (!bmp_check_palette_index(im, index)) { + return 1; + } + if (im->open[index]) { + im->open[index] = 0; + } + gdImageSetPixel(im, xpos, row, index); + } + } + } else if (rle_length == BMP_RLE_COMMAND && rle_data > 2) { + /* Uncompressed RLE needs to be even */ + padding = 0; + for (i = 0; i < rle_data; i += pixels_per_byte) { + int max_pixels = pixels_per_byte; + + if (!gdGetByte(&index, infile)) { + return 1; + } + padding++; + + if (rle_data - i < max_pixels) { + max_pixels = rle_data - i; + } + + for (j = 1; j <= max_pixels; j++) { + int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1); + if (xpos >= info->width) { + return 1; + } + if (!bmp_check_palette_index(im, temp)) { + return 1; + } + if (im->open[temp]) { + im->open[temp] = 0; + } + gdImageSetPixel(im, xpos, row, temp); + xpos++; + } + } + + /* Make sure the bytes read are even */ + if (padding % 2 && !gdGetByte(&index, infile)) { + return 1; + } + } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE) { + /* Next Line */ + xpos = 0; + ypos++; + } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA) { + /* Delta Record, used for bmp files that contain other data*/ + if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile)) { + return 1; + } + if (xpos + rle_length > info->width || ypos + rle_data >= info->height) { + return 1; + } + xpos += rle_length; + ypos += rle_data; + } else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP) { + /* End of bitmap */ + break; + } + } + return 0; } diff --git a/ext/gd/libgd/gd_color.c b/ext/gd/libgd/gd_color.c new file mode 100644 index 000000000000..88c0cfff3fbf --- /dev/null +++ b/ext/gd/libgd/gd_color.c @@ -0,0 +1,38 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd.h" +#include "gd_color.h" +#define GD_MAX_COLOR_DIST_SQ 260100 + +/** + * The threshold method works relatively well for border cropping purposes. + * A perceptually uniform metric (L*a*b* + Delta-E) would give better + * color discrimination but at significant computational cost per pixel, + * which is hard to justify for this use case. + */ +int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold) +{ + const int dr = gdImageRed(im, col1) - gdImageRed(im, col2); + const int dg = gdImageGreen(im, col1) - gdImageGreen(im, col2); + const int db = gdImageBlue(im, col1) - gdImageBlue(im, col2); + const int da = (gdImageAlpha(im, col1) - gdImageAlpha(im, col2)) * 2; + const int dist = dr * dr + dg * dg + db * db + da * da; + + const float t = threshold / 100.0f; + return dist <= (int)(t * t * GD_MAX_COLOR_DIST_SQ); +} + +/* + * To be implemented when we have more image formats. + * Buffer like gray8 gray16 or rgb8 will require some tweak + * and can be done in this function (called from the autocrop + * function. (Pierre) + */ +#if 0 +static int colors_equal (const int col1, const in col2) +{ + +} +#endif diff --git a/ext/gd/libgd/gd_color.h b/ext/gd/libgd/gd_color.h new file mode 100644 index 000000000000..834f45ec8adc --- /dev/null +++ b/ext/gd/libgd/gd_color.h @@ -0,0 +1,14 @@ +#ifndef GD_COLOR_H +#define GD_COLOR_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/gd/libgd/gd_color_map.c b/ext/gd/libgd/gd_color_map.c new file mode 100644 index 000000000000..b1785e2eafa3 --- /dev/null +++ b/ext/gd/libgd/gd_color_map.c @@ -0,0 +1,793 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gd.h" +#include "gd_color_map.h" + +static const gdColorMapEntry GD_COLOR_MAP_X11_ENTRIES[] = { + {"AliceBlue", 240, 248, 255}, + {"AntiqueWhite", 250, 235, 215}, + {"AntiqueWhite1", 255, 239, 219}, + {"AntiqueWhite2", 238, 223, 204}, + {"AntiqueWhite3", 205, 192, 176}, + {"AntiqueWhite4", 139, 131, 120}, + {"BlanchedAlmond", 255, 235, 205}, + {"BlueViolet", 138, 43, 226}, + {"CadetBlue", 95, 158, 160}, + {"CadetBlue1", 152, 245, 255}, + {"CadetBlue2", 142, 229, 238}, + {"CadetBlue3", 122, 197, 205}, + {"CadetBlue4", 83, 134, 139}, + {"CornflowerBlue", 100, 149, 237}, + {"DarkBlue", 0, 0, 139}, + {"DarkCyan", 0, 139, 139}, + {"DarkGoldenrod", 184, 134, 11}, + {"DarkGoldenrod1", 255, 185, 15}, + {"DarkGoldenrod2", 238, 173, 14}, + {"DarkGoldenrod3", 205, 149, 12}, + {"DarkGoldenrod4", 139, 101, 8}, + {"DarkGray", 169, 169, 169}, + {"DarkGreen", 0, 100, 0}, + {"DarkGrey", 169, 169, 169}, + {"DarkKhaki", 189, 183, 107}, + {"DarkMagenta", 139, 0, 139}, + {"DarkOliveGreen", 85, 107, 47}, + {"DarkOliveGreen1", 202, 255, 112}, + {"DarkOliveGreen2", 188, 238, 104}, + {"DarkOliveGreen3", 162, 205, 90}, + {"DarkOliveGreen4", 110, 139, 61}, + {"DarkOrange", 255, 140, 0}, + {"DarkOrange1", 255, 127, 0}, + {"DarkOrange2", 238, 118, 0}, + {"DarkOrange3", 205, 102, 0}, + {"DarkOrange4", 139, 69, 0}, + {"DarkOrchid", 153, 50, 204}, + {"DarkOrchid1", 191, 62, 255}, + {"DarkOrchid2", 178, 58, 238}, + {"DarkOrchid3", 154, 50, 205}, + {"DarkOrchid4", 104, 34, 139}, + {"DarkRed", 139, 0, 0}, + {"DarkSalmon", 233, 150, 122}, + {"DarkSeaGreen", 143, 188, 143}, + {"DarkSeaGreen1", 193, 255, 193}, + {"DarkSeaGreen2", 180, 238, 180}, + {"DarkSeaGreen3", 155, 205, 155}, + {"DarkSeaGreen4", 105, 139, 105}, + {"DarkSlateBlue", 72, 61, 139}, + {"DarkSlateGray", 47, 79, 79}, + {"DarkSlateGray1", 151, 255, 255}, + {"DarkSlateGray2", 141, 238, 238}, + {"DarkSlateGray3", 121, 205, 205}, + {"DarkSlateGray4", 82, 139, 139}, + {"DarkSlateGrey", 47, 79, 79}, + {"DarkTurquoise", 0, 206, 209}, + {"DarkViolet", 148, 0, 211}, + {"DeepPink", 255, 20, 147}, + {"DeepPink1", 255, 20, 147}, + {"DeepPink2", 238, 18, 137}, + {"DeepPink3", 205, 16, 118}, + {"DeepPink4", 139, 10, 80}, + {"DeepSkyBlue", 0, 191, 255}, + {"DeepSkyBlue1", 0, 191, 255}, + {"DeepSkyBlue2", 0, 178, 238}, + {"DeepSkyBlue3", 0, 154, 205}, + {"DeepSkyBlue4", 0, 104, 139}, + {"DimGray", 105, 105, 105}, + {"DimGrey", 105, 105, 105}, + {"DodgerBlue", 30, 144, 255}, + {"DodgerBlue1", 30, 144, 255}, + {"DodgerBlue2", 28, 134, 238}, + {"DodgerBlue3", 24, 116, 205}, + {"DodgerBlue4", 16, 78, 139}, + {"FloralWhite", 255, 250, 240}, + {"ForestGreen", 34, 139, 34}, + {"GhostWhite", 248, 248, 255}, + {"GreenYellow", 173, 255, 47}, + {"HotPink", 255, 105, 180}, + {"HotPink1", 255, 110, 180}, + {"HotPink2", 238, 106, 167}, + {"HotPink3", 205, 96, 144}, + {"HotPink4", 139, 58, 98}, + {"IndianRed", 205, 92, 92}, + {"IndianRed1", 255, 106, 106}, + {"IndianRed2", 238, 99, 99}, + {"IndianRed3", 205, 85, 85}, + {"IndianRed4", 139, 58, 58}, + {"LavenderBlush", 255, 240, 245}, + {"LavenderBlush1", 255, 240, 245}, + {"LavenderBlush2", 238, 224, 229}, + {"LavenderBlush3", 205, 193, 197}, + {"LavenderBlush4", 139, 131, 134}, + {"LawnGreen", 124, 252, 0}, + {"LemonChiffon", 255, 250, 205}, + {"LemonChiffon1", 255, 250, 205}, + {"LemonChiffon2", 238, 233, 191}, + {"LemonChiffon3", 205, 201, 165}, + {"LemonChiffon4", 139, 137, 112}, + {"LightBlue", 173, 216, 230}, + {"LightBlue1", 191, 239, 255}, + {"LightBlue2", 178, 223, 238}, + {"LightBlue3", 154, 192, 205}, + {"LightBlue4", 104, 131, 139}, + {"LightCoral", 240, 128, 128}, + {"LightCyan", 224, 255, 255}, + {"LightCyan1", 224, 255, 255}, + {"LightCyan2", 209, 238, 238}, + {"LightCyan3", 180, 205, 205}, + {"LightCyan4", 122, 139, 139}, + {"LightGoldenrod", 238, 221, 130}, + {"LightGoldenrod1", 255, 236, 139}, + {"LightGoldenrod2", 238, 220, 130}, + {"LightGoldenrod3", 205, 190, 112}, + {"LightGoldenrod4", 139, 129, 76}, + {"LightGoldenrodYellow", 250, 250, 210}, + {"LightGray", 211, 211, 211}, + {"LightGreen", 144, 238, 144}, + {"LightGrey", 211, 211, 211}, + {"LightPink", 255, 182, 193}, + {"LightPink1", 255, 174, 185}, + {"LightPink2", 238, 162, 173}, + {"LightPink3", 205, 140, 149}, + {"LightPink4", 139, 95, 101}, + {"LightSalmon", 255, 160, 122}, + {"LightSalmon1", 255, 160, 122}, + {"LightSalmon2", 238, 149, 114}, + {"LightSalmon3", 205, 129, 98}, + {"LightSalmon4", 139, 87, 66}, + {"LightSeaGreen", 32, 178, 170}, + {"LightSkyBlue", 135, 206, 250}, + {"LightSkyBlue1", 176, 226, 255}, + {"LightSkyBlue2", 164, 211, 238}, + {"LightSkyBlue3", 141, 182, 205}, + {"LightSkyBlue4", 96, 123, 139}, + {"LightSlateBlue", 132, 112, 255}, + {"LightSlateGray", 119, 136, 153}, + {"LightSlateGrey", 119, 136, 153}, + {"LightSteelBlue", 176, 196, 222}, + {"LightSteelBlue1", 202, 225, 255}, + {"LightSteelBlue2", 188, 210, 238}, + {"LightSteelBlue3", 162, 181, 205}, + {"LightSteelBlue4", 110, 123, 139}, + {"LightYellow", 255, 255, 224}, + {"LightYellow1", 255, 255, 224}, + {"LightYellow2", 238, 238, 209}, + {"LightYellow3", 205, 205, 180}, + {"LightYellow4", 139, 139, 122}, + {"LimeGreen", 50, 205, 50}, + {"MediumAquamarine", 102, 205, 170}, + {"MediumBlue", 0, 0, 205}, + {"MediumOrchid", 186, 85, 211}, + {"MediumOrchid1", 224, 102, 255}, + {"MediumOrchid2", 209, 95, 238}, + {"MediumOrchid3", 180, 82, 205}, + {"MediumOrchid4", 122, 55, 139}, + {"MediumPurple", 147, 112, 219}, + {"MediumPurple1", 171, 130, 255}, + {"MediumPurple2", 159, 121, 238}, + {"MediumPurple3", 137, 104, 205}, + {"MediumPurple4", 93, 71, 139}, + {"MediumSeaGreen", 60, 179, 113}, + {"MediumSlateBlue", 123, 104, 238}, + {"MediumSpringGreen", 0, 250, 154}, + {"MediumTurquoise", 72, 209, 204}, + {"MediumVioletRed", 199, 21, 133}, + {"MidnightBlue", 25, 25, 112}, + {"MintCream", 245, 255, 250}, + {"MistyRose", 255, 228, 225}, + {"MistyRose1", 255, 228, 225}, + {"MistyRose2", 238, 213, 210}, + {"MistyRose3", 205, 183, 181}, + {"MistyRose4", 139, 125, 123}, + {"NavajoWhite", 255, 222, 173}, + {"NavajoWhite1", 255, 222, 173}, + {"NavajoWhite2", 238, 207, 161}, + {"NavajoWhite3", 205, 179, 139}, + {"NavajoWhite4", 139, 121, 94}, + {"NavyBlue", 0, 0, 128}, + {"OldLace", 253, 245, 230}, + {"OliveDrab", 107, 142, 35}, + {"OliveDrab1", 192, 255, 62}, + {"OliveDrab2", 179, 238, 58}, + {"OliveDrab3", 154, 205, 50}, + {"OliveDrab4", 105, 139, 34}, + {"OrangeRed", 255, 69, 0}, + {"OrangeRed1", 255, 69, 0}, + {"OrangeRed2", 238, 64, 0}, + {"OrangeRed3", 205, 55, 0}, + {"OrangeRed4", 139, 37, 0}, + {"PaleGoldenrod", 238, 232, 170}, + {"PaleGreen", 152, 251, 152}, + {"PaleGreen1", 154, 255, 154}, + {"PaleGreen2", 144, 238, 144}, + {"PaleGreen3", 124, 205, 124}, + {"PaleGreen4", 84, 139, 84}, + {"PaleTurquoise", 175, 238, 238}, + {"PaleTurquoise1", 187, 255, 255}, + {"PaleTurquoise2", 174, 238, 238}, + {"PaleTurquoise3", 150, 205, 205}, + {"PaleTurquoise4", 102, 139, 139}, + {"PaleVioletRed", 219, 112, 147}, + {"PaleVioletRed1", 255, 130, 171}, + {"PaleVioletRed2", 238, 121, 159}, + {"PaleVioletRed3", 205, 104, 137}, + {"PaleVioletRed4", 139, 71, 93}, + {"PapayaWhip", 255, 239, 213}, + {"PeachPuff", 255, 218, 185}, + {"PeachPuff1", 255, 218, 185}, + {"PeachPuff2", 238, 203, 173}, + {"PeachPuff3", 205, 175, 149}, + {"PeachPuff4", 139, 119, 101}, + {"PowderBlue", 176, 224, 230}, + {"RosyBrown", 188, 143, 143}, + {"RosyBrown1", 255, 193, 193}, + {"RosyBrown2", 238, 180, 180}, + {"RosyBrown3", 205, 155, 155}, + {"RosyBrown4", 139, 105, 105}, + {"RoyalBlue", 65, 105, 225}, + {"RoyalBlue1", 72, 118, 255}, + {"RoyalBlue2", 67, 110, 238}, + {"RoyalBlue3", 58, 95, 205}, + {"RoyalBlue4", 39, 64, 139}, + {"SaddleBrown", 139, 69, 19}, + {"SandyBrown", 244, 164, 96}, + {"SeaGreen", 46, 139, 87}, + {"SeaGreen1", 84, 255, 159}, + {"SeaGreen2", 78, 238, 148}, + {"SeaGreen3", 67, 205, 128}, + {"SeaGreen4", 46, 139, 87}, + {"SkyBlue", 135, 206, 235}, + {"SkyBlue1", 135, 206, 255}, + {"SkyBlue2", 126, 192, 238}, + {"SkyBlue3", 108, 166, 205}, + {"SkyBlue4", 74, 112, 139}, + {"SlateBlue", 106, 90, 205}, + {"SlateBlue1", 131, 111, 255}, + {"SlateBlue2", 122, 103, 238}, + {"SlateBlue3", 105, 89, 205}, + {"SlateBlue4", 71, 60, 139}, + {"SlateGray", 112, 128, 144}, + {"SlateGray1", 198, 226, 255}, + {"SlateGray2", 185, 211, 238}, + {"SlateGray3", 159, 182, 205}, + {"SlateGray4", 108, 123, 139}, + {"SlateGrey", 112, 128, 144}, + {"SpringGreen", 0, 255, 127}, + {"SpringGreen1", 0, 255, 127}, + {"SpringGreen2", 0, 238, 118}, + {"SpringGreen3", 0, 205, 102}, + {"SpringGreen4", 0, 139, 69}, + {"SteelBlue", 70, 130, 180}, + {"SteelBlue1", 99, 184, 255}, + {"SteelBlue2", 92, 172, 238}, + {"SteelBlue3", 79, 148, 205}, + {"SteelBlue4", 54, 100, 139}, + {"VioletRed", 208, 32, 144}, + {"VioletRed1", 255, 62, 150}, + {"VioletRed2", 238, 58, 140}, + {"VioletRed3", 205, 50, 120}, + {"VioletRed4", 139, 34, 82}, + {"WhiteSmoke", 245, 245, 245}, + {"YellowGreen", 154, 205, 50}, + {"alice blue", 240, 248, 255}, + {"antique white", 250, 235, 215}, + {"aquamarine", 127, 255, 212}, + {"aquamarine1", 127, 255, 212}, + {"aquamarine2", 118, 238, 198}, + {"aquamarine3", 102, 205, 170}, + {"aquamarine4", 69, 139, 116}, + {"azure", 240, 255, 255}, + {"azure1", 240, 255, 255}, + {"azure2", 224, 238, 238}, + {"azure3", 193, 205, 205}, + {"azure4", 131, 139, 139}, + {"beige", 245, 245, 220}, + {"bisque", 255, 228, 196}, + {"bisque1", 255, 228, 196}, + {"bisque2", 238, 213, 183}, + {"bisque3", 205, 183, 158}, + {"bisque4", 139, 125, 107}, + {"black", 0, 0, 0}, + {"blanched almond", 255, 235, 205}, + {"blue", 0, 0, 255}, + {"blue violet", 138, 43, 226}, + {"blue1", 0, 0, 255}, + {"blue2", 0, 0, 238}, + {"blue3", 0, 0, 205}, + {"blue4", 0, 0, 139}, + {"brown", 165, 42, 42}, + {"brown1", 255, 64, 64}, + {"brown2", 238, 59, 59}, + {"brown3", 205, 51, 51}, + {"brown4", 139, 35, 35}, + {"burlywood", 222, 184, 135}, + {"burlywood1", 255, 211, 155}, + {"burlywood2", 238, 197, 145}, + {"burlywood3", 205, 170, 125}, + {"burlywood4", 139, 115, 85}, + {"cadet blue", 95, 158, 160}, + {"chartreuse", 127, 255, 0}, + {"chartreuse1", 127, 255, 0}, + {"chartreuse2", 118, 238, 0}, + {"chartreuse3", 102, 205, 0}, + {"chartreuse4", 69, 139, 0}, + {"chocolate", 210, 105, 30}, + {"chocolate1", 255, 127, 36}, + {"chocolate2", 238, 118, 33}, + {"chocolate3", 205, 102, 29}, + {"chocolate4", 139, 69, 19}, + {"coral", 255, 127, 80}, + {"coral1", 255, 114, 86}, + {"coral2", 238, 106, 80}, + {"coral3", 205, 91, 69}, + {"coral4", 139, 62, 47}, + {"cornflower blue", 100, 149, 237}, + {"cornsilk", 255, 248, 220}, + {"cornsilk1", 255, 248, 220}, + {"cornsilk2", 238, 232, 205}, + {"cornsilk3", 205, 200, 177}, + {"cornsilk4", 139, 136, 120}, + {"cyan", 0, 255, 255}, + {"cyan1", 0, 255, 255}, + {"cyan2", 0, 238, 238}, + {"cyan3", 0, 205, 205}, + {"cyan4", 0, 139, 139}, + {"dark blue", 0, 0, 139}, + {"dark cyan", 0, 139, 139}, + {"dark goldenrod", 184, 134, 11}, + {"dark gray", 169, 169, 169}, + {"dark green", 0, 100, 0}, + {"dark grey", 169, 169, 169}, + {"dark khaki", 189, 183, 107}, + {"dark magenta", 139, 0, 139}, + {"dark olive green", 85, 107, 47}, + {"dark orange", 255, 140, 0}, + {"dark orchid", 153, 50, 204}, + {"dark red", 139, 0, 0}, + {"dark salmon", 233, 150, 122}, + {"dark sea green", 143, 188, 143}, + {"dark slate blue", 72, 61, 139}, + {"dark slate gray", 47, 79, 79}, + {"dark slate grey", 47, 79, 79}, + {"dark turquoise", 0, 206, 209}, + {"dark violet", 148, 0, 211}, + {"deep pink", 255, 20, 147}, + {"deep sky blue", 0, 191, 255}, + {"dim gray", 105, 105, 105}, + {"dim grey", 105, 105, 105}, + {"dodger blue", 30, 144, 255}, + {"firebrick", 178, 34, 34}, + {"firebrick1", 255, 48, 48}, + {"firebrick2", 238, 44, 44}, + {"firebrick3", 205, 38, 38}, + {"firebrick4", 139, 26, 26}, + {"floral white", 255, 250, 240}, + {"forest green", 34, 139, 34}, + {"gainsboro", 220, 220, 220}, + {"ghost white", 248, 248, 255}, + {"gold", 255, 215, 0}, + {"gold1", 255, 215, 0}, + {"gold2", 238, 201, 0}, + {"gold3", 205, 173, 0}, + {"gold4", 139, 117, 0}, + {"goldenrod", 218, 165, 32}, + {"goldenrod1", 255, 193, 37}, + {"goldenrod2", 238, 180, 34}, + {"goldenrod3", 205, 155, 29}, + {"goldenrod4", 139, 105, 20}, + {"gray", 190, 190, 190}, + {"gray0", 0, 0, 0}, + {"gray1", 3, 3, 3}, + {"gray10", 26, 26, 26}, + {"gray100", 255, 255, 255}, + {"gray11", 28, 28, 28}, + {"gray12", 31, 31, 31}, + {"gray13", 33, 33, 33}, + {"gray14", 36, 36, 36}, + {"gray15", 38, 38, 38}, + {"gray16", 41, 41, 41}, + {"gray17", 43, 43, 43}, + {"gray18", 46, 46, 46}, + {"gray19", 48, 48, 48}, + {"gray2", 5, 5, 5}, + {"gray20", 51, 51, 51}, + {"gray21", 54, 54, 54}, + {"gray22", 56, 56, 56}, + {"gray23", 59, 59, 59}, + {"gray24", 61, 61, 61}, + {"gray25", 64, 64, 64}, + {"gray26", 66, 66, 66}, + {"gray27", 69, 69, 69}, + {"gray28", 71, 71, 71}, + {"gray29", 74, 74, 74}, + {"gray3", 8, 8, 8}, + {"gray30", 77, 77, 77}, + {"gray31", 79, 79, 79}, + {"gray32", 82, 82, 82}, + {"gray33", 84, 84, 84}, + {"gray34", 87, 87, 87}, + {"gray35", 89, 89, 89}, + {"gray36", 92, 92, 92}, + {"gray37", 94, 94, 94}, + {"gray38", 97, 97, 97}, + {"gray39", 99, 99, 99}, + {"gray4", 10, 10, 10}, + {"gray40", 102, 102, 102}, + {"gray41", 105, 105, 105}, + {"gray42", 107, 107, 107}, + {"gray43", 110, 110, 110}, + {"gray44", 112, 112, 112}, + {"gray45", 115, 115, 115}, + {"gray46", 117, 117, 117}, + {"gray47", 120, 120, 120}, + {"gray48", 122, 122, 122}, + {"gray49", 125, 125, 125}, + {"gray5", 13, 13, 13}, + {"gray50", 127, 127, 127}, + {"gray51", 130, 130, 130}, + {"gray52", 133, 133, 133}, + {"gray53", 135, 135, 135}, + {"gray54", 138, 138, 138}, + {"gray55", 140, 140, 140}, + {"gray56", 143, 143, 143}, + {"gray57", 145, 145, 145}, + {"gray58", 148, 148, 148}, + {"gray59", 150, 150, 150}, + {"gray6", 15, 15, 15}, + {"gray60", 153, 153, 153}, + {"gray61", 156, 156, 156}, + {"gray62", 158, 158, 158}, + {"gray63", 161, 161, 161}, + {"gray64", 163, 163, 163}, + {"gray65", 166, 166, 166}, + {"gray66", 168, 168, 168}, + {"gray67", 171, 171, 171}, + {"gray68", 173, 173, 173}, + {"gray69", 176, 176, 176}, + {"gray7", 18, 18, 18}, + {"gray70", 179, 179, 179}, + {"gray71", 181, 181, 181}, + {"gray72", 184, 184, 184}, + {"gray73", 186, 186, 186}, + {"gray74", 189, 189, 189}, + {"gray75", 191, 191, 191}, + {"gray76", 194, 194, 194}, + {"gray77", 196, 196, 196}, + {"gray78", 199, 199, 199}, + {"gray79", 201, 201, 201}, + {"gray8", 20, 20, 20}, + {"gray80", 204, 204, 204}, + {"gray81", 207, 207, 207}, + {"gray82", 209, 209, 209}, + {"gray83", 212, 212, 212}, + {"gray84", 214, 214, 214}, + {"gray85", 217, 217, 217}, + {"gray86", 219, 219, 219}, + {"gray87", 222, 222, 222}, + {"gray88", 224, 224, 224}, + {"gray89", 227, 227, 227}, + {"gray9", 23, 23, 23}, + {"gray90", 229, 229, 229}, + {"gray91", 232, 232, 232}, + {"gray92", 235, 235, 235}, + {"gray93", 237, 237, 237}, + {"gray94", 240, 240, 240}, + {"gray95", 242, 242, 242}, + {"gray96", 245, 245, 245}, + {"gray97", 247, 247, 247}, + {"gray98", 250, 250, 250}, + {"gray99", 252, 252, 252}, + {"green", 0, 255, 0}, + {"green yellow", 173, 255, 47}, + {"green1", 0, 255, 0}, + {"green2", 0, 238, 0}, + {"green3", 0, 205, 0}, + {"green4", 0, 139, 0}, + {"grey", 190, 190, 190}, + {"grey0", 0, 0, 0}, + {"grey1", 3, 3, 3}, + {"grey10", 26, 26, 26}, + {"grey100", 255, 255, 255}, + {"grey11", 28, 28, 28}, + {"grey12", 31, 31, 31}, + {"grey13", 33, 33, 33}, + {"grey14", 36, 36, 36}, + {"grey15", 38, 38, 38}, + {"grey16", 41, 41, 41}, + {"grey17", 43, 43, 43}, + {"grey18", 46, 46, 46}, + {"grey19", 48, 48, 48}, + {"grey2", 5, 5, 5}, + {"grey20", 51, 51, 51}, + {"grey21", 54, 54, 54}, + {"grey22", 56, 56, 56}, + {"grey23", 59, 59, 59}, + {"grey24", 61, 61, 61}, + {"grey25", 64, 64, 64}, + {"grey26", 66, 66, 66}, + {"grey27", 69, 69, 69}, + {"grey28", 71, 71, 71}, + {"grey29", 74, 74, 74}, + {"grey3", 8, 8, 8}, + {"grey30", 77, 77, 77}, + {"grey31", 79, 79, 79}, + {"grey32", 82, 82, 82}, + {"grey33", 84, 84, 84}, + {"grey34", 87, 87, 87}, + {"grey35", 89, 89, 89}, + {"grey36", 92, 92, 92}, + {"grey37", 94, 94, 94}, + {"grey38", 97, 97, 97}, + {"grey39", 99, 99, 99}, + {"grey4", 10, 10, 10}, + {"grey40", 102, 102, 102}, + {"grey41", 105, 105, 105}, + {"grey42", 107, 107, 107}, + {"grey43", 110, 110, 110}, + {"grey44", 112, 112, 112}, + {"grey45", 115, 115, 115}, + {"grey46", 117, 117, 117}, + {"grey47", 120, 120, 120}, + {"grey48", 122, 122, 122}, + {"grey49", 125, 125, 125}, + {"grey5", 13, 13, 13}, + {"grey50", 127, 127, 127}, + {"grey51", 130, 130, 130}, + {"grey52", 133, 133, 133}, + {"grey53", 135, 135, 135}, + {"grey54", 138, 138, 138}, + {"grey55", 140, 140, 140}, + {"grey56", 143, 143, 143}, + {"grey57", 145, 145, 145}, + {"grey58", 148, 148, 148}, + {"grey59", 150, 150, 150}, + {"grey6", 15, 15, 15}, + {"grey60", 153, 153, 153}, + {"grey61", 156, 156, 156}, + {"grey62", 158, 158, 158}, + {"grey63", 161, 161, 161}, + {"grey64", 163, 163, 163}, + {"grey65", 166, 166, 166}, + {"grey66", 168, 168, 168}, + {"grey67", 171, 171, 171}, + {"grey68", 173, 173, 173}, + {"grey69", 176, 176, 176}, + {"grey7", 18, 18, 18}, + {"grey70", 179, 179, 179}, + {"grey71", 181, 181, 181}, + {"grey72", 184, 184, 184}, + {"grey73", 186, 186, 186}, + {"grey74", 189, 189, 189}, + {"grey75", 191, 191, 191}, + {"grey76", 194, 194, 194}, + {"grey77", 196, 196, 196}, + {"grey78", 199, 199, 199}, + {"grey79", 201, 201, 201}, + {"grey8", 20, 20, 20}, + {"grey80", 204, 204, 204}, + {"grey81", 207, 207, 207}, + {"grey82", 209, 209, 209}, + {"grey83", 212, 212, 212}, + {"grey84", 214, 214, 214}, + {"grey85", 217, 217, 217}, + {"grey86", 219, 219, 219}, + {"grey87", 222, 222, 222}, + {"grey88", 224, 224, 224}, + {"grey89", 227, 227, 227}, + {"grey9", 23, 23, 23}, + {"grey90", 229, 229, 229}, + {"grey91", 232, 232, 232}, + {"grey92", 235, 235, 235}, + {"grey93", 237, 237, 237}, + {"grey94", 240, 240, 240}, + {"grey95", 242, 242, 242}, + {"grey96", 245, 245, 245}, + {"grey97", 247, 247, 247}, + {"grey98", 250, 250, 250}, + {"grey99", 252, 252, 252}, + {"honeydew", 240, 255, 240}, + {"honeydew1", 240, 255, 240}, + {"honeydew2", 224, 238, 224}, + {"honeydew3", 193, 205, 193}, + {"honeydew4", 131, 139, 131}, + {"hot pink", 255, 105, 180}, + {"indian red", 205, 92, 92}, + {"ivory", 255, 255, 240}, + {"ivory1", 255, 255, 240}, + {"ivory2", 238, 238, 224}, + {"ivory3", 205, 205, 193}, + {"ivory4", 139, 139, 131}, + {"khaki", 240, 230, 140}, + {"khaki1", 255, 246, 143}, + {"khaki2", 238, 230, 133}, + {"khaki3", 205, 198, 115}, + {"khaki4", 139, 134, 78}, + {"lavender", 230, 230, 250}, + {"lavender blush", 255, 240, 245}, + {"lawn green", 124, 252, 0}, + {"lemon chiffon", 255, 250, 205}, + {"light blue", 173, 216, 230}, + {"light coral", 240, 128, 128}, + {"light cyan", 224, 255, 255}, + {"light goldenrod", 238, 221, 130}, + {"light goldenrod yellow", 250, 250, 210}, + {"light gray", 211, 211, 211}, + {"light green", 144, 238, 144}, + {"light grey", 211, 211, 211}, + {"light pink", 255, 182, 193}, + {"light salmon", 255, 160, 122}, + {"light sea green", 32, 178, 170}, + {"light sky blue", 135, 206, 250}, + {"light slate blue", 132, 112, 255}, + {"light slate gray", 119, 136, 153}, + {"light slate grey", 119, 136, 153}, + {"light steel blue", 176, 196, 222}, + {"light yellow", 255, 255, 224}, + {"lime green", 50, 205, 50}, + {"linen", 250, 240, 230}, + {"magenta", 255, 0, 255}, + {"magenta1", 255, 0, 255}, + {"magenta2", 238, 0, 238}, + {"magenta3", 205, 0, 205}, + {"magenta4", 139, 0, 139}, + {"maroon", 176, 48, 96}, + {"maroon1", 255, 52, 179}, + {"maroon2", 238, 48, 167}, + {"maroon3", 205, 41, 144}, + {"maroon4", 139, 28, 98}, + {"medium aquamarine", 102, 205, 170}, + {"medium blue", 0, 0, 205}, + {"medium orchid", 186, 85, 211}, + {"medium purple", 147, 112, 219}, + {"medium sea green", 60, 179, 113}, + {"medium slate blue", 123, 104, 238}, + {"medium spring green", 0, 250, 154}, + {"medium turquoise", 72, 209, 204}, + {"medium violet red", 199, 21, 133}, + {"midnight blue", 25, 25, 112}, + {"mint cream", 245, 255, 250}, + {"misty rose", 255, 228, 225}, + {"moccasin", 255, 228, 181}, + {"navajo white", 255, 222, 173}, + {"navy", 0, 0, 128}, + {"navy blue", 0, 0, 128}, + {"old lace", 253, 245, 230}, + {"olive drab", 107, 142, 35}, + {"orange", 255, 165, 0}, + {"orange red", 255, 69, 0}, + {"orange1", 255, 165, 0}, + {"orange2", 238, 154, 0}, + {"orange3", 205, 133, 0}, + {"orange4", 139, 90, 0}, + {"orchid", 218, 112, 214}, + {"orchid1", 255, 131, 250}, + {"orchid2", 238, 122, 233}, + {"orchid3", 205, 105, 201}, + {"orchid4", 139, 71, 137}, + {"pale goldenrod", 238, 232, 170}, + {"pale green", 152, 251, 152}, + {"pale turquoise", 175, 238, 238}, + {"pale violet red", 219, 112, 147}, + {"papaya whip", 255, 239, 213}, + {"peach puff", 255, 218, 185}, + {"peru", 205, 133, 63}, + {"pink", 255, 192, 203}, + {"pink1", 255, 181, 197}, + {"pink2", 238, 169, 184}, + {"pink3", 205, 145, 158}, + {"pink4", 139, 99, 108}, + {"plum", 221, 160, 221}, + {"plum1", 255, 187, 255}, + {"plum2", 238, 174, 238}, + {"plum3", 205, 150, 205}, + {"plum4", 139, 102, 139}, + {"powder blue", 176, 224, 230}, + {"purple", 160, 32, 240}, + {"purple1", 155, 48, 255}, + {"purple2", 145, 44, 238}, + {"purple3", 125, 38, 205}, + {"purple4", 85, 26, 139}, + {"red", 255, 0, 0}, + {"red1", 255, 0, 0}, + {"red2", 238, 0, 0}, + {"red3", 205, 0, 0}, + {"red4", 139, 0, 0}, + {"rosy brown", 188, 143, 143}, + {"royal blue", 65, 105, 225}, + {"saddle brown", 139, 69, 19}, + {"salmon", 250, 128, 114}, + {"salmon1", 255, 140, 105}, + {"salmon2", 238, 130, 98}, + {"salmon3", 205, 112, 84}, + {"salmon4", 139, 76, 57}, + {"sandy brown", 244, 164, 96}, + {"sea green", 46, 139, 87}, + {"seashell", 255, 245, 238}, + {"seashell1", 255, 245, 238}, + {"seashell2", 238, 229, 222}, + {"seashell3", 205, 197, 191}, + {"seashell4", 139, 134, 130}, + {"sienna", 160, 82, 45}, + {"sienna1", 255, 130, 71}, + {"sienna2", 238, 121, 66}, + {"sienna3", 205, 104, 57}, + {"sienna4", 139, 71, 38}, + {"sky blue", 135, 206, 235}, + {"slate blue", 106, 90, 205}, + {"slate gray", 112, 128, 144}, + {"slate grey", 112, 128, 144}, + {"snow", 255, 250, 250}, + {"snow1", 255, 250, 250}, + {"snow2", 238, 233, 233}, + {"snow3", 205, 201, 201}, + {"snow4", 139, 137, 137}, + {"spring green", 0, 255, 127}, + {"steel blue", 70, 130, 180}, + {"tan", 210, 180, 140}, + {"tan1", 255, 165, 79}, + {"tan2", 238, 154, 73}, + {"tan3", 205, 133, 63}, + {"tan4", 139, 90, 43}, + {"thistle", 216, 191, 216}, + {"thistle1", 255, 225, 255}, + {"thistle2", 238, 210, 238}, + {"thistle3", 205, 181, 205}, + {"thistle4", 139, 123, 139}, + {"tomato", 255, 99, 71}, + {"tomato1", 255, 99, 71}, + {"tomato2", 238, 92, 66}, + {"tomato3", 205, 79, 57}, + {"tomato4", 139, 54, 38}, + {"turquoise", 64, 224, 208}, + {"turquoise1", 0, 245, 255}, + {"turquoise2", 0, 229, 238}, + {"turquoise3", 0, 197, 205}, + {"turquoise4", 0, 134, 139}, + {"violet", 238, 130, 238}, + {"violet red", 208, 32, 144}, + {"wheat", 245, 222, 179}, + {"wheat1", 255, 231, 186}, + {"wheat2", 238, 216, 174}, + {"wheat3", 205, 186, 150}, + {"wheat4", 139, 126, 102}, + {"white", 255, 255, 255}, + {"white smoke", 245, 245, 245}, + {"yellow", 255, 255, 0}, + {"yellow green", 154, 205, 50}, + {"yellow1", 255, 255, 0}, + {"yellow2", 238, 238, 0}, + {"yellow3", 205, 205, 0}, + {"yellow4", 139, 139, 0}, +}; + +BGD_EXPORT_DATA_PROT gdColorMap GD_COLOR_MAP_X11 = {sizeof(GD_COLOR_MAP_X11_ENTRIES) / + sizeof(gdColorMapEntry), + (gdColorMapEntry *)GD_COLOR_MAP_X11_ENTRIES}; + +/* + Function: gdColorMapLookup +*/ +BGD_DECLARE(int) +gdColorMapLookup(const gdColorMap color_map, const char *color_name, int *r, int *g, int *b) +{ + gdColorMapEntry *entries = color_map.entries; + int low = 0; + int high = color_map.num_entries - 1; + while (low <= high) { + int i = (low + high) / 2; + int result = strcmp(color_name, entries[i].color_name); + if (result == 0) { + *r = entries[i].red; + *g = entries[i].green; + *b = entries[i].blue; + return 1; + } else if (result < 0) { + high = i - 1; + } else { + low = i + 1; + } + } + return 0; +} diff --git a/ext/gd/libgd/gd_color_map.h b/ext/gd/libgd/gd_color_map.h new file mode 100644 index 000000000000..48e2fa280a2b --- /dev/null +++ b/ext/gd/libgd/gd_color_map.h @@ -0,0 +1,31 @@ +#ifndef GD_COLOR_MAP_H +#define GD_COLOR_MAP_H 1 + +#include "gd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char *color_name; + int red; + int green; + int blue; +} gdColorMapEntry; + +typedef struct { + int num_entries; + gdColorMapEntry *entries; +} gdColorMap; + +extern BGD_EXPORT_DATA_PROT gdColorMap GD_COLOR_MAP_X11; + +BGD_DECLARE(int) +gdColorMapLookup(const gdColorMap color_map, const char *color_name, int *r, int *g, int *b); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/gd/libgd/gd_color_match.c b/ext/gd/libgd/gd_color_match.c index 19fdfe6d031d..217498a37a1d 100644 --- a/ext/gd/libgd/gd_color_match.c +++ b/ext/gd/libgd/gd_color_match.c @@ -1,62 +1,63 @@ #include "gd.h" #include "gdhelpers.h" +#include #include "gd_intern.h" #include "php.h" -/* bring the palette colors in im2 to be closer to im1 - * +/* + Function: gdImageColorMatch + + Bring the palette colors in im2 to be closer to im1. */ -int gdImageColorMatch (gdImagePtr im1, gdImagePtr im2) +BGD_DECLARE(int) gdImageColorMatch(gdImagePtr im1, gdImagePtr im2) { - unsigned long *buf; /* stores our calculations */ - unsigned long *bp; /* buf ptr */ - int color, rgb; - int x,y; - int count; - - if( !im1->trueColor ) { - return -1; /* im1 must be True Color */ - } - if( im2->trueColor ) { - return -2; /* im2 must be indexed */ - } - if( (im1->sx != im2->sx) || (im1->sy != im2->sy) ) { - return -3; /* the images are meant to be the same dimensions */ - } - if (im2->colorsTotal<1) { - return -4; /* At least 1 color must be allocated */ - } + unsigned long *buf; /* stores our calculations */ + unsigned long *bp; /* buf ptr */ + int color, rgb; + int x, y; + int count; - buf = (unsigned long *)safe_emalloc(sizeof(unsigned long), 5 * gdMaxColors, 0); - memset( buf, 0, sizeof(unsigned long) * 5 * gdMaxColors ); + if (!im1->trueColor) { + return -1; /* im1 must be True Color */ + } + if (im2->trueColor) { + return -2; /* im2 must be indexed */ + } + if ((im1->sx != im2->sx) || (im1->sy != im2->sy)) { + return -3; /* the images are meant to be the same dimensions */ + } + if (im2->colorsTotal < 1) { + return -4; /* At least 1 color must be allocated */ + } + // gdMaxColors == 255 + buf = (unsigned long *)gdMalloc(sizeof(unsigned long) * 5 * gdMaxColors); + memset(buf, 0, sizeof(unsigned long) * 5 * gdMaxColors); - for (x=0; xsx; x++) { - for( y=0; ysy; y++ ) { - color = im2->pixels[y][x]; - rgb = im1->tpixels[y][x]; - bp = buf + (color * 5); - (*(bp++))++; - *(bp++) += gdTrueColorGetRed(rgb); - *(bp++) += gdTrueColorGetGreen(rgb); - *(bp++) += gdTrueColorGetBlue(rgb); - *(bp++) += gdTrueColorGetAlpha(rgb); - } - } - bp = buf; - for (color=0; colorcolorsTotal; color++) { - count = *(bp++); - if( count > 0 ) { - im2->red[color] = *(bp++) / count; - im2->green[color] = *(bp++) / count; - im2->blue[color] = *(bp++) / count; - im2->alpha[color] = *(bp++) / count; - } else { - bp += 4; - } - } - gdFree(buf); - return 0; + for (x = 0; x < im1->sx; x++) { + for (y = 0; y < im1->sy; y++) { + color = im2->pixels[y][x]; + rgb = im1->tpixels[y][x]; + bp = buf + (color * 5); + (*(bp++))++; + *(bp++) += gdTrueColorGetRed(rgb); + *(bp++) += gdTrueColorGetGreen(rgb); + *(bp++) += gdTrueColorGetBlue(rgb); + *(bp++) += gdTrueColorGetAlpha(rgb); + } + } + bp = buf; + for (color = 0; color < im2->colorsTotal; color++) { + count = *(bp++); + if (count > 0) { + im2->red[color] = *(bp++) / count; + im2->green[color] = *(bp++) / count; + im2->blue[color] = *(bp++) / count; + im2->alpha[color] = *(bp++) / count; + } else { + bp += 4; + } + } + gdFree(buf); + return 0; } - - diff --git a/ext/gd/libgd/gd_compositor.c b/ext/gd/libgd/gd_compositor.c new file mode 100644 index 000000000000..66c50b64b4c2 --- /dev/null +++ b/ext/gd/libgd/gd_compositor.c @@ -0,0 +1,339 @@ +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd_compositor.h" +#include "gd_vector2d_private.h" + +typedef enum { + FACTOR_ZERO, + FACTOR_ONE, + FACTOR_SRC_ALPHA, + FACTOR_DST_ALPHA, + FACTOR_ONE_MINUS_SRC_ALPHA, + FACTOR_ONE_MINUS_DST_ALPHA, + FACTOR_SATURATE +} FactorKind; + +typedef struct { + FactorKind src, dst; +} Factors; + +static const Factors porter_duff[] = {{FACTOR_ZERO, FACTOR_ZERO}, + {FACTOR_ONE, FACTOR_ZERO}, + {FACTOR_ONE, FACTOR_ONE_MINUS_SRC_ALPHA}, + {FACTOR_DST_ALPHA, FACTOR_ZERO}, + {FACTOR_ONE_MINUS_DST_ALPHA, FACTOR_ZERO}, + {FACTOR_DST_ALPHA, FACTOR_ONE_MINUS_SRC_ALPHA}, + {FACTOR_ZERO, FACTOR_ONE}, + {FACTOR_ONE_MINUS_DST_ALPHA, FACTOR_ONE}, + {FACTOR_ZERO, FACTOR_SRC_ALPHA}, + {FACTOR_ZERO, FACTOR_ONE_MINUS_SRC_ALPHA}, + {FACTOR_ONE_MINUS_DST_ALPHA, FACTOR_SRC_ALPHA}, + {FACTOR_ONE_MINUS_DST_ALPHA, FACTOR_ONE_MINUS_SRC_ALPHA}, + {FACTOR_ONE, FACTOR_ONE}, + {FACTOR_SATURATE, FACTOR_ONE}}; + +static float clamp01(float v) { return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v); } + +static gdPremulPixelF clamp_pixel(gdPremulPixelF p) +{ + p.a = clamp01(p.a); + p.r = fminf(clamp01(p.r), p.a); + p.g = fminf(clamp01(p.g), p.a); + p.b = fminf(clamp01(p.b), p.a); + return p; +} + +static gdPremulPixelF scale_pixel(gdPremulPixelF p, float scale) +{ + p.r *= scale; + p.g *= scale; + p.b *= scale; + p.a *= scale; + return p; +} + +static gdPremulPixelF lerp_pixel(gdPremulPixelF a, gdPremulPixelF b, float t) +{ + gdPremulPixelF r; + t = clamp01(t); + r.r = a.r + (b.r - a.r) * t; + r.g = a.g + (b.g - a.g) * t; + r.b = a.b + (b.b - a.b) * t; + r.a = a.a + (b.a - a.a) * t; + return clamp_pixel(r); +} + +static float factor(FactorKind kind, float sa, float da) +{ + switch (kind) { + case FACTOR_ZERO: + return 0.0f; + case FACTOR_ONE: + return 1.0f; + case FACTOR_SRC_ALPHA: + return sa; + case FACTOR_DST_ALPHA: + return da; + case FACTOR_ONE_MINUS_SRC_ALPHA: + return 1.0f - sa; + case FACTOR_ONE_MINUS_DST_ALPHA: + return 1.0f - da; + case FACTOR_SATURATE: + return sa > 0.0f ? fminf(1.0f, (1.0f - da) / sa) : 0.0f; + } + return 0.0f; +} + +static gdPremulPixelF composite_porter_duff(gdCompositeOperator op, gdPremulPixelF s, + gdPremulPixelF d) +{ + const Factors f = porter_duff[op]; + const float sf = factor(f.src, s.a, d.a); + const float df = factor(f.dst, s.a, d.a); + gdPremulPixelF r; + r.r = s.r * sf + d.r * df; + r.g = s.g * sf + d.g * df; + r.b = s.b * sf + d.b * df; + r.a = s.a * sf + d.a * df; + return clamp_pixel(r); +} + +static float blend_channel(gdCompositeOperator op, float s, float d) +{ + switch (op) { + case GD_OP_MULTIPLY: + return s * d; + case GD_OP_SCREEN: + return s + d - s * d; + case GD_OP_OVERLAY: + return d <= 0.5f ? 2.0f * s * d : 1.0f - 2.0f * (1.0f - s) * (1.0f - d); + case GD_OP_DARKEN: + return fminf(s, d); + case GD_OP_LIGHTEN: + return fmaxf(s, d); + case GD_OP_COLOR_DODGE: + return s >= 1.0f ? 1.0f : fminf(1.0f, d / (1.0f - s)); + case GD_OP_COLOR_BURN: + return s <= 0.0f ? 0.0f : 1.0f - fminf(1.0f, (1.0f - d) / s); + case GD_OP_HARD_LIGHT: + return s <= 0.5f ? 2.0f * s * d : 1.0f - 2.0f * (1.0f - s) * (1.0f - d); + case GD_OP_SOFT_LIGHT: + if (s <= 0.5f) + return d - (1.0f - 2.0f * s) * d * (1.0f - d); + else { + float g = d <= 0.25f ? ((16.0f * d - 12.0f) * d + 4.0f) * d : sqrtf(d); + return d + (2.0f * s - 1.0f) * (g - d); + } + case GD_OP_DIFFERENCE: + return fabsf(d - s); + case GD_OP_EXCLUSION: + return s + d - 2.0f * s * d; + default: + return s; + } +} + +typedef struct { + float r, g, b; +} StraightColor; + +static float color_lum(StraightColor c) { return 0.30f * c.r + 0.59f * c.g + 0.11f * c.b; } + +static float color_sat(StraightColor c) +{ + return fmaxf(c.r, fmaxf(c.g, c.b)) - fminf(c.r, fminf(c.g, c.b)); +} + +static StraightColor clip_color(StraightColor c) +{ + float l = color_lum(c); + float n = fminf(c.r, fminf(c.g, c.b)); + float x = fmaxf(c.r, fmaxf(c.g, c.b)); + if (n < 0.0f) { + float k = l / (l - n); + c.r = l + (c.r - l) * k; + c.g = l + (c.g - l) * k; + c.b = l + (c.b - l) * k; + } + x = fmaxf(c.r, fmaxf(c.g, c.b)); + if (x > 1.0f) { + float k = (1.0f - l) / (x - l); + c.r = l + (c.r - l) * k; + c.g = l + (c.g - l) * k; + c.b = l + (c.b - l) * k; + } + return c; +} + +static StraightColor set_lum(StraightColor c, float l) +{ + float d = l - color_lum(c); + c.r += d; + c.g += d; + c.b += d; + return clip_color(c); +} + +static StraightColor set_sat(StraightColor c, float s) +{ + float *v[3] = {&c.r, &c.g, &c.b}; + float *tmp; + if (*v[0] > *v[1]) { + tmp = v[0]; + v[0] = v[1]; + v[1] = tmp; + } + if (*v[1] > *v[2]) { + tmp = v[1]; + v[1] = v[2]; + v[2] = tmp; + } + if (*v[0] > *v[1]) { + tmp = v[0]; + v[0] = v[1]; + v[1] = tmp; + } + if (*v[2] > *v[0]) { + *v[1] = (*v[1] - *v[0]) * s / (*v[2] - *v[0]); + *v[2] = s; + } else { + *v[1] = *v[2] = 0.0f; + } + *v[0] = 0.0f; + return c; +} + +static StraightColor blend_hsl(gdCompositeOperator op, StraightColor s, StraightColor d) +{ + switch (op) { + case GD_OP_HSL_HUE: + return set_lum(set_sat(s, color_sat(d)), color_lum(d)); + case GD_OP_HSL_SATURATION: + return set_lum(set_sat(d, color_sat(s)), color_lum(d)); + case GD_OP_HSL_COLOR: + return set_lum(s, color_lum(d)); + case GD_OP_HSL_LUMINOSITY: + return set_lum(d, color_lum(s)); + default: + return s; + } +} + +static gdPremulPixelF composite_blend(gdCompositeOperator op, gdPremulPixelF s, gdPremulPixelF d) +{ + StraightColor cs = {0, 0, 0}, cd = {0, 0, 0}, b; + gdPremulPixelF r; + if (s.a > 0.0f) { + cs.r = s.r / s.a; + cs.g = s.g / s.a; + cs.b = s.b / s.a; + } + if (d.a > 0.0f) { + cd.r = d.r / d.a; + cd.g = d.g / d.a; + cd.b = d.b / d.a; + } + if (op >= GD_OP_HSL_HUE) + b = blend_hsl(op, cs, cd); + else { + b.r = blend_channel(op, cs.r, cd.r); + b.g = blend_channel(op, cs.g, cd.g); + b.b = blend_channel(op, cs.b, cd.b); + } + r.a = s.a + d.a * (1.0f - s.a); + r.r = (1.0f - d.a) * s.r + (1.0f - s.a) * d.r + s.a * d.a * b.r; + r.g = (1.0f - d.a) * s.g + (1.0f - s.a) * d.g + s.a * d.a * b.g; + r.b = (1.0f - d.a) * s.b + (1.0f - s.a) * d.b + s.a * d.a * b.b; + return clamp_pixel(r); +} + +int gdCompositeOperatorIsValid(gdCompositeOperator op) +{ + return op >= GD_OP_CLEAR && op < GD_OP_COUNT; +} + +int gdCompositeOperatorIsUnbounded(gdCompositeOperator op) +{ + return op == GD_OP_IN || op == GD_OP_OUT || op == GD_OP_DEST_IN || op == GD_OP_DEST_ATOP; +} + +gdPremulPixelF gdCompositePixel(gdCompositeOperator op, gdPremulPixelF src, gdPremulPixelF dst, + float coverage) +{ + gdPremulPixelF result; + coverage = clamp01(coverage); + src = clamp_pixel(src); + dst = clamp_pixel(dst); + if (!gdCompositeOperatorIsValid(op)) + return dst; + if (op == GD_OP_CLEAR || op == GD_OP_SOURCE) { + result = composite_porter_duff(op, src, dst); + return lerp_pixel(dst, result, coverage); + } + src = scale_pixel(src, coverage); + if (op <= GD_OP_SATURATE) + return composite_porter_duff(op, src, dst); + return composite_blend(op, src, dst); +} + +void gdCompositeSpan(gdCompositeOperator op, const gdPremulPixelF *src, ptrdiff_t src_stride, + gdPremulPixelF *dst, const float *coverage, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) { + float c = coverage ? coverage[i] : 1.0f; + dst[i] = gdCompositePixel(op, *src, dst[i], c); + src = (const gdPremulPixelF *)((const char *)src + src_stride); + } +} + +gdPremulPixelF gdCompositePixelFromArgb32(uint32_t p) +{ + gdPremulPixelF r; + r.a = ((p >> 24) & 255) / 255.0f; + r.r = ((p >> 16) & 255) / 255.0f; + r.g = ((p >> 8) & 255) / 255.0f; + r.b = (p & 255) / 255.0f; + return clamp_pixel(r); +} + +uint32_t gdCompositePixelToArgb32(gdPremulPixelF p) +{ + uint32_t a, r, g, b; + p = clamp_pixel(p); + a = (uint32_t)floorf(p.a * 255.0f + 0.5f); + r = (uint32_t)floorf(p.r * 255.0f + 0.5f); + g = (uint32_t)floorf(p.g * 255.0f + 0.5f); + b = (uint32_t)floorf(p.b * 255.0f + 0.5f); + return (a << 24) | (r << 16) | (g << 8) | b; +} + +gdPremulPixelF gdCompositePixelFromGd(int p) +{ + float a = (gdAlphaMax - gdTrueColorGetAlpha(p)) / (float)gdAlphaMax; + gdPremulPixelF r; + r.a = a; + r.r = gdTrueColorGetRed(p) / 255.0f * a; + r.g = gdTrueColorGetGreen(p) / 255.0f * a; + r.b = gdTrueColorGetBlue(p) / 255.0f * a; + return clamp_pixel(r); +} + +int gdCompositePixelToGd(gdPremulPixelF p) +{ + int a, r = 0, g = 0, b = 0; + p = clamp_pixel(p); + a = (int)floorf((1.0f - p.a) * gdAlphaMax + 0.5f); + if (p.a > 0.0f) { + r = (int)floorf(p.r / p.a * 255.0f + 0.5f); + g = (int)floorf(p.g / p.a * 255.0f + 0.5f); + b = (int)floorf(p.b / p.a * 255.0f + 0.5f); + } + return gdTrueColorAlpha(r, g, b, a); +} diff --git a/ext/gd/libgd/gd_compositor.h b/ext/gd/libgd/gd_compositor.h new file mode 100644 index 000000000000..e5fedf57fc8d --- /dev/null +++ b/ext/gd/libgd/gd_compositor.h @@ -0,0 +1,23 @@ +#ifndef GD_COMPOSITOR_H +#define GD_COMPOSITOR_H + +#include "gd_vector2d_private.h" +#include +#include + +typedef struct { + float r, g, b, a; +} gdPremulPixelF; + +int gdCompositeOperatorIsValid(gdCompositeOperator op); +int gdCompositeOperatorIsUnbounded(gdCompositeOperator op); +gdPremulPixelF gdCompositePixel(gdCompositeOperator op, gdPremulPixelF src, gdPremulPixelF dst, + float coverage); +void gdCompositeSpan(gdCompositeOperator op, const gdPremulPixelF *src, ptrdiff_t src_stride, + gdPremulPixelF *dst, const float *coverage, size_t n); +gdPremulPixelF gdCompositePixelFromArgb32(uint32_t pixel); +uint32_t gdCompositePixelToArgb32(gdPremulPixelF pixel); +gdPremulPixelF gdCompositePixelFromGd(int pixel); +int gdCompositePixelToGd(gdPremulPixelF pixel); + +#endif diff --git a/ext/gd/libgd/gd_crop.c b/ext/gd/libgd/gd_crop.c index 676545c4dbc9..fc4bf2e4a8ef 100644 --- a/ext/gd/libgd/gd_crop.c +++ b/ext/gd/libgd/gd_crop.c @@ -1,249 +1,271 @@ /** - * Title: Crop + * File: Cropping * - * A couple of functions to crop images, automatically (auto detection of - * the borders color), using a given color (with or without tolerance) - * or using a selection. + * Crop an image * - * The threshold method works relatively well but it can be improved. - * Maybe L*a*b* and Delta-E will give better results (and a better - * granularity). + * Some functions to crop images, automatically (auto detection of the border + * color), using a given color (with or without tolerance) or using a given + * rectangle. * * Example: * (start code) * im2 = gdImageAutoCrop(im, GD_CROP_SIDES); * if (im2) { - + * gdImageDestroy(im); // unless you need the original image subsequently + * // do something with the cropped image * } * gdImageDestroy(im2); * (end code) **/ +#include #include #include -#include #include "gd.h" +#include "gd_color.h" +#include static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color); -static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold); /** * Function: gdImageCrop - * Crops the src image using the area defined by the rectangle. - * The result is returned as a new image. * + * Crop an image to a given rectangle * * Parameters: - * src - Source image - * crop - Rectangular region to crop + * src - The image. + * crop - The cropping rectangle, see . * * Returns: - * on success or NULL + * The newly created cropped image, or NULL on failure. + * + * See also: + * - + * - */ -gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop) +BGD_DECLARE(gdImagePtr) gdImageCrop(gdImagePtr src, const gdRect *crop) { - gdImagePtr dst; - int alphaBlendingFlag; - - if (gdImageTrueColor(src)) { - dst = gdImageCreateTrueColor(crop->width, crop->height); - } else { - dst = gdImageCreate(crop->width, crop->height); - } - if (!dst) return NULL; - alphaBlendingFlag = dst->alphaBlendingFlag; - gdImageAlphaBlending(dst, gdEffectReplace); - gdImageCopy(dst, src, 0, 0, crop->x, crop->y, crop->width, crop->height); - gdImageAlphaBlending(dst, alphaBlendingFlag); - - return dst; + gdImagePtr dst; + int alphaBlendingFlag; + + if (gdImageTrueColor(src)) { + dst = gdImageCreateTrueColor(crop->width, crop->height); + } else { + dst = gdImageCreate(crop->width, crop->height); + } + if (!dst) + return NULL; + alphaBlendingFlag = dst->alphaBlendingFlag; + gdImageAlphaBlending(dst, gdEffectReplace); + gdImageCopy(dst, src, 0, 0, crop->x, crop->y, crop->width, crop->height); + gdImageAlphaBlending(dst, alphaBlendingFlag); + + return dst; } /** - * Function: gdImageAutoCrop - * Automatic croping of the src image using the given mode - * (see ) + * Function: gdImageCropAuto + * + * Crop an image automatically * + * This function detects the cropping area according to the given _mode_. * * Parameters: - * im - Source image - * mode - crop mode + * im - The image. + * mode - The cropping mode, see . * * Returns: - * on success or NULL + * The newly created cropped image, or NULL on failure. * * See also: - * + * - + * - */ -gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode) +BGD_DECLARE(gdImagePtr) +gdImageCropAuto(gdImagePtr im, const unsigned int mode) { - const int width = gdImageSX(im); - const int height = gdImageSY(im); - - int x,y; - int color, match; - gdRect crop; - - crop.x = 0; - crop.y = 0; - crop.width = 0; - crop.height = 0; - - switch (mode) { - case GD_CROP_TRANSPARENT: - color = gdImageGetTransparent(im); - break; - - case GD_CROP_BLACK: - color = gdImageColorClosestAlpha(im, 0, 0, 0, 0); - break; - - case GD_CROP_WHITE: - color = gdImageColorClosestAlpha(im, 255, 255, 255, 0); - break; - - case GD_CROP_SIDES: - gdGuessBackgroundColorFromCorners(im, &color); - break; - - case GD_CROP_DEFAULT: - default: - color = gdImageGetTransparent(im); - break; - } - - /* TODO: Add gdImageGetRowPtr and works with ptr at the row level - * for the true color and palette images - * new formats will simply work with ptr - */ - match = 1; - for (y = 0; match && y < height; y++) { - for (x = 0; match && x < width; x++) { - int c2 = gdImageGetPixel(im, x, y); - match = (color == c2); - } - } - - /* Whole image would be cropped > bye */ - if (match) { - return NULL; - } - - crop.y = y - 1; - - match = 1; - for (y = height - 1; match && y >= 0; y--) { - for (x = 0; match && x < width; x++) { - match = (color == gdImageGetPixel(im, x,y)); - } - } - crop.height = y - crop.y + 2; - - match = 1; - for (x = 0; match && x < width; x++) { - for (y = 0; match && y < crop.y + crop.height; y++) { - match = (color == gdImageGetPixel(im, x,y)); - } - } - crop.x = x - 1; - - match = 1; - for (x = width - 1; match && x >= 0; x--) { - for (y = 0; match && y < crop.y + crop.height; y++) { - match = (color == gdImageGetPixel(im, x,y)); - } - } - crop.width = x - crop.x + 2; - - return gdImageCrop(im, &crop); + const int width = gdImageSX(im); + const int height = gdImageSY(im); + + int x, y; + int color; + gdRect crop; + + crop.x = 0; + crop.y = 0; + crop.width = 0; + crop.height = 0; + + switch (mode) { + case GD_CROP_TRANSPARENT: + color = gdImageGetTransparent(im); + break; + + case GD_CROP_BLACK: + color = gdImageColorClosestAlpha(im, 0, 0, 0, 0); + break; + + case GD_CROP_WHITE: + color = gdImageColorClosestAlpha(im, 255, 255, 255, 0); + break; + + case GD_CROP_SIDES: + gdGuessBackgroundColorFromCorners(im, &color); + break; + + case GD_CROP_DEFAULT: + default: + color = gdImageGetTransparent(im); + break; + } + + for (x = 0, y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (color != gdImageGetPixel(im, x, y)) { + goto break1; + } + } + } +break1: + + /* Whole image would be cropped > bye */ + if (y == height && x == width) { + return NULL; + } + + crop.y = y; + + for (y = height - 1; y >= 0; y--) { + for (x = 0; x < width; x++) { + if (color != gdImageGetPixel(im, x, y)) { + goto break2; + } + } + } +break2: + + crop.height = y - crop.y + 1; + + for (x = 0; x < width; x++) { + for (y = crop.y; y < crop.y + crop.height; y++) { + if (color != gdImageGetPixel(im, x, y)) { + goto break3; + } + } + } +break3: + + crop.x = x; + + for (x = width - 1; x >= 0; x--) { + for (y = crop.y; y < crop.y + crop.height; y++) { + if (color != gdImageGetPixel(im, x, y)) { + goto break4; + } + } + } +break4: + + crop.width = x - crop.x + 1; + + return gdImageCrop(im, &crop); } -/*TODOs: Implement DeltaE instead, way better perceptual differences */ + /** - * Function: gdImageThresholdCrop - * Crop an image using a given color. The threshold argument defines - * the tolerance to be used while comparing the image color and the - * color to crop. The method used to calculate the color difference - * is based on the color distance in the RGB(a) cube. + * Function: gdImageCropThreshold + * + * Crop an image using a given color * + * The _threshold_ defines the tolerance to be used while comparing the image + * color and the color to crop. The method used to calculate the color + * difference is based on the color distance in the RGB(A) cube. * * Parameters: - * im - Source image - * color - color to crop - * threshold - tolerance (0..100) + * im - The image. + * color - The crop color. + * threshold - The crop threshold. * * Returns: - * on success or NULL + * The newly created cropped image, or NULL on failure. * * See also: - * , or + * - + * - */ -gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold) +BGD_DECLARE(gdImagePtr) +gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold) { - const int width = gdImageSX(im); - const int height = gdImageSY(im); - - int x,y; - int match; - gdRect crop; - - crop.x = 0; - crop.y = 0; - crop.width = 0; - crop.height = 0; - - /* Pierre: crop everything sounds bad */ - if (threshold > 100.0) { - return NULL; - } - - if (!gdImageTrueColor(im) && color >= gdImageColorsTotal(im)) { - return NULL; - } - - /* TODO: Add gdImageGetRowPtr and works with ptr at the row level - * for the true color and palette images - * new formats will simply work with ptr - */ - match = 1; - for (y = 0; match && y < height; y++) { - for (x = 0; match && x < width; x++) { - match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; - } - } - - /* Whole image would be cropped > bye */ - if (match) { - return NULL; - } - - crop.y = y - 1; - - match = 1; - for (y = height - 1; match && y >= 0; y--) { - for (x = 0; match && x < width; x++) { - match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0; - } - } - crop.height = y - crop.y + 2; - - match = 1; - for (x = 0; match && x < width; x++) { - for (y = 0; match && y < crop.y + crop.height; y++) { - match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; - } - } - crop.x = x - 1; - - match = 1; - for (x = width - 1; match && x >= 0; x--) { - for (y = 0; match && y < crop.y + crop.height; y++) { - match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0; - } - } - crop.width = x - crop.x + 2; - - return gdImageCrop(im, &crop); + const int width = gdImageSX(im); + const int height = gdImageSY(im); + + int x, y; + gdRect crop; + + crop.x = 0; + crop.y = 0; + crop.width = 0; + crop.height = 0; + + /* To crop everything sounds bad */ + if (threshold > 100.0) { + return NULL; + } + + if (!gdImageTrueColor(im) && color >= (unsigned int)gdImageColorsTotal(im)) { + return NULL; + } + + for (x = 0, y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + if (!gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) { + goto break1; + } + } + } +break1: + + /* Whole image would be cropped > bye */ + if (y == height && x == width) { + return NULL; + } + + crop.y = y; + + for (y = height - 1; y >= 0; y--) { + for (x = 0; x < width; x++) { + if (!gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) { + goto break2; + } + } + } +break2: + + crop.height = y - crop.y + 1; + + for (x = 0; x < width; x++) { + for (y = crop.y; y < crop.y + crop.height; y++) { + if (!gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) { + goto break3; + } + } + } +break3: + + crop.x = x; + + for (x = width - 1; x >= 0; x--) { + for (y = crop.y; y < crop.y + crop.height; y++) { + if (!gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) { + goto break4; + } + } + } +break4: + + crop.width = x - crop.x + 1; + + return gdImageCrop(im, &crop); } /* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/) @@ -254,64 +276,48 @@ gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const f */ static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color) { - const int tl = gdImageGetPixel(im, 0, 0); - const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0); - const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1); - const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1); - - if (tr == bl && tr == br) { - *color = tr; - return 3; - } else if (tl == bl && tl == br) { - *color = tl; - return 3; - } else if (tl == tr && tl == br) { - *color = tl; - return 3; - } else if (tl == tr && tl == bl) { - *color = tl; - return 3; - } else if (tl == tr || tl == bl || tl == br) { - *color = tl; - return 2; - } else if (tr == bl || tr == br) { - *color = tr; - return 2; - } else if (br == bl) { - *color = bl; - return 2; - } else { - register int r,b,g,a; - - r = (int)(0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4); - g = (int)(0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4); - b = (int)(0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4); - a = (int)(0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4); - *color = gdImageColorClosestAlpha(im, r, g, b, a); - return 0; - } -} - -static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold) -{ - const int dr = gdImageRed(im, col1) - gdImageRed(im, col2); - const int dg = gdImageGreen(im, col1) - gdImageGreen(im, col2); - const int db = gdImageBlue(im, col1) - gdImageBlue(im, col2); - const int da = gdImageAlpha(im, col1) - gdImageAlpha(im, col2); - const int dist = dr * dr + dg * dg + db * db + da * da; - - return (100.0 * dist / 195075) < threshold; -} - -/* - * To be implemented when we have more image formats. - * Buffer like gray8 gray16 or rgb8 will require some tweak - * and can be done in this function (called from the autocrop - * function. (Pierre) - */ -#if 0 -static int colors_equal (const int col1, const in col2) -{ - + const int tl = gdImageGetPixel(im, 0, 0); + const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0); + const int bl = gdImageGetPixel(im, 0, gdImageSY(im) - 1); + const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) - 1); + + if (tr == bl && tr == br) { + *color = tr; + return 3; + } else if (tl == bl && tl == br) { + *color = tl; + return 3; + } else if (tl == tr && tl == br) { + *color = tl; + return 3; + } else if (tl == tr && tl == bl) { + *color = tl; + return 3; + } else if (tl == tr || tl == bl || tl == br) { + *color = tl; + return 2; + } else if (tr == bl || tr == br) { + *color = tr; + return 2; + } else if (br == bl) { + *color = bl; + return 2; + } else { + register int r, b, g, a; + + r = (2 + gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + + gdImageRed(im, br)) / + 4; + g = (2 + gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + + gdImageGreen(im, br)) / + 4; + b = (2 + gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + + gdImageBlue(im, br)) / + 4; + a = (2 + gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + + gdImageAlpha(im, br)) / + 4; + *color = gdImageColorClosestAlpha(im, r, g, b, a); + return 0; + } } -#endif diff --git a/ext/gd/libgd/gd_draw.c b/ext/gd/libgd/gd_draw.c new file mode 100644 index 000000000000..444d53bff84e --- /dev/null +++ b/ext/gd/libgd/gd_draw.c @@ -0,0 +1,483 @@ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd_intern.h" + +/* 2.03: don't include zlib here or we can't build without PNG */ +#include "gd_color.h" +#include "gd_compositor.h" +#include "gd_draw_blend.h" +#include "gd_errors.h" +#include "gd_path.h" +#include "gd_path_dash.h" +#include "gd_path_matrix.h" +#include "gd_span_rle.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +/* Conversion helpers: legacy gdImage truecolor <-> premultiplied ARGB32 */ +static inline uint32_t gdcolor_to_premul(int gdcolor) +{ + return gdCompositePixelToArgb32(gdCompositePixelFromGd(gdcolor)); +} + +static inline int premul_to_gdcolor(uint32_t pm) +{ + return gdCompositePixelToGd(gdCompositePixelFromArgb32(pm)); +} + +BGD_DECLARE(void) +gdContextSetSourceRgba(gdContextPtr context, double r, double g, double b, double a) +{ + gdPaintPtr source = gdPaintCreateRgba(r, g, b, a); + gdContextSetSource(context, source); + gdPaintDestroy(source); +} + +BGD_DECLARE(void) +gdContextSetSourceRgb(gdContextPtr context, double r, double g, double b) +{ + gdPaintPtr source = gdPaintCreateRgba(r, g, b, 1.0); + gdContextSetSource(context, source); + gdPaintDestroy(source); +} + +GD_VECTOR2D_INTERNAL void gdContextSetSourceSurface(gdContextPtr context, gdSurfacePtr surface, + double x, double y) +{ + gdPaintSetSourceSurface(context, surface, x, y); +} + +BGD_DECLARE(void) +gdContextSetSourceImage(gdContextPtr context, gdImagePtr image, double x, double y) +{ + gdPathPatternPtr pattern; + gdPaintPtr paint; + gdPathMatrix matrix; + + if (!context || !image) + return; + pattern = gdPathPatternCreateForImage(image); + if (!pattern) + return; + gdPathMatrixInitTranslate(&matrix, x, y); + gdPathPatternSetMatrix(pattern, &matrix); + paint = gdPaintCreateFromPattern(pattern); + gdPathPatternDestroy(pattern); + if (!paint) + return; + gdContextSetSource(context, paint); + gdPaintDestroy(paint); +} + +BGD_DECLARE(void) +gdContextSetOperator(gdContextPtr context, gdCompositeOperator op) +{ + if (!context) + return; + if (!gdCompositeOperatorIsValid(op)) { + gd_error("gdContextSetOperator: invalid operator %d.\n", (int)op); + return; + } + context->state->op = op; +} + +BGD_DECLARE(void) +gdContextSetOpacity(gdContextPtr context, double opacity) +{ + if (!context || !isfinite(opacity)) + return; + context->state->opacity = CLAMP(opacity, 0.0, 1.0); +} + +BGD_DECLARE(void) +gdContextNewPath(gdContextPtr context) { gdPathClear(context->path); } + +BGD_DECLARE(void) gdContextAppendPath(gdContextPtr cr, gdPathPtr source) +{ + gdPathAppendPath(cr->path, source); +} + +GD_VECTOR2D_INTERNAL gdContextPtr gdContextCreate(gdSurfacePtr surface) +{ + gdContextPtr context = gdMalloc(sizeof(gdContext)); + if (!context) { + return NULL; + } + context->state = gdStateCreate(); + if (!context->state) { + goto failState; + } + context->path = gdPathCreate(); + if (!context->path) { + goto failPath; + } + context->rle = gdSpanRleCreate(); + if (!context->rle) { + goto failRle; + } + context->ref = 1; + context->surface = gdSurfaceAddRef(surface); + context->image = NULL; + context->clippath = NULL; + context->clip.x = 0.0; + context->clip.y = 0.0; + context->clip.w = surface->width; + context->clip.h = surface->height; + return context; +failRle: + gdFree(context->path); +failPath: + gdFree(context->state); +failState: + gdFree(context); + return NULL; +} + +BGD_DECLARE(gdContextPtr) +gdContextCreateForImage(gdImagePtr im) +{ + if (!im || !im->trueColor) { + return NULL; + } + + gdSurfacePtr scratch = gdSurfaceCreate(im->sx, im->sy, GD_SURFACE_ARGB32); + if (!scratch) { + return NULL; + } + + for (int y = 0; y < im->sy; y++) { + uint32_t *dst = (uint32_t *)(scratch->data + y * scratch->stride); + for (int x = 0; x < im->sx; x++) { + dst[x] = gdcolor_to_premul(im->tpixels[y][x]); + } + } + + gdContextPtr ctx = gdContextCreate(scratch); + if (!ctx) { + gdSurfaceDestroy(scratch); + return NULL; + } + gdSurfaceDestroy(scratch); + + ctx->image = im; + ctx->imageOwned = 0; + return ctx; +} + +BGD_DECLARE(void) +gdContextFlushImage(gdContextPtr ctx) +{ + if (!ctx || !ctx->image) { + return; + } + gdImagePtr im = ctx->image; + gdSurfacePtr scratch = ctx->surface; + + for (int y = 0; y < im->sy; y++) { + uint32_t *src = (uint32_t *)(scratch->data + y * scratch->stride); + for (int x = 0; x < im->sx; x++) { + im->tpixels[y][x] = premul_to_gdcolor(src[x]); + } + } +} + +BGD_DECLARE(gdImagePtr) +gdContextGetImage(gdContextPtr ctx) { return ctx ? ctx->image : NULL; } + +BGD_DECLARE(void) +gdContextStrokePreserve(gdContextPtr context) +{ + gdStatePtr state = context->state; + gdSpanRleClear(context->rle); + gdSpanRleRasterize(context->rle, context->path, &state->matrix, &context->clip, &state->stroke, + gdFillRuleNonZero); + if (!gdCompositeOperatorIsUnbounded(state->op)) + gdSpanRlePathClip(context->rle, state->clippath); + gdPathBlend(context, context->rle); +} + +BGD_DECLARE(void) +gdContextFillPreserve(gdContextPtr context) +{ + gdStatePtr state = context->state; + gdSpanRleClear(context->rle); + // gdPathDumpPathTransform(context->path, NULL); + gdSpanRleRasterize(context->rle, context->path, &state->matrix, &context->clip, NULL, + state->winding); + if (!gdCompositeOperatorIsUnbounded(state->op)) + gdSpanRlePathClip(context->rle, state->clippath); + gdPathBlend(context, context->rle); +} + +BGD_DECLARE(void) +gdContextFill(gdContextPtr context) +{ + gdContextFillPreserve(context); + gdContextNewPath(context); +} + +BGD_DECLARE(int) +gdContextClipPreserve(gdContextPtr context) +{ + unsigned int context_path_size; + gdPathPtr currentPath; + gdStatePtr state; + gdSpanRlePtr newclip; + + if (context == NULL) { + gd_error("gdContextClipPreserve: context must not be NULL"); + return 0; + } + currentPath = context->path; + state = context->state; + context_path_size = gdArrayNumElements(¤tPath->elements); + + if (context_path_size == 0) + return 1; + + gdSpanRleClear(context->rle); + gdSpanRleRasterize(context->rle, context->path, &state->matrix, &context->clip, NULL, + state->winding); + if (state->clippath) { + newclip = gdSpanHorizontalClip(state->clippath, context->rle); + } else { + newclip = gdSpanRleClone(context->rle); + } + if (!newclip) { + gd_error("gdContextClipPreserve: failed to allocate clip state"); + return 0; + } + gdSpanRleDestroy(state->clippath); + state->clippath = newclip; + return 1; +} + +BGD_DECLARE(void) +gdContextPaint(gdContextPtr context) +{ + gdSpanRlePtr rle; + gdStatePtr state = context->state; + if (state->clippath == NULL && context->clippath == NULL) { + gdPathPtr path = gdPathCreate(); + gdPathRectangle(path, context->clip.x, context->clip.y, context->clip.w, context->clip.h); + context->clippath = gdSpanRleCreate(); + gdSpanRleRasterize(context->clippath, path, &state->matrix, &context->clip, NULL, + gdFillRuleNonZero); + gdPathDestroy(path); + } + rle = state->clippath ? state->clippath : context->clippath; + gdPathBlend(context, rle); +} + +BGD_DECLARE(int) +gdContextClip(gdContextPtr context) +{ + if (!gdContextClipPreserve(context)) + return 0; + gdContextNewPath(context); + return 1; +} + +BGD_DECLARE(int) +gdContextSave(gdContextPtr context) +{ + gdStatePtr current; + gdStatePtr saved; + + if (context == NULL) { + gd_error("gdContextSave: context must not be NULL"); + return 0; + } + current = context->state; + saved = gdMalloc(sizeof(gdState)); + if (!saved) { + gd_error("gdContextSave: failed to allocate graphics state"); + return 0; + } + *saved = *current; + saved->source = gdPaintAddRef(current->source); + saved->clippath = gdSpanRleRetain(current->clippath); + saved->stroke.dash = gdPathDashClone(current->stroke.dash); + if (current->stroke.dash != NULL && saved->stroke.dash == NULL) { + gdSpanRleDestroy(saved->clippath); + gdPaintDestroy(saved->source); + gdFree(saved); + gd_error("gdContextSave: failed to clone dash state"); + return 0; + } + saved->next = current; + context->state = saved; + return 1; +} + +BGD_DECLARE(int) +gdContextRestore(gdContextPtr context) +{ + gdStatePtr restored; + + if (context == NULL) { + gd_error("gdContextRestore: context must not be NULL"); + return 0; + } + if (context->state->next == NULL) { + gd_error("gdContextRestore: no saved graphics state"); + return 0; + } + restored = context->state->next; + context->state->next = NULL; + gdStateDestroy(context->state); + context->state = restored; + return 1; +} + +BGD_DECLARE(void) +gdContextDestroy(gdContextPtr context) +{ + if (context == NULL) + return; + context->ref--; + if (context->ref == 0) { + if (context->image) { + gdContextFlushImage(context); + } + gdSurfaceDestroy(context->surface); + gdPathDestroy(context->path); + while (context->state) { + gdStatePtr next = context->state->next; + context->state->next = NULL; + gdStateDestroy(context->state); + context->state = next; + } + gdSpanRleDestroy(context->clippath); + gdSpanRleDestroy(context->rle); + gdFree(context); + } +} + +BGD_DECLARE(void) +gdContextMoveTo(gdContextPtr context, double x, double y) { gdPathMoveTo(context->path, x, y); } + +BGD_DECLARE(void) +gdContextRelMoveTo(gdContextPtr context, double dx, double dy) +{ + gdPathRelMoveTo(context->path, dx, dy); +} + +BGD_DECLARE(void) +gdContextLineTo(gdContextPtr context, double x, double y) { gdPathLineTo(context->path, x, y); } + +BGD_DECLARE(void) +gdContextRelLineTo(gdContextPtr context, double dx, double dy) +{ + gdPathRelLineTo(context->path, dx, dy); +} + +BGD_DECLARE(void) +gdContextSetLineWidth(gdContextPtr context, double width) { context->state->stroke.width = width; } + +BGD_DECLARE(void) +gdContextSetDash(gdContextPtr context, double offset, const double *data, int size) +{ + gdPathDashDestroy(context->state->stroke.dash); + context->state->stroke.dash = gdPathDashCreate(data, size, offset); +} + +BGD_DECLARE(void) +gdContextSetLineCap(gdContextPtr context, gdLineCap cap) { context->state->stroke.cap = cap; } + +BGD_DECLARE(void) +gdContextSetLineJoin(gdContextPtr context, gdLineJoin join) { context->state->stroke.join = join; } + +BGD_DECLARE(void) +gdContextCurveTo(gdContextPtr context, double x1, double y1, double x2, double y2, double x3, + double y3) +{ + gdPathCurveTo(context->path, x1, y1, x2, y2, x3, y3); +} + +BGD_DECLARE(void) +gdContextRelCurveTo(gdContextPtr context, double dx1, double dy1, double dx2, double dy2, + double dx3, double dy3) +{ + gdPathRelCurveTo(context->path, dx1, dy1, dx2, dy2, dx3, dy3); +} + +BGD_DECLARE(void) +gdContextQuadTo(gdContextPtr context, double x1, double y1, double x2, double y2) +{ + gdPathQuadTo(context->path, x1, y1, x2, y2); +} + +BGD_DECLARE(void) +gdContextRelQuadTo(gdContextPtr context, double dx1, double dy1, double dx2, double dy2) +{ + gdPathRelQuadTo(context->path, dx1, dy1, dx2, dy2); +} + +BGD_DECLARE(void) +gdContextArc(gdContextPtr context, double cx, double cy, double r, double a0, double a1) +{ + gdPathArc(context->path, cx, cy, r, a0, a1); +} + +BGD_DECLARE(void) +gdContextNegativeArc(gdContextPtr context, double cx, double cy, double r, double a0, double a1) +{ + gdPathNegativeArc(context->path, cx, cy, r, a0, a1); +} + +BGD_DECLARE(void) +gdContextRectangle(gdContextPtr context, double x, double y, double w, double h) +{ + gdPathRectangle(context->path, x, y, w, h); +} + +BGD_DECLARE(void) +gdContextClosePath(gdContextPtr context) +{ + if (!context) + return; + gdPathClose(context->path); +} + +BGD_DECLARE(void) +gdContextScale(gdContextPtr context, double x, double y) +{ + gdPathMatrixScale(&context->state->matrix, x, y); +} + +BGD_DECLARE(void) +gdContextTranslate(gdContextPtr context, double x, double y) +{ + gdPathMatrixTranslate(&context->state->matrix, x, y); +} + +BGD_DECLARE(void) +gdContextRotate(gdContextPtr context, double radians) +{ + gdPathMatrixRotate(&context->state->matrix, radians); +} + +BGD_DECLARE(void) +gdContextTransform(gdContextPtr context, const gdPathMatrixPtr matrix) +{ + gdPathMatrixMultiply(&context->state->matrix, matrix, &context->state->matrix); +} + +BGD_DECLARE(void) +gdContextSetFillRule(gdContextPtr context, gdFillRule winding) +{ + context->state->winding = winding; +} + +BGD_DECLARE(void) +gdContextStroke(gdContextPtr context) +{ + gdContextStrokePreserve(context); + gdContextNewPath(context); +} diff --git a/ext/gd/libgd/gd_draw_blend.c b/ext/gd/libgd/gd_draw_blend.c new file mode 100644 index 000000000000..48b35a228985 --- /dev/null +++ b/ext/gd/libgd/gd_draw_blend.c @@ -0,0 +1,788 @@ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_errors.h" +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "gd_array.h" +#include "gd_path_matrix.h" +#include "gd_span_rle.h" +#include "gd_surface.h" + +#include "gd_compositor.h" +#include "gd_fixed.h" +#include "gd_gradient.h" + +#define BILINEAR_INTERPOLATION_BITS 7 +#define BILINEAR_INTERPOLATION_RANGE (1 << BILINEAR_INTERPOLATION_BITS) + +#define CLIP(v, low, high) ((v) < (low) ? (low) : ((v) > (high) ? (high) : (v))) +#define MOD(a, b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) + +static inline int _fixed_to_bilinear_weight(gd_fixed_t x) +{ + return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) & ((1 << BILINEAR_INTERPOLATION_BITS) - 1); +} + +static inline uint32_t fetch_pixel_general(gdSurfacePtr surface, int x, int y, int check_bounds) +{ + if (check_bounds && (x < 0 || x >= surface->width || y < 0 || y >= surface->height)) { + return 0; + } + + uint32_t *src = (uint32_t *)(surface->data); + return src[y * surface->stride / 4 + x]; +} + +static inline uint32_t bilinear_interpolation(uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, + int distx, int disty) +{ + uint64_t distxy, distxiy, distixy, distixiy; + uint64_t tl64, tr64, bl64, br64; + uint64_t f, r; + + if (tl == tr && tl == bl && tl == br) + return tl; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = (uint64_t)distx * disty; + distxiy = (uint64_t)distx * (256 - disty); + distixy = (uint64_t)(256 - distx) * disty; + distixiy = (uint64_t)(256 - distx) * (256 - disty); + + /* Alpha and Blue */ + tl64 = tl & 0xff0000ff; + tr64 = tr & 0xff0000ff; + bl64 = bl & 0xff0000ff; + br64 = br & 0xff0000ff; + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r = f & 0x0000ff0000ff0000ull; + + /* Red and Green */ + tl64 = tl; + tl64 = ((tl64 << 16) & 0x000000ff00000000ull) | (tl64 & 0x0000ff00ull); + + tr64 = tr; + tr64 = ((tr64 << 16) & 0x000000ff00000000ull) | (tr64 & 0x0000ff00ull); + + bl64 = bl; + bl64 = ((bl64 << 16) & 0x000000ff00000000ull) | (bl64 & 0x0000ff00ull); + + br64 = br; + br64 = ((br64 << 16) & 0x000000ff00000000ull) | (br64 & 0x0000ff00ull); + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull); + + return (uint32_t)(r >> 16); +} + +static inline int _update_w_repeat(gdExtendMode repeat, int *c, int size) +{ + if (repeat == GD_EXTEND_NONE) { + if (*c < 0 || *c >= size) + return 0; + } else if (repeat == GD_EXTEND_REPEAT) { + while (*c >= size) + *c -= size; + while (*c < 0) + *c += size; + } else if (repeat == GD_EXTEND_PAD) { + *c = CLIP(*c, 0, size - 1); + } else /* REFLECT */ + { + *c = MOD(*c, size * 2); + if (*c >= size) + *c = size * 2 - *c - 1; + } + return 1; +} + +static inline uint32_t _surface_fetch_pixel_bilinear(gdSurfacePtr image, gd_fixed_t x, gd_fixed_t y, + gdExtendMode repeat_mode) +{ + int width = image->width; + int height = image->height; + int x1, y1, x2, y2; + uint32_t tl, tr, bl, br; + int32_t distx, disty; + + x1 = x - gd_fixed_1 / 2; + y1 = y - gd_fixed_1 / 2; + + distx = _fixed_to_bilinear_weight(x1); + disty = _fixed_to_bilinear_weight(y1); + x1 = gd_fixed_to_int(x1); + y1 = gd_fixed_to_int(y1); + x2 = x1 + 1; + y2 = y1 + 1; + if (repeat_mode != GD_EXTEND_NONE) { + _update_w_repeat(repeat_mode, &x1, width); + _update_w_repeat(repeat_mode, &y1, height); + _update_w_repeat(repeat_mode, &x2, width); + _update_w_repeat(repeat_mode, &y2, height); + + tl = fetch_pixel_general(image, x1, y1, 0); + bl = fetch_pixel_general(image, x1, y2, 0); + tr = fetch_pixel_general(image, x2, y1, 0); + br = fetch_pixel_general(image, x2, y2, 0); + } else { + tl = fetch_pixel_general(image, x1, y1, 1); + tr = fetch_pixel_general(image, x2, y1, 1); + bl = fetch_pixel_general(image, x1, y2, 1); + br = fetch_pixel_general(image, x2, y2, 1); + } + return bilinear_interpolation(tl, tr, bl, br, distx, disty); +} + +#define ALPHA(c) ((c) >> 24) +static void operator_argb_color_source(uint32_t *dest, int length, uint32_t color, uint32_t alpha) +{ + gdPremulPixelF src = gdCompositePixelFromArgb32(color); + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32(gdCompositePixel( + GD_OP_SOURCE, src, gdCompositePixelFromArgb32(dest[i]), alpha / 255.0f)); +} + +static void operator_argb_color_source_over(uint32_t *dest, int length, uint32_t color, + uint32_t const_alpha) +{ + gdPremulPixelF src = gdCompositePixelFromArgb32(color); + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32(gdCompositePixel( + GD_OP_OVER, src, gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void operator_argb_color_destination_in(uint32_t *dest, int length, uint32_t color, + uint32_t const_alpha) +{ + gdPremulPixelF src = gdCompositePixelFromArgb32(color); + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32(gdCompositePixel( + GD_OP_DEST_IN, src, gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void operator_argb_color_destination_out(uint32_t *dest, int length, uint32_t color, + uint32_t const_alpha) +{ + gdPremulPixelF src = gdCompositePixelFromArgb32(color); + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32(gdCompositePixel( + GD_OP_DEST_OUT, src, gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void operator_argb_color(gdCompositeOperator op, uint32_t *dest, int length, uint32_t color, + uint32_t alpha) +{ + gdPremulPixelF src = gdCompositePixelFromArgb32(color); + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32( + gdCompositePixel(op, src, gdCompositePixelFromArgb32(dest[i]), alpha / 255.0f)); +} + +#define spanBlendLoop(func) \ + while (count--) { \ + uint32_t *target = \ + (uint32_t *)(surface->data + currentSpan->y * surface->stride) + currentSpan->x; \ + func(target, currentSpan->len, color, currentSpan->coverage); \ + ++currentSpan; \ + }; + +static void argb32_blend_color(gdSurfacePtr surface, gdImageOp op, const gdSpanRlePtr rle, + uint32_t color) +{ + int count = rle->spans.size; + gdSpanPtr currentSpan = rle->spans.data; + switch (op) { + case gdImageOpsSrc: + spanBlendLoop(operator_argb_color_source); + break; + case gdImageOpsSrcOver: + spanBlendLoop(operator_argb_color_source_over); + break; + case gdImageOpsDstIn: + spanBlendLoop(operator_argb_color_destination_in); + break; + case gdImageOpsDstOut: + spanBlendLoop(operator_argb_color_destination_out); + break; + default: + while (count--) { + uint32_t *target = + (uint32_t *)(surface->data + currentSpan->y * surface->stride) + currentSpan->x; + operator_argb_color(op, target, currentSpan->len, color, currentSpan->coverage); + ++currentSpan; + } + break; + } +} + +static inline uint32_t premultiply_color(const gdColorPtr color, double opacity) +{ + const double a = color->a * opacity; + const uint32_t alpha = (uint8_t)floor(a * 255.0 + 0.5); + const uint32_t pr = (uint8_t)floor(color->r * a * 255.0 + 0.5); + const uint32_t pg = (uint8_t)floor(color->g * a * 255.0 + 0.5); + const uint32_t pb = (uint8_t)floor(color->b * a * 255.0 + 0.5); + + return (alpha << 24) | (pr << 16) | (pg << 8) | (pb); +} + +#define _getVarName(var) #var +void gdBlendColor(gdContextPtr context, const gdSpanRlePtr rle, const gdColorPtr color) +{ + if (color == NULL) + return; + + gdStatePtr state = context->state; + // replace once we have more than + switch (context->surface->type) { + case GD_SURFACE_ARGB32: { + uint32_t pm_color = premultiply_color(color, state->opacity); + argb32_blend_color(context->surface, state->op, rle, pm_color); + break; + } + case GD_SURFACE_XRGB32: + case GD_SURFACE_A8: + gd_error("gdDraw does not implement %s yet.\n", _getVarName(GD_SURFACE_XRGB32)); + break; + default: + gd_error("gdDraw: provided surface has an unknown type.\n"); + return; + } +} + +typedef struct { + gdPathMatrix matrix; + gdExtendMode extend; + uint8_t *data; + gdSurfacePtr surface; + int width; + int height; + int stride; + int alpha; +} _spans_pattern; + +// TODO: Once the rest is a tat bit faster, use func ptr for all but *_compose_source +// Macros are not an option, unreadable and painful to debug. +static void argb32_compose_source(uint32_t *dest, int length, const uint32_t *src, + uint32_t const_alpha) +{ + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32( + gdCompositePixel(GD_OP_SOURCE, gdCompositePixelFromArgb32(src[i]), + gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void argb32_compose_source_over(uint32_t *dest, int length, const uint32_t *src, + uint32_t const_alpha) +{ + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32( + gdCompositePixel(GD_OP_OVER, gdCompositePixelFromArgb32(src[i]), + gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void argb32_compose_dst_out(uint32_t *dest, int length, const uint32_t *src, + uint32_t const_alpha) +{ + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32( + gdCompositePixel(GD_OP_DEST_OUT, gdCompositePixelFromArgb32(src[i]), + gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void argb32_compose_dst_in(uint32_t *dest, int length, const uint32_t *src, + uint32_t const_alpha) +{ + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32( + gdCompositePixel(GD_OP_DEST_IN, gdCompositePixelFromArgb32(src[i]), + gdCompositePixelFromArgb32(dest[i]), const_alpha / 255.0f)); +} + +static void argb32_compose(gdCompositeOperator op, uint32_t *dest, int length, const uint32_t *src, + uint32_t const_alpha) +{ + for (int i = 0; i < length; i++) + dest[i] = gdCompositePixelToArgb32(gdCompositePixel(op, gdCompositePixelFromArgb32(src[i]), + gdCompositePixelFromArgb32(dest[i]), + const_alpha / 255.0f)); +} + +#define BUFFER_SIZE 1024 +static void render_spans_compose_source(const gdSurface *surface, const _spans_pattern *pattern, + uint32_t *buffer, const gdExtendMode extend, int fdx, + int fdy, int count, gdSpanPtr spans) +{ + while (count--) { + uint32_t *target = (uint32_t *)(surface->data + spans->y * surface->stride) + spans->x; + const double cx = spans->x + 0.5; + const double cy = spans->y + 0.5; + int x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + int y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + const int coverage = (spans->coverage * pattern->alpha + 127) / 255; + int length = spans->len; + while (length) { + int l = MIN(length, BUFFER_SIZE); + const uint32_t *end = buffer + l; + uint32_t *b = buffer; + while (b < end) { + *b = _surface_fetch_pixel_bilinear(pattern->surface, x, y, extend); + x += fdx; + y += fdy; + ++b; + } + argb32_compose_source(target, l, buffer, coverage); + target += l; + length -= l; + } + ++spans; + } +} + +static void render_spans_compose_source_over(const gdSurface *surface, + const _spans_pattern *pattern, uint32_t *buffer, + const gdExtendMode extend, int fdx, int fdy, int count, + gdSpanPtr spans) +{ + while (count--) { + uint32_t *target = (uint32_t *)(surface->data + spans->y * surface->stride) + spans->x; + const double cx = spans->x + 0.5; + const double cy = spans->y + 0.5; + int x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + int y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + const int coverage = (spans->coverage * pattern->alpha + 127) / 255; + int length = spans->len; + while (length) { + int l = MIN(length, BUFFER_SIZE); + const uint32_t *end = buffer + l; + uint32_t *b = buffer; + while (b < end) { + *b = _surface_fetch_pixel_bilinear(pattern->surface, x, y, extend); + x += fdx; + y += fdy; + ++b; + } + argb32_compose_source_over(target, l, buffer, coverage); + target += l; + length -= l; + } + ++spans; + } +} + +static void render_spans_compose_dst_in(const gdSurface *surface, const _spans_pattern *pattern, + uint32_t *buffer, const gdExtendMode extend, int fdx, + int fdy, int count, gdSpanPtr spans) +{ + while (count--) { + uint32_t *target = (uint32_t *)(surface->data + spans->y * surface->stride) + spans->x; + const double cx = spans->x + 0.5; + const double cy = spans->y + 0.5; + int x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + int y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + const int coverage = (spans->coverage * pattern->alpha + 127) / 255; + int length = spans->len; + while (length) { + int l = MIN(length, BUFFER_SIZE); + const uint32_t *end = buffer + l; + uint32_t *b = buffer; + while (b < end) { + *b = _surface_fetch_pixel_bilinear(pattern->surface, x, y, extend); + x += fdx; + y += fdy; + ++b; + } + argb32_compose_dst_in(target, l, buffer, coverage); + target += l; + length -= l; + } + ++spans; + } +} + +static void render_spans_compose_dst_out(const gdSurface *surface, const _spans_pattern *pattern, + uint32_t *buffer, const gdExtendMode extend, int fdx, + int fdy, int count, gdSpanPtr spans) +{ + while (count--) { + uint32_t *target = (uint32_t *)(surface->data + spans->y * surface->stride) + spans->x; + const double cx = spans->x + 0.5; + const double cy = spans->y + 0.5; + int x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + int y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + const int coverage = (spans->coverage * pattern->alpha + 127) / 255; + int length = spans->len; + while (length) { + int l = MIN(length, BUFFER_SIZE); + const uint32_t *end = buffer + l; + uint32_t *b = buffer; + while (b < end) { + *b = _surface_fetch_pixel_bilinear(pattern->surface, x, y, extend); + x += fdx; + y += fdy; + ++b; + } + argb32_compose_dst_out(target, l, buffer, coverage); + target += l; + length -= l; + } + ++spans; + } +} + +static void render_spans_compose(const gdSurface *surface, gdCompositeOperator op, + const _spans_pattern *pattern, uint32_t *buffer, + gdExtendMode extend, int fdx, int fdy, int count, gdSpanPtr spans) +{ + while (count--) { + uint32_t *target = (uint32_t *)(surface->data + spans->y * surface->stride) + spans->x; + const double cx = spans->x + 0.5; + const double cy = spans->y + 0.5; + int x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + int y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + int coverage = (spans->coverage * pattern->alpha + 127) / 255; + int length = spans->len; + while (length) { + int l = MIN(length, BUFFER_SIZE); + for (int i = 0; i < l; i++) { + buffer[i] = _surface_fetch_pixel_bilinear(pattern->surface, x, y, extend); + x += fdx; + y += fdy; + } + argb32_compose(op, target, l, buffer, coverage); + target += l; + length -= l; + } + ++spans; + } +} + +static void argb32_pattern_tiled_blend_transformed(gdSurfacePtr surface, gdImageOp op, + const gdSpanRlePtr rle, + const _spans_pattern *pattern) +{ + uint32_t buffer[BUFFER_SIZE]; + const gdExtendMode extend = pattern->extend; + int fdx = gd_double_to_fixed(pattern->matrix.m00); + int fdy = gd_double_to_fixed(pattern->matrix.m10); + int count = rle->spans.size; + gdSpanPtr spans = rle->spans.data; + switch (op) { + case gdImageOpsSrc: + render_spans_compose_source(surface, pattern, buffer, extend, fdx, fdy, count, spans); + break; + case gdImageOpsSrcOver: + render_spans_compose_source_over(surface, pattern, buffer, extend, fdx, fdy, count, spans); + break; + case gdImageOpsDstIn: + render_spans_compose_dst_in(surface, pattern, buffer, extend, fdx, fdy, count, spans); + break; + case gdImageOpsDstOut: + render_spans_compose_dst_out(surface, pattern, buffer, extend, fdx, fdy, count, spans); + break; + default: + render_spans_compose(surface, op, pattern, buffer, extend, fdx, fdy, count, spans); + break; + } +} + +#define spans_untransformed_blend_loop(composition) \ + while (count--) { \ + int x = spans->x; \ + int length = spans->len; \ + int sx = xoff + x; \ + int sy = yoff + spans->y; \ + if (sy >= 0 && sy < image_height && sx < image_width) { \ + if (sx < 0) { \ + x -= sx; \ + length += sx; \ + sx = 0; \ + } \ + if (sx + length > image_width) \ + length = image_width - sx; \ + if (length > 0) { \ + const int coverage = (spans->coverage * pattern->alpha + 127) / 255; \ + const uint32_t *src = \ + (const uint32_t *)(pattern->data + sy * pattern->stride) + sx; \ + uint32_t *dest = (uint32_t *)(surface->data + spans->y * surface->stride) + x; \ + composition(dest, length, src, coverage); \ + } \ + } \ + ++spans; \ + } + +static void argb32_pattern_blend_untransformed(gdSurfacePtr surface, gdImageOp op, + const gdSpanRlePtr rle, + const _spans_pattern *pattern) +{ + const int image_width = pattern->width; + const int image_height = pattern->height; + int xoff = (int)(pattern->matrix.m02); + int yoff = (int)(pattern->matrix.m12); + + int count = rle->spans.size; + gdSpanPtr spans = rle->spans.data; + + switch (op) { + case gdImageOpsSrc: + spans_untransformed_blend_loop(argb32_compose_source); + break; + case gdImageOpsSrcOver: + spans_untransformed_blend_loop(argb32_compose_source_over); + break; + case gdImageOpsDstIn: + spans_untransformed_blend_loop(argb32_compose_dst_in); + break; + case gdImageOpsDstOut: + spans_untransformed_blend_loop(argb32_compose_dst_out); + break; + default: + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = xoff + x; + int sy = yoff + spans->y; + if (sy >= 0 && sy < image_height && sx < image_width) { + if (sx < 0) { + x -= sx; + length += sx; + sx = 0; + } + if (sx + length > image_width) + length = image_width - sx; + if (length > 0) { + int coverage = (spans->coverage * pattern->alpha + 127) / 255; + const uint32_t *src = + (const uint32_t *)(pattern->data + sy * pattern->stride) + sx; + uint32_t *dest = (uint32_t *)(surface->data + spans->y * surface->stride) + x; + argb32_compose(op, dest, length, src, coverage); + } + } + ++spans; + } + } +} + +void gdDrawBlendPattern(gdContextPtr context, const gdSpanRlePtr rle, + const gdPathPatternPtr pattern) +{ + if (pattern == NULL) + return; + + gdStatePtr state = context->state; + _spans_pattern pattern_impl; + pattern_impl.extend = pattern->extend; + pattern_impl.data = pattern->surface->data; + pattern_impl.surface = pattern->surface; + pattern_impl.width = pattern->surface->width; + pattern_impl.height = pattern->surface->height; + pattern_impl.stride = pattern->surface->stride; + pattern_impl.alpha = (int)(state->opacity * pattern->opacity * 255.0 + 0.5); + + pattern_impl.matrix = pattern->matrix; + gdPathMatrixMultiply(&pattern_impl.matrix, &pattern_impl.matrix, &state->matrix); + gdPathMatrixInvert(&pattern_impl.matrix); + + const gdPathMatrixPtr matrix = &pattern_impl.matrix; + int translating = + (matrix->m00 == 1.0 && matrix->m10 == 0.0 && matrix->m01 == 0.0 && matrix->m11 == 1.0); + if (translating) { + if (pattern->extend == GD_EXTEND_NONE) + argb32_pattern_blend_untransformed(context->surface, state->op, rle, &pattern_impl); + else + argb32_pattern_tiled_blend_transformed(context->surface, state->op, rle, &pattern_impl); + } else { + if (pattern->extend == GD_EXTEND_NONE) + // argb32_pattern_blend_untransformed(context->surface, state->op, rle, &pattern_impl); + argb32_pattern_tiled_blend_transformed(context->surface, state->op, rle, &pattern_impl); + else + argb32_pattern_tiled_blend_transformed(context->surface, state->op, rle, &pattern_impl); + } +} + +static unsigned int rle_coverage_at(const gdSpanRlePtr rle, int x, int y, int *cursor) +{ + if (!rle) + return 255; + while (*cursor < rle->spans.size) { + const gdSpanPtr s = &rle->spans.data[*cursor]; + if (s->y < y || (s->y == y && s->x + s->len <= x)) { + (*cursor)++; + continue; + } + if (s->y == y && x >= s->x && x < s->x + s->len) + return s->coverage; + break; + } + return 0; +} + +static gdPremulPixelF pattern_pixel_at(const _spans_pattern *pattern, int px, int py) +{ + double cx = px + 0.5; + double cy = py + 0.5; + gd_fixed_t x = gd_double_to_fixed(pattern->matrix.m01 * cy + pattern->matrix.m00 * cx + + pattern->matrix.m02); + gd_fixed_t y = gd_double_to_fixed(pattern->matrix.m11 * cy + pattern->matrix.m10 * cx + + pattern->matrix.m12); + return gdCompositePixelFromArgb32( + _surface_fetch_pixel_bilinear(pattern->surface, x, y, pattern->extend)); +} + +typedef struct { + const gdGradient *gradient; + gdPathMatrix device_to_pattern; + int valid; +} gdGradientSampler; + +static void gradient_sampler_init(gdGradientSampler *s, const gdGradient *g, const gdStatePtr state) +{ + s->gradient = g; + s->device_to_pattern = g->matrix; + gdPathMatrixMultiply(&s->device_to_pattern, &s->device_to_pattern, &state->matrix); + s->valid = gdPathMatrixInvert(&s->device_to_pattern); +} + +static gdPremulPixelF gradient_pixel_at(const gdGradientSampler *s, int x, int y) +{ + gdPremulPixelF z = {0, 0, 0, 0}; + return s->valid ? gdGradientSample(s->gradient, &s->device_to_pattern, x + 0.5, y + 0.5) : z; +} + +static void gdDrawBlendGradient(gdContextPtr context, const gdSpanRlePtr rle, + const gdGradient *gradient) +{ + gdGradientSampler sampler; + gdStatePtr state = context->state; + int count = rle->spans.size; + gdSpanPtr span = rle->spans.data; + gradient_sampler_init(&sampler, gradient, state); + while (count--) { + uint32_t *dst = + (uint32_t *)(context->surface->data + span->y * context->surface->stride) + span->x; + float coverage = (float)(span->coverage / 255.0 * state->opacity); + for (int i = 0; i < span->len; i++) { + gdPremulPixelF src = gradient_pixel_at(&sampler, span->x + i, span->y); + gdPremulPixelF old = gdCompositePixelFromArgb32(dst[i]); + dst[i] = gdCompositePixelToArgb32(gdCompositePixel(state->op, src, old, coverage)); + } + span++; + } +} + +static gdPremulPixelF lerp_clip(gdPremulPixelF dst, gdPremulPixelF result, float coverage) +{ + gdPremulPixelF p; + p.r = dst.r + (result.r - dst.r) * coverage; + p.g = dst.g + (result.g - dst.g) * coverage; + p.b = dst.b + (result.b - dst.b) * coverage; + p.a = dst.a + (result.a - dst.a) * coverage; + return p; +} + +static void blend_unbounded(gdContextPtr context, const gdSpanRlePtr shape) +{ + gdStatePtr state = context->state; + gdPaintPtr source = state->source; + gdSpanRlePtr clip_path = state->clippath; + gdPremulPixelF solid = {0, 0, 0, 0}; + _spans_pattern pattern; + gdGradientSampler gradient; + float paint_opacity = (float)state->opacity; + int shape_cursor = 0, clip_cursor = 0; + int x0 = MAX(0, (int)floor(context->clip.x)); + int y0 = MAX(0, (int)floor(context->clip.y)); + int x1 = MIN(context->surface->width, (int)ceil(context->clip.x + context->clip.w)); + int y1 = MIN(context->surface->height, (int)ceil(context->clip.y + context->clip.h)); + + if (source->type == gdPaintTypeColor) { + solid = gdCompositePixelFromArgb32(premultiply_color(source->color, 1.0)); + } else if (source->type == gdPaintTypePattern) { + gdPathPatternPtr p = source->pattern; + pattern.extend = p->extend; + pattern.surface = p->surface; + pattern.matrix = p->matrix; + gdPathMatrixMultiply(&pattern.matrix, &pattern.matrix, &state->matrix); + gdPathMatrixInvert(&pattern.matrix); + paint_opacity *= (float)p->opacity; + } else if (source->type == gdPaintTypeGradient) { + gradient_sampler_init(&gradient, source->gradient, state); + } else { + gd_error("Paint method not implemented or does not exist."); + return; + } + + for (int y = y0; y < y1; y++) { + uint32_t *row = (uint32_t *)(context->surface->data + y * context->surface->stride); + for (int x = x0; x < x1; x++) { + float mask = rle_coverage_at(shape, x, y, &shape_cursor) / 255.0f; + float clip = clip_path ? rle_coverage_at(clip_path, x, y, &clip_cursor) / 255.0f : 1.0f; + gdPremulPixelF src = + source->type == gdPaintTypeColor + ? solid + : (source->type == gdPaintTypePattern ? pattern_pixel_at(&pattern, x, y) + : gradient_pixel_at(&gradient, x, y)); + gdPremulPixelF dst = gdCompositePixelFromArgb32(row[x]); + gdPremulPixelF result; + if (clip == 0.0f) + continue; + result = gdCompositePixel(state->op, src, dst, mask * paint_opacity); + if (clip < 1.0f) + result = lerp_clip(dst, result, clip); + row[x] = gdCompositePixelToArgb32(result); + } + } +} + +void gdPathBlend(gdContextPtr context, const gdSpanRlePtr rle) +{ + if (rle == NULL) + return; + + if (gdCompositeOperatorIsUnbounded(context->state->op) && rle == context->rle) { + blend_unbounded(context, rle); + return; + } + if (rle->spans.size == 0 || context->state->opacity == 0.0) + return; + + gdPaintPtr source = context->state->source; + switch (source->type) { + case gdPaintTypeColor: + gdBlendColor(context, rle, source->color); + break; + case gdPaintTypePattern: + gdDrawBlendPattern(context, rle, source->pattern); + break; + case gdPaintTypeGradient: + gdDrawBlendGradient(context, rle, source->gradient); + break; + case gdPaintTypeSurface: + default: + gd_error("Paint method not implemented or does not exist."); + break; + } +} diff --git a/ext/gd/libgd/gd_draw_blend.h b/ext/gd/libgd/gd_draw_blend.h new file mode 100644 index 000000000000..720bcb054e28 --- /dev/null +++ b/ext/gd/libgd/gd_draw_blend.h @@ -0,0 +1,7 @@ +#ifndef GD_DRAW_BLEND_H +#define GD_DRAW_BLEND_H + +void gdBlendColor(gdContextPtr context, const gdSpanRlePtr rle, const gdColorPtr color); +void gdPathBlend(gdContextPtr context, const gdSpanRlePtr rle); + +#endif // GD_DRAW_BLEND_H diff --git a/ext/gd/libgd/gd_errors.h b/ext/gd/libgd/gd_errors.h index 8a28bf605197..fb8a470e0da1 100644 --- a/ext/gd/libgd/gd_errors.h +++ b/ext/gd/libgd/gd_errors.h @@ -2,9 +2,9 @@ #define GD_ERRORS_H #ifndef _WIN32 -# include +#include #else -# include "win32/syslog.h" +#include "win32/syslog.h" #endif /* diff --git a/ext/gd/libgd/gd_filename.c b/ext/gd/libgd/gd_filename.c new file mode 100644 index 000000000000..9de4dc470a08 --- /dev/null +++ b/ext/gd/libgd/gd_filename.c @@ -0,0 +1,278 @@ +/* Convenience functions to read or write images from or to disk, + * determining file type from the filename extension. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gd.h" +#include "gd_intern.h" + +typedef gdImagePtr(BGD_STDCALL *ReadFn)(FILE *in); +typedef void(BGD_STDCALL *WriteFn)(gdImagePtr im, FILE *out); +typedef gdImagePtr(BGD_STDCALL *LoadFn)(char *filename); + +#ifdef HAVE_LIBZ +static void BGD_STDCALL writegd2(gdImagePtr im, FILE *out) +{ + gdImageGd2(im, out, 0, GD2_FMT_COMPRESSED); +} /* writegd*/ +#endif + +#ifdef HAVE_LIBJPEG +static void BGD_STDCALL writejpeg(gdImagePtr im, FILE *out) +{ + gdImageJpeg(im, out, -1); +} /* writejpeg*/ +#endif + +static void BGD_STDCALL writewbmp(gdImagePtr im, FILE *out) +{ + int fg = gdImageColorClosest(im, 0, 0, 0); + + gdImageWBMP(im, fg, out); +} /* writejpeg*/ + +static void BGD_STDCALL writebmp(gdImagePtr im, FILE *out) +{ + gdImageBmp(im, out, GD_TRUE); +} /* writejpeg*/ + +static const struct FileType { + const char *ext; + ReadFn reader; + WriteFn writer; + LoadFn loader; +} Types[] = {{".gif", gdImageCreateFromGif, gdImageGif, NULL}, + {".gd", gdImageCreateFromGd, gdImageGd, NULL}, + {".wbmp", gdImageCreateFromWBMP, writewbmp, NULL}, + {".bmp", gdImageCreateFromBmp, writebmp, NULL}, + + {".xbm", gdImageCreateFromXbm, NULL, NULL}, + {".tga", gdImageCreateFromTga, NULL, NULL}, + +#ifdef HAVE_LIBAVIF + {".avif", gdImageCreateFromAvif, gdImageAvif, NULL}, +#endif + +#ifdef HAVE_LIBPNG + {".png", gdImageCreateFromPng, gdImagePng, NULL}, +#endif + + {".qoi", gdImageCreateFromQoi, gdImageQoi, NULL}, + +#ifdef HAVE_LIBJPEG + {".jpg", gdImageCreateFromJpeg, writejpeg, NULL}, + {".jpeg", gdImageCreateFromJpeg, writejpeg, NULL}, +#endif + +#ifdef HAVE_LIBHEIF + {".heic", gdImageCreateFromHeif, gdImageHeif, NULL}, + {".heix", gdImageCreateFromHeif, NULL, NULL}, +#endif + +#ifdef HAVE_LIBTIFF + {".tiff", gdImageCreateFromTiff, gdImageTiff, NULL}, + {".tif", gdImageCreateFromTiff, gdImageTiff, NULL}, +#endif + +#ifdef HAVE_LIBZ + {".gd2", gdImageCreateFromGd2, writegd2, NULL}, +#endif + +#ifdef HAVE_LIBWEBP + {".webp", gdImageCreateFromWebp, gdImageWebp, NULL}, +#endif + +#ifdef HAVE_LIBJXL + {".jxl", gdImageCreateFromJxl, gdImageJxl, NULL}, +#endif + +#ifdef HAVE_LIBXPM + {".xpm", NULL, NULL, gdImageCreateFromXpm}, +#endif + + {NULL, NULL, NULL, NULL}}; + +static const struct FileType *ftype(const char *filename) +{ + int n; + const char *ext; + + /* Find the file extension (i.e. the last period in the string. */ + ext = strrchr(filename, '.'); + if (!ext) + return NULL; + + for (n = 0; Types[n].ext; n++) { + if (gd_strcasecmp(ext, Types[n].ext) == 0) { + return &Types[n]; + } /* if */ + } /* for */ + + return NULL; +} /* ftype*/ + +/* + Function: gdSupportsFileType + + Tests if a given file type is supported by GD. + + Given the name of an image file (which does not have to exist), + returns 1 (i.e. TRUE) if can read a file + of that type. This is useful if you do not know which image types + were enabled at compile time. + + If _writing_ is true, the result will be true only if + can write a file of this type. + + Note that filename parsing is done exactly the same as is done by + and and is subject to the + same limitations. + + Assuming LibGD is compiled with support for these image types, the + following extensions are supported: + + - .gif + - .gd, .gd2 + - .wbmp + - .bmp + - .xbm + - .tga + - .png + - .qoi + - .jpg, .jpeg + - .heif, .heix + - .avif + - .tiff, .tif + - .webp + - .jxl + - .xpm + + Names are parsed case-insenstively. + + Parameters: + + filename - Filename with tested extension. + writing - Flag: true tests if writing works + + Returns: + + GD_TRUE (1) if the file type is supported, GD_FALSE (0) if not. + +*/ +BGD_DECLARE(int) +gdSupportsFileType(const char *filename, int writing) +{ + const struct FileType *entry = ftype(filename); + return !!entry && (!writing || !!entry->writer); +} /* gdSupportsFileType*/ + +/* + Function: gdImageCreateFromFile + + Read an image file of any supported. + + Given the path to a file, will open the + file, read its contents with the appropriate _gdImageCreateFrom*_ + function and return it. + + File type is determined by the filename extension, so having an + incorrect extension will probably not work. For example, renaming + PNG image "foo.png" to "foo.gif" and then attempting to load it + will fail even if GD supports both formats. See + for more details. + + NULL is returned on error. + + Parameters: + + filename - the input file name + + Returns: + + A pointer to the new image or NULL if an error occurred. + +*/ + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromFile(const char *filename) +{ + const struct FileType *entry = ftype(filename); + FILE *fh; + gdImagePtr result; + + if (!entry) + return NULL; + if (entry->loader) + return entry->loader((char *)filename); + if (!entry->reader) + return NULL; + + fh = fopen(filename, "rb"); + if (!fh) + return NULL; + + result = entry->reader(fh); + + fclose(fh); + + return result; +} /* gdImageCreateFromFile*/ + +/* + Function: gdImageFile + + Writes an image to a file in the format indicated by the filename. + + File type is determined by the extension of the file name. See + for an overview of the parsing. + + For file types that require extra arguments, + attempts to use sane defaults: + + - chunk size = 0, compression is enabled. + - quality = -1 (i.e. the reasonable default) + - foreground is the darkest available color + + Everything else is called with the two-argument function and so + will use the default values. + + has some rudimentary error detection and will return + GD_FALSE (0) if a detectable error occurred. However, the image + loaders do not normally return their error status so a result of + GD_TRUE (1) does **not** mean the file was saved successfully. + + Parameters: + + im - The image to save. + filename - The path to the file to which the image is saved. + + Returns: + + GD_FALSE (0) if an error was detected, GD_TRUE (1) if not. + +*/ + +BGD_DECLARE(int) +gdImageFile(gdImagePtr im, const char *filename) +{ + const struct FileType *entry = ftype(filename); + FILE *fh; + + if (!entry || !entry->writer) + return GD_FALSE; + + fh = fopen(filename, "wb"); + if (!fh) + return GD_FALSE; + + entry->writer(im, fh); + + fclose(fh); + + return GD_TRUE; +} /* gdImageFile*/ diff --git a/ext/gd/libgd/gd_filter.c b/ext/gd/libgd/gd_filter.c index 97dea5c552cd..7fe9b6b8f5e8 100644 --- a/ext/gd/libgd/gd_filter.c +++ b/ext/gd/libgd/gd_filter.c @@ -1,639 +1,1056 @@ +/** + * File: Image Filters + */ + #include "gd.h" #include "gd_intern.h" +#include "gdhelpers.h" #ifdef _WIN32 -# include +#include #else -# include +#include #endif #include #include #include -/* Filters function added on 2003/12 - * by Pierre-Alain Joye (pierre@php.net) - * - * Scatter filter added in libgd 2.1.0 - * by Kalle Sommer Nielsen (kalle@php.net) - **/ +#undef NDEBUG +/* Comment out this line to enable asserts. + * TODO: This logic really belongs in cmake and configure. + */ +#define NDEBUG 1 +#include -/* Begin filters function */ -#define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel) +typedef int(BGD_STDCALL *FuncPtr)(gdImagePtr, int, int); + +#define GET_PIXEL_FUNCTION(src) (src->trueColor ? gdImageGetTrueColorPixel : gdImageGetPixel) static int gdClampFloatToByte(float value) { - if (!isfinite(value)) { - return value > 0.0f ? 255 : 0; - } - if (value > 255.0f) { - return 255; - } - if (value < 0.0f) { - return 0; - } - return (int)value; + if (!isfinite(value)) { + return value > 0.0f ? 255 : 0; + } + if (value > 255.0f) { + return 255; + } + if (value < 0.0f) { + return 0; + } + return (int)value; } #ifdef _WIN32 -# define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId()) +#define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId()) #else -# define GD_SCATTER_SEED() (unsigned int)(time(0) * getpid()) +#define GD_SCATTER_SEED() (unsigned int)(time(0) * getpid()) #endif -int gdImageScatter(gdImagePtr im, int sub, int plus) +/* + Function: gdImageScatter + */ +BGD_DECLARE(int) gdImageScatter(gdImagePtr im, int sub, int plus) { - gdScatter s; + gdScatter s; - s.sub = sub; - s.plus = plus; - s.num_colors = 0; - s.seed = GD_SCATTER_SEED(); - return gdImageScatterEx(im, &s); + s.sub = sub; + s.plus = plus; + s.num_colors = 0; + s.seed = GD_SCATTER_SEED(); + return gdImageScatterEx(im, &s); } -int gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors) +/* + Function: gdImageScatterColor + */ +BGD_DECLARE(int) +gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors) { - gdScatter s; - - s.sub = sub; - s.plus = plus; - s.colors = colors; - s.num_colors = num_colors; - s.seed = GD_SCATTER_SEED(); - return gdImageScatterEx(im, &s); + gdScatter s; + + s.sub = sub; + s.plus = plus; + s.colors = colors; + s.num_colors = num_colors; + s.seed = GD_SCATTER_SEED(); + return gdImageScatterEx(im, &s); } -int gdImageScatterEx(gdImagePtr im, gdScatterPtr scatter) +/* + Function: gdImageScatterEx + */ +BGD_DECLARE(int) gdImageScatterEx(gdImagePtr im, gdScatterPtr scatter) { - register int x, y; - int dest_x, dest_y; - int pxl, new_pxl; - unsigned int n; - int sub = scatter->sub, plus = scatter->plus; - - if (plus == 0 && sub == 0) { - return 1; - } - else if (sub >= plus) { - return 0; - } - - (void)srand(scatter->seed); - - if (scatter->num_colors) { - for (y = 0; y < im->sy; y++) { - for (x = 0; x < im->sx; x++) { - dest_x = (int)(x + ((rand() % (plus - sub)) + sub)); - dest_y = (int)(y + ((rand() % (plus - sub)) + sub)); - - if (!gdImageBoundsSafe(im, dest_x, dest_y)) { - continue; - } - - pxl = gdImageGetPixel(im, x, y); - new_pxl = gdImageGetPixel(im, dest_x, dest_y); - - for (n = 0; n < scatter->num_colors; n++) { - if (pxl == scatter->colors[n]) { - gdImageSetPixel(im, dest_x, dest_y, pxl); - gdImageSetPixel(im, x, y, new_pxl); - } - } - } - } - } - else { - for (y = 0; y < im->sy; y++) { - for (x = 0; x < im->sx; x++) { - dest_x = (int)(x + ((rand() % (plus - sub)) + sub)); - dest_y = (int)(y + ((rand() % (plus - sub)) + sub)); - - if (!gdImageBoundsSafe(im, dest_x, dest_y)) { - continue; - } - - pxl = gdImageGetPixel(im, x, y); - new_pxl = gdImageGetPixel(im, dest_x, dest_y); - - gdImageSetPixel(im, dest_x, dest_y, pxl); - gdImageSetPixel(im, x, y, new_pxl); - } - } - } - - return 1; + register int x, y; + int dest_x, dest_y; + int pxl, new_pxl; + unsigned int n; + int sub = scatter->sub, plus = scatter->plus; + + if (plus == 0 && sub == 0) { + return 1; + } else if (sub >= plus) { + return 0; + } + + (void)srand(scatter->seed); + + if (scatter->num_colors) { + for (y = 0; y < im->sy; y++) { + for (x = 0; x < im->sx; x++) { + dest_x = (int)(x + ((rand() % (plus - sub)) + sub)); + dest_y = (int)(y + ((rand() % (plus - sub)) + sub)); + + if (!gdImageBoundsSafe(im, dest_x, dest_y)) { + continue; + } + + pxl = gdImageGetPixel(im, x, y); + new_pxl = gdImageGetPixel(im, dest_x, dest_y); + + for (n = 0; n < scatter->num_colors; n++) { + if (pxl == scatter->colors[n]) { + gdImageSetPixel(im, dest_x, dest_y, pxl); + gdImageSetPixel(im, x, y, new_pxl); + } + } + } + } + } else { + for (y = 0; y < im->sy; y++) { + for (x = 0; x < im->sx; x++) { + dest_x = (int)(x + ((rand() % (plus - sub)) + sub)); + dest_y = (int)(y + ((rand() % (plus - sub)) + sub)); + + if (!gdImageBoundsSafe(im, dest_x, dest_y)) { + continue; + } + + pxl = gdImageGetPixel(im, x, y); + new_pxl = gdImageGetPixel(im, dest_x, dest_y); + + gdImageSetPixel(im, dest_x, dest_y, pxl); + gdImageSetPixel(im, x, y, new_pxl); + } + } + } + + return 1; } -int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode) +/* + Function: gdImagePixelate + */ +BGD_DECLARE(int) +gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode) { - int x, y; - - if (block_size <= 0) { - return 0; - } else if (block_size == 1) { - return 1; - } - switch (mode) { - case GD_PIXELATE_UPPERLEFT: - for (y = 0; y < im->sy; y += block_size) { - for (x = 0; x < im->sx; x += block_size) { - if (gdImageBoundsSafe(im, x, y)) { - int c = gdImageGetPixel(im, x, y); - gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c); - } - } - } - break; - case GD_PIXELATE_AVERAGE: - for (y = 0; y < im->sy; y += block_size) { - for (x = 0; x < im->sx; x += block_size) { - int a, r, g, b, c; - int total; - int cx, cy; - - a = r = g = b = c = total = 0; - /* sampling */ - for (cy = 0; cy < block_size; cy++) { - for (cx = 0; cx < block_size; cx++) { - if (!gdImageBoundsSafe(im, x + cx, y + cy)) { - continue; - } - c = gdImageGetPixel(im, x + cx, y + cy); - a += gdImageAlpha(im, c); - r += gdImageRed(im, c); - g += gdImageGreen(im, c); - b += gdImageBlue(im, c); - total++; - } - } - /* drawing */ - if (total > 0) { - c = gdImageColorResolveAlpha(im, r / total, g / total, b / total, a / total); - gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c); - } - } - } - break; - default: - return 0; - } - return 1; + int x, y; + + if (block_size <= 0) { + return 0; + } else if (block_size == 1) { + return 1; + } + switch (mode) { + case GD_PIXELATE_UPPERLEFT: + for (y = 0; y < im->sy; y += block_size) { + for (x = 0; x < im->sx; x += block_size) { + if (gdImageBoundsSafe(im, x, y)) { + int c = gdImageGetPixel(im, x, y); + gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c); + } + } + } + break; + case GD_PIXELATE_AVERAGE: + for (y = 0; y < im->sy; y += block_size) { + for (x = 0; x < im->sx; x += block_size) { + int a, r, g, b, c; + int total; + int cx, cy; + + a = r = g = b = c = total = 0; + /* sampling */ + for (cy = 0; cy < block_size; cy++) { + for (cx = 0; cx < block_size; cx++) { + if (!gdImageBoundsSafe(im, x + cx, y + cy)) { + continue; + } + c = gdImageGetPixel(im, x + cx, y + cy); + a += gdImageAlpha(im, c); + r += gdImageRed(im, c); + g += gdImageGreen(im, c); + b += gdImageBlue(im, c); + total++; + } + } + /* drawing */ + if (total > 0) { + c = gdImageColorResolveAlpha(im, r / total, g / total, b / total, a / total); + gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c); + } + } + } + break; + default: + return 0; + } + return 1; } -/* invert src image */ -int gdImageNegate(gdImagePtr src) +/** + * Function: gdImageNegate + * + * Invert an image + * + * Parameters: + * src - The image. + * + * Returns: + * Non-zero on success, zero on failure. + */ +BGD_DECLARE(int) gdImageNegate(gdImagePtr src) { - int x, y; - int r,g,b,a; - int new_pxl, pxl; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src==NULL) { - return 0; - } - - f = GET_PIXEL_FUNCTION(src); - - for (y=0; ysy; ++y) { - for (x=0; xsx; ++x) { - pxl = f (src, x, y); - r = gdImageRed(src, pxl); - g = gdImageGreen(src, pxl); - b = gdImageBlue(src, pxl); - a = gdImageAlpha(src, pxl); - - new_pxl = gdImageColorAllocateAlpha(src, 255-r, 255-g, 255-b, a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, 255-r, 255-g, 255-b, a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - return 1; + int x, y; + int r, g, b, a; + int new_pxl, pxl; + FuncPtr f; + + if (src == NULL) { + return 0; + } + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; ++y) { + for (x = 0; x < src->sx; ++x) { + pxl = f(src, x, y); + r = gdImageRed(src, pxl); + g = gdImageGreen(src, pxl); + b = gdImageBlue(src, pxl); + a = gdImageAlpha(src, pxl); + + new_pxl = gdImageColorAllocateAlpha(src, 255 - r, 255 - g, 255 - b, a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, 255 - r, 255 - g, 255 - b, a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + return 1; } -/* Convert the image src to a grayscale image */ -int gdImageGrayScale(gdImagePtr src) +/** + * Function: gdImageGrayScale + * + * Convert an image to grayscale + * + * The red, green and blue components of each pixel are replaced by their + * weighted sum using the same coefficients as the REC.601 luma (Y') + * calculation. The alpha components are retained. + * + * For palette images the result may differ due to palette limitations. + * + * Parameters: + * src - The image. + * + * Returns: + * Non-zero on success, zero on failure. + */ +BGD_DECLARE(int) gdImageGrayScale(gdImagePtr src) { - int x, y; - int r,g,b,a; - int new_pxl, pxl; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - int alpha_blending; - - f = GET_PIXEL_FUNCTION(src); - - if (src==NULL) { - return 0; - } - - alpha_blending = src->alphaBlendingFlag; - gdImageAlphaBlending(src, gdEffectReplace); - - for (y=0; ysy; ++y) { - for (x=0; xsx; ++x) { - pxl = f (src, x, y); - r = gdImageRed(src, pxl); - g = gdImageGreen(src, pxl); - b = gdImageBlue(src, pxl); - a = gdImageAlpha(src, pxl); - r = g = b = (int) (.299 * r + .587 * g + .114 * b); - - new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, r, g, b, a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - gdImageAlphaBlending(src, alpha_blending); - - return 1; + int x, y; + int r, g, b, a; + int new_pxl, pxl; + FuncPtr f; + int alpha_blending; + + if (src == NULL) { + return 0; + } + + alpha_blending = src->alphaBlendingFlag; + gdImageAlphaBlending(src, gdEffectReplace); + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; ++y) { + for (x = 0; x < src->sx; ++x) { + pxl = f(src, x, y); + r = gdImageRed(src, pxl); + g = gdImageGreen(src, pxl); + b = gdImageBlue(src, pxl); + a = gdImageAlpha(src, pxl); + r = g = b = (int)(.299 * r + .587 * g + .114 * b); + + new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, r, g, b, a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + gdImageAlphaBlending(src, alpha_blending); + + return 1; } -/* Set the brightness level for the image src */ -int gdImageBrightness(gdImagePtr src, int brightness) +/** + * Function: gdImageBrightness + * + * Change the brightness of an image + * + * Parameters: + * src - The image. + * brightness - The value to add to the color channels of all pixels. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) gdImageBrightness(gdImagePtr src, int brightness) { - int x, y; - int r,g,b,a; - int new_pxl, pxl; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - f = GET_PIXEL_FUNCTION(src); - - if (src==NULL || (brightness < -255 || brightness>255)) { - return 0; - } - - if (brightness==0) { - return 1; - } - - for (y=0; ysy; ++y) { - for (x=0; xsx; ++x) { - pxl = f (src, x, y); - - r = gdImageRed(src, pxl); - g = gdImageGreen(src, pxl); - b = gdImageBlue(src, pxl); - a = gdImageAlpha(src, pxl); - - r = r + brightness; - g = g + brightness; - b = b + brightness; - - r = (r > 255)? 255 : ((r < 0)? 0:r); - g = (g > 255)? 255 : ((g < 0)? 0:g); - b = (b > 255)? 255 : ((b < 0)? 0:b); - - new_pxl = gdImageColorAllocateAlpha(src, (int)r, (int)g, (int)b, a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, (int)r, (int)g, (int)b, a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - return 1; + int x, y; + int r, g, b, a; + int new_pxl, pxl; + FuncPtr f; + + if (src == NULL || (brightness < -255 || brightness > 255)) { + return 0; + } + + if (brightness == 0) { + return 1; + } + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; ++y) { + for (x = 0; x < src->sx; ++x) { + pxl = f(src, x, y); + + r = gdImageRed(src, pxl); + g = gdImageGreen(src, pxl); + b = gdImageBlue(src, pxl); + a = gdImageAlpha(src, pxl); + + r = r + brightness; + g = g + brightness; + b = b + brightness; + + r = (r > 255) ? 255 : ((r < 0) ? 0 : r); + g = (g > 255) ? 255 : ((g < 0) ? 0 : g); + b = (b > 255) ? 255 : ((b < 0) ? 0 : b); + + new_pxl = gdImageColorAllocateAlpha(src, (int)r, (int)g, (int)b, a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, (int)r, (int)g, (int)b, a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + return 1; } - -int gdImageContrast(gdImagePtr src, double contrast) +/** + * Function: gdImageContrast + * + * Change the contrast of an image + * + * Parameters: + * src - The image. + * contrast - The contrast adjustment value. Negative values increase, postive + * values decrease the contrast. The larger the absolute value, the + * stronger the effect. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImageContrast(gdImagePtr src, double contrast) { - int x, y; - int r,g,b,a; - double rf,gf,bf; - int new_pxl, pxl; - typedef int (*FuncPtr)(gdImagePtr, int, int); - - FuncPtr f; - f = GET_PIXEL_FUNCTION(src); - - if (src==NULL) { - return 0; - } - - contrast = (double)(100.0-contrast)/100.0; - contrast = contrast*contrast; - - for (y=0; ysy; ++y) { - for (x=0; xsx; ++x) { - pxl = f(src, x, y); - - r = gdImageRed(src, pxl); - g = gdImageGreen(src, pxl); - b = gdImageBlue(src, pxl); - a = gdImageAlpha(src, pxl); - - rf = (double)r/255.0; - rf = rf-0.5; - rf = rf*contrast; - rf = rf+0.5; - rf = rf*255.0; - - bf = (double)b/255.0; - bf = bf-0.5; - bf = bf*contrast; - bf = bf+0.5; - bf = bf*255.0; - - gf = (double)g/255.0; - gf = gf-0.5; - gf = gf*contrast; - gf = gf+0.5; - gf = gf*255.0; - - rf = (rf > 255.0)? 255.0 : ((rf < 0.0)? 0.0:rf); - gf = (gf > 255.0)? 255.0 : ((gf < 0.0)? 0.0:gf); - bf = (bf > 255.0)? 255.0 : ((bf < 0.0)? 0.0:bf); - - new_pxl = gdImageColorAllocateAlpha(src, (int)rf, (int)gf, (int)bf, a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, (int)rf, (int)gf, (int)bf, a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - return 1; + int x, y; + int r, g, b, a; + double rf, gf, bf; + int new_pxl, pxl; + + FuncPtr f; + + if (src == NULL) { + return 0; + } + + f = GET_PIXEL_FUNCTION(src); + + contrast = (double)(100.0 - contrast) / 100.0; + contrast = contrast * contrast; + + for (y = 0; y < src->sy; ++y) { + for (x = 0; x < src->sx; ++x) { + pxl = f(src, x, y); + + r = gdImageRed(src, pxl); + g = gdImageGreen(src, pxl); + b = gdImageBlue(src, pxl); + a = gdImageAlpha(src, pxl); + + rf = (double)r / 255.0; + rf = rf - 0.5; + rf = rf * contrast; + rf = rf + 0.5; + rf = rf * 255.0; + + bf = (double)b / 255.0; + bf = bf - 0.5; + bf = bf * contrast; + bf = bf + 0.5; + bf = bf * 255.0; + + gf = (double)g / 255.0; + gf = gf - 0.5; + gf = gf * contrast; + gf = gf + 0.5; + gf = gf * 255.0; + + rf = (rf > 255.0) ? 255.0 : ((rf < 0.0) ? 0.0 : rf); + gf = (gf > 255.0) ? 255.0 : ((gf < 0.0) ? 0.0 : gf); + bf = (bf > 255.0) ? 255.0 : ((bf < 0.0) ? 0.0 : bf); + + new_pxl = gdImageColorAllocateAlpha(src, (int)rf, (int)gf, (int)bf, a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, (int)rf, (int)gf, (int)bf, a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + return 1; } - -int gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha) +/** + * Function: gdImageColor + * + * Change channel values of an image + * + * Parameters: + * src - The image. + * red - The value to add to the red channel of all pixels. + * green - The value to add to the green channel of all pixels. + * blue - The value to add to the blue channel of all pixels. + * alpha - The value to add to the alpha channel of all pixels. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + */ +BGD_DECLARE(int) +gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha) { - int x, y; - int new_pxl, pxl; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src == NULL) { - return 0; - } - - f = GET_PIXEL_FUNCTION(src); - - for (y=0; ysy; ++y) { - for (x=0; xsx; ++x) { - int r,g,b,a; - - pxl = f(src, x, y); - r = gdImageRed(src, pxl); - g = gdImageGreen(src, pxl); - b = gdImageBlue(src, pxl); - a = gdImageAlpha(src, pxl); - - r = r + red; - g = g + green; - b = b + blue; - a = a + alpha; - - r = (r > 255)? 255 : ((r < 0)? 0 : r); - g = (g > 255)? 255 : ((g < 0)? 0 : g); - b = (b > 255)? 255 : ((b < 0)? 0 : b); - a = (a > 127)? 127 : ((a < 0)? 0 : a); - - new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, r, g, b, a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - return 1; + int x, y; + int new_pxl, pxl; + FuncPtr f; + + if (src == NULL) { + return 0; + } + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; ++y) { + for (x = 0; x < src->sx; ++x) { + int r, g, b, a; + + pxl = f(src, x, y); + r = gdImageRed(src, pxl); + g = gdImageGreen(src, pxl); + b = gdImageBlue(src, pxl); + a = gdImageAlpha(src, pxl); + + r = r + red; + g = g + green; + b = b + blue; + a = a + alpha; + + r = (r > 255) ? 255 : ((r < 0) ? 0 : r); + g = (g > 255) ? 255 : ((g < 0) ? 0 : g); + b = (b > 255) ? 255 : ((b < 0) ? 0 : b); + a = (a > 127) ? 127 : ((a < 0) ? 0 : a); + + new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, r, g, b, a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + return 1; } -int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, float offset) +/** + * Function: gdImageConvolution + * + * Apply a convolution matrix to an image + * + * Depending on the matrix a wide range of effects can be accomplished, e.g. + * blurring, sharpening, embossing and edge detection. + * + * Parameters: + * src - The image. + * filter - The 3x3 convolution matrix. + * filter_div - The value to divide the convoluted channel values by. + * offset - The value to add to the convoluted channel values. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + * - + * - + * - + * - + */ +BGD_DECLARE(int) +gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, float offset) { - int x, y, i, j, new_a; - float new_r, new_g, new_b; - int new_pxl, pxl=0; - gdImagePtr srcback; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src==NULL) { - return 0; - } - - /* We need the orinal image with each safe neoghb. pixel */ - srcback = gdImageCreateTrueColor (src->sx, src->sy); - if (srcback==NULL) { - return 0; - } - - gdImageSaveAlpha(srcback, 1); - new_pxl = gdImageColorAllocateAlpha(srcback, 0, 0, 0, 127); - gdImageFill(srcback, 0, 0, new_pxl); - - gdImageCopy(srcback, src,0,0,0,0,src->sx,src->sy); - - f = GET_PIXEL_FUNCTION(src); - - for ( y=0; ysy; y++) { - for(x=0; xsx; x++) { - int new_ri, new_gi, new_bi; - new_r = new_g = new_b = 0; - pxl = f(srcback, x, y); - new_a = gdImageAlpha(srcback, pxl); - - for (j=0; j<3; j++) { - int yv = MIN(MAX(y - 1 + j, 0), src->sy - 1); - for (i=0; i<3; i++) { - pxl = f(srcback, MIN(MAX(x - 1 + i, 0), src->sx - 1), yv); - new_r += (float)gdImageRed(srcback, pxl) * filter[j][i]; - new_g += (float)gdImageGreen(srcback, pxl) * filter[j][i]; - new_b += (float)gdImageBlue(srcback, pxl) * filter[j][i]; - } - } - - new_r = (new_r/filter_div)+offset; - new_g = (new_g/filter_div)+offset; - new_b = (new_b/filter_div)+offset; - - new_ri = gdClampFloatToByte(new_r); - new_gi = gdClampFloatToByte(new_g); - new_bi = gdClampFloatToByte(new_b); - - new_pxl = gdImageColorAllocateAlpha(src, new_ri, new_gi, new_bi, new_a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, new_ri, new_gi, new_bi, new_a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - gdImageDestroy(srcback); - return 1; + int x, y, i, j, new_a; + float new_r, new_g, new_b; + int new_pxl, pxl = 0; + gdImagePtr srcback; + FuncPtr f; + + if (src == NULL) { + return 0; + } + + /* We need the orinal image with each safe neoghb. pixel */ + srcback = gdImageCreateTrueColor(src->sx, src->sy); + if (srcback == NULL) { + return 0; + } + + gdImageSaveAlpha(srcback, 1); + new_pxl = gdImageColorAllocateAlpha(srcback, 0, 0, 0, 127); + gdImageFill(srcback, 0, 0, new_pxl); + + gdImageCopy(srcback, src, 0, 0, 0, 0, src->sx, src->sy); + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; y++) { + for (x = 0; x < src->sx; x++) { + int new_ri, new_gi, new_bi; + new_r = new_g = new_b = 0; + pxl = f(srcback, x, y); + new_a = gdImageAlpha(srcback, pxl); + + for (j = 0; j < 3; j++) { + int yv = MIN(MAX(y - 1 + j, 0), src->sy - 1); + for (i = 0; i < 3; i++) { + pxl = f(srcback, MIN(MAX(x - 1 + i, 0), src->sx - 1), yv); + new_r += (float)gdImageRed(srcback, pxl) * filter[j][i]; + new_g += (float)gdImageGreen(srcback, pxl) * filter[j][i]; + new_b += (float)gdImageBlue(srcback, pxl) * filter[j][i]; + } + } + + new_r = (new_r / filter_div) + offset; + new_g = (new_g / filter_div) + offset; + new_b = (new_b / filter_div) + offset; + + new_ri = gdClampFloatToByte(new_r); + new_gi = gdClampFloatToByte(new_g); + new_bi = gdClampFloatToByte(new_b); + + new_pxl = gdImageColorAllocateAlpha(src, new_ri, new_gi, new_bi, new_a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, new_ri, new_gi, new_bi, new_a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + gdImageDestroy(srcback); + return 1; } -int gdImageSelectiveBlur( gdImagePtr src) +/* + Function: gdImageSelectiveBlur + */ +BGD_DECLARE(int) gdImageSelectiveBlur(gdImagePtr src) { - int x, y, i, j; - float new_r, new_g, new_b; - int new_pxl, cpxl, pxl, new_a=0; - float flt_r [3][3]; - float flt_g [3][3]; - float flt_b [3][3]; - float flt_r_sum, flt_g_sum, flt_b_sum; - - gdImagePtr srcback; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src==NULL) { - return 0; - } - - /* We need the orinal image with each safe neoghb. pixel */ - srcback = gdImageCreateTrueColor (src->sx, src->sy); - if (srcback==NULL) { - return 0; - } - gdImageCopy(srcback, src,0,0,0,0,src->sx,src->sy); - - f = GET_PIXEL_FUNCTION(src); - - for(y = 0; ysy; y++) { - for (x=0; xsx; x++) { - flt_r_sum = flt_g_sum = flt_b_sum = 0.0; - cpxl = f(src, x, y); - - for (j=0; j<3; j++) { - for (i=0; i<3; i++) { - if ((j == 1) && (i == 1)) { - flt_r[1][1] = flt_g[1][1] = flt_b[1][1] = 0.5; - } else { - pxl = f(src, x-(3>>1)+i, y-(3>>1)+j); - new_a = gdImageAlpha(srcback, pxl); - - new_r = ((float)gdImageRed(srcback, cpxl)) - ((float)gdImageRed (srcback, pxl)); - - if (new_r < 0.0f) { - new_r = -new_r; - } - if (new_r != 0) { - flt_r[j][i] = 1.0f/new_r; - } else { - flt_r[j][i] = 1.0f; - } - - new_g = ((float)gdImageGreen(srcback, cpxl)) - ((float)gdImageGreen(srcback, pxl)); - - if (new_g < 0.0f) { - new_g = -new_g; - } - if (new_g != 0) { - flt_g[j][i] = 1.0f/new_g; - } else { - flt_g[j][i] = 1.0f; - } - - new_b = ((float)gdImageBlue(srcback, cpxl)) - ((float)gdImageBlue(srcback, pxl)); - - if (new_b < 0.0f) { - new_b = -new_b; - } - if (new_b != 0) { - flt_b[j][i] = 1.0f/new_b; - } else { - flt_b[j][i] = 1.0f; - } - } - - flt_r_sum += flt_r[j][i]; - flt_g_sum += flt_g[j][i]; - flt_b_sum += flt_b [j][i]; - } - } - - for (j=0; j<3; j++) { - for (i=0; i<3; i++) { - if (flt_r_sum != 0.0) { - flt_r[j][i] /= flt_r_sum; - } - if (flt_g_sum != 0.0) { - flt_g[j][i] /= flt_g_sum; - } - if (flt_b_sum != 0.0) { - flt_b [j][i] /= flt_b_sum; - } - } - } - - new_r = new_g = new_b = 0.0; - - for (j=0; j<3; j++) { - for (i=0; i<3; i++) { - pxl = f(src, x-(3>>1)+i, y-(3>>1)+j); - new_r += (float)gdImageRed(srcback, pxl) * flt_r[j][i]; - new_g += (float)gdImageGreen(srcback, pxl) * flt_g[j][i]; - new_b += (float)gdImageBlue(srcback, pxl) * flt_b[j][i]; - } - } - - new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r); - new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g); - new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b); - new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); - if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); - } - gdImageSetPixel (src, x, y, new_pxl); - } - } - gdImageDestroy(srcback); - return 1; + int x, y, i, j; + float new_r, new_g, new_b; + int new_pxl, cpxl, pxl, new_a = 0; + float flt_r[3][3]; + float flt_g[3][3]; + float flt_b[3][3]; + float flt_r_sum, flt_g_sum, flt_b_sum; + + gdImagePtr srcback; + FuncPtr f; + + if (src == NULL) { + return 0; + } + + /* We need the orinal image with each safe neoghb. pixel */ + srcback = gdImageCreateTrueColor(src->sx, src->sy); + if (srcback == NULL) { + return 0; + } + gdImageCopy(srcback, src, 0, 0, 0, 0, src->sx, src->sy); + + f = GET_PIXEL_FUNCTION(src); + + for (y = 0; y < src->sy; y++) { + for (x = 0; x < src->sx; x++) { + flt_r_sum = flt_g_sum = flt_b_sum = 0.0; + cpxl = f(src, x, y); + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + if ((j == 1) && (i == 1)) { + flt_r[1][1] = flt_g[1][1] = flt_b[1][1] = 0.5; + } else { + pxl = f(src, x - (3 >> 1) + i, y - (3 >> 1) + j); + new_a = gdImageAlpha(srcback, pxl); + + new_r = + ((float)gdImageRed(srcback, cpxl)) - ((float)gdImageRed(srcback, pxl)); + + if (new_r < 0.0f) { + new_r = -new_r; + } + if (new_r != 0) { + flt_r[j][i] = 1.0f / new_r; + } else { + flt_r[j][i] = 1.0f; + } + + new_g = ((float)gdImageGreen(srcback, cpxl)) - + ((float)gdImageGreen(srcback, pxl)); + + if (new_g < 0.0f) { + new_g = -new_g; + } + if (new_g != 0) { + flt_g[j][i] = 1.0f / new_g; + } else { + flt_g[j][i] = 1.0f; + } + + new_b = ((float)gdImageBlue(srcback, cpxl)) - + ((float)gdImageBlue(srcback, pxl)); + + if (new_b < 0.0f) { + new_b = -new_b; + } + if (new_b != 0) { + flt_b[j][i] = 1.0f / new_b; + } else { + flt_b[j][i] = 1.0f; + } + } + + flt_r_sum += flt_r[j][i]; + flt_g_sum += flt_g[j][i]; + flt_b_sum += flt_b[j][i]; + } + } + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + if (flt_r_sum != 0.0) { + flt_r[j][i] /= flt_r_sum; + } + if (flt_g_sum != 0.0) { + flt_g[j][i] /= flt_g_sum; + } + if (flt_b_sum != 0.0) { + flt_b[j][i] /= flt_b_sum; + } + } + } + + new_r = new_g = new_b = 0.0; + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + pxl = f(src, x - (3 >> 1) + i, y - (3 >> 1) + j); + new_r += (float)gdImageRed(srcback, pxl) * flt_r[j][i]; + new_g += (float)gdImageGreen(srcback, pxl) * flt_g[j][i]; + new_b += (float)gdImageBlue(srcback, pxl) * flt_b[j][i]; + } + } + + new_r = (new_r > 255.0f) ? 255.0f : ((new_r < 0.0f) ? 0.0f : new_r); + new_g = (new_g > 255.0f) ? 255.0f : ((new_g < 0.0f) ? 0.0f : new_g); + new_b = (new_b > 255.0f) ? 255.0f : ((new_b < 0.0f) ? 0.0f : new_b); + new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + if (new_pxl == -1) { + new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + } + gdImageSetPixel(src, x, y, new_pxl); + } + } + gdImageDestroy(srcback); + return 1; } -int gdImageEdgeDetectQuick(gdImagePtr src) +/** + * Function: gdImageEdgeDetectQuick + * + * Edge detection of an image + * + * (see edge_detect_quick.jpg) + * + * Parameters: + * src - The image. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) gdImageEdgeDetectQuick(gdImagePtr src) { - float filter[3][3] = {{-1.0,0.0,-1.0}, - {0.0,4.0,0.0}, - {-1.0,0.0,-1.0}}; + float filter[3][3] = {{-1.0, 0.0, -1.0}, {0.0, 4.0, 0.0}, {-1.0, 0.0, -1.0}}; - return gdImageConvolution(src, filter, 1, 127); + return gdImageConvolution(src, filter, 1, 127); } -int gdImageGaussianBlur(gdImagePtr im) +/* + Function: gdImageGaussianBlur + + performs a Gaussian blur of radius 1 on the + image. The image is modified in place. + + *NOTE:* You will almost certain want to use + instead, as it allows you to change + your kernel size and sigma value. Future versions of this + function may fall back to calling it instead of + , causing subtle changes so be warned. + + Parameters: + im - The image to blur + + Returns: + GD_TRUE (1) on success, GD_FALSE (0) on failure. + +*/ + +BGD_DECLARE(int) gdImageGaussianBlur(gdImagePtr im) { - float filter[3][3] = {{1.0,2.0,1.0}, - {2.0,4.0,2.0}, - {1.0,2.0,1.0}}; + float filter[3][3] = {{1.0, 2.0, 1.0}, {2.0, 4.0, 2.0}, {1.0, 2.0, 1.0}}; - return gdImageConvolution(im, filter, 16, 0); + return gdImageConvolution(im, filter, 16, 0); } -int gdImageEmboss(gdImagePtr im) +/** + * Function: gdImageEmboss + * + * Emboss an image + * + * (see emboss.jpg) + * + * Parameters: + * im - The image. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImageEmboss(gdImagePtr im) { -/* - float filter[3][3] = {{1.0,1.0,1.0}, - {0.0,0.0,0.0}, - {-1.0,-1.0,-1.0}}; -*/ - float filter[3][3] = {{ 1.5, 0.0, 0.0}, - { 0.0, 0.0, 0.0}, - { 0.0, 0.0,-1.5}}; - - return gdImageConvolution(im, filter, 1, 127); + /* + float filter[3][3] = {{1.0,1.0,1.0}, + {0.0,0.0,0.0}, + {-1.0,-1.0,-1.0}}; + */ + float filter[3][3] = {{1.5, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, -1.5}}; + + return gdImageConvolution(im, filter, 1, 127); } -int gdImageMeanRemoval(gdImagePtr im) +/** + * Function: gdImageMeanRemoval + * + * Mean removal of an image + * + * (see mean_removal.jpg) + * + * Parameters: + * im - The image. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) gdImageMeanRemoval(gdImagePtr im) { - float filter[3][3] = {{-1.0,-1.0,-1.0}, - {-1.0,9.0,-1.0}, - {-1.0,-1.0,-1.0}}; + float filter[3][3] = {{-1.0, -1.0, -1.0}, {-1.0, 9.0, -1.0}, {-1.0, -1.0, -1.0}}; - return gdImageConvolution(im, filter, 1, 0); + return gdImageConvolution(im, filter, 1, 0); } -int gdImageSmooth(gdImagePtr im, float weight) +/** + * Function: gdImageSmooth + * + * Smooth an image + * + * (see smooth.jpg) + * + * Parameters: + * im - The image. + * weight - The strength of the smoothing. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + */ +BGD_DECLARE(int) gdImageSmooth(gdImagePtr im, float weight) { - float filter[3][3] = {{1.0,1.0,1.0}, - {1.0,0.0,1.0}, - {1.0,1.0,1.0}}; + float filter[3][3] = {{1.0, 1.0, 1.0}, {1.0, 0.0, 1.0}, {1.0, 1.0, 1.0}}; - filter[1][1] = weight; + filter[1][1] = weight; - return gdImageConvolution(im, filter, weight+8, 0); + return gdImageConvolution(im, filter, weight + 8, 0); } + +/* Return an array of coefficients for 'radius' and 'sigma' (sigma >= + * 0 means compute it). Result length is 2*radius+1. */ +static double *gaussian_coeffs(int radius, double sigmaArg) +{ + const double sigma = (sigmaArg <= 0.0) ? (2.0 / 3.0) * radius : sigmaArg; + const double s = 2.0 * sigma * sigma; + double *result; + double sum = 0; + int x, n, count; + + count = 2 * radius + 1; + + result = gdMalloc(sizeof(double) * count); + if (!result) { + return NULL; + } /* if */ + + for (x = -radius; x <= radius; x++) { + double coeff = exp(-(x * x) / s); + + sum += coeff; + result[x + radius] = coeff; + } /* for */ + + for (n = 0; n < count; n++) { + result[n] /= sum; + } /* for */ + + return result; +} /* gaussian_coeffs*/ + +static inline int reflect(int max, int x) +{ + assert(x > -max && x < 2 * max); + + if (x < 0) + return -x; + if (x >= max) + return max - (x - max) - 1; + return x; +} /* reflect*/ + +static inline void applyCoeffsLine(gdImagePtr src, gdImagePtr dst, int line, int linelen, + double *coeffs, int radius, gdAxis axis) +{ + int ndx; + + for (ndx = 0; ndx < linelen; ndx++) { + double r = 0, g = 0, b = 0, a = 0; + int cndx; + int *dest = (axis == HORIZONTAL) ? &dst->tpixels[line][ndx] : &dst->tpixels[ndx][line]; + + for (cndx = -radius; cndx <= radius; cndx++) { + const double coeff = coeffs[cndx + radius]; + const int rndx = reflect(linelen, ndx + cndx); + + const int srcpx = + (axis == HORIZONTAL) ? src->tpixels[line][rndx] : src->tpixels[rndx][line]; + + r += coeff * (double)gdTrueColorGetRed(srcpx); + g += coeff * (double)gdTrueColorGetGreen(srcpx); + b += coeff * (double)gdTrueColorGetBlue(srcpx); + a += coeff * (double)gdTrueColorGetAlpha(srcpx); + } /* for */ + + *dest = gdTrueColorAlpha(uchar_clamp(r, 0xFF), uchar_clamp(g, 0xFF), uchar_clamp(b, 0xFF), + uchar_clamp(a, 0x7F)); + } /* for */ +} /* applyCoeffsLine*/ + +static void applyCoeffs(gdImagePtr src, gdImagePtr dst, double *coeffs, int radius, gdAxis axis) +{ + int line, numlines, linelen; + + if (axis == HORIZONTAL) { + numlines = src->sy; + linelen = src->sx; + } else { + numlines = src->sx; + linelen = src->sy; + } /* if .. else*/ + + for (line = 0; line < numlines; line++) { + applyCoeffsLine(src, dst, line, linelen, coeffs, radius, axis); + } /* for */ +} /* applyCoeffs*/ + +/* + Function: gdImageCopyGaussianBlurred + + Return a copy of the source image _src_ blurred according to the + parameters using the Gaussian Blur algorithm. + + _radius_ is a radius, not a diameter so a radius of 2 (for + example) will blur across a region 5 pixels across (2 to the + center, 1 for the center itself and another 2 to the other edge). + + _sigma_ represents the "fatness" of the curve (lower == fatter). + If _sigma_ is less than or equal to 0, + ignores it and instead computes an + "optimal" value. Be warned that future versions of this function + may compute sigma differently. + + The resulting image is always truecolor. + + More Details: + + A Gaussian Blur is generated by replacing each pixel's color + values with the average of the surrounding pixels' colors. This + region is a circle whose radius is given by argument _radius_. + Thus, a larger radius will yield a blurrier image. + + This average is not a simple mean of the values. Instead, values + are weighted using the Gaussian function (roughly a bell curve + centered around the destination pixel) giving it much more + influence on the result than its neighbours. Thus, a fatter curve + will give the center pixel more weight and make the image less + blurry; lower _sigma_ values will yield flatter curves. + + Currently, computes the default sigma + as + + (2/3)*radius + + Note, however that we reserve the right to change this if we find + a better ratio. If you absolutely need the current sigma value, + you should set it yourself. + + Parameters: + + src - the source image + radius - the blur radius (*not* diameter--range is 2*radius + 1) + sigma - the sigma value or a value <= 0.0 to use the computed default + + Returns: + + The new image or NULL if an error occurred. The result is always + truecolor. + + Example: + (start code) + + FILE *in; + gdImagePtr result, src; + + in = fopen("foo.png", "rb"); + src = gdImageCreateFromPng(in); + + result = gdImageCopyGaussianBlurred(im, src->sx / 10, -1.0); + + (end code) +*/ + +/* TODO: Look into turning this into a generic seperable filter + * function with Gaussian Blur being one special case. (At the + * moment, I can't find any other useful separable filter so for not, + * it's just blur.) */ +BGD_DECLARE(gdImagePtr) +gdImageCopyGaussianBlurred(gdImagePtr src, int radius, double sigma) +{ + gdImagePtr tmp = NULL, result = NULL; + double *coeffs; + int freeSrc = 0; + + if (radius < 1) { + return NULL; + } /* if */ + + /* Compute the coefficients. */ + coeffs = gaussian_coeffs(radius, sigma); + if (!coeffs) { + return NULL; + } /* if */ + + /* If the image is not truecolor, we first make a truecolor + * scratch copy. */ + if (!src->trueColor) { + int tcstat; + + src = gdImageClone(src); + if (!src) { + gdFree(coeffs); + return NULL; + } + + tcstat = gdImagePaletteToTrueColor(src); + if (!tcstat) { + gdImageDestroy(src); + gdFree(coeffs); + return NULL; + } /* if */ + + freeSrc = 1; + } /* if */ + + /* Apply the filter horizontally. */ + tmp = gdImageCreateTrueColor(src->sx, src->sy); + if (!tmp) { + if (freeSrc) { + gdImageDestroy(src); + } + gdFree(coeffs); + return NULL; + } + applyCoeffs(src, tmp, coeffs, radius, HORIZONTAL); + + /* Apply the filter vertically. */ + result = gdImageCreateTrueColor(src->sx, src->sy); + if (result) { + applyCoeffs(tmp, result, coeffs, radius, VERTICAL); + } /* if */ + + gdImageDestroy(tmp); + gdFree(coeffs); + + if (freeSrc) + gdImageDestroy(src); + + return result; +} /* gdImageCopyGaussianBlurred*/ /* End filters function */ diff --git a/ext/gd/libgd/gd_fixed.h b/ext/gd/libgd/gd_fixed.h new file mode 100644 index 000000000000..495e25859da3 --- /dev/null +++ b/ext/gd/libgd/gd_fixed.h @@ -0,0 +1,32 @@ +// +// Created by pierr on 7/21/2021. +// + +#ifndef GD_GD_FIXED_H +#define GD_GD_FIXED_H +/* Add *BSD and co for inttypes.h */ +#if defined(_MSC_VER) && _MSC_VER < 1600 +#include "msinttypes/stdint.h" +#else +#include +#endif +/* only used here, let do a generic fixed point integers later if required by other + part of GD */ +typedef uint32_t gdFixed; +typedef int32_t gd_fixed_16_16; +typedef gd_fixed_16_16 gd_fixed_t; +#define gd_fixed_e ((gd_fixed_t)1) +#define gd_fixed_1 (gd_int_to_fixed(1)) +#define gd_fixed_1_minus_e (gd_fixed_1 - gd_fixed_e) +#define gd_fixed_minus_1 (pixman_int_to_fixed(-1)) +#define gd_fixed_to_int(f) ((int)((f) >> 16)) +#define gd_int_to_fixed(i) ((gd_fixed_t)(i) * (gd_fixed_t)65536) +#define gd_fixed_to_double(f) (double)((f) / (double)gd_fixed_1) +#define gd_double_to_fixed(d) ((gd_fixed_t)((d) * 65536.0)) +#define gd_fixed_frac(f) ((f) & gd_fixed_1_minus_e) +#define gd_fixed_floor(f) ((f) & ~gd_fixed_1_minus_e) +#define gd_fixed_ceil(f) gd_fixed_floor((f) + gd_fixed_1_minus_e) +#define gd_fixed_fraction(f) ((f) & gd_fixed_1_minus_e) +#define gd_fixed_mod_2(f) ((f) & (gd_fixed1 | gd_fixed_1_minus_e)) + +#endif // GD_GD_FIXED_H diff --git a/ext/gd/libgd/gd_gd.c b/ext/gd/libgd/gd_gd.c index d5fe16a9af16..da2a8449ce43 100644 --- a/ext/gd/libgd/gd_gd.c +++ b/ext/gd/libgd/gd_gd.c @@ -1,280 +1,402 @@ -#include +/** + * File: GD IO + * + * Read and write GD images. + * + * The GD image format is a proprietary image format of libgd. *It has to be* + * *regarded as being obsolete, and should only be used for development and* + * *testing purposes.* + * + * Structure of a GD image file: + * - file header + * - color header (either truecolor or palette) + * - image data + * + * All numbers are stored in big-endian format. Note that all GD output is done + * in the GD 2.x format (not to be confused with the GD2 format), but input may + * also be in the GD 1.x format. + * + * GD 1.x file header structure: + * width - 1 word + * height - 1 word + * + * GD 1.x color header (palette only): + * count - 1 byte (the number of used palette colors) + * transparent - 1 word (257 signals no transparency) + * palette - 256×3 bytes (RGB triplets) + * + * GD 2.x file header structure: + * signature - 1 word ("\xFF\xFE" for truecolor, "\xFF\xFF" for palette) + * width - 1 word + * height - 1 word + * + * GD 2.x truecolor image color header: + * truecolor - 1 byte (always "\001") + * transparent - 1 dword (ARGB color); "\377\377\377\377" means that no + * transparent color is set + * + * GD 2.x palette image color header: + * truecolor - 1 byte (always "\0") + * count - 1 word (the number of used palette colors) + * transparent - 1 dword (palette index); "\377\377\377\377" means that no + * transparent color is set + * palette - 256 dwords (RGBA colors) + * + * Image data: + * Sequential pixel data; row-major from top to bottom, left to right: + * - 1 byte per pixel for palette images + * - 1 dword (ARGB) per pixel for truecolor images + */ + +#include "gd.h" +#include "gd_errors.h" #include -#include +#include #include -#include "gd.h" +#include #define TRUE 1 #define FALSE 0 /* Exported functions: */ -extern void gdImageGd (gdImagePtr im, FILE * out); - +extern void gdImageGd(gdImagePtr im, FILE *out); /* Use this for commenting out debug-print statements. */ /* Just use the first '#define' to allow all the prints... */ /*#define GD2_DBG(s) (s) */ #define GD2_DBG(s) -/* */ -/* Shared code to read color tables from gd file. */ -/* */ -int _gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag) +/* + * Shared code to read color tables from gd file. + */ +int _gdGetColors(gdIOCtx *in, gdImagePtr im, int gd2xFlag) { - int i; - if (gd2xFlag) { - int trueColorFlag; - if (!gdGetByte(&trueColorFlag, in)) { - goto fail1; - } - /* 2.0.12: detect bad truecolor .gd files created by pre-2.0.12. - * Beginning in 2.0.12 truecolor is indicated by the initial 2-byte - * signature. - */ - if (trueColorFlag != im->trueColor) { - goto fail1; - } - /* This should have been a word all along */ - if (!im->trueColor) { - if (!gdGetWord(&im->colorsTotal, in)) { - goto fail1; - } - if (im->colorsTotal > gdMaxColors) { - goto fail1; - } - } - /* Int to accommodate truecolor single-color transparency */ - if (!gdGetInt(&im->transparent, in)) { - goto fail1; - } - } else { - if (!gdGetByte(&im->colorsTotal, in)) { - goto fail1; - } - if (!gdGetWord(&im->transparent, in)) { - goto fail1; - } - if (im->transparent == 257) { - im->transparent = (-1); - } - } - - GD2_DBG(printf("Palette had %d colours (T=%d)\n", im->colorsTotal, im->transparent)); - - if (im->trueColor) { - return TRUE; - } - - for (i = 0; i < gdMaxColors; i++) { - if (!gdGetByte(&im->red[i], in)) { - goto fail1; - } - if (!gdGetByte(&im->green[i], in)) { - goto fail1; - } - if (!gdGetByte(&im->blue[i], in)) { - goto fail1; - } - if (gd2xFlag) { - if (!gdGetByte(&im->alpha[i], in)) { - goto fail1; - } - } - } - - for (i = 0; i < im->colorsTotal; i++) { - im->open[i] = 0; - } - - return TRUE; + int i; + if (gd2xFlag) { + int trueColorFlag; + if (!gdGetByte(&trueColorFlag, in)) { + goto fail1; + } + /* 2.0.12: detect bad truecolor .gd files created by pre-2.0.12. + Beginning in 2.0.12 truecolor is indicated by the initial 2-byte + signature. */ + if (trueColorFlag != im->trueColor) { + goto fail1; + } + /* This should have been a word all along */ + if (!im->trueColor) { + if (!gdGetWord(&im->colorsTotal, in)) { + goto fail1; + } + if (im->colorsTotal > gdMaxColors) { + goto fail1; + } + } + /* Int to accommodate truecolor single-color transparency */ + if (!gdGetInt(&im->transparent, in)) { + goto fail1; + } + } else { + if (!gdGetByte(&im->colorsTotal, in)) { + goto fail1; + } + if (!gdGetWord(&im->transparent, in)) { + goto fail1; + } + } + /* Make sure transparent index is within bounds of the palette. */ + if (!(im->trueColor) && (im->transparent >= im->colorsTotal || im->transparent < 0)) { + im->transparent = (-1); + } + GD2_DBG(printf("Palette had %d colours (T=%d)\n", im->colorsTotal, im->transparent)); + if (im->trueColor) { + return TRUE; + } + for (i = 0; (i < gdMaxColors); i++) { + if (!gdGetByte(&im->red[i], in)) { + goto fail1; + } + if (!gdGetByte(&im->green[i], in)) { + goto fail1; + } + if (!gdGetByte(&im->blue[i], in)) { + goto fail1; + } + if (gd2xFlag) { + if (!gdGetByte(&im->alpha[i], in)) { + goto fail1; + } + } + } + + for (i = 0; (i < im->colorsTotal); i++) { + im->open[i] = 0; + } + + return TRUE; fail1: - return FALSE; + return FALSE; } /* */ /* Use the common basic header info to make the image object. */ /* */ -static gdImagePtr _gdCreateFromFile (gdIOCtx * in, int *sx, int *sy) +static gdImagePtr _gdCreateFromFile(gdIOCtx *in, int *sx, int *sy) { - gdImagePtr im; - int gd2xFlag = 0; - int trueColorFlag = 0; - - if (!gdGetWord(sx, in)) { - goto fail1; - } - if (*sx == 65535 || *sx == 65534) { - /* This is a gd 2.0 .gd file */ - gd2xFlag = 1; - /* 2.0.12: 65534 signals a truecolor .gd file. There is a slight redundancy here but we can live with it. */ - if (*sx == 65534) { - trueColorFlag = 1; - } - if (!gdGetWord(sx, in)) { - goto fail1; - } - } - if (!gdGetWord(sy, in)) { - goto fail1; - } - - GD2_DBG(printf("Image is %dx%d\n", *sx, *sy)); - - if (trueColorFlag) { - im = gdImageCreateTrueColor(*sx, *sy); - } else { - im = gdImageCreate(*sx, *sy); - } - if(!im) { - goto fail1; - } - if (!_gdGetColors(in, im, gd2xFlag)) { - goto fail2; - } - - return im; + gdImagePtr im; + int gd2xFlag = 0; + int trueColorFlag = 0; + + if (!gdGetWord(sx, in)) { + goto fail1; + } + if ((*sx == 65535) || (*sx == 65534)) { + /* This is a gd 2.0 .gd file */ + gd2xFlag = 1; + /* 2.0.12: 65534 signals a truecolor .gd file. There is a slight redundancy here but we can + * live with it. */ + if (*sx == 65534) { + trueColorFlag = 1; + } + if (!gdGetWord(sx, in)) { + goto fail1; + } + } + if (!gdGetWord(sy, in)) { + goto fail1; + } + + GD2_DBG(printf("Image is %dx%d\n", *sx, *sy)); + + if (trueColorFlag) { + im = gdImageCreateTrueColor(*sx, *sy); + } else { + im = gdImageCreate(*sx, *sy); + } + if (!im) { + goto fail1; + } + if (!_gdGetColors(in, im, gd2xFlag)) { + goto fail2; + } + + return im; fail2: - gdImageDestroy(im); + gdImageDestroy(im); fail1: - return 0; + return 0; } -gdImagePtr gdImageCreateFromGd (FILE * inFile) +/* + Function: gdImageCreateFromGd + + is called to load images from gd format + files. Invoke with an already opened pointer + to a file containing the desired image in the gd file format, + which is specific to gd and intended for very fast loading. (It is + not intended for compression; for compression, use PNG or JPEG.) + + returns a to the new image, or + NULL if unable to load the image (most often because the file is + corrupt or does not contain a gd format + image). does not close the file. You can + inspect the sx and sy members of the image to determine its + size. The image must eventually be destroyed using + . + + Variants: + + creates an image from GD data (i.e. the + contents of a GD file) already in memory. + + reads in an image using the functions in + a struct. + + Parameters: + + infile - The input FILE pointer + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + + > gdImagePtr im; + > FILE *in; + > in = fopen("mygd.gd", "rb"); + > im = gdImageCreateFromGd(in); + > fclose(in); + > // ... Use the image ... + > gdImageDestroy(im); +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd(FILE *inFile) { - gdImagePtr im; - gdIOCtx *in; + gdImagePtr im; + gdIOCtx *in; - in = gdNewFileCtx(inFile); - im = gdImageCreateFromGdCtx(in); + in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromGdCtx(in); - in->gd_free(in); + in->gd_free(in); - return im; + return im; } -gdImagePtr gdImageCreateFromGdPtr (int size, void *data) -{ - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromGdCtx(in); - in->gd_free(in); +/* + Function: gdImageCreateFromGdPtr + + Parameters: - return im; + size - size of GD data in bytes. + data - GD data (i.e. contents of a GIF file). + + Reads in GD data from memory. See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGdPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromGdCtx(in); + in->gd_free(in); + + return im; } -gdImagePtr gdImageCreateFromGdCtx (gdIOCtxPtr in) +/* + Function: gdImageCreateFromGdCtx + + Reads in a GD image via a struct. See + . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGdCtx(gdIOCtxPtr in) { - int sx, sy; - int x, y; - gdImagePtr im; - - /* Read the header */ - im = _gdCreateFromFile(in, &sx, &sy); - - if (im == NULL) { - goto fail1; - } - - /* Then the data... */ - /* 2.0.12: support truecolor properly in .gd as well as in .gd2. Problem reported by Andreas Pfaller. */ - if (im->trueColor) { - for (y = 0; y < sy; y++) { - for (x = 0; x < sx; x++) { - int pix; - if (!gdGetInt(&pix, in)) { - goto fail2; - } - im->tpixels[y][x] = pix; - } - } - } else { - for (y = 0; y < sy; y++) { - for (x = 0; x < sx; x++) { - int ch; - ch = gdGetC(in); - if (ch == EOF) { - goto fail2; - } - /* ROW-MAJOR IN GD 1.3 */ - im->pixels[y][x] = ch; - } - } - } - - return im; + int sx, sy; + int x, y; + gdImagePtr im; + + /* Read the header */ + im = _gdCreateFromFile(in, &sx, &sy); + + if (im == NULL) { + goto fail1; + } + + /* Then the data... */ + /* 2.0.12: support truecolor properly in .gd as well as in .gd2. Problem reported by Andreas + * Pfaller. */ + if (im->trueColor) { + for (y = 0; (y < sy); y++) { + for (x = 0; (x < sx); x++) { + int pix; + if (!gdGetInt(&pix, in)) { + goto fail2; + } + im->tpixels[y][x] = pix; + } + } + } else { + for (y = 0; (y < sy); y++) { + for (x = 0; (x < sx); x++) { + int ch; + ch = gdGetC(in); + if (ch == EOF) { + goto fail2; + } + /* ROW-MAJOR IN GD 1.3 */ + im->pixels[y][x] = ch; + } + } + } + + return im; fail2: - gdImageDestroy (im); + gdImageDestroy(im); fail1: - return 0; + return 0; } -void _gdPutColors (gdImagePtr im, gdIOCtx * out) +void _gdPutColors(gdImagePtr im, gdIOCtx *out) { - int i; - - gdPutC(im->trueColor, out); - if (!im->trueColor) { - gdPutWord(im->colorsTotal, out); - } - gdPutInt(im->transparent, out); - if (!im->trueColor) { - for (i = 0; i < gdMaxColors; i++) { - gdPutC((unsigned char) im->red[i], out); - gdPutC((unsigned char) im->green[i], out); - gdPutC((unsigned char) im->blue[i], out); - gdPutC((unsigned char) im->alpha[i], out); - } - } + int i; + + gdPutC(im->trueColor, out); + if (!im->trueColor) { + gdPutWord(im->colorsTotal, out); + } + gdPutInt(im->transparent, out); + if (!im->trueColor) { + for (i = 0; (i < gdMaxColors); i++) { + gdPutC((unsigned char)im->red[i], out); + gdPutC((unsigned char)im->green[i], out); + gdPutC((unsigned char)im->blue[i], out); + gdPutC((unsigned char)im->alpha[i], out); + } + } } -static void _gdPutHeader (gdImagePtr im, gdIOCtx * out) +static void _gdPutHeader(gdImagePtr im, gdIOCtx *out) { - /* 65535 indicates this is a gd 2.x .gd file. - * 2.0.12: 65534 indicates truecolor. - */ - if (im->trueColor) { - gdPutWord(65534, out); - } else { - gdPutWord(65535, out); - } - gdPutWord(im->sx, out); - gdPutWord(im->sy, out); - - _gdPutColors(im, out); + /* 65535 indicates this is a gd 2.x .gd file. + * 2.0.12: 65534 indicates truecolor. + */ + if (im->trueColor) { + gdPutWord(65534, out); + } else { + gdPutWord(65535, out); + } + gdPutWord(im->sx, out); + gdPutWord(im->sy, out); + + _gdPutColors(im, out); } -static void _gdImageGd (gdImagePtr im, gdIOCtx * out) +static void _gdImageGd(gdImagePtr im, gdIOCtx *out) { - int x, y; - - _gdPutHeader(im, out); - - for (y = 0; y < im->sy; y++) { - for (x = 0; x < im->sx; x++) { - /* ROW-MAJOR IN GD 1.3 */ - if (im->trueColor) { - gdPutInt(im->tpixels[y][x], out); - } else { - gdPutC((unsigned char) im->pixels[y][x], out); - } - } - } + int x, y; + + _gdPutHeader(im, out); + + for (y = 0; (y < im->sy); y++) { + for (x = 0; (x < im->sx); x++) { + /* ROW-MAJOR IN GD 1.3 */ + if (im->trueColor) { + gdPutInt(im->tpixels[y][x], out); + } else { + gdPutC((unsigned char)im->pixels[y][x], out); + } + } + } } -void gdImageGd (gdImagePtr im, FILE * outFile) +/* + Function: gdImageGd + */ +BGD_DECLARE(void) gdImageGd(gdImagePtr im, FILE *outFile) { - gdIOCtx *out = gdNewFileCtx(outFile); - _gdImageGd(im, out); - out->gd_free(out); + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + _gdImageGd(im, out); + out->gd_free(out); } -void *gdImageGdPtr (gdImagePtr im, int *size) +/* + Function: gdImageGdPtr + */ +BGD_DECLARE(void *) gdImageGdPtr(gdImagePtr im, int *size) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - _gdImageGd(im, out); - rv = gdDPExtractData(out, size); - out->gd_free(out); - return rv; + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + _gdImageGd(im, out); + rv = gdDPExtractData(out, size); + out->gd_free(out); + return rv; } diff --git a/ext/gd/libgd/gd_gd2.c b/ext/gd/libgd/gd_gd2.c index 8e0307e5b120..24fc19c892ee 100644 --- a/ext/gd/libgd/gd_gd2.c +++ b/ext/gd/libgd/gd_gd2.c @@ -1,23 +1,80 @@ /* - * gd_gd2.c - * - * Implements the I/O and support for the GD2 format. - * - * Changing the definition of GD2_DBG (below) will cause copious messages - * to be displayed while it processes requests. - * - * Designed, Written & Copyright 1999, Philip Warner. - * + * gd_gd2.c + * + * Implements the I/O and support for the GD2 format. + * + * Changing the definition of GD2_DBG (below) will cause copious messages + * to be displayed while it processes requests. + * + * Designed, Written & Copyright 1999, Philip Warner. + * */ -#include -#include -#include -#include -#include +/** + * File: GD2 IO + * + * Read and write GD2 images. + * + * The GD2 image format is a proprietary image format of libgd. *It has to be* + * *regarded as being obsolete, and should only be used for development and* + * *testing purposes.* + * + * Structure of a GD2 image file: + * - file header + * - chunk headers (only for compressed data) + * - color header (either truecolor or palette) + * - chunks of image data (chunk-row-major, top to bottom, left to right) + * + * All numbers are stored in big-endian format. + * + * File header structure: + * signature - 4 bytes (always "gd2\0") + * version - 1 word (e.g. "\0\002") + * width - 1 word + * height - 1 word + * chunk_size - 1 word + * format - 1 word + * x_chunk_count - 1 word + * y_chunk_count - 1 word + * + * Recognized formats: + * 1 - raw palette image data + * 2 - compressed palette image data + * 3 - raw truecolor image data + * 4 - compressed truecolor image data + * + * Chunk header: + * offset - 1 dword + * size - 1 dword + * + * There are x_chunk_count * y_chunk_count chunk headers. + * + * Truecolor image color header: + * truecolor - 1 byte (always "\001") + * transparent - 1 dword (ARGB color); "\377\377\377\377" means that no + * transparent color is set + * + * Palette image color header: + * truecolor - 1 byte (always "\0") + * count - 1 word (the number of used palette colors) + * transparent - 1 dword (palette index); "\377\377\377\377" means that no + * transparent color is set + * palette - 256 dwords (RGBA colors) + * + * Chunk structure: + * Sequential pixel data of a rectangular area (chunk_size x chunk_size), + * row-major from top to bottom, left to right: + * - 1 byte per pixel for palette images + * - 1 dword (ARGB) per pixel for truecolor images + * + * Depending on format, the chunk may be ZLIB compressed. + */ #include "gd.h" #include "gd_errors.h" #include "gdhelpers.h" +#include +#include +#include #include @@ -28,824 +85,977 @@ * from im->trueColor, and the load routine doesn't need to tell * the end user the saved format. NOTE: adding 2 is assumed * to result in the correct format value for truecolor! -*/ + */ #define GD2_FMT_TRUECOLOR_RAW 3 #define GD2_FMT_TRUECOLOR_COMPRESSED 4 -#define gd2_compressed(fmt) (((fmt) == GD2_FMT_COMPRESSED) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED)) -#define gd2_truecolor(fmt) (((fmt) == GD2_FMT_TRUECOLOR_RAW) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED)) +#define gd2_compressed(fmt) \ + (((fmt) == GD2_FMT_COMPRESSED) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED)) +#define gd2_truecolor(fmt) \ + (((fmt) == GD2_FMT_TRUECOLOR_RAW) || ((fmt) == GD2_FMT_TRUECOLOR_COMPRESSED)) /* Use this for commenting out debug-print statements. */ /* Just use the first '#define' to allow all the prints... */ /* #define GD2_DBG(s) (s) */ #define GD2_DBG(s) -typedef struct -{ - int offset; - int size; +typedef struct { + int offset; + int size; } t_chunk_info; -extern int _gdGetColors(gdIOCtx * in, gdImagePtr im, int gd2xFlag); -extern void _gdPutColors(gdImagePtr im, gdIOCtx * out); +extern int _gdGetColors(gdIOCtx *in, gdImagePtr im, int gd2xFlag); +extern void _gdPutColors(gdImagePtr im, gdIOCtx *out); /* */ /* Read the extra info in the gd2 header. */ /* */ -static int _gd2GetHeader(gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info ** chunkIdx) +static int _gd2GetHeader(gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, int *ncx, + int *ncy, t_chunk_info **chunkIdx) { - int i; - int ch; - char id[5]; - t_chunk_info *cidx; - int sidx; - int nc; - - GD2_DBG(gd_error("Reading gd2 header info")); - - for (i = 0; i < 4; i++) { - ch = gdGetC(in); - if (ch == EOF) { - goto fail1; - } - id[i] = ch; - } - id[4] = 0; - - GD2_DBG(gd_error("Got file code: %s", id)); - - /* Equiv. of 'magick'. */ - if (strcmp(id, GD2_ID) != 0) { - GD2_DBG(gd_error("Not a valid gd2 file")); - goto fail1; - } - - /* Version */ - if (gdGetWord(vers, in) != 1) { - goto fail1; - } - GD2_DBG(gd_error("Version: %d", *vers)); - - if ((*vers != 1) && (*vers != 2)) { - GD2_DBG(gd_error("Bad version: %d", *vers)); - goto fail1; - } - - /* Image Size */ - if (!gdGetWord(sx, in)) { - GD2_DBG(gd_error("Could not get x-size")); - goto fail1; - } - if (!gdGetWord(sy, in)) { - GD2_DBG(gd_error("Could not get y-size")); - goto fail1; - } - GD2_DBG(gd_error("Image is %dx%d", *sx, *sy)); - - /* Chunk Size (pixels, not bytes!) */ - if (gdGetWord(cs, in) != 1) { - goto fail1; - } - GD2_DBG(gd_error("ChunkSize: %d", *cs)); - - if ((*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX)) { - GD2_DBG(gd_error("Bad chunk size: %d", *cs)); - goto fail1; - } - - /* Data Format */ - if (gdGetWord(fmt, in) != 1) { - goto fail1; - } - GD2_DBG(gd_error("Format: %d", *fmt)); - - if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED) && (*fmt != GD2_FMT_TRUECOLOR_RAW) && (*fmt != GD2_FMT_TRUECOLOR_COMPRESSED)) { - GD2_DBG(gd_error("Bad data format: %d", *fmt)); - goto fail1; - } - - /* # of chunks wide */ - if (gdGetWord(ncx, in) != 1) { - goto fail1; - } - GD2_DBG(gd_error("%d Chunks Wide", *ncx)); - - /* # of chunks high */ - if (gdGetWord(ncy, in) != 1) { - goto fail1; - } - GD2_DBG(gd_error("%d Chunks vertically", *ncy)); - - if (gd2_compressed(*fmt)) { - if (overflow2(*ncx, *ncy)) { - GD2_DBG(printf ("Illegal chunk counts: %d * %d\n", *ncx, *ncy)); - goto fail1; - } - nc = (*ncx) * (*ncy); - GD2_DBG(gd_error("Reading %d chunk index entries", nc)); - if (overflow2(sizeof(t_chunk_info), nc)) { - goto fail1; - } - sidx = sizeof(t_chunk_info) * nc; - if (sidx <= 0) { - goto fail1; - } - cidx = gdCalloc(sidx, 1); - if (cidx == NULL) { - goto fail1; - } - - for (i = 0; i < nc; i++) { - if (gdGetInt(&cidx[i].offset, in) != 1) { - gdFree(cidx); - goto fail1; - } - if (gdGetInt(&cidx[i].size, in) != 1) { - gdFree(cidx); - goto fail1; - } - if (cidx[i].offset < 0 || cidx[i].size < 0) { - gdFree(cidx); - goto fail1; - } - } - *chunkIdx = cidx; - } - - GD2_DBG(gd_error("gd2 header complete")); - - return 1; - + int i; + int ch; + char id[5]; + t_chunk_info *cidx; + int sidx; + int nc; + + GD2_DBG(gd_error("Reading gd2 header info")); + + for (i = 0; i < 4; i++) { + ch = gdGetC(in); + if (ch == EOF) { + goto fail1; + }; + id[i] = ch; + }; + id[4] = 0; + + GD2_DBG(gd_error("Got file code: %s", id)); + + /* Equiv. of 'magick'. */ + if (strcmp(id, GD2_ID) != 0) { + GD2_DBG(gd_error("Not a valid gd2 file")); + goto fail1; + }; + + /* Version */ + if (gdGetWord(vers, in) != 1) { + goto fail1; + }; + GD2_DBG(gd_error("Version: %d", *vers)); + + if ((*vers != 1) && (*vers != 2)) { + GD2_DBG(gd_error("Bad version: %d", *vers)); + goto fail1; + }; + + /* Image Size */ + if (!gdGetWord(sx, in)) { + GD2_DBG(gd_error("Could not get x-size")); + goto fail1; + } + if (!gdGetWord(sy, in)) { + GD2_DBG(gd_error("Could not get y-size")); + goto fail1; + } + GD2_DBG(gd_error("Image is %dx%d", *sx, *sy)); + + /* Chunk Size (pixels, not bytes!) */ + if (gdGetWord(cs, in) != 1) { + goto fail1; + }; + GD2_DBG(gd_error("ChunkSize: %d", *cs)); + + if ((*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX)) { + GD2_DBG(gd_error("Bad chunk size: %d", *cs)); + goto fail1; + }; + + /* Data Format */ + if (gdGetWord(fmt, in) != 1) { + goto fail1; + }; + GD2_DBG(gd_error("Format: %d", *fmt)); + + if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED) && (*fmt != GD2_FMT_TRUECOLOR_RAW) && + (*fmt != GD2_FMT_TRUECOLOR_COMPRESSED)) { + GD2_DBG(gd_error("Bad data format: %d", *fmt)); + goto fail1; + }; + + /* # of chunks wide */ + if (gdGetWord(ncx, in) != 1) { + goto fail1; + }; + GD2_DBG(gd_error("%d Chunks Wide", *ncx)); + + /* # of chunks high */ + if (gdGetWord(ncy, in) != 1) { + goto fail1; + }; + GD2_DBG(gd_error("%d Chunks vertically", *ncy)); + + if (gd2_compressed(*fmt)) { + if (overflow2(*ncx, *ncy)) { + GD2_DBG(printf("Illegal chunk counts: %d * %d\n", *ncx, *ncy)); + goto fail1; + } + nc = (*ncx) * (*ncy); + GD2_DBG(gd_error("Reading %d chunk index entries", nc)); + if (overflow2(sizeof(t_chunk_info), nc)) { + goto fail1; + } + sidx = sizeof(t_chunk_info) * nc; + if (sidx <= 0) { + goto fail1; + } + + cidx = gdCalloc(sidx, 1); + if (cidx == NULL) { + goto fail1; + } + + for (i = 0; i < nc; i++) { + if (gdGetInt(&cidx[i].offset, in) != 1) { + goto fail2; + }; + if (gdGetInt(&cidx[i].size, in) != 1) { + goto fail2; + }; + if (cidx[i].offset < 0 || cidx[i].size < 0 || cidx[i].size == INT_MAX) + goto fail2; + }; + *chunkIdx = cidx; + }; + + GD2_DBG(gd_error("gd2 header complete")); + + return 1; +fail2: + gdFree(cidx); fail1: - return 0; + return 0; } -static gdImagePtr _gd2CreateFromFile (gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info ** cidx) +static gdImagePtr _gd2CreateFromFile(gdIOCtxPtr in, int *sx, int *sy, int *cs, int *vers, int *fmt, + int *ncx, int *ncy, t_chunk_info **cidx) { - gdImagePtr im; - - if (_gd2GetHeader (in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) { - GD2_DBG(gd_error("Bad GD2 header")); - goto fail1; - } - - if (gd2_truecolor(*fmt)) { - im = gdImageCreateTrueColor(*sx, *sy); - } else { - im = gdImageCreate(*sx, *sy); - } - if (im == NULL) { - GD2_DBG(gd_error("Could not create gdImage")); - goto fail2; - } - - if (!_gdGetColors(in, im, (*vers) == 2)) { - GD2_DBG(gd_error("Could not read color palette")); - goto fail3; - } - GD2_DBG(gd_error("Image palette completed: %d colours", im->colorsTotal)); - - return im; + gdImagePtr im; + + if (_gd2GetHeader(in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) { + GD2_DBG(gd_error("Bad GD2 header")); + goto fail1; + } + + if (gd2_truecolor(*fmt)) { + im = gdImageCreateTrueColor(*sx, *sy); + } else { + im = gdImageCreate(*sx, *sy); + } + if (im == NULL) { + GD2_DBG(gd_error("Could not create gdImage")); + goto fail2; + } + + if (!_gdGetColors(in, im, (*vers) == 2)) { + GD2_DBG(gd_error("Could not read color palette")); + goto fail3; + } + GD2_DBG(gd_error("Image palette completed: %d colours", im->colorsTotal)); + + return im; fail3: - gdImageDestroy(im); + gdImageDestroy(im); fail2: - gdFree(*cidx); + gdFree(*cidx); fail1: - return 0; + return 0; } -static int _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf, uLongf * chunkLen, gdIOCtx * in) +static int _gd2ReadChunk(int offset, char *compBuf, int compSize, char *chunkBuf, uLongf *chunkLen, + gdIOCtx *in) { - int zerr; - - if (gdTell(in) != offset) { - GD2_DBG(gd_error("Positioning in file to %d", offset)); - gdSeek(in, offset); - } else { - GD2_DBG(gd_error("Already Positioned in file to %d", offset)); - } - - /* Read and uncompress an entire chunk. */ - GD2_DBG(gd_error("Reading file")); - if (gdGetBuf(compBuf, compSize, in) != compSize) { - return FALSE; - } - GD2_DBG(gd_error("Got %d bytes. Uncompressing into buffer of %d bytes", compSize, (int)*chunkLen)); - zerr = uncompress((unsigned char *) chunkBuf, chunkLen, (unsigned char *) compBuf, compSize); - if (zerr != Z_OK) { - GD2_DBG(gd_error("Error %d from uncompress", zerr)); - return FALSE; - } - GD2_DBG(gd_error("Got chunk")); - - return TRUE; + int zerr; + + if (gdTell(in) != offset) { + GD2_DBG(gd_error("Positioning in file to %d", offset)); + gdSeek(in, offset); + } else { + GD2_DBG(gd_error("Already Positioned in file to %d", offset)); + } + + /* Read and uncompress an entire chunk. */ + GD2_DBG(gd_error("Reading file")); + if (gdGetBuf(compBuf, compSize, in) != compSize) { + return FALSE; + } + GD2_DBG( + gd_error("Got %d bytes. Uncompressing into buffer of %d bytes", compSize, (int)*chunkLen)); + zerr = uncompress((unsigned char *)chunkBuf, chunkLen, (unsigned char *)compBuf, compSize); + if (zerr != Z_OK) { + GD2_DBG(gd_error("Error %d from uncompress", zerr)); + return FALSE; + } + GD2_DBG(gd_error("Got chunk")); + + return TRUE; } +/* + Function: gdImageCreateFromGd2 + + is called to load images from gd2 format + files. Invoke with an already opened + pointer to a file containing the desired image in the gd2 file + format, which is specific to gd2 and intended for fast loading of + parts of large images. (It is a compressed format, but generally + not as good as maximum compression of the entire image would be.) + + returns a to the new image, or + NULL if unable to load the image (most often because the file is + corrupt or does not contain a gd format + image). does not close the file. You can + inspect the sx and sy members of the image to determine its + size. The image must eventually be destroyed using + . + + + Variants: + + creates an image from GD data (i.e. the + contents of a GD2 file) already in memory. -gdImagePtr gdImageCreateFromGd2 (FILE * inFile) + reads in an image using the functions in + a struct. + + Parameters: + + infile - The input FILE pointer + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + + > gdImagePtr im; + > FILE *in; + > in = fopen("mygd.gd2", "rb"); + > im = gdImageCreateFromGd2(in); + > fclose(in); + > // ... Use the image ... + > gdImageDestroy(im); +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2(FILE *inFile) { - gdIOCtx *in = gdNewFileCtx(inFile); - gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + gdImagePtr im; - im = gdImageCreateFromGd2Ctx(in); + if (in == NULL) + return NULL; + im = gdImageCreateFromGd2Ctx(in); - in->gd_free(in); + in->gd_free(in); - return im; + return im; } -gdImagePtr gdImageCreateFromGd2Ptr (int size, void *data) -{ - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromGd2Ctx(in); - in->gd_free(in); +/* + Function: gdImageCreateFromGd2Ptr + + Parameters: - return im; + size - size of GD2 data in bytes. + data - GD2 data (i.e. contents of a GIF file). + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ptr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromGd2Ctx(in); + in->gd_free(in); + + return im; } -gdImagePtr gdImageCreateFromGd2Ctx (gdIOCtxPtr in) +/* + Function: gdImageCreateFromGd2Ctx + + Reads in a GD2 image via a struct. See + . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx(gdIOCtxPtr in) { - int sx, sy; - int i; - int ncx, ncy, nc, cs, cx, cy; - int x, y, ylo, yhi, xlo, xhi; - int vers, fmt; - t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */ - unsigned char *chunkBuf = NULL; /* So we can gdFree it with impunity. */ - int chunkNum = 0; - int chunkMax = 0; - uLongf chunkLen; - int chunkPos = 0; - int compMax = 0; - int bytesPerPixel; - char *compBuf = NULL; /* So we can gdFree it with impunity. */ - - gdImagePtr im; - - /* Get the header */ - if (!(im = _gd2CreateFromFile(in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx))) { - return 0; - } - - bytesPerPixel = im->trueColor ? 4 : 1; - nc = ncx * ncy; - - if (gd2_compressed(fmt)) { - /* Find the maximum compressed chunk size. */ - compMax = 0; - for (i = 0; (i < nc); i++) { - if (chunkIdx[i].size > compMax) { - compMax = chunkIdx[i].size; - } - } - compMax++; - - /* Allocate buffers */ - chunkMax = cs * bytesPerPixel * cs; - if (chunkMax <= 0) { - return 0; - } - chunkBuf = gdCalloc(chunkMax, 1); - compBuf = gdCalloc(compMax, 1); - - GD2_DBG(gd_error("Largest compressed chunk is %d bytes", compMax)); - } - - /* Read the data... */ - for (cy = 0; (cy < ncy); cy++) { - for (cx = 0; (cx < ncx); cx++) { - ylo = cy * cs; - yhi = ylo + cs; - if (yhi > im->sy) { - yhi = im->sy; - } - - GD2_DBG(gd_error("Processing Chunk %d (%d, %d), y from %d to %d", chunkNum, cx, cy, ylo, yhi)); - - if (gd2_compressed(fmt)) { - chunkLen = chunkMax; - - if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *) chunkBuf, &chunkLen, in)) { - GD2_DBG(gd_error("Error reading comproessed chunk")); - goto fail2; - } - - chunkPos = 0; - } - - for (y = ylo; (y < yhi); y++) { - xlo = cx * cs; - xhi = xlo + cs; - if (xhi > im->sx) { - xhi = im->sx; - } - - if (!gd2_compressed(fmt)) { - for (x = xlo; x < xhi; x++) { - if (im->trueColor) { - if (!gdGetInt(&im->tpixels[y][x], in)) { - gd_error("gd2: EOF while reading\n"); - gdImageDestroy(im); - return NULL; - } - } else { - int ch; - if (!gdGetByte(&ch, in)) { - gd_error("gd2: EOF while reading\n"); - gdImageDestroy(im); - return NULL; - } - im->pixels[y][x] = ch; - } - } - } else { - for (x = xlo; x < xhi; x++) { - if (im->trueColor) { - /* 2.0.1: work around a gcc bug by being verbose. TBB */ - int a = chunkBuf[chunkPos++] << 24; - int r = chunkBuf[chunkPos++] << 16; - int g = chunkBuf[chunkPos++] << 8; - int b = chunkBuf[chunkPos++]; - im->tpixels[y][x] = a + r + g + b; - } else { - im->pixels[y][x] = chunkBuf[chunkPos++]; - } - } - } - } - chunkNum++; - } - } - - GD2_DBG(gd_error("Freeing memory")); - - if (chunkBuf) { - gdFree(chunkBuf); - } - if (compBuf) { - gdFree(compBuf); - } - if (chunkIdx) { - gdFree(chunkIdx); - } - - GD2_DBG(gd_error("Done")); - - return im; + int sx, sy; + int i; + int ncx, ncy, nc, cs, cx, cy; + int x, y, ylo, yhi, xlo, xhi; + int vers, fmt; + t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */ + unsigned char *chunkBuf = NULL; /* So we can gdFree it with impunity. */ + int chunkNum = 0; + int chunkMax = 0; + uLongf chunkLen; + int chunkPos = 0; + int compMax = 0; + int bytesPerPixel; + char *compBuf = NULL; /* So we can gdFree it with impunity. */ + + gdImagePtr im; + + /* Get the header */ + if (!(im = _gd2CreateFromFile(in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx))) { + return 0; + } + + bytesPerPixel = im->trueColor ? 4 : 1; + if (overflow2(ncx, ncy)) + goto fail; + nc = ncx * ncy; + + if (overflow2(ncy, cs) || overflow2(ncx, cs) || overflow2(bytesPerPixel, cs)) + goto fail; + + if (gd2_compressed(fmt)) { + /* Find the maximum compressed chunk size. */ + compMax = 0; + for (i = 0; (i < nc); i++) { + if (chunkIdx[i].size > compMax) { + compMax = chunkIdx[i].size; + } + } + compMax++; + + /* Allocate buffers */ + chunkMax = cs * bytesPerPixel * cs; + chunkBuf = gdCalloc(chunkMax, 1); + if (!chunkBuf) { + goto fail; + } + compBuf = gdCalloc(compMax, 1); + if (!compBuf) { + goto fail; + } + + GD2_DBG(printf("Largest compressed chunk is %d bytes\n", compMax)); + }; + + /* Read the data... */ + for (cy = 0; (cy < ncy); cy++) { + for (cx = 0; (cx < ncx); cx++) { + ylo = cy * cs; + yhi = ylo + cs; + if (yhi > im->sy) { + yhi = im->sy; + } + + GD2_DBG(gd_error("Processing Chunk %d (%d, %d), y from %d to %d", chunkNum, cx, cy, ylo, + yhi)); + + if (gd2_compressed(fmt)) { + chunkLen = chunkMax; + + if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, + (char *)chunkBuf, &chunkLen, in)) { + GD2_DBG(gd_error("Error reading comproessed chunk")); + goto fail; + } + + chunkPos = 0; + } + + for (y = ylo; (y < yhi); y++) { + xlo = cx * cs; + xhi = xlo + cs; + if (xhi > im->sx) { + xhi = im->sx; + } + + if (!gd2_compressed(fmt)) { + for (x = xlo; x < xhi; x++) { + if (im->trueColor) { + if (!gdGetInt(&im->tpixels[y][x], in)) { + gd_error("gd2: EOF while reading\n"); + goto fail; + } + } else { + int ch; + if (!gdGetByte(&ch, in)) { + gd_error("gd2: EOF while reading\n"); + goto fail; + } + im->pixels[y][x] = ch; + } + } + } else { + for (x = xlo; x < xhi; x++) { + if (im->trueColor) { + /* 2.0.1: work around a gcc bug by being verbose. TBB */ + int a = chunkBuf[chunkPos++] << 24; + int r = chunkBuf[chunkPos++] << 16; + int g = chunkBuf[chunkPos++] << 8; + int b = chunkBuf[chunkPos++]; + im->tpixels[y][x] = a + r + g + b; + } else { + im->pixels[y][x] = chunkBuf[chunkPos++]; + } + } + } + } + chunkNum++; + } + } + + GD2_DBG(gd_error("Freeing memory")); + + if (chunkBuf) { + gdFree(chunkBuf); + } + if (compBuf) { + gdFree(compBuf); + } + if (chunkIdx) { + gdFree(chunkIdx); + } + + GD2_DBG(gd_error("Done")); + + return im; -fail2: - gdImageDestroy(im); - if (chunkBuf) { - gdFree(chunkBuf); - } - if (compBuf) { - gdFree(compBuf); - } - if (chunkIdx) { - gdFree(chunkIdx); - } - - return 0; +fail: + gdImageDestroy(im); + if (chunkBuf) { + gdFree(chunkBuf); + } + if (compBuf) { + gdFree(compBuf); + } + if (chunkIdx) { + gdFree(chunkIdx); + } + + return 0; } -gdImagePtr gdImageCreateFromGd2PartPtr (int size, void *data, int srcx, int srcy, int w, int h) +/* + Function: gdImageCreateFromGd2Part + + is called to load parts of images from + gd2 format files. Invoked in the same way as , + but with extra parameters indicating the source (x, y) and + width/height of the desired image. + returns a to the new image, or NULL if unable to load + the image. The image must eventually be destroyed using + . + + Variants: + + creates an image from GD2 data + (i.e. the contents of a GD2 file) already in memory. + + reads in an image using the functions in + a struct. + + Parameters: + + infile - The input FILE pointer + srcx, srcy - The source X and Y coordinates + w, h - The resulting image's width and height + + Returns: + + A pointer to the new image or NULL if an error occurred. + +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Part(FILE *inFile, int srcx, int srcy, int w, int h) { - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h); - in->gd_free(in); + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h); + + in->gd_free(in); - return im; + return im; } -gdImagePtr gdImageCreateFromGd2Part (FILE * inFile, int srcx, int srcy, int w, int h) -{ - gdImagePtr im; - gdIOCtx *in = gdNewFileCtx(inFile); +/* + Function: gdImageCreateFromGd2PartPtr - im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h); + Parameters: - in->gd_free(in); + size - size of GD data in bytes. + data - GD data (i.e. contents of a GIF file). + srcx, srcy - The source X and Y coordinates + w, h - The resulting image's width and height - return im; + Reads in part of a GD2 image file stored from memory. See + . +*/ +BGD_DECLARE(gdImagePtr) +gdImageCreateFromGd2PartPtr(int size, void *data, int srcx, int srcy, int w, int h) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h); + in->gd_free(in); + return im; } -gdImagePtr gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h) +/* + Function: gdImageCreateFromGd2PartCtx + + Parameters: + + in - The data source. + srcx, srcy - The source X and Y coordinates + w, h - The resulting image's width and height + + Reads in part of a GD2 data image file via a struct. See + . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartCtx(gdIOCtx *in, int srcx, int srcy, int w, int h) { - int scx, scy, ecx, ecy, fsx, fsy; - int nc, ncx, ncy, cs, cx, cy; - int x, y, ylo, yhi, xlo, xhi; - int dstart, dpos; - int i; - /* 2.0.12: unsigned is correct; fixes problems with color munging. Thanks to Steven Brown. */ - unsigned int ch; - int vers, fmt; - t_chunk_info *chunkIdx = NULL; - unsigned char *chunkBuf = NULL; - int chunkNum; - int chunkMax = 0; - uLongf chunkLen; - int chunkPos = 0; - int compMax; - char *compBuf = NULL; - - gdImagePtr im; - - if (w<1 || h <1) { - return 0; - } - - /* The next few lines are basically copied from gd2CreateFromFile - * we change the file size, so don't want to use the code directly. - * but we do need to know the file size. - */ - if (_gd2GetHeader(in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1) { - goto fail1; - } - - GD2_DBG(gd_error("File size is %dx%d", fsx, fsy)); - - /* This is the difference - make a file based on size of chunks. */ - if (gd2_truecolor(fmt)) { - im = gdImageCreateTrueColor(w, h); - } else { - im = gdImageCreate(w, h); - } - if (im == NULL) { - goto fail1; - } - - if (!_gdGetColors(in, im, vers == 2)) { - goto fail2; - } - GD2_DBG(gd_error("Image palette completed: %d colours", im->colorsTotal)); - - /* Process the header info */ - nc = ncx * ncy; - - if (gd2_compressed(fmt)) { - /* Find the maximum compressed chunk size. */ - compMax = 0; - for (i = 0; (i < nc); i++) { - if (chunkIdx[i].size > compMax) { - compMax = chunkIdx[i].size; - } - } - compMax++; - - if (im->trueColor) { - chunkMax = cs * cs * 4; - } else { - chunkMax = cs * cs; - } - if (chunkMax <= 0) { - goto fail2; - } - - chunkBuf = gdCalloc(chunkMax, 1); - compBuf = gdCalloc(compMax, 1); - } - - /* Work out start/end chunks */ - scx = srcx / cs; - scy = srcy / cs; - if (scx < 0) { - scx = 0; - } - if (scy < 0) { - scy = 0; - } - - ecx = (srcx + w) / cs; - ecy = (srcy + h) / cs; - if (ecx >= ncx) { - ecx = ncx - 1; - } - if (ecy >= ncy) { - ecy = ncy - 1; - } - - /* Remember file position of image data. */ - dstart = gdTell(in); - GD2_DBG(gd_error("Data starts at %d", dstart)); - - /* Loop through the chunks. */ - for (cy = scy; (cy <= ecy); cy++) { - ylo = cy * cs; - yhi = ylo + cs; - if (yhi > fsy) { - yhi = fsy; - } - - for (cx = scx; cx <= ecx; cx++) { - - xlo = cx * cs; - xhi = xlo + cs; - if (xhi > fsx) { - xhi = fsx; - } - - GD2_DBG(gd_error("Processing Chunk (%d, %d), from %d to %d", cx, cy, ylo, yhi)); - - if (!gd2_compressed(fmt)) { - GD2_DBG(gd_error("Using raw format data")); - if (im->trueColor) { - dpos = (cy * (cs * fsx) * 4 + cx * cs * (yhi - ylo) * 4) + dstart; - } else { - dpos = cy * (cs * fsx) + cx * cs * (yhi - ylo) + dstart; - } - - /* gd 2.0.11: gdSeek returns TRUE on success, not 0. Longstanding bug. 01/16/03 */ - if (!gdSeek(in, dpos)) { - gd_error_ex(E_WARNING, "Error from seek: %d", errno); - goto fail2; - } - GD2_DBG(gd_error("Reading (%d, %d) from position %d", cx, cy, dpos - dstart)); - } else { - chunkNum = cx + cy * ncx; - - chunkLen = chunkMax; - if (!_gd2ReadChunk (chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *)chunkBuf, &chunkLen, in)) { - gd_error("Error reading comproessed chunk"); - goto fail2; - } - chunkPos = 0; - GD2_DBG(gd_error("Reading (%d, %d) from chunk %d", cx, cy, chunkNum)); - } - - GD2_DBG(gd_error(" into (%d, %d) - (%d, %d)", xlo, ylo, xhi, yhi)); - - for (y = ylo; (y < yhi); y++) { - for (x = xlo; x < xhi; x++) { - if (!gd2_compressed(fmt)) { - if (im->trueColor) { - if (!gdGetInt((int *)&ch, in)) { - ch = 0; - } - } else { - ch = gdGetC(in); - if ((int)ch == EOF) { - ch = 0; - } - } - } else { - if (im->trueColor) { - ch = chunkBuf[chunkPos++]; - ch = (ch << 8) + chunkBuf[chunkPos++]; - ch = (ch << 8) + chunkBuf[chunkPos++]; - ch = (ch << 8) + chunkBuf[chunkPos++]; - } else { - ch = chunkBuf[chunkPos++]; - } - } - - /* Only use a point that is in the image. */ - if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0) && (y >= srcy) && (y < (srcy + h)) && (y < fsy) && (y >= 0)) { - if (im->trueColor) { - im->tpixels[y - srcy][x - srcx] = ch; - } else { - im->pixels[y - srcy][x - srcx] = ch; - } - } - } - } - } - } - - if (chunkBuf) { - gdFree(chunkBuf); - } - if (compBuf) { - gdFree(compBuf); - } - if (chunkIdx) { - gdFree(chunkIdx); - } - - return im; + int scx, scy, ecx, ecy, fsx, fsy; + int nc, ncx, ncy, cs, cx, cy; + int x, y, ylo, yhi, xlo, xhi; + int dstart, dpos; + int i; + /* 2.0.12: unsigned is correct; fixes problems with color munging. Thanks to Steven Brown. */ + unsigned int ch; + int vers, fmt; + t_chunk_info *chunkIdx = NULL; + unsigned char *chunkBuf = NULL; + int chunkNum; + int chunkMax = 0; + uLongf chunkLen; + int chunkPos = 0; + int compMax; + char *compBuf = NULL; + + gdImagePtr im; + if (w < 1 || h < 1) { + return 0; + } + + /* The next few lines are basically copied from gd2CreateFromFile + * we change the file size, so don't want to use the code directly. + * but we do need to know the file size. + */ + if (_gd2GetHeader(in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1) { + goto fail1; + } + + GD2_DBG(gd_error("File size is %dx%d", fsx, fsy)); + + /* This is the difference - make a file based on size of chunks. */ + if (gd2_truecolor(fmt)) { + im = gdImageCreateTrueColor(w, h); + } else { + im = gdImageCreate(w, h); + } + if (im == NULL) { + goto fail1; + } + + if (!_gdGetColors(in, im, vers == 2)) { + goto fail2; + } + GD2_DBG(gd_error("Image palette completed: %d colours", im->colorsTotal)); + + /* Process the header info */ + nc = ncx * ncy; + + if (gd2_compressed(fmt)) { + /* Find the maximum compressed chunk size. */ + compMax = 0; + for (i = 0; (i < nc); i++) { + if (chunkIdx[i].size > compMax) { + compMax = chunkIdx[i].size; + } + } + compMax++; + + if (im->trueColor) { + chunkMax = cs * cs * 4; + } else { + chunkMax = cs * cs; + } + if (chunkMax <= 0) { + goto fail2; + } + + chunkBuf = gdCalloc(chunkMax, 1); + if (!chunkBuf) { + goto fail2; + } + compBuf = gdCalloc(compMax, 1); + if (!compBuf) { + goto fail2; + } + } + + /* Work out start/end chunks */ + scx = srcx / cs; + scy = srcy / cs; + if (scx < 0) { + scx = 0; + } + if (scy < 0) { + scy = 0; + } + + ecx = (srcx + w) / cs; + ecy = (srcy + h) / cs; + if (ecx >= ncx) { + ecx = ncx - 1; + } + if (ecy >= ncy) { + ecy = ncy - 1; + } + + /* Remember file position of image data. */ + dstart = gdTell(in); + GD2_DBG(gd_error("Data starts at %d", dstart)); + + /* Loop through the chunks. */ + for (cy = scy; (cy <= ecy); cy++) { + ylo = cy * cs; + yhi = ylo + cs; + if (yhi > fsy) { + yhi = fsy; + } + + for (cx = scx; (cx <= ecx); cx++) { + + xlo = cx * cs; + xhi = xlo + cs; + if (xhi > fsx) { + xhi = fsx; + } + + GD2_DBG(gd_error("Processing Chunk (%d, %d), from %d to %d", cx, cy, ylo, yhi)); + + if (!gd2_compressed(fmt)) { + GD2_DBG(gd_error("Using raw format data")); + if (im->trueColor) { + dpos = (cy * (cs * fsx) * 4 + cx * cs * (yhi - ylo) * 4) + dstart; + } else { + dpos = cy * (cs * fsx) + cx * cs * (yhi - ylo) + dstart; + } + + /* gd 2.0.11: gdSeek returns TRUE on success, not 0. Longstanding bug. 01/16/03 */ + if (!gdSeek(in, dpos)) { + gd_error_ex(E_WARNING, "Error from seek: %d", errno); + goto fail2; + } + GD2_DBG(gd_error("Reading (%d, %d) from position %d", cx, cy, dpos - dstart)); + } else { + chunkNum = cx + cy * ncx; + + chunkLen = chunkMax; + if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, + (char *)chunkBuf, &chunkLen, in)) { + gd_error("Error reading comproessed chunk"); + goto fail2; + } + chunkPos = 0; + GD2_DBG(gd_error("Reading (%d, %d) from chunk %d", cx, cy, chunkNum)); + } + + GD2_DBG(gd_error(" into (%d, %d) - (%d, %d)", xlo, ylo, xhi, yhi)); + for (y = ylo; (y < yhi); y++) { + for (x = xlo; x < xhi; x++) { + if (!gd2_compressed(fmt)) { + if (im->trueColor) { + if (!gdGetInt((int *)&ch, in)) { + ch = 0; + } + } else { + ch = gdGetC(in); + if ((int)ch == EOF) { + ch = 0; + } + } + } else { + if (im->trueColor) { + ch = chunkBuf[chunkPos++]; + ch = (ch << 8) + chunkBuf[chunkPos++]; + ch = (ch << 8) + chunkBuf[chunkPos++]; + ch = (ch << 8) + chunkBuf[chunkPos++]; + } else { + ch = chunkBuf[chunkPos++]; + } + } + + /* Only use a point that is in the image. */ + if ((x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0) && (y >= srcy) && + (y < (srcy + h)) && (y < fsy) && (y >= 0)) { + if (im->trueColor) { + im->tpixels[y - srcy][x - srcx] = ch; + } else { + im->pixels[y - srcy][x - srcx] = ch; + } + } + } + } + } + } + + if (chunkBuf) { + gdFree(chunkBuf); + } + if (compBuf) { + gdFree(compBuf); + } + if (chunkIdx) { + gdFree(chunkIdx); + } + return im; fail2: - gdImageDestroy(im); + gdImageDestroy(im); fail1: - if (chunkBuf) { - gdFree(chunkBuf); - } - if (compBuf) { - gdFree(compBuf); - } - if (chunkIdx) { - gdFree(chunkIdx); - } - - return 0; + if (chunkBuf) { + gdFree(chunkBuf); + } + if (compBuf) { + gdFree(compBuf); + } + if (chunkIdx) { + gdFree(chunkIdx); + } + return 0; } -static void _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy) +static void _gd2PutHeader(gdImagePtr im, gdIOCtx *out, int cs, int fmt, int cx, int cy) { - int i; - - /* Send the gd2 id, to verify file format. */ - for (i = 0; i < 4; i++) { - gdPutC((unsigned char) (GD2_ID[i]), out); - } - - /* We put the version info first, so future versions can easily change header info. */ - - gdPutWord(GD2_VERS, out); - gdPutWord(im->sx, out); - gdPutWord(im->sy, out); - gdPutWord(cs, out); - gdPutWord(fmt, out); - gdPutWord(cx, out); - gdPutWord(cy, out); + int i; + + /* Send the gd2 id, to verify file format. */ + for (i = 0; i < 4; i++) { + gdPutC((unsigned char)(GD2_ID[i]), out); + } + + /* We put the version info first, so future versions can easily change header info. */ + + gdPutWord(GD2_VERS, out); + gdPutWord(im->sx, out); + gdPutWord(im->sy, out); + gdPutWord(cs, out); + gdPutWord(fmt, out); + gdPutWord(cx, out); + gdPutWord(cy, out); } -static void _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) +/* returns 0 on success, 1 on failure */ +static int _gdImageGd2(gdImagePtr im, gdIOCtx *out, int cs, int fmt) { - int ncx, ncy, cx, cy; - int x, y, ylo, yhi, xlo, xhi; - int chunkLen; - int chunkNum = 0; - char *chunkData = NULL; /* So we can gdFree it with impunity. */ - char *compData = NULL; /* So we can gdFree it with impunity. */ - uLongf compLen; - int idxPos = 0; - int idxSize; - t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */ - int posSave; - int bytesPerPixel = im->trueColor ? 4 : 1; - int compMax = 0; - - /* Force fmt to a valid value since we don't return anything. */ - if ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)) { - fmt = GD2_FMT_COMPRESSED; - } - if (im->trueColor) { - fmt += 2; - } - /* Make sure chunk size is valid. These are arbitrary values; 64 because it seems - * a little silly to expect performance improvements on a 64x64 bit scale, and - * 4096 because we buffer one chunk, and a 16MB buffer seems a little large - it may be - * OK for one user, but for another to read it, they require the buffer. - */ - if (cs == 0) { - cs = GD2_CHUNKSIZE; - } else if (cs < GD2_CHUNKSIZE_MIN) { - cs = GD2_CHUNKSIZE_MIN; - } else if (cs > GD2_CHUNKSIZE_MAX) { - cs = GD2_CHUNKSIZE_MAX; - } - - /* Work out number of chunks. */ - ncx = (im->sx + cs - 1) / cs; - ncy = (im->sy + cs - 1) / cs; - - /* Write the standard header. */ - _gd2PutHeader (im, out, cs, fmt, ncx, ncy); - - if (gd2_compressed(fmt)) { - /* Work out size of buffer for compressed data, If CHUNKSIZE is large, - * then these will be large! - */ - - /* The zlib notes say output buffer size should be (input size) * 1.01 * 12 - * - we'll use 1.02 to be paranoid. - */ - compMax = (int)(cs * bytesPerPixel * cs * 1.02f) + 12; - - /* Allocate the buffers. */ - chunkData = safe_emalloc(cs * bytesPerPixel, cs, 0); - memset(chunkData, 0, cs * bytesPerPixel * cs); - if (compMax <= 0) { - goto fail; - } - compData = gdCalloc(compMax, 1); - - /* Save the file position of chunk index, and allocate enough space for - * each chunk_info block . - */ - idxPos = gdTell(out); - idxSize = ncx * ncy * sizeof(t_chunk_info); - GD2_DBG(gd_error("Index size is %d", idxSize)); - gdSeek(out, idxPos + idxSize); - - chunkIdx = safe_emalloc(idxSize, sizeof(t_chunk_info), 0); - memset(chunkIdx, 0, idxSize * sizeof(t_chunk_info)); - } - - _gdPutColors (im, out); - - GD2_DBG(gd_error("Size: %dx%d", im->sx, im->sy)); - GD2_DBG(gd_error("Chunks: %dx%d", ncx, ncy)); - - for (cy = 0; (cy < ncy); cy++) { - for (cx = 0; (cx < ncx); cx++) { - ylo = cy * cs; - yhi = ylo + cs; - if (yhi > im->sy) { - yhi = im->sy; - } - - GD2_DBG(gd_error("Processing Chunk (%dx%d), y from %d to %d", cx, cy, ylo, yhi)); - chunkLen = 0; - for (y = ylo; (y < yhi); y++) { - GD2_DBG(gd_error("y=%d: ",y)); - xlo = cx * cs; - xhi = xlo + cs; - if (xhi > im->sx) { - xhi = im->sx; - } - - if (gd2_compressed(fmt)) { - for (x = xlo; x < xhi; x++) { - GD2_DBG(gd_error("%d...",x)); - if (im->trueColor) { - int p = im->tpixels[y][x]; - chunkData[chunkLen++] = gdTrueColorGetAlpha(p); - chunkData[chunkLen++] = gdTrueColorGetRed(p); - chunkData[chunkLen++] = gdTrueColorGetGreen(p); - chunkData[chunkLen++] = gdTrueColorGetBlue(p); - } else { - chunkData[chunkLen++] = im->pixels[y][x]; - } - } - } else { - for (x = xlo; x < xhi; x++) { - GD2_DBG(gd_error("%d, ",x)); - - if (im->trueColor) { - gdPutInt(im->tpixels[y][x], out); - } else { - gdPutC((unsigned char) im->pixels[y][x], out); - } - } - } - GD2_DBG(gd_error("y=%d done.",y)); - } - - if (gd2_compressed(fmt)) { - compLen = compMax; - if (compress((unsigned char *) &compData[0], &compLen, (unsigned char *) &chunkData[0], chunkLen) != Z_OK) { - gd_error("Error from compressing"); - } else { - chunkIdx[chunkNum].offset = gdTell(out); - chunkIdx[chunkNum++].size = compLen; - GD2_DBG(gd_error("Chunk %d size %d offset %d", chunkNum, chunkIdx[chunkNum - 1].size, chunkIdx[chunkNum - 1].offset)); - - if (gdPutBuf (compData, compLen, out) <= 0) { - /* Any alternate suggestions for handling this? */ - gd_error_ex(E_WARNING, "Error %d on write", errno); - } - } - } - } - } - - if (gd2_compressed(fmt)) { - /* Save the position, write the index, restore position (paranoia). */ - GD2_DBG(gd_error("Seeking %d to write index", idxPos)); - posSave = gdTell(out); - gdSeek(out, idxPos); - GD2_DBG(gd_error("Writing index")); - for (x = 0; x < chunkNum; x++) { - GD2_DBG(gd_error("Chunk %d size %d offset %d", x, chunkIdx[x].size, chunkIdx[x].offset)); - gdPutInt(chunkIdx[x].offset, out); - gdPutInt(chunkIdx[x].size, out); - } - gdSeek(out, posSave); - } + int ret = 0; + int ncx, ncy, cx, cy; + int x, y, ylo, yhi, xlo, xhi; + int chunkLen; + int chunkNum = 0; + char *chunkData = NULL; /* So we can gdFree it with impunity. */ + char *compData = NULL; /* So we can gdFree it with impunity. */ + uLongf compLen; + int idxPos = 0; + int idxSize; + t_chunk_info *chunkIdx = NULL; /* So we can gdFree it with impunity. */ + int posSave; + int bytesPerPixel = im->trueColor ? 4 : 1; + int compMax = 0; + + /* Force fmt to a valid value since we don't return anything. */ + if ((fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED)) { + fmt = GD2_FMT_COMPRESSED; + } + if (im->trueColor) { + fmt += 2; + } + /* Make sure chunk size is valid. These are arbitrary values; 64 because it seems + * a little silly to expect performance improvements on a 64x64 bit scale, and + * 4096 because we buffer one chunk, and a 16MB buffer seems a little large - it may be + * OK for one user, but for another to read it, they require the buffer. + */ + if (cs == 0) { + cs = GD2_CHUNKSIZE; + } else if (cs < GD2_CHUNKSIZE_MIN) { + cs = GD2_CHUNKSIZE_MIN; + } else if (cs > GD2_CHUNKSIZE_MAX) { + cs = GD2_CHUNKSIZE_MAX; + } + + /* Work out number of chunks. */ + ncx = (im->sx + cs - 1) / cs; + ncy = (im->sy + cs - 1) / cs; + + /* Write the standard header. */ + _gd2PutHeader(im, out, cs, fmt, ncx, ncy); + + if (gd2_compressed(fmt)) { + /* Work out size of buffer for compressed data, If CHUNKSIZE is large, + * then these will be large! + * The zlib notes say output buffer size should be (input size) * 1.01 * 12 + * - we'll use 1.02 to be paranoid. + */ + compMax = (int)(cs * bytesPerPixel * cs * 1.02f) + 12; + + chunkData = gdCalloc(cs * bytesPerPixel * cs, 1); + if (!chunkData) { + ret = 1; + goto fail; + } + compData = gdCalloc(compMax, 1); + if (!compData) { + ret = 1; + goto fail; + } + + /* Save the file position of chunk index, and allocate enough space for + * each chunk_info block . + */ + idxPos = gdTell(out); + idxSize = ncx * ncy * sizeof(t_chunk_info); + GD2_DBG(gd_error("Index size is %d", idxSize)); + gdSeek(out, idxPos + idxSize); + + chunkIdx = gdCalloc(idxSize * sizeof(t_chunk_info), 1); + if (!chunkIdx) { + ret = 1; + goto fail; + } + } + + _gdPutColors(im, out); + + GD2_DBG(gd_error("Size: %dx%d", im->sx, im->sy)); + GD2_DBG(gd_error("Chunks: %dx%d", ncx, ncy)); + + for (cy = 0; (cy < ncy); cy++) { + for (cx = 0; (cx < ncx); cx++) { + ylo = cy * cs; + yhi = ylo + cs; + if (yhi > im->sy) { + yhi = im->sy; + } + + GD2_DBG(gd_error("Processing Chunk (%dx%d), y from %d to %d", cx, cy, ylo, yhi)); + chunkLen = 0; + for (y = ylo; (y < yhi); y++) { + xlo = cx * cs; + xhi = xlo + cs; + if (xhi > im->sx) { + xhi = im->sx; + } + + if (gd2_compressed(fmt)) { + for (x = xlo; x < xhi; x++) { + if (im->trueColor) { + int p = im->tpixels[y][x]; + chunkData[chunkLen++] = gdTrueColorGetAlpha(p); + chunkData[chunkLen++] = gdTrueColorGetRed(p); + chunkData[chunkLen++] = gdTrueColorGetGreen(p); + chunkData[chunkLen++] = gdTrueColorGetBlue(p); + } else { + chunkData[chunkLen++] = im->pixels[y][x]; + } + } + } else { + for (x = xlo; x < xhi; x++) { + if (im->trueColor) { + gdPutInt(im->tpixels[y][x], out); + } else { + gdPutC((unsigned char)im->pixels[y][x], out); + } + } + } + } + if (gd2_compressed(fmt)) { + compLen = compMax; + if (compress((unsigned char *)&compData[0], &compLen, + (unsigned char *)&chunkData[0], chunkLen) != Z_OK) { + gd_error("Error from compressing"); + } else { + chunkIdx[chunkNum].offset = gdTell(out); + chunkIdx[chunkNum++].size = compLen; + GD2_DBG(gd_error("Chunk %d size %d offset %d", chunkNum, + chunkIdx[chunkNum - 1].size, chunkIdx[chunkNum - 1].offset)); + + if (gdPutBuf(compData, compLen, out) <= 0) { + /* Any alternate suggestions for handling this? */ + gd_error_ex(E_WARNING, "Error %d on write", errno); + } + } + } + } + } + if (gd2_compressed(fmt)) { + /* Save the position, write the index, restore position (paranoia). */ + GD2_DBG(gd_error("Seeking %d to write index", idxPos)); + posSave = gdTell(out); + gdSeek(out, idxPos); + GD2_DBG(gd_error("Writing index")); + for (x = 0; x < chunkNum; x++) { + GD2_DBG( + gd_error("Chunk %d size %d offset %d", x, chunkIdx[x].size, chunkIdx[x].offset)); + gdPutInt(chunkIdx[x].offset, out); + gdPutInt(chunkIdx[x].size, out); + } + gdSeek(out, posSave); + } fail: - GD2_DBG(gd_error("Freeing memory")); - if (chunkData) { - gdFree(chunkData); - } - if (compData) { - gdFree(compData); - } - if (chunkIdx) { - gdFree(chunkIdx); - } - GD2_DBG(gd_error("Done")); + GD2_DBG(gd_error("Freeing memory")); + if (chunkData) { + gdFree(chunkData); + } + if (compData) { + gdFree(compData); + } + if (chunkIdx) { + gdFree(chunkIdx); + } + GD2_DBG(gd_error("Done")); + + return ret; } -void gdImageGd2 (gdImagePtr im, FILE * outFile, int cs, int fmt) +/* + Function: gdImageGd2 +*/ +BGD_DECLARE(void) gdImageGd2(gdImagePtr im, FILE *outFile, int cs, int fmt) { - gdIOCtx *out = gdNewFileCtx(outFile); - - _gdImageGd2(im, out, cs, fmt); - - out->gd_free(out); + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + _gdImageGd2(im, out, cs, fmt); + out->gd_free(out); } -void *gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size) +/* + Function: gdImageGd2Ptr +*/ +BGD_DECLARE(void *) gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - - _gdImageGd2(im, out, cs, fmt); - rv = gdDPExtractData(out, size); - out->gd_free(out); - - return rv; + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + + if (_gdImageGd2(im, out, cs, fmt)) { + rv = NULL; + } else { + rv = gdDPExtractData(out, size); + } + + out->gd_free(out); + return rv; } diff --git a/ext/gd/libgd/gd_gif_in.c b/ext/gd/libgd/gd_gif_in.c index 0204c4158db4..5a19a8a3abd1 100644 --- a/ext/gd/libgd/gd_gif_in.c +++ b/ext/gd/libgd/gd_gif_in.c @@ -1,9 +1,15 @@ -#include -#include -#include -#include +/** + * File: GIF Input + * + * Read GIF images. + */ #include "gd.h" #include "gd_errors.h" +#include "gdhelpers.h" +#include +#include +#include +#include #include "php.h" @@ -14,13 +20,13 @@ static int verbose_set = 0; static int verbose; -#define VERBOSE (verbose_set?verbose:set_verbose()) +#define VERBOSE (verbose_set ? verbose : set_verbose()) static int set_verbose(void) { - verbose = !!getenv("GIF_VERBOSE"); - verbose_set = 1; - return(verbose); + verbose = !!getenv("GIF_VERBOSE"); + verbose_set = 1; + return (verbose); } #else @@ -29,25 +35,25 @@ static int set_verbose(void) #endif +#define MAXCOLORMAPSIZE 256 -#define MAXCOLORMAPSIZE 256 +#define TRUE 1 +#define FALSE 0 -#define TRUE 1 -#define FALSE 0 +#define CM_RED 0 +#define CM_GREEN 1 +#define CM_BLUE 2 -#define CM_RED 0 -#define CM_GREEN 1 -#define CM_BLUE 2 +#define MAX_LWZ_BITS 12 -#define MAX_LWZ_BITS 12 +#define INTERLACE 0x40 +#define LOCALCOLORMAP 0x80 -#define INTERLACE 0x40 -#define LOCALCOLORMAP 0x80 -#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) +#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) -#define ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) > 0) +#define ReadOK(file, buffer, len) (gdGetBuf(buffer, len, file) == len) -#define LM_to_uint(a,b) (((b)<<8)|(a)) +#define LM_to_uint(a, b) (((b) << 8) | (a)) /* We may eventually want to use this information, but def it out for now */ #if 0 @@ -71,581 +77,1469 @@ static struct { } Gif89 = { -1, -1, -1, 0 }; #endif -#define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2) +#define STACK_SIZE ((1 << (MAX_LWZ_BITS)) * 2) #define CSD_BUF_SIZE 280 typedef struct { - unsigned char buf[CSD_BUF_SIZE]; - int curbit, lastbit, done, last_byte; + unsigned char buf[CSD_BUF_SIZE]; + int curbit; + int lastbit; + int done; + int last_byte; } CODE_STATIC_DATA; typedef struct { - int fresh; - int code_size, set_code_size; - int max_code, max_code_size; - int firstcode, oldcode; - int clear_code, end_code; - int table[2][(1<< MAX_LWZ_BITS)]; - int stack[STACK_SIZE], *sp; - CODE_STATIC_DATA scd; + int fresh; + int code_size, set_code_size; + int max_code, max_code_size; + int firstcode, oldcode; + int clear_code, end_code; + int table[2][(1 << MAX_LWZ_BITS)]; + int stack[STACK_SIZE], *sp; + CODE_STATIC_DATA scd; } LZW_STATIC_DATA; -static int ReadColorMap (gdIOCtx *fd, int number, unsigned char (*buffer)[256]); -static int DoExtension (gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP); -static int GetDataBlock (gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP); -static int GetCode (gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP); -static int LWZReadByte (gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP); +static int ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256]); +static int DoExtension(gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP); +static int GetDataBlock(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP); +static int GetCode(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, + int *ZeroDataBlockP); +static int LWZReadByte(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, + int *ZeroDataBlockP); + +static int ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], + int colorCount, int interlace, int *ZeroDataBlockP); /*1.4//, int ignore); */ + +typedef struct { + int transparent; + int delay; + int disposal; +} GifGraphicControl; + +typedef struct gdGifReadStruct { + gdIOCtxPtr in; + int ownsCtx; + int done; + int error; + int pendingSeparator; + int frameIndex; + int screenWidth; + int screenHeight; + int backgroundIndex; + int haveGlobalColormap; + int globalColorCount; + int loopCount; + unsigned char globalColorMap[3][MAXCOLORMAPSIZE]; + unsigned char localColorMap[3][MAXCOLORMAPSIZE]; + GifGraphicControl gce; + gdImagePtr rawFrame; + gdImagePtr canvas; + gdImagePtr previousCanvas; + gdGifFrameInfo lastInfo; +} gdGifRead; + +static void GifResetGraphicControl(GifGraphicControl *gce); +static void GifTrimColorTable(gdImagePtr im); +static int GifReadHeader(gdGifRead *gif); +static int GifPrimeFirstImage(gdGifRead *gif); +static int GifSkipSubBlocks(gdIOCtxPtr in, int *ZeroDataBlockP); +static int GifReadApplicationExtension(gdGifRead *gif, int *ZeroDataBlockP); +static int GifReadExtension(gdGifRead *gif, int label, int *ZeroDataBlockP); +static void GifFillFrameInfo(gdGifRead *gif, gdGifFrameInfo *info); +static int GifFrameToColor(gdImagePtr frame, int color); +static int GifBackgroundColor(gdGifRead *gif, int transparentIndex); +static int GifEnsureCanvas(gdGifRead *gif, int transparentIndex); +static gdImagePtr GifCloneImage(gdImagePtr src); +static void GifApplyPreviousDisposal(gdGifRead *gif); +static int GifCompositeFrame(gdGifRead *gif); +static int GifProbeIsAnimated(gdIOCtxPtr in); + +static void GifResetGraphicControl(GifGraphicControl *gce) +{ + gce->transparent = -1; + gce->delay = 0; + gce->disposal = gdDisposalUnknown; +} + +static void GifTrimColorTable(gdImagePtr im) +{ + int i; + + for (i = im->colorsTotal - 1; i >= 0; i--) { + if (im->open[i]) { + im->colorsTotal--; + } else { + break; + } + } +} -static void ReadImage (gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP); /*1.4//, int ignore); */ +static int GifReadHeader(gdGifRead *gif) +{ + unsigned char buf[16]; + int bitPixel; + + memset(gif->globalColorMap, 0, 3 * MAXCOLORMAPSIZE); + memset(gif->localColorMap, 0, 3 * MAXCOLORMAPSIZE); + GifResetGraphicControl(&gif->gce); + gif->loopCount = 1; + + if (!ReadOK(gif->in, buf, 6)) { + return 0; + } + if (strncmp((char *)buf, "GIF", 3) != 0) { + return 0; + } + if (memcmp((char *)buf + 3, "87a", 3) != 0 && memcmp((char *)buf + 3, "89a", 3) != 0) { + return 0; + } + if (!ReadOK(gif->in, buf, 7)) { + return 0; + } + + gif->screenWidth = LM_to_uint(buf[0], buf[1]); + gif->screenHeight = LM_to_uint(buf[2], buf[3]); + if (gif->screenWidth <= 0 || gif->screenHeight <= 0) { + return 0; + } + + gif->backgroundIndex = buf[5]; + bitPixel = 2 << (buf[4] & 0x07); + gif->globalColorCount = bitPixel; + gif->haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP); + if (gif->haveGlobalColormap) { + if (ReadColorMap(gif->in, bitPixel, gif->globalColorMap)) { + return 0; + } + } + + return 1; +} -gdImagePtr gdImageCreateFromGifSource(gdSourcePtr inSource) /* {{{ */ +static int GifSkipSubBlocks(gdIOCtxPtr in, int *ZeroDataBlockP) { - gdIOCtx *in = gdNewSSCtx(inSource, NULL); - gdImagePtr im; + unsigned char buf[256]; + int count; - im = gdImageCreateFromGifCtx(in); + do { + count = GetDataBlock(in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + } while (count > 0); + + return 1; +} - in->gd_free(in); +static int GifReadApplicationExtension(gdGifRead *gif, int *ZeroDataBlockP) +{ + unsigned char buf[256]; + int count; + + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + + if (count == 11 && memcmp(buf, "NETSCAPE2.0", 11) == 0) { + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + if (count >= 3 && buf[0] == 1) { + gif->loopCount = LM_to_uint(buf[1], buf[2]); + } + while (count > 0) { + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + } + return 1; + } + + while (count > 0) { + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + } + return 1; +} - return im; +static int GifReadExtension(gdGifRead *gif, int label, int *ZeroDataBlockP) +{ + unsigned char buf[256]; + int count; + + if (label == 0xf9) { + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + if (count >= 4) { + gif->gce.disposal = (buf[0] >> 2) & 0x7; + if (gif->gce.disposal == 4) { + gif->gce.disposal = gdDisposalRestorePrevious; + } + gif->gce.delay = LM_to_uint(buf[1], buf[2]); + gif->gce.transparent = (buf[0] & 0x1) ? buf[3] : -1; + } + while (count > 0) { + count = GetDataBlock(gif->in, buf, ZeroDataBlockP); + if (count < 0) { + return 0; + } + } + return 1; + } + + if (label == 0xff) { + return GifReadApplicationExtension(gif, ZeroDataBlockP); + } + + return GifSkipSubBlocks(gif->in, ZeroDataBlockP); } -/* }}} */ -gdImagePtr gdImageCreateFromGif(FILE *fdFile) /* {{{ */ +static int GifPrimeFirstImage(gdGifRead *gif) { - gdIOCtx *fd = gdNewFileCtx(fdFile); - gdImagePtr im = 0; + unsigned char c; + int ZeroDataBlock = FALSE; + + for (;;) { + if (!ReadOK(gif->in, &c, 1)) { + return 0; + } + if (c == ';') { + gif->done = 1; + return 1; + } + if (c == ',') { + gif->pendingSeparator = 1; + return 1; + } + if (c == '!') { + if (!ReadOK(gif->in, &c, 1) || !GifReadExtension(gif, c, &ZeroDataBlock)) { + return 0; + } + continue; + } + } +} - im = gdImageCreateFromGifCtx(fd); +static void GifFillFrameInfo(gdGifRead *gif, gdGifFrameInfo *info) +{ + if (info != NULL) { + *info = gif->lastInfo; + } +} - fd->gd_free(fd); +static int GifFrameToColor(gdImagePtr frame, int color) +{ + return gdTrueColorAlpha(frame->red[color], frame->green[color], frame->blue[color], + frame->alpha[color]); +} - return im; +static int GifBackgroundColor(gdGifRead *gif, int transparentIndex) +{ + int bg = gif->backgroundIndex; + + if (bg == transparentIndex) { + return gdTrueColorAlpha(0, 0, 0, gdAlphaTransparent); + } + if (gif->haveGlobalColormap && bg >= 0 && bg < MAXCOLORMAPSIZE) { + return gdTrueColorAlpha(gif->globalColorMap[CM_RED][bg], gif->globalColorMap[CM_GREEN][bg], + gif->globalColorMap[CM_BLUE][bg], gdAlphaOpaque); + } + return gdTrueColorAlpha(0, 0, 0, gdAlphaTransparent); +} + +static int GifEnsureCanvas(gdGifRead *gif, int transparentIndex) +{ + int x, y, bg; + + if (gif->canvas != NULL) { + return 1; + } + + gif->canvas = gdImageCreateTrueColor(gif->screenWidth, gif->screenHeight); + if (gif->canvas == NULL) { + return 0; + } + gdImageAlphaBlending(gif->canvas, 0); + gdImageSaveAlpha(gif->canvas, 1); + + bg = GifBackgroundColor(gif, transparentIndex); + for (y = 0; y < gif->screenHeight; y++) { + for (x = 0; x < gif->screenWidth; x++) { + gdImageSetPixel(gif->canvas, x, y, bg); + } + } + + return 1; +} + +static gdImagePtr GifCloneImage(gdImagePtr src) +{ + gdImagePtr dst; + int x, y; + + if (src == NULL) { + return NULL; + } + + dst = + src->trueColor ? gdImageCreateTrueColor(src->sx, src->sy) : gdImageCreate(src->sx, src->sy); + if (dst == NULL) { + return NULL; + } + + if (src->trueColor) { + gdImageAlphaBlending(dst, 0); + gdImageSaveAlpha(dst, src->saveAlphaFlag); + for (y = 0; y < src->sy; y++) { + for (x = 0; x < src->sx; x++) { + gdImageSetPixel(dst, x, y, gdImageGetPixel(src, x, y)); + } + } + } else { + for (x = 0; x < gdMaxColors; x++) { + dst->red[x] = src->red[x]; + dst->green[x] = src->green[x]; + dst->blue[x] = src->blue[x]; + dst->alpha[x] = src->alpha[x]; + dst->open[x] = src->open[x]; + } + dst->colorsTotal = src->colorsTotal; + dst->transparent = src->transparent; + for (y = 0; y < src->sy; y++) { + for (x = 0; x < src->sx; x++) { + gdImageSetPixel(dst, x, y, gdImageGetPixel(src, x, y)); + } + } + } + + return dst; +} + +static void GifApplyPreviousDisposal(gdGifRead *gif) +{ + gdGifFrameInfo *info = &gif->lastInfo; + int x, y, bg; + + if (gif->canvas == NULL || gif->frameIndex <= 0) { + return; + } + + if (info->disposal == gdDisposalRestoreBackground) { + bg = GifBackgroundColor(gif, info->transparentIndex); + for (y = info->y; y < info->y + info->height; y++) { + for (x = info->x; x < info->x + info->width; x++) { + gdImageSetPixel(gif->canvas, x, y, bg); + } + } + } else if (info->disposal == gdDisposalRestorePrevious && gif->previousCanvas != NULL) { + for (y = info->y; y < info->y + info->height; y++) { + for (x = info->x; x < info->x + info->width; x++) { + gdImageSetPixel(gif->canvas, x, y, gdImageGetPixel(gif->previousCanvas, x, y)); + } + } + } + + if (gif->previousCanvas != NULL) { + gdImageDestroy(gif->previousCanvas); + gif->previousCanvas = NULL; + } +} + +static int GifCompositeFrame(gdGifRead *gif) +{ + gdGifFrameInfo *info = &gif->lastInfo; + int x, y, c; + + if (!GifEnsureCanvas(gif, info->transparentIndex)) { + return 0; + } + + if (info->disposal == gdDisposalRestorePrevious) { + gif->previousCanvas = GifCloneImage(gif->canvas); + if (gif->previousCanvas == NULL) { + return 0; + } + } + + for (y = 0; y < info->height; y++) { + for (x = 0; x < info->width; x++) { + c = gdImageGetPixel(gif->rawFrame, x, y); + if (c == info->transparentIndex) { + continue; + } + gdImageSetPixel(gif->canvas, info->x + x, info->y + y, + GifFrameToColor(gif->rawFrame, c)); + } + } + + return 1; +} + +static int GifProbeIsAnimated(gdIOCtxPtr in) +{ + unsigned char buf[16], c; + int bitPixel, frameCount = 0, zero = 0; + + if (in == NULL || !ReadOK(in, buf, 6)) { + return -1; + } + if (strncmp((char *)buf, "GIF", 3) != 0 || + (memcmp((char *)buf + 3, "87a", 3) != 0 && memcmp((char *)buf + 3, "89a", 3) != 0)) { + return -1; + } + if (!ReadOK(in, buf, 7)) { + return -1; + } + if (LM_to_uint(buf[0], buf[1]) <= 0 || LM_to_uint(buf[2], buf[3]) <= 0) { + return -1; + } + bitPixel = 2 << (buf[4] & 0x07); + if (BitSet(buf[4], LOCALCOLORMAP)) { + while (bitPixel-- > 0) { + if (!ReadOK(in, buf, 3)) { + return -1; + } + } + } + + for (;;) { + if (!ReadOK(in, &c, 1)) { + return -1; + } + if (c == ';') { + return frameCount > 1 ? 1 : 0; + } + if (c == '!') { + if (!ReadOK(in, &c, 1) || !GifSkipSubBlocks(in, &zero)) { + return -1; + } + continue; + } + if (c == ',') { + int localColorCount; + if (!ReadOK(in, buf, 9)) { + return -1; + } + localColorCount = BitSet(buf[8], LOCALCOLORMAP) ? (2 << (buf[8] & 0x07)) : 0; + while (localColorCount-- > 0) { + if (!ReadOK(in, buf, 3)) { + return -1; + } + } + if (!ReadOK(in, &c, 1) || !GifSkipSubBlocks(in, &zero)) { + return -1; + } + frameCount++; + if (frameCount > 1) { + return 1; + } + continue; + } + return -1; + } +} + +BGD_DECLARE(int) gdGifIsAnimated(FILE *fdFile) +{ + gdIOCtx *fd; + int result, pos; + + if (fdFile == NULL) { + return -1; + } + fd = gdNewFileCtx(fdFile); + if (fd == NULL) { + return -1; + } + pos = (int)gdTell(fd); + if (pos < 0) { + fd->gd_free(fd); + return -1; + } + result = GifProbeIsAnimated(fd); + if (!gdSeek(fd, pos)) { + result = -1; + } + fd->gd_free(fd); + return result; +} + +BGD_DECLARE(int) gdGifIsAnimatedCtx(gdIOCtxPtr in) +{ + int result, pos; + + if (in == NULL || in->tell == NULL || in->seek == NULL) { + return -1; + } + pos = (int)gdTell(in); + if (pos < 0) { + return -1; + } + result = GifProbeIsAnimated(in); + if (!gdSeek(in, pos)) { + return -1; + } + return result; +} + +BGD_DECLARE(int) gdGifIsAnimatedPtr(int size, void *data) +{ + gdIOCtx *in; + int result; + + if (size <= 0 || data == NULL) { + return -1; + } + in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) { + return -1; + } + result = GifProbeIsAnimated(in); + in->gd_free(in); + return result; +} + +BGD_DECLARE(gdGifReadPtr) gdGifReadOpen(FILE *fdFile) +{ + gdIOCtx *fd; + gdGifReadPtr gif; + + if (fdFile == NULL) { + return NULL; + } + fd = gdNewFileCtx(fdFile); + if (fd == NULL) { + return NULL; + } + gif = gdGifReadOpenCtx(fd); + if (gif == NULL) { + fd->gd_free(fd); + return NULL; + } + gif->ownsCtx = 1; + return gif; +} + +BGD_DECLARE(gdGifReadPtr) gdGifReadOpenPtr(int size, void *data) +{ + gdIOCtx *in; + gdGifReadPtr gif; + + if (size <= 0 || data == NULL) { + return NULL; + } + in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) { + return NULL; + } + gif = gdGifReadOpenCtx(in); + if (gif == NULL) { + in->gd_free(in); + return NULL; + } + gif->ownsCtx = 1; + return gif; +} + +BGD_DECLARE(gdGifReadPtr) gdGifReadOpenCtx(gdIOCtxPtr in) +{ + gdGifReadPtr gif; + + if (in == NULL) { + return NULL; + } + + gif = (gdGifReadPtr)gdCalloc(1, sizeof(gdGifRead)); + if (gif == NULL) { + return NULL; + } + gif->in = in; + gif->ownsCtx = 0; + GifResetGraphicControl(&gif->gce); + if (!GifReadHeader(gif) || !GifPrimeFirstImage(gif)) { + gdFree(gif); + return NULL; + } + + return gif; } -/* }}} */ -gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr fd) /* {{{ */ +BGD_DECLARE(void) gdGifReadClose(gdGifReadPtr gif) { - int BitPixel; + if (gif == NULL) { + return; + } + if (gif->rawFrame != NULL) { + gdImageDestroy(gif->rawFrame); + } + if (gif->canvas != NULL) { + gdImageDestroy(gif->canvas); + } + if (gif->previousCanvas != NULL) { + gdImageDestroy(gif->previousCanvas); + } + if (gif->ownsCtx && gif->in != NULL) { + gif->in->gd_free(gif->in); + } + gdFree(gif); +} + +BGD_DECLARE(int) gdGifReadGetInfo(gdGifReadPtr gif, gdGifInfo *info) +{ + if (gif == NULL || info == NULL) { + return 0; + } + info->width = gif->screenWidth; + info->height = gif->screenHeight; + info->backgroundIndex = gif->backgroundIndex; + info->globalColorTable = gif->haveGlobalColormap; + info->loopCount = gif->loopCount; + return 1; +} + +BGD_DECLARE(int) +gdGifReadNextFrame(gdGifReadPtr gif, gdGifFrameInfo *info, gdImagePtr *frame) +{ + unsigned char buf[16], c; + int ZeroDataBlock = FALSE; + + if (frame != NULL) { + *frame = NULL; + } + if (gif == NULL || gif->error) { + return -1; + } + if (gif->done) { + return 0; + } + + for (;;) { + int top, left, width, height; + int useGlobalColormap, bitPixel, interlace, hasLocal; + + if (gif->pendingSeparator) { + c = ','; + gif->pendingSeparator = 0; + } else if (!ReadOK(gif->in, &c, 1)) { + gif->error = 1; + return -1; + } + if (c == ';') { + gif->done = 1; + return 0; + } + if (c == '!') { + if (!ReadOK(gif->in, &c, 1) || !GifReadExtension(gif, c, &ZeroDataBlock)) { + gif->error = 1; + return -1; + } + continue; + } + if (c != ',') { + continue; + } + + if (!ReadOK(gif->in, buf, 9)) { + gif->error = 1; + return -1; + } + + hasLocal = BitSet(buf[8], LOCALCOLORMAP); + useGlobalColormap = !hasLocal; + bitPixel = 1 << ((buf[8] & 0x07) + 1); + left = LM_to_uint(buf[0], buf[1]); + top = LM_to_uint(buf[2], buf[3]); + width = LM_to_uint(buf[4], buf[5]); + height = LM_to_uint(buf[6], buf[7]); + interlace = BitSet(buf[8], INTERLACE); + + if (width <= 0 || height <= 0 || ((left + width) > gif->screenWidth) || + ((top + height) > gif->screenHeight)) { + gif->error = 1; + return -1; + } + if (useGlobalColormap && !gif->haveGlobalColormap) { + gif->globalColorMap[CM_RED][1] = 0xff; + gif->globalColorMap[CM_GREEN][1] = 0xff; + gif->globalColorMap[CM_BLUE][1] = 0xff; + } + + if (gif->rawFrame != NULL) { + gdImageDestroy(gif->rawFrame); + gif->rawFrame = NULL; + } + gif->rawFrame = gdImageCreate(width, height); + if (gif->rawFrame == NULL) { + gif->error = 1; + return -1; + } + gif->rawFrame->interlace = interlace; + + if (hasLocal) { + if (ReadColorMap(gif->in, bitPixel, gif->localColorMap) || + !ReadImage(gif->rawFrame, gif->in, width, height, gif->localColorMap, bitPixel, + interlace, &ZeroDataBlock)) { + gif->error = 1; + return -1; + } + } else { + if (!ReadImage(gif->rawFrame, gif->in, width, height, gif->globalColorMap, + gif->globalColorCount, interlace, &ZeroDataBlock)) { + gif->error = 1; + return -1; + } + } + + if (gif->gce.transparent != -1) { + gdImageColorTransparent(gif->rawFrame, gif->gce.transparent); + } + GifTrimColorTable(gif->rawFrame); + if (!gif->rawFrame->colorsTotal) { + gif->error = 1; + return -1; + } + + gif->lastInfo.frameIndex = gif->frameIndex; + gif->lastInfo.x = left; + gif->lastInfo.y = top; + gif->lastInfo.width = width; + gif->lastInfo.height = height; + gif->lastInfo.delay = gif->gce.delay; + gif->lastInfo.disposal = gif->gce.disposal; + gif->lastInfo.transparentIndex = gif->gce.transparent; + gif->lastInfo.localColorTable = hasLocal; + gif->lastInfo.interlace = interlace; + gif->frameIndex++; + GifFillFrameInfo(gif, info); + if (frame != NULL) { + *frame = gif->rawFrame; + } + GifResetGraphicControl(&gif->gce); + return 1; + } +} + +BGD_DECLARE(int) +gdGifReadNextImage(gdGifReadPtr gif, gdGifFrameInfo *info, gdImagePtr *image) +{ + int result; + + if (image != NULL) { + *image = NULL; + } + if (gif == NULL) { + return -1; + } + + GifApplyPreviousDisposal(gif); + result = gdGifReadNextFrame(gif, info, NULL); + if (result <= 0) { + return result; + } + if (!GifCompositeFrame(gif)) { + gif->error = 1; + return -1; + } + if (image != NULL) { + *image = gif->canvas; + } + return 1; +} + +BGD_DECLARE(gdImagePtr) gdGifReadCloneImage(gdGifReadPtr gif) +{ + if (gif == NULL) { + return NULL; + } + return GifCloneImage(gif->canvas); +} + +/* + Function: gdImageCreateFromGif + + is called to load images from GIF format + files. Invoke with an already opened + pointer to a file containing the desired + image. + + returns a to the new image, or + NULL if unable to load the image (most often because the file is + corrupt or does not contain a GIF image). + does not close the file. You can inspect the sx and sy members of + the image to determine its size. The image must eventually be + destroyed using . + + Variants: + + creates an image from GIF data (i.e. the + contents of a GIF file) already in memory. + + reads in an image using the functions in + a struct. + + Parameters: + + infile - The input FILE pointer + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + + > gdImagePtr im; + > ... inside a function ... + > FILE *in; + > in = fopen("mygif.gif", "rb"); + > im = gdImageCreateFromGif(in); + > fclose(in); + > // ... Use the image ... + > gdImageDestroy(im); + +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGif(FILE *fdFile) +{ + gdIOCtx *fd = gdNewFileCtx(fdFile); + gdImagePtr im; + + if (fd == NULL) + return NULL; + im = gdImageCreateFromGifCtx(fd); + + fd->gd_free(fd); + + return im; +} + +/* + Function: gdImageCreateFromGifPtr + + Parameters: + + size - size of GIF data in bytes. + data - GIF data (i.e. contents of a GIF file). + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGifPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return 0; + } + im = gdImageCreateFromGifCtx(in); + in->gd_free(in); + return im; +} + +/* + Function: gdImageCreateFromGifCtx + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromGifCtx(gdIOCtxPtr fd) +{ + int BitPixel; #if 0 int ColorResolution; int Background; int AspectRatio; #endif - int Transparent = (-1); - unsigned char buf[16]; - unsigned char c; - unsigned char ColorMap[3][MAXCOLORMAPSIZE]; - unsigned char localColorMap[3][MAXCOLORMAPSIZE]; - int imw, imh, screen_width, screen_height; - int useGlobalColormap; - int bitPixel; - int i; - /*1.4//int imageCount = 0; */ - - int ZeroDataBlock = FALSE; - int haveGlobalColormap; - gdImagePtr im = 0; - - memset(ColorMap, 0, 3 * MAXCOLORMAPSIZE); - memset(localColorMap, 0, 3 * MAXCOLORMAPSIZE); - - /*1.4//imageNumber = 1; */ - if (! ReadOK(fd,buf,6)) { - return 0; - } - if (strncmp((char *)buf,"GIF",3) != 0) { - return 0; - } - - if (memcmp((char *)buf+3, "87a", 3) == 0) { - /* GIF87a */ - } else if (memcmp((char *)buf+3, "89a", 3) == 0) { - /* GIF89a */ - } else { - return 0; - } - - if (! ReadOK(fd,buf,7)) { - return 0; - } - - BitPixel = 2<<(buf[4]&0x07); + int Transparent = (-1); + unsigned char buf[16]; + unsigned char c; + unsigned char ColorMap[3][MAXCOLORMAPSIZE]; + unsigned char localColorMap[3][MAXCOLORMAPSIZE]; + int imw, imh, screen_width, screen_height; + int useGlobalColormap; + int bitPixel, i; + /*1.4//int imageCount = 0; */ + /* 2.0.28: threadsafe storage */ + int ZeroDataBlock = FALSE; + int haveGlobalColormap; + + gdImagePtr im = 0; + + memset(ColorMap, 0, 3 * MAXCOLORMAPSIZE); + memset(localColorMap, 0, 3 * MAXCOLORMAPSIZE); + + if (!ReadOK(fd, buf, 6)) { + return 0; + } + + if (strncmp((char *)buf, "GIF", 3) != 0) { + return 0; + } + + if (memcmp((char *)buf + 3, "87a", 3) == 0) { + /* GIF87a */ + } else if (memcmp((char *)buf + 3, "89a", 3) == 0) { + /* GIF89a */ + } else { + return 0; + } + + if (!ReadOK(fd, buf, 7)) { + return 0; + } + + BitPixel = 2 << (buf[4] & 0x07); #if 0 ColorResolution = (int) (((buf[4]&0x70)>>3)+1); Background = buf[5]; AspectRatio = buf[6]; #endif - screen_width = imw = LM_to_uint(buf[0],buf[1]); - screen_height = imh = LM_to_uint(buf[2],buf[3]); - - haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP); /* Global Colormap */ - if (haveGlobalColormap) { - if (ReadColorMap(fd, BitPixel, ColorMap)) { - return 0; - } - } - - for (;;) { - int top, left; - int width, height; - - if (! ReadOK(fd,&c,1)) { - return 0; - } - if (c == ';') { /* GIF terminator */ - goto terminated; - } - - if (c == '!') { /* Extension */ - if (! ReadOK(fd,&c,1)) { - return 0; - } - DoExtension(fd, c, &Transparent, &ZeroDataBlock); - continue; - } - - if (c != ',') { /* Not a valid start character */ - continue; - } - - /*1.4//++imageCount; */ - - if (! ReadOK(fd,buf,9)) { - return 0; - } - - useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP); - - bitPixel = 1<<((buf[8]&0x07)+1); - left = LM_to_uint(buf[0], buf[1]); - top = LM_to_uint(buf[2], buf[3]); - width = LM_to_uint(buf[4], buf[5]); - height = LM_to_uint(buf[6], buf[7]); - - if (left + width > screen_width || top + height > screen_height) { - if (VERBOSE) { - printf("Frame is not confined to screen dimension.\n"); - } - return 0; - } - - if (!(im = gdImageCreate(width, height))) { - return 0; - } - im->interlace = BitSet(buf[8], INTERLACE); - if (!useGlobalColormap) { - if (ReadColorMap(fd, bitPixel, localColorMap)) { - gdImageDestroy(im); - return 0; - } - ReadImage(im, fd, width, height, localColorMap, - BitSet(buf[8], INTERLACE), &ZeroDataBlock); - } else { - if (!haveGlobalColormap) { - // Still a valid gif, apply simple default palette as per spec - ColorMap[CM_RED][1] = 0xff; - ColorMap[CM_GREEN][1] = 0xff; - ColorMap[CM_BLUE][1] = 0xff; - } - ReadImage(im, fd, width, height, - ColorMap, - BitSet(buf[8], INTERLACE), &ZeroDataBlock); - } - if (Transparent != (-1)) { - gdImageColorTransparent(im, Transparent); - } - goto terminated; - } + screen_width = imw = LM_to_uint(buf[0], buf[1]); + screen_height = imh = LM_to_uint(buf[2], buf[3]); + + haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP); /* Global Colormap */ + if (haveGlobalColormap) { + if (ReadColorMap(fd, BitPixel, ColorMap)) { + return 0; + } + } + + for (;;) { + int top, left; + int width, height; + + if (!ReadOK(fd, &c, 1)) { + return 0; + } + + if (c == ';') { /* GIF terminator */ + goto terminated; + } + + if (c == '!') { /* Extension */ + if (!ReadOK(fd, &c, 1)) { + return 0; + } + + DoExtension(fd, c, &Transparent, &ZeroDataBlock); + continue; + } + + if (c != ',') { /* Not a valid start character */ + continue; + } + + /*1.4//++imageCount; */ + + if (!ReadOK(fd, buf, 9)) { + return 0; + } + + useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP); + + bitPixel = 1 << ((buf[8] & 0x07) + 1); + left = LM_to_uint(buf[0], buf[1]); + top = LM_to_uint(buf[2], buf[3]); + width = LM_to_uint(buf[4], buf[5]); + height = LM_to_uint(buf[6], buf[7]); + + if (((left + width) > screen_width) || ((top + height) > screen_height)) { + if (VERBOSE) { + printf("Frame is not confined to screen dimension.\n"); + } + return 0; + } + + if (!(im = gdImageCreate(width, height))) { + return 0; + } + + im->interlace = BitSet(buf[8], INTERLACE); + if (!useGlobalColormap) { + if (ReadColorMap(fd, bitPixel, localColorMap)) { + gdImageDestroy(im); + return 0; + } + + /* Legacy gdImageCreateFromGif* is intentionally tolerant of + * out-of-palette LZW results and maps them to color 0. The newer + * iterator API passes the actual color table size and rejects those + * malformed frames instead. + */ + if (!ReadImage(im, fd, width, height, localColorMap, 0, BitSet(buf[8], INTERLACE), + &ZeroDataBlock)) { + gdImageDestroy(im); + return 0; + } + } else { + if (!haveGlobalColormap) { + // Still a valid gif, apply simple default palette as per spec + ColorMap[CM_RED][1] = 0xff; + ColorMap[CM_GREEN][1] = 0xff; + ColorMap[CM_BLUE][1] = 0xff; + } + + /* Keep legacy tolerance here as above; strict validation is used by + * the animated GIF iterator. + */ + if (!ReadImage(im, fd, width, height, ColorMap, 0, BitSet(buf[8], INTERLACE), + &ZeroDataBlock)) { + gdImageDestroy(im); + return 0; + } + } + + if (Transparent != (-1)) { + gdImageColorTransparent(im, Transparent); + } + + goto terminated; + } terminated: - /* Terminator before any image was declared! */ - if (!im) { - return 0; - } - /* Check for open colors at the end, so - we can reduce colorsTotal and ultimately - BitsPerPixel */ - for (i=((im->colorsTotal-1)); (i>=0); i--) { - if (im->open[i]) { - im->colorsTotal--; - } else { - break; - } - } - if (!im->colorsTotal) { - gdImageDestroy(im); - return 0; - } - return im; + /* Terminator before any image was declared! */ + if (!im) { + return 0; + } + + /* Check for open colors at the end, so + * we can reduce colorsTotal and ultimately + * BitsPerPixel */ + for (i = im->colorsTotal - 1; i >= 0; i--) { + if (im->open[i]) { + im->colorsTotal--; + } else { + break; + } + } + + if (!im->colorsTotal) { + gdImageDestroy(im); + return 0; + } + + return im; } -/* }}} */ -static int ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256]) /* {{{ */ +static int ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256]) { - int i; - unsigned char rgb[3]; + int i; + unsigned char rgb[3]; + for (i = 0; i < number; ++i) { + if (!ReadOK(fd, rgb, sizeof(rgb))) { + return TRUE; + } - for (i = 0; i < number; ++i) { - if (! ReadOK(fd, rgb, sizeof(rgb))) { - return TRUE; - } - buffer[CM_RED][i] = rgb[0] ; - buffer[CM_GREEN][i] = rgb[1] ; - buffer[CM_BLUE][i] = rgb[2] ; - } + buffer[CM_RED][i] = rgb[0]; + buffer[CM_GREEN][i] = rgb[1]; + buffer[CM_BLUE][i] = rgb[2]; + } - - return FALSE; + return FALSE; } -/* }}} */ -static int -DoExtension(gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP) +static int DoExtension(gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP) { - unsigned char buf[256]; + unsigned char buf[256]; - switch (label) { - case 0xf9: /* Graphic Control Extension */ - memset(buf, 0, 4); /* initialize a few bytes in the case the next function fails */ - (void) GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP); + switch (label) { + case 0xf9: /* Graphic Control Extension */ + memset(buf, 0, 4); /* initialize a few bytes in the case the next function fails */ + (void)GetDataBlock(fd, (unsigned char *)buf, ZeroDataBlockP); #if 0 Gif89.disposal = (buf[0] >> 2) & 0x7; Gif89.inputFlag = (buf[0] >> 1) & 0x1; Gif89.delayTime = LM_to_uint(buf[1],buf[2]); #endif - if ((buf[0] & 0x1) != 0) - *Transparent = buf[3]; + if ((buf[0] & 0x1) != 0) { + *Transparent = buf[3]; + } - while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0); - return FALSE; - default: - break; - } - while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0) - ; + while (GetDataBlock(fd, (unsigned char *)buf, ZeroDataBlockP) > 0) + ; + return FALSE; - return FALSE; + default: + break; + } + + while (GetDataBlock(fd, (unsigned char *)buf, ZeroDataBlockP) > 0) + ; + + return FALSE; } -/* }}} */ -static int -GetDataBlock_(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP) +static int GetDataBlock_(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP) { - unsigned char count; + unsigned char count; - if (! ReadOK(fd,&count,1)) { - return -1; - } + if (!ReadOK(fd, &count, 1)) { + return -1; + } - *ZeroDataBlockP = count == 0; + *ZeroDataBlockP = count == 0; - if ((count != 0) && (! ReadOK(fd, buf, count))) { - return -1; - } + if ((count != 0) && (!ReadOK(fd, buf, count))) { + return -1; + } - return count; + return count; } -/* }}} */ -static int -GetDataBlock(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP) -{ - int rv; - int i; - - rv = GetDataBlock_(fd,buf, ZeroDataBlockP); - if (VERBOSE) { - char *tmp = NULL; - if (rv > 0) { - tmp = safe_emalloc(3 * rv, sizeof(char), 1); - for (i=0;i 0) { + printf(":"); + for (i = 0; i < rv; i++) { + printf(" %02x", buf[i]); + } + } + printf("]\n"); + } + + return rv; } -/* }}} */ -static int -GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP) +static int GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, + int *ZeroDataBlockP) { - int i, j, ret; - int count; - - if (flag) { - scd->curbit = 0; - scd->lastbit = 0; - scd->last_byte = 2; - scd->done = FALSE; - return 0; - } - - if ( (scd->curbit + code_size) >= scd->lastbit) { - if (scd->done) { - if (scd->curbit >= scd->lastbit) { - /* Oh well */ - } - return -1; - } - scd->buf[0] = scd->buf[scd->last_byte-2]; - scd->buf[1] = scd->buf[scd->last_byte-1]; + int i, j, ret; + int count; + + if (flag) { + scd->curbit = 0; + scd->lastbit = 0; + scd->last_byte = 2; + scd->done = FALSE; + return 0; + } + + if ((scd->curbit + code_size) >= scd->lastbit) { + if (scd->done) { + if (scd->curbit >= scd->lastbit) { + /* Oh well */ + } + return -1; + } + + scd->buf[0] = scd->buf[scd->last_byte - 2]; + scd->buf[1] = scd->buf[scd->last_byte - 1]; + scd->last_byte = 2; + scd->curbit = (scd->curbit - scd->lastbit) + 16; + scd->lastbit = 16; + + do { + if (scd->last_byte > (CSD_BUF_SIZE - 255)) { + return -1; + } + + if ((count = GetDataBlock(fd, &scd->buf[scd->last_byte], ZeroDataBlockP)) <= 0) { + scd->done = TRUE; + break; + } + + scd->last_byte += count; + scd->lastbit += count * 8; + } while ((scd->curbit + code_size) > scd->lastbit); + } + + if ((scd->curbit + code_size) > scd->lastbit) { + return -1; + } + + if ((scd->curbit + code_size - 1) >= (CSD_BUF_SIZE * 8)) { + ret = -1; + } else { + ret = 0; + for (i = scd->curbit, j = 0; j < code_size; ++i, ++j) { + ret |= ((scd->buf[i / 8] & (1 << (i % 8))) != 0) << j; + } + } + + scd->curbit += code_size; + + return ret; +} - if ((count = GetDataBlock(fd, &scd->buf[2], ZeroDataBlockP)) <= 0) - scd->done = TRUE; +static int GetCode(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP) +{ + int rv; - scd->last_byte = 2 + count; - scd->curbit = (scd->curbit - scd->lastbit) + 16; - scd->lastbit = (2+count)*8 ; - } + rv = GetCode_(fd, scd, code_size, flag, ZeroDataBlockP); - if ((scd->curbit + code_size - 1) >= (CSD_BUF_SIZE * 8)) { - ret = -1; - } else { - ret = 0; - for (i = scd->curbit, j = 0; j < code_size; ++i, ++j) { - ret |= ((scd->buf[i / 8] & (1 << (i % 8))) != 0) << j; - } - } + if (VERBOSE) { + printf("[GetCode(,%d,%d) returning %d]\n", code_size, flag, rv); + } - scd->curbit += code_size; - return ret; + return rv; } -static int -GetCode(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP) +static int LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, + int *ZeroDataBlockP) { - int rv; - - rv = GetCode_(fd, scd, code_size,flag, ZeroDataBlockP); - if (VERBOSE) printf("[GetCode(,%d,%d) returning %d]\n",code_size,flag,rv); - return(rv); + int code, incode, i; + + if (flag) { + sd->set_code_size = input_code_size; + sd->code_size = sd->set_code_size + 1; + sd->clear_code = 1 << sd->set_code_size; + sd->end_code = sd->clear_code + 1; + sd->max_code_size = 2 * sd->clear_code; + sd->max_code = sd->clear_code + 2; + + GetCode(fd, &sd->scd, 0, TRUE, ZeroDataBlockP); + + sd->fresh = TRUE; + + for (i = 0; i < sd->clear_code; ++i) { + sd->table[0][i] = 0; + sd->table[1][i] = i; + } + + for (; i < (1 << MAX_LWZ_BITS); ++i) { + sd->table[0][i] = sd->table[1][0] = 0; + } + + sd->sp = sd->stack; + + return 0; + + } else if (sd->fresh) { + sd->fresh = FALSE; + + do { + sd->firstcode = sd->oldcode = + GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP); + } while (sd->firstcode == sd->clear_code); + + return sd->firstcode; + } + + if (sd->sp > sd->stack) { + return *--sd->sp; + } + + while ((code = GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP)) >= 0) { + if (code == sd->clear_code) { + for (i = 0; i < sd->clear_code; ++i) { + sd->table[0][i] = 0; + sd->table[1][i] = i; + } + + for (; i < (1 << MAX_LWZ_BITS); ++i) { + sd->table[0][i] = sd->table[1][i] = 0; + } + + sd->code_size = sd->set_code_size + 1; + sd->max_code_size = 2 * sd->clear_code; + sd->max_code = sd->clear_code + 2; + sd->sp = sd->stack; + sd->firstcode = sd->oldcode = + GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP); + + return sd->firstcode; + } else if (code == sd->end_code) { + int count; + unsigned char buf[260]; + + if (*ZeroDataBlockP) { + return -2; + } + + while ((count = GetDataBlock(fd, buf, ZeroDataBlockP)) > 0) + ; + + if (count != 0) { + return -2; + } + } + + incode = code; + + if (sd->sp == (sd->stack + STACK_SIZE)) { + /* Bad compressed data stream */ + return -1; + } + + if (code >= sd->max_code) { + *sd->sp++ = sd->firstcode; + code = sd->oldcode; + } + + while (code >= sd->clear_code) { + if (sd->sp == (sd->stack + STACK_SIZE)) { + /* Bad compressed data stream */ + return -1; + } + if (code >= (1 << MAX_LWZ_BITS)) { + /* Corrupted code */ + return -1; + } + + *sd->sp++ = sd->table[1][code]; + + if (code == sd->table[0][code]) { + /* Oh well */ + } + + code = sd->table[0][code]; + } + if (code >= (1 << MAX_LWZ_BITS)) { + /* Corrupted code */ + return -1; + } + + *sd->sp++ = sd->firstcode = sd->table[1][code]; + + if ((code = sd->max_code) < (1 << MAX_LWZ_BITS)) { + sd->table[0][code] = sd->oldcode; + sd->table[1][code] = sd->firstcode; + ++sd->max_code; + + if ((sd->max_code >= sd->max_code_size) && (sd->max_code_size < (1 << MAX_LWZ_BITS))) { + sd->max_code_size *= 2; + ++sd->code_size; + } + } + + sd->oldcode = incode; + + if (sd->sp > sd->stack) { + return *--sd->sp; + } + } + + return code; } -/* }}} */ -static int -LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP) -{ - int code, incode, i; - - if (flag) { - sd->set_code_size = input_code_size; - sd->code_size = sd->set_code_size+1; - sd->clear_code = 1 << sd->set_code_size ; - sd->end_code = sd->clear_code + 1; - sd->max_code_size = 2*sd->clear_code; - sd->max_code = sd->clear_code+2; - - GetCode(fd, &sd->scd, 0, TRUE, ZeroDataBlockP); - - sd->fresh = TRUE; - - for (i = 0; i < sd->clear_code; ++i) { - sd->table[0][i] = 0; - sd->table[1][i] = i; - } - for (; i < (1<table[0][i] = sd->table[1][0] = 0; - - sd->sp = sd->stack; - - return 0; - } else if (sd->fresh) { - sd->fresh = FALSE; - do { - sd->firstcode = sd->oldcode = - GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP); - } while (sd->firstcode == sd->clear_code); - return sd->firstcode; - } - - if (sd->sp > sd->stack) - return *--sd->sp; - - while ((code = GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP)) >= 0) { - if (code == sd->clear_code) { - for (i = 0; i < sd->clear_code; ++i) { - sd->table[0][i] = 0; - sd->table[1][i] = i; - } - for (; i < (1<table[0][i] = sd->table[1][i] = 0; - sd->code_size = sd->set_code_size+1; - sd->max_code_size = 2*sd->clear_code; - sd->max_code = sd->clear_code+2; - sd->sp = sd->stack; - sd->firstcode = sd->oldcode = - GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP); - return sd->firstcode; - } else if (code == sd->end_code) { - int count; - unsigned char buf[260]; - - if (*ZeroDataBlockP) - return -2; - - while ((count = GetDataBlock(fd, buf, ZeroDataBlockP)) > 0) - ; - - if (count != 0) - return -2; - } - - incode = code; - - if (sd->sp == (sd->stack + STACK_SIZE)) { - /* Bad compressed data stream */ - return -1; - } - - if (code >= sd->max_code) { - *sd->sp++ = sd->firstcode; - code = sd->oldcode; - } - - while (code >= sd->clear_code) { - if (sd->sp == (sd->stack + STACK_SIZE)) { - /* Bad compressed data stream */ - return -1; - } - *sd->sp++ = sd->table[1][code]; - if (code == sd->table[0][code]) { - /* Oh well */ - } - code = sd->table[0][code]; - } - - *sd->sp++ = sd->firstcode = sd->table[1][code]; - - if ((code = sd->max_code) <(1<table[0][code] = sd->oldcode; - sd->table[1][code] = sd->firstcode; - ++sd->max_code; - if ((sd->max_code >= sd->max_code_size) && - (sd->max_code_size < (1<max_code_size *= 2; - ++sd->code_size; - } - } - - sd->oldcode = incode; - - if (sd->sp > sd->stack) - return *--sd->sp; - } - return code; -} -/* }}} */ - -static int -LWZReadByte(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP) +static int LWZReadByte(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, + int *ZeroDataBlockP) { - int rv; + int rv; + + rv = LWZReadByte_(fd, sd, flag, input_code_size, ZeroDataBlockP); - rv = LWZReadByte_(fd, sd, flag, input_code_size, ZeroDataBlockP); - if (VERBOSE) printf("[LWZReadByte(,%d,%d) returning %d]\n",flag,input_code_size,rv); - return(rv); + if (VERBOSE) { + printf("[LWZReadByte(,%d,%d) returning %d]\n", flag, input_code_size, rv); + } + + return rv; } -/* }}} */ -static void -ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP) /*1.4//, int ignore) */ -{ - unsigned char c; - int v; - int xpos = 0, ypos = 0, pass = 0; - int i; - LZW_STATIC_DATA sd; - - - /* - ** Initialize the Compression routines - */ - if (! ReadOK(fd,&c,1)) { - return; - } - - if (c > MAX_LWZ_BITS) { - return; - } - - /* Stash the color map into the image */ - for (i=0; (ired[i] = cmap[CM_RED][i]; - im->green[i] = cmap[CM_GREEN][i]; - im->blue[i] = cmap[CM_BLUE][i]; - im->open[i] = 1; - } - /* Many (perhaps most) of these colors will remain marked open. */ - im->colorsTotal = gdMaxColors; - if (LWZReadByte(fd, &sd, TRUE, c, ZeroDataBlockP) < 0) { - return; - } - - /* - ** If this is an "uninteresting picture" ignore it. - ** REMOVED For 1.4 - */ - /*if (ignore) { */ - /* while (LWZReadByte(fd, &sd, FALSE, c) >= 0) */ - /* ; */ - /* return; */ - /*} */ - - while ((v = LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP)) >= 0) { - if (v >= gdMaxColors) { - v = 0; - } - /* This how we recognize which colors are actually used. */ - if (im->open[v]) { - im->open[v] = 0; - } - gdImageSetPixel(im, xpos, ypos, v); - ++xpos; - if (xpos == len) { - xpos = 0; - if (interlace) { - switch (pass) { - case 0: - case 1: - ypos += 8; break; - case 2: - ypos += 4; break; - case 3: - ypos += 2; break; - } - - if (ypos >= height) { - ++pass; - switch (pass) { - case 1: - ypos = 4; break; - case 2: - ypos = 2; break; - case 3: - ypos = 1; break; - default: - goto fini; - } - } - } else { - ++ypos; - } - } - if (ypos >= height) - break; - } +static int ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], + int colorCount, int interlace, int *ZeroDataBlockP) /*1.4//, int ignore) */ +{ + unsigned char c; + int xpos = 0, ypos = 0, pass = 0; + int v, i; + LZW_STATIC_DATA sd; + + /* Initialize the Compression routines */ + if (!ReadOK(fd, &c, 1)) { + return 0; + } + + if (c > MAX_LWZ_BITS) { + return 0; + } + + /* Stash the color map into the image */ + for (i = 0; (i < gdMaxColors); i++) { + im->red[i] = cmap[CM_RED][i]; + im->green[i] = cmap[CM_GREEN][i]; + im->blue[i] = cmap[CM_BLUE][i]; + im->open[i] = 1; + } + + /* Many (perhaps most) of these colors will remain marked open. */ + im->colorsTotal = gdMaxColors; + if (LWZReadByte(fd, &sd, TRUE, c, ZeroDataBlockP) < 0) { + return 0; + } + + /* + ** If this is an "uninteresting picture" ignore it. + ** REMOVED For 1.4 + */ + /*if (ignore) { */ + /* while (LWZReadByte(fd, &sd, FALSE, c) >= 0) */ + /* ; */ + /* return; */ + /*} */ + + while ((v = LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP)) >= 0) { + if (colorCount > 0 && v >= colorCount) { + return 0; + } + if (v >= gdMaxColors) { + v = 0; + } + + /* This how we recognize which colors are actually used. */ + if (im->open[v]) { + im->open[v] = 0; + } + + gdImageSetPixel(im, xpos, ypos, v); + + ++xpos; + if (xpos == len) { + xpos = 0; + if (interlace) { + switch (pass) { + case 0: + case 1: + ypos += 8; + break; + case 2: + ypos += 4; + break; + case 3: + ypos += 2; + break; + } + + if (ypos >= height) { + ++pass; + switch (pass) { + case 1: + ypos = 4; + break; + case 2: + ypos = 2; + break; + case 3: + ypos = 1; + break; + default: + goto fini; + } + } + } else { + ++ypos; + } + } + + if (ypos >= height) { + break; + } + } fini: - if (LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP) >=0) { - /* Ignore extra */ - } + if (LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP) >= 0) { + /* Ignore extra */ + } + return 1; } /* }}} */ diff --git a/ext/gd/libgd/gd_gif_out.c b/ext/gd/libgd/gd_gif_out.c index e721992b48cb..8f284ca23a6b 100644 --- a/ext/gd/libgd/gd_gif_out.c +++ b/ext/gd/libgd/gd_gif_out.c @@ -1,8 +1,15 @@ -#include +/** + * File: GIF Output + * + * Write GIF images. + */ + +#include "gd.h" +#include "gdhelpers.h" #include -#include +#include #include -#include "gd.h" +#include /* Code drawn from ppmtogif.c, from the pbmplus package ** @@ -25,16 +32,14 @@ ** CompuServe Incorporated. */ -/* - * a code_int must be able to hold 2**GIFBITS values of type int, and also -1 - */ -typedef int code_int; +/* a code_int must be able to hold 2**GIFBITS values of type int, and also -1 */ +typedef int code_int; #ifdef SIGNED_COMPARE_SLOW typedef unsigned long int count_int; typedef unsigned short int count_short; -#else /*SIGNED_COMPARE_SLOW*/ -typedef long int count_int; +#else /*SIGNED_COMPARE_SLOW*/ +typedef long int count_int; #endif /*SIGNED_COMPARE_SLOW*/ /* 2.0.28: threadsafe */ @@ -44,139 +49,934 @@ typedef long int count_int; /* should NEVER generate this code */ #define maxmaxcode ((code_int)1 << GIFBITS) -#define HSIZE 5003 /* 80% occupancy */ -#define hsize HSIZE /* Apparently invariant, left over from - compress */ +#define HSIZE 5003 /* 80% occupancy */ +#define hsize HSIZE /* Apparently invariant, left over from compress */ typedef struct { - int Width, Height; - int curx, cury; - long CountDown; - int Pass; - int Interlace; - int n_bits; /* number of bits/code */ - code_int maxcode; /* maximum code, given n_bits */ - count_int htab [HSIZE]; - unsigned short codetab [HSIZE]; - code_int free_ent; /* first unused entry */ - /* - * block compression parameters -- after all codes are used up, - * and compression rate changes, start over. - */ - int clear_flg; - int offset; - long int in_count; /* length of input */ - long int out_count; /* # of codes output (for debugging) */ - - int g_init_bits; - gdIOCtx * g_outfile; - - int ClearCode; - int EOFCode; - unsigned long cur_accum; - int cur_bits; - /* - * Number of characters so far in this 'packet' - */ - int a_count; - /* - * Define the storage for the packet accumulator - */ - char accum[ 256 ]; + int Width, Height; + int curx, cury; + long CountDown; + int Pass; + int Interlace; + int n_bits; + code_int maxcode; + count_int htab[HSIZE]; + unsigned short codetab[HSIZE]; + /* first unused entry */ + code_int free_ent; + /* block compression parameters -- after all codes are used up, + * and compression rate changes, start over. */ + int clear_flg; + int offset; + long int in_count; + /* # of codes output (for debugging) */ + long int out_count; + int g_init_bits; + gdIOCtx *g_outfile; + int ClearCode; + int EOFCode; + unsigned long cur_accum; + int cur_bits; + int a_count; + char accum[256]; } GifCtx; static int gifPutWord(int w, gdIOCtx *out); static int colorstobpp(int colors); -static void BumpPixel (GifCtx *ctx); -static int GIFNextPixel (gdImagePtr im, GifCtx *ctx); -static void GIFEncode (gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im); -static void compress (int init_bits, gdIOCtx *outfile, gdImagePtr im, GifCtx *ctx); -static void output (code_int code, GifCtx *ctx); -static void cl_block (GifCtx *ctx); -static void cl_hash (register count_int chsize, GifCtx *ctx); -static void char_init (GifCtx *ctx); -static void char_out (int c, GifCtx *ctx); -static void flush_char (GifCtx *ctx); +static void BumpPixel(GifCtx *ctx); +static int GIFNextPixel(gdImagePtr im, GifCtx *ctx); +static void GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, + int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, + gdImagePtr im); +static void GIFAnimEncode(gdIOCtxPtr fp, int IWidth, int IHeight, int LeftOfs, int TopOfs, + int GInterlace, int Transparent, int Delay, int Disposal, + int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im); +static void compress(int init_bits, gdIOCtx *outfile, gdImagePtr im, GifCtx *ctx); +static void output(code_int code, GifCtx *ctx); +static void cl_block(GifCtx *ctx); +static void cl_hash(register count_int chsize, GifCtx *ctx); +static void char_init(GifCtx *ctx); +static void char_out(int c, GifCtx *ctx); +static void flush_char(GifCtx *ctx); static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out); +static int _gdImageGifAnimAddCtx(gdImagePtr im, gdIOCtxPtr out, int LocalCM, int LeftOfs, + int TopOfs, int Delay, int Disposal, gdImagePtr previm); + +/* + Function: gdImageGifPtr + + Identical to except that it returns a pointer to a + memory area with the GIF data. This memory must be freed by the + caller when it is no longer needed. + + The caller *must* invoke , not _free()_. This is because + it is not guaranteed that libgd will use the same implementation + of malloc, free, etc. as your proggram. + + The 'size' parameter receives the total size of the block of + memory. + + Parameters: + + im - The image to write + size - Output: the size of the resulting image. + + Returns: + + A pointer to the GIF data or NULL if an error occurred. + +*/ +BGD_DECLARE(void *) gdImageGifPtr(gdImagePtr im, int *size) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageGifCtx(im, out)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; +} + +/* + Function: gdImageGif + + outputs the specified image to the specified file in + GIF format. The file must be open for binary writing. (Under MSDOS + and all versions of Windows, it is important to use "wb" as + opposed to simply "w" as the mode when opening the file; under + Unix there is no penalty for doing so). does not close + the file; your code must do so. + + GIF does not support true color; GIF images can contain a maximum + of 256 colors. If the image to be written is a truecolor image, + such as those created with gdImageCreateTrueColor or loaded from a + JPEG or a truecolor PNG image file, a palette-based temporary + image will automatically be created internally using the + function. The original image + pixels are not modified. This conversion produces high quality + palettes but does require some CPU time. If you are regularly + converting truecolor to palette in this way, you should consider + creating your image as a palette-based image in the first place. + + Variants: + + outputs the image via a struct. + + stores the image in a large array of bytes. + + Parameters: + + im - The image to write + outFile - The FILE pointer to write the image to. + + Returns: + + Nothing + + Example: + + > gdImagePtr im; + > int black, white; + > FILE *out; + > // Create the image + > im = gdImageCreate(100, 100); + > // Allocate background + > white = gdImageColorAllocate(im, 255, 255, 255); + > // Allocate drawing color + > black = gdImageColorAllocate(im, 0, 0, 0); + > // Draw rectangle + > gdImageRectangle(im, 0, 0, 99, 99, black); + > // Open output file in binary mode + > out = fopen("rect.gif", "wb"); + > // Write GIF + > gdImageGif(im, out); + > // Close file + > fclose(out); + > // Destroy image + > gdImageDestroy(im); + +*/ +BGD_DECLARE(void) gdImageGif(gdImagePtr im, FILE *outFile) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageGifCtx(im, out); + out->gd_free(out); +} + +/* + Function: gdImageGifCtx + + Writes a GIF image via a . See . + + Parameters: + + im - The image to write + out - The struct used to do the writing. + + Returns: + + Nothing. + +*/ +BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) { _gdImageGifCtx(im, out); } + +/* returns 0 on success, 1 on failure */ +static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) +{ + gdImagePtr pim = 0, tim = im; + int interlace, BitsPerPixel; + interlace = im->interlace; + + if (im->trueColor) { + /* Expensive, but the only way that produces an + acceptable result: mix down to a palette + based temporary image. */ + pim = gdImageCreatePaletteFromTrueColor(im, 1, 256); + if (!pim) { + return 1; + } + tim = pim; + } + + BitsPerPixel = colorstobpp(tim->colorsTotal); + + /* All set, let's do it. */ + GIFEncode(out, tim->sx, tim->sy, interlace, 0, tim->transparent, BitsPerPixel, tim->red, + tim->green, tim->blue, tim); + + if (pim) { + /* Destroy palette based temporary image. */ + gdImageDestroy(pim); + } + + return 0; +} + +/* + Function: gdImageGifAnimBeginPtr + + Like except that it outputs to a memory + buffer. See . + + The returned memory must be freed by the caller when it is no + longer needed. **The caller must invoke (), not free()**, + unless the caller is absolutely certain that the same + implementations of malloc, free, etc. are used both at library + build time and at application build time (but don't; it could + always change). + + The 'size' parameter receives the total size of the block of + memory. + + Parameters: + + im - The reference image + size - Output: the size in bytes of the result. + GlobalCM - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default + Loops - Loop count; 0 -> infinite, -1 means no loop + + Returns: + + A pointer to the resulting data (the contents of the start of the + GIF) or NULL if an error occurred. + +*/ + +BGD_DECLARE(void *) +gdImageGifAnimBeginPtr(gdImagePtr im, int *size, int GlobalCM, int Loops) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + gdImageGifAnimBeginCtx(im, out, GlobalCM, Loops); + rv = gdDPExtractData(out, size); + out->gd_free(out); + return rv; +} + +/* + Function: gdImageGifAnimBegin + + This function must be called as the first function when creating a + GIF animation. It writes the correct GIF file headers to selected + file output, and prepares for frames to be added for the + animation. The image argument is not used to produce an image + frame to the file, it is only used to establish the GIF animation + frame size, interlacing options and the color + palette. is used to add the first and + subsequent frames to the animation, and the animation must be + terminated by writing a semicolon character (;) to it or by using + gdImageGifAnimEnd to do that. + + The GlobalCM flag indicates if a global color map (or palette) is + used in the GIF89A header. A nonzero value specifies that a global + color map should be used to reduce the size of the animation. Of + course, if the color maps of individual frames differ greatly, a + global color map may not be a good idea. GlobalCM=1 means write + global color map, GlobalCM=0 means do not, and GlobalCM=-1 means + to do the default, which currently is to use a global color map. + + If Loops is 0 or greater, the Netscape 2.0 extension for animation + loop count is written. 0 means infinite loop count. -1 means that + the extension is not added which results in no looping. -1 is the + default. + + Variants: + + outputs the image via a struct. + + stores the image in a large array of bytes. + + Parameters: + + im - The reference image + outfile - The output FILE*. + GlobalCM - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default + Loops - Loop count; 0 -> infinite, -1 means no loop + + Returns: + + Nothing. + + Example: + + See . + +*/ + +BGD_DECLARE(void) +gdImageGifAnimBegin(gdImagePtr im, FILE *outFile, int GlobalCM, int Loops) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageGifAnimBeginCtx(im, out, GlobalCM, Loops); + out->gd_free(out); +} + +/* + Function: gdImageGifAnimBeginCtx + + Like except that it outputs to . + See . -void * gdImageGifPtr (gdImagePtr im, int *size) + Parameters: + + im - The reference image + out - Pointer to the output . + GlobalCM - Global colormap flag: 1 -> yes, 0 -> no, -1 -> do default + Loops - Loop count; 0 -> infinite, -1 means no loop + + Returns: + + Nothing. + +*/ +BGD_DECLARE(void) +gdImageGifAnimBeginCtx(gdImagePtr im, gdIOCtxPtr out, int GlobalCM, int Loops) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx (2048, NULL); - if (!_gdImageGifCtx(im, out)) { - rv = gdDPExtractData(out, size); - } else { - rv = NULL; - } - out->gd_free (out); - return rv; + int B; + int RWidth, RHeight; + int Resolution; + int ColorMapSize; + int BitsPerPixel; + int Background = 0; + int i; + + /* Default is to use global color map */ + if (GlobalCM < 0) { + GlobalCM = 1; + } + + BitsPerPixel = colorstobpp(im->colorsTotal); + ColorMapSize = 1 << BitsPerPixel; + + RWidth = im->sx; + RHeight = im->sy; + + Resolution = BitsPerPixel; + + /* Write the Magic header */ + gdPutBuf("GIF89a", 6, out); + + /* Write out the screen width and height */ + gifPutWord(RWidth, out); + gifPutWord(RHeight, out); + + /* Indicate that there is a global colour map */ + B = GlobalCM ? 0x80 : 0; + + /* OR in the resolution */ + B |= (Resolution - 1) << 4; + + /* OR in the Bits per Pixel */ + B |= (BitsPerPixel - 1); + + /* Write it out */ + gdPutC(B, out); + + /* Write out the Background colour */ + gdPutC(Background, out); + + /* Byte of 0's (future expansion) */ + gdPutC(0, out); + + /* Write out the Global Colour Map */ + if (GlobalCM) { + for (i = 0; i < ColorMapSize; ++i) { + gdPutC(im->red[i], out); + gdPutC(im->green[i], out); + gdPutC(im->blue[i], out); + } + } + + if (Loops >= 0) { + gdPutBuf("!\377\13NETSCAPE2.0\3\1", 16, out); + gifPutWord(Loops, out); + gdPutC(0, out); + } } -void gdImageGif (gdImagePtr im, FILE * outFile) +/* + Function: gdImageGifAnimAddPtr + + Like (which contains more information) except + that it stores the data to write into memory and returns a pointer + to it. + + This memory must be freed by the caller when it is no longer + needed. **The caller must invoke (), not free(),** unless + the caller is absolutely certain that the same implementations of + malloc, free, etc. are used both at library build time and at + application build time (but don't; it could always change). + + The 'size' parameter receives the total size of the block of + memory. + + Parameters: + + im - The image to add. + size - Output: the size of the resulting buffer. + LocalCM - Flag. If 1, use a local color map for this frame. + LeftOfs - Left offset of image in frame. + TopOfs - Top offset of image in frame. + Delay - Delay before next frame (in 1/100 seconds) + Disposal - MODE: How to treat this frame when the next one loads. + previm - NULL or a pointer to the previous image written. + + Returns: + + Pointer to the resulting data or NULL if an error occurred. + +*/ +BGD_DECLARE(void *) +gdImageGifAnimAddPtr(gdImagePtr im, int *size, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm) { - gdIOCtx *out = gdNewFileCtx (outFile); - gdImageGifCtx (im, out); - out->gd_free (out); + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageGifAnimAddCtx(im, out, LocalCM, LeftOfs, TopOfs, Delay, Disposal, previm)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; } -void gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) +/* + Function: gdImageGifAnimAdd + + This function writes GIF animation frames to GIF animation, which + was initialized with . With _LeftOfs_ and + _TopOfs_ you can place this frame in different offset than (0,0) + inside the image screen as defined in . Delay + between the previous frame and this frame is in 1/100s + units. _Disposal_ is usually , meaning that the + pixels changed by this frame should remain on the display when the + next frame begins to render, but can also be + (not recommended), (restores the + first allocated color of the global palette), or + (restores the appearance of the + affected area before the frame was rendered). Only + is a sensible choice for the first frame. If + _previm_ is passed, the built-in GIF optimizer will always use + regardless of the Disposal parameter. + + Setting the _LocalCM_ flag to 1 adds a local palette for this + image to the animation. Otherwise the global palette is assumed + and the user must make sure the palettes match. Use + to do that. + + Automatic optimization is activated by giving the previous image + as a parameter. This function then compares the images and only + writes the changed pixels to the new frame in animation. The + _Disposal_ parameter for optimized animations must be set to 1, + also for the first frame. _LeftOfs_ and _TopOfs_ parameters are + ignored for optimized frames. To achieve good optimization, it is + usually best to use a single global color map. To allow + to compress unchanged pixels via the use of a + transparent color, the image must include a transparent color. + + + Variants: + + outputs its data via a struct. + + outputs its data to a memory buffer which + it returns. + + Parameters: + + im - The image to add. + outfile - The output FILE* being written. + LocalCM - Flag. If 1, use a local color map for this frame. + LeftOfs - Left offset of image in frame. + TopOfs - Top offset of image in frame. + Delay - Delay before next frame (in 1/100 seconds) + Disposal - MODE: How to treat this frame when the next one loads. + previm - NULL or a pointer to the previous image written. + + Returns: + + Nothing. + + Example: + (start code) + +{ + gdImagePtr im, im2, im3; + int black, white, trans; + FILE *out; + + im = gdImageCreate(100, 100); // Create the image + white = gdImageColorAllocate(im, 255, 255, 255); // Allocate background + black = gdImageColorAllocate(im, 0, 0, 0); // Allocate drawing color + trans = gdImageColorAllocate(im, 1, 1, 1); // trans clr for compression + gdImageRectangle(im, 0, 0, 10, 10, black); // Draw rectangle + + out = fopen("anim.gif", "wb");// Open output file in binary mode + gdImageGifAnimBegin(im, out, 1, 3);// Write GIF hdr, global clr map,loops + // Write the first frame. No local color map. Delay = 1s + gdImageGifAnimAdd(im, out, 0, 0, 0, 100, 1, NULL); + + // construct the second frame + im2 = gdImageCreate(100, 100); + (void)gdImageColorAllocate(im2, 255, 255, 255); // White background + gdImagePaletteCopy (im2, im); // Make sure the palette is identical + gdImageRectangle(im2, 0, 0, 15, 15, black); // Draw something + // Allow animation compression with transparent pixels + gdImageColorTransparent (im2, trans); + gdImageGifAnimAdd(im2, out, 0, 0, 0, 100, 1, im); // Add second frame + + // construct the third frame + im3 = gdImageCreate(100, 100); + (void)gdImageColorAllocate(im3, 255, 255, 255); // white background + gdImagePaletteCopy (im3, im); // Make sure the palette is identical + gdImageRectangle(im3, 0, 0, 15, 20, black); // Draw something + // Allow animation compression with transparent pixels + gdImageColorTransparent (im3, trans); + // Add the third frame, compressing against the second one + gdImageGifAnimAdd(im3, out, 0, 0, 0, 100, 1, im2); + gdImageGifAnimEnd(out); // End marker, same as putc(';', out); + fclose(out); // Close file + + // Destroy images + gdImageDestroy(im); + gdImageDestroy(im2); + gdImageDestroy(im3); + } + + (end code) +*/ + +BGD_DECLARE(void) +gdImageGifAnimAdd(gdImagePtr im, FILE *outFile, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm) { - _gdImageGifCtx(im, out); + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageGifAnimAddCtx(im, out, LocalCM, LeftOfs, TopOfs, Delay, Disposal, previm); + out->gd_free(out); +} + +static int comparewithmap(gdImagePtr im1, gdImagePtr im2, int c1, int c2, int *colorMap) +{ + if (!colorMap) { + return c1 == c2; + } + + if (-2 != colorMap[c1]) { + return colorMap[c1] == c2; + } + + return (colorMap[c1] = gdImageColorExactAlpha(im2, im1->red[c1], im1->green[c1], im1->blue[c1], + im1->alpha[c1])) == c2; +} + +/* + Function: gdImageGifAnimAddCtx + + Adds an animation frame via a . See gdImageGifAnimAdd>. + + Parameters: + + im - The image to add. + out - The output . + LocalCM - Flag. If 1, use a local color map for this frame. + LeftOfs - Left offset of image in frame. + TopOfs - Top offset of image in frame. + Delay - Delay before next frame (in 1/100 seconds) + Disposal - MODE: How to treat this frame when the next one loads. + previm - NULL or a pointer to the previous image written. + + Returns: + + Nothing. + +*/ +BGD_DECLARE(void) +gdImageGifAnimAddCtx(gdImagePtr im, gdIOCtxPtr out, int LocalCM, int LeftOfs, int TopOfs, int Delay, + int Disposal, gdImagePtr previm) +{ + _gdImageGifAnimAddCtx(im, out, LocalCM, LeftOfs, TopOfs, Delay, Disposal, previm); } /* returns 0 on success, 1 on failure */ -static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) +static int _gdImageGifAnimAddCtx(gdImagePtr im, gdIOCtxPtr out, int LocalCM, int LeftOfs, + int TopOfs, int Delay, int Disposal, gdImagePtr previm) { - gdImagePtr pim = 0, tim = im; - int interlace, BitsPerPixel; - interlace = im->interlace; - if (im->trueColor) { - /* Expensive, but the only way that produces an - acceptable result: mix down to a palette - based temporary image. */ - pim = gdImageCreatePaletteFromTrueColor(im, 1, 256); - if (!pim) { - return 1; - } - tim = pim; - } - BitsPerPixel = colorstobpp(tim->colorsTotal); - /* All set, let's do it. */ - GIFEncode( - out, tim->sx, tim->sy, interlace, 0, tim->transparent, BitsPerPixel, - tim->red, tim->green, tim->blue, tim); - if (pim) { - /* Destroy palette based temporary image. */ - gdImageDestroy( pim); - } + gdImagePtr pim = NULL, tim = im; + int interlace, transparent, BitsPerPixel; + interlace = im->interlace; + transparent = im->transparent; + + /* Default is no local color map */ + if (LocalCM < 0) { + LocalCM = 0; + } + + if (im->trueColor) { + /* Expensive, but the only way that produces an + acceptable result: mix down to a palette + based temporary image. */ + pim = gdImageCreatePaletteFromTrueColor(im, 1, 256); + if (!pim) { + return 1; + } + tim = pim; + } + + if (previm) { + /* create optimized animation. Compare this image to + the previous image and crop the temporary copy of + current image to include only changed rectangular + area. Also replace unchanged pixels inside this + area with transparent color. Transparent color + needs to be already allocated! + Preconditions: + TopOfs, LeftOfs are assumed 0 + + Images should be of same size. If not, a temporary + copy is made with the same size as previous image. + + */ + gdImagePtr prev_pim = 0, prev_tim = previm; + int x, y; + int no_changes = 0; + int min_x = 0; + int min_y = tim->sy; + int max_x = 0; + int max_y = 0; + int colorMap[256]; + + if (previm->trueColor) { + prev_pim = gdImageCreatePaletteFromTrueColor(previm, 1, 256); + if (!prev_pim) { + goto fail_end; + } + prev_tim = prev_pim; + } + + for (x = 0; x < 256; ++x) { + colorMap[x] = -2; + } + + /* First find bounding box of changed areas. */ + /* first find the top changed row */ + for (y = 0; y < tim->sy; ++y) { + for (x = 0; x < tim->sx; ++x) { + if (!comparewithmap(prev_tim, tim, prev_tim->pixels[y][x], tim->pixels[y][x], + colorMap)) { + min_y = max_y = y; + min_x = max_x = x; + goto break_top; + } + } + } + + break_top: + if (tim->sy == min_y) { + /* No changes in this frame. Encode a 1x1 transparent placeholder. + */ + no_changes = 1; + transparent = 0; + min_x = min_y = 0; + max_x = max_y = 0; + } else { + /* Then the bottom row */ + for (y = tim->sy - 1; y > min_y; --y) { + for (x = 0; x < tim->sx; ++x) { + if (!gdImageBoundsSafe(prev_tim, x, y)) + continue; + if (!comparewithmap(prev_tim, tim, prev_tim->pixels[y][x], tim->pixels[y][x], + colorMap)) { + max_y = y; + if (x < min_x) { + min_x = x; + } + if (x > max_x) { + max_x = x; + } + goto break_bot; + } + } + } + + break_bot: + /* left side */ + for (x = 0; x < min_x; ++x) { + for (y = min_y; y <= max_y; ++y) { + if (!gdImageBoundsSafe(prev_tim, x, y)) + continue; + if (!comparewithmap(prev_tim, tim, prev_tim->pixels[y][x], tim->pixels[y][x], + colorMap)) { + min_x = x; + goto break_left; + } + } + } + + break_left: + /* right side */ + for (x = tim->sx - 1; x > max_x; --x) { + for (y = min_y; y <= max_y; ++y) { + if (!gdImageBoundsSafe(prev_tim, x, y)) + continue; + if (!comparewithmap(prev_tim, tim, prev_tim->pixels[y][x], tim->pixels[y][x], + colorMap)) { + max_x = x; + goto break_right; + } + } + } + + break_right:; + } + + LeftOfs = min_x; + TopOfs = min_y; + Disposal = 1; + + /* Make a copy of the image with the new offsets. + But only if necessary. */ + if (min_x != 0 || max_x != tim->sx - 1 || min_y != 0 || max_y != tim->sy - 1 || + transparent >= 0) { + + gdImagePtr pim2 = gdImageCreate(max_x - min_x + 1, max_y - min_y + 1); + + if (!pim2) { + if (prev_pim) { + gdImageDestroy(prev_pim); + } + goto fail_end; + } + + gdImagePaletteCopy(pim2, LocalCM ? tim : prev_tim); + gdImageCopy(pim2, tim, 0, 0, min_x, min_y, max_x - min_x + 1, max_y - min_y + 1); + + if (pim) { + gdImageDestroy(pim); + } + + tim = pim = pim2; + } + + /* now let's compare pixels for transparent + optimization. But only if transparent is set. */ + if (transparent >= 0) { + for (y = 0; y < tim->sy; ++y) { + for (x = 0; x < tim->sx; ++x) { + if (no_changes || + comparewithmap(prev_tim, tim, prev_tim->pixels[min_y + y][min_x + x], + tim->pixels[y][x], 0)) { + gdImageSetPixel(tim, x, y, transparent); + break; + } + } + } + } + if (prev_pim) { + gdImageDestroy(prev_pim); + } + } + + BitsPerPixel = colorstobpp(tim->colorsTotal); + + /* All set, let's do it. */ + GIFAnimEncode(out, tim->sx, tim->sy, LeftOfs, TopOfs, interlace, transparent, Delay, Disposal, + BitsPerPixel, LocalCM ? tim->red : 0, tim->green, tim->blue, tim); + if (pim) { + /* Destroy palette based temporary image. */ + gdImageDestroy(pim); + } return 0; + +fail_end: + if (pim) { + /* Destroy palette based temporary image. */ + gdImageDestroy(pim); + } + return 1; } -static int -colorstobpp(int colors) +/* + Function: gdImageGifAnimEnd + + Terminates the GIF file properly. + + (Previous versions of this function's documentation suggested just + manually writing a semicolon (';') instead since that is all this + function does. While that has no longer changed, we now suggest + that you do not do this and instead always call + (or equivalent) since later versions could + possibly do more or different things.) + + Variants: + + outputs its data via a struct. + + outputs its data to a memory buffer which + it returns. + + Parameters: + + outfile - the destination FILE*. + + Returns: + + Nothing. + +*/ + +BGD_DECLARE(void) gdImageGifAnimEnd(FILE *outFile) +{ +#if 1 + putc(';', outFile); +#else + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageGifAnimEndCtx(out); + out->gd_free(out); +#endif +} + +/* + Function: gdImageGifAnimEndPtr + + Like (which contains more information) except + that it stores the data to write into memory and returns a pointer + to it. + + This memory must be freed by the caller when it is no longer + needed. **The caller must invoke (), not free(),** unless + the caller is absolutely certain that the same implementations of + malloc, free, etc. are used both at library build time and at + application build time (but don't; it could always change). + + The 'size' parameter receives the total size of the block of + memory. + + Parameters: + + size - Output: the size of the resulting buffer. + + Returns: + + Pointer to the resulting data or NULL if an error occurred. + +*/ + +BGD_DECLARE(void *) gdImageGifAnimEndPtr(int *size) +{ + char *rv = (char *)gdMalloc(1); + if (!rv) { + return 0; + } + *rv = ';'; + *size = 1; + return (void *)rv; +} + +/* + Function: gdImageGifAnimEndCtx + + Like , but writes its data via a . + + Parameters: + + out - the destination . + + Returns: + + Nothing. + +*/ + +BGD_DECLARE(void) gdImageGifAnimEndCtx(gdIOCtx *out) +{ + /* + * Write the GIF file terminator + */ + gdPutC(';', out); +} + +static int colorstobpp(int colors) { int bpp = 0; - if ( colors <= 2 ) + if (colors <= 2) bpp = 1; - else if ( colors <= 4 ) + else if (colors <= 4) bpp = 2; - else if ( colors <= 8 ) + else if (colors <= 8) bpp = 3; - else if ( colors <= 16 ) + else if (colors <= 16) bpp = 4; - else if ( colors <= 32 ) + else if (colors <= 32) bpp = 5; - else if ( colors <= 64 ) + else if (colors <= 64) bpp = 6; - else if ( colors <= 128 ) + else if (colors <= 128) bpp = 7; - else if ( colors <= 256 ) + else if (colors <= 256) bpp = 8; + return bpp; - } +} /***************************************************************************** * @@ -189,236 +989,289 @@ colorstobpp(int colors) #define TRUE 1 #define FALSE 0 -/* - * Bump the 'curx' and 'cury' to point to the next pixel - */ -static void -BumpPixel(GifCtx *ctx) + +/* Bump the 'curx' and 'cury' to point to the next pixel */ +static void BumpPixel(GifCtx *ctx) { - /* - * Bump the current X position - */ - ++(ctx->curx); - - /* - * If we are at the end of a scan line, set curx back to the beginning - * If we are interlaced, bump the cury to the appropriate spot, - * otherwise, just increment it. - */ - if( ctx->curx == ctx->Width ) { - ctx->curx = 0; - - if( !ctx->Interlace ) - ++(ctx->cury); - else { - switch( ctx->Pass ) { - - case 0: - ctx->cury += 8; - if( ctx->cury >= ctx->Height ) { - ++(ctx->Pass); - ctx->cury = 4; - } - break; - - case 1: - ctx->cury += 8; - if( ctx->cury >= ctx->Height ) { - ++(ctx->Pass); - ctx->cury = 2; - } - break; - - case 2: - ctx->cury += 4; - if( ctx->cury >= ctx->Height ) { - ++(ctx->Pass); - ctx->cury = 1; - } - break; - - case 3: - ctx->cury += 2; - break; - } + /* Bump the current X position */ + ++(ctx->curx); + + /* If we are at the end of a scan line, set curx back to the beginning + * If we are interlaced, bump the cury to the appropriate spot, + * otherwise, just increment it. */ + if (ctx->curx == ctx->Width) { + ctx->curx = 0; + + if (!ctx->Interlace) { + ++(ctx->cury); + } else { + switch (ctx->Pass) { + + case 0: + ctx->cury += 8; + if (ctx->cury >= ctx->Height) { + ++(ctx->Pass); + ctx->cury = 4; } + break; + + case 1: + ctx->cury += 8; + if (ctx->cury >= ctx->Height) { + ++(ctx->Pass); + ctx->cury = 2; + } + break; + + case 2: + ctx->cury += 4; + if (ctx->cury >= ctx->Height) { + ++(ctx->Pass); + ctx->cury = 1; + } + break; + + case 3: + ctx->cury += 2; + break; + } } + } } -/* - * Return the next pixel from the image - */ -static int -GIFNextPixel(gdImagePtr im, GifCtx *ctx) +/* Return the next pixel from the image */ +static int GIFNextPixel(gdImagePtr im, GifCtx *ctx) { - int r; + int r; - if( ctx->CountDown == 0 ) - return EOF; + if (ctx->CountDown == 0) { + return EOF; + } - --(ctx->CountDown); + --(ctx->CountDown); - r = gdImageGetPixel(im, ctx->curx, ctx->cury); + r = gdImageGetPixel(im, ctx->curx, ctx->cury); - BumpPixel(ctx); + BumpPixel(ctx); - return r; + return r; } /* public */ -static void -GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im) +static void GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background, + int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, + gdImagePtr im) +{ + int B; + int RWidth, RHeight; + int LeftOfs, TopOfs; + int Resolution; + int ColorMapSize; + int InitCodeSize; + int i; + GifCtx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + ctx.Interlace = GInterlace; + ctx.in_count = 1; + + ColorMapSize = 1 << BitsPerPixel; + + RWidth = ctx.Width = GWidth; + RHeight = ctx.Height = GHeight; + LeftOfs = TopOfs = 0; + + Resolution = BitsPerPixel; + + /* Calculate number of bits we are expecting */ + ctx.CountDown = (long)ctx.Width * (long)ctx.Height; + + /* Indicate which pass we are on (if interlace) */ + ctx.Pass = 0; + + /* The initial code size */ + if (BitsPerPixel <= 1) { + InitCodeSize = 2; + } else { + InitCodeSize = BitsPerPixel; + } + + /* Set up the current x and y position */ + ctx.curx = ctx.cury = 0; + + /* Write the Magic header */ + gdPutBuf(Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp); + + /* Write out the screen width and height */ + gifPutWord(RWidth, fp); + gifPutWord(RHeight, fp); + + /* Indicate that there is a global colour map */ + /* Yes, there is a color map */ + B = 0x80; + + /* OR in the resolution */ + B |= (Resolution - 1) << 4; + + /* OR in the Bits per Pixel */ + B |= (BitsPerPixel - 1); + + /* Write it out */ + gdPutC(B, fp); + + /* Write out the Background colour */ + gdPutC(Background, fp); + + /* Byte of 0's (future expansion) */ + gdPutC(0, fp); + + /* Write out the Global Colour Map */ + for (i = 0; i < ColorMapSize; ++i) { + gdPutC(Red[i], fp); + gdPutC(Green[i], fp); + gdPutC(Blue[i], fp); + } + + /* Write out extension for transparent colour index, if necessary. */ + if (Transparent >= 0) { + gdPutC('!', fp); + gdPutC(0xf9, fp); + gdPutC(4, fp); + gdPutC(1, fp); + gdPutC(0, fp); + gdPutC(0, fp); + gdPutC((unsigned char)Transparent, fp); + gdPutC(0, fp); + } + + /* Write an Image separator */ + gdPutC(',', fp); + + /* Write the Image header */ + gifPutWord(LeftOfs, fp); + gifPutWord(TopOfs, fp); + gifPutWord(ctx.Width, fp); + gifPutWord(ctx.Height, fp); + + /* Write out whether or not the image is interlaced */ + if (ctx.Interlace) { + gdPutC(0x40, fp); + } else { + gdPutC(0x00, fp); + } + + /* Write out the initial code size */ + gdPutC(InitCodeSize, fp); + + /* Go and actually compress the data */ + compress(InitCodeSize + 1, fp, im, &ctx); + + /* Write out a Zero-length packet (to end the series) */ + gdPutC(0, fp); + + /* Write the GIF file terminator */ + gdPutC(';', fp); +} + +static void GIFAnimEncode(gdIOCtxPtr fp, int IWidth, int IHeight, int LeftOfs, int TopOfs, + int GInterlace, int Transparent, int Delay, int Disposal, + int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im) { - int B; - int RWidth, RHeight; - int LeftOfs, TopOfs; - int Resolution; - int ColorMapSize; - int InitCodeSize; - int i; - GifCtx ctx; - - memset(&ctx, 0, sizeof(ctx)); - ctx.Interlace = GInterlace; - ctx.in_count = 1; - - ColorMapSize = 1 << BitsPerPixel; - - RWidth = ctx.Width = GWidth; - RHeight = ctx.Height = GHeight; - LeftOfs = TopOfs = 0; - - Resolution = BitsPerPixel; - - /* - * Calculate number of bits we are expecting - */ - ctx.CountDown = (long)ctx.Width * (long)ctx.Height; - - /* - * Indicate which pass we are on (if interlace) - */ - ctx.Pass = 0; - - /* - * The initial code size - */ - if( BitsPerPixel <= 1 ) - InitCodeSize = 2; - else - InitCodeSize = BitsPerPixel; - - /* - * Set up the current x and y position - */ - ctx.curx = ctx.cury = 0; - - /* - * Write the Magic header - */ - gdPutBuf(Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp ); - - /* - * Write out the screen width and height - */ - gifPutWord( RWidth, fp ); - gifPutWord( RHeight, fp ); - - /* - * Indicate that there is a global colour map - */ - B = 0x80; /* Yes, there is a color map */ - - /* - * OR in the resolution - */ - B |= (Resolution - 1) << 4; - - /* - * OR in the Bits per Pixel - */ - B |= (BitsPerPixel - 1); - - /* - * Write it out - */ - gdPutC( B, fp ); - - /* - * Write out the Background colour - */ - gdPutC( Background, fp ); - - /* - * Byte of 0's (future expansion) - */ - gdPutC( 0, fp ); - - /* - * Write out the Global Colour Map - */ - for( i=0; i= 0 ? 1 : 0) | (Disposal << 2), fp); + gdPutC((unsigned char)(Delay & 255), fp); + gdPutC((unsigned char)((Delay >> 8) & 255), fp); + gdPutC((unsigned char)Transparent, fp); + gdPutC(0, fp); + + /* Write an Image separator */ + gdPutC(',', fp); + + /* Write out the Image header */ + gifPutWord(LeftOfs, fp); + gifPutWord(TopOfs, fp); + gifPutWord(ctx.Width, fp); + gifPutWord(ctx.Height, fp); + + /* Indicate that there is a local colour map */ + B = (Red && Green && Blue) ? 0x80 : 0; + + /* OR in the interlacing */ + B |= ctx.Interlace ? 0x40 : 0; + + /* OR in the Bits per Pixel */ + B |= (Red && Green && Blue) ? (BitsPerPixel - 1) : 0; + + /* Write it out */ + gdPutC(B, fp); + + /* Write out the Local Colour Map */ + if (Red && Green && Blue) { + for (i = 0; i < ColorMapSize; ++i) { + gdPutC(Red[i], fp); + gdPutC(Green[i], fp); + gdPutC(Blue[i], fp); } + } + + /* Write out the initial code size */ + gdPutC(InitCodeSize, fp); - /* - * Write out extension for transparent colour index, if necessary. - */ - if ( Transparent >= 0 ) { - gdPutC( '!', fp ); - gdPutC( 0xf9, fp ); - gdPutC( 4, fp ); - gdPutC( 1, fp ); - gdPutC( 0, fp ); - gdPutC( 0, fp ); - gdPutC( (unsigned char) Transparent, fp ); - gdPutC( 0, fp ); - } - - /* - * Write an Image separator - */ - gdPutC( ',', fp ); - - /* - * Write the Image header - */ - - gifPutWord( LeftOfs, fp ); - gifPutWord( TopOfs, fp ); - gifPutWord( ctx.Width, fp ); - gifPutWord( ctx.Height, fp ); - - /* - * Write out whether or not the image is interlaced - */ - if( ctx.Interlace ) - gdPutC( 0x40, fp ); - else - gdPutC( 0x00, fp ); - - /* - * Write out the initial code size - */ - gdPutC( InitCodeSize, fp ); - - /* - * Go and actually compress the data - */ - compress( InitCodeSize+1, fp, im, &ctx ); - - /* - * Write out a Zero-length packet (to end the series) - */ - gdPutC( 0, fp ); - - /* - * Write the GIF file terminator - */ - gdPutC( ';', fp ); + /* Go and actually compress the data */ + compress(InitCodeSize + 1, fp, im, &ctx); + + /* Write out a Zero-length packet (to end the series) */ + gdPutC(0, fp); } /*************************************************************************** @@ -430,16 +1283,14 @@ GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background * ***************************************************************************/ -/* - * General DEFINEs - */ +/* General DEFINEs */ -#define GIFBITS 12 +#define GIFBITS 12 #ifdef NO_UCHAR - typedef char char_type; -#else /*NO_UCHAR*/ - typedef unsigned char char_type; +typedef char char_type; +#else /*NO_UCHAR*/ +typedef unsigned char char_type; #endif /*NO_UCHAR*/ /* @@ -460,15 +1311,14 @@ GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background #define ARGVAL() (*++(*argv) || (--argc && *++argv)) -#ifdef COMPATIBLE /* But wrong! */ -# define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1) +#ifdef COMPATIBLE /* But wrong! */ +#define MAXCODE(n_bits) ((code_int)1 << (n_bits) - 1) #else /*COMPATIBLE*/ -# define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) +#define MAXCODE(n_bits) (((code_int)1 << (n_bits)) - 1) #endif /*COMPATIBLE*/ -#define HashTabOf(i) ctx->htab[i] -#define CodeTabOf(i) ctx->codetab[i] - +#define HashTabOf(i) ctx->htab[i] +#define CodeTabOf(i) ctx->codetab[i] /* * To save much memory, we overlay the table used by compress() with those @@ -480,8 +1330,8 @@ GIFEncode(gdIOCtxPtr fp, int GWidth, int GHeight, int GInterlace, int Background */ #define tab_prefixof(i) CodeTabOf(i) -#define tab_suffixof(i) ((char_type*)(htab))[i] -#define de_stack ((char_type*)&tab_suffixof((code_int)1<g_init_bits = init_bits; ctx->g_outfile = outfile; - /* - * Set up the necessary values - */ + /* Set up the necessary values */ ctx->offset = 0; ctx->out_count = 0; ctx->clear_flg = 0; @@ -535,67 +1380,77 @@ compress(int init_bits, gdIOCtxPtr outfile, gdImagePtr im, GifCtx *ctx) char_init(ctx); - ent = GIFNextPixel( im, ctx ); + ent = GIFNextPixel(im, ctx); hshift = 0; - for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L ) + for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) { ++hshift; - hshift = 8 - hshift; /* set hash code range bound */ + } + hshift = 8 - hshift; /* set hash code range bound */ hsize_reg = hsize; - cl_hash( (count_int) hsize_reg, ctx ); /* clear hash table */ + cl_hash((count_int)hsize_reg, ctx); /* clear hash table */ - output( (code_int)ctx->ClearCode, ctx ); + output((code_int)ctx->ClearCode, ctx); #ifdef SIGNED_COMPARE_SLOW - while ( (c = GIFNextPixel( im, ctx )) != (unsigned) EOF ) { -#else /*SIGNED_COMPARE_SLOW*/ - while ( (c = GIFNextPixel( im, ctx )) != EOF ) { /* } */ + while ((c = GIFNextPixel(im, ctx)) != (unsigned)EOF) { +#else /*SIGNED_COMPARE_SLOW*/ + while ((c = GIFNextPixel(im, ctx)) != EOF) { #endif /*SIGNED_COMPARE_SLOW*/ ++(ctx->in_count); - fcode = (long) (((long) c << maxbits) + ent); - i = (((code_int)c << hshift) ^ ent); /* xor hashing */ + fcode = (long)(((long)c << maxbits) + ent); + i = (((code_int)c << hshift) ^ ent); /* xor hashing */ - if ( HashTabOf (i) == fcode ) { - ent = CodeTabOf (i); + if (HashTabOf(i) == fcode) { + ent = CodeTabOf(i); continue; - } else if ( (long)HashTabOf (i) < 0 ) /* empty slot */ + } else if ((long)HashTabOf(i) < 0) { /* empty slot */ goto nomatch; - disp = hsize_reg - i; /* secondary hash (after G. Knott) */ - if ( i == 0 ) + } + + disp = hsize_reg - i; /* secondary hash (after G. Knott) */ + + if (i == 0) { disp = 1; -probe: - if ( (i -= disp) < 0 ) + } + + probe: + if ((i -= disp) < 0) { i += hsize_reg; + } - if ( HashTabOf (i) == fcode ) { - ent = CodeTabOf (i); + if (HashTabOf(i) == fcode) { + ent = CodeTabOf(i); continue; } - if ( (long)HashTabOf (i) > 0 ) + + if ((long)HashTabOf(i) > 0) { goto probe; -nomatch: - output ( (code_int) ent, ctx ); + } + + nomatch: + output((code_int)ent, ctx); ++(ctx->out_count); ent = c; #ifdef SIGNED_COMPARE_SLOW - if ( (unsigned) ctx->free_ent < (unsigned) maxmaxcode) { -#else /*SIGNED_COMPARE_SLOW*/ - if ( ctx->free_ent < maxmaxcode ) { /* } */ -#endif /*SIGNED_COMPARE_SLOW*/ - CodeTabOf (i) = ctx->free_ent++; /* code -> hashtable */ - HashTabOf (i) = fcode; - } else - cl_block(ctx); + if ((unsigned)ctx->free_ent < (unsigned)maxmaxcode) { +#else /*SIGNED_COMPARE_SLOW*/ + if (ctx->free_ent < maxmaxcode) { /* } */ +#endif /*SIGNED_COMPARE_SLOW*/ + CodeTabOf(i) = ctx->free_ent++; /* code -> hashtable */ + HashTabOf(i) = fcode; + } else { + cl_block(ctx); + } } - /* - * Put out the final code. - */ - output( (code_int)ent, ctx ); + + /* Put out the final code. */ + output((code_int)ent, ctx); ++(ctx->out_count); - output( (code_int) ctx->EOFCode, ctx ); + output((code_int)ctx->EOFCode, ctx); } /***************************************************************** @@ -615,11 +1470,9 @@ compress(int init_bits, gdIOCtxPtr outfile, gdImagePtr im, GifCtx *ctx) * code in turn. When the buffer fills up empty it and start over. */ -static const unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, - 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, - 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - +static const unsigned long masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, + 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; /* Arbitrary value to mark output is done. When we see EOFCode, then we don't * expect to see any more data. If we do (e.g. corrupt image inputs), cur_bits @@ -627,25 +1480,22 @@ static const unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, */ #define CUR_BITS_FINISHED -1000 - -static void -output(code_int code, GifCtx *ctx) +static void output(code_int code, GifCtx *ctx) { - if (ctx->cur_bits == CUR_BITS_FINISHED) { - return; - } + if (ctx->cur_bits == CUR_BITS_FINISHED) + return; + ctx->cur_accum &= masks[ctx->cur_bits]; - ctx->cur_accum &= masks[ ctx->cur_bits ]; - - if( ctx->cur_bits > 0 ) + if (ctx->cur_bits > 0) { ctx->cur_accum |= ((long)code << ctx->cur_bits); - else + } else { ctx->cur_accum = code; + } ctx->cur_bits += ctx->n_bits; - while( ctx->cur_bits >= 8 ) { - char_out( (unsigned int)(ctx->cur_accum & 0xff), ctx ); + while (ctx->cur_bits >= 8) { + char_out((unsigned int)(ctx->cur_accum & 0xff), ctx); ctx->cur_accum >>= 8; ctx->cur_bits -= 8; } @@ -654,35 +1504,29 @@ output(code_int code, GifCtx *ctx) * If the next entry is going to be too big for the code size, * then increase it, if possible. */ - if ( ctx->free_ent > ctx->maxcode || ctx->clear_flg ) { - - if( ctx->clear_flg ) { - - ctx->maxcode = MAXCODE (ctx->n_bits = ctx->g_init_bits); - ctx->clear_flg = 0; - + if (ctx->free_ent > ctx->maxcode || ctx->clear_flg) { + if (ctx->clear_flg) { + ctx->maxcode = MAXCODE(ctx->n_bits = ctx->g_init_bits); + ctx->clear_flg = 0; + } else { + ++(ctx->n_bits); + if (ctx->n_bits == maxbits) { + ctx->maxcode = maxmaxcode; } else { - - ++(ctx->n_bits); - if ( ctx->n_bits == maxbits ) - ctx->maxcode = maxmaxcode; - else - ctx->maxcode = MAXCODE(ctx->n_bits); + ctx->maxcode = MAXCODE(ctx->n_bits); } } + } - if( code == ctx->EOFCode ) { - /* - * At EOF, write the rest of the buffer. - */ - while( ctx->cur_bits > 0 ) { - char_out( (unsigned int)(ctx->cur_accum & 0xff), ctx); - ctx->cur_accum >>= 8; - ctx->cur_bits -= 8; + if (code == ctx->EOFCode) { + /* At EOF, write the rest of the buffer. */ + while (ctx->cur_bits > 0) { + char_out((unsigned int)(ctx->cur_accum & 0xff), ctx); + ctx->cur_accum >>= 8; + ctx->cur_bits -= 8; } - - /* Flag that it's done to prevent re-entry. */ - ctx->cur_bits = CUR_BITS_FINISHED; + /* Flag that it's done to prevent re-entry. */ + ctx->cur_bits = CUR_BITS_FINISHED; flush_char(ctx); } @@ -691,50 +1535,45 @@ output(code_int code, GifCtx *ctx) /* * Clear out the hash table */ -static void -cl_block (GifCtx *ctx) /* table clear for block compress */ +static void cl_block(GifCtx *ctx) /* table clear for block compress */ { + cl_hash((count_int)hsize, ctx); + ctx->free_ent = ctx->ClearCode + 2; + ctx->clear_flg = 1; - cl_hash ( (count_int) hsize, ctx ); - ctx->free_ent = ctx->ClearCode + 2; - ctx->clear_flg = 1; - - output( (code_int)ctx->ClearCode, ctx); + output((code_int)ctx->ClearCode, ctx); } -static void -cl_hash(register count_int chsize, GifCtx *ctx) /* reset code table */ - +static void cl_hash(register count_int chsize, GifCtx *ctx) /* reset code table */ { - - register count_int *htab_p = ctx->htab+chsize; - - register long i; - register long m1 = -1; - - i = chsize - 16; - do { /* might use Sys V memset(3) here */ - *(htab_p-16) = m1; - *(htab_p-15) = m1; - *(htab_p-14) = m1; - *(htab_p-13) = m1; - *(htab_p-12) = m1; - *(htab_p-11) = m1; - *(htab_p-10) = m1; - *(htab_p-9) = m1; - *(htab_p-8) = m1; - *(htab_p-7) = m1; - *(htab_p-6) = m1; - *(htab_p-5) = m1; - *(htab_p-4) = m1; - *(htab_p-3) = m1; - *(htab_p-2) = m1; - *(htab_p-1) = m1; - htab_p -= 16; - } while ((i -= 16) >= 0); - - for ( i += 16; i > 0; --i ) - *--htab_p = m1; + register count_int *htab_p = ctx->htab + chsize; + register long i; + register long m1 = -1; + + i = chsize - 16; + do { /* might use Sys V memset(3) here */ + *(htab_p - 16) = m1; + *(htab_p - 15) = m1; + *(htab_p - 14) = m1; + *(htab_p - 13) = m1; + *(htab_p - 12) = m1; + *(htab_p - 11) = m1; + *(htab_p - 10) = m1; + *(htab_p - 9) = m1; + *(htab_p - 8) = m1; + *(htab_p - 7) = m1; + *(htab_p - 6) = m1; + *(htab_p - 5) = m1; + *(htab_p - 4) = m1; + *(htab_p - 3) = m1; + *(htab_p - 2) = m1; + *(htab_p - 1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + + for (i += 16; i > 0; --i) { + *--htab_p = m1; + } } /****************************************************************************** @@ -746,43 +1585,36 @@ cl_hash(register count_int chsize, GifCtx *ctx) /* reset code table */ /* * Set up the 'byte output' routine */ -static void -char_init(GifCtx *ctx) -{ - ctx->a_count = 0; -} +static void char_init(GifCtx *ctx) { ctx->a_count = 0; } /* * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. */ -static void -char_out(int c, GifCtx *ctx) +static void char_out(int c, GifCtx *ctx) { - ctx->accum[ ctx->a_count++ ] = c; - if( ctx->a_count >= 254 ) - flush_char(ctx); + ctx->accum[ctx->a_count++] = c; + if (ctx->a_count >= 254) { + flush_char(ctx); + } } /* * Flush the packet to disk, and reset the accumulator */ -static void -flush_char(GifCtx *ctx) +static void flush_char(GifCtx *ctx) { - if( ctx->a_count > 0 ) { - gdPutC( ctx->a_count, ctx->g_outfile ); - gdPutBuf( ctx->accum, ctx->a_count, ctx->g_outfile ); - ctx->a_count = 0; - } + if (ctx->a_count > 0) { + gdPutC(ctx->a_count, ctx->g_outfile); + gdPutBuf(ctx->accum, ctx->a_count, ctx->g_outfile); + ctx->a_count = 0; + } } static int gifPutWord(int w, gdIOCtx *out) { - /* Byte order is little-endian */ - gdPutC(w & 0xFF, out); - gdPutC((w >> 8) & 0xFF, out); - return 0; + /* Byte order is little-endian */ + gdPutC(w & 0xFF, out); + gdPutC((w >> 8) & 0xFF, out); + return 0; } - - diff --git a/ext/gd/libgd/gd_gradient.c b/ext/gd/libgd/gd_gradient.c new file mode 100644 index 000000000000..e1281f3e10e8 --- /dev/null +++ b/ext/gd/libgd/gd_gradient.c @@ -0,0 +1,318 @@ +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_gradient.h" +#include "gd_path_matrix.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +static int finite_matrix(const gdPathMatrix *m) +{ + return m && isfinite(m->m00) && isfinite(m->m10) && isfinite(m->m01) && isfinite(m->m11) && + isfinite(m->m02) && isfinite(m->m12); +} + +static int valid_extend(gdExtendMode e) { return e >= GD_EXTEND_NONE && e <= GD_EXTEND_PAD; } + +static gdPaintPtr create_gradient(gdGradientKind kind) +{ + gdPaintPtr paint = gdMalloc(sizeof(*paint)); + gdGradient *g; + if (!paint) + return NULL; + g = gdCalloc(1, sizeof(*g)); + if (!g) { + gdFree(paint); + return NULL; + } + g->kind = kind; + g->extend = GD_EXTEND_PAD; + gdPathMatrixInitIdentity(&g->matrix); + gdPathMatrixInitIdentity(&g->inverse); + paint->ref = 1; + paint->type = gdPaintTypeGradient; + paint->gradient = g; + return paint; +} + +BGD_DECLARE(gdPaintPtr) gdPaintCreateLinear(double x0, double y0, double x1, double y1) +{ + gdPaintPtr p; + gdGradient *g; + double dx, dy, ls, s; + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) + return NULL; + p = create_gradient(GD_GRADIENT_LINEAR); + if (!p) + return NULL; + g = p->gradient; + dx = x1 - x0; + dy = y1 - y0; + ls = dx * dx + dy * dy; + s = fmax(fmax(fabs(x0), fabs(y0)), fmax(fabs(x1), fabs(y1))); + g->geometry.linear.x0 = x0; + g->geometry.linear.y0 = y0; + g->geometry.linear.dx = dx; + g->geometry.linear.dy = dy; + g->geometry.linear.scale = s; + g->geometry.linear.inverse_length_squared = + (isfinite(ls) && ls > 64.0 * DBL_EPSILON * fmax(1.0, s * s)) ? 1.0 / ls : 0.0; + return p; +} + +BGD_DECLARE(gdPaintPtr) +gdPaintCreateRadial(double x0, double y0, double r0, double x1, double y1, double r1) +{ + gdPaintPtr p; + gdGradient *g; + double s; + if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) || !isfinite(x1) || !isfinite(y1) || + !isfinite(r1) || r0 < 0 || r1 < 0) + return NULL; + p = create_gradient(GD_GRADIENT_RADIAL); + if (!p) + return NULL; + g = p->gradient; + g->geometry.radial.x0 = x0; + g->geometry.radial.y0 = y0; + g->geometry.radial.r0 = r0; + g->geometry.radial.x1 = x1; + g->geometry.radial.y1 = y1; + g->geometry.radial.r1 = r1; + g->geometry.radial.cdx = x1 - x0; + g->geometry.radial.cdy = y1 - y0; + g->geometry.radial.dr = r1 - r0; + g->geometry.radial.quadratic_a = g->geometry.radial.cdx * g->geometry.radial.cdx + + g->geometry.radial.cdy * g->geometry.radial.cdy - + g->geometry.radial.dr * g->geometry.radial.dr; + s = fmax(fmax(fabs(x0), fabs(y0)), fmax(fabs(x1), fabs(y1))); + s = fmax(s, fmax(r0, r1)); + g->geometry.radial.scale = s; + return p; +} + +void gdGradientDestroy(gdGradient *g) +{ + if (g) { + gdFree(g->stops); + gdFree(g); + } +} + +static gdPremulPixelF ramp_at(const gdColorStop *s, size_t n, double t) +{ + size_t i, last; + gdPremulPixelF z = {0, 0, 0, 0}, a, b, out; + double u; + if (!n) + return z; + if (n == 1) + return s[0].color; + if (t < s[0].offset) + return s[0].color; + if (t >= s[n - 1].offset) + return s[n - 1].color; + for (i = 0; i < n && s[i].offset < t; i++) { + } + if (s[i].offset == t) { + while (i + 1 < n && s[i + 1].offset == t) + i++; + return s[i].color; + } + last = i; + a = s[i - 1].color; + b = s[last].color; + u = (t - s[i - 1].offset) / (s[last].offset - s[i - 1].offset); + out.r = (float)(a.r + (b.r - a.r) * u); + out.g = (float)(a.g + (b.g - a.g) * u); + out.b = (float)(a.b + (b.b - a.b) * u); + out.a = (float)(a.a + (b.a - a.a) * u); + return out; +} + +BGD_DECLARE(int) +gdPaintAddColorStopRgba(gdPaintPtr p, double off, double r, double g, double b, double a) +{ + gdGradient *gradient; + gdColorStop *candidate; + gdPremulPixelF lut[GD_GRADIENT_LUT_SIZE]; + size_t n, i, j; + if (!p || p->type != gdPaintTypeGradient || !isfinite(off) || !isfinite(r) || !isfinite(g) || + !isfinite(b) || !isfinite(a) || off < 0 || off > 1 || r < 0 || r > 1 || g < 0 || g > 1 || + b < 0 || b > 1 || a < 0 || a > 1) + return 0; + gradient = p->gradient; + n = gradient->stop_count + 1; + candidate = gdMalloc(n * sizeof(*candidate)); + if (!candidate) + return 0; + if (gradient->stop_count) + memcpy(candidate, gradient->stops, gradient->stop_count * sizeof(*candidate)); + candidate[n - 1].offset = off; + candidate[n - 1].sequence = gradient->next_sequence; + candidate[n - 1].color.r = (float)(r * a); + candidate[n - 1].color.g = (float)(g * a); + candidate[n - 1].color.b = (float)(b * a); + candidate[n - 1].color.a = (float)a; + for (i = 1; i < n; i++) { + gdColorStop v = candidate[i]; + j = i; + while (j && + (candidate[j - 1].offset > v.offset || + (candidate[j - 1].offset == v.offset && candidate[j - 1].sequence > v.sequence))) { + candidate[j] = candidate[j - 1]; + j--; + } + candidate[j] = v; + } + for (i = 0; i < GD_GRADIENT_LUT_SIZE; i++) + lut[i] = ramp_at(candidate, n, i / (double)(GD_GRADIENT_LUT_SIZE - 1)); + gdFree(gradient->stops); + gradient->stops = candidate; + gradient->stop_count = n; + gradient->stop_capacity = n; + gradient->next_sequence++; + memcpy(gradient->lut, lut, sizeof(lut)); + return 1; +} + +BGD_DECLARE(int) gdPaintAddColorStopRgb(gdPaintPtr p, double o, double r, double g, double b) +{ + return gdPaintAddColorStopRgba(p, o, r, g, b, 1.0); +} + +BGD_DECLARE(int) gdPaintSetExtend(gdPaintPtr p, gdExtendMode e) +{ + if (!p || !valid_extend(e)) + return 0; + if (p->type == gdPaintTypeGradient) { + p->gradient->extend = e; + return 1; + } + if (p->type == gdPaintTypePattern) { + p->pattern->extend = e; + return 1; + } + return 0; +} + +BGD_DECLARE(int) gdPaintSetMatrix(gdPaintPtr p, const gdPathMatrixPtr m) +{ + gdPathMatrix inv; + if (!p || !finite_matrix(m)) + return 0; + inv = *m; + if (!gdPathMatrixInvert(&inv)) + return 0; + if (p->type == gdPaintTypeGradient) { + p->gradient->matrix = *m; + p->gradient->inverse = inv; + return 1; + } + if (p->type == gdPaintTypePattern) { + p->pattern->matrix = *m; + return 1; + } + return 0; +} + +int gdExtendFold(double raw, gdExtendMode mode, double *f) +{ + double m; + if (!f || !isfinite(raw) || !valid_extend(mode)) + return 0; + if (raw == 0.0) + raw = 0.0; + switch (mode) { + case GD_EXTEND_NONE: + if (raw < 0 || raw > 1) + return 0; + *f = raw; + return 1; + case GD_EXTEND_PAD: + *f = raw < 0 ? 0 : (raw > 1 ? 1 : raw); + return 1; + case GD_EXTEND_REPEAT: + *f = raw - floor(raw); + return 1; + case GD_EXTEND_REFLECT: + m = fmod(raw, 2.0); + if (m < 0) + m += 2; + *f = m <= 1 ? m : 2 - m; + return 1; + default: + return 0; + } +} + +static int radial_t(const gdGradient *g, double x, double y, double *out) +{ + const double cx = g->geometry.radial.cdx, cy = g->geometry.radial.cdy, + dr = g->geometry.radial.dr; + double px = x - g->geometry.radial.x0, py = y - g->geometry.radial.y0, + r0 = g->geometry.radial.r0; + double A = g->geometry.radial.quadratic_a, B = px * cx + py * cy + r0 * dr, + C = px * px + py * py - r0 * r0; + double scale = fmax(1.0, g->geometry.radial.scale + fabs(px) + fabs(py)); + double coord_tol = 64 * DBL_EPSILON * scale; + double tol = coord_tol * scale, roots[2], disc; + int n = 0, valid = 0; + if (fabs(cx) <= coord_tol && fabs(cy) <= coord_tol && fabs(dr) <= coord_tol) + return 0; + if (fabs(A) <= tol) { + if (fabs(B) <= tol) + return 0; + roots[n++] = C / (2 * B); + } else { + disc = B * B - A * C; + if (disc < 0) { + if (disc >= -tol * scale * scale) + disc = 0; + else + return 0; + } + disc = sqrt(disc); + roots[n++] = (B + disc) / A; + roots[n++] = (B - disc) / A; + } + for (int i = 0; i < n; i++) + if (isfinite(roots[i]) && r0 + roots[i] * dr >= -coord_tol && + (g->extend != GD_EXTEND_NONE || (roots[i] >= 0 && roots[i] <= 1))) { + if (!valid || roots[i] > *out) + *out = roots[i]; + valid = 1; + } + return valid; +} + +gdPremulPixelF gdGradientSample(const gdGradient *g, const gdPathMatrix *m, double x, double y) +{ + gdPremulPixelF z = {0, 0, 0, 0}; + double px, py, t, f; + long i; + if (!g || !m || !g->stop_count) + return z; + px = m->m01 * y + m->m00 * x + m->m02; + py = m->m11 * y + m->m10 * x + m->m12; + if (g->kind == GD_GRADIENT_LINEAR) { + if (g->geometry.linear.inverse_length_squared == 0) + return z; + t = ((px - g->geometry.linear.x0) * g->geometry.linear.dx + + (py - g->geometry.linear.y0) * g->geometry.linear.dy) * + g->geometry.linear.inverse_length_squared; + } else if (!radial_t(g, px, py, &t)) + return z; + if (!gdExtendFold(t, g->extend, &f)) + return z; + i = (long)floor(f * (GD_GRADIENT_LUT_SIZE - 1) + 0.5); + if (i < 0) + i = 0; + if (i >= GD_GRADIENT_LUT_SIZE) + i = GD_GRADIENT_LUT_SIZE - 1; + return g->lut[i]; +} diff --git a/ext/gd/libgd/gd_gradient.h b/ext/gd/libgd/gd_gradient.h new file mode 100644 index 000000000000..308654232f68 --- /dev/null +++ b/ext/gd/libgd/gd_gradient.h @@ -0,0 +1,43 @@ +#ifndef GD_GRADIENT_H +#define GD_GRADIENT_H + +#include "gd_compositor.h" +#include "gd_vector2d_private.h" +#include +#include + +#define GD_GRADIENT_LUT_SIZE 1024 + +typedef enum { GD_GRADIENT_LINEAR, GD_GRADIENT_RADIAL } gdGradientKind; + +typedef struct { + double offset; + gdPremulPixelF color; + uint64_t sequence; +} gdColorStop; + +struct gdGradientStruct { + gdGradientKind kind; + gdExtendMode extend; + gdPathMatrix matrix; + gdPathMatrix inverse; + gdColorStop *stops; + size_t stop_count, stop_capacity; + uint64_t next_sequence; + gdPremulPixelF lut[GD_GRADIENT_LUT_SIZE]; + union { + struct { + double x0, y0, dx, dy, inverse_length_squared, scale; + } linear; + struct { + double x0, y0, r0, x1, y1, r1, cdx, cdy, dr, quadratic_a, scale; + } radial; + } geometry; +}; + +void gdGradientDestroy(gdGradient *gradient); +int gdExtendFold(double raw, gdExtendMode mode, double *folded); +gdPremulPixelF gdGradientSample(const gdGradient *gradient, const gdPathMatrix *device_to_pattern, + double device_x, double device_y); + +#endif diff --git a/ext/gd/libgd/gd_heif.c b/ext/gd/libgd/gd_heif.c new file mode 100644 index 000000000000..c8b6057d00bc --- /dev/null +++ b/ext/gd/libgd/gd_heif.c @@ -0,0 +1,621 @@ +/** + * File: HEIF IO + * + * Read and write HEIF images. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "gd.h" +#include "gd_errors.h" +#include "gdhelpers.h" +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBHEIF +#include + +#define GD_HEIF_ALLOC_STEP (4 * 1024) +#define GD_HEIF_HEADER 12 + +typedef enum gd_heif_brand { + GD_HEIF_BRAND_AVIF = 1, + GD_HEIF_BRAND_MIF1 = 2, + GD_HEIF_BRAND_HEIC = 4, + GD_HEIF_BRAND_HEIX = 8, +} gd_heif_brand; + +/* + Function: gdImageCreateFromHeif + + is called to load truecolor images from + HEIF format files. Invoke with an + already opened pointer to a file containing the desired + image. returns a to the new + truecolor image, or NULL if unable to load the image (most often + because the file is corrupt or does not contain a HEIF + image). does not close the file. + + You can inspect the sx and sy members of the image to determine + its size. The image must eventually be destroyed using + . + + *The returned image is always a truecolor image.* + + Parameters: + + infile - The input FILE pointer. + + Returns: + + A pointer to the new *truecolor* image. This will need to be + destroyed with once it is no longer needed. + + On error, returns NULL. +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + + if (!in) + return NULL; + im = gdImageCreateFromHeifCtx(in); + in->gd_free(in); + + return im; +} + +/* + Function: gdImageCreateFromHeifPtr + + See . + + Parameters: + + size - size of HEIF data in bytes. + data - pointer to HEIF data. +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + + if (!in) + return NULL; + im = gdImageCreateFromHeifCtx(in); + in->gd_free(in); + + return im; +} + +static int _gdHeifCheckBrand(unsigned char *magic, gd_heif_brand expected_brand) +{ + if (memcmp(magic + 4, "ftyp", 4) != 0) + return GD_FALSE; + if (memcmp(magic + 8, "avif", 4) == 0 && expected_brand & GD_HEIF_BRAND_AVIF) + return GD_TRUE; + if (memcmp(magic + 8, "heic", 4) == 0 && expected_brand & GD_HEIF_BRAND_HEIC) + return GD_TRUE; + if (memcmp(magic + 8, "heix", 4) == 0 && expected_brand & GD_HEIF_BRAND_HEIX) + return GD_TRUE; + if (memcmp(magic + 8, "mif1", 4) == 0 && expected_brand & GD_HEIF_BRAND_MIF1) + return GD_TRUE; + + return GD_FALSE; +} + +static gdImagePtr _gdImageCreateFromHeifCtx(gdIOCtx *infile, gd_heif_brand expected_brand) +{ + struct heif_context *heif_ctx; + struct heif_decoding_options *heif_dec_opts; + struct heif_image_handle *heif_imhandle; + struct heif_image *heif_im; + struct heif_error err; + int width, height; + uint8_t *filedata = NULL; + uint8_t *rgba = NULL; + unsigned char *read, *temp, magic[GD_HEIF_HEADER]; + int magic_len; + size_t size = 0, n = GD_HEIF_ALLOC_STEP; + gdImagePtr im; + int x, y; + uint8_t *p, *row_start; + int stride; + + magic_len = gdGetBuf(magic, GD_HEIF_HEADER, infile); + if (magic_len != GD_HEIF_HEADER || !_gdHeifCheckBrand(magic, expected_brand)) { + gd_error("gd-heif incorrect type of file\n"); + return NULL; + } + gdSeek(infile, 0); + + while (n == GD_HEIF_ALLOC_STEP) { + temp = gdRealloc(filedata, size + GD_HEIF_ALLOC_STEP); + if (temp) { + filedata = temp; + read = temp + size; + } else { + gdFree(filedata); + gd_error("gd-heif decode realloc failed\n"); + return NULL; + } + + n = gdGetBuf(read, GD_HEIF_ALLOC_STEP, infile); + if (n > 0) { + size += n; + } + } + + heif_ctx = heif_context_alloc(); + if (heif_ctx == NULL) { + gd_error("gd-heif could not allocate context\n"); + gdFree(filedata); + return NULL; + } + err = heif_context_read_from_memory_without_copy(heif_ctx, filedata, size, NULL); + if (err.code != heif_error_Ok) { + gd_error("gd-heif context creation failed\n"); + gdFree(filedata); + heif_context_free(heif_ctx); + return NULL; + } + + heif_imhandle = NULL; + err = heif_context_get_primary_image_handle(heif_ctx, &heif_imhandle); + if (err.code != heif_error_Ok) { + gd_error("gd-heif cannot retreive handle\n"); + gdFree(filedata); + heif_context_free(heif_ctx); + return NULL; + } + + heif_im = NULL; + heif_dec_opts = heif_decoding_options_alloc(); + if (heif_dec_opts == NULL) { + gd_error("gd-heif could not allocate decode options\n"); + gdFree(filedata); + heif_image_handle_release(heif_imhandle); + heif_context_free(heif_ctx); + return NULL; + } + + heif_dec_opts->convert_hdr_to_8bit = GD_TRUE; + heif_dec_opts->ignore_transformations = GD_TRUE; + err = heif_decode_image(heif_imhandle, &heif_im, heif_colorspace_RGB, + heif_chroma_interleaved_RGBA, heif_dec_opts); + heif_decoding_options_free(heif_dec_opts); + if (err.code != heif_error_Ok) { + gd_error("gd-heif decoding failed\n"); + gdFree(filedata); + heif_image_handle_release(heif_imhandle); + heif_context_free(heif_ctx); + return NULL; + } + + width = heif_image_get_width(heif_im, heif_channel_interleaved); + height = heif_image_get_height(heif_im, heif_channel_interleaved); + + im = gdImageCreateTrueColor(width, height); + if (!im) { + gdFree(filedata); + heif_image_release(heif_im); + heif_image_handle_release(heif_imhandle); + heif_context_free(heif_ctx); + return NULL; + } + rgba = (uint8_t *)heif_image_get_plane_readonly(heif_im, heif_channel_interleaved, &stride); + if (!rgba) { + gd_error("gd-heif cannot get image plane\n"); + gdFree(filedata); + heif_image_release(heif_im); + heif_image_handle_release(heif_imhandle); + heif_context_free(heif_ctx); + gdImageDestroy(im); + return NULL; + } + row_start = rgba; + for (y = 0, p = rgba; y < height; y++) { + p = row_start; + for (x = 0; x < width; x++) { + uint8_t r = *(p++); + uint8_t g = *(p++); + uint8_t b = *(p++); + uint8_t a = gdAlphaMax - (*(p++) >> 1); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a); + } + row_start += stride; + } + gdFree(filedata); + heif_image_release(heif_im); + heif_image_handle_release(heif_imhandle); + heif_context_free(heif_ctx); + + return im; +} + +/* + Function: gdImageCreateFromHeifCtx + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtx *infile) +{ + return _gdImageCreateFromHeifCtx(infile, GD_HEIF_BRAND_AVIF | GD_HEIF_BRAND_MIF1 | + GD_HEIF_BRAND_HEIC | GD_HEIF_BRAND_HEIX); +} + +static struct heif_error _gdImageWriteHeif(struct heif_context *heif_ctx, const void *data, + size_t size, void *userdata) +{ + ARG_NOT_USED(heif_ctx); + gdIOCtx *outfile; + struct heif_error err; + int bytes_written; + + outfile = (gdIOCtx *)userdata; + if (outfile == NULL || data == NULL || size > INT_MAX) { + err.code = heif_error_Encoding_error; + err.subcode = heif_suberror_Cannot_write_output_data; + err.message = "gd-heif write callback received invalid arguments"; + return err; + } + + bytes_written = gdPutBuf(data, (int)size, outfile); + if (bytes_written != (int)size) { + err.code = heif_error_Encoding_error; + err.subcode = heif_suberror_Cannot_write_output_data; + err.message = "gd-heif failed to write output data"; + return err; + } + + err.code = heif_error_Ok; + err.subcode = heif_suberror_Unspecified; + err.message = ""; + + return err; +} + +/* returns GD_TRUE on success, GD_FALSE on failure */ +static int _gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, + gdHeifChroma chroma) +{ + struct heif_context *heif_ctx; + struct heif_encoder *heif_enc; + struct heif_image *heif_im; + struct heif_writer heif_wr; + struct heif_error err; + uint8_t *rgba; + int x, y; + uint8_t *p; + uint8_t *row_start; + int stride; + if (im == NULL) { + return GD_FALSE; + } + + if (codec != GD_HEIF_CODEC_HEVC && codec != GD_HEIF_CODEC_AV1) { + gd_error("Unsupported format by heif"); + return GD_FALSE; + } + + if (!gdImageTrueColor(im)) { + gd_error("Palette image not supported by heif\n"); + return GD_FALSE; + } + + if (overflow2(gdImageSX(im), 4)) { + return GD_FALSE; + } + + if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) { + return GD_FALSE; + } + + heif_ctx = heif_context_alloc(); + if (heif_ctx == NULL) { + gd_error("gd-heif could not allocate context\n"); + return GD_FALSE; + } + err = heif_context_get_encoder_for_format(heif_ctx, (enum heif_compression_format)codec, + &heif_enc); + if (err.code != heif_error_Ok) { + gd_error("gd-heif encoder acquisition failed (missing codec support?: " + "code: %d, subcode: %d, message: %s)\n", + err.code, err.subcode, err.message); + heif_context_free(heif_ctx); + return GD_FALSE; + } + + if (quality == 200) { + err = heif_encoder_set_lossless(heif_enc, GD_TRUE); + } else if (quality == -1) { + err = heif_encoder_set_lossy_quality(heif_enc, 80); + } else { + err = heif_encoder_set_lossy_quality(heif_enc, quality); + } + if (err.code != heif_error_Ok) { + gd_error("gd-heif invalid quality number\n"); + heif_encoder_release(heif_enc); + heif_context_free(heif_ctx); + return GD_FALSE; + } + + if (heif_get_version_number_major() >= 1 && heif_get_version_number_minor() >= 9) { + err = heif_encoder_set_parameter_string(heif_enc, "chroma", chroma); + if (err.code != heif_error_Ok) { + gd_error("gd-heif invalid chroma subsampling parameter\n"); + heif_encoder_release(heif_enc); + heif_context_free(heif_ctx); + return GD_FALSE; + } + } + + err = heif_image_create(gdImageSX(im), gdImageSY(im), heif_colorspace_RGB, + heif_chroma_interleaved_RGBA, &heif_im); + if (err.code != heif_error_Ok) { + gd_error("gd-heif image creation failed"); + heif_encoder_release(heif_enc); + heif_context_free(heif_ctx); + return GD_FALSE; + } + + err = heif_image_add_plane(heif_im, heif_channel_interleaved, gdImageSX(im), gdImageSY(im), 32); + if (err.code != heif_error_Ok) { + gd_error("gd-heif cannot add image plane\n"); + heif_image_release(heif_im); + heif_encoder_release(heif_enc); + heif_context_free(heif_ctx); + return GD_FALSE; + } + + rgba = (uint8_t *)heif_image_get_plane_readonly(heif_im, heif_channel_interleaved, &stride); + if (!rgba) { + gd_error("gd-heif cannot get image plane\n"); + heif_image_release(heif_im); + heif_encoder_release(heif_enc); + heif_context_free(heif_ctx); + return GD_FALSE; + } + row_start = rgba; + for (y = 0; y < gdImageSY(im); y++) { + p = row_start; + for (x = 0; x < gdImageSX(im); x++) { + int c; + char a; + c = im->tpixels[y][x]; + a = gdTrueColorGetAlpha(c); + if (a == 127) { + a = 0; + } else { + a = 255 - ((a << 1) + (a >> 6)); + } + *(p++) = gdTrueColorGetRed(c); + *(p++) = gdTrueColorGetGreen(c); + *(p++) = gdTrueColorGetBlue(c); + *(p++) = a; + } + row_start += stride; + } + err = heif_context_encode_image(heif_ctx, heif_im, heif_enc, NULL, NULL); + heif_encoder_release(heif_enc); + if (err.code != heif_error_Ok) { + gd_error("gd-heif encoding failed\n"); + heif_image_release(heif_im); + heif_context_free(heif_ctx); + return GD_FALSE; + } + heif_wr.write = _gdImageWriteHeif; + heif_wr.writer_api_version = 1; + err = heif_context_write(heif_ctx, &heif_wr, (void *)outfile); + + heif_image_release(heif_im); + heif_context_free(heif_ctx); + if (err.code != heif_error_Ok) { + gd_error("gd-heif write failed (code: %d, subcode: %d, message: %s)\n", err.code, + err.subcode, err.message); + return GD_FALSE; + } + + return GD_TRUE; +} + +/* + Function: gdImageHeifCtx + + Write the image as HEIF data via a . See + for more details. + + Parameters: + + im - The image to write. + outfile - The output sink. + quality - Image quality. + codec - The output coding format. + chroma - The output chroma subsampling format. + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) +gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + _gdImageHeifCtx(im, outfile, quality, codec, chroma); +} + +/* + Function: gdImageHeifEx + + outputs the specified image to the specified file in + HEIF format. The file must be open for writing. Under MSDOS and + all versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + If _quality_ is -1, a reasonable quality value (which should yield a + good general quality / size tradeoff for most situations) is used. Otherwise + _quality_ should be a value in the range 0-100, higher quality values + usually implying both higher quality and larger image sizes or 200, for + lossless codec. + + For _codec_, the default and most widely supported option is + GD_HEIF_CODEC_HEVC. GD_HEIF_CODEC_AV1 is a newer codec that may not be + supported by all decoders but can offer better compression efficiency. + They must be installed on the system and enabled at compile time to be used. + + Variants: + + stores the image using a struct. + + stores the image to RAM. + + Parameters: + + im - The image to save. + outFile - The FILE pointer to write to. + quality - Codec quality (0-100). + codec - The output coding format. + chroma - The output chroma subsampling format. + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) +gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageHeifCtx(im, out, quality, codec, chroma); + out->gd_free(out); +} + +/* + Function: gdImageHeif + + Variant of which uses the default quality (-1), the + default codec (GD_HEIF_Codec_HEVC) and the default chroma + subsampling (GD_HEIF_CHROMA_444). + + Parameters: + + im - The image to save + outFile - The FILE pointer to write to. + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444); + out->gd_free(out); +} + +/* + Function: gdImageHeifPtr + + See . +*/ +BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + + return rv; +} + +/* + Function: gdImageHeifPtrEx + + See . +*/ +BGD_DECLARE(void *) +gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageHeifCtx(im, out, quality, codec, chroma)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; +} + +#else /* HAVE_LIBHEIF */ + +static void _noHeifError(void) { gd_error("HEIF image support has been disabled\n"); } + +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile) +{ + _noHeifError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data) +{ + _noHeifError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtx *infile) +{ + _noHeifError(); + return NULL; +} + +BGD_DECLARE(void) +gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + _noHeifError(); +} + +BGD_DECLARE(void) +gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + _noHeifError(); +} + +BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile) { _noHeifError(); } + +BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size) +{ + _noHeifError(); + return NULL; +} + +BGD_DECLARE(void *) +gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma) +{ + _noHeifError(); + return NULL; +} + +#endif /* HAVE_LIBHEIF */ diff --git a/ext/gd/libgd/gd_intern.h b/ext/gd/libgd/gd_intern.h index c7098d21b837..2a4b874c5ac3 100644 --- a/ext/gd/libgd/gd_intern.h +++ b/ext/gd/libgd/gd_intern.h @@ -1,20 +1,195 @@ +/* Internal header for random common utility functions. */ + #ifndef GD_INTERN_H #define GD_INTERN_H + +#include "gd.h" +#include +#ifdef _MSC_VER +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX _I64_MAX +#else +#define SSIZE_MAX INT_MAX +#endif +#endif +#endif +#if defined(_MSC_VER) +#define UNUSED_PARAM(x) x +#elif defined(__GNUC__) || defined(__clang__) +#define UNUSED_PARAM(x) x __attribute__((unused)) +#else +#define UNUSED_PARAM(x) x +#endif #ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif -#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c))) +#define MIN3(a, b, c) ((a) < (b) ? (MIN(a, c)) : (MIN(b, c))) #ifndef MAX -#define MAX(a,b) ((a)<(b)?(b):(a)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif +#define MAX3(a, b, c) ((a) < (b) ? (MAX(b, c)) : (MAX(a, c))) +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +#ifdef _MSC_VER +#define gd_strcasecmp _stricmp +#else +#define gd_strcasecmp strcasecmp #endif -#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c))) -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +typedef enum { + HORIZONTAL, + VERTICAL, +} gdAxis; + +/* Convert a double to an unsigned char, rounding to the nearest + * integer and clamping the result between 0 and max. The absolute + * value of clr must be less than the maximum value of an unsigned + * short. */ +static inline unsigned char uchar_clamp(double clr, unsigned char max) +{ + unsigned short result; + + // assert(fabs(clr) <= SHRT_MAX); + + /* Casting a negative float to an unsigned short is undefined. + * However, casting a float to a signed truncates toward zero and + * casting a negative signed value to an unsigned of the same size + * results in a bit-identical value (assuming twos-complement + * arithmetic). This is what we want: all legal negative values + * for clr will be greater than 255. */ + + /* Convert and clamp. */ + result = (unsigned short)(short)(clr + 0.5); + if (result > max) { + result = (clr < 0) ? 0 : max; + } /* if */ + + return result; +} /* uchar_clamp*/ /* Internal prototypes: */ +/* gd_jpeg.c */ +void *gdImageJpegPtrWithMetadataNoSubsampling(gdImagePtr im, int *size, int quality, + const gdImageMetadata *metadata); + /* gd_rotate.c */ gdImagePtr gdImageRotate90(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate180(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate270(gdImagePtr src, int ignoretransparent); -#endif +/** + * Clip a copy operation to the destination image bounds. + * Adjusts srcX/srcY to match any clipping applied to dstX/dstY. + * Returns 0 if the operation is entirely outside dst (nothing to do), + * 1 if there is work to do. + */ +static inline int gdImageClipCopy(gdImagePtr dst, int *dstX, int *dstY, int *srcX, int *srcY, + int *w, int *h) +{ + int x1, y1, x2, y2; + + /* overflow-safe dst rect: [dstX, dstY] to [dstX+w, dstY+h] */ + x1 = *dstX; + y1 = *dstY; + + /* check w/h are positive */ + if (*w <= 0 || *h <= 0) { + return 0; + } + + /* overflow check for dstX+w and dstY+h */ + if (*dstX > 0 && *w > INT_MAX - *dstX) { + x2 = INT_MAX; + } else { + x2 = *dstX + *w; + } + if (*dstY > 0 && *h > INT_MAX - *dstY) { + y2 = INT_MAX; + } else { + y2 = *dstY + *h; + } + + /* entirely outside dst? */ + if (x1 >= gdImageSX(dst) || y1 >= gdImageSY(dst) || x2 <= 0 || y2 <= 0) { + return 0; + } + + /* clip left */ + if (x1 < 0) { + *srcX -= x1; /* advance srcX by the same amount */ + *w += x1; /* reduce width */ + *dstX = 0; + } + + /* clip top */ + if (y1 < 0) { + *srcY -= y1; + *h += y1; + *dstY = 0; + } + + /* clip right */ + if (*dstX + *w > gdImageSX(dst)) { + *w = gdImageSX(dst) - *dstX; + } + + /* clip bottom */ + if (*dstY + *h > gdImageSY(dst)) { + *h = gdImageSY(dst) - *dstY; + } + + /* sanity: clipping may have reduced w/h to zero */ + if (*w <= 0 || *h <= 0) { + return 0; + } + + return 1; +} +static inline int gdImageClipCopyResized(gdImagePtr dst, int *dstX, int *dstY, int *dstW, int *dstH, + int *srcX, int *srcY, int *srcW, int *srcH) +{ + int orig_dstW = *dstW; + int orig_dstH = *dstH; + + if (*dstW <= 0 || *dstH <= 0 || *srcW <= 0 || *srcH <= 0) { + return 0; + } + if (*dstX >= gdImageSX(dst) || *dstY >= gdImageSY(dst) || *dstX + *dstW <= 0 || + *dstY + *dstH <= 0) { + return 0; + } + + /* clip left — adjust srcX proportionally */ + if (*dstX < 0) { + *srcX += (-*dstX) * *srcW / orig_dstW; + *srcW -= (-*dstX) * *srcW / orig_dstW; + *dstW += *dstX; + *dstX = 0; + } + /* clip top */ + if (*dstY < 0) { + *srcY += (-*dstY) * *srcH / orig_dstH; + *srcH -= (-*dstY) * *srcH / orig_dstH; + *dstH += *dstY; + *dstY = 0; + } + /* clip right */ + if (*dstX + *dstW > gdImageSX(dst)) { + int clip = *dstX + *dstW - gdImageSX(dst); + *srcW -= clip * *srcW / orig_dstW; + *dstW = gdImageSX(dst) - *dstX; + } + /* clip bottom */ + if (*dstY + *dstH > gdImageSY(dst)) { + int clip = *dstY + *dstH - gdImageSY(dst); + *srcH -= clip * *srcH / orig_dstH; + *dstH = gdImageSY(dst) - *dstY; + } + + if (*dstW <= 0 || *dstH <= 0) + return 0; + return 1; +} +#endif /* GD_INTERN_H */ diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c index 7731d47a0c7c..cdc62f09b520 100644 --- a/ext/gd/libgd/gd_interpolation.c +++ b/ext/gd/libgd/gd_interpolation.c @@ -18,76 +18,77 @@ */ /* - Summary: + Summary: - - Horizontal filter contributions are calculated on the fly, - as each column is mapped from src to dst image. This lets - us omit having to allocate a temporary full horizontal stretch - of the src image. + - Horizontal filter contributions are calculated on the fly, + as each column is mapped from src to dst image. This lets + us omit having to allocate a temporary full horizontal stretch + of the src image. - - If none of the src pixels within a sampling region differ, - then the output pixel is forced to equal (any of) the source pixel. - This ensures that filters do not corrupt areas of constant color. - - - Filter weight contribution results, after summing, are - rounded to the nearest pixel color value instead of - being casted to ILubyte (usually an int or char). Otherwise, - artifacting occurs. + - If none of the src pixels within a sampling region differ, + then the output pixel is forced to equal (any of) the source pixel. + This ensures that filters do not corrupt areas of constant color. + - Filter weight contribution results, after summing, are + rounded to the nearest pixel color value instead of + being casted to ILubyte (usually an int or char). Otherwise, + artifacting occurs. */ /* - Additional functions are available for simple rotation or up/downscaling. - downscaling using the fixed point implementations are usually much faster - than the existing gdImageCopyResampled while having a similar or better - quality. - - For image rotations, the optimized versions have a lazy antialiasing for - the edges of the images. For a much better antialiased result, the affine - function is recommended. + Additional functions are available for simple rotation or up/downscaling. + downscaling using the fixed point implementations are usually much faster + than the existing gdImageCopyResampled while having a similar or better + quality. + + For image rotations, the optimized versions have a lazy antialiasing for + the edges of the images. For a much better antialiased result, the affine + function is recommended. */ /* TODO: - Optimize pixel accesses and loops once we have continuous buffer - - Add scale support for a portion only of an image (equivalent of copyresized/resampled) + - Add scale support for a portion only of an image (equivalent of +copyresized/resampled) */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include #include #include #include -#include -#include + +#undef NDEBUG +/* Comment out this line to enable asserts. + * TODO: This logic really belongs in cmake and configure. + */ +/* #define NDEBUG 1 */ +#include #include "gd.h" -#include "gdhelpers.h" #include "gd_intern.h" +#include "gdhelpers.h" -static gdImagePtr gdImageScaleBilinear(gdImagePtr im, - const unsigned int new_width, +static gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height); -static gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, - const unsigned int width, +static gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height); -static gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, - const unsigned int width, const unsigned int height); -static gdImagePtr gdImageScaleTwoPass(const gdImagePtr pOrigImage, - const unsigned int uOrigWidth, - const unsigned int uOrigHeight, - const unsigned int uNewWidth, - const unsigned int uNewHeight); -static gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, - const float degrees, +static gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, + const unsigned int height); +static gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor); -static gdImagePtr gdImageRotateGeneric(gdImagePtr src, - const float degrees, - const int bgColor); +static gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor); -/* only used here, let do a generic fixed point integers later if required by other - part of GD */ +#ifdef FUNCTION_NOT_USED_YET +/* only used by the inactive edge helper below */ typedef long gdFixed; /* Integer to fixed point */ -#define gd_itofx(x) (long)((unsigned long)(x) << 8) +#define gd_itofx(x) ((x) << 8) /* Float to fixed point */ #define gd_ftofx(x) (long)((x) * 256) @@ -99,255 +100,174 @@ typedef long gdFixed; #define gd_fxtoi(x) ((x) >> 8) /* Fixed point to float */ -# define gd_fxtof(x) ((float)(x) / 256) +#define gd_fxtof(x) ((float)(x) / 256) /* Fixed point to double */ #define gd_fxtod(x) ((double)(x) / 256) /* Multiply a fixed by a fixed */ -#define gd_mulfx(x,y) (((x) * (y)) >> 8) +#define gd_mulfx(x, y) (((x) * (y)) >> 8) /* Divide a fixed by a fixed */ -#define gd_divfx(x,y) ((long)((unsigned long)(x) << 8) / (y)) +#define gd_divfx(x, y) (((x) << 8) / (y)) +#endif -typedef struct -{ - double *Weights; /* Normalized weights of neighboring pixels */ - int Left,Right; /* Bounds of source pixels window */ +typedef struct _FilterInfo { + double (*function)(const double, const double), support; +} FilterInfo; + +typedef struct { + double *Weights; /* Normalized weights of neighboring pixels */ + int Left, Right; /* Bounds of source pixels window */ } ContributionType; /* Contirbution information for a single pixel */ -typedef struct -{ - ContributionType *ContribRow; /* Row (or column) of contribution weights */ - unsigned int WindowSize, /* Filter window size (of affecting source pixels) */ - LineLength; /* Length of line (no. or rows / cols) */ +typedef struct { + ContributionType *ContribRow; /* Row (or column) of contribution weights */ + unsigned int WindowSize, /* Filter window size (of affecting source pixels) */ + LineLength; /* Length of line (no. or rows / cols) */ } LineContribType; -/* Each core filter has its own radius */ -#define DEFAULT_FILTER_LINEAR 1.0f -#define DEFAULT_FILTER_BICUBIC 3.0f -#define DEFAULT_FILTER_BOX 0.5f -#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5f -#define DEFAULT_FILTER_RADIUS 1.0f -#define DEFAULT_LANCZOS8_RADIUS 8.0f -#define DEFAULT_LANCZOS3_RADIUS 3.0f -#define DEFAULT_HERMITE_RADIUS 1.0f -#define DEFAULT_BOX_RADIUS 0.5f -#define DEFAULT_TRIANGLE_RADIUS 1.0f -#define DEFAULT_BELL_RADIUS 1.5f -#define DEFAULT_CUBICSPLINE_RADIUS 2.0f -#define DEFAULT_MITCHELL_RADIUS 2.0f -#define DEFAULT_COSINE_RADIUS 1.0f -#define DEFAULT_CATMULLROM_RADIUS 2.0f -#define DEFAULT_QUADRATIC_RADIUS 1.5f -#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5f -#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0f -#define DEFAULT_GAUSSIAN_RADIUS 1.0f -#define DEFAULT_HANNING_RADIUS 1.0f -#define DEFAULT_HAMMING_RADIUS 1.0f -#define DEFAULT_SINC_RADIUS 1.0f -#define DEFAULT_WELSH_RADIUS 1.0f - -enum GD_RESIZE_FILTER_TYPE{ - FILTER_DEFAULT = 0, - FILTER_BELL, - FILTER_BESSEL, - FILTER_BLACKMAN, - FILTER_BOX, - FILTER_BSPLINE, - FILTER_CATMULLROM, - FILTER_COSINE, - FILTER_CUBICCONVOLUTION, - FILTER_CUBICSPLINE, - FILTER_HERMITE, - FILTER_LANCZOS3, - FILTER_LANCZOS8, - FILTER_MITCHELL, - FILTER_QUADRATIC, - FILTER_QUADRATICBSPLINE, - FILTER_TRIANGLE, - FILTER_GAUSSIAN, - FILTER_HANNING, - FILTER_HAMMING, - FILTER_SINC, - FILTER_WELSH, - - FILTER_CALLBACK = 999 -}; - -typedef enum GD_RESIZE_FILTER_TYPE gdResizeFilterType; - static double KernelBessel_J1(const double x) { - double p, q; - - register long i; - - static const double - Pone[] = - { - 0.581199354001606143928050809e+21, - -0.6672106568924916298020941484e+20, - 0.2316433580634002297931815435e+19, - -0.3588817569910106050743641413e+17, - 0.2908795263834775409737601689e+15, - -0.1322983480332126453125473247e+13, - 0.3413234182301700539091292655e+10, - -0.4695753530642995859767162166e+7, - 0.270112271089232341485679099e+4 - }, - Qone[] = - { - 0.11623987080032122878585294e+22, - 0.1185770712190320999837113348e+20, - 0.6092061398917521746105196863e+17, - 0.2081661221307607351240184229e+15, - 0.5243710262167649715406728642e+12, - 0.1013863514358673989967045588e+10, - 0.1501793594998585505921097578e+7, - 0.1606931573481487801970916749e+4, - 0.1e+1 - }; - - p = Pone[8]; - q = Qone[8]; - for (i=7; i >= 0; i--) - { - p = p*x*x+Pone[i]; - q = q*x*x+Qone[i]; - } - return (double)(p/q); + double p, q; + + register long i; + + static const double Pone[] = {0.581199354001606143928050809e+21, + -0.6672106568924916298020941484e+20, + 0.2316433580634002297931815435e+19, + -0.3588817569910106050743641413e+17, + 0.2908795263834775409737601689e+15, + -0.1322983480332126453125473247e+13, + 0.3413234182301700539091292655e+10, + -0.4695753530642995859767162166e+7, + 0.270112271089232341485679099e+4}, + Qone[] = {0.11623987080032122878585294e+22, + 0.1185770712190320999837113348e+20, + 0.6092061398917521746105196863e+17, + 0.2081661221307607351240184229e+15, + 0.5243710262167649715406728642e+12, + 0.1013863514358673989967045588e+10, + 0.1501793594998585505921097578e+7, + 0.1606931573481487801970916749e+4, + 0.1e+1}; + + p = Pone[8]; + q = Qone[8]; + for (i = 7; i >= 0; i--) { + p = p * x * x + Pone[i]; + q = q * x * x + Qone[i]; + } + return (double)(p / q); } static double KernelBessel_P1(const double x) { - double p, q; - - register long i; - - static const double - Pone[] = - { - 0.352246649133679798341724373e+5, - 0.62758845247161281269005675e+5, - 0.313539631109159574238669888e+5, - 0.49854832060594338434500455e+4, - 0.2111529182853962382105718e+3, - 0.12571716929145341558495e+1 - }, - Qone[] = - { - 0.352246649133679798068390431e+5, - 0.626943469593560511888833731e+5, - 0.312404063819041039923015703e+5, - 0.4930396490181088979386097e+4, - 0.2030775189134759322293574e+3, - 0.1e+1 - }; - - p = Pone[5]; - q = Qone[5]; - for (i=4; i >= 0; i--) - { - p = p*(8.0/x)*(8.0/x)+Pone[i]; - q = q*(8.0/x)*(8.0/x)+Qone[i]; - } - return (double)(p/q); + double p, q; + + register long i; + + static const double Pone[] = {0.352246649133679798341724373e+5, 0.62758845247161281269005675e+5, + 0.313539631109159574238669888e+5, 0.49854832060594338434500455e+4, + 0.2111529182853962382105718e+3, 0.12571716929145341558495e+1}, + Qone[] = { + 0.352246649133679798068390431e+5, 0.626943469593560511888833731e+5, + 0.312404063819041039923015703e+5, 0.4930396490181088979386097e+4, + 0.2030775189134759322293574e+3, 0.1e+1}; + + p = Pone[5]; + q = Qone[5]; + for (i = 4; i >= 0; i--) { + p = p * (8.0 / x) * (8.0 / x) + Pone[i]; + q = q * (8.0 / x) * (8.0 / x) + Qone[i]; + } + return (double)(p / q); } static double KernelBessel_Q1(const double x) { - double p, q; - - register long i; - - static const double - Pone[] = - { - 0.3511751914303552822533318e+3, - 0.7210391804904475039280863e+3, - 0.4259873011654442389886993e+3, - 0.831898957673850827325226e+2, - 0.45681716295512267064405e+1, - 0.3532840052740123642735e-1 - }, - Qone[] = - { - 0.74917374171809127714519505e+4, - 0.154141773392650970499848051e+5, - 0.91522317015169922705904727e+4, - 0.18111867005523513506724158e+4, - 0.1038187585462133728776636e+3, - 0.1e+1 - }; - - p = Pone[5]; - q = Qone[5]; - for (i=4; i >= 0; i--) - { - p = p*(8.0/x)*(8.0/x)+Pone[i]; - q = q*(8.0/x)*(8.0/x)+Qone[i]; - } - return (double)(p/q); + double p, q; + + register long i; + + static const double Pone[] = {0.3511751914303552822533318e+3, 0.7210391804904475039280863e+3, + 0.4259873011654442389886993e+3, 0.831898957673850827325226e+2, + 0.45681716295512267064405e+1, 0.3532840052740123642735e-1}, + Qone[] = {0.74917374171809127714519505e+4, 0.154141773392650970499848051e+5, + 0.91522317015169922705904727e+4, 0.18111867005523513506724158e+4, + 0.1038187585462133728776636e+3, 0.1e+1}; + + p = Pone[5]; + q = Qone[5]; + for (i = 4; i >= 0; i--) { + p = p * (8.0 / x) * (8.0 / x) + Pone[i]; + q = q * (8.0 / x) * (8.0 / x) + Qone[i]; + } + return (double)(p / q); } static double KernelBessel_Order1(double x) { - double p, q; - - if (x == 0.0) - return (0.0f); - p = x; - if (x < 0.0) - x=(-x); - if (x < 8.0) - return (p*KernelBessel_J1(x)); - q = (double)sqrt(2.0f/(M_PI*x))*(double)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)* - (-1.0f/sqrt(2.0f)*(sin(x)+cos(x)))); - if (p < 0.0f) - q = (-q); - return (q); + double p, q; + + if (x == 0.0) + return (0.0f); + p = x; + if (x < 0.0) + x = (-x); + if (x < 8.0) + return (p * KernelBessel_J1(x)); + q = (double)sqrt(2.0f / (M_PI * x)) * + (double)(KernelBessel_P1(x) * (1.0f / sqrt(2.0f) * (sin(x) - cos(x))) - + 8.0f / x * KernelBessel_Q1(x) * (-1.0f / sqrt(2.0f) * (sin(x) + cos(x)))); + if (p < 0.0f) + q = (-q); + return (q); } -static double filter_bessel(const double x) +static double filter_sinc(const double x, const double support) { - if (x == 0.0f) - return (double)(M_PI/4.0f); - return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x)); + ARG_NOT_USED(support); + /* X-scaled Sinc(x) function. */ + if (x == 0.0) + return (1.0); + return (sin(M_PI * (double)x) / (M_PI * (double)x)); } +static double filter_bessel(const double x, const double support) +{ + ARG_NOT_USED(support); + if (x == 0.0f) + return (double)(M_PI / 4.0f); + return (KernelBessel_Order1((double)M_PI * x) / (2.0f * x)); +} -static double filter_blackman(const double x) +static double filter_blackman(const double x, const double support) { - return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x)); + ARG_NOT_USED(support); + return (0.42f + 0.5f * (double)cos(M_PI * x) + 0.08f * (double)cos(2.0f * M_PI * x)); } -double filter_linear(const double x) { - double ax = fabs(x); - if (ax < 1.0f) { - return (1.0f - ax); - } - return 0.0f; +static double filter_linear(const double x, const double support) +{ + ARG_NOT_USED(support); + double ax = fabs(x); + if (ax < 1.0f) { + return (1.0f - ax); + } + return 0.0f; } -/** - * Bicubic interpolation kernel (a=-1): - \verbatim - / - | 1-2|t|**2+|t|**3 , if |t| < 1 - h(t) = | 4-8|t|+5|t|**2-|t|**3 , if 1<=|t|<2 - | 0 , otherwise - \ - \endverbatim - * ***bd*** 2.2004 - */ -static double filter_bicubic(const double t) +static double filter_blackman_bessel(const double x, const double support) { - const double abs_t = (double)fabs(t); - const double abs_t_sq = abs_t * abs_t; - if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t; - if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t; - return 0; + ARG_NOT_USED(support); + return (filter_blackman(x / support, support) * filter_bessel(x, support)); +} + +static double filter_blackman_sinc(const double x, const double support) +{ + ARG_NOT_USED(support); + return (filter_blackman(x / support, support) * filter_sinc(x, support)); } /** @@ -361,386 +281,460 @@ static double filter_bicubic(const double t) \endverbatim * Often used values for a are -1 and -1/2. */ -static double filter_generalized_cubic(const double t) -{ - const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC; - double abs_t = (double)fabs(t); - double abs_t_sq = abs_t * abs_t; - if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1; - if (abs_t < 2) return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a; - return 0; +static double filter_generalized_cubic(const double t, const double support) +{ + const double a = -support; + double abs_t = (double)fabs(t); + double abs_t_sq = abs_t * abs_t; + if (abs_t < 1) + return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1; + if (abs_t < 2) + return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a; + return 0; } -#ifdef FUNCTION_NOT_USED_YET /* CubicSpline filter, default radius 2 */ -static double filter_cubic_spline(const double x1) +static double filter_cubic_spline(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + const double x = x1 < 0.0 ? -x1 : x1; - if (x < 1.0 ) { - const double x2 = x*x; + if (x < 1.0) { + const double x2 = x * x; - return (0.5 * x2 * x - x2 + 2.0 / 3.0); - } - if (x < 2.0) { - return (pow(2.0 - x, 3.0)/6.0); - } - return 0; + return (0.5 * x2 * x - x2 + 2.0 / 3.0); + } + if (x < 2.0) { + return (pow(2.0 - x, 3.0) / 6.0); + } + return 0; } -#endif #ifdef FUNCTION_NOT_USED_YET /* CubicConvolution filter, default radius 3 */ -static double filter_cubic_convolution(const double x1) +static double filter_cubic_convolution(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; - const double x2 = x1 * x1; - const double x2_x = x2 * x; - - if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0); - if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5); - if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5); - return 0; + const double x = x1 < 0.0 ? -x1 : x1; + const double x2 = x1 * x1; + const double x2_x = x2 * x; + ARG_NOT_USED(support); + if (x <= 1.0) + return ((4.0 / 3.0) * x2_x - (7.0 / 3.0) * x2 + 1.0); + if (x <= 2.0) + return (-(7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5); + if (x <= 3.0) + return ((1.0 / 12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5); + return 0; } #endif -static double filter_box(double x) { - if (x < - DEFAULT_FILTER_BOX) - return 0.0f; - if (x < DEFAULT_FILTER_BOX) - return 1.0f; - return 0.0f; -} - -static double filter_catmullrom(const double x) -{ - if (x < -2.0) - return(0.0f); - if (x < -1.0) - return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x)))); - if (x < 0.0) - return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x))); - if (x < 1.0) - return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x))); - if (x < 2.0) - return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x)))); - return(0.0f); +static double filter_box(double x, const double support) +{ + if (x < -support) + return 0.0f; + if (x < support) + return 1.0f; + return 0.0f; } -#ifdef FUNCTION_NOT_USED_YET -static double filter_filter(double t) +static double filter_catmullrom(const double x, const double support) { - /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ - if(t < 0.0) t = -t; - if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); - return(0.0); + ARG_NOT_USED(support); + if (x < -2.0) + return (0.0f); + if (x < -1.0) + return (0.5f * (4.0f + x * (8.0f + x * (5.0f + x)))); + if (x < 0.0) + return (0.5f * (2.0f + x * x * (-5.0f - 3.0f * x))); + if (x < 1.0) + return (0.5f * (2.0f + x * x * (-5.0f + 3.0f * x))); + if (x < 2.0) + return (0.5f * (4.0f + x * (-8.0f + x * (5.0f - x)))); + return (0.0f); } -#endif -#ifdef FUNCTION_NOT_USED_YET /* Lanczos8 filter, default radius 8 */ -static double filter_lanczos8(const double x1) +static double filter_lanczos8(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; -#define R DEFAULT_LANCZOS8_RADIUS + const double x = x1 < 0.0 ? -x1 : x1; - if ( x == 0.0) return 1; + if (x == 0.0) + return 1; - if ( x < R) { - return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI); - } - return 0.0; -#undef R + if (x < support) { + return support * sin(x * M_PI) * sin(x * M_PI / support) / (x * M_PI * x * M_PI); + } + return 0.0; } -#endif -#ifdef FUNCTION_NOT_USED_YET -/* Lanczos3 filter, default radius 3 */ -static double filter_lanczos3(const double x1) +static double filter_lanczos3(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; -#define R DEFAULT_LANCZOS3_RADIUS - - if ( x == 0.0) return 1; - - if ( x < R) - { - return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI); - } - return 0.0; -#undef R + if (x1 < -3.0) + return (0.0); + if (x1 < 0.0) + return (filter_sinc(-x1, support) * filter_sinc(-x1 / 3.0, support)); + if (x1 < 3.0) + return (filter_sinc(x1, support) * filter_sinc(x1 / 3.0, support)); + return (0.0); } -#endif /* Hermite filter, default radius 1 */ -static double filter_hermite(const double x1) +static double filter_hermite(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; + const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); - if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 ); + if (x < 1.0) + return ((2.0 * x - 3) * x * x + 1.0); - return 0.0; + return 0.0; } /* Trangle filter, default radius 1 */ -static double filter_triangle(const double x1) +static double filter_triangle(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; - if (x < 1.0) return (1.0 - x); - return 0.0; + const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + + if (x < 1.0) + return (1.0 - x); + return 0.0; } /* Bell filter, default radius 1.5 */ -static double filter_bell(const double x1) +static double filter_bell(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; - - if (x < 0.5) return (0.75 - x*x); - if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0)); - return 0.0; + const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + + if (x < 0.5) + return (0.75 - x * x); + if (x < 1.5) + return (0.5 * pow(x - 1.5, 2.0)); + return 0.0; } /* Mitchell filter, default radius 2.0 */ -static double filter_mitchell(const double x) +static double filter_mitchell(const double x, const double support) { -#define KM_B (1.0f/3.0f) -#define KM_C (1.0f/3.0f) -#define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f) + ARG_NOT_USED(support); +#define KM_B (1.0f / 3.0f) +#define KM_C (1.0f / 3.0f) +#define KM_P0 ((6.0f - 2.0f * KM_B) / 6.0f) #define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f) -#define KM_P3 (( 12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f) -#define KM_Q0 (( 8.0f * KM_B + 24.0f * KM_C) / 6.0f) +#define KM_P3 ((12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f) +#define KM_Q0 ((8.0f * KM_B + 24.0f * KM_C) / 6.0f) #define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f) -#define KM_Q2 (( 6.0f * KM_B + 30.0f * KM_C) / 6.0f) -#define KM_Q3 (( -1.0f * KM_B - 6.0f * KM_C) / 6.0f) - - if (x < -2.0) - return(0.0f); - if (x < -1.0) - return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3))); - if (x < 0.0f) - return(KM_P0+x*x*(KM_P2-x*KM_P3)); - if (x < 1.0f) - return(KM_P0+x*x*(KM_P2+x*KM_P3)); - if (x < 2.0f) - return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3))); - return(0.0f); +#define KM_Q2 ((6.0f * KM_B + 30.0f * KM_C) / 6.0f) +#define KM_Q3 ((-1.0f * KM_B - 6.0f * KM_C) / 6.0f) + + if (x < -2.0) + return (0.0f); + if (x < -1.0) + return (KM_Q0 - x * (KM_Q1 - x * (KM_Q2 - x * KM_Q3))); + if (x < 0.0f) + return (KM_P0 + x * x * (KM_P2 - x * KM_P3)); + if (x < 1.0f) + return (KM_P0 + x * x * (KM_P2 + x * KM_P3)); + if (x < 2.0f) + return (KM_Q0 + x * (KM_Q1 + x * (KM_Q2 + x * KM_Q3))); + + return (0.0f); } - - -#ifdef FUNCTION_NOT_USED_YET /* Cosine filter, default radius 1 */ -static double filter_cosine(const double x) +static double filter_cosine(const double x, const double support) { - if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0); + ARG_NOT_USED(support); + if ((x >= -1.0) && (x <= 1.0)) + return ((cos(x * M_PI) + 1.0) / 2.0); - return 0; + return 0; } -#endif /* Quadratic filter, default radius 1.5 */ -static double filter_quadratic(const double x1) +static double filter_quadratic(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; - - if (x <= 0.5) return (- 2.0 * x * x + 1); - if (x <= 1.5) return (x * x - 2.5* x + 1.5); - return 0.0; + const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + if (x <= 0.5) + return (-2.0 * x * x + 1); + if (x <= 1.5) + return (x * x - 2.5 * x + 1.5); + return 0.0; } -static double filter_bspline(const double x) +static double filter_bspline(const double x, const double support) { - if (x>2.0f) { - return 0.0f; - } else { - double a, b, c, d; - /* Was calculated anyway cause the "if((x-1.0f) < 0)" */ - const double xm1 = x - 1.0f; - const double xp1 = x + 1.0f; - const double xp2 = x + 2.0f; - - if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2; - if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1; - if (x <= 0) c = 0.0f; else c = x*x*x; - if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1; - - return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d))); - } + ARG_NOT_USED(support); + if (x > 2.0f) { + return 0.0f; + } else { + double a, b, c, d; + /* Was calculated anyway cause the "if((x-1.0f) < 0)" */ + const double xm1 = x - 1.0f; + const double xp1 = x + 1.0f; + const double xp2 = x + 2.0f; + + if ((xp2) <= 0.0f) + a = 0.0f; + else + a = xp2 * xp2 * xp2; + if ((xp1) <= 0.0f) + b = 0.0f; + else + b = xp1 * xp1 * xp1; + if (x <= 0) + c = 0.0f; + else + c = x * x * x; + if ((xm1) <= 0.0f) + d = 0.0f; + else + d = xm1 * xm1 * xm1; + + return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d))); + } } -#ifdef FUNCTION_NOT_USED_YET /* QuadraticBSpline filter, default radius 1.5 */ -static double filter_quadratic_bspline(const double x1) +static double filter_quadratic_bspline(const double x1, const double support) { - const double x = x1 < 0.0 ? -x1 : x1; - - if (x <= 0.5) return (- x * x + 0.75); - if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125); - return 0.0; + const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + if (x <= 0.5) + return (-x * x + 0.75); + if (x <= 1.5) + return (0.5 * x * x - 1.5 * x + 1.125); + return 0.0; } -#endif -static double filter_gaussian(const double x) +static double filter_gaussian(const double x, const double support) { - /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */ - return (double)(exp(-2.0f * x * x) * 0.79788456080287f); + ARG_NOT_USED(support); + /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */ + return (double)(exp(-2.0f * x * x) * 0.79788456080287f); } -static double filter_hanning(const double x) +static double filter_hanning(const double x, const double support) { - /* A Cosine windowing function */ - return(0.5 + 0.5 * cos(M_PI * x)); + ARG_NOT_USED(support); + /* A Cosine windowing function */ + return (0.5 + 0.5 * cos(M_PI * x)); } -static double filter_hamming(const double x) -{ - /* should be - (0.54+0.46*cos(M_PI*(double) x)); - but this approximation is sufficient */ - if (x < -1.0f) - return 0.0f; - if (x < 0.0f) - return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f; - if (x < 1.0f) - return 0.92f*(2.0f*x-3.0f)*x*x+1.0f; - return 0.0f; +static double filter_hamming(const double x, const double support) +{ + ARG_NOT_USED(support); + /* should be + (0.54+0.46*cos(M_PI*(double) x)); + but this approximation is sufficient */ + if (x < -1.0f) + return 0.0f; + if (x < 0.0f) + return 0.92f * (-2.0f * x - 3.0f) * x * x + 1.0f; + if (x < 1.0f) + return 0.92f * (2.0f * x - 3.0f) * x * x + 1.0f; + return 0.0f; } -static double filter_power(const double x) +static double filter_power(const double x, const double support) { - const double a = 2.0f; - if (fabs(x)>1) return 0.0f; - return (1.0f - (double)fabs(pow(x,a))); + ARG_NOT_USED(support); + const double a = 2.0f; + if (fabs(x) > 1) + return 0.0f; + return (1.0f - (double)fabs(pow(x, a))); } -static double filter_sinc(const double x) +static double filter_welsh(const double x, const double support) { - /* X-scaled Sinc(x) function. */ - if (x == 0.0) return(1.0); - return (sin(M_PI * (double) x) / (M_PI * (double) x)); + ARG_NOT_USED(support); + /* Welsh parabolic windowing filter */ + if (x < 1.0) + return (1 - x * x); + return (0.0); } +#if defined(_MSC_VER) && !defined(inline) +#define inline __inline +#endif + +/* keep it for future usage for affine copy over an existing image, targetting + * fix for 2.2.2 */ #ifdef FUNCTION_NOT_USED_YET -static double filter_welsh(const double x) +/* Copied from upstream's libgd */ +static inline int _color_blend(const int dst, const int src) { - /* Welsh parabolic windowing filter */ - if (x < 1.0) - return(1 - x*x); - return(0.0); + const int src_alpha = gdTrueColorGetAlpha(src); + + if (src_alpha == gdAlphaOpaque) { + return src; + } else { + const int dst_alpha = gdTrueColorGetAlpha(dst); + + if (src_alpha == gdAlphaTransparent) + return dst; + if (dst_alpha == gdAlphaTransparent) { + return src; + } else { + register int alpha, red, green, blue; + const int src_weight = gdAlphaTransparent - src_alpha; + const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax; + const int tot_weight = src_weight + dst_weight; + + alpha = src_alpha * dst_alpha / gdAlphaMax; + + red = (gdTrueColorGetRed(src) * src_weight + gdTrueColorGetRed(dst) * dst_weight) / + tot_weight; + green = + (gdTrueColorGetGreen(src) * src_weight + gdTrueColorGetGreen(dst) * dst_weight) / + tot_weight; + blue = (gdTrueColorGetBlue(src) * src_weight + gdTrueColorGetBlue(dst) * dst_weight) / + tot_weight; + + return ((alpha << 24) + (red << 16) + (green << 8) + blue); + } + } +} + +static inline int _setEdgePixel(const gdImagePtr src, unsigned int x, unsigned int y, + gdFixed coverage, const int bgColor) +{ + const gdFixed f_127 = gd_itofx(127); + register int c = src->tpixels[y][x]; + c = c | (((int)(gd_fxtof(gd_mulfx(coverage, f_127)) + 50.5f)) << 24); + return _color_blend(bgColor, c); } #endif +static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, + const int bgColor /* 31bit ARGB TC */) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->tpixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return c; /* 31bit ARGB TC */ + } else { + return bgColor; /* 31bit ARGB TC */ + } +} -/* keep it for future usage for affine copy over an existing image, targetting fix for 2.2.2 */ -#if 0 -/* Copied from upstream's libgd */ -static inline int _color_blend (const int dst, const int src) +static inline int getPixelOverflowTCClipped(gdImagePtr im, const int x, const int y, + const int bgColor, const gdRectPtr clip) { - const int src_alpha = gdTrueColorGetAlpha(src); + if (clip != NULL && + (x < clip->x || y < clip->y || x >= clip->x + clip->width || y >= clip->y + clip->height)) { + return bgColor; + } + return getPixelOverflowTC(im, x, y, bgColor); +} - if( src_alpha == gdAlphaOpaque ) { - return src; - } else { - const int dst_alpha = gdTrueColorGetAlpha(dst); - - if( src_alpha == gdAlphaTransparent ) return dst; - if( dst_alpha == gdAlphaTransparent ) { - return src; - } else { - register int alpha, red, green, blue; - const int src_weight = gdAlphaTransparent - src_alpha; - const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax; - const int tot_weight = src_weight + dst_weight; - - alpha = src_alpha * dst_alpha / gdAlphaMax; - - red = (gdTrueColorGetRed(src) * src_weight - + gdTrueColorGetRed(dst) * dst_weight) / tot_weight; - green = (gdTrueColorGetGreen(src) * src_weight - + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight; - blue = (gdTrueColorGetBlue(src) * src_weight - + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight; - - return ((alpha << 24) + (red << 16) + (green << 8) + blue); - } - } +#define colorIndex2RGBA(c) \ + gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)]) +#define colorIndex2RGBcustomA(c, a) \ + gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)]) +static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, + const int bgColor /* 31bit ARGB TC */) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->pixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return colorIndex2RGBA(c); + } else { + return bgColor; /* 31bit ARGB TC */ + } +} + +static inline int getPixelOverflowPaletteClipped(gdImagePtr im, const int x, const int y, + const int bgColor, const gdRectPtr clip) +{ + if (clip != NULL && + (x < clip->x || y < clip->y || x >= clip->x + clip->width || y >= clip->y + clip->height)) { + return bgColor; + } + return getPixelOverflowPalette(im, x, y, bgColor); } -#endif -static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, const int bgColor) -{ - if (gdImageBoundsSafe(im, x, y)) { - const int c = im->tpixels[y][x]; - if (c == im->transparent) { - return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; - } - return c; - } else { - return bgColor; - } +static inline int gdClampInt(const int value, const int min, const int max) +{ + return value < min ? min : (value > max ? max : value); } -#define colorIndex2RGBA(c) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)]) -#define colorIndex2RGBcustomA(c, a) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)]) -static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, const int bgColor) -{ - if (gdImageBoundsSafe(im, x, y)) { - const int c = im->pixels[y][x]; - if (c == im->transparent) { - return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; - } - return colorIndex2RGBA(c); - } else { - return bgColor; - } +static inline interpolation_method gdImageGetEffectiveInterpolation(const gdImagePtr im) +{ + switch (im->interpolation_id) { + case GD_DEFAULT: + case GD_BILINEAR_FIXED: + case GD_LINEAR: + return filter_linear; + + case GD_BICUBIC: + case GD_BICUBIC_FIXED: + return filter_cubic_spline; + + default: + return im->interpolation; + } } -static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double y, const int bgColor) -{ - /* Closest pixel <= (xf,yf) */ - int sx = (int)(x); - int sy = (int)(y); - const double xf = x - (double)sx; - const double yf = y - (double)sy; - const double nxf = (double) 1.0 - xf; - const double nyf = (double) 1.0 - yf; - const double m1 = xf * yf; - const double m2 = nxf * yf; - const double m3 = xf * nyf; - const double m4 = nxf * nyf; - - /* get color values of neighbouring pixels */ - const int c1 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy, bgColor) : getPixelOverflowPalette(im, sx, sy, bgColor); - const int c2 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy, bgColor) : getPixelOverflowPalette(im, sx - 1, sy, bgColor); - const int c3 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); - const int c4 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); - int r, g, b, a; - - if (x < 0) sx--; - if (y < 0) sy--; - - /* component-wise summing-up of color values */ - if (im->trueColor) { - r = (int)(m1*gdTrueColorGetRed(c1) + m2*gdTrueColorGetRed(c2) + m3*gdTrueColorGetRed(c3) + m4*gdTrueColorGetRed(c4)); - g = (int)(m1*gdTrueColorGetGreen(c1) + m2*gdTrueColorGetGreen(c2) + m3*gdTrueColorGetGreen(c3) + m4*gdTrueColorGetGreen(c4)); - b = (int)(m1*gdTrueColorGetBlue(c1) + m2*gdTrueColorGetBlue(c2) + m3*gdTrueColorGetBlue(c3) + m4*gdTrueColorGetBlue(c4)); - a = (int)(m1*gdTrueColorGetAlpha(c1) + m2*gdTrueColorGetAlpha(c2) + m3*gdTrueColorGetAlpha(c3) + m4*gdTrueColorGetAlpha(c4)); - } else { - r = (int)(m1*im->red[(c1)] + m2*im->red[(c2)] + m3*im->red[(c3)] + m4*im->red[(c4)]); - g = (int)(m1*im->green[(c1)] + m2*im->green[(c2)] + m3*im->green[(c3)] + m4*im->green[(c4)]); - b = (int)(m1*im->blue[(c1)] + m2*im->blue[(c2)] + m3*im->blue[(c3)] + m4*im->blue[(c4)]); - a = (int)(m1*im->alpha[(c1)] + m2*im->alpha[(c2)] + m3*im->alpha[(c3)] + m4*im->alpha[(c4)]); - } - - r = CLAMP(r, 0, 255); - g = CLAMP(g, 0, 255); - b = CLAMP(b, 0, 255); - a = CLAMP(a, 0, gdAlphaMax); - return gdTrueColorAlpha(r, g, b, a); +static int getPixelInterpolateWeightClipped(gdImagePtr im, const double x, const double y, + const int bgColor, const gdRectPtr clip) +{ + /* Closest pixel <= (xf,yf) */ + int sx = (int)(x); + int sy = (int)(y); + const double xf = x - (double)sx; + const double yf = y - (double)sy; + const double nxf = (double)1.0 - xf; + const double nyf = (double)1.0 - yf; + const double m1 = xf * yf; + const double m2 = nxf * yf; + const double m3 = xf * nyf; + const double m4 = nxf * nyf; + + /* get color values of neighbouring pixels */ + const int c1 = im->trueColor == 1 ? getPixelOverflowTCClipped(im, sx, sy, bgColor, clip) + : getPixelOverflowPaletteClipped(im, sx, sy, bgColor, clip); + const int c2 = im->trueColor == 1 + ? getPixelOverflowTCClipped(im, sx - 1, sy, bgColor, clip) + : getPixelOverflowPaletteClipped(im, sx - 1, sy, bgColor, clip); + const int c3 = im->trueColor == 1 + ? getPixelOverflowTCClipped(im, sx, sy - 1, bgColor, clip) + : getPixelOverflowPaletteClipped(im, sx, sy - 1, bgColor, clip); + const int c4 = im->trueColor == 1 + ? getPixelOverflowTCClipped(im, sx - 1, sy - 1, bgColor, clip) + : getPixelOverflowPaletteClipped(im, sx, sy - 1, bgColor, clip); + int r, g, b, a; + + if (x < 0) + sx--; + if (y < 0) + sy--; + + /* component-wise summing-up of color values */ + r = (int)(m1 * gdTrueColorGetRed(c1) + m2 * gdTrueColorGetRed(c2) + m3 * gdTrueColorGetRed(c3) + + m4 * gdTrueColorGetRed(c4)); + g = (int)(m1 * gdTrueColorGetGreen(c1) + m2 * gdTrueColorGetGreen(c2) + + m3 * gdTrueColorGetGreen(c3) + m4 * gdTrueColorGetGreen(c4)); + b = (int)(m1 * gdTrueColorGetBlue(c1) + m2 * gdTrueColorGetBlue(c2) + + m3 * gdTrueColorGetBlue(c3) + m4 * gdTrueColorGetBlue(c4)); + a = (int)(m1 * gdTrueColorGetAlpha(c1) + m2 * gdTrueColorGetAlpha(c2) + + m3 * gdTrueColorGetAlpha(c3) + m4 * gdTrueColorGetAlpha(c4)); + + r = CLAMP(r, 0, 255); + g = CLAMP(g, 0, 255); + b = CLAMP(b, 0, 255); + a = CLAMP(a, 0, gdAlphaMax); + return gdTrueColorAlpha(r, g, b, a); } /** - * Function: getPixelInterpolated + * InternalFunction: getPixelInterpolated * Returns the interpolated color value using the default interpolation * method. The returned color is always in the ARGB format (truecolor). * @@ -751,873 +745,733 @@ static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double * method - Interpolation method * * Returns: - * GD_TRUE if the affine is rectilinear or GD_FALSE + * the interpolated color or -1 on error * * See also: * */ -int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor) -{ - const int xi=(int)(x); - const int yi=(int)(y); - int yii; - int i; - double kernel, kernel_cache_y; - double kernel_x[12], kernel_y[4]; - double new_r = 0.0f, new_g = 0.0f, new_b = 0.0f, new_a = 0.0f; - - /* These methods use special implementations */ - if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) { - return -1; - } - - if (im->interpolation_id == GD_WEIGHTED4) { - return getPixelInterpolateWeight(im, x, y, bgColor); - } - - if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) { - if (im->trueColor == 1) { - return getPixelOverflowTC(im, xi, yi, bgColor); - } else { - return getPixelOverflowPalette(im, xi, yi, bgColor); - } - } - if (im->interpolation) { - for (i=0; i<4; i++) { - kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x)); - kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y)); - } - } else { - return -1; - } - - /* - * TODO: use the known fast rgba multiplication implementation once - * the new formats are in place - */ - for (yii = yi-1; yii < yi+3; yii++) { - int xii; - kernel_cache_y = kernel_y[yii-(yi-1)]; - if (im->trueColor) { - for (xii=xi-1; xiiinterpolation_id == GD_NEAREST_NEIGHBOUR) { + const int sx = gdClampInt((int)floor(x + 0.5), 0, gdImageSX(im) - 1); + const int sy = gdClampInt((int)floor(y + 0.5), 0, gdImageSY(im) - 1); + + return im->trueColor == 1 ? getPixelOverflowTCClipped(im, sx, sy, bgColor, clip) + : getPixelOverflowPaletteClipped(im, sx, sy, bgColor, clip); + } -static inline LineContribType * _gdContributionsAlloc(unsigned int line_length, unsigned int windows_size) -{ - unsigned int u = 0; - LineContribType *res; - size_t weights_size; - - if (overflow2(windows_size, sizeof(double))) { - return NULL; - } else { - weights_size = windows_size * sizeof(double); - } - res = (LineContribType *) gdMalloc(sizeof(LineContribType)); - if (!res) { - return NULL; - } - res->WindowSize = windows_size; - res->LineLength = line_length; - if (overflow2(line_length, sizeof(ContributionType))) { - gdFree(res); - return NULL; - } - res->ContribRow = (ContributionType *) gdMalloc(line_length * sizeof(ContributionType)); - if (res->ContribRow == NULL) { - gdFree(res); - return NULL; - } - for (u = 0 ; u < line_length ; u++) { - res->ContribRow[u].Weights = (double *) gdMalloc(weights_size); - if (res->ContribRow[u].Weights == NULL) { - unsigned int i; - for (i=0;iContribRow[i].Weights); - } - gdFree(res->ContribRow); - gdFree(res); - return NULL; - } - } - return res; + if (im->interpolation_id == GD_WEIGHTED4) { + return getPixelInterpolateWeightClipped(im, x, y, bgColor, clip); + } + + if (interpolation) { + for (i = 0; i < 4; i++) { + kernel_x[i] = (double)interpolation((double)(xi + i - 1 - x), 1.0); + kernel_y[i] = (double)interpolation((double)(yi + i - 1 - y), 1.0); + } + } else { + return -1; + } + + /* + * TODO: use the known fast rgba multiplication implementation once + * the new formats are in place + */ + for (yii = yi - 1; yii < yi + 3; yii++) { + int xii; + kernel_cache_y = kernel_y[yii - (yi - 1)]; + if (im->trueColor) { + for (xii = xi - 1; xii < xi + 3; xii++) { + const int rgbs = getPixelOverflowTCClipped(im, xii, yii, bgColor, clip); + + kernel = kernel_cache_y * kernel_x[xii - (xi - 1)]; + new_r += kernel * gdTrueColorGetRed(rgbs); + new_g += kernel * gdTrueColorGetGreen(rgbs); + new_b += kernel * gdTrueColorGetBlue(rgbs); + new_a += kernel * gdTrueColorGetAlpha(rgbs); + } + } else { + for (xii = xi - 1; xii < xi + 3; xii++) { + const int rgbs = getPixelOverflowPaletteClipped(im, xii, yii, bgColor, clip); + + kernel = kernel_cache_y * kernel_x[xii - (xi - 1)]; + new_r += kernel * gdTrueColorGetRed(rgbs); + new_g += kernel * gdTrueColorGetGreen(rgbs); + new_b += kernel * gdTrueColorGetBlue(rgbs); + new_a += kernel * gdTrueColorGetAlpha(rgbs); + } + } + } + + new_r = CLAMP(new_r, 0, 255); + new_g = CLAMP(new_g, 0, 255); + new_b = CLAMP(new_b, 0, 255); + new_a = CLAMP(new_a, 0, gdAlphaMax); + + return gdTrueColorAlpha((int)(new_r + 0.5), (int)(new_g + 0.5), (int)(new_b + 0.5), + (int)(new_a + 0.5)); } -static inline void _gdContributionsFree(LineContribType * p) +static int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor) { - unsigned int u; - for (u = 0; u < p->LineLength; u++) { - gdFree(p->ContribRow[u].Weights); - } - gdFree(p->ContribRow); - gdFree(p); + return getPixelInterpolatedClipped(im, x, y, bgColor, NULL); } -static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const interpolation_method pFilter) -{ - double width_d; - double scale_f_d = 1.0; - const double filter_width_d = DEFAULT_BOX_RADIUS; - int windows_size; - unsigned int u; - LineContribType *res; - - if (scale_d < 1.0) { - width_d = filter_width_d / scale_d; - scale_f_d = scale_d; - } else { - width_d= filter_width_d; - } - - windows_size = 2 * (int)ceil(width_d) + 1; - res = _gdContributionsAlloc(line_size, windows_size); - if (res == NULL) { - return NULL; - } - for (u = 0; u < line_size; u++) { - const double dCenter = (double)u / scale_d; - /* get the significant edge points affecting the pixel */ - register int iLeft = MAX(0, (int)floor (dCenter - width_d)); - int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1); - double dTotalWeight = 0.0; - int iSrc; - - /* Cut edge points to fit in filter window in case of spill-off */ - if (iRight - iLeft + 1 > windows_size) { - if (iLeft < ((int)src_size - 1 / 2)) { - iLeft++; - } else { - iRight--; - } - } - - res->ContribRow[u].Left = iLeft; - res->ContribRow[u].Right = iRight; - - for (iSrc = iLeft; iSrc <= iRight; iSrc++) { - dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc))); - } - - if (dTotalWeight < 0.0) { - _gdContributionsFree(res); - return NULL; - } - - if (dTotalWeight > 0.0) { - for (iSrc = iLeft; iSrc <= iRight; iSrc++) { - res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight; - } - } - } - return res; +static inline LineContribType *_gdContributionsAlloc(unsigned int line_length, + unsigned int windows_size) +{ + unsigned int u = 0; + LineContribType *res; + size_t weights_size; + + if (overflow2(windows_size, sizeof(double))) { + return NULL; + } else { + weights_size = windows_size * sizeof(double); + } + res = (LineContribType *)gdMalloc(sizeof(LineContribType)); + if (!res) { + return NULL; + } + res->WindowSize = windows_size; + res->LineLength = line_length; + if (overflow2(line_length, sizeof(ContributionType))) { + gdFree(res); + return NULL; + } + res->ContribRow = (ContributionType *)gdMalloc(line_length * sizeof(ContributionType)); + if (res->ContribRow == NULL) { + gdFree(res); + return NULL; + } + for (u = 0; u < line_length; u++) { + res->ContribRow[u].Weights = (double *)gdMalloc(weights_size); + if (res->ContribRow[u].Weights == NULL) { + unsigned int i; + + for (i = 0; i < u; i++) { + gdFree(res->ContribRow[i].Weights); + } + gdFree(res->ContribRow); + gdFree(res); + return NULL; + } + } + return res; } -/* Convert a double to an unsigned char, rounding to the nearest - * integer and clamping the result between 0 and max. The absolute - * value of clr must be less than the maximum value of an unsigned - * short. */ -static inline unsigned char -uchar_clamp(double clr, unsigned char max) { - unsigned short result; - - //assert(fabs(clr) <= SHRT_MAX); - - /* Casting a negative float to an unsigned short is undefined. - * However, casting a float to a signed truncates toward zero and - * casting a negative signed value to an unsigned of the same size - * results in a bit-identical value (assuming twos-complement - * arithmetic). This is what we want: all legal negative values - * for clr will be greater than 255. */ - - /* Convert and clamp. */ - result = (unsigned short)(short)(clr + 0.5); - if (result > max) { - result = (clr < 0) ? 0 : max; - }/* if */ - - return result; -}/* uchar_clamp*/ - -static inline void _gdScaleRow(gdImagePtr pSrc, unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib) -{ - int *p_src_row = pSrc->tpixels[row]; - int *p_dst_row = dst->tpixels[row]; - unsigned int x; - - for (x = 0; x < dst_width; x++) { - double r = 0, g = 0, b = 0, a = 0; - const int left = contrib->ContribRow[x].Left; - const int right = contrib->ContribRow[x].Right; - int i; - - /* Accumulate each channel */ - for (i = left; i <= right; i++) { - const int left_channel = i - left; - r += contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i])); - g += contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i])); - b += contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i])); - a += contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i])); - } - p_dst_row[x] = gdTrueColorAlpha(uchar_clamp(r, 0xFF), uchar_clamp(g, 0xFF), - uchar_clamp(b, 0xFF), - uchar_clamp(a, 0x7F)); /* alpha is 0..127 */ +static inline void _gdContributionsFree(LineContribType *p) +{ + unsigned int u; + for (u = 0; u < p->LineLength; u++) { + gdFree(p->ContribRow[u].Weights); } + gdFree(p->ContribRow); + gdFree(p); } -static inline int _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst, unsigned int dst_width, unsigned int dst_height) -{ - unsigned int u; - LineContribType * contrib; - - /* same width, just copy it */ - if (dst_width == src_width) { - unsigned int y; - for (y = 0; y < src_height - 1; ++y) { - memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); - } - } - - contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation); - if (contrib == NULL) { - return 0; - } - /* Scale each row */ - for (u = 0; u < dst_height; u++) { - _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib); - } - _gdContributionsFree (contrib); - return 1; -} +static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, + double scale_d, const double support, + const interpolation_method pFilter) +{ + double width_d; + double scale_f_d = 1.0; + const double filter_width_d = support; + int windows_size; + unsigned int u; + LineContribType *res; + + if (scale_d < 1.0) { + width_d = filter_width_d / scale_d; + scale_f_d = scale_d; + } else { + width_d = filter_width_d; + } -static inline void _gdScaleCol (gdImagePtr pSrc, unsigned int src_width, gdImagePtr pRes, unsigned int dst_width, unsigned int dst_height, unsigned int uCol, LineContribType *contrib) -{ - unsigned int y; - for (y = 0; y < dst_height; y++) { - double r = 0, g = 0, b = 0, a = 0; - const int iLeft = contrib->ContribRow[y].Left; - const int iRight = contrib->ContribRow[y].Right; - int i; - - /* Accumulate each channel */ - for (i = iLeft; i <= iRight; i++) { - const int pCurSrc = pSrc->tpixels[i][uCol]; - const int i_iLeft = i - iLeft; - r += contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc)); - g += contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc)); - b += contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc)); - a += contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc)); - } - pRes->tpixels[y][uCol] = gdTrueColorAlpha(uchar_clamp(r, 0xFF), uchar_clamp(g, 0xFF), - uchar_clamp(b, 0xFF), - uchar_clamp(a, 0x7F)); /* alpha is 0..127 */ - } + windows_size = 2 * (int)ceil(width_d) + 1; + res = _gdContributionsAlloc(line_size, windows_size); + if (res == NULL) { + return NULL; + } + for (u = 0; u < line_size; u++) { + const double dCenter = (double)u / scale_d; + /* get the significant edge points affecting the pixel */ + register int iLeft = MAX(0, (int)floor(dCenter - width_d)); + int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1); + double dTotalWeight = 0.0; + int iSrc; + + /* Cut edge points to fit in filter window in case of spill-off */ + if (iRight - iLeft + 1 > windows_size) { + if (iLeft < ((int)src_size - 1 / 2)) { + iLeft++; + } else { + iRight--; + } + } + + res->ContribRow[u].Left = iLeft; + res->ContribRow[u].Right = iRight; + + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + dTotalWeight += + (res->ContribRow[u].Weights[iSrc - iLeft] = + scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc), support)); + } + + if (dTotalWeight < 0.0) { + _gdContributionsFree(res); + return NULL; + } + + if (dTotalWeight > 0.0) { + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + res->ContribRow[u].Weights[iSrc - iLeft] /= dTotalWeight; + } + } + } + return res; } -static inline int _gdScaleVert (const gdImagePtr pSrc, const unsigned int src_width, const unsigned int src_height, const gdImagePtr pDst, const unsigned int dst_width, const unsigned int dst_height) -{ - unsigned int u; - LineContribType * contrib; - - /* same height, copy it */ - if (src_height == dst_height) { - unsigned int y; - for (y = 0; y < src_height - 1; ++y) { - memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); - } - } - - contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation); - if (contrib == NULL) { - return 0; - } - /* scale each column */ - for (u = 0; u < dst_width; u++) { - _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib); - } - _gdContributionsFree(contrib); - return 1; -} +static inline void _gdScaleOneAxis(gdImagePtr pSrc, gdImagePtr dst, unsigned int dst_len, + unsigned int row, LineContribType *contrib, gdAxis axis) +{ + unsigned int ndx; + + for (ndx = 0; ndx < dst_len; ndx++) { + double r = 0, g = 0, b = 0, a = 0; + const int left = contrib->ContribRow[ndx].Left; + const int right = contrib->ContribRow[ndx].Right; + int *dest = (axis == HORIZONTAL) ? &dst->tpixels[row][ndx] : &dst->tpixels[ndx][row]; + + int i; + + /* Accumulate each channel */ + for (i = left; i <= right; i++) { + const int left_channel = i - left; + const int srcpx = (axis == HORIZONTAL) ? pSrc->tpixels[row][i] : pSrc->tpixels[i][row]; + + r += + contrib->ContribRow[ndx].Weights[left_channel] * (double)(gdTrueColorGetRed(srcpx)); + g += contrib->ContribRow[ndx].Weights[left_channel] * + (double)(gdTrueColorGetGreen(srcpx)); + b += contrib->ContribRow[ndx].Weights[left_channel] * + (double)(gdTrueColorGetBlue(srcpx)); + a += contrib->ContribRow[ndx].Weights[left_channel] * + (double)(gdTrueColorGetAlpha(srcpx)); + } /* for */ + + *dest = gdTrueColorAlpha(uchar_clamp(r, 0xFF), uchar_clamp(g, 0xFF), uchar_clamp(b, 0xFF), + uchar_clamp(a, 0x7F)); /* alpha is 0..127 */ + } /* for */ +} /* _gdScaleOneAxis*/ + +static inline int _gdScalePass(const gdImagePtr pSrc, const unsigned int src_len, + const gdImagePtr pDst, const unsigned int dst_len, + const unsigned int num_lines, const gdAxis axis, + const FilterInfo *filter) +{ + unsigned int line_ndx; + LineContribType *contrib; + + /* Same dim, just copy it. */ + assert(dst_len != src_len); // TODO: caller should handle this. + + contrib = _gdContributionsCalc(dst_len, src_len, (double)dst_len / (double)src_len, + filter->support, filter->function); + if (contrib == NULL) { + return 0; + } + + /* Scale each line */ + for (line_ndx = 0; line_ndx < num_lines; line_ndx++) { + _gdScaleOneAxis(pSrc, pDst, dst_len, line_ndx, contrib, axis); + } + _gdContributionsFree(contrib); + return 1; +} /* _gdScalePass*/ + +static const FilterInfo filters[GD_METHOD_COUNT + 1] = { + {filter_box, 0.0}, + {filter_bell, 1.5}, + {filter_bessel, 0.0}, + {NULL, 0.0}, /* NA bilenear/bilinear fixed */ + {NULL, 0.0}, /* NA bicubic */ + {NULL, 0.0}, /* NA bicubic fixed */ + {filter_blackman, 1.0}, + {filter_box, 0.5}, + {filter_bspline, 1.5}, + {filter_catmullrom, 2.0}, + {filter_gaussian, 1.25}, + {filter_generalized_cubic, 0.5}, + {filter_hermite, 1.0}, + {filter_hamming, 1.0}, + {filter_hanning, 1.0}, + {filter_mitchell, 2.0}, + {NULL, 0.0}, /* NA Nearest */ + {filter_power, 0.0}, + {filter_quadratic, 1.5}, + {filter_sinc, 1.0}, + {filter_triangle, 1.0}, + {NULL, 1.0}, /* NA weighted4 */ + {filter_linear, 1.0}, + {filter_lanczos3, 3.0}, + {filter_lanczos8, 8.0}, + {filter_blackman_bessel, 3.2383}, + {filter_blackman_sinc, 4.0}, + {filter_quadratic_bspline, 1.5}, + {filter_cubic_spline, 0.0}, + {filter_cosine, 0.0}, + {filter_welsh, 0.0}, +}; + +static const FilterInfo *_get_filterinfo_for_id(gdInterpolationMethod id) +{ -static gdImagePtr -gdImageScaleTwoPass(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const unsigned int new_width, const unsigned int new_height) -{ - gdImagePtr tmp_im; - gdImagePtr dst; - int scale_pass_res; - - /* Convert to truecolor if it isn't; this code requires it. */ - if (!src->trueColor) { - gdImagePaletteToTrueColor(src); - } - - tmp_im = gdImageCreateTrueColor(new_width, src_height); - if (tmp_im == NULL) { - return NULL; - } - gdImageSetInterpolationMethod(tmp_im, src->interpolation_id); - scale_pass_res = _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height); - if (scale_pass_res != 1) { - gdImageDestroy(tmp_im); - return NULL; - } - - dst = gdImageCreateTrueColor(new_width, new_height); - if (dst == NULL) { - gdImageDestroy(tmp_im); - return NULL; - } - gdImageSetInterpolationMethod(dst, src->interpolation_id); - scale_pass_res = _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height); - if (scale_pass_res != 1) { - gdImageDestroy(dst); - gdImageDestroy(tmp_im); - return NULL; - } - gdImageDestroy(tmp_im); - - return dst; + if (id >= GD_METHOD_COUNT) { + id = GD_DEFAULT; + } + return &filters[id]; } +static gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int new_width, + const unsigned int new_height) +{ + const unsigned int src_width = src->sx; + const unsigned int src_height = src->sy; + gdImagePtr tmp_im = NULL; + gdImagePtr dst = NULL; + int scale_pass_res; + const FilterInfo *filter = _get_filterinfo_for_id(src->interpolation_id); + + /* First, handle the trivial case. */ + if (src_width == new_width && src_height == new_height) { + return gdImageClone(src); + } + + /* Convert to truecolor if it isn't; this code requires it. */ + if (!src->trueColor) { + gdImagePaletteToTrueColor(src); + } + + /* Scale horizontally unless sizes are the same. */ + if (src_width == new_width) { + tmp_im = src; + } else { + tmp_im = gdImageCreateTrueColor(new_width, src_height); + if (tmp_im == NULL) { + return NULL; + } + gdImageSetInterpolationMethod(tmp_im, src->interpolation_id); + + scale_pass_res = + _gdScalePass(src, src_width, tmp_im, new_width, src_height, HORIZONTAL, filter); + if (scale_pass_res != 1) { + gdImageDestroy(tmp_im); + return NULL; + } + } + + /* If vertical sizes match, we're done. */ + if (src_height == new_height) { + assert(tmp_im != src); + return tmp_im; + } + + /* Otherwise, we need to scale vertically. */ + dst = gdImageCreateTrueColor(new_width, new_height); + if (dst != NULL) { + gdImageSetInterpolationMethod(dst, src->interpolation_id); + scale_pass_res = + _gdScalePass(tmp_im, src_height, dst, new_height, new_width, VERTICAL, filter); + if (scale_pass_res != 1) { + gdImageDestroy(dst); + if (src != tmp_im) { + gdImageDestroy(tmp_im); + } + return NULL; + } + } + + if (src != tmp_im) { + gdImageDestroy(tmp_im); + } + + return dst; +} /* gdImageScaleTwoPass*/ + /* - BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx - http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class - Integer only implementation, good to have for common usages like pre scale very large - images before using another interpolation methods for the last step. + Bilinear, bicubic and nearest implementations are + rewamped versions of the implementation in CBitmapEx + + http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class + + The GD_BILINEAR_FIXED and GD_BICUBIC_FIXED public names are kept for + compatibility, but these implementations use floating point arithmetic. */ -static gdImagePtr -gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height) -{ - const unsigned long new_width = MAX(1, width); - const unsigned long new_height = MAX(1, height); - const float dx = (float)im->sx / (float)new_width; - const float dy = (float)im->sy / (float)new_height; - const gdFixed f_dx = gd_ftofx(dx); - const gdFixed f_dy = gd_ftofx(dy); - - gdImagePtr dst_img; - unsigned long dst_offset_x; - unsigned long dst_offset_y = 0; - unsigned int i; - - dst_img = gdImageCreateTrueColor(new_width, new_height); - - if (dst_img == NULL) { - return NULL; - } - - for (i=0; itrueColor) { - for (j=0; jtpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n]; - } - } else { - for (j=0; jtpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]); - } - } - dst_offset_y++; - } - return dst_img; +static gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, + const unsigned int height) +{ + const unsigned long new_width = MAX(1, width); + const unsigned long new_height = MAX(1, height); + const double dx = (double)im->sx / (double)new_width; + const double dy = (double)im->sy / (double)new_height; + + gdImagePtr dst_img; + unsigned long dst_offset_x; + unsigned long dst_offset_y = 0; + unsigned int i; + + dst_img = gdImageCreateTrueColor(new_width, new_height); + + if (dst_img == NULL) { + return NULL; + } + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + if (im->trueColor) { + for (j = 0; j < new_width; j++) { + const long m = gdClampInt((int)((double)i * dy), 0, gdImageSY(im) - 1); + const long n = gdClampInt((int)((double)j * dx), 0, gdImageSX(im) - 1); + + dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n]; + } + } else { + for (j = 0; j < new_width; j++) { + const long m = gdClampInt((int)((double)i * dy), 0, gdImageSY(im) - 1); + const long n = gdClampInt((int)((double)j * dx), 0, gdImageSX(im) - 1); + + dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]); + } + } + dst_offset_y++; + } + return dst_img; } -static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) -{ - long _width = MAX(1, new_width); - long _height = MAX(1, new_height); - float dx = (float)gdImageSX(im) / (float)_width; - float dy = (float)gdImageSY(im) / (float)_height; - gdFixed f_dx = gd_ftofx(dx); - gdFixed f_dy = gd_ftofx(dy); - gdFixed f_1 = gd_itofx(1); - - int dst_offset_h; - int dst_offset_v = 0; - long i; - gdImagePtr new_img; - const int transparent = im->transparent; - - new_img = gdImageCreateTrueColor(new_width, new_height); - if (new_img == NULL) { - return NULL; - } - - if (transparent < 0) { - /* uninitialized */ - new_img->transparent = -1; - } else { - new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]); - } - - for (i=0; i < _height; i++) { - long j; - const gdFixed f_i = gd_itofx(i); - const gdFixed f_a = gd_mulfx(f_i, f_dy); - register long m = gd_fxtoi(f_a); - - dst_offset_h = 0; - - for (j=0; j < _width; j++) { - /* Update bitmap */ - gdFixed f_j = gd_itofx(j); - gdFixed f_b = gd_mulfx(f_j, f_dx); - - const long n = gd_fxtoi(f_b); - gdFixed f_f = f_a - gd_itofx(m); - gdFixed f_g = f_b - gd_itofx(n); - - const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); - const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); - const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); - const gdFixed f_w4 = gd_mulfx(f_f, f_g); - unsigned int pixel1; - unsigned int pixel2; - unsigned int pixel3; - unsigned int pixel4; - register gdFixed f_r1, f_r2, f_r3, f_r4, - f_g1, f_g2, f_g3, f_g4, - f_b1, f_b2, f_b3, f_b4, - f_a1, f_a2, f_a3, f_a4; - - /* 0 for bgColor; (n,m) is supposed to be valid anyway */ - pixel1 = getPixelOverflowPalette(im, n, m, 0); - pixel2 = getPixelOverflowPalette(im, n + 1, m, pixel1); - pixel3 = getPixelOverflowPalette(im, n, m + 1, pixel1); - pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, pixel1); - - f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); - f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); - f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); - f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); - f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); - f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); - f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); - f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); - f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); - f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); - f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); - f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); - f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); - f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); - f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); - f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); - - { - const unsigned char red = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); - const unsigned char green = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); - const unsigned char blue = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); - const unsigned char alpha = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); - - new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); - } - - dst_offset_h++; - } - - dst_offset_v++; - } - return new_img; +static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, + const unsigned int new_height) +{ + long _width = MAX(1, new_width); + long _height = MAX(1, new_height); + double dx = (double)gdImageSX(im) / (double)_width; + double dy = (double)gdImageSY(im) / (double)_height; + + int dst_offset_h; + int dst_offset_v = 0; + long i; + gdImagePtr new_img; + const int transparent = im->transparent; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (new_img == NULL) { + return NULL; + } + + if (transparent < 0) { + /* uninitialized */ + new_img->transparent = -1; + } else { + new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], + im->blue[transparent], im->alpha[transparent]); + } + + for (i = 0; i < _height; i++) { + long j; + const double src_y = (double)i * dy; + register long m = gdClampInt((int)src_y, 0, gdImageSY(im) - 1); + + dst_offset_h = 0; + + for (j = 0; j < _width; j++) { + /* Update bitmap */ + const double src_x = (double)j * dx; + const long n = gdClampInt((int)src_x, 0, gdImageSX(im) - 1); + const double f_f = src_y - (double)m; + const double f_g = src_x - (double)n; + + const double f_w1 = (1.0 - f_f) * (1.0 - f_g); + const double f_w2 = (1.0 - f_f) * f_g; + const double f_w3 = f_f * (1.0 - f_g); + const double f_w4 = f_f * f_g; + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + + /* 0 for bgColor; (n,m) is supposed to be valid anyway */ + pixel1 = getPixelOverflowPalette(im, n, m, 0); + pixel2 = getPixelOverflowPalette(im, n + 1, m, pixel1); + pixel3 = getPixelOverflowPalette(im, n, m + 1, pixel1); + pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, pixel1); + + { + const unsigned char red = uchar_clamp( + f_w1 * gdTrueColorGetRed(pixel1) + f_w2 * gdTrueColorGetRed(pixel2) + + f_w3 * gdTrueColorGetRed(pixel3) + f_w4 * gdTrueColorGetRed(pixel4), + 0xFF); + const unsigned char green = uchar_clamp( + f_w1 * gdTrueColorGetGreen(pixel1) + f_w2 * gdTrueColorGetGreen(pixel2) + + f_w3 * gdTrueColorGetGreen(pixel3) + f_w4 * gdTrueColorGetGreen(pixel4), + 0xFF); + const unsigned char blue = uchar_clamp( + f_w1 * gdTrueColorGetBlue(pixel1) + f_w2 * gdTrueColorGetBlue(pixel2) + + f_w3 * gdTrueColorGetBlue(pixel3) + f_w4 * gdTrueColorGetBlue(pixel4), + 0xFF); + const unsigned char alpha = uchar_clamp( + f_w1 * gdTrueColorGetAlpha(pixel1) + f_w2 * gdTrueColorGetAlpha(pixel2) + + f_w3 * gdTrueColorGetAlpha(pixel3) + f_w4 * gdTrueColorGetAlpha(pixel4), + 0x7F); + + new_img->tpixels[dst_offset_v][dst_offset_h] = + gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; } -static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) -{ - long dst_w = MAX(1, new_width); - long dst_h = MAX(1, new_height); - float dx = (float)gdImageSX(im) / (float)dst_w; - float dy = (float)gdImageSY(im) / (float)dst_h; - gdFixed f_dx = gd_ftofx(dx); - gdFixed f_dy = gd_ftofx(dy); - gdFixed f_1 = gd_itofx(1); - - int dst_offset_h; - int dst_offset_v = 0; - long i; - gdImagePtr new_img; - - new_img = gdImageCreateTrueColor(new_width, new_height); - if (!new_img){ - return NULL; - } - - for (i=0; i < dst_h; i++) { - long j; - dst_offset_h = 0; - for (j=0; j < dst_w; j++) { - /* Update bitmap */ - gdFixed f_i = gd_itofx(i); - gdFixed f_j = gd_itofx(j); - gdFixed f_a = gd_mulfx(f_i, f_dy); - gdFixed f_b = gd_mulfx(f_j, f_dx); - const long m = gd_fxtoi(f_a); - const long n = gd_fxtoi(f_b); - gdFixed f_f = f_a - gd_itofx(m); - gdFixed f_g = f_b - gd_itofx(n); - - const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); - const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); - const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); - const gdFixed f_w4 = gd_mulfx(f_f, f_g); - unsigned int pixel1; - unsigned int pixel2; - unsigned int pixel3; - unsigned int pixel4; - register gdFixed f_r1, f_r2, f_r3, f_r4, - f_g1, f_g2, f_g3, f_g4, - f_b1, f_b2, f_b3, f_b4, - f_a1, f_a2, f_a3, f_a4; - /* 0 for bgColor; (n,m) is supposed to be valid anyway */ - pixel1 = getPixelOverflowTC(im, n, m, 0); - pixel2 = getPixelOverflowTC(im, n + 1, m, pixel1); - pixel3 = getPixelOverflowTC(im, n, m + 1, pixel1); - pixel4 = getPixelOverflowTC(im, n + 1, m + 1, pixel1); - - f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); - f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); - f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); - f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); - f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); - f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); - f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); - f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); - f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); - f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); - f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); - f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); - f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); - f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); - f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); - f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); - { - const unsigned char red = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); - const unsigned char green = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); - const unsigned char blue = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); - const unsigned char alpha = (unsigned char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); - - new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); - } - - dst_offset_h++; - } - - dst_offset_v++; - } - return new_img; +static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, + const unsigned int new_height) +{ + long dst_w = MAX(1, new_width); + long dst_h = MAX(1, new_height); + double dx = (double)gdImageSX(im) / (double)dst_w; + double dy = (double)gdImageSY(im) / (double)dst_h; + + int dst_offset_h; + int dst_offset_v = 0; + long i; + gdImagePtr new_img; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (!new_img) { + return NULL; + } + + for (i = 0; i < dst_h; i++) { + long j; + dst_offset_h = 0; + for (j = 0; j < dst_w; j++) { + /* Update bitmap */ + const double src_y = (double)i * dy; + const double src_x = (double)j * dx; + const long m = gdClampInt((int)src_y, 0, gdImageSY(im) - 1); + const long n = gdClampInt((int)src_x, 0, gdImageSX(im) - 1); + const double f_f = src_y - (double)m; + const double f_g = src_x - (double)n; + + const double f_w1 = (1.0 - f_f) * (1.0 - f_g); + const double f_w2 = (1.0 - f_f) * f_g; + const double f_w3 = f_f * (1.0 - f_g); + const double f_w4 = f_f * f_g; + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + /* 0 for bgColor; (n,m) is supposed to be valid anyway */ + pixel1 = getPixelOverflowTC(im, n, m, 0); + pixel2 = getPixelOverflowTC(im, n + 1, m, pixel1); + pixel3 = getPixelOverflowTC(im, n, m + 1, pixel1); + pixel4 = getPixelOverflowTC(im, n + 1, m + 1, pixel1); + + { + const unsigned char red = uchar_clamp( + f_w1 * gdTrueColorGetRed(pixel1) + f_w2 * gdTrueColorGetRed(pixel2) + + f_w3 * gdTrueColorGetRed(pixel3) + f_w4 * gdTrueColorGetRed(pixel4), + 0xFF); + const unsigned char green = uchar_clamp( + f_w1 * gdTrueColorGetGreen(pixel1) + f_w2 * gdTrueColorGetGreen(pixel2) + + f_w3 * gdTrueColorGetGreen(pixel3) + f_w4 * gdTrueColorGetGreen(pixel4), + 0xFF); + const unsigned char blue = uchar_clamp( + f_w1 * gdTrueColorGetBlue(pixel1) + f_w2 * gdTrueColorGetBlue(pixel2) + + f_w3 * gdTrueColorGetBlue(pixel3) + f_w4 * gdTrueColorGetBlue(pixel4), + 0xFF); + const unsigned char alpha = uchar_clamp( + f_w1 * gdTrueColorGetAlpha(pixel1) + f_w2 * gdTrueColorGetAlpha(pixel2) + + f_w3 * gdTrueColorGetAlpha(pixel3) + f_w4 * gdTrueColorGetAlpha(pixel4), + 0x7F); + + new_img->tpixels[dst_offset_v][dst_offset_h] = + gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; } -static gdImagePtr -gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +static gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, + const unsigned int new_height) { - if (im->trueColor) { - return gdImageScaleBilinearTC(im, new_width, new_height); - } else { - return gdImageScaleBilinearPalette(im, new_width, new_height); - } + if (im->trueColor) { + return gdImageScaleBilinearTC(im, new_width, new_height); + } else { + return gdImageScaleBilinearPalette(im, new_width, new_height); + } } -static gdImagePtr -gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height) -{ - const long new_width = MAX(1, width); - const long new_height = MAX(1, height); - const int src_w = gdImageSX(src); - const int src_h = gdImageSY(src); - const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width); - const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height); - const gdFixed f_1 = gd_itofx(1); - const gdFixed f_2 = gd_itofx(2); - const gdFixed f_4 = gd_itofx(4); - const gdFixed f_6 = gd_itofx(6); - const gdFixed f_gamma = gd_ftofx(1.04f); - gdImagePtr dst; - - unsigned int dst_offset_x; - unsigned int dst_offset_y = 0; - long i; - - /* impact perf a bit, but not that much. Implementation for palette - images can be done at a later point. - */ - if (src->trueColor == 0) { - gdImagePaletteToTrueColor(src); - } - - dst = gdImageCreateTrueColor(new_width, new_height); - if (!dst) { - return NULL; - } - - dst->saveAlphaFlag = 1; - - for (i=0; i < new_height; i++) { - long j; - dst_offset_x = 0; - - for (j=0; j < new_width; j++) { - const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy); - const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx); - const long m = gd_fxtoi(f_a); - const long n = gd_fxtoi(f_b); - const gdFixed f_f = f_a - gd_itofx(m); - const gdFixed f_g = f_b - gd_itofx(n); - unsigned int src_offset_x[16], src_offset_y[16]; - long k; - register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0; - unsigned char red, green, blue, alpha = 0; - int *dst_row = dst->tpixels[dst_offset_y]; - - if ((m < 1) || (n < 1)) { - src_offset_x[0] = n; - src_offset_y[0] = m; - } else { - src_offset_x[0] = n - 1; - src_offset_y[0] = m; - } - - src_offset_x[1] = n; - src_offset_y[1] = m; - - if ((m < 1) || (n >= src_w - 1)) { - src_offset_x[2] = n; - src_offset_y[2] = m; - } else { - src_offset_x[2] = n + 1; - src_offset_y[2] = m; - } - - if ((m < 1) || (n >= src_w - 2)) { - src_offset_x[3] = n; - src_offset_y[3] = m; - } else { - src_offset_x[3] = n + 1 + 1; - src_offset_y[3] = m; - } - - if (n < 1) { - src_offset_x[4] = n; - src_offset_y[4] = m; - } else { - src_offset_x[4] = n - 1; - src_offset_y[4] = m; - } - - src_offset_x[5] = n; - src_offset_y[5] = m; - if (n >= src_w-1) { - src_offset_x[6] = n; - src_offset_y[6] = m; - } else { - src_offset_x[6] = n + 1; - src_offset_y[6] = m; - } - - if (n >= src_w - 2) { - src_offset_x[7] = n; - src_offset_y[7] = m; - } else { - src_offset_x[7] = n + 1 + 1; - src_offset_y[7] = m; - } - - if ((m >= src_h - 1) || (n < 1)) { - src_offset_x[8] = n; - src_offset_y[8] = m; - } else { - src_offset_x[8] = n - 1; - src_offset_y[8] = m; - } - - src_offset_x[9] = n; - src_offset_y[9] = m; - - if ((m >= src_h-1) || (n >= src_w-1)) { - src_offset_x[10] = n; - src_offset_y[10] = m; - } else { - src_offset_x[10] = n + 1; - src_offset_y[10] = m; - } - - if ((m >= src_h - 1) || (n >= src_w - 2)) { - src_offset_x[11] = n; - src_offset_y[11] = m; - } else { - src_offset_x[11] = n + 1 + 1; - src_offset_y[11] = m; - } - - if ((m >= src_h - 2) || (n < 1)) { - src_offset_x[12] = n; - src_offset_y[12] = m; - } else { - src_offset_x[12] = n - 1; - src_offset_y[12] = m; - } - - src_offset_x[13] = n; - src_offset_y[13] = m; - - if ((m >= src_h - 2) || (n >= src_w - 1)) { - src_offset_x[14] = n; - src_offset_y[14] = m; - } else { - src_offset_x[14] = n + 1; - src_offset_y[14] = m; - } - - if ((m >= src_h - 2) || (n >= src_w - 2)) { - src_offset_x[15] = n; - src_offset_y[15] = m; - } else { - src_offset_x[15] = n + 1 + 1; - src_offset_y[15] = m; - } - - for (k = -1; k < 3; k++) { - const gdFixed f = gd_itofx(k)-f_f; - const gdFixed f_fm1 = f - f_1; - const gdFixed f_fp1 = f + f_1; - const gdFixed f_fp2 = f + f_2; - register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0; - register gdFixed f_RY; - int l; - - if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2)); - if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1)); - if (f > 0) f_c = gd_mulfx(f, gd_mulfx(f,f)); - if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1)); - - f_RY = gd_divfx((f_a - gd_mulfx(f_4,f_b) + gd_mulfx(f_6,f_c) - gd_mulfx(f_4,f_d)),f_6); - - for (l = -1; l < 3; l++) { - const gdFixed f = gd_itofx(l) - f_g; - const gdFixed f_fm1 = f - f_1; - const gdFixed f_fp1 = f + f_1; - const gdFixed f_fp2 = f + f_2; - register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0; - register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba; - register int c; - const int _k = ((k+1)*4) + (l+1); - - if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); - - if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); - - if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f)); - - if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); - - f_RX = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6); - f_R = gd_mulfx(f_RY,f_RX); - - c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)]; - f_rs = gd_itofx(gdTrueColorGetRed(c)); - f_gs = gd_itofx(gdTrueColorGetGreen(c)); - f_bs = gd_itofx(gdTrueColorGetBlue(c)); - f_ba = gd_itofx(gdTrueColorGetAlpha(c)); - - f_red += gd_mulfx(f_rs,f_R); - f_green += gd_mulfx(f_gs,f_R); - f_blue += gd_mulfx(f_bs,f_R); - f_alpha += gd_mulfx(f_ba,f_R); - } - } - - red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gamma)), 0, 255); - green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)), 0, 255); - blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gamma)), 0, 255); - alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gamma)), 0, 127); - - *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha); - - dst_offset_x++; - } - dst_offset_y++; - } - return dst; +static gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, + const unsigned int height) +{ + const long new_width = MAX(1, width); + const long new_height = MAX(1, height); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const double dx = (double)src_w / (double)new_width; + const double dy = (double)src_h / (double)new_height; + const double gamma = 1.04; + gdImagePtr dst; + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + long i; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + long j; + const double src_y = (double)i * dy; + const long m = gdClampInt((int)src_y, 0, src_h - 1); + const double f_f = src_y - (double)m; + dst_offset_x = 0; + + for (j = 0; j < new_width; j++) { + const double src_x = (double)j * dx; + const long n = gdClampInt((int)src_x, 0, src_w - 1); + const double f_g = src_x - (double)n; + long k; + double red_acc = 0.0, green_acc = 0.0, blue_acc = 0.0, alpha_acc = 0.0; + unsigned char red, green, blue, alpha = 0; + int *dst_row = dst->tpixels[dst_offset_y]; + + for (k = -1; k < 3; k++) { + int l; + const int src_y_sample = gdClampInt((int)m + (int)k, 0, src_h - 1); + const double weight_y = filter_cubic_spline((double)k - f_f, 0.0); + + for (l = -1; l < 3; l++) { + const int src_x_sample = gdClampInt((int)n + (int)l, 0, src_w - 1); + const double weight = weight_y * filter_cubic_spline((double)l - f_g, 0.0); + const int c = src->tpixels[src_y_sample][src_x_sample]; + + red_acc += weight * (double)gdTrueColorGetRed(c); + green_acc += weight * (double)gdTrueColorGetGreen(c); + blue_acc += weight * (double)gdTrueColorGetBlue(c); + alpha_acc += weight * (double)gdTrueColorGetAlpha(c); + } + } + + red = uchar_clamp(red_acc * gamma, 0xFF); + green = uchar_clamp(green_acc * gamma, 0xFF); + blue = uchar_clamp(blue_acc * gamma, 0xFF); + alpha = uchar_clamp(alpha_acc * gamma, 0x7F); + + *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha); + + dst_offset_x++; + } + dst_offset_y++; + } + return dst; } -gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height) -{ - gdImagePtr im_scaled = NULL; - - if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) { - return NULL; - } - - if (new_width == 0 || new_height == 0) { - return NULL; - } - - if (new_width == gdImageSX(src) && new_height == gdImageSY(src)) { - return gdImageClone(src); - } - switch (src->interpolation_id) { - /*Special cases, optimized implementations */ - case GD_NEAREST_NEIGHBOUR: - im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height); - break; - - case GD_BILINEAR_FIXED: - case GD_LINEAR: - im_scaled = gdImageScaleBilinear(src, new_width, new_height); - break; - - case GD_BICUBIC_FIXED: - case GD_BICUBIC: - im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height); - break; - - /* generic */ - default: - if (src->interpolation == NULL) { - return NULL; - } - im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height); - break; - } - return im_scaled; +/** + * Function: gdImageScale + * + * Scale an image + * + * Creates a new image, scaled to the requested size using the current + * . + * + * Note that GD_WEIGHTED4 is not yet supported by this function. + * + * Parameters: + * src - The source image. + * new_width - The new width. + * new_height - The new height. + * + * Returns: + * The scaled image on success, NULL on failure. + * + * See also: + * - + * - + */ +BGD_DECLARE(gdImagePtr) +gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr im_scaled = NULL; + + if (src == NULL || (uintmax_t)src->interpolation_id >= GD_METHOD_COUNT) { + return NULL; + } + + if (new_width == 0 || new_height == 0) { + return NULL; + } + if ((int)new_width == gdImageSX(src) && (int)new_height == gdImageSY(src)) { + return gdImageClone(src); + } + switch (src->interpolation_id) { + /*Special cases, optimized implementations */ + case GD_NEAREST_NEIGHBOUR: + im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height); + break; + + case GD_BILINEAR_FIXED: + case GD_LINEAR: + im_scaled = gdImageScaleBilinear(src, new_width, new_height); + break; + + case GD_BICUBIC_FIXED: + case GD_BICUBIC: + im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height); + break; + + /* generic */ + default: + if (src->interpolation == NULL) { + return NULL; + } + im_scaled = gdImageScaleTwoPass(src, new_width, new_height); + break; + } + + return im_scaled; } static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox) @@ -1637,207 +1491,223 @@ static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox) return GD_TRUE; } -static gdImagePtr -gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor) -{ - float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); - const int src_w = gdImageSX(src); - const int src_h = gdImageSY(src); - const gdFixed f_0_5 = gd_ftofx(0.5f); - const gdFixed f_H = gd_itofx(src_h/2); - const gdFixed f_W = gd_itofx(src_w/2); - const gdFixed f_cos = gd_ftofx(cos(-_angle)); - const gdFixed f_sin = gd_ftofx(sin(-_angle)); - - unsigned int dst_offset_x; - unsigned int dst_offset_y = 0; - unsigned int i; - gdImagePtr dst; - gdRect bbox; - int new_height, new_width; +static gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, + const int bgColor) +{ + double _angle = ((double)(-degrees) / 180.0) * M_PI; + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const double H = (double)(src_h / 2); + const double W = (double)(src_w / 2); + const double cos_angle = cos(-_angle); + const double sin_angle = sin(-_angle); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + gdRect bbox; + unsigned int new_height, new_width; gdRotatedImageSize(src, degrees, &bbox); new_width = bbox.width; new_height = bbox.height; - dst = gdImageCreateTrueColor(new_width, new_height); - if (!dst) { - return NULL; - } - dst->saveAlphaFlag = 1; - for (i = 0; i < new_height; i++) { - unsigned int j; - dst_offset_x = 0; - for (j = 0; j < new_width; j++) { - gdFixed f_i = gd_itofx((int)i - (int)new_height/2); - gdFixed f_j = gd_itofx((int)j - (int)new_width/2); - gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; - gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; - long m = gd_fxtoi(f_m); - long n = gd_fxtoi(f_n); - - if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) { - if (dst_offset_y < new_height) { - dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n]; - } - } else { - if (dst_offset_y < new_height) { - dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; - } - } - } - dst_offset_y++; - } - return dst; + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + const double dst_y = (double)((int)i - (int)new_height / 2); + const double dst_x = (double)((int)j - (int)new_width / 2); + const double src_y = dst_x * sin_angle + dst_y * cos_angle + H; + const double src_x = dst_x * cos_angle - dst_y * sin_angle + W; + long m = (long)floor(src_y + 0.5); + long n = (long)floor(src_x + 0.5); + + if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n]; + } + } else { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } + } + } + dst_offset_y++; + } + return dst; } -static gdImagePtr -gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor) -{ - float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); - const int src_w = gdImageSX(src); - const int src_h = gdImageSY(src); - const gdFixed f_0_5 = gd_ftofx(0.5f); - const gdFixed f_H = gd_itofx(src_h/2); - const gdFixed f_W = gd_itofx(src_w/2); - const gdFixed f_cos = gd_ftofx(cos(-_angle)); - const gdFixed f_sin = gd_ftofx(sin(-_angle)); - - unsigned int dst_offset_x; - unsigned int dst_offset_y = 0; - unsigned int i; - gdImagePtr dst; - int new_width, new_height; - gdRect bbox; - - if (bgColor < 0) { - return NULL; - } - - if (src->interpolation == NULL) { - gdImageSetInterpolationMethod(src, GD_DEFAULT); - } +static gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor) +{ + double _angle = ((double)(-degrees) / 180.0) * M_PI; + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const double H = (double)(src_h / 2); + const double W = (double)(src_w / 2); + const double cos_angle = cos(-_angle); + const double sin_angle = sin(-_angle); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + unsigned int new_width, new_height; + gdRect bbox; + + if (bgColor < 0) { + return NULL; + } gdRotatedImageSize(src, degrees, &bbox); new_width = bbox.width; new_height = bbox.height; - dst = gdImageCreateTrueColor(new_width, new_height); - if (!dst) { - return NULL; - } - dst->saveAlphaFlag = 1; - - for (i = 0; i < new_height; i++) { - unsigned int j; - dst_offset_x = 0; - for (j = 0; j < new_width; j++) { - gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2); - gdFixed f_j = gd_itofx((int)j - (int)new_width / 2); - gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; - gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; - long m = gd_fxtoi(f_m); - long n = gd_fxtoi(f_n); - - if (m < -1 || n < -1 || m >= src_h || n >= src_w ) { - dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; - } else { - dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, gd_fxtod(f_n), gd_fxtod(f_m), bgColor); - } - } - dst_offset_y++; - } - return dst; -} - -gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor) -{ - /* round to two decimals and keep the 100x multiplication to use it in the common square angles - case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for - slow animations. */ - const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100); - - if (bgcolor < 0) { - return NULL; - } - - /* impact perf a bit, but not that much. Implementation for palette - images can be done at a later point. - */ - if (src->trueColor == 0) { - if (bgcolor < gdMaxColors) { - bgcolor = gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]); - } - gdImagePaletteToTrueColor(src); - } - - /* no interpolation needed here */ - switch (angle_rounded) { - case 0: { - gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy); - if (dst == NULL) { - return NULL; - } - dst->transparent = src->transparent; - dst->saveAlphaFlag = 1; - dst->alphaBlendingFlag = gdEffectReplace; - - gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy); - return dst; - } - case -27000: - case 9000: - return gdImageRotate90(src, 0); - case -18000: - case 18000: - return gdImageRotate180(src, 0); - case -9000: - case 27000: - return gdImageRotate270(src, 0); - } - - if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) { - return NULL; - } - - switch (src->interpolation_id) { - case GD_NEAREST_NEIGHBOUR: - return gdImageRotateNearestNeighbour(src, angle, bgcolor); - break; - - case GD_BILINEAR_FIXED: - case GD_BICUBIC_FIXED: - default: - return gdImageRotateGeneric(src, angle, bgcolor); - } - return NULL; + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + const double dst_y = (double)((int)i - (int)new_height / 2); + const double dst_x = (double)((int)j - (int)new_width / 2); + const double src_y = dst_x * sin_angle + dst_y * cos_angle + H; + const double src_x = dst_x * cos_angle - dst_y * sin_angle + W; + long m = (long)floor(src_y); + long n = (long)floor(src_x); + + if (m < -1 || n < -1 || m >= src_h || n >= src_w) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } else { + dst->tpixels[dst_offset_y][dst_offset_x++] = + getPixelInterpolated(src, src_x, src_y, bgColor); + } + } + dst_offset_y++; + } + return dst; } /** - * Title: Affine transformation - **/ + * Function: gdImageRotateInterpolated + * + * Rotate an image + * + * Creates a new image, counter-clockwise rotated by the requested angle + * using the current . Non-square angles will add a + * border with bgcolor. + * + * Parameters: + * src - The source image. + * angle - The angle in degrees. + * bgcolor - The color to fill the added background with. + * + * Returns: + * The rotated image on success, NULL on failure. + * + * See also: + * - + */ +BGD_DECLARE(gdImagePtr) +gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor) +{ + /* round to two decimals and keep the 100x multiplication to use it in the + common square angles case later. Keep the two decimal precisions so + smaller rotation steps can be done, useful for slow animations, f.e. */ + const int angle_rounded = fmod((int)floorf(angle * 100), 360 * 100); + gdImagePtr src_tc = src; + int src_cloned = 0; + if (src == NULL || bgcolor < 0) { + return NULL; + } -/** - * Group: Transform - **/ + if (!gdImageTrueColor(src)) { + if (bgcolor < gdMaxColors) { + bgcolor = gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], + src->alpha[bgcolor]); + } + src_tc = gdImageClone(src); + gdImagePaletteToTrueColor(src_tc); + src_cloned = 1; + } - static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r) -{ - int c1x, c1y, c2x, c2y; - int x1,y1; + /* 0 && 90 degrees multiple rotation, 0 rotation simply clones the return + image and convert it to truecolor, as we must return truecolor image. */ + switch (angle_rounded) { + case 0: + return src_cloned ? src_tc : gdImageClone(src); + + case -27000: + case 9000: + if (src_cloned) + gdImageDestroy(src_tc); + return gdImageRotate90(src, 0); + + case -18000: + case 18000: + if (src_cloned) + gdImageDestroy(src_tc); + return gdImageRotate180(src, 0); + + case -9000: + case 27000: + if (src_cloned) + gdImageDestroy(src_tc); + return gdImageRotate270(src, 0); + } - gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y); - x1 = r->x + r->width - 1; - y1 = r->y + r->height - 1; - r->x = CLAMP(r->x, c1x, c2x); - r->y = CLAMP(r->y, c1y, c2y); - r->width = CLAMP(x1, c1x, c2x) - r->x + 1; - r->height = CLAMP(y1, c1y, c2y) - r->y + 1; + if (src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) { + if (src_cloned) + gdImageDestroy(src_tc); + return NULL; + } + + switch (src->interpolation_id) { + case GD_NEAREST_NEIGHBOUR: { + gdImagePtr res = gdImageRotateNearestNeighbour(src_tc, angle, bgcolor); + if (src_cloned) + gdImageDestroy(src_tc); + return res; + } + + case GD_BILINEAR_FIXED: + case GD_BICUBIC_FIXED: + default: { + gdImagePtr res = gdImageRotateGeneric(src_tc, angle, bgcolor); + if (src_cloned) + gdImageDestroy(src_tc); + return res; + } + } + return NULL; } -void gdDumpRect(const char *msg, gdRectPtr r) +/** + * Group: Affine Transformation + **/ + +static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r) { - printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height); + int c1x, c1y, c2x, c2y; + int x1, y1; + + gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y); + x1 = r->x + r->width - 1; + y1 = r->y + r->height - 1; + r->x = CLAMP(r->x, c1x, c2x); + r->y = CLAMP(r->y, c1y, c2y); + r->width = CLAMP(x1, c1x, c2x) - r->x + 1; + r->height = CLAMP(y1, c1y, c2y) - r->y + 1; } /** @@ -1856,58 +1726,135 @@ void gdDumpRect(const char *msg, gdRectPtr r) * Returns: * GD_TRUE if the affine is rectilinear or GD_FALSE */ -int gdTransformAffineGetImage(gdImagePtr *dst, - const gdImagePtr src, - gdRectPtr src_area, - const double affine[6]) -{ - int res; - double m[6]; - gdRect bbox; - gdRect area_full; - - if (src_area == NULL) { - area_full.x = 0; - area_full.y = 0; - area_full.width = gdImageSX(src); - area_full.height = gdImageSY(src); - src_area = &area_full; - } - - if (gdTransformAffineBoundingBox(src_area, affine, &bbox) != GD_TRUE) { - *dst = NULL; - return GD_FALSE; - } - - *dst = gdImageCreateTrueColor(bbox.width, bbox.height); - if (*dst == NULL) { - return GD_FALSE; - } - (*dst)->saveAlphaFlag = 1; - - if (!src->trueColor) { - gdImagePaletteToTrueColor(src); - } - - /* Translate to dst origin (0,0) */ - gdAffineTranslate(m, -bbox.x, -bbox.y); - gdAffineConcat(m, affine, m); - - gdImageAlphaBlending(*dst, 0); - - res = gdTransformAffineCopy(*dst, - 0,0, - src, - src_area, - m); - - if (res != GD_TRUE) { - gdImageDestroy(*dst); - dst = NULL; - return GD_FALSE; - } else { - return GD_TRUE; - } +BGD_DECLARE(int) +gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, + const double affine[6]) +{ + int res; + double m[6]; + gdRect bbox; + gdRect area_full; + + if (src_area == NULL) { + area_full.x = 0; + area_full.y = 0; + area_full.width = gdImageSX(src); + area_full.height = gdImageSY(src); + src_area = &area_full; + } + + if (gdTransformAffineBoundingBox(src_area, affine, &bbox) != GD_TRUE) { + *dst = NULL; + return GD_FALSE; + } + + *dst = gdImageCreateTrueColor(bbox.width, bbox.height); + if (*dst == NULL) { + return GD_FALSE; + } + (*dst)->saveAlphaFlag = 1; + gdImageAlphaBlending(*dst, 0); + /* The API has no background-color parameter, so uncovered output starts + * transparent. */ + gdImageFilledRectangle(*dst, 0, 0, bbox.width - 1, bbox.height - 1, + gdTrueColorAlpha(0, 0, 0, gdAlphaTransparent)); + + if (!src->trueColor) { + gdImagePaletteToTrueColor(src); + } + + /* Translate to dst origin (0,0) */ + gdAffineTranslate(m, -bbox.x, -bbox.y); + gdAffineConcat(m, affine, m); + + res = gdTransformAffineCopy(*dst, 0, 0, src, src_area, m); + + if (res != GD_TRUE) { + gdImageDestroy(*dst); + *dst = NULL; + return GD_FALSE; + } else { + return GD_TRUE; + } +} + +/** Function: getPixelRgbInterpolated + * get the index of the image's colors + * + * Parameters: + * im - Image to draw the transformed image + * tcolor - TrueColor + * + * Return: + * index of colors + */ +static int getPixelRgbInterpolated(gdImagePtr im, const int tcolor) +{ + unsigned char r, g, b, a; + int ct; + int i; + + b = (unsigned char)(tcolor); + g = (unsigned char)(tcolor >> 8); + r = (unsigned char)(tcolor >> 16); + a = (unsigned char)(tcolor >> 24); + + for (i = 0; i < im->colorsTotal; i++) { + if (im->red[i] == r && im->green[i] == g && im->blue[i] == b && im->alpha[i] == a) { + return i; + } + } + + ct = im->colorsTotal; + if (ct == gdMaxColors) { + return -1; + } + + im->colorsTotal++; + im->red[ct] = r; + im->green[ct] = g; + im->blue[ct] = b; + im->alpha[ct] = a; + im->open[ct] = 0; + + return ct; +} + +#define GD_AFFINE_KERNEL_SUPPORT 2.0 + +typedef struct { + gdImagePtr src; + gdRectPtr clip; + int bgColor; +} gdAffineSampleContext; + +static inline int gdAffineSampleIntersectsRegion(const double x, const double y, + const gdRectPtr region) +{ + const double left = (double)region->x - GD_AFFINE_KERNEL_SUPPORT; + const double top = (double)region->y - GD_AFFINE_KERNEL_SUPPORT; + const double right = (double)(region->x + region->width - 1) + GD_AFFINE_KERNEL_SUPPORT; + const double bottom = (double)(region->y + region->height - 1) + GD_AFFINE_KERNEL_SUPPORT; + + return x >= left && x <= right && y >= top && y <= bottom; +} + +static inline int gdAffineSample(const gdAffineSampleContext *ctx, const double x, const double y, + int *color) +{ + int c; + + if (!gdAffineSampleIntersectsRegion(x, y, ctx->clip)) { + return GD_FALSE; + } + + c = getPixelInterpolatedClipped(ctx->src, x, y, ctx->bgColor, ctx->clip); + if (gdTrueColorGetAlpha(c) == gdAlphaTransparent) { + return GD_FALSE; + } + + *color = c; + return GD_TRUE; } /** @@ -1925,116 +1872,120 @@ int gdTransformAffineGetImage(gdImagePtr *dst, * Returns: * GD_TRUE on success or GD_FALSE on failure */ -int gdTransformAffineCopy(gdImagePtr dst, - int dst_x, int dst_y, - const gdImagePtr src, - gdRectPtr src_region, - const double affine[6]) -{ - int c1x,c1y,c2x,c2y; - int backclip = 0; - int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2; - register int x, y, src_offset_x, src_offset_y; - double inv[6]; - gdPointF pt, src_pt; - gdRect bbox; - int end_x, end_y; - gdInterpolationMethod interpolation_id_bak = src->interpolation_id; - - /* These methods use special implementations */ - if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) { - interpolation_id_bak = src->interpolation_id; - - gdImageSetInterpolationMethod(src, GD_BICUBIC); - } - - - gdImageClipRectangle(src, src_region); - - if (src_region->x > 0 || src_region->y > 0 - || src_region->width < gdImageSX(src) - || src_region->height < gdImageSY(src)) { - backclip = 1; - - gdImageGetClip(src, &backup_clipx1, &backup_clipy1, - &backup_clipx2, &backup_clipy2); - - gdImageSetClip(src, src_region->x, src_region->y, - src_region->x + src_region->width - 1, - src_region->y + src_region->height - 1); - } - - if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) { - if (backclip) { - gdImageSetClip(src, backup_clipx1, backup_clipy1, - backup_clipx2, backup_clipy2); - } - gdImageSetInterpolationMethod(src, interpolation_id_bak); - return GD_FALSE; - } - - gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y); - - end_x = bbox.width + abs(bbox.x); - end_y = bbox.height + abs(bbox.y); - - /* Get inverse affine to let us work with destination -> source */ - if (gdAffineInvert(inv, affine) == GD_FALSE) { - gdImageSetInterpolationMethod(src, interpolation_id_bak); - return GD_FALSE; - } - - src_offset_x = src_region->x; - src_offset_y = src_region->y; - - if (dst->alphaBlendingFlag) { - for (y = bbox.y; y <= end_y; y++) { - pt.y = y + 0.5; - for (x = 0; x <= end_x; x++) { - pt.x = x + 0.5; - gdAffineApplyToPointF(&src_pt, &pt, inv); - gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0)); - } - } - } else { - for (y = 0; y <= end_y; y++) { - unsigned char *dst_p = NULL; - int *tdst_p = NULL; - - pt.y = y + 0.5 + bbox.y; - if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) { - continue; - } - if (dst->trueColor) { - tdst_p = dst->tpixels[dst_y + y] + dst_x; - } else { - dst_p = dst->pixels[dst_y + y] + dst_x; - } - - for (x = 0; x <= end_x; x++) { - pt.x = x + 0.5 + bbox.x; - gdAffineApplyToPointF(&src_pt, &pt, inv); - - if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) { - break; - } - if (dst->trueColor) { - *(tdst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1); - } else { - *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1); - } - } - } - } - - /* Restore clip if required */ - if (backclip) { - gdImageSetClip(src, backup_clipx1, backup_clipy1, - backup_clipx2, backup_clipy2); - } - - gdImageSetInterpolationMethod(src, interpolation_id_bak); - return GD_TRUE; +BGD_DECLARE(int) +gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, + gdRectPtr src_region, const double affine[6]) +{ + int backclip = 0; + int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2; + register int x, y, src_offset_x, src_offset_y; + double inv[6]; + gdPointF pt, src_pt; + gdRect bbox; + int end_x, end_y; + const int transparent = gdTrueColorAlpha(0, 0, 0, gdAlphaTransparent); + gdAffineSampleContext sample_ctx; + + gdImageClipRectangle(src, src_region); + + if (src_region->x > 0 || src_region->y > 0 || src_region->width < gdImageSX(src) || + src_region->height < gdImageSY(src)) { + backclip = 1; + + gdImageGetClip(src, &backup_clipx1, &backup_clipy1, &backup_clipx2, &backup_clipy2); + + gdImageSetClip(src, src_region->x, src_region->y, src_region->x + src_region->width - 1, + src_region->y + src_region->height - 1); + } + + if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) { + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2); + } + return GD_FALSE; + } + + end_x = bbox.width + abs(bbox.x); + end_y = bbox.height + abs(bbox.y); + + /* Get inverse affine to let us work with destination -> source */ + if (gdAffineInvert(inv, affine) == GD_FALSE) { + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2); + } + return GD_FALSE; + } + + src_offset_x = src_region->x; + src_offset_y = src_region->y; + sample_ctx.src = src; + sample_ctx.clip = src_region; + sample_ctx.bgColor = transparent; + + if (dst->alphaBlendingFlag) { + for (y = bbox.y; y <= end_y; y++) { + pt.y = y + 0.5; + for (x = bbox.x; x <= end_x; x++) { + pt.x = x + 0.5; + int c; + gdAffineApplyToPointF(&src_pt, &pt, inv); + const double sample_x = src_offset_x + src_pt.x; + const double sample_y = src_offset_y + src_pt.y; + if (!gdAffineSample(&sample_ctx, sample_x, sample_y, &c)) { + continue; + } + gdImageSetPixel(dst, dst_x + x, dst_y + y, c); + } + } + } else { + for (y = bbox.y; y <= end_y; y++) { + unsigned char *dst_p = NULL; + int *tdst_p = NULL; + + pt.y = y + 0.5; + if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) - 1)) { + continue; + } + if (dst->trueColor) { + tdst_p = dst->tpixels[dst_y + y]; + } else { + dst_p = dst->pixels[dst_y + y]; + } + + for (x = bbox.x; x <= end_x; x++) { + pt.x = x + 0.5; + gdAffineApplyToPointF(&src_pt, &pt, inv); + + if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) { + break; + } + { + const double sample_x = src_offset_x + src_pt.x; + const double sample_y = src_offset_y + src_pt.y; + int c; + + if (!gdAffineSample(&sample_ctx, sample_x, sample_y, &c)) { + continue; + } + if (dst->trueColor) { + *(tdst_p + dst_x + x) = c; + } else { + const int pc = getPixelRgbInterpolated(dst, c); + if (pc >= 0) { + *(dst_p + dst_x + x) = pc; + } + } + } + } + } + } + + /* Restore clip if required */ + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2); + } + + return GD_TRUE; } /** @@ -2050,147 +2001,195 @@ int gdTransformAffineCopy(gdImagePtr dst, * Returns: * GD_TRUE if the affine is rectilinear or GD_FALSE */ -int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) -{ - gdPointF extent[4], min, max, point; - double width, height; - int bbox_x, bbox_y, bbox_width, bbox_height; - int i; - - extent[0].x=0.0; - extent[0].y=0.0; - extent[1].x=(double) src->width; - extent[1].y=0.0; - extent[2].x=(double) src->width; - extent[2].y=(double) src->height; - extent[3].x=0.0; - extent[3].y=(double) src->height; - - for (i=0; i < 4; i++) { - point=extent[i]; - if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) { - return GD_FALSE; - } - } - min=extent[0]; - max=extent[0]; - - for (i=1; i < 4; i++) { - if (min.x > extent[i].x) - min.x=extent[i].x; - if (min.y > extent[i].y) - min.y=extent[i].y; - if (max.x < extent[i].x) - max.x=extent[i].x; - if (max.y < extent[i].y) - max.y=extent[i].y; - } - width = floor(max.x - min.x); - height = floor(max.y - min.y); - if (!isfinite(min.x) || !isfinite(min.y) || !isfinite(width) || !isfinite(height) - || min.x <= INT_MIN || min.x > INT_MAX - || min.y <= INT_MIN || min.y > INT_MAX - || width < 1.0 || width > INT_MAX - || height < 0.0 || height > INT_MAX) { - return GD_FALSE; - } - bbox_x = (int) min.x; - bbox_y = (int) min.y; - bbox_width = (int) width - 1; - bbox_height = (int) height; - if ((bbox_x < 0 && bbox_width > INT_MAX + bbox_x) - || (bbox_x > 0 && bbox_width > INT_MAX - bbox_x) - || (bbox_y < 0 && bbox_height > INT_MAX + bbox_y) - || (bbox_y > 0 && bbox_height > INT_MAX - bbox_y)) { - return GD_FALSE; - } - bbox->x = bbox_x; - bbox->y = bbox_y; - bbox->width = bbox_width; - bbox->height = bbox_height; - return GD_TRUE; +BGD_DECLARE(int) +gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) +{ + gdPointF extent[4], min, max, point; + double bbox_x_d, bbox_y_d, bbox_width_d, bbox_height_d; + int bbox_x, bbox_y, bbox_width, bbox_height; + int i; + + extent[0].x = 0.0; + extent[0].y = 0.0; + extent[1].x = (double)src->width; + extent[1].y = 0.0; + extent[2].x = (double)src->width; + extent[2].y = (double)src->height; + extent[3].x = 0.0; + extent[3].y = (double)src->height; + + for (i = 0; i < 4; i++) { + point = extent[i]; + if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) { + return GD_FALSE; + } + } + min = extent[0]; + max = extent[0]; + + for (i = 1; i < 4; i++) { + if (min.x > extent[i].x) + min.x = extent[i].x; + if (min.y > extent[i].y) + min.y = extent[i].y; + if (max.x < extent[i].x) + max.x = extent[i].x; + if (max.y < extent[i].y) + max.y = extent[i].y; + } + bbox_x_d = floor(min.x); + bbox_y_d = floor(min.y); + bbox_width_d = ceil(max.x) - bbox_x_d; + bbox_height_d = ceil(max.y) - bbox_y_d; + if (!isfinite(bbox_x_d) || !isfinite(bbox_y_d) || !isfinite(bbox_width_d) || + !isfinite(bbox_height_d) || bbox_x_d < INT_MIN || bbox_x_d > INT_MAX || + bbox_y_d < INT_MIN || bbox_y_d > INT_MAX || bbox_width_d < 0.0 || bbox_width_d > INT_MAX || + bbox_height_d < 0.0 || bbox_height_d > INT_MAX) { + return GD_FALSE; + } + bbox_x = (int)bbox_x_d; + bbox_y = (int)bbox_y_d; + bbox_width = (int)bbox_width_d; + bbox_height = (int)bbox_height_d; + if ((bbox_x < 0 && bbox_width > INT_MAX + bbox_x) || + (bbox_x > 0 && bbox_width > INT_MAX - bbox_x) || + (bbox_y < 0 && bbox_height > INT_MAX + bbox_y) || + (bbox_y > 0 && bbox_height > INT_MAX - bbox_y)) { + return GD_FALSE; + } + bbox->x = bbox_x; + bbox->y = bbox_y; + bbox->width = bbox_width; + bbox->height = bbox_height; + + return GD_TRUE; } -int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id) -{ - if (im == NULL || id < 0 || id > GD_METHOD_COUNT) { - return 0; - } - - switch (id) { - case GD_NEAREST_NEIGHBOUR: - case GD_WEIGHTED4: - im->interpolation = NULL; - break; - - /* generic versions*/ - /* GD_BILINEAR_FIXED and GD_BICUBIC_FIXED are kept for BC reasons */ - case GD_BILINEAR_FIXED: - case GD_LINEAR: - im->interpolation = filter_linear; - break; - case GD_BELL: - im->interpolation = filter_bell; - break; - case GD_BESSEL: - im->interpolation = filter_bessel; - break; - case GD_BICUBIC_FIXED: - case GD_BICUBIC: - im->interpolation = filter_bicubic; - break; - case GD_BLACKMAN: - im->interpolation = filter_blackman; - break; - case GD_BOX: - im->interpolation = filter_box; - break; - case GD_BSPLINE: - im->interpolation = filter_bspline; - break; - case GD_CATMULLROM: - im->interpolation = filter_catmullrom; - break; - case GD_GAUSSIAN: - im->interpolation = filter_gaussian; - break; - case GD_GENERALIZED_CUBIC: - im->interpolation = filter_generalized_cubic; - break; - case GD_HERMITE: - im->interpolation = filter_hermite; - break; - case GD_HAMMING: - im->interpolation = filter_hamming; - break; - case GD_HANNING: - im->interpolation = filter_hanning; - break; - case GD_MITCHELL: - im->interpolation = filter_mitchell; - break; - case GD_POWER: - im->interpolation = filter_power; - break; - case GD_QUADRATIC: - im->interpolation = filter_quadratic; - break; - case GD_SINC: - im->interpolation = filter_sinc; - break; - case GD_TRIANGLE: - im->interpolation = filter_triangle; - break; - case GD_DEFAULT: - id = GD_LINEAR; - im->interpolation = filter_linear; - break; - - default: - return 0; - } - im->interpolation_id = id; - return 1; +/** + * Group: Interpolation Method + */ + +/** + * Function: gdImageSetInterpolationMethod + * + * Set the interpolation method for subsequent operations + * + * Parameters: + * im - The image. + * id - The interpolation method. + * + * Returns: + * Non-zero on success, zero on failure. + * + * See also: + * - + * - + */ +BGD_DECLARE(int) +gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id) +{ + if (im == NULL || (uintmax_t)id > GD_METHOD_COUNT) { + return 0; + } + + switch (id) { + case GD_NEAREST_NEIGHBOUR: + case GD_WEIGHTED4: + im->interpolation = NULL; + break; + + /* generic versions*/ + /* GD_BILINEAR_FIXED and GD_BICUBIC_FIXED are kept for BC reasons */ + case GD_BILINEAR_FIXED: + case GD_LINEAR: + im->interpolation = filter_linear; + break; + case GD_BELL: + im->interpolation = filter_bell; + break; + case GD_BESSEL: + im->interpolation = filter_bessel; + break; + case GD_BICUBIC_FIXED: + case GD_BICUBIC: + /* no interpolation as gdImageScale calls a dedicated function */ + im->interpolation = NULL; + break; + case GD_BLACKMAN: + im->interpolation = filter_blackman; + break; + case GD_BOX: + im->interpolation = filter_box; + break; + case GD_BSPLINE: + im->interpolation = filter_bspline; + break; + case GD_CATMULLROM: + im->interpolation = filter_catmullrom; + break; + case GD_GAUSSIAN: + im->interpolation = filter_gaussian; + break; + case GD_GENERALIZED_CUBIC: + im->interpolation = filter_generalized_cubic; + break; + case GD_HERMITE: + im->interpolation = filter_hermite; + break; + case GD_HAMMING: + im->interpolation = filter_hamming; + break; + case GD_HANNING: + im->interpolation = filter_hanning; + break; + case GD_MITCHELL: + im->interpolation = filter_mitchell; + break; + case GD_POWER: + im->interpolation = filter_power; + break; + case GD_QUADRATIC: + im->interpolation = filter_quadratic; + break; + case GD_SINC: + im->interpolation = filter_sinc; + break; + case GD_TRIANGLE: + im->interpolation = filter_triangle; + break; + case GD_LANCZOS3: + im->interpolation = filter_lanczos3; + break; + case GD_LANCZOS8: + im->interpolation = filter_lanczos8; + break; + case GD_BLACKMAN_BESSEL: + im->interpolation = filter_blackman_bessel; + break; + case GD_BLACKMAN_SINC: + im->interpolation = filter_blackman_sinc; + break; + case GD_QUADRATIC_BSPLINE: + im->interpolation = filter_quadratic_bspline; + break; + case GD_CUBIC_SPLINE: + im->interpolation = filter_cubic_spline; + break; + case GD_COSINE: + im->interpolation = filter_cosine; + break; + case GD_WELSH: + im->interpolation = filter_welsh; + break; + case GD_DEFAULT: + id = GD_LINEAR; + im->interpolation = filter_linear; + break; + default: + return 0; + } + im->interpolation_id = id; + return 1; } /** @@ -2211,11 +2210,9 @@ int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id) * - * - */ -gdInterpolationMethod gdImageGetInterpolationMethod(gdImagePtr im) -{ - return im->interpolation_id; -} +BGD_DECLARE(gdInterpolationMethod) +gdImageGetInterpolationMethod(gdImagePtr im) { return im->interpolation_id; } #ifdef _MSC_VER -# pragma optimize("", on) +#pragma optimize("", on) #endif diff --git a/ext/gd/libgd/gd_io.c b/ext/gd/libgd/gd_io.c index 1ca822fc9b63..b443ae8856eb 100644 --- a/ext/gd/libgd/gd_io.c +++ b/ext/gd/libgd/gd_io.c @@ -1,205 +1,201 @@ /* - * io.c - * - * Implements the simple I/O 'helper' routines. - * - * Not really essential, but these routines were used extensively in GD, - * so they were moved here. They also make IOCtx calls look better... - * - * Written (or, at least, moved) 1999, Philip Warner. - * + * io.c + * + * Implements the simple I/O 'helper' routines. + * + * Not really essential, but these routines were used extensively in GD, + * so they were moved here. They also make IOCtx calls look better... + * + * Written (or, at least, moved) 1999, Philip Warner. */ +#include "gd.h" #include -#include +#include #include -#include "gd.h" +#include /* Use this for commenting out debug-print statements. */ /* Just use the first '#define' to allow all the prints... */ /*#define IO_DBG(s) (s) */ #define IO_DBG(s) +#define GD_IO_EOF_CHK(r) \ + if (r == EOF) { \ + return 0; \ + } -#define GD_IO_EOF_CHK(r) \ - if (r == EOF) { \ - return 0; \ - } \ +void gdPutC(const unsigned char c, gdIOCtx *ctx) { (ctx->putC)(ctx, c); } -/* - * Write out a word to the I/O context pointer - */ -void Putword (int w, gdIOCtx * ctx) +void gdPutWord(int w, gdIOCtx *ctx) { - unsigned char buf[2]; - - buf[0] = w & 0xff; - buf[1] = (w / 256) & 0xff; - (ctx->putBuf) (ctx, (char *) buf, 2); + IO_DBG(printf("Putting word...\n")); + (ctx->putC)(ctx, (unsigned char)(w >> 8)); + (ctx->putC)(ctx, (unsigned char)(w & 0xFF)); + IO_DBG(printf("put.\n")); } -void Putchar (int c, gdIOCtx * ctx) +void gdPutInt(int w, gdIOCtx *ctx) { - (ctx->putC) (ctx, c & 0xff); + IO_DBG(printf("Putting int...\n")); + (ctx->putC)(ctx, (unsigned char)(w >> 24)); + (ctx->putC)(ctx, (unsigned char)((w >> 16) & 0xFF)); + (ctx->putC)(ctx, (unsigned char)((w >> 8) & 0xFF)); + (ctx->putC)(ctx, (unsigned char)(w & 0xFF)); + IO_DBG(printf("put.\n")); } -void gdPutC (const unsigned char c, gdIOCtx * ctx) -{ - (ctx->putC) (ctx, c); -} +int gdGetC(gdIOCtx *ctx) { return ((ctx->getC)(ctx)); } -void gdPutWord (int w, gdIOCtx * ctx) +int gdGetByte(int *result, gdIOCtx *ctx) { - IO_DBG (gd_error("Putting word...")); - (ctx->putC) (ctx, (unsigned char) (w >> 8)); - (ctx->putC) (ctx, (unsigned char) (w & 0xFF)); - IO_DBG (gd_error("put.")); -} + int r; -void gdPutInt (int w, gdIOCtx * ctx) -{ - IO_DBG (gd_error("Putting int...")); - (ctx->putC) (ctx, (unsigned char) (w >> 24)); - (ctx->putC) (ctx, (unsigned char) ((w >> 16) & 0xFF)); - (ctx->putC) (ctx, (unsigned char) ((w >> 8) & 0xFF)); - (ctx->putC) (ctx, (unsigned char) (w & 0xFF)); - IO_DBG (gd_error("put.")); -} + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } -int gdGetC (gdIOCtx * ctx) -{ - return ((ctx->getC) (ctx)); -} + *result = r; -int gdGetByte (int *result, gdIOCtx * ctx) -{ - int r; - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result = r; - return 1; + return 1; } -int gdGetWord (int *result, gdIOCtx * ctx) +int gdGetWord(int *result, gdIOCtx *ctx) { - int r; - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result = r << 8; - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result += r; - return 1; -} + int r; + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + *result = r << 8; + + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + *result += r; + + return 1; +} int gdGetWordLSB(signed short int *result, gdIOCtx *ctx) { - int high = 0, low = 0; - low = (ctx->getC) (ctx); - if (low == EOF) { - return 0; - } - - high = (ctx->getC) (ctx); - if (high == EOF) { - return 0; - } - - if (result) { - *result = (high << 8) | low; - } - - return 1; + int high = 0, low = 0; + low = (ctx->getC)(ctx); + if (low == EOF) { + return 0; + } + + high = (ctx->getC)(ctx); + if (high == EOF) { + return 0; + } + + if (result) { + *result = (high << 8) | low; + } + + return 1; } -int gdGetInt (int *result, gdIOCtx * ctx) +int gdGetInt(int *result, gdIOCtx *ctx) { - unsigned int r; - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result = r << 24; - - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result += r << 16; - - r = (ctx->getC) (ctx); - if (r == EOF) { - return 0; - } - *result += r << 8; - - r = (ctx->getC) (ctx); - GD_IO_EOF_CHK(r); - *result += r; - - return 1; + int r; + uint32_t value; + + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + value = (uint32_t)r << 24; + + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + value |= (uint32_t)r << 16; + + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + value |= (uint32_t)r << 8; + + r = (ctx->getC)(ctx); + if (r == EOF) { + return 0; + } + + value |= (uint32_t)r; + *result = (int32_t)value; + + return 1; } int gdGetIntLSB(signed int *result, gdIOCtx *ctx) { - unsigned int c; - unsigned int r = 0; - - c = (ctx->getC) (ctx); - if (c == EOF) { - return 0; - } - r |= (c << 24); - r >>= 8; - - c = (ctx->getC) (ctx); - if (c == EOF) { - return 0; - } - r |= (c << 24); - r >>= 8; - - c = (ctx->getC) (ctx); - if (c == EOF) { - return 0; - } - r |= (c << 24); - r >>= 8; - - c = (ctx->getC) (ctx); - if (c == EOF) { - return 0; - } - r |= (c << 24); - - if (result) { - *result = (signed int)r; - } - - return 1; + int c; + uint32_t r; + + c = (ctx->getC)(ctx); + if (c == EOF) { + return 0; + } + r = (uint32_t)c; + + c = (ctx->getC)(ctx); + if (c == EOF) { + return 0; + } + r |= (uint32_t)c << 8; + + c = (ctx->getC)(ctx); + if (c == EOF) { + return 0; + } + r |= (uint32_t)c << 16; + + c = (ctx->getC)(ctx); + if (c == EOF) { + return 0; + } + r |= (uint32_t)c << 24; + + if (result) { + *result = (int32_t)r; + } + + return 1; } -int gdPutBuf (const void *buf, int size, gdIOCtx * ctx) +int gdPutBuf(const void *buf, int size, gdIOCtx *ctx) { - IO_DBG (gd_error("Putting buf...")); - return (ctx->putBuf) (ctx, buf, size); - IO_DBG (gd_error("put.")); + IO_DBG(printf("Putting buf...\n")); + return (ctx->putBuf)(ctx, buf, size); + IO_DBG(printf("put.\n")); } -int gdGetBuf (void *buf, int size, gdIOCtx * ctx) -{ - return (ctx->getBuf) (ctx, buf, size); -} +int gdGetBuf(void *buf, int size, gdIOCtx *ctx) { return (ctx->getBuf)(ctx, buf, size); } -int gdSeek (gdIOCtx * ctx, const int pos) +int gdSeek(gdIOCtx *ctx, const int pos) { - IO_DBG (gd_error("Seeking...")); - return ((ctx->seek) (ctx, pos)); - IO_DBG (gd_error("Done.")); + IO_DBG(printf("Seeking...\n")); + return ((ctx->seek)(ctx, pos)); + IO_DBG(printf("Done.\n")); } -long gdTell (gdIOCtx * ctx) +long gdTell(gdIOCtx *ctx) { - IO_DBG (gd_error("Telling...")); - return ((ctx->tell) (ctx)); - IO_DBG (gd_error ("told.")); + IO_DBG(printf("Telling...\n")); + return ((ctx->tell)(ctx)); + IO_DBG(printf("told.\n")); } diff --git a/ext/gd/libgd/gd_io.h b/ext/gd/libgd/gd_io.h index 00f3a88ed620..7a11748de5dc 100644 --- a/ext/gd/libgd/gd_io.h +++ b/ext/gd/libgd/gd_io.h @@ -8,32 +8,32 @@ #endif typedef struct gdIOCtx { - int (*getC)(struct gdIOCtx*); - int (*getBuf)(struct gdIOCtx*, void*, int); + int (*getC)(struct gdIOCtx *); + int (*getBuf)(struct gdIOCtx *, void *, int); - void (*putC)(struct gdIOCtx*, int); - int (*putBuf)(struct gdIOCtx*, const void*, int); + void (*putC)(struct gdIOCtx *, int); + int (*putBuf)(struct gdIOCtx *, const void *, int); - int (*seek)(struct gdIOCtx*, const int); - long (*tell)(struct gdIOCtx*); + int (*seek)(struct gdIOCtx *, const int); + long (*tell)(struct gdIOCtx *); - void (*gd_free)(struct gdIOCtx*); + void (*gd_free)(struct gdIOCtx *); - void *data; + void *data; } gdIOCtx; -typedef struct gdIOCtx *gdIOCtxPtr; +typedef struct gdIOCtx *gdIOCtxPtr; void Putword(int w, gdIOCtx *ctx); void Putchar(int c, gdIOCtx *ctx); void gdPutC(const unsigned char c, gdIOCtx *ctx); -int gdPutBuf(const void *, int, gdIOCtx*); +int gdPutBuf(const void *, int, gdIOCtx *); void gdPutWord(int w, gdIOCtx *ctx); void gdPutInt(int w, gdIOCtx *ctx); int gdGetC(gdIOCtx *ctx); -int gdGetBuf(void *, int, gdIOCtx*); +int gdGetBuf(void *, int, gdIOCtx *); int gdGetByte(int *result, gdIOCtx *ctx); int gdGetWord(int *result, gdIOCtx *ctx); int gdGetWordLSB(signed short int *result, gdIOCtx *ctx); diff --git a/ext/gd/libgd/gd_io_dp.c b/ext/gd/libgd/gd_io_dp.c index 81b988157fb2..45699e0f7a3b 100644 --- a/ext/gd/libgd/gd_io_dp.c +++ b/ext/gd/libgd/gd_io_dp.c @@ -1,363 +1,426 @@ /* - * io_dp.c - * - * Implements the dynamic pointer interface. - * - * Based on GD.pm code by Lincoln Stein for interfacing to libgd. - * Added support for reading as well as support for 'tell' and 'seek'. - * - * As will all I/O modules, most functions are for local use only (called - * via function pointers in the I/O context). - * - * gdDPExtractData is the exception to this: it will return the pointer to - * the internal data, and reset the internal storage. - * - * Written/Modified 1999, Philip Warner. - * + * io_dp.c + * + * Implements the dynamic pointer interface. + * + * Based on GD.pm code by Lincoln Stein for interfacing to libgd. + * Added support for reading as well as support for 'tell' and 'seek'. + * + * As with all I/O modules, most functions are for local use only (called + * via function pointers in the I/O context). + * + * gdDPExtractData is the exception to this: it will return the pointer to + * the internal data, and reset the internal storage. + * + * Written/Modified 1999, Philip Warner. */ -#include -#include -#include #include "gd.h" #include "gdhelpers.h" +#include +#include +#include #define TRUE 1 #define FALSE 0 /* this is used for creating images in main memory */ -typedef struct dpStruct -{ - void *data; - int logicalSize; - int realSize; - int dataGood; - int pos; - int freeOK; +typedef struct dpStruct { + void *data; + int logicalSize; + int realSize; + int dataGood; + int pos; + int freeOK; } dynamicPtr; -typedef struct dpIOCtx -{ - gdIOCtx ctx; - dynamicPtr *dp; +typedef struct dpIOCtx { + gdIOCtx ctx; + dynamicPtr *dp; } dpIOCtx; typedef struct dpIOCtx *dpIOCtxPtr; /* these functions operate on in-memory dynamic pointers */ -static int allocDynamic (dynamicPtr * dp, int initialSize, void *data); -static int appendDynamic (dynamicPtr * dp, const void *src, int size); -static int gdReallocDynamic (dynamicPtr * dp, int required); -static int trimDynamic (dynamicPtr * dp); -static void gdFreeDynamicCtx (struct gdIOCtx *ctx); -static dynamicPtr *newDynamic (int initialSize, void *data, int freeOKFlag); +static int allocDynamic(dynamicPtr *dp, int initialSize, void *data); +static int appendDynamic(dynamicPtr *dp, const void *src, int size); +static int gdReallocDynamic(dynamicPtr *dp, int required); +static int trimDynamic(dynamicPtr *dp); +static void gdFreeDynamicCtx(gdIOCtxPtr ctx); +static dynamicPtr *newDynamic(int initialSize, void *data, int freeOKFlag); -static int dynamicPutbuf (struct gdIOCtx *, const void *, int); -static void dynamicPutchar (struct gdIOCtx *, int a); +static int dynamicPutbuf(gdIOCtxPtr, const void *, int); +static void dynamicPutchar(gdIOCtxPtr, int a); -static int dynamicGetbuf (gdIOCtxPtr ctx, void *buf, int len); -static int dynamicGetchar (gdIOCtxPtr ctx); +static int dynamicGetbuf(gdIOCtxPtr ctx, void *buf, int len); +static int dynamicGetchar(gdIOCtxPtr ctx); -static int dynamicSeek (struct gdIOCtx *, const int); -static long dynamicTell (struct gdIOCtx *); +static int dynamicSeek(gdIOCtxPtr, const int); +static long dynamicTell(gdIOCtxPtr); -/* return data as a dynamic pointer */ -gdIOCtx * gdNewDynamicCtx (int initialSize, void *data) +/* + Function: gdNewDynamicCtx + + Return data as a dynamic pointer. +*/ +BGD_DECLARE(gdIOCtx *) gdNewDynamicCtx(int initialSize, void *data) { - return gdNewDynamicCtxEx(initialSize, data, 1); + /* 2.0.23: Phil Moore: 'return' keyword was missing! */ + return gdNewDynamicCtxEx(initialSize, data, 1); } -gdIOCtx * gdNewDynamicCtxEx (int initialSize, void *data, int freeOKFlag) +/* + Function: gdNewDynamicCtxEx +*/ +BGD_DECLARE(gdIOCtx *) +gdNewDynamicCtxEx(int initialSize, void *data, int freeOKFlag) { - dpIOCtx *ctx; - dynamicPtr *dp; + dpIOCtx *ctx; + dynamicPtr *dp; - ctx = (dpIOCtx *) gdMalloc (sizeof (dpIOCtx)); + ctx = (dpIOCtx *)gdMalloc(sizeof(dpIOCtx)); + if (ctx == NULL) { + return NULL; + } - dp = newDynamic(initialSize, data, freeOKFlag); + dp = newDynamic(initialSize, data, freeOKFlag); + if (!dp) { + gdFree(ctx); + return NULL; + }; - ctx->dp = dp; + ctx->dp = dp; - ctx->ctx.getC = dynamicGetchar; - ctx->ctx.putC = dynamicPutchar; + ctx->ctx.getC = dynamicGetchar; + ctx->ctx.putC = dynamicPutchar; - ctx->ctx.getBuf = dynamicGetbuf; - ctx->ctx.putBuf = dynamicPutbuf; + ctx->ctx.getBuf = dynamicGetbuf; + ctx->ctx.putBuf = dynamicPutbuf; - ctx->ctx.seek = dynamicSeek; - ctx->ctx.tell = dynamicTell; + ctx->ctx.seek = dynamicSeek; + ctx->ctx.tell = dynamicTell; - ctx->ctx.gd_free = gdFreeDynamicCtx; + ctx->ctx.gd_free = gdFreeDynamicCtx; - return (gdIOCtx *) ctx; + return (gdIOCtx *)ctx; } -void * gdDPExtractData (struct gdIOCtx *ctx, int *size) +/* + Function: gdDPExtractData +*/ +BGD_DECLARE(void *) gdDPExtractData(gdIOCtxPtr ctx, int *size) { - dynamicPtr *dp; - dpIOCtx *dctx; - void *data; - - dctx = (dpIOCtx *) ctx; - dp = dctx->dp; - - /* clean up the data block and return it */ - if (dp->dataGood) { - trimDynamic (dp); - *size = dp->logicalSize; - data = dp->data; - } else { - *size = 0; - data = NULL; - if (dp->data != NULL && dp->freeOK) { - gdFree(dp->data); - } - } - - dp->data = NULL; - dp->realSize = 0; - dp->logicalSize = 0; - - return data; + dynamicPtr *dp; + dpIOCtx *dctx; + void *data; + + dctx = (dpIOCtx *)ctx; + dp = dctx->dp; + + /* clean up the data block and return it */ + if (dp->dataGood) { + trimDynamic(dp); + *size = dp->logicalSize; + data = dp->data; + } else { + *size = 0; + data = NULL; + /* 2.0.21: never free memory we don't own */ + if ((dp->data != NULL) && (dp->freeOK)) { + gdFree(dp->data); + } + } + + dp->data = NULL; + dp->realSize = 0; + dp->logicalSize = 0; + + return data; } -static void gdFreeDynamicCtx (struct gdIOCtx *ctx) +static void gdFreeDynamicCtx(gdIOCtxPtr ctx) { - dynamicPtr *dp; - dpIOCtx *dctx; + dynamicPtr *dp; + dpIOCtx *dctx; - dctx = (dpIOCtx *) ctx; - dp = dctx->dp; + dctx = (dpIOCtx *)ctx; + dp = dctx->dp; - gdFree(ctx); + gdFree(ctx); - dp->realSize = 0; - dp->logicalSize = 0; + /* clean up the data block and return it */ + /* 2.0.21: never free memory we don't own */ + if ((dp->data != NULL) && (dp->freeOK)) { + gdFree(dp->data); + dp->data = NULL; + } - gdFree(dp); + dp->realSize = 0; + dp->logicalSize = 0; + + gdFree(dp); } -static long dynamicTell (struct gdIOCtx *ctx) +static long dynamicTell(gdIOCtxPtr ctx) { - dpIOCtx *dctx; - - dctx = (dpIOCtx *) ctx; + dpIOCtx *dctx; - return (dctx->dp->pos); + dctx = (dpIOCtx *)ctx; + return (dctx->dp->pos); } -static int dynamicSeek (struct gdIOCtx *ctx, const int pos) +static int dynamicSeek(gdIOCtxPtr ctx, const int pos) { - int bytesNeeded; - dynamicPtr *dp; - dpIOCtx *dctx; - - if (pos < 0) { - return FALSE; - } - dctx = (dpIOCtx *) ctx; - dp = dctx->dp; - - if (!dp->dataGood) { - return FALSE; - } - - bytesNeeded = pos; - if (bytesNeeded > dp->realSize) { - /* 2.0.21 */ - if (!dp->freeOK) { - return FALSE; - } - gdReallocDynamic (dp, dp->realSize * 2); - } - - /* if we get here, we can be sure that we have enough bytes to copy safely */ - - /* Extend the logical size if we seek beyond EOF. */ - if (pos > dp->logicalSize) { - dp->logicalSize = pos; - } - - dp->pos = pos; - - return TRUE; + int bytesNeeded; + dynamicPtr *dp; + dpIOCtx *dctx; + + if (pos < 0) { + return FALSE; + } + dctx = (dpIOCtx *)ctx; + dp = dctx->dp; + + if (!dp->dataGood) { + return FALSE; + } + + bytesNeeded = pos; + if (bytesNeeded > dp->realSize) { + /* 2.0.21 */ + if (!dp->freeOK) { + return FALSE; + } + + if (overflow2(dp->realSize, 2)) { + return FALSE; + } + + if (!gdReallocDynamic(dp, dp->realSize * 2)) { + dp->dataGood = FALSE; + return FALSE; + } + } + + /* if we get here, we can be sure that we have enough bytes + * to copy safely */ + + /* Extend the logical size if we seek beyond EOF. */ + if (pos > dp->logicalSize) { + dp->logicalSize = pos; + }; + + dp->pos = pos; + + return TRUE; } /* return data as a dynamic pointer */ -static dynamicPtr * newDynamic (int initialSize, void *data, int freeOKFlag) +static dynamicPtr *newDynamic(int initialSize, void *data, int freeOKFlag) { - dynamicPtr *dp; - dp = (dynamicPtr *) gdMalloc (sizeof (dynamicPtr)); + dynamicPtr *dp; - allocDynamic (dp, initialSize, data); + dp = (dynamicPtr *)gdMalloc(sizeof(dynamicPtr)); + if (dp == NULL) { + return NULL; + } + + if (!allocDynamic(dp, initialSize, data)) { + gdFree(dp); + return NULL; + } - dp->pos = 0; - dp->freeOK = freeOKFlag; + dp->pos = 0; + dp->freeOK = freeOKFlag; - return dp; + return dp; } -static int -dynamicPutbuf (struct gdIOCtx *ctx, const void *buf, int size) +static int dynamicPutbuf(gdIOCtxPtr ctx, const void *buf, int size) { - dpIOCtx *dctx; - dctx = (dpIOCtx *) ctx; + dpIOCtx *dctx; + dctx = (dpIOCtx *)ctx; - appendDynamic (dctx->dp, buf, size); + appendDynamic(dctx->dp, buf, size); - if (dctx->dp->dataGood) - { - return size; - } - else - { - return -1; + if (dctx->dp->dataGood) { + return size; + } else { + return -1; }; - } -static void dynamicPutchar (struct gdIOCtx *ctx, int a) +static void dynamicPutchar(gdIOCtxPtr ctx, int a) { - unsigned char b; - dpIOCtxPtr dctx; + unsigned char b; + dpIOCtxPtr dctx; - b = a; - dctx = (dpIOCtxPtr) ctx; + b = a; + dctx = (dpIOCtxPtr)ctx; - appendDynamic(dctx->dp, &b, 1); + appendDynamic(dctx->dp, &b, 1); } -static int dynamicGetbuf (gdIOCtxPtr ctx, void *buf, int len) +/* returns the number of bytes actually read; 0 on EOF and error */ +static int dynamicGetbuf(gdIOCtxPtr ctx, void *buf, int len) { - int rlen, remain; - dpIOCtxPtr dctx; - dynamicPtr *dp; - - dctx = (dpIOCtxPtr) ctx; - dp = dctx->dp; - - remain = dp->logicalSize - dp->pos; - if (remain >= len) { - rlen = len; - } else { - if (remain <= 0) { - return EOF; - } - rlen = remain; - } - - memcpy(buf, (void *) ((char *) dp->data + dp->pos), rlen); - dp->pos += rlen; - - return rlen; + int rlen, remain; + dpIOCtxPtr dctx; + dynamicPtr *dp; + + dctx = (dpIOCtxPtr)ctx; + dp = dctx->dp; + + if (dp->pos < 0 || dp->pos >= dp->realSize) { + return 0; + } + + remain = dp->logicalSize - dp->pos; + if (remain >= len) { + rlen = len; + } else { + if (remain <= 0) { + return 0; + } + + rlen = remain; + } + + if (dp->pos + rlen > dp->realSize) { + rlen = dp->realSize - dp->pos; + } + + if (rlen < 0) { + return 0; + } + + memcpy(buf, (void *)((char *)dp->data + dp->pos), rlen); + dp->pos += rlen; + + return rlen; } -static int dynamicGetchar (gdIOCtxPtr ctx) +static int dynamicGetchar(gdIOCtxPtr ctx) { - unsigned char b; - int rv; - - rv = dynamicGetbuf (ctx, &b, 1); - if (rv != 1) { - return EOF; - } else { - return b; /* (b & 0xff); */ - } + unsigned char b; + int rv; + + rv = dynamicGetbuf(ctx, &b, 1); + + if (rv != 1) { + return EOF; + } else { + return b; /* (b & 0xff); */ + } } /* ********************************************************************* - * InitDynamic - Return a dynamically resizable void* - * - * ********************************************************************* - */ -static int -allocDynamic (dynamicPtr * dp, int initialSize, void *data) + **********************************************************************/ +static int allocDynamic(dynamicPtr *dp, int initialSize, void *data) { + if (data == NULL) { + dp->logicalSize = 0; + dp->dataGood = FALSE; + dp->data = gdMalloc(initialSize); + } else { + dp->logicalSize = initialSize; + dp->dataGood = TRUE; + dp->data = data; + } - if (data == NULL) { - dp->logicalSize = 0; - dp->dataGood = FALSE; - dp->data = gdMalloc(initialSize); - } else { - dp->logicalSize = initialSize; - dp->dataGood = TRUE; - dp->data = data; - } - - dp->realSize = initialSize; - dp->dataGood = TRUE; - dp->pos = 0; - - return TRUE; + if (dp->data != NULL) { + dp->realSize = initialSize; + dp->dataGood = TRUE; + dp->pos = 0; + return TRUE; + } else { + dp->realSize = 0; + return FALSE; + } } /* append bytes to the end of a dynamic pointer */ -static int appendDynamic (dynamicPtr * dp, const void *src, int size) +static int appendDynamic(dynamicPtr *dp, const void *src, int size) { - int bytesNeeded; - char *tmp; + int bytesNeeded; + char *tmp; + + if (!dp->dataGood) { + return FALSE; + } + + /* bytesNeeded = dp->logicalSize + size; */ + bytesNeeded = dp->pos + size; - if (!dp->dataGood) { - return FALSE; - } + if (bytesNeeded > dp->realSize) { + /* 2.0.21 */ + if (!dp->freeOK) { + return FALSE; + } - /* bytesNeeded = dp->logicalSize + size; */ - bytesNeeded = dp->pos + size; + if (overflow2(dp->realSize, 2)) { + return FALSE; + } - if (bytesNeeded > dp->realSize) { - /* 2.0.21 */ - if (!dp->freeOK) { - return FALSE; - } - gdReallocDynamic(dp, bytesNeeded * 2); - } + if (!gdReallocDynamic(dp, bytesNeeded * 2)) { + dp->dataGood = FALSE; + return FALSE; + } + } + + /* if we get here, we can be sure that we have enough bytes + * to copy safely */ - /* if we get here, we can be sure that we have enough bytes to copy safely */ - /*printf("Mem OK Size: %d, Pos: %d\n", dp->realSize, dp->pos); */ + /*printf("Mem OK Size: %d, Pos: %d\n", dp->realSize, dp->pos); */ - tmp = (char *) dp->data; - memcpy((void *) (tmp + (dp->pos)), src, size); - dp->pos += size; + tmp = (char *)dp->data; + memcpy((void *)(tmp + (dp->pos)), src, size); + dp->pos += size; - if (dp->pos > dp->logicalSize) { - dp->logicalSize = dp->pos; - } + if (dp->pos > dp->logicalSize) { + dp->logicalSize = dp->pos; + }; - return TRUE; + return TRUE; } /* grow (or shrink) dynamic pointer */ -static int gdReallocDynamic (dynamicPtr * dp, int required) +static int gdReallocDynamic(dynamicPtr *dp, int required) { - void *newPtr; - - /* First try gdRealloc(). If that doesn't work, make a new memory block and copy. */ - if ((newPtr = gdRealloc(dp->data, required))) { - dp->realSize = required; - dp->data = newPtr; - return TRUE; - } - - /* create a new pointer */ - newPtr = gdMalloc(required); + void *newPtr; + + /* First try gdRealloc(). If that doesn't work, make a new + * memory block and copy. */ + if ((newPtr = gdRealloc(dp->data, required))) { + dp->realSize = required; + dp->data = newPtr; + return TRUE; + } - /* copy the old data into it */ - memcpy(newPtr, dp->data, dp->logicalSize); - gdFree(dp->data); - dp->data = newPtr; + /* create a new pointer */ + newPtr = gdMalloc(required); + if (!newPtr) { + dp->dataGood = FALSE; + return FALSE; + } - dp->realSize = required; + /* copy the old data into it */ + memcpy(newPtr, dp->data, dp->logicalSize); + gdFree(dp->data); + dp->data = newPtr; - return TRUE; + dp->realSize = required; + return TRUE; } /* trim pointer so that its real and logical sizes match */ -static int trimDynamic (dynamicPtr * dp) +static int trimDynamic(dynamicPtr *dp) { - /* 2.0.21: we don't reallocate memory we don't own */ - if (!dp->freeOK) { - return FALSE; - } - return gdReallocDynamic(dp, dp->logicalSize); + /* 2.0.21: we don't reallocate memory we don't own */ + if (!dp->freeOK) { + return TRUE; + } + + return gdReallocDynamic(dp, dp->logicalSize); } diff --git a/ext/gd/libgd/gd_io_file.c b/ext/gd/libgd/gd_io_file.c index 8318a45969a2..4728f596e4f9 100644 --- a/ext/gd/libgd/gd_io_file.c +++ b/ext/gd/libgd/gd_io_file.c @@ -1,128 +1,128 @@ - /* - * io_file.c - * - * Implements the file interface. - * - * As will all I/O modules, most functions are for local use only (called - * via function pointers in the I/O context). - * - * Most functions are just 'wrappers' for standard file functions. - * - * Written/Modified 1999, Philip Warner. - * + * io_file.c + * + * Implements the file interface. + * + * As will all I/O modules, most functions are for local use only (called + * via function pointers in the I/O context). + * + * Most functions are just 'wrappers' for standard file functions. + * + * Written/Modified 1999, Philip Warner. + * */ /* For platforms with incomplete ANSI defines. Fortunately, - SEEK_SET is defined to be zero by the standard. */ + * SEEK_SET is defined to be zero by the standard. */ #ifndef SEEK_SET #define SEEK_SET 0 #endif /* SEEK_SET */ -#include -#include -#include #include "gd.h" #include "gdhelpers.h" +#include +#include +#include /* this is used for creating images in main memory */ -typedef struct fileIOCtx -{ - gdIOCtx ctx; - FILE *f; +typedef struct fileIOCtx { + gdIOCtx ctx; + FILE *f; } fileIOCtx; -gdIOCtx *newFileCtx (FILE * f); +gdIOCtxPtr newFileCtx(FILE *f); -static int fileGetbuf (gdIOCtx *, void *, int); -static int filePutbuf (gdIOCtx *, const void *, int); -static void filePutchar (gdIOCtx *, int); -static int fileGetchar (gdIOCtx * ctx); +static int fileGetbuf(gdIOCtxPtr, void *, int); +static int filePutbuf(gdIOCtxPtr, const void *, int); +static void filePutchar(gdIOCtxPtr, int); +static int fileGetchar(gdIOCtxPtr ctx); -static int fileSeek (struct gdIOCtx *, const int); -static long fileTell (struct gdIOCtx *); -static void gdFreeFileCtx (gdIOCtx * ctx); +static int fileSeek(gdIOCtxPtr, const int); +static long fileTell(gdIOCtxPtr); +static void gdFreeFileCtx(gdIOCtxPtr ctx); -/* return data as a dynamic pointer */ -gdIOCtx * gdNewFileCtx (FILE * f) -{ - fileIOCtx *ctx; +/* + Function: gdNewFileCtx - ctx = (fileIOCtx *) gdMalloc(sizeof (fileIOCtx)); + Return data as a dynamic pointer. +*/ +BGD_DECLARE(gdIOCtxPtr) gdNewFileCtx(FILE *f) +{ + fileIOCtx *ctx; - ctx->f = f; + if (f == NULL) + return NULL; + ctx = (fileIOCtx *)gdMalloc(sizeof(fileIOCtx)); + if (ctx == NULL) { + return NULL; + } - ctx->ctx.getC = fileGetchar; - ctx->ctx.putC = filePutchar; + ctx->f = f; - ctx->ctx.getBuf = fileGetbuf; - ctx->ctx.putBuf = filePutbuf; + ctx->ctx.getC = fileGetchar; + ctx->ctx.putC = filePutchar; - ctx->ctx.tell = fileTell; - ctx->ctx.seek = fileSeek; + ctx->ctx.getBuf = fileGetbuf; + ctx->ctx.putBuf = filePutbuf; - ctx->ctx.gd_free = gdFreeFileCtx; + ctx->ctx.tell = fileTell; + ctx->ctx.seek = fileSeek; - return (gdIOCtx *) ctx; -} + ctx->ctx.gd_free = gdFreeFileCtx; -static void gdFreeFileCtx (gdIOCtx * ctx) -{ - gdFree(ctx); + return (gdIOCtxPtr)ctx; } +static void gdFreeFileCtx(gdIOCtxPtr ctx) { gdFree(ctx); } -static int filePutbuf (gdIOCtx * ctx, const void *buf, int size) +static int filePutbuf(gdIOCtxPtr ctx, const void *buf, int size) { - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; - - return fwrite(buf, 1, size, fctx->f); + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; + return fwrite(buf, 1, size, fctx->f); } -static int fileGetbuf (gdIOCtx * ctx, void *buf, int size) +static int fileGetbuf(gdIOCtxPtr ctx, void *buf, int size) { - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; - return fread(buf, 1, size, fctx->f); + return (fread(buf, 1, size, fctx->f)); } -static void filePutchar (gdIOCtx * ctx, int a) +static void filePutchar(gdIOCtxPtr ctx, int a) { - unsigned char b; - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; + unsigned char b; + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; - b = a; + b = a; - putc (b, fctx->f); + putc(b, fctx->f); } -static int fileGetchar (gdIOCtx * ctx) +static int fileGetchar(gdIOCtxPtr ctx) { - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; - return getc (fctx->f); + return getc(fctx->f); } - -static int fileSeek (struct gdIOCtx *ctx, const int pos) +static int fileSeek(gdIOCtxPtr ctx, const int pos) { - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; - - return (fseek (fctx->f, pos, SEEK_SET) == 0); + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; + return (fseek(fctx->f, pos, SEEK_SET) == 0); } -static long fileTell (struct gdIOCtx *ctx) +static long fileTell(gdIOCtxPtr ctx) { - fileIOCtx *fctx; - fctx = (fileIOCtx *) ctx; + fileIOCtx *fctx; + fctx = (fileIOCtx *)ctx; - return ftell (fctx->f); + return ftell(fctx->f); } diff --git a/ext/gd/libgd/gd_io_ss.c b/ext/gd/libgd/gd_io_ss.c index 0275cdc40330..24c957a3e613 100644 --- a/ext/gd/libgd/gd_io_ss.c +++ b/ext/gd/libgd/gd_io_ss.c @@ -1,138 +1,139 @@ /* - * io_ss.c - * - * Implements the Source/Sink interface. - * - * As will all I/O modules, most functions are for local use only (called - * via function pointers in the I/O context). - * - * The Source/Sink model is the primary 'user' interface for alternate data - * sources; the IOCtx interface is intended (at least in version 1.5) to be - * used internally until it settles down a bit. - * - * This module just layers the Source/Sink interface on top of the IOCtx; no - * support is provided for tell/seek, so GD2 writing is not possible, and - * retrieving parts of GD2 files is also not possible. - * - * A new SS context does not need to be created with both a Source and a Sink. - * - * Written/Modified 1999, Philip Warner. - * + * io_ss.c + * + * Implements the Source/Sink interface. + * + * As will all I/O modules, most functions are for local use only (called + * via function pointers in the I/O context). + * + * The Source/Sink model is the primary 'user' interface for alternate data + * sources; the IOCtx interface is intended (at least in version 1.5) to be + * used internally until it settles down a bit. + * + * This module just layers the Source/Sink interface on top of the IOCtx; no + * support is provided for tell/seek, so GD2 writing is not possible, and + * retrieving parts of GD2 files is also not possible. + * + * A new SS context does not need to be created with both a Source and a Sink. + * + * Written/Modified 1999, Philip Warner. + * */ -#include -#include -#include #include "gd.h" #include "gdhelpers.h" +#include +#include +#include /* this is used for creating images in main memory */ -typedef struct ssIOCtx -{ - gdIOCtx ctx; - gdSourcePtr src; - gdSinkPtr snk; +typedef struct ssIOCtx { + gdIOCtx ctx; + gdSourcePtr src; + gdSinkPtr snk; } ssIOCtx; typedef struct ssIOCtx *ssIOCtxPtr; -gdIOCtx *gdNewSSCtx (gdSourcePtr src, gdSinkPtr snk); +static int sourceGetbuf(gdIOCtx *, void *, int); +static int sourceGetchar(gdIOCtx *ctx); +static int sinkPutbuf(gdIOCtx *ctx, const void *buf, int size); +static void sinkPutchar(gdIOCtx *ctx, int a); +static void gdFreeSsCtx(gdIOCtx *ctx); -static int sourceGetbuf (gdIOCtx *, void *, int); -static int sourceGetchar (gdIOCtx * ctx); -static int sinkPutbuf (gdIOCtx * ctx, const void *buf, int size); -static void sinkPutchar (gdIOCtx * ctx, int a); -static void gdFreeSsCtx (gdIOCtx * ctx); +/* + Function: gdNewSSCtx -/* return data as a dynamic pointer */ -gdIOCtx * gdNewSSCtx (gdSourcePtr src, gdSinkPtr snk) + Return data as a dynamic pointer. +*/ +BGD_DECLARE(gdIOCtx *) gdNewSSCtx(gdSourcePtr src, gdSinkPtr snk) { - ssIOCtxPtr ctx; - - ctx = (ssIOCtxPtr) gdMalloc (sizeof (ssIOCtx)); + ssIOCtxPtr ctx; - ctx->src = src; - ctx->snk = snk; + ctx = (ssIOCtxPtr)gdMalloc(sizeof(ssIOCtx)); + if (ctx == NULL) { + return NULL; + } - ctx->ctx.getC = sourceGetchar; - ctx->ctx.getBuf = sourceGetbuf; + ctx->src = src; + ctx->snk = snk; - ctx->ctx.putC = sinkPutchar; - ctx->ctx.putBuf = sinkPutbuf; + ctx->ctx.getC = sourceGetchar; + ctx->ctx.getBuf = sourceGetbuf; - ctx->ctx.tell = NULL; - ctx->ctx.seek = NULL; + ctx->ctx.putC = sinkPutchar; + ctx->ctx.putBuf = sinkPutbuf; - ctx->ctx.gd_free = gdFreeSsCtx; + ctx->ctx.tell = NULL; + ctx->ctx.seek = NULL; - return (gdIOCtx *) ctx; -} + ctx->ctx.gd_free = gdFreeSsCtx; -static void gdFreeSsCtx (gdIOCtx * ctx) -{ - gdFree(ctx); + return (gdIOCtx *)ctx; } +static void gdFreeSsCtx(gdIOCtx *ctx) { gdFree(ctx); } -static int sourceGetbuf (gdIOCtx * ctx, void *buf, int size) +static int sourceGetbuf(gdIOCtx *ctx, void *buf, int size) { - ssIOCtx *lctx; - int res; + ssIOCtx *lctx; + int res; - lctx = (ssIOCtx *) ctx; + lctx = (ssIOCtx *)ctx; - res = ((lctx->src->source) (lctx->src->context, buf, size)); + res = ((lctx->src->source)(lctx->src->context, buf, size)); - /* - * Translate the return values from the Source object: - * 0 is EOF, -1 is error - */ + /* + * Translate the return values from the Source object: + * 0 is EOF, -1 is error + */ - if (res == 0) { - return EOF; - } else if (res < 0) { - return 0; - } else { - return res; - } + if (res == 0) { + return 0; + } else if (res < 0) { + return 0; + } else { + return res; + } } -static int sourceGetchar (gdIOCtx * ctx) +static int sourceGetchar(gdIOCtx *ctx) { - int res; - unsigned char buf; + int res; + unsigned char buf; - res = sourceGetbuf (ctx, &buf, 1); + res = sourceGetbuf(ctx, &buf, 1); - if (res == 1) { - return buf; - } else { - return EOF; - } + if (res == 1) { + return buf; + } else { + return EOF; + } } -static int sinkPutbuf (gdIOCtx * ctx, const void *buf, int size) +static int sinkPutbuf(gdIOCtx *ctx, const void *buf, int size) { - ssIOCtxPtr lctx; - int res; + ssIOCtxPtr lctx; + int res; - lctx = (ssIOCtx *) ctx; + lctx = (ssIOCtx *)ctx; - res = (lctx->snk->sink) (lctx->snk->context, buf, size); + res = (lctx->snk->sink)(lctx->snk->context, buf, size); - if (res <= 0) { - return 0; - } else { - return res; - } + if (res <= 0) { + return 0; + } else { + return res; + } } -static void sinkPutchar (gdIOCtx * ctx, int a) +static void sinkPutchar(gdIOCtx *ctx, int a) { - unsigned char b; + unsigned char b; + + b = a; - b = a; - sinkPutbuf (ctx, &b, 1); + sinkPutbuf(ctx, &b, 1); } diff --git a/ext/gd/libgd/gd_jpeg.c b/ext/gd/libgd/gd_jpeg.c index c15a97c7b592..fde1102f4157 100644 --- a/ext/gd/libgd/gd_jpeg.c +++ b/ext/gd/libgd/gd_jpeg.c @@ -1,6 +1,6 @@ /* * gd_jpeg.c: Read and write JPEG (JFIF) format image files using the - * gd graphics library (http://www.boutell.com/gd/). + * gd graphics library (https://libgd.github.io). * * This software is based in part on the work of the Independent JPEG * Group. For more information on the IJG JPEG software (and JPEG @@ -21,120 +21,134 @@ * Christian Aberger */ -#include -#include -#include +/** + * File: JPEG IO + * + * Read and write JPEG images. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include +#include +#include #include #include "gd.h" #include "gd_errors.h" +#include "gd_intern.h" /* TBB: move this up so include files are not brought in */ /* JCE: arrange HAVE_LIBJPEG so that it can be set in gd.h */ #ifdef HAVE_LIBJPEG #include "gdhelpers.h" -#undef HAVE_STDLIB_H + +#if defined(_WIN32) && defined(__MINGW32__) +#define HAVE_BOOLEAN +#endif /* 1.8.1: remove dependency on jinclude.h */ -#include "jpeglib.h" #include "jerror.h" +#include "jpeglib.h" static const char *const GD_JPEG_VERSION = "1.0"; -typedef struct _jmpbuf_wrapper -{ - jmp_buf jmpbuf; - int ignore_warning; +typedef struct _jmpbuf_wrapper { + jmp_buf jmpbuf; + int ignore_warning; } jmpbuf_wrapper; -static void php_jpeg_emit_message(j_common_ptr jpeg_info, int level) +static void jpeg_emit_message(j_common_ptr jpeg_info, int level) { - char message[JMSG_LENGTH_MAX]; - jmpbuf_wrapper *jmpbufw; - int ignore_warning = 0; - - jmpbufw = (jmpbuf_wrapper *) jpeg_info->client_data; - - if (jmpbufw != 0) { - ignore_warning = jmpbufw->ignore_warning; - } - - (jpeg_info->err->format_message)(jpeg_info,message); - - /* It is a warning message */ - if (level < 0) { - /* display only the 1st warning, as would do a default libjpeg - * unless strace_level >= 3 - */ - if ((jpeg_info->err->num_warnings == 0) || (jpeg_info->err->trace_level >= 3)) { - if (!ignore_warning) { - gd_error("gd-jpeg, libjpeg: recoverable error: %s\n", message); - } - } - - jpeg_info->err->num_warnings++; - } else { - /* strace msg, Show it if trace_level >= level. */ - if (jpeg_info->err->trace_level >= level) { - if (!ignore_warning) { - gd_error("gd-jpeg, libjpeg: strace message: %s\n", message); - } - } - } + char message[JMSG_LENGTH_MAX]; + jmpbuf_wrapper *jmpbufw; + int ignore_warning = 0; + + jmpbufw = (jmpbuf_wrapper *)jpeg_info->client_data; + + if (jmpbufw != 0) { + ignore_warning = jmpbufw->ignore_warning; + } + + (jpeg_info->err->format_message)(jpeg_info, message); + + /* It is a warning message */ + if (level < 0) { + /* display only the 1st warning, as would do a default libjpeg + * unless strace_level >= 3 + */ + if ((jpeg_info->err->num_warnings == 0) || (jpeg_info->err->trace_level >= 3)) { + if (!ignore_warning) { + gd_error("gd-jpeg, libjpeg: recoverable error: %s\n", message); + } + } + + jpeg_info->err->num_warnings++; + } else { + /* strace msg, Show it if trace_level >= level. */ + if (jpeg_info->err->trace_level >= level) { + if (!ignore_warning) { + gd_error("gd-jpeg, libjpeg: strace message: %s\n", message); + } + } + } } - /* Called by the IJG JPEG library upon encountering a fatal error */ -static void fatal_jpeg_error (j_common_ptr cinfo) +static void fatal_jpeg_error(j_common_ptr cinfo) { - jmpbuf_wrapper *jmpbufw; - char buffer[JMSG_LENGTH_MAX]; + jmpbuf_wrapper *jmpbufw; + char buffer[JMSG_LENGTH_MAX]; - (*cinfo->err->format_message)(cinfo, buffer); - gd_error_ex(GD_WARNING, "gd-jpeg: JPEG library reports unrecoverable error: %s", buffer); + (*cinfo->err->format_message)(cinfo, buffer); + gd_error_ex(GD_WARNING, "gd-jpeg: JPEG library reports unrecoverable error: %s", buffer); - jmpbufw = (jmpbuf_wrapper *) cinfo->client_data; - jpeg_destroy (cinfo); + jmpbufw = (jmpbuf_wrapper *)cinfo->client_data; + jpeg_destroy(cinfo); - if (jmpbufw != 0) { - longjmp (jmpbufw->jmpbuf, 1); - gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: longjmp returned control; terminating"); - } else { - gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: jmpbuf unrecoverable; terminating"); - } + if (jmpbufw != 0) { + longjmp(jmpbufw->jmpbuf, 1); + gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: longjmp " + "returned control; terminating\n"); + } else { + gd_error_ex(GD_ERROR, "gd-jpeg: EXTREMELY fatal error: jmpbuf " + "unrecoverable; terminating\n"); + } - exit (99); + exit(99); } -const char * gdJpegGetVersionString() +BGD_DECLARE(const char *) gdJpegGetVersionString() { - switch(JPEG_LIB_VERSION) { - case 62: - return "6b"; - break; - - case 70: - return "7"; - break; - - case 80: - return "8"; - break; - - case 90: - return "9 compatible"; - break; - - case 100: - return "10 compatible"; - break; - - default: - return "unknown"; - } + switch (JPEG_LIB_VERSION) { + case 62: + return "6b"; + break; + + case 70: + return "7"; + break; + + case 80: + return "8"; + break; + + case 90: + return "9 compatible"; + break; + + case 100: + return "10 compatible"; + break; + + default: + return "unknown"; + } } -static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality); +static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality, + const gdImageMetadata *metadata, int force_no_subsampling); /* * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality @@ -145,436 +159,1097 @@ static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality); * library documentation for more details. */ -void gdImageJpeg (gdImagePtr im, FILE * outFile, int quality) +/* + Function: gdImageJpeg + + outputs the specified image to the specified file in + JPEG format. The file must be open for writing. Under MSDOS and + all versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + If _quality_ is negative, the default IJG JPEG quality value (which + should yield a good general quality / size tradeoff for most + situations) is used. Otherwise, for practical purposes, _quality_ + should be a value in the range 0-95, higher quality values usually + implying both higher quality and larger image sizes. + + If you have set image interlacing using , this + function will interpret that to mean you wish to output a + progressive JPEG. Some programs (e.g., Web browsers) can display + progressive JPEGs incrementally; this can be useful when browsing + over a relatively slow communications link, for + example. Progressive JPEGs can also be slightly smaller than + sequential (non-progressive) JPEGs. + + Variants: + + stores the image using a struct. + + stores the image to RAM. + + Parameters: + + im - The image to save + outFile - The FILE pointer to write to. + quality - Compression quality (0-95, 0 means use the default). + + Returns: + + Nothing. + + Example: + (start code) + + gdImagePtr im; + int black, white; + FILE *out; + // Create the image + im = gdImageCreate(100, 100); + // Allocate background + white = gdImageColorAllocate(im, 255, 255, 255); + // Allocate drawing color + black = gdImageColorAllocate(im, 0, 0, 0); + // Draw rectangle + gdImageRectangle(im, 0, 0, 99, 99, black); + // Open output file in binary mode + out = fopen("rect.jpg", "wb"); + // Write JPEG using default quality + gdImageJpeg(im, out, -1); + // Close file + fclose(out); + // Destroy image + gdImageDestroy(im); + + (end code) +*/ + +BGD_DECLARE(void) gdImageJpeg(gdImagePtr im, FILE *outFile, int quality) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageJpegCtx(im, out, quality); + out->gd_free(out); +} + +/* + Function: gdImageJpegPtr + + Identical to except that it returns a pointer to a + memory area with the JPEG data. This memory must be freed by the + caller when it is no longer needed. + + The caller *must* invoke , not free(). This is because it + is not guaranteed that libgd will use the same implementation of + malloc, free, etc. as your proggram. + + The 'size' parameter receives the total size of the block of + memory. + + Parameters: + + im - The image to write + size - Output: the size of the resulting image. + quality - Compression quality. + + Returns: + + A pointer to the JPEG data or NULL if an error occurred. + +*/ +BGD_DECLARE(void *) gdImageJpegPtr(gdImagePtr im, int *size, int quality) { - gdIOCtx *out = gdNewFileCtx (outFile); - gdImageJpegCtx (im, out, quality); - out->gd_free (out); + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageJpegCtx(im, out, quality, NULL, 0)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; } -void *gdImageJpegPtr (gdImagePtr im, int *size, int quality) +BGD_DECLARE(void *) +gdImageJpegPtrWithMetadata(gdImagePtr im, int *size, int quality, const gdImageMetadata *metadata) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx (2048, NULL); - if (!_gdImageJpegCtx(im, out, quality)) { - rv = gdDPExtractData(out, size); - } else { - rv = NULL; - } - out->gd_free (out); + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageJpegCtx(im, out, quality, metadata, 0)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; +} - return rv; +void *gdImageJpegPtrWithMetadataNoSubsampling(gdImagePtr im, int *size, int quality, + const gdImageMetadata *metadata) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageJpegCtx(im, out, quality, metadata, 1)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; } -void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile); +static void jpeg_gdIOCtx_dest(j_compress_ptr cinfo, gdIOCtx *outfile); + +/* + Function: gdImageJpegCtx + + Write the image as JPEG data via a . See + for more details. + + Parameters: + + im - The image to write. + outfile - The output sink. + quality - Image quality. + +*/ +BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) +{ + _gdImageJpegCtx(im, outfile, quality, NULL, 0); +} -void gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) +BGD_DECLARE(void) +gdImageJpegCtxWithMetadata(gdImagePtr im, gdIOCtx *outfile, int quality, + const gdImageMetadata *metadata) { - _gdImageJpegCtx(im, outfile, quality); + _gdImageJpegCtx(im, outfile, quality, metadata, 0); } /* returns 0 on success, 1 on failure */ -static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - int i, j, jidx; - /* volatile so we can gdFree it on return from longjmp */ - volatile JSAMPROW row = 0; - JSAMPROW rowptr[1]; - jmpbuf_wrapper jmpbufw; - JDIMENSION nlines; - char comment[255]; - - memset (&cinfo, 0, sizeof (cinfo)); - memset (&jerr, 0, sizeof (jerr)); - - cinfo.err = jpeg_std_error (&jerr); - cinfo.client_data = &jmpbufw; - if (setjmp (jmpbufw.jmpbuf) != 0) { - /* we're here courtesy of longjmp */ - if (row) { - gdFree (row); - } - return 1; - } +static int gdJpegWriteAppMarker(j_compress_ptr cinfo, int marker, const unsigned char *data, + size_t size) +{ + if (data == NULL && size != 0) { + return 1; + } + if (size > 65533) { + return 1; + } + jpeg_write_marker(cinfo, marker, data, (unsigned int)size); + return 0; +} - cinfo.err->error_exit = fatal_jpeg_error; +static int gdJpegWriteIccProfile(j_compress_ptr cinfo, const unsigned char *data, size_t size) +{ + static const unsigned char icc_signature[] = "ICC_PROFILE"; + unsigned char *segment; + size_t offset = 0; + size_t max_payload = 65533 - 14; + int segment_count; + int segment_index; + + if (data == NULL && size != 0) { + return 1; + } + if (size == 0) { + return 0; + } + if (size > max_payload * 255) { + return 1; + } + + segment_count = (int)((size + max_payload - 1) / max_payload); + segment = (unsigned char *)gdMalloc(65533); + if (segment == NULL) { + return 1; + } + memcpy(segment, icc_signature, 12); + + for (segment_index = 1; segment_index <= segment_count; segment_index++) { + size_t chunk_size = size - offset; + if (chunk_size > max_payload) { + chunk_size = max_payload; + } + segment[12] = (unsigned char)segment_index; + segment[13] = (unsigned char)segment_count; + memcpy(segment + 14, data + offset, chunk_size); + jpeg_write_marker(cinfo, JPEG_APP0 + 2, segment, (unsigned int)(chunk_size + 14)); + offset += chunk_size; + } + + gdFree(segment); + return 0; +} - jpeg_create_compress (&cinfo); +static int gdJpegWriteMetadata(j_compress_ptr cinfo, const gdImageMetadata *metadata) +{ + const unsigned char *data; + size_t size; + + if (metadata == NULL) { + return 0; + } + + data = gdImageMetadataGetProfile(metadata, "exif", &size); + if (data != NULL && gdJpegWriteAppMarker(cinfo, JPEG_APP0 + 1, data, size)) { + return 1; + } + data = gdImageMetadataGetProfile(metadata, "xmp", &size); + if (data != NULL && gdJpegWriteAppMarker(cinfo, JPEG_APP0 + 1, data, size)) { + return 1; + } + data = gdImageMetadataGetProfile(metadata, "icc", &size); + if (data != NULL && gdJpegWriteIccProfile(cinfo, data, size)) { + return 1; + } + data = gdImageMetadataGetProfile(metadata, "iptc", &size); + if (data != NULL && gdJpegWriteAppMarker(cinfo, JPEG_APP0 + 13, data, size)) { + return 1; + } + + return 0; +} - cinfo.image_width = im->sx; - cinfo.image_height = im->sy; - cinfo.input_components = 3; /* # of color components per pixel */ - cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ - jpeg_set_defaults (&cinfo); +static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality, + const gdImageMetadata *metadata, int force_no_subsampling) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + int i, j, jidx; + /* volatile so we can gdFree it on return from longjmp */ + volatile JSAMPROW row = 0; + JSAMPROW rowptr[1]; + jmpbuf_wrapper jmpbufw; + JDIMENSION nlines; + char comment[255]; + +#ifdef JPEG_DEBUG + gd_error_ex(GD_DEBUG, "gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION); + gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG library version %d, %d-bit sample values\n", + JPEG_LIB_VERSION, BITS_IN_JSAMPLE); + if (!im->trueColor) { + for (i = 0; i < im->colorsTotal; i++) { + if (!im->open[i]) { + gd_error_ex(GD_DEBUG, "gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i, + im->red[i], im->green[i], im->blue[i]); + } + } + } +#endif /* JPEG_DEBUG */ + + memset(&cinfo, 0, sizeof(cinfo)); + memset(&jerr, 0, sizeof(jerr)); + + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = &jmpbufw; + + if (setjmp(jmpbufw.jmpbuf) != 0) { + /* we're here courtesy of longjmp */ + if (row) { + gdFree(row); + } + return 1; + } + + cinfo.err->emit_message = jpeg_emit_message; + cinfo.err->error_exit = fatal_jpeg_error; + + jpeg_create_compress(&cinfo); + + cinfo.image_width = im->sx; + cinfo.image_height = im->sy; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + + cinfo.density_unit = 1; + cinfo.X_density = im->res_x; + cinfo.Y_density = im->res_y; + + if (quality >= 0) { + jpeg_set_quality(&cinfo, quality, TRUE); + } + if (force_no_subsampling || quality >= 90) { + for (i = 0; i < cinfo.num_components; i++) { + cinfo.comp_info[i].h_samp_factor = 1; + cinfo.comp_info[i].v_samp_factor = 1; + } + } + + /* If user requests interlace, translate that to progressive JPEG */ + if (gdImageGetInterlaced(im)) { +#ifdef JPEG_DEBUG + gd_error_ex(GD_DEBUG, "gd-jpeg: interlace set, outputting progressive JPEG image\n"); +#endif + jpeg_simple_progression(&cinfo); + } - cinfo.density_unit = 1; - cinfo.X_density = im->res_x; - cinfo.Y_density = im->res_y; + jpeg_gdIOCtx_dest(&cinfo, outfile); - if (quality >= 0) { - jpeg_set_quality (&cinfo, quality, TRUE); - } + row = (JSAMPROW)gdCalloc(1, cinfo.image_width * cinfo.input_components * sizeof(JSAMPLE)); + if (row == 0) { + gd_error("gd-jpeg: error: unable to allocate JPEG row structure: " + "gdCalloc returns NULL\n"); + jpeg_destroy_compress(&cinfo); + return 1; + } - /* If user requests interlace, translate that to progressive JPEG */ - if (gdImageGetInterlaced (im)) { - jpeg_simple_progression (&cinfo); - } + rowptr[0] = row; - jpeg_gdIOCtx_dest (&cinfo, outfile); + jpeg_start_compress(&cinfo, TRUE); - row = (JSAMPROW) safe_emalloc(cinfo.image_width * cinfo.input_components, sizeof(JSAMPLE), 0); - memset(row, 0, cinfo.image_width * cinfo.input_components * sizeof(JSAMPLE)); - rowptr[0] = row; + if (gdJpegWriteMetadata(&cinfo, metadata)) { + gd_error("gd-jpeg: error: unable to write metadata\n"); + goto error; + } - jpeg_start_compress (&cinfo, TRUE); + sprintf(comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),", GD_JPEG_VERSION, + JPEG_LIB_VERSION); - if (quality >= 0) { - snprintf(comment, sizeof(comment)-1, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d), quality = %d\n", GD_JPEG_VERSION, JPEG_LIB_VERSION, quality); - } else { - snprintf(comment, sizeof(comment)-1, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d), default quality\n", GD_JPEG_VERSION, JPEG_LIB_VERSION); - } - jpeg_write_marker (&cinfo, JPEG_COM, (unsigned char *) comment, (unsigned int) strlen (comment)); - if (im->trueColor) { + if (quality >= 0) { + sprintf(comment + strlen(comment), " quality = %d\n", quality); + } else { + strcat(comment + strlen(comment), " default quality\n"); + } + + jpeg_write_marker(&cinfo, JPEG_COM, (unsigned char *)comment, (unsigned int)strlen(comment)); + if (im->trueColor) { #if BITS_IN_JSAMPLE == 12 - gd_error("gd-jpeg: error: jpeg library was compiled for 12-bit precision. This is mostly useless, because JPEGs on the web are 8-bit and such versions of the jpeg library won't read or write them. GD doesn't support these unusual images. Edit your jmorecfg.h file to specify the correct precision and completely 'make clean' and 'make install' libjpeg again. Sorry"); - goto error; + gd_error("gd-jpeg: error: jpeg library was compiled for 12-bit\n" + "precision. This is mostly useless, because JPEGs on the web are\n" + "8-bit and such versions of the jpeg library won't read or write\n" + "them. GD doesn't support these unusual images. Edit your\n" + "jmorecfg.h file to specify the correct precision and completely\n" + "'make clean' and 'make install' libjpeg again. Sorry.\n"); + goto error; #endif /* BITS_IN_JSAMPLE == 12 */ - - for (i = 0; i < im->sy; i++) { - for (jidx = 0, j = 0; j < im->sx; j++) { - int val = im->tpixels[i][j]; - - row[jidx++] = gdTrueColorGetRed (val); - row[jidx++] = gdTrueColorGetGreen (val); - row[jidx++] = gdTrueColorGetBlue (val); - } - - nlines = jpeg_write_scanlines (&cinfo, rowptr, 1); - if (nlines != 1) { - gd_error_ex(GD_WARNING, "gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1", nlines); - } - } - } else { - for (i = 0; i < im->sy; i++) { - for (jidx = 0, j = 0; j < im->sx; j++) { - int idx = im->pixels[i][j]; - - /* NB: Although gd RGB values are ints, their max value is - * 255 (see the documentation for gdImageColorAllocate()) - * -- perfect for 8-bit JPEG encoding (which is the norm) - */ + for (i = 0; i < im->sy; i++) { + for (jidx = 0, j = 0; j < im->sx; j++) { + int val = im->tpixels[i][j]; + row[jidx++] = gdTrueColorGetRed(val); + row[jidx++] = gdTrueColorGetGreen(val); + row[jidx++] = gdTrueColorGetBlue(val); + } + + nlines = jpeg_write_scanlines(&cinfo, rowptr, 1); + + if (nlines != 1) { + gd_error("gd_jpeg: warning: jpeg_write_scanlines returns %u -- " + "expected 1\n", + nlines); + } + } + } else { + for (i = 0; i < im->sy; i++) { + for (jidx = 0, j = 0; j < im->sx; j++) { + int idx = im->pixels[i][j]; + + /* + * NB: Although gd RGB values are ints, their max value is + * 255 (see the documentation for gdImageColorAllocate()) + * -- perfect for 8-bit JPEG encoding (which is the norm) + */ #if BITS_IN_JSAMPLE == 8 - row[jidx++] = im->red[idx]; - row[jidx++] = im->green[idx]; - row[jidx++] = im->blue[idx]; + row[jidx++] = im->red[idx]; + row[jidx++] = im->green[idx]; + row[jidx++] = im->blue[idx]; #elif BITS_IN_JSAMPLE == 12 - row[jidx++] = im->red[idx] << 4; - row[jidx++] = im->green[idx] << 4; - row[jidx++] = im->blue[idx] << 4; + row[jidx++] = im->red[idx] << 4; + row[jidx++] = im->green[idx] << 4; + row[jidx++] = im->blue[idx] << 4; #else #error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12 #endif - } - - nlines = jpeg_write_scanlines (&cinfo, rowptr, 1); - if (nlines != 1) { - gd_error_ex(GD_WARNING, "gd_jpeg: warning: jpeg_write_scanlines returns %u -- expected 1", nlines); - } - } - } + } + + nlines = jpeg_write_scanlines(&cinfo, rowptr, 1); + if (nlines != 1) { + gd_error("gd_jpeg: warning: jpeg_write_scanlines" + " returns %u -- expected 1\n", + nlines); + } + } + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + gdFree(row); + return 0; - jpeg_finish_compress (&cinfo); - jpeg_destroy_compress (&cinfo); - gdFree (row); - return 0; +error: + jpeg_destroy_compress(&cinfo); + if (row) { + gdFree(row); + } + return 1; } -gdImagePtr gdImageCreateFromJpeg (FILE * inFile) +/* + Function: gdImageCreateFromJpeg + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpeg(FILE *inFile) { - return gdImageCreateFromJpegEx(inFile, 1); + return gdImageCreateFromJpegEx(inFile, 1); } -gdImagePtr gdImageCreateFromJpegEx (FILE * inFile, int ignore_warning) -{ - gdImagePtr im; - gdIOCtx *in = gdNewFileCtx(inFile); - im = gdImageCreateFromJpegCtxEx(in, ignore_warning); - in->gd_free (in); +/* + Function: gdImageCreateFromJpegEx - return im; -} + is called to load truecolor images from + JPEG format files. Invoke with an + already opened pointer to a file containing the desired + image. returns a to the new + truecolor image, or NULL if unable to load the image (most often + because the file is corrupt or does not contain a JPEG + image). does not close the file. + + You can inspect the sx and sy members of the image to determine + its size. The image must eventually be destroyed using + . -gdImagePtr gdImageCreateFromJpegPtr (int size, void *data) + *The returned image is always a truecolor image.* + + Variants: + + creates an image from JPEG data + already in memory. + + reads its data via the function + pointers in a structure. + + , and + are equivalent to calling their + _Ex_-named counterparts with an ignore_warning set to 1 + (i.e. TRUE). + + Parameters: + + infile - The input FILE pointer. + ignore_warning - Flag. If true, ignores recoverable warnings. + + Returns: + + A pointer to the new *truecolor* image. This will need to be + destroyed with once it is no longer needed. + + On error, returns NULL. + + Example: + (start code) + + gdImagePtr im; + FILE *in; + in = fopen("myjpeg.jpg", "rb"); + im = gdImageCreateFromJpegEx(in, GD_TRUE); + fclose(in); + // ... Use the image ... + gdImageDestroy(im); + + (end code) +*/ +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegEx(FILE *inFile, int ignore_warning) { - return gdImageCreateFromJpegPtrEx(size, data, 1); + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromJpegCtxEx(in, ignore_warning); + in->gd_free(in); + return im; } -gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning) -{ - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromJpegCtxEx(in, ignore_warning); - in->gd_free(in); +/* + Function: gdImageCreateFromJpegPtr + + Parameters: - return im; + size - size of JPEG data in bytes. + data - pointer to JPEG data. + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegPtr(int size, void *data) +{ + return gdImageCreateFromJpegPtrEx(size, data, 1); } -void jpeg_gdIOCtx_src (j_decompress_ptr cinfo, gdIOCtx * infile); +/* + Function: gdImageCreateFromJpegPtrEx -static int CMYKToRGB(int c, int m, int y, int k, int inverted); + Parameters: + size - size of JPEG data in bytes. + data - pointer to JPEG data. + ignore_warning - if true, ignore recoverable warnings -/* - * Create a gd-format image from the JPEG-format INFILE. Returns the - * image, or NULL upon error. - */ -gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile) -{ - return gdImageCreateFromJpegCtxEx(infile, 1); -} - -gdImagePtr gdImageCreateFromJpegCtxEx (gdIOCtx * infile, int ignore_warning) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - jmpbuf_wrapper jmpbufw; - /* volatile so we can gdFree them after longjmp */ - volatile JSAMPROW row = 0; - volatile gdImagePtr im = 0; - JSAMPROW rowptr[1]; - unsigned int i, j; - int retval; - JDIMENSION nrows; - int channels = 3; - int inverted = 0; - - memset (&cinfo, 0, sizeof (cinfo)); - memset (&jerr, 0, sizeof (jerr)); - - jmpbufw.ignore_warning = ignore_warning; - - cinfo.err = jpeg_std_error (&jerr); - cinfo.client_data = &jmpbufw; - cinfo.err->emit_message = php_jpeg_emit_message; - - if (setjmp (jmpbufw.jmpbuf) != 0) { - /* we're here courtesy of longjmp */ - if (row) { - gdFree (row); - } - if (im) { - gdImageDestroy (im); - } - return 0; - } + See . +*/ +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrEx(int size, void *data, int ignore_warning) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return 0; + } + im = gdImageCreateFromJpegCtxEx(in, ignore_warning); + in->gd_free(in); + return im; +} - cinfo.err->error_exit = fatal_jpeg_error; +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrWithMetadata(int size, void *data, gdImageMetadata *metadata) +{ + return gdImageCreateFromJpegPtrExWithMetadata(size, data, 1, metadata); +} - jpeg_create_decompress (&cinfo); +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrExWithMetadata(int size, void *data, int ignore_warning, + gdImageMetadata *metadata) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return 0; + } + im = gdImageCreateFromJpegCtxExWithMetadata(in, ignore_warning, metadata); + in->gd_free(in); + return im; +} - jpeg_gdIOCtx_src (&cinfo, infile); +static void jpeg_gdIOCtx_src(j_decompress_ptr cinfo, gdIOCtx *infile); - /* 2.0.22: save the APP14 marker to check for Adobe Photoshop CMYK files with inverted components. */ - jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 256); +static int CMYKToRGB(int c, int m, int y, int k, int inverted); - retval = jpeg_read_header (&cinfo, TRUE); - if (retval != JPEG_HEADER_OK) { - gd_error_ex(GD_WARNING, "gd-jpeg: warning: jpeg_read_header returned %d, expected %d", retval, JPEG_HEADER_OK); - } +static int gdJpegMarkerStartsWith(jpeg_saved_marker_ptr marker, const unsigned char *prefix, + size_t prefix_size) +{ + return marker->data_length >= prefix_size && memcmp(marker->data, prefix, prefix_size) == 0; +} - if (cinfo.image_height > INT_MAX) { - gd_error_ex(GD_WARNING, "gd-jpeg: warning: JPEG image height (%u) is greater than INT_MAX (%d) (and thus greater than gd can handle)", cinfo.image_height, INT_MAX); - } +static int gdJpegCollectIccProfile(j_decompress_ptr cinfo, gdImageMetadata *metadata) +{ + static const unsigned char icc_signature[] = "ICC_PROFILE"; + jpeg_saved_marker_ptr marker; + jpeg_saved_marker_ptr segments[256]; + unsigned int segment_sizes[256]; + unsigned int segment_count = 0; + unsigned int i; + size_t total_size = 0; + size_t offset = 0; + unsigned char *icc; + int status; + + memset(segments, 0, sizeof(segments)); + memset(segment_sizes, 0, sizeof(segment_sizes)); + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + unsigned int sequence; + unsigned int count; + + if (marker->marker != JPEG_APP0 + 2 || !gdJpegMarkerStartsWith(marker, icc_signature, 12) || + marker->data_length < 14) { + continue; + } + + sequence = marker->data[12]; + count = marker->data[13]; + if (sequence == 0 || count == 0 || sequence > count) { + return GD_META_ERR_PARSE; + } + if (segment_count == 0) { + segment_count = count; + } else if (segment_count != count) { + return GD_META_ERR_PARSE; + } + if (segments[sequence] != NULL) { + return GD_META_ERR_PARSE; + } + segments[sequence] = marker; + segment_sizes[sequence] = marker->data_length - 14; + if ((size_t)-1 - total_size < segment_sizes[sequence]) { + return GD_META_ERR_LIMIT; + } + total_size += segment_sizes[sequence]; + } + + if (segment_count == 0) { + return GD_META_OK; + } + + for (i = 1; i <= segment_count; i++) { + if (segments[i] == NULL) { + return GD_META_ERR_PARSE; + } + } + + icc = (unsigned char *)gdMalloc(total_size); + if (icc == NULL && total_size != 0) { + return GD_META_ERR_NOMEM; + } + + for (i = 1; i <= segment_count; i++) { + if (segment_sizes[i] != 0) { + // codechecker_false_positive [all] suppress all checker results + memcpy(icc + offset, segments[i]->data + 14, segment_sizes[i]); + } + offset += segment_sizes[i]; + } + + status = gdImageMetadataSetProfile(metadata, "icc", icc, total_size); + if (icc != NULL) { + gdFree(icc); + } + return status; +} - if (cinfo.image_width > INT_MAX) { - gd_error_ex(GD_WARNING, "gd-jpeg: warning: JPEG image width (%u) is greater than INT_MAX (%d) (and thus greater than gd can handle)", cinfo.image_width, INT_MAX); - } +static int gdJpegCollectMetadata(j_decompress_ptr cinfo, gdImageMetadata *metadata) +{ + static const unsigned char exif_signature[] = {'E', 'x', 'i', 'f', '\0', '\0'}; + static const unsigned char xmp_signature[] = "http://ns.adobe.com/xap/1.0/"; + static const unsigned char iptc_signature[] = "Photoshop 3.0"; + jpeg_saved_marker_ptr marker; + int status; + + if (metadata == NULL) { + return GD_META_OK; + } + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker->marker == JPEG_APP0 + 1 && + gdJpegMarkerStartsWith(marker, exif_signature, sizeof(exif_signature))) { + status = gdImageMetadataSetProfile(metadata, "exif", marker->data, marker->data_length); + if (status != GD_META_OK) { + return status; + } + } else if (marker->marker == JPEG_APP0 + 1 && + gdJpegMarkerStartsWith(marker, xmp_signature, sizeof(xmp_signature))) { + status = gdImageMetadataSetProfile(metadata, "xmp", marker->data, marker->data_length); + if (status != GD_META_OK) { + return status; + } + } else if (marker->marker == JPEG_APP0 + 13 && + gdJpegMarkerStartsWith(marker, iptc_signature, sizeof(iptc_signature))) { + status = gdImageMetadataSetProfile(metadata, "iptc", marker->data, marker->data_length); + if (status != GD_META_OK) { + return status; + } + } + } + + return gdJpegCollectIccProfile(cinfo, metadata); +} - im = gdImageCreateTrueColor ((int) cinfo.image_width, (int) cinfo.image_height); - if (im == 0) { - gd_error("gd-jpeg error: cannot allocate gdImage struct"); - goto error; - } +/* + Function: gdImageCreateFromJpegCtx - /* check if the resolution is specified */ - switch (cinfo.density_unit) { - case 1: - im->res_x = cinfo.X_density; - im->res_y = cinfo.Y_density; - break; - case 2: - im->res_x = DPCM2DPI(cinfo.X_density); - im->res_y = DPCM2DPI(cinfo.Y_density); - break; - } + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegCtx(gdIOCtx *infile) +{ + return gdImageCreateFromJpegCtxEx(infile, 1); +} - /* 2.0.22: very basic support for reading CMYK colorspace files. Nice for - * thumbnails but there's no support for fussy adjustment of the - * assumed properties of inks and paper. */ - if ((cinfo.jpeg_color_space == JCS_CMYK) || (cinfo.jpeg_color_space == JCS_YCCK)) { - cinfo.out_color_space = JCS_CMYK; - } else { - cinfo.out_color_space = JCS_RGB; - } +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxWithMetadata(gdIOCtx *infile, gdImageMetadata *metadata) +{ + return gdImageCreateFromJpegCtxExWithMetadata(infile, 1, metadata); +} - if (jpeg_start_decompress (&cinfo) != TRUE) { - gd_error("gd-jpeg: warning: jpeg_start_decompress reports suspended data source"); - } +/* + Function: gdImageCreateFromJpegCtxEx - /* REMOVED by TBB 2/12/01. This field of the structure is - * documented as private, and sure enough it's gone in the - * latest libjpeg, replaced by something else. Unfortunately - * there is still no right way to find out if the file was - * progressive or not; just declare your intent before you - * write one by calling gdImageInterlace(im, 1) yourself. - * After all, we're not really supposed to rework JPEGs and - * write them out again anyway. Lossy compression, remember? - */ -#if 0 - gdImageInterlace (im, cinfo.progressive_mode != 0); -#endif + See . +*/ +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning) +{ + return gdImageCreateFromJpegCtxExWithMetadata(infile, ignore_warning, NULL); +} - if (cinfo.out_color_space == JCS_RGB) { - if (cinfo.output_components != 3) { - gd_error_ex(GD_WARNING, "gd-jpeg: error: JPEG color quantization request resulted in output_components == %d (expected 3 for RGB)", cinfo.output_components); - goto error; - } - channels = 3; - } else if (cinfo.out_color_space == JCS_CMYK) { - jpeg_saved_marker_ptr marker; - if (cinfo.output_components != 4) { - gd_error_ex(GD_WARNING, "gd-jpeg: error: JPEG color quantization request resulted in output_components == %d (expected 4 for CMYK)", cinfo.output_components); - goto error; - } - channels = 4; - marker = cinfo.marker_list; - while (marker) { - if ((marker->marker == (JPEG_APP0 + 14)) && (marker->data_length >= 12) && (!strncmp((const char *) marker->data, "Adobe", 5))) { - inverted = 1; - break; - } - marker = marker->next; - } - } else { - gd_error_ex(GD_WARNING, "gd-jpeg: error: unexpected colorspace."); - goto error; - } +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxExWithMetadata(gdIOCtx *infile, int ignore_warning, + gdImageMetadata *metadata) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jmpbuf_wrapper jmpbufw; + /* volatile so we can gdFree them after longjmp */ + volatile JSAMPROW row = 0; + volatile gdImagePtr im = 0; + JSAMPROW rowptr[1]; + JDIMENSION i, j; + int retval; + JDIMENSION nrows; + int channels = 3; + int inverted = 0; + +#ifdef JPEG_DEBUG + gd_error_ex(GD_DEBUG, "gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION); + gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG library version %d, %d-bit sample values\n", + JPEG_LIB_VERSION, BITS_IN_JSAMPLE); + gd_error_ex(GD_DEBUG, "sizeof: %d\n", sizeof(struct jpeg_decompress_struct)); +#endif + memset(&cinfo, 0, sizeof(cinfo)); + memset(&jerr, 0, sizeof(jerr)); + + jmpbufw.ignore_warning = ignore_warning; + + cinfo.err = jpeg_std_error(&jerr); + cinfo.client_data = &jmpbufw; + + cinfo.err->emit_message = jpeg_emit_message; + + if (setjmp(jmpbufw.jmpbuf) != 0) { + /* we're here courtesy of longjmp */ + if (row) { + gdFree(row); + } + if (im) { + gdImageDestroy(im); + } + return 0; + } + + cinfo.err->error_exit = fatal_jpeg_error; + + jpeg_create_decompress(&cinfo); + + jpeg_gdIOCtx_src(&cinfo, infile); + + /* 2.0.22: save the APP14 marker to check for Adobe Photoshop CMYK + * files with inverted components. + */ + jpeg_save_markers(&cinfo, JPEG_APP0 + 14, 256); + if (metadata != NULL) { + jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xFFFF); + jpeg_save_markers(&cinfo, JPEG_APP0 + 2, 0xFFFF); + jpeg_save_markers(&cinfo, JPEG_APP0 + 13, 0xFFFF); + } + + retval = jpeg_read_header(&cinfo, TRUE); + if (retval != JPEG_HEADER_OK) { + gd_error("gd-jpeg: warning: jpeg_read_header returns" + " %d, expected %d\n", + retval, JPEG_HEADER_OK); + } + + retval = gdJpegCollectMetadata(&cinfo, metadata); + if (retval != GD_META_OK) { + gd_error("gd-jpeg: error: unable to read metadata\n"); + goto error; + } + + if (cinfo.image_height > INT_MAX) { + gd_error("gd-jpeg: warning: JPEG image height (%u) is" + " greater than INT_MAX (%d) (and thus greater than" + " gd can handle)", + cinfo.image_height, INT_MAX); + } + + if (cinfo.image_width > INT_MAX) { + gd_error("gd-jpeg: warning: JPEG image width (%u) is" + " greater than INT_MAX (%d) (and thus greater than" + " gd can handle)\n", + cinfo.image_width, INT_MAX); + } + + im = gdImageCreateTrueColor((int)cinfo.image_width, (int)cinfo.image_height); + if (im == 0) { + gd_error("gd-jpeg error: cannot allocate gdImage struct\n"); + goto error; + } + + /* check if the resolution is specified */ + switch (cinfo.density_unit) { + case 1: + im->res_x = cinfo.X_density; + im->res_y = cinfo.Y_density; + break; + case 2: + im->res_x = DPCM2DPI(cinfo.X_density); + im->res_y = DPCM2DPI(cinfo.Y_density); + break; + } + + /* 2.0.22: very basic support for reading CMYK colorspace files. Nice for + * thumbnails but there's no support for fussy adjustment of the + * assumed properties of inks and paper. + */ + if ((cinfo.jpeg_color_space == JCS_CMYK) || (cinfo.jpeg_color_space == JCS_YCCK)) { + cinfo.out_color_space = JCS_CMYK; + } else { + cinfo.out_color_space = JCS_RGB; + } + + if (jpeg_start_decompress(&cinfo) != TRUE) { + gd_error("gd-jpeg: warning: jpeg_start_decompress" + " reports suspended data source\n"); + } + +#ifdef JPEG_DEBUG + gd_error_ex(GD_DEBUG, "gd-jpeg: JPEG image information:"); + if (cinfo.saw_JFIF_marker) { + gd_error_ex(GD_DEBUG, " JFIF version %d.%.2d", (int)cinfo.JFIF_major_version, + (int)cinfo.JFIF_minor_version); + } else if (cinfo.saw_Adobe_marker) { + gd_error_ex(GD_DEBUG, " Adobe format"); + } else { + gd_error_ex(GD_DEBUG, " UNKNOWN format"); + } + + gd_error_ex(GD_DEBUG, " %ux%u (raw) / %ux%u (scaled) %d-bit", cinfo.image_width, + cinfo.image_height, cinfo.output_width, cinfo.output_height, cinfo.data_precision); + gd_error_ex(GD_DEBUG, " %s", (cinfo.progressive_mode ? "progressive" : "baseline")); + gd_error_ex(GD_DEBUG, " image, %d quantized colors, ", cinfo.actual_number_of_colors); + + switch (cinfo.jpeg_color_space) { + case JCS_GRAYSCALE: + gd_error_ex(GD_DEBUG, "grayscale"); + break; + + case JCS_RGB: + gd_error_ex(GD_DEBUG, "RGB"); + break; + + case JCS_YCbCr: + gd_error_ex(GD_DEBUG, "YCbCr (a.k.a. YUV)"); + break; + + case JCS_CMYK: + gd_error_ex(GD_DEBUG, "CMYK"); + break; + + case JCS_YCCK: + gd_error_ex(GD_DEBUG, "YCbCrK"); + break; + + default: + gd_error_ex(GD_DEBUG, "UNKNOWN (value: %d)", (int)cinfo.jpeg_color_space); + break; + } + + gd_error_ex(GD_DEBUG, " colorspace\n"); + fflush(stdout); +#endif /* JPEG_DEBUG */ + + /* REMOVED by TBB 2/12/01. This field of the structure is + * documented as private, and sure enough it's gone in the + * latest libjpeg, replaced by something else. Unfortunately + * there is still no right way to find out if the file was + * progressive or not; just declare your intent before you + * write one by calling gdImageInterlace(im, 1) yourself. + * After all, we're not really supposed to rework JPEGs and + * write them out again anyway. Lossy compression, remember? */ +#if 0 + gdImageInterlace (im, cinfo.progressive_mode != 0); +#endif + if (cinfo.out_color_space == JCS_RGB) { + if (cinfo.output_components != 3) { + gd_error("gd-jpeg: error: JPEG color quantization" + " request resulted in output_components == %d" + " (expected 3 for RGB)\n", + cinfo.output_components); + goto error; + } + channels = 3; + } else if (cinfo.out_color_space == JCS_CMYK) { + jpeg_saved_marker_ptr marker; + if (cinfo.output_components != 4) { + gd_error("gd-jpeg: error: JPEG color quantization" + " request resulted in output_components == %d" + " (expected 4 for CMYK)\n", + cinfo.output_components); + goto error; + } + channels = 4; + + marker = cinfo.marker_list; + while (marker) { + if ((marker->marker == (JPEG_APP0 + 14)) && (marker->data_length >= 12) && + (!strncmp((const char *)marker->data, "Adobe", 5))) { + inverted = 1; + break; + } + marker = marker->next; + } + } else { + gd_error("gd-jpeg: error: unexpected colorspace\n"); + goto error; + } #if BITS_IN_JSAMPLE == 12 - gd_error("gd-jpeg: error: jpeg library was compiled for 12-bit precision. This is mostly useless, because JPEGs on the web are 8-bit and such versions of the jpeg library won't read or write them. GD doesn't support these unusual images. Edit your jmorecfg.h file to specify the correct precision and completely 'make clean' and 'make install' libjpeg again. Sorry."); - goto error; + gd_error_ex(GD_ERROR, "gd-jpeg: error: jpeg library was compiled for 12-bit\n" + "precision. This is mostly useless, because JPEGs on the web are\n" + "8-bit and such versions of the jpeg library won't read or write\n" + "them. GD doesn't support these unusual images. Edit your\n" + "jmorecfg.h file to specify the correct precision and completely\n" + "'make clean' and 'make install' libjpeg again. Sorry.\n"); + goto error; #endif /* BITS_IN_JSAMPLE == 12 */ - row = safe_emalloc(cinfo.output_width * channels, sizeof(JSAMPLE), 0); - memset(row, 0, cinfo.output_width * channels * sizeof(JSAMPLE)); - rowptr[0] = row; - - if (cinfo.out_color_space == JCS_CMYK) { - for (i = 0; i < cinfo.output_height; i++) { - register JSAMPROW currow = row; - register int *tpix = im->tpixels[i]; - nrows = jpeg_read_scanlines (&cinfo, rowptr, 1); - if (nrows != 1) { - gd_error_ex(GD_WARNING, "gd-jpeg: error: jpeg_read_scanlines returns %u, expected 1", nrows); - goto error; - } - for (j = 0; j < cinfo.output_width; j++, currow += 4, tpix++) { - *tpix = CMYKToRGB (currow[0], currow[1], currow[2], currow[3], inverted); - } - } - } else { - for (i = 0; i < cinfo.output_height; i++) { - register JSAMPROW currow = row; - register int *tpix = im->tpixels[i]; - nrows = jpeg_read_scanlines (&cinfo, rowptr, 1); - if (nrows != 1) { - gd_error_ex(GD_WARNING, "gd-jpeg: error: jpeg_read_scanlines returns %u, expected 1", nrows); - goto error; - } - for (j = 0; j < cinfo.output_width; j++, currow += 3, tpix++) { - *tpix = gdTrueColor (currow[0], currow[1], currow[2]); - } - } - } + row = gdCalloc(cinfo.output_width * channels, sizeof(JSAMPLE)); + if (row == 0) { + gd_error("gd-jpeg: error: unable to allocate row for" + " JPEG scanline: gdCalloc returns NULL\n"); + goto error; + } + rowptr[0] = row; + if (cinfo.out_color_space == JCS_CMYK) { + for (i = 0; i < cinfo.output_height; i++) { + register JSAMPROW currow = row; + register int *tpix = im->tpixels[i]; + nrows = jpeg_read_scanlines(&cinfo, rowptr, 1); + if (nrows != 1) { + gd_error("gd-jpeg: error: jpeg_read_scanlines" + " returns %u, expected 1\n", + nrows); + goto error; + } + for (j = 0; j < cinfo.output_width; j++, currow += 4, tpix++) { + *tpix = CMYKToRGB(currow[0], currow[1], currow[2], currow[3], inverted); + } + } + } else { + for (i = 0; i < cinfo.output_height; i++) { + register JSAMPROW currow = row; + register int *tpix = im->tpixels[i]; + nrows = jpeg_read_scanlines(&cinfo, rowptr, 1); + if (nrows != 1) { + gd_error("gd-jpeg: error: jpeg_read_scanlines" + " returns %u, expected 1\n", + nrows); + goto error; + } + for (j = 0; j < cinfo.output_width; j++, currow += 3, tpix++) { + *tpix = gdTrueColor(currow[0], currow[1], currow[2]); + } + } + } + + if (jpeg_finish_decompress(&cinfo) != TRUE) { + gd_error("gd-jpeg: warning: jpeg_finish_decompress" + " reports suspended data source\n"); + } + /* TBB 2.0.29: we should do our best to read whatever we can read, and a + * warning is a warning. A fatal error on warnings doesn't make sense. */ +#if 0 + /* This was originally added by Truxton Fulton */ + if (cinfo.err->num_warnings > 0) + goto error; +#endif - if (jpeg_finish_decompress (&cinfo) != TRUE) { - gd_error("gd-jpeg: warning: jpeg_finish_decompress reports suspended data source"); - } - if (!ignore_warning) { - if (cinfo.err->num_warnings > 0) { - goto error; - } - } + jpeg_destroy_decompress(&cinfo); + gdFree(row); + return im; - jpeg_destroy_decompress (&cinfo); - gdFree (row); +error: + jpeg_destroy_decompress(&cinfo); - return im; + if (row) { + gdFree(row); + } + if (im) { + gdImageDestroy(im); + } -error: - jpeg_destroy_decompress (&cinfo); - if (row) { - gdFree (row); - } - if (im) { - gdImageDestroy (im); - } - return 0; + return 0; } /* A very basic conversion approach, TBB */ + static int CMYKToRGB(int c, int m, int y, int k, int inverted) { + if (inverted) { + c = 255 - c; + m = 255 - m; + y = 255 - y; + k = 255 - k; + } + + return gdTrueColor((255 - c) * (255 - k) / 255, (255 - m) * (255 - k) / 255, + (255 - y) * (255 - k) / 255); +#if 0 if (inverted) { c = 255 - c; m = 255 - m; y = 255 - y; k = 255 - k; } - return gdTrueColor((255 - c) * (255 - k) / 255, (255 - m) * (255 - k) / 255, (255 - y) * (255 - k) / 255); + c = c * (255 - k) / 255 + k; + if (c > 255) { + c = 255; + } + if (c < 0) { + c = 0; + } + m = m * (255 - k) / 255 + k; + if (m > 255) { + m = 255; + } + if (m < 0) { + m = 0; + } + y = y * (255 - k) / 255 + k; + if (y > 255) { + y = 255; + } + if (y < 0) { + y = 0; + } + c = 255 - c; + m = 255 - m; + y = 255 - y; + return gdTrueColor (c, m, y); +#endif } /* * gdIOCtx JPEG data sources and sinks, T. Boutell * almost a simple global replace from T. Lane's stdio versions. - * */ /* Expanded data source object for gdIOCtx input */ - -typedef struct -{ - struct jpeg_source_mgr pub; /* public fields */ - - gdIOCtx *infile; /* source stream */ - unsigned char *buffer; /* start of buffer */ - boolean start_of_file; /* have we gotten any data yet? */ +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + gdIOCtx *infile; /* source stream */ + unsigned char *buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ } my_source_mgr; typedef my_source_mgr *my_src_ptr; -#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ /* * Initialize source --- called by jpeg_read_header * before any data is actually read. */ -void init_source (j_decompress_ptr cinfo) +static void init_source(j_decompress_ptr cinfo) { - my_src_ptr src = (my_src_ptr) cinfo->src; + my_src_ptr src = (my_src_ptr)cinfo->src; - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - src->start_of_file = TRUE; + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; } - /* * Fill the input buffer --- called whenever buffer is emptied. * @@ -610,48 +1285,46 @@ void init_source (j_decompress_ptr cinfo) #define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n" -boolean fill_input_buffer (j_decompress_ptr cinfo) +static boolean fill_input_buffer(j_decompress_ptr cinfo) { - my_src_ptr src = (my_src_ptr) cinfo->src; - /* 2.0.12: signed size. Thanks to Geert Jansen */ - ssize_t nbytes = 0; - - /* ssize_t got; */ - /* char *s; */ - memset(src->buffer, 0, INPUT_BUF_SIZE); - - while (nbytes < INPUT_BUF_SIZE) { - int got = gdGetBuf(src->buffer + nbytes, INPUT_BUF_SIZE - nbytes, src->infile); - - if (got == EOF || got == 0) { - /* EOF or error. If we got any data, don't worry about it. If we didn't, then this is unexpected. */ - if (!nbytes) { - nbytes = -1; - } - break; - } - nbytes += got; - } - - if (nbytes <= 0) { - if (src->start_of_file) { /* Treat empty input file as fatal error */ - ERREXIT (cinfo, JERR_INPUT_EMPTY); - } - WARNMS (cinfo, JWRN_JPEG_EOF); - /* Insert a fake EOI marker */ - src->buffer[0] = (unsigned char) 0xFF; - src->buffer[1] = (unsigned char) JPEG_EOI; - nbytes = 2; - } - - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_file = FALSE; - - return TRUE; + my_src_ptr src = (my_src_ptr)cinfo->src; + /* 2.0.12: signed size. Thanks to Geert Jansen */ + ssize_t nbytes = 0; + memset(src->buffer, 0, INPUT_BUF_SIZE); + + while (nbytes < INPUT_BUF_SIZE) { + int got = gdGetBuf(src->buffer + nbytes, INPUT_BUF_SIZE - nbytes, src->infile); + + if ((got == EOF) || (got == 0)) { + /* EOF or error. If we got any data, don't worry about it. + * If we didn't, then this is unexpected. */ + if (!nbytes) { + nbytes = -1; + } + break; + } + nbytes += got; + } + + if (nbytes <= 0) { + if (src->start_of_file) { + /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + } + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (unsigned char)0xFF; + src->buffer[1] = (unsigned char)JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; } - /* * Skip data --- used to skip over a potentially large amount of * uninteresting data (such as an APPn marker). @@ -664,27 +1337,26 @@ boolean fill_input_buffer (j_decompress_ptr cinfo) * buffer is the application writer's problem. */ -void skip_input_data (j_decompress_ptr cinfo, long num_bytes) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* Just a dumb implementation for now. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) { - while (num_bytes > (long) src->pub.bytes_in_buffer) { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) fill_input_buffer (cinfo); - /* note we assume that fill_input_buffer will never return FALSE, - * so suspension need not be handled. - */ - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; - } +static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr)cinfo->src; + + /* Just a dumb implementation for now. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long)src->pub.bytes_in_buffer) { + num_bytes -= (long)src->pub.bytes_in_buffer; + (void)fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t)num_bytes; + src->pub.bytes_in_buffer -= (size_t)num_bytes; + } } - /* * An additional method that can be provided by data source modules is the * resync_to_restart method for error recovery in the presence of RST markers. @@ -693,7 +1365,6 @@ void skip_input_data (j_decompress_ptr cinfo, long num_bytes) * is possible. */ - /* * Terminate source --- called by jpeg_finish_decompress * after all data has been read. Often a no-op. @@ -702,15 +1373,7 @@ void skip_input_data (j_decompress_ptr cinfo, long num_bytes) * application must deal with any cleanup that should happen even * for error exit. */ - -void term_source (j_decompress_ptr cinfo) -{ -#if 0 - * never used */ - my_src_ptr src = (my_src_ptr) cinfo->src; -#endif -} - +static void term_source(j_decompress_ptr cinfo) { (void)cinfo; } /* * Prepare for input from a gdIOCtx stream. @@ -718,66 +1381,66 @@ void term_source (j_decompress_ptr cinfo) * for closing it after finishing decompression. */ -void jpeg_gdIOCtx_src (j_decompress_ptr cinfo, gdIOCtx * infile) +static void jpeg_gdIOCtx_src(j_decompress_ptr cinfo, gdIOCtx *infile) { - my_src_ptr src; - - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src - * only before the first one. (If we discarded the buffer at the end of - * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. - */ - if (cinfo->src == NULL) { /* first time for this JPEG object? */ - cinfo->src = (struct jpeg_source_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr)); - src = (my_src_ptr) cinfo->src; - src->buffer = (unsigned char *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof (unsigned char)); - - } - - src = (my_src_ptr) cinfo->src; - src->pub.init_source = init_source; - src->pub.fill_input_buffer = fill_input_buffer; - src->pub.skip_input_data = skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ - src->pub.term_source = term_source; - src->infile = infile; - src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - src->pub.next_input_byte = NULL; /* until buffer loaded */ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { + /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr)); + src = (my_src_ptr)cinfo->src; + src->buffer = (unsigned char *)(*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(unsigned char)); + } + + src = (my_src_ptr)cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ } /* Expanded data destination object for stdio output */ - -typedef struct -{ - struct jpeg_destination_mgr pub; /* public fields */ - gdIOCtx *outfile; /* target stream */ - unsigned char *buffer; /* start of buffer */ +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + gdIOCtx *outfile; /* target stream */ + unsigned char *buffer; /* start of buffer */ } my_destination_mgr; typedef my_destination_mgr *my_dest_ptr; -#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ /* * Initialize destination --- called by jpeg_start_compress * before any data is actually written. */ -void init_destination (j_compress_ptr cinfo) +static void init_destination(j_compress_ptr cinfo) { - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + my_dest_ptr dest = (my_dest_ptr)cinfo->dest; - /* Allocate the output buffer --- it will be released when done with image */ - dest->buffer = (unsigned char *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof (unsigned char)); + /* Allocate the output buffer --- it will be released when done with image + */ + dest->buffer = (unsigned char *)(*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof(unsigned char)); - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; } - /* * Empty the output buffer --- called whenever buffer fills up. * @@ -801,21 +1464,20 @@ void init_destination (j_compress_ptr cinfo) * write it out when emptying the buffer externally. */ -boolean empty_output_buffer (j_compress_ptr cinfo) +static boolean empty_output_buffer(j_compress_ptr cinfo) { - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + my_dest_ptr dest = (my_dest_ptr)cinfo->dest; - if (gdPutBuf (dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) != (size_t) OUTPUT_BUF_SIZE) { - ERREXIT (cinfo, JERR_FILE_WRITE); - } + if (gdPutBuf(dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) != (size_t)OUTPUT_BUF_SIZE) { + ERREXIT(cinfo, JERR_FILE_WRITE); + } - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; - return TRUE; + return TRUE; } - /* * Terminate destination --- called by jpeg_finish_compress * after all data has been written. Usually needs to flush buffer. @@ -825,43 +1487,200 @@ boolean empty_output_buffer (j_compress_ptr cinfo) * for error exit. */ -void term_destination (j_compress_ptr cinfo) +static void term_destination(j_compress_ptr cinfo) { - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; - - /* Write any data remaining in the buffer */ - if (datacount > 0 && ((size_t)gdPutBuf (dest->buffer, datacount, dest->outfile) != datacount)) { - ERREXIT (cinfo, JERR_FILE_WRITE); - } + my_dest_ptr dest = (my_dest_ptr)cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if ((size_t)gdPutBuf(dest->buffer, datacount, dest->outfile) != datacount) { + ERREXIT(cinfo, JERR_FILE_WRITE); + } + } } - /* * Prepare for output to a stdio stream. * The caller must have already opened the stream, and is responsible * for closing it after finishing compression. */ -void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile) +static void jpeg_gdIOCtx_dest(j_compress_ptr cinfo, gdIOCtx *outfile) { - my_dest_ptr dest; + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { + /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr)cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} - /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_stdio_dest. - * This makes it dangerous to use this manager and a different destination - * manager serially with the same JPEG object, because their private object - * sizes may be different. Caveat programmer. - */ - if (cinfo->dest == NULL) { /* first time for this JPEG object? */ - cinfo->dest = (struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (my_destination_mgr)); - } +#else /* !HAVE_LIBJPEG */ + +static void _noJpegError(void) { gd_error("JPEG image support has been disabled\n"); } + +BGD_DECLARE(void) gdImageJpeg(gdImagePtr im, FILE *outFile, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + ARG_NOT_USED(quality); + _noJpegError(); +} + +BGD_DECLARE(void *) gdImageJpegPtr(gdImagePtr im, int *size, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(quality); + _noJpegError(); + return NULL; +} - dest = (my_dest_ptr) cinfo->dest; - dest->pub.init_destination = init_destination; - dest->pub.empty_output_buffer = empty_output_buffer; - dest->pub.term_destination = term_destination; - dest->outfile = outfile; +BGD_DECLARE(void *) +gdImageJpegPtrWithMetadata(gdImagePtr im, int *size, int quality, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(quality); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; +} + +void *gdImageJpegPtrWithMetadataNoSubsampling(gdImagePtr im, int *size, int quality, + const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(quality); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(quality); + _noJpegError(); +} + +BGD_DECLARE(void) +gdImageJpegCtxWithMetadata(gdImagePtr im, gdIOCtx *outfile, int quality, + const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(quality); + ARG_NOT_USED(metadata); + _noJpegError(); +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpeg(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegEx(FILE *inFile, int ignore_warning) +{ + ARG_NOT_USED(inFile); + ARG_NOT_USED(ignore_warning); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrEx(int size, void *data, int ignore_warning) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + ARG_NOT_USED(ignore_warning); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrWithMetadata(int size, void *data, gdImageMetadata *metadata) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegPtrExWithMetadata(int size, void *data, int ignore_warning, + gdImageMetadata *metadata) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + ARG_NOT_USED(ignore_warning); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJpegCtx(gdIOCtx *infile) +{ + ARG_NOT_USED(infile); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning) +{ + ARG_NOT_USED(infile); + ARG_NOT_USED(ignore_warning); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxWithMetadata(gdIOCtx *infile, gdImageMetadata *metadata) +{ + ARG_NOT_USED(infile); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromJpegCtxExWithMetadata(gdIOCtx *infile, int ignore_warning, + gdImageMetadata *metadata) +{ + ARG_NOT_USED(infile); + ARG_NOT_USED(ignore_warning); + ARG_NOT_USED(metadata); + _noJpegError(); + return NULL; } #endif /* HAVE_LIBJPEG */ diff --git a/ext/gd/libgd/gd_jxl.c b/ext/gd/libgd/gd_jxl.c new file mode 100644 index 000000000000..77e0c576cbc5 --- /dev/null +++ b/ext/gd/libgd/gd_jxl.c @@ -0,0 +1,1507 @@ +/** + * File: JPEG XL IO + * + * Read and write JPEG XL images. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "gd.h" +#include "gd_errors.h" +#include "gd_intern.h" +#include "gdhelpers.h" +#include +#include +#include +#include + +#ifdef HAVE_LIBJXL +#include +#include +#include +#include + +#define GD_JXL_ALLOC_STEP (4 * 1024) + +/* ---- Internal helpers ---- */ + +/* Slurp gdIOCtx into dynamic buffer (pattern from gd_webp.c) */ +static uint8_t *JxlReadCtxData(gdIOCtx *infile, size_t *size) +{ + uint8_t *filedata = NULL, *temp, *read; + ssize_t n; + + *size = 0; + do { + temp = gdRealloc(filedata, *size + GD_JXL_ALLOC_STEP); + if (temp == NULL) { + gdFree(filedata); + gd_error("JXL decode: realloc failed"); + return NULL; + } + filedata = temp; + read = temp + *size; + n = gdGetBuf(read, GD_JXL_ALLOC_STEP, infile); + if (n > 0 && n != EOF) { + *size += n; + } + } while (n > 0 && n != EOF); + + if (*size == 0) { + gdFree(filedata); + return NULL; + } + + return filedata; +} + +/* JXL alpha (0-255) -> gd alpha (0-127, 0=opaque) */ +static int JxlAlphaJxlToGd(uint8_t jxl_alpha) +{ + if (jxl_alpha == 0) { + return gdAlphaTransparent; + } + return gdAlphaMax - (jxl_alpha >> 1); +} + +/* gd alpha (0-127) -> JXL alpha (0-255) */ +static uint8_t JxlAlphaGdToJxl(int gd_alpha) +{ + if (gd_alpha == gdAlphaTransparent) { + return 0; + } + return (uint8_t)((gdAlphaMax - gd_alpha) << 1); +} + +static int JxlImageHasAlpha(gdImagePtr im) +{ + int x, y; + + if (im == NULL) { + return 0; + } + + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + if (gdTrueColorGetAlpha(im->tpixels[y][x]) != gdAlphaOpaque) { + return 1; + } + } + } + + return 0; +} + +static int JxlDurationToMs(uint32_t duration, const JxlAnimationHeader *animation) +{ + if (animation == NULL || animation->tps_numerator == 0) { + return 0; + } + + return (int)((uint64_t)duration * 1000 * animation->tps_denominator / animation->tps_numerator); +} + +/* Build gdImagePtr from RGBA u8 buffer */ +static gdImagePtr JxlImageFromRGBA(const uint8_t *rgba, int width, int height, int has_alpha) +{ + gdImagePtr im; + const uint8_t *p; + int x, y; + + if (rgba == NULL || width <= 0 || height <= 0) { + return NULL; + } + im = gdImageCreateTrueColor(width, height); + if (im == NULL) { + return NULL; + } + gdImageAlphaBlending(im, 0); + gdImageSaveAlpha(im, has_alpha); + for (y = 0, p = rgba; y < height; y++) { + for (x = 0; x < width; x++) { + uint8_t r = *(p++); + uint8_t g = *(p++); + uint8_t b = *(p++); + uint8_t a = *(p++); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, JxlAlphaJxlToGd(a)); + } + } + return im; +} + +/* Extract RGBA u8 buffer from gdImagePtr */ +static int JxlImageToRGBA(gdImagePtr im, uint8_t **rgba, int *has_alpha) +{ + uint8_t *p; + int x, y; + int w, h; + + *rgba = NULL; + *has_alpha = 0; + + if (im == NULL) { + return 0; + } + + /* Convert palette to truecolor if needed */ + if (!gdImageTrueColor(im)) { + gdImagePaletteToTrueColor(im); + } + + w = gdImageSX(im); + h = gdImageSY(im); + + /* Check for alpha presence */ + if (gdImageGetTransparent(im) != -1 || JxlImageHasAlpha(im)) { + *has_alpha = 1; + } + + /* Overflow checks */ + if (overflow2(w, 4)) { + return 0; + } + if (overflow2(w * 4, h)) { + return 0; + } + + *rgba = (uint8_t *)gdMalloc((size_t)w * 4 * (size_t)h); + if (*rgba == NULL) { + return 0; + } + + p = *rgba; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + int c = im->tpixels[y][x]; + *(p++) = gdTrueColorGetRed(c); + *(p++) = gdTrueColorGetGreen(c); + *(p++) = gdTrueColorGetBlue(c); + *(p++) = JxlAlphaGdToJxl(gdTrueColorGetAlpha(c)); + } + } + return 1; +} + +static uint8_t *JxlRGBFromRGBA(const uint8_t *rgba, int width, int height) +{ + uint8_t *rgb, *dst; + const uint8_t *src; + int x, y; + + if (overflow2(width, 3) || overflow2(width * 3, height)) { + return NULL; + } + + rgb = (uint8_t *)gdMalloc((size_t)width * 3 * (size_t)height); + if (rgb == NULL) { + return NULL; + } + + src = rgba; + dst = rgb; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } + } + + return rgb; +} + +/* ---- Still-image decode ---- */ + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxl(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (!in) { + return 0; + } + im = gdImageCreateFromJxlCtx(in); + in->gd_free(in); + return im; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return 0; + } + im = gdImageCreateFromJxlCtx(in); + in->gd_free(in); + return im; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlCtx(gdIOCtxPtr inCtx) +{ + size_t buf_len = 0; + uint8_t *buf = NULL; + JxlDecoder *dec = NULL; + JxlBasicInfo info = {0}; + uint8_t *pixels = NULL; + gdImagePtr im = NULL; + int has_alpha = 0; + int have_basic_info = 0; + + if (inCtx == NULL) { + return NULL; + } + + buf = JxlReadCtxData(inCtx, &buf_len); + if (buf == NULL) { + return NULL; + } + + dec = JxlDecoderCreate(NULL); + if (dec == NULL) { + gd_error("gdImageCreateFromJxl: JxlDecoderCreate failed"); + gdFree(buf); + return NULL; + } + + JxlDecoderSubscribeEvents(dec, + JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE); + + /* Attach CMS for color space conversion to sRGB */ + JxlDecoderSetCms(dec, *JxlGetDefaultCms()); + + /* Set desired output color profile (sRGB) */ + JxlColorEncoding srgb_enc; + JxlColorEncodingSetToSRGB(&srgb_enc, JXL_FALSE); + JxlDecoderSetPreferredColorProfile(dec, &srgb_enc); + + JxlDecoderSetInput(dec, buf, buf_len); + JxlDecoderCloseInput(dec); + + for (;;) { + JxlDecoderStatus status = JxlDecoderProcessInput(dec); + + if (status == JXL_DEC_BASIC_INFO) { + status = JxlDecoderGetBasicInfo(dec, &info); + if (status != JXL_DEC_SUCCESS) { + gd_error("gdImageCreateFromJxl: failed to get basic info"); + goto decode_fail; + } + have_basic_info = 1; + has_alpha = (info.alpha_bits > 0); + } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + JxlPixelFormat fmt = {.num_channels = 4, /* RGBA */ + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align = 0}; + size_t pixels_size; + + if (!have_basic_info) { + gd_error("gdImageCreateFromJxl: missing basic info"); + goto decode_fail; + } + if (overflow2((int)info.xsize, (int)info.ysize) || + overflow2((int)info.xsize * (int)info.ysize, 4)) { + gd_error("gdImageCreateFromJxl: image dimensions overflow"); + goto decode_fail; + } + + pixels_size = (size_t)info.xsize * (size_t)info.ysize * 4; + pixels = (uint8_t *)gdMalloc(pixels_size); + if (pixels == NULL) { + gd_error("gdImageCreateFromJxl: pixel buffer allocation failed"); + goto decode_fail; + } + + if (JxlDecoderSetImageOutBuffer(dec, &fmt, pixels, pixels_size) != JXL_DEC_SUCCESS) { + gd_error("gdImageCreateFromJxl: failed to set output buffer"); + goto decode_fail; + } + } else if (status == JXL_DEC_FULL_IMAGE) { + if (!have_basic_info || pixels == NULL) { + gd_error("gdImageCreateFromJxl: incomplete decoded image"); + goto decode_fail; + } + break; /* pixels buffer is populated */ + } else if (status == JXL_DEC_SUCCESS || status == JXL_DEC_ERROR) { + gd_error("gdImageCreateFromJxl: decoder error"); + goto decode_fail; + } + } + + /* Build gdImagePtr from RGBA buffer */ + im = JxlImageFromRGBA(pixels, (int)info.xsize, (int)info.ysize, has_alpha); + if (!im) { + gd_error("gdImageCreateFromJxl: JxlImageFromRGBA failed"); + goto decode_fail; + } + + /* Success path - pixels consumed by im */ + gdFree(pixels); + pixels = NULL; + gdFree(buf); + buf = NULL; + JxlDecoderDestroy(dec); + return im; + +decode_fail: + if (pixels) + gdFree(pixels); + if (buf) + gdFree(buf); + if (dec) + JxlDecoderDestroy(dec); + return NULL; +} + +/* ---- Still-image encode ---- */ + +static int _gdImageJxlCtxEx(gdImagePtr im, gdIOCtx *outfile, int lossless, float distance, + int effort) +{ + JxlEncoder *enc = NULL; + JxlEncoderFrameSettings *frame_opts = NULL; + uint8_t *pixels = NULL; + int has_alpha = 0; + uint8_t outbuf[65536]; + int w, h; + int ret = 1; + + if (im == NULL) { + return 1; + } + + w = gdImageSX(im); + h = gdImageSY(im); + + /* Extract RGBA pixels */ + if (!JxlImageToRGBA(im, &pixels, &has_alpha)) { + gd_error("gdImageJxl: pixel extraction failed"); + return 1; + } + + enc = JxlEncoderCreate(NULL); + if (enc == NULL) { + gd_error("gdImageJxl: JxlEncoderCreate failed"); + gdFree(pixels); + return 1; + } + + /* Set basic info */ + JxlBasicInfo info; + JxlEncoderInitBasicInfo(&info); + info.xsize = w; + info.ysize = h; + info.bits_per_sample = 8; + info.exponent_bits_per_sample = 0; + info.num_color_channels = 3; + info.num_extra_channels = has_alpha ? 1 : 0; + info.alpha_bits = has_alpha ? 8 : 0; + info.alpha_exponent_bits = 0; + info.alpha_premultiplied = JXL_FALSE; + info.uses_original_profile = lossless ? JXL_TRUE : JXL_FALSE; + + if (JxlEncoderSetBasicInfo(enc, &info) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderSetBasicInfo failed"); + goto encode_fail; + } + + /* Set color encoding (sRGB) */ + JxlColorEncoding srgb_enc; + JxlColorEncodingSetToSRGB(&srgb_enc, JXL_FALSE); + if (JxlEncoderSetColorEncoding(enc, &srgb_enc) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderSetColorEncoding failed"); + goto encode_fail; + } + + /* Configure frame options */ + frame_opts = JxlEncoderFrameSettingsCreate(enc, NULL); + if (frame_opts == NULL) { + gd_error("gdImageJxl: JxlEncoderFrameSettingsCreate failed"); + goto encode_fail; + } + + if (lossless) { + if (JxlEncoderSetFrameLossless(frame_opts, JXL_TRUE) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderSetFrameLossless failed"); + goto encode_fail; + } + } else { + if (JxlEncoderSetFrameDistance(frame_opts, distance) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderSetFrameDistance failed"); + goto encode_fail; + } + } + + if (JxlEncoderFrameSettingsSetOption(frame_opts, JXL_ENC_FRAME_SETTING_EFFORT, effort) != + JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderFrameSettingsSetOption effort failed"); + goto encode_fail; + } + + /* Add image frame */ + uint8_t *frame_pixels = pixels; + size_t frame_pixels_size = (size_t)w * (size_t)h * 4; + JxlPixelFormat fmt = {.num_channels = has_alpha ? 4u : 3u, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align = 0}; + + if (!has_alpha) { + frame_pixels = JxlRGBFromRGBA(pixels, w, h); + if (frame_pixels == NULL) { + gd_error("gdImageJxl: RGB buffer allocation failed"); + goto encode_fail; + } + frame_pixels_size = (size_t)w * (size_t)h * 3; + } + + if (JxlEncoderAddImageFrame(frame_opts, &fmt, frame_pixels, frame_pixels_size) != + JXL_ENC_SUCCESS) { + gd_error("gdImageJxl: JxlEncoderAddImageFrame failed"); + if (frame_pixels != pixels) + gdFree(frame_pixels); + goto encode_fail; + } + if (frame_pixels != pixels) + gdFree(frame_pixels); + + JxlEncoderCloseInput(enc); + for (;;) { + uint8_t *next_out = outbuf; + size_t avail = sizeof(outbuf); + JxlEncoderStatus st = JxlEncoderProcessOutput(enc, &next_out, &avail); + + size_t written = sizeof(outbuf) - avail; + if (written > 0) { + if (gdPutBuf(outbuf, (int)written, outfile) != (int)written) { + gd_error("gdImageJxl: write error"); + goto encode_fail; + } + } + + if (st == JXL_ENC_SUCCESS) { + ret = 0; + break; + } + if (st != JXL_ENC_NEED_MORE_OUTPUT) { + gd_error("gdImageJxl: encoder error"); + goto encode_fail; + } + } + +encode_fail: + // frame_opts is owned by enc, so no separate destroy needed + if (enc) + JxlEncoderDestroy(enc); + if (pixels) + gdFree(pixels); + return ret; +} + +BGD_DECLARE(void) gdImageJxl(gdImagePtr im, FILE *outFile) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageJxlCtxEx(im, out, 0, 1.0f, 7); + out->gd_free(out); +} + +BGD_DECLARE(void) +gdImageJxlEx(gdImagePtr im, FILE *outFile, int lossless, float distance, int effort) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageJxlCtxEx(im, out, lossless, distance, effort); + out->gd_free(out); +} + +BGD_DECLARE(void *) gdImageJxlPtr(gdImagePtr im, int *size) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageJxlCtxEx(im, out, 0, 1.0f, 7)) { + rv = NULL; + } else { + rv = gdDPExtractData(out, size); + } + out->gd_free(out); + return rv; +} + +BGD_DECLARE(void *) +gdImageJxlPtrEx(gdImagePtr im, int *size, int lossless, float distance, int effort) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageJxlCtxEx(im, out, lossless, distance, effort)) { + rv = NULL; + } else { + rv = gdDPExtractData(out, size); + } + out->gd_free(out); + return rv; +} + +BGD_DECLARE(void) gdImageJxlCtx(gdImagePtr im, gdIOCtxPtr outfile) +{ + _gdImageJxlCtxEx(im, outfile, 0, 1.0f, 7); +} + +BGD_DECLARE(void) +gdImageJxlCtxEx(gdImagePtr im, gdIOCtxPtr outfile, int lossless, float distance, int effort) +{ + _gdImageJxlCtxEx(im, outfile, lossless, distance, effort); +} + +/* ---- Animation structures ---- */ + +typedef struct gdJxlAnim { + JxlEncoder *enc; + JxlEncoderFrameSettings *frame_opts; + gdIOCtxPtr ctx; + uint32_t width; + uint32_t height; + int has_alpha; + int ownsCtx; + int memoryWriter; + int finalized; + int timestamp; +} gdJxlAnim; + +typedef struct gdJxlAnimReader { + JxlDecoder *dec; + uint8_t *buf; + size_t buf_len; + JxlBasicInfo info; + int coalesced; + int done; + int last_frame_seen; +} gdJxlAnimReader; + +/* ---- Animation write ---- */ + +static int JxlAnimDrainEncoder(gdJxlAnim *anim) +{ + uint8_t outbuf[65536]; + for (;;) { + uint8_t *next_out = outbuf; + size_t avail = sizeof(outbuf); + JxlEncoderStatus st = JxlEncoderProcessOutput(anim->enc, &next_out, &avail); + + size_t written = sizeof(outbuf) - avail; + if (written > 0) { + if (gdPutBuf(outbuf, (int)written, anim->ctx) != (int)written) { + gd_error("gdImageJxlAnim: write error"); + return 0; + } + } + + if (st == JXL_ENC_SUCCESS) { + return 1; + } + if (st != JXL_ENC_NEED_MORE_OUTPUT) { + gd_error("gdImageJxlAnim: encoder error"); + return 0; + } + } +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBegin(FILE *outFile, int width, int height, int lossless, float distance, int effort) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return NULL; + } + gdJxlAnimPtr anim = gdImageJxlAnimBeginCtx(out, width, height, lossless, distance, effort); + if (anim) { + anim->ownsCtx = 1; + } else { + out->gd_free(out); + } + return anim; +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginCtx(gdIOCtxPtr outCtx, int width, int height, int lossless, float distance, + int effort) +{ + gdJxlAnimPtr anim; + JxlBasicInfo info; + + if (outCtx == NULL || width <= 0 || height <= 0) { + return NULL; + } + + anim = (gdJxlAnimPtr)gdCalloc(1, sizeof(struct gdJxlAnim)); + if (anim == NULL) { + return NULL; + } + + anim->enc = JxlEncoderCreate(NULL); + if (anim->enc == NULL) { + gd_error("gdImageJxlAnimBegin: JxlEncoderCreate failed"); + gdFree(anim); + return NULL; + } + + anim->ctx = outCtx; + anim->width = width; + anim->height = height; + anim->ownsCtx = 0; + anim->memoryWriter = 0; + anim->finalized = 0; + anim->timestamp = 0; + + /* Set basic info for animation */ + JxlEncoderInitBasicInfo(&info); + info.xsize = width; + info.ysize = height; + info.bits_per_sample = 8; + info.exponent_bits_per_sample = 0; + info.num_color_channels = 3; + info.num_extra_channels = 1; + info.alpha_bits = 8; + info.alpha_exponent_bits = 0; + info.alpha_premultiplied = JXL_FALSE; + info.uses_original_profile = lossless ? JXL_TRUE : JXL_FALSE; + info.have_animation = JXL_TRUE; + info.animation.tps_numerator = 1000; /* ms == ticks */ + info.animation.tps_denominator = 1; + info.animation.num_loops = 0; /* loop forever */ + + if (JxlEncoderSetBasicInfo(anim->enc, &info) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimBegin: JxlEncoderSetBasicInfo failed"); + JxlEncoderDestroy(anim->enc); + gdFree(anim); + return NULL; + } + + /* Set color encoding (sRGB) */ + JxlColorEncoding srgb_enc; + JxlColorEncodingSetToSRGB(&srgb_enc, JXL_FALSE); + if (JxlEncoderSetColorEncoding(anim->enc, &srgb_enc) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimBegin: JxlEncoderSetColorEncoding failed"); + JxlEncoderDestroy(anim->enc); + gdFree(anim); + return NULL; + } + + /* Create frame settings once, reuse for all frames */ + anim->frame_opts = JxlEncoderFrameSettingsCreate(anim->enc, NULL); + if (anim->frame_opts == NULL) { + gd_error("gdImageJxlAnimBegin: JxlEncoderFrameSettingsCreate failed"); + JxlEncoderDestroy(anim->enc); + gdFree(anim); + return NULL; + } + + if (lossless) { + if (JxlEncoderSetFrameLossless(anim->frame_opts, JXL_TRUE) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimBegin: JxlEncoderSetFrameLossless failed"); + goto anim_begin_fail; + } + } else { + if (JxlEncoderSetFrameDistance(anim->frame_opts, distance) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimBegin: JxlEncoderSetFrameDistance failed"); + goto anim_begin_fail; + } + } + + if (JxlEncoderFrameSettingsSetOption(anim->frame_opts, JXL_ENC_FRAME_SETTING_EFFORT, effort) != + JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimBegin: JxlEncoderFrameSettingsSetOption effort " + "failed"); + goto anim_begin_fail; + } + + return anim; + +anim_begin_fail: + // anim->frame_opts is owned by anim->enc, so no separate destroy needed + if (anim->enc) + JxlEncoderDestroy(anim->enc); + gdFree(anim); + return NULL; +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginPtr(int width, int height, int lossless, float distance, int effort) +{ + gdIOCtx *out; + gdJxlAnimPtr anim; + + out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + + anim = gdImageJxlAnimBeginCtx(out, width, height, lossless, distance, effort); + if (anim == NULL) { + out->gd_free(out); + return NULL; + } + + anim->ownsCtx = 1; + anim->memoryWriter = 1; + return anim; +} + +BGD_DECLARE(int) +gdImageJxlAnimAddFrame(gdJxlAnimPtr anim, gdImagePtr im, int delay_ms) +{ + JxlFrameHeader fhdr; + JxlPixelFormat fmt; + uint8_t *pixels = NULL; + int has_alpha = 0; + int w, h; + + if (anim == NULL || im == NULL || delay_ms < 0 || anim->finalized) { + return 0; + } + + if (!gdImageTrueColor(im)) { + gd_error("Palette image not supported by JXL animation"); + return 0; + } + + w = gdImageSX(im); + h = gdImageSY(im); + + if (w != (int)anim->width || h != (int)anim->height) { + gd_error("gdImageJxlAnimAddFrame: frame size must match canvas size"); + return 0; + } + + if (!JxlImageToRGBA(im, &pixels, &has_alpha)) { + gd_error("gdImageJxlAnimAddFrame: pixel extraction failed"); + return 0; + } + + /* Update alpha info for this frame if needed */ + if (has_alpha && anim->has_alpha == 0) { + anim->has_alpha = 1; + } + + /* Set frame header - duration only, REPLACE blend, full canvas */ + JxlEncoderInitFrameHeader(&fhdr); + fhdr.duration = (uint32_t)delay_ms; /* ticks == ms */ + + if (JxlEncoderSetFrameHeader(anim->frame_opts, &fhdr) != JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimAddFrame: JxlEncoderSetFrameHeader failed"); + gdFree(pixels); + return 0; + } + + fmt.num_channels = 4; + fmt.data_type = JXL_TYPE_UINT8; + fmt.endianness = JXL_NATIVE_ENDIAN; + fmt.align = 0; + + if (JxlEncoderAddImageFrame(anim->frame_opts, &fmt, pixels, (size_t)w * (size_t)h * 4) != + JXL_ENC_SUCCESS) { + gd_error("gdImageJxlAnimAddFrame: JxlEncoderAddImageFrame failed"); + gdFree(pixels); + return 0; + } + + gdFree(pixels); + pixels = NULL; + + anim->timestamp += delay_ms; + return 1; +} + +BGD_DECLARE(int) gdImageJxlAnimEnd(gdJxlAnimPtr anim) +{ + int ret = 0; + + if (anim == NULL) { + return 0; + } + + if (!anim->finalized) { + JxlEncoderCloseInput(anim->enc); + + if (!JxlAnimDrainEncoder(anim)) { + goto anim_end_cleanup; + } + + anim->finalized = 1; + } + + ret = 1; + +anim_end_cleanup: + // anim->frame_opts is owned by anim->enc, so no separate destroy needed + if (anim->enc) + JxlEncoderDestroy(anim->enc); + if (anim->ownsCtx && anim->ctx) + anim->ctx->gd_free(anim->ctx); + gdFree(anim); + return ret; +} + +BGD_DECLARE(void *) gdImageJxlAnimEndPtr(gdJxlAnimPtr anim, int *size) +{ + void *rv = NULL; + + if (size != NULL) { + *size = 0; + } + if (anim == NULL || !anim->memoryWriter) { + if (anim != NULL) { + gdImageJxlAnimEnd(anim); + } + return NULL; + } + + if (!anim->finalized) { + JxlEncoderCloseInput(anim->enc); + + if (!JxlAnimDrainEncoder(anim)) { + goto anim_end_ptr_cleanup; + } + + anim->finalized = 1; + } + + rv = gdDPExtractData(anim->ctx, size); + +anim_end_ptr_cleanup: + if (anim->enc) + JxlEncoderDestroy(anim->enc); + if (anim->ctx) + anim->ctx->gd_free(anim->ctx); + gdFree(anim); + return rv; +} + +/* ---- Animation read (coalesced & raw) ---- */ + +static gdJxlAnimReaderPtr _gdImageJxlAnimReaderCreateCtx(gdIOCtxPtr inCtx, int coalesced) +{ + size_t buf_len = 0; + uint8_t *buf = NULL; + JxlDecoder *dec = NULL; + gdJxlAnimReaderPtr reader = NULL; + + if (inCtx == NULL) { + return NULL; + } + + buf = JxlReadCtxData(inCtx, &buf_len); + if (buf == NULL) { + return NULL; + } + + dec = JxlDecoderCreate(NULL); + if (dec == NULL) { + gd_error("gdImageJxlAnimReaderCreate: JxlDecoderCreate failed"); + gdFree(buf); + return NULL; + } + + reader = (gdJxlAnimReaderPtr)gdCalloc(1, sizeof(struct gdJxlAnimReader)); + if (reader == NULL) { + JxlDecoderDestroy(dec); + gdFree(buf); + return NULL; + } + + if (coalesced) { + JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | + JXL_DEC_FULL_IMAGE); + JxlDecoderSetCoalescing(dec, JXL_TRUE); + } else { + JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | + JXL_DEC_FULL_IMAGE); + JxlDecoderSetCoalescing(dec, JXL_FALSE); + } + + /* Attach CMS for color space conversion to sRGB */ + JxlDecoderSetCms(dec, *JxlGetDefaultCms()); + + /* Set desired output color profile (sRGB) */ + JxlColorEncoding srgb_enc; + JxlColorEncodingSetToSRGB(&srgb_enc, JXL_FALSE); + JxlDecoderSetPreferredColorProfile(dec, &srgb_enc); + + JxlDecoderSetInput(dec, buf, buf_len); + JxlDecoderCloseInput(dec); + + /* Process until BASIC_INFO to get dimensions */ + for (;;) { + JxlDecoderStatus status = JxlDecoderProcessInput(dec); + if (status == JXL_DEC_BASIC_INFO) { + if (JxlDecoderGetBasicInfo(dec, &reader->info) != JXL_DEC_SUCCESS) { + gd_error("gdImageJxlAnimReaderCreate: failed to get basic info"); + JxlDecoderDestroy(dec); + gdFree(buf); + gdFree(reader); + return NULL; + } + break; + } + if (status == JXL_DEC_ERROR || status == JXL_DEC_SUCCESS) { + gd_error("gdImageJxlAnimReaderCreate: failed to get basic info"); + JxlDecoderDestroy(dec); + gdFree(buf); + gdFree(reader); + return NULL; + } + } + + reader->dec = dec; + reader->buf = buf; + reader->buf_len = buf_len; + reader->coalesced = coalesced; + reader->done = 0; + reader->last_frame_seen = 0; + + return reader; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreate(FILE *inFile) +{ + gdIOCtx *in = gdNewFileCtx(inFile); + if (!in) { + return NULL; + } + gdJxlAnimReaderPtr reader = _gdImageJxlAnimReaderCreateCtx(in, JXL_TRUE); + in->gd_free(in); + return reader; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreatePtr(int size, void *data) +{ + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return NULL; + } + gdJxlAnimReaderPtr reader = _gdImageJxlAnimReaderCreateCtx(in, JXL_TRUE); + in->gd_free(in); + return reader; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateCtx(gdIOCtxPtr inCtx) +{ + return _gdImageJxlAnimReaderCreateCtx(inCtx, JXL_TRUE); +} + +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateRaw(FILE *inFile) +{ + gdIOCtx *in = gdNewFileCtx(inFile); + if (!in) { + return NULL; + } + gdJxlAnimReaderPtr reader = _gdImageJxlAnimReaderCreateCtx(in, JXL_FALSE); + in->gd_free(in); + return reader; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateRawPtr(int size, void *data) +{ + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return NULL; + } + gdJxlAnimReaderPtr reader = _gdImageJxlAnimReaderCreateCtx(in, JXL_FALSE); + in->gd_free(in); + return reader; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateRawCtx(gdIOCtxPtr inCtx) +{ + return _gdImageJxlAnimReaderCreateCtx(inCtx, JXL_FALSE); +} + +/* Helper: build gdImagePtr from current decoder state */ +static gdImagePtr JxlAnimReaderGetCurrentFrame(gdJxlAnimReaderPtr reader, int *delay_ms) +{ + JxlDecoder *dec = reader->dec; + JxlBasicInfo info = reader->info; + uint8_t *pixels = NULL; + gdImagePtr im = NULL; + int have_frame_header = 0; + + if (reader->done) { + return NULL; + } + + for (;;) { + JxlDecoderStatus status = JxlDecoderProcessInput(dec); + + if (status == JXL_DEC_FRAME) { + JxlFrameHeader fhdr = {0}; + status = JxlDecoderGetFrameHeader(dec, &fhdr); + if (status != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to get frame header"); + if (pixels) + gdFree(pixels); + return NULL; + } + have_frame_header = 1; + reader->last_frame_seen = fhdr.is_last == JXL_TRUE; + if (delay_ms) { + *delay_ms = JxlDurationToMs(fhdr.duration, &info.animation); + } + } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + JxlPixelFormat fmt = {.num_channels = 4, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align = 0}; + size_t pixels_size; + int w, h; + + if (reader->coalesced) { + w = (int)info.xsize; + h = (int)info.ysize; + } else { + /* For non-coalesced, we need to query the frame size */ + size_t buf_size; + if (!have_frame_header) { + gd_error("gdJxlReadNextFrame: missing frame header"); + return NULL; + } + if (JxlDecoderImageOutBufferSize(dec, &fmt, &buf_size) != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to get buffer size"); + return NULL; + } + w = (int)sqrt(buf_size / 4); /* approximation - we'll get actual size from fmt */ + h = (int)(buf_size / 4 / w); + /* Actually better: we know the frame size from layer_info but + * for simplicity with coalesced=false we allocate based on + * the buffer size reported */ + } + + if (overflow2(w, h) || overflow2(w * h, 4)) { + gd_error("gdJxlReadNextFrame: frame dimensions overflow"); + return NULL; + } + + pixels_size = (size_t)w * (size_t)h * 4; + pixels = (uint8_t *)gdMalloc(pixels_size); + if (pixels == NULL) { + gd_error("gdJxlReadNextFrame: pixel buffer allocation failed"); + return NULL; + } + + if (JxlDecoderSetImageOutBuffer(dec, &fmt, pixels, pixels_size) != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to set output buffer"); + gdFree(pixels); + return NULL; + } + } else if (status == JXL_DEC_FULL_IMAGE) { + int has_alpha = (info.alpha_bits > 0); + int w, h; + + if (reader->coalesced) { + w = (int)info.xsize; + h = (int)info.ysize; + } else { + /* For non-coalesced, we don't know exact dimensions here. + * We'll reconstruct from the buffer. This is a simplification. + */ + w = (int)info.xsize; + h = (int)info.ysize; + } + + im = JxlImageFromRGBA(pixels, w, h, has_alpha); + if (pixels) + gdFree(pixels); + if (reader->last_frame_seen) + reader->done = 1; + return im; + } else if (status == JXL_DEC_SUCCESS) { + reader->done = 1; + if (pixels) + gdFree(pixels); + return NULL; + } else if (status == JXL_DEC_ERROR) { + gd_error("gdJxlReadNextFrame: decoder error"); + if (pixels) + gdFree(pixels); + return NULL; + } + } +} + +BGD_DECLARE(gdImagePtr) +gdJxlReadNextImage(gdJxlAnimReaderPtr reader, int *delay_ms) +{ + if (reader == NULL || reader->coalesced == 0) { + return NULL; + } + return JxlAnimReaderGetCurrentFrame(reader, delay_ms); +} + +/* For non-coalesced reader, we need to extract frame info */ +static void JxlFrameHeaderToInfo(const JxlFrameHeader *fhdr, const JxlBasicInfo *info, + gdJxlFrameInfo *out) +{ + out->delay_ms = JxlDurationToMs(fhdr->duration, &info->animation); + out->x_offset = (int)fhdr->layer_info.crop_x0; + out->y_offset = (int)fhdr->layer_info.crop_y0; + out->width = (int)fhdr->layer_info.xsize; + out->height = (int)fhdr->layer_info.ysize; + out->blend_mode = (int)fhdr->layer_info.blend_info.blendmode; + out->is_last = (int)fhdr->is_last; +} + +static gdImagePtr JxlAnimReaderGetCurrentRawFrame(gdJxlAnimReaderPtr reader, gdJxlFrameInfo *info) +{ + JxlDecoder *dec = reader->dec; + JxlBasicInfo basic = reader->info; + JxlFrameHeader fhdr = {0}; + uint8_t *pixels = NULL; + gdImagePtr im = NULL; + int has_alpha = 0; + int have_frame_header = 0; + int w = 0, h = 0; + + if (reader->done) { + return NULL; + } + + for (;;) { + JxlDecoderStatus status = JxlDecoderProcessInput(dec); + + if (status == JXL_DEC_FRAME) { + status = JxlDecoderGetFrameHeader(dec, &fhdr); + if (status != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to get frame header"); + return NULL; + } + have_frame_header = 1; + reader->last_frame_seen = fhdr.is_last == JXL_TRUE; + JxlFrameHeaderToInfo(&fhdr, &basic, info); + has_alpha = (basic.alpha_bits > 0); + } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + JxlPixelFormat fmt = {.num_channels = 4, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align = 0}; + size_t pixels_size; + + /* Query the exact buffer size for this frame */ + if (!have_frame_header) { + gd_error("gdJxlReadNextFrame: missing frame header"); + return NULL; + } + if (JxlDecoderImageOutBufferSize(dec, &fmt, &pixels_size) != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to get buffer size"); + return NULL; + } + w = (int)fhdr.layer_info.xsize; + h = (int)fhdr.layer_info.ysize; + + if (overflow2(w, h) || overflow2(w * h, 4)) { + gd_error("gdJxlReadNextFrame: frame dimensions overflow"); + return NULL; + } + + /* Verify pixels_size matches expected */ + if (pixels_size != (size_t)w * (size_t)h * 4) { + gd_error("gdJxlReadNextFrame: unexpected buffer size"); + return NULL; + } + + pixels = (uint8_t *)gdMalloc(pixels_size); + if (pixels == NULL) { + gd_error("gdJxlReadNextFrame: pixel buffer allocation failed"); + return NULL; + } + + if (JxlDecoderSetImageOutBuffer(dec, &fmt, pixels, pixels_size) != JXL_DEC_SUCCESS) { + gd_error("gdJxlReadNextFrame: failed to set output buffer"); + gdFree(pixels); + return NULL; + } + } else if (status == JXL_DEC_FULL_IMAGE) { + if (pixels == NULL) { + gd_error("gdJxlReadNextFrame: incomplete decoded frame"); + return NULL; + } + w = info->width; + h = info->height; + im = JxlImageFromRGBA(pixels, w, h, has_alpha); + if (pixels) + gdFree(pixels); + if (reader->last_frame_seen) + reader->done = 1; + return im; + } else if (status == JXL_DEC_SUCCESS) { + reader->done = 1; + if (pixels) + gdFree(pixels); + return NULL; + } else if (status == JXL_DEC_ERROR) { + gd_error("gdJxlReadNextFrame: decoder error"); + if (pixels) + gdFree(pixels); + return NULL; + } + } +} + +BGD_DECLARE(gdImagePtr) +gdJxlReadNextFrame(gdJxlAnimReaderPtr reader, gdJxlFrameInfo *info) +{ + if (reader == NULL || reader->coalesced != 0 || info == NULL) { + return NULL; + } + return JxlAnimReaderGetCurrentRawFrame(reader, info); +} + +BGD_DECLARE(void) gdImageJxlAnimReaderDestroy(gdJxlAnimReaderPtr reader) +{ + if (reader == NULL) { + return; + } + if (reader->dec) + JxlDecoderDestroy(reader->dec); + if (reader->buf) + gdFree(reader->buf); + gdFree(reader); +} + +#else /* !HAVE_LIBJXL */ + +static void _noJxlError(void) { gd_error("JXL image support has been disabled\n"); } + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxl(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromJxlCtx(gdIOCtx *infile) +{ + ARG_NOT_USED(infile); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(void) gdImageJxl(gdImagePtr im, FILE *outFile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + _noJxlError(); +} + +BGD_DECLARE(void) +gdImageJxlEx(gdImagePtr im, FILE *outFile, int lossless, float distance, int effort) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); +} + +BGD_DECLARE(void *) gdImageJxlPtr(gdImagePtr im, int *size) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(void *) +gdImageJxlPtrEx(gdImagePtr im, int *size, int lossless, float distance, int effort) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(void) gdImageJxlCtx(gdImagePtr im, gdIOCtxPtr outfile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + _noJxlError(); +} + +BGD_DECLARE(void) +gdImageJxlCtxEx(gdImagePtr im, gdIOCtxPtr outfile, int lossless, float distance, int effort) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); +} + +/* Animation stubs */ +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreate(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreatePtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateCtx(gdIOCtxPtr inCtx) +{ + ARG_NOT_USED(inCtx); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdJxlReadNextImage(gdJxlAnimReaderPtr reader, int *delay_ms) +{ + ARG_NOT_USED(reader); + ARG_NOT_USED(delay_ms); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) gdImageJxlAnimReaderCreateRaw(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateRawPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimReaderPtr) +gdImageJxlAnimReaderCreateRawCtx(gdIOCtxPtr inCtx) +{ + ARG_NOT_USED(inCtx); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdJxlReadNextFrame(gdJxlAnimReaderPtr reader, gdJxlFrameInfo *info) +{ + ARG_NOT_USED(reader); + ARG_NOT_USED(info); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(void) gdImageJxlAnimReaderDestroy(gdJxlAnimReaderPtr reader) +{ + ARG_NOT_USED(reader); + _noJxlError(); +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBegin(FILE *outFile, int width, int height, int lossless, float distance, int effort) +{ + ARG_NOT_USED(outFile); + ARG_NOT_USED(width); + ARG_NOT_USED(height); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginCtx(gdIOCtxPtr outCtx, int width, int height, int lossless, float distance, + int effort) +{ + ARG_NOT_USED(outCtx); + ARG_NOT_USED(width); + ARG_NOT_USED(height); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(gdJxlAnimPtr) +gdImageJxlAnimBeginPtr(int width, int height, int lossless, float distance, int effort) +{ + ARG_NOT_USED(width); + ARG_NOT_USED(height); + ARG_NOT_USED(lossless); + ARG_NOT_USED(distance); + ARG_NOT_USED(effort); + _noJxlError(); + return NULL; +} + +BGD_DECLARE(int) +gdImageJxlAnimAddFrame(gdJxlAnimPtr anim, gdImagePtr im, int delay_ms) +{ + ARG_NOT_USED(anim); + ARG_NOT_USED(im); + ARG_NOT_USED(delay_ms); + _noJxlError(); + return 0; +} + +BGD_DECLARE(int) gdImageJxlAnimEnd(gdJxlAnimPtr anim) +{ + ARG_NOT_USED(anim); + _noJxlError(); + return 0; +} + +BGD_DECLARE(void *) gdImageJxlAnimEndPtr(gdJxlAnimPtr anim, int *size) +{ + ARG_NOT_USED(anim); + if (size != NULL) { + *size = 0; + } + _noJxlError(); + return NULL; +} + +#endif /* HAVE_LIBJXL */ diff --git a/ext/gd/libgd/gd_matrix.c b/ext/gd/libgd/gd_matrix.c index ec4067483358..d79f9e69314b 100644 --- a/ext/gd/libgd/gd_matrix.c +++ b/ext/gd/libgd/gd_matrix.c @@ -2,12 +2,33 @@ #include #ifndef M_PI -# define M_PI 3.14159265358979323846 +#define M_PI 3.14159265358979323846 #endif /** * Title: Matrix * Group: Affine Matrix + * + * Matrix functions to initialize, transform and various other operations + * on these matrices. + * They can be used with gdTransformAffineCopy and are also used in various + * transformations functions in GD. + * + * matrix are create using a 6 elements double array: + * (start code) + * matrix[0] == xx + * matrix[1] == yx + * matrix[2] == xy + * matrix[3] == xy + * matrix[4] == x0 + * matrix[5] == y0 + * (end code) + * where the transformation of a given point (x,y) is given by: + * + * (start code) + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; + * (end code) */ /** @@ -24,14 +45,14 @@ * Returns: * GD_TRUE if the affine is rectilinear or GD_FALSE */ -int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, - const double affine[6]) +BGD_DECLARE(int) +gdAffineApplyToPointF(gdPointFPtr dst, const gdPointFPtr src, const double affine[6]) { - double x = src->x; - double y = src->y; - dst->x = x * affine[0] + y * affine[2] + affine[4]; - dst->y = x * affine[1] + y * affine[3] + affine[5]; - return GD_TRUE; + double x = src->x; + double y = src->y; + dst->x = x * affine[0] + y * affine[2] + affine[4]; + dst->y = x * affine[1] + y * affine[3] + affine[5]; + return GD_TRUE; } /** @@ -55,22 +76,25 @@ int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, * Returns: * GD_TRUE on success or GD_FALSE on failure */ -int gdAffineInvert (double dst[6], const double src[6]) +BGD_DECLARE(int) gdAffineInvert(double dst[6], const double src[6]) { - double r_det = (src[0] * src[3] - src[1] * src[2]); + double r_det = (src[0] * src[3] - src[1] * src[2]); - if (r_det <= 0.0) { - return GD_FALSE; - } + if (!isfinite(r_det)) { + return GD_FALSE; + } + if (r_det == 0) { + return GD_FALSE; + } - r_det = 1.0 / r_det; - dst[0] = src[3] * r_det; - dst[1] = -src[1] * r_det; - dst[2] = -src[2] * r_det; - dst[3] = src[0] * r_det; - dst[4] = -src[4] * dst[0] - src[5] * dst[2]; - dst[5] = -src[4] * dst[1] - src[5] * dst[3]; - return GD_TRUE; + r_det = 1.0 / r_det; + dst[0] = src[3] * r_det; + dst[1] = -src[1] * r_det; + dst[2] = -src[2] * r_det; + dst[3] = src[0] * r_det; + dst[4] = -src[4] * dst[0] - src[5] * dst[2]; + dst[5] = -src[4] * dst[1] - src[5] * dst[3]; + return GD_TRUE; } /** @@ -88,17 +112,18 @@ int gdAffineInvert (double dst[6], const double src[6]) * flip_v - Whether or not to flip vertically * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineFlip (double dst[6], const double src[6], const int flip_h, const int flip_v) +BGD_DECLARE(int) +gdAffineFlip(double dst[6], const double src[6], const int flip_h, const int flip_v) { - dst[0] = flip_h ? - src[0] : src[0]; - dst[1] = flip_h ? - src[1] : src[1]; - dst[2] = flip_v ? - src[2] : src[2]; - dst[3] = flip_v ? - src[3] : src[3]; - dst[4] = flip_h ? - src[4] : src[4]; - dst[5] = flip_v ? - src[5] : src[5]; - return GD_TRUE; + dst[0] = flip_h ? -src[0] : src[0]; + dst[1] = flip_h ? -src[1] : src[1]; + dst[2] = flip_v ? -src[2] : src[2]; + dst[3] = flip_v ? -src[3] : src[3]; + dst[4] = flip_h ? -src[4] : src[4]; + dst[5] = flip_v ? -src[5] : src[5]; + return GD_TRUE; } /** @@ -116,25 +141,26 @@ int gdAffineFlip (double dst[6], const double src[6], const int flip_h, const in * m2 - Second affine matrix * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]) +BGD_DECLARE(int) +gdAffineConcat(double dst[6], const double m1[6], const double m2[6]) { - double dst0, dst1, dst2, dst3, dst4, dst5; + double dst0, dst1, dst2, dst3, dst4, dst5; - dst0 = m1[0] * m2[0] + m1[1] * m2[2]; - dst1 = m1[0] * m2[1] + m1[1] * m2[3]; - dst2 = m1[2] * m2[0] + m1[3] * m2[2]; - dst3 = m1[2] * m2[1] + m1[3] * m2[3]; - dst4 = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; - dst5 = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; - dst[0] = dst0; - dst[1] = dst1; - dst[2] = dst2; - dst[3] = dst3; - dst[4] = dst4; - dst[5] = dst5; - return GD_TRUE; + dst0 = m1[0] * m2[0] + m1[1] * m2[2]; + dst1 = m1[0] * m2[1] + m1[1] * m2[3]; + dst2 = m1[2] * m2[0] + m1[3] * m2[2]; + dst3 = m1[2] * m2[1] + m1[3] * m2[3]; + dst4 = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; + dst5 = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; + dst[0] = dst0; + dst[1] = dst1; + dst[2] = dst2; + dst[3] = dst3; + dst[4] = dst4; + dst[5] = dst5; + return GD_TRUE; } /** @@ -145,17 +171,17 @@ int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]) * dst - Where to store the resulting affine transform * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineIdentity (double dst[6]) +BGD_DECLARE(int) gdAffineIdentity(double dst[6]) { - dst[0] = 1; - dst[1] = 0; - dst[2] = 0; - dst[3] = 1; - dst[4] = 0; - dst[5] = 0; - return GD_TRUE; + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; } /** @@ -167,17 +193,18 @@ int gdAffineIdentity (double dst[6]) * scale_y - Y scale factor * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineScale (double dst[6], const double scale_x, const double scale_y) +BGD_DECLARE(int) +gdAffineScale(double dst[6], const double scale_x, const double scale_y) { - dst[0] = scale_x; - dst[1] = 0; - dst[2] = 0; - dst[3] = scale_y; - dst[4] = 0; - dst[5] = 0; - return GD_TRUE; + dst[0] = scale_x; + dst[1] = 0; + dst[2] = 0; + dst[3] = scale_y; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; } /** @@ -192,20 +219,20 @@ int gdAffineScale (double dst[6], const double scale_x, const double scale_y) * angle - Rotation angle in degrees * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineRotate (double dst[6], const double angle) +BGD_DECLARE(int) gdAffineRotate(double dst[6], const double angle) { - const double sin_t = sin (angle * M_PI / 180.0); - const double cos_t = cos (angle * M_PI / 180.0); + const double sin_t = sin(angle * M_PI / 180.0); + const double cos_t = cos(angle * M_PI / 180.0); - dst[0] = cos_t; - dst[1] = sin_t; - dst[2] = -sin_t; - dst[3] = cos_t; - dst[4] = 0; - dst[5] = 0; - return GD_TRUE; + dst[0] = cos_t; + dst[1] = sin_t; + dst[2] = -sin_t; + dst[3] = cos_t; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; } /** @@ -217,17 +244,17 @@ int gdAffineRotate (double dst[6], const double angle) * angle - Shear angle in degrees * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineShearHorizontal(double dst[6], const double angle) +BGD_DECLARE(int) gdAffineShearHorizontal(double dst[6], const double angle) { - dst[0] = 1; - dst[1] = 0; - dst[2] = tan(angle * M_PI / 180.0); - dst[3] = 1; - dst[4] = 0; - dst[5] = 0; - return GD_TRUE; + dst[0] = 1; + dst[1] = 0; + dst[2] = tan(angle * M_PI / 180.0); + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; } /** @@ -239,17 +266,17 @@ int gdAffineShearHorizontal(double dst[6], const double angle) * angle - Shear angle in degrees * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineShearVertical(double dst[6], const double angle) +BGD_DECLARE(int) gdAffineShearVertical(double dst[6], const double angle) { - dst[0] = 1; - dst[1] = tan(angle * M_PI / 180.0); - dst[2] = 0; - dst[3] = 1; - dst[4] = 0; - dst[5] = 0; - return GD_TRUE; + dst[0] = 1; + dst[1] = tan(angle * M_PI / 180.0); + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; } /** @@ -262,16 +289,17 @@ int gdAffineShearVertical(double dst[6], const double angle) * offset_y - Vertical translation amount * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y) +BGD_DECLARE(int) +gdAffineTranslate(double dst[6], const double offset_x, const double offset_y) { - dst[0] = 1; - dst[1] = 0; - dst[2] = 0; - dst[3] = 1; - dst[4] = offset_x; - dst[5] = offset_y; + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = offset_x; + dst[5] = offset_y; return GD_TRUE; } @@ -284,11 +312,11 @@ int gdAffineTranslate (double dst[6], const double offset_x, const double offset * composed of scaling, rotation, shearing, and translation, returns * the amount of scaling. * - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE **/ -double gdAffineExpansion (const double src[6]) +BGD_DECLARE(double) gdAffineExpansion(const double src[6]) { - return sqrt (fabs (src[0] * src[3] - src[1] * src[2])); + return sqrt(fabs(src[0] * src[3] - src[1] * src[2])); } /** @@ -302,10 +330,10 @@ double gdAffineExpansion (const double src[6]) * Returns: * GD_TRUE if the affine is rectilinear or GD_FALSE */ -int gdAffineRectilinear (const double m[6]) +BGD_DECLARE(int) gdAffineRectilinear(const double m[6]) { - return ((fabs (m[1]) < GD_EPSILON && fabs (m[2]) < GD_EPSILON) || - (fabs (m[0]) < GD_EPSILON && fabs (m[3]) < GD_EPSILON)); + return ((fabs(m[1]) < GD_EPSILON && fabs(m[2]) < GD_EPSILON) || + (fabs(m[0]) < GD_EPSILON && fabs(m[3]) < GD_EPSILON)); } /** @@ -318,14 +346,11 @@ int gdAffineRectilinear (const double m[6]) * m2 - The first affine transformation * * Returns: - * GD_SUCCESS on success or GD_FAILURE + * GD_TRUE on success or GD_FALSE */ -int gdAffineEqual (const double m1[6], const double m2[6]) +BGD_DECLARE(int) gdAffineEqual(const double m1[6], const double m2[6]) { - return (fabs (m1[0] - m2[0]) < GD_EPSILON && - fabs (m1[1] - m2[1]) < GD_EPSILON && - fabs (m1[2] - m2[2]) < GD_EPSILON && - fabs (m1[3] - m2[3]) < GD_EPSILON && - fabs (m1[4] - m2[4]) < GD_EPSILON && - fabs (m1[5] - m2[5]) < GD_EPSILON); + return (fabs(m1[0] - m2[0]) < GD_EPSILON && fabs(m1[1] - m2[1]) < GD_EPSILON && + fabs(m1[2] - m2[2]) < GD_EPSILON && fabs(m1[3] - m2[3]) < GD_EPSILON && + fabs(m1[4] - m2[4]) < GD_EPSILON && fabs(m1[5] - m2[5]) < GD_EPSILON); } diff --git a/ext/gd/libgd/gd_metadata.c b/ext/gd/libgd/gd_metadata.c new file mode 100644 index 000000000000..d5876fb0fbd9 --- /dev/null +++ b/ext/gd/libgd/gd_metadata.c @@ -0,0 +1,324 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gd.h" +#include "gdhelpers.h" + +typedef struct gdImageMetadataProfile { + char *key; + unsigned char *data; + size_t size; + struct gdImageMetadataProfile *next; +} gdImageMetadataProfile; + +struct gdImageMetadata { + gdImageMetadataProfile *profiles; + size_t profile_count; + size_t total_size; + size_t max_profile_size; + size_t max_total_size; +}; + +static int gdMetadataKeyIsValid(const char *key) { return key != NULL && key[0] != '\0'; } + +static char *gdMetadataStrdup(const char *src) +{ + size_t len; + char *dst; + + len = strlen(src); + dst = (char *)gdMalloc(len + 1); + if (dst == NULL) { + return NULL; + } + memcpy(dst, src, len + 1); + return dst; +} + +static gdImageMetadataProfile *gdMetadataFindProfile(const gdImageMetadata *metadata, + const char *key) +{ + gdImageMetadataProfile *profile; + + if (metadata == NULL || !gdMetadataKeyIsValid(key)) { + return NULL; + } + + profile = metadata->profiles; + while (profile != NULL) { + if (strcmp(profile->key, key) == 0) { + return profile; + } + profile = profile->next; + } + + return NULL; +} + +static void gdMetadataFreeProfile(gdImageMetadataProfile *profile) +{ + if (profile == NULL) { + return; + } + if (profile->key != NULL) { + gdFree(profile->key); + } + if (profile->data != NULL) { + gdFree(profile->data); + } + gdFree(profile); +} + +BGD_DECLARE(gdImageMetadata *) gdImageMetadataCreate(void) +{ + gdImageMetadata *metadata; + + metadata = (gdImageMetadata *)gdCalloc(1, sizeof(gdImageMetadata)); + if (metadata == NULL) { + return NULL; + } + + metadata->max_profile_size = GD_METADATA_DEFAULT_MAX_PROFILE_SIZE; + metadata->max_total_size = GD_METADATA_DEFAULT_MAX_TOTAL_SIZE; + + return metadata; +} + +BGD_DECLARE(void) gdImageMetadataFree(gdImageMetadata *metadata) +{ + if (metadata == NULL) { + return; + } + + gdImageMetadataReset(metadata); + gdFree(metadata); +} + +BGD_DECLARE(void) gdImageMetadataReset(gdImageMetadata *metadata) +{ + gdImageMetadataProfile *profile; + + if (metadata == NULL) { + return; + } + + profile = metadata->profiles; + while (profile != NULL) { + gdImageMetadataProfile *next = profile->next; + gdMetadataFreeProfile(profile); + profile = next; + } + + metadata->profiles = NULL; + metadata->profile_count = 0; + metadata->total_size = 0; +} + +BGD_DECLARE(int) +gdImageMetadataSetLimits(gdImageMetadata *metadata, size_t max_profile_size, size_t max_total_size) +{ + if (metadata == NULL) { + return GD_META_ERR_INVALID; + } + + if (max_profile_size != 0 || max_total_size != 0) { + gdImageMetadataProfile *profile; + + if (max_total_size != 0 && metadata->total_size > max_total_size) { + return GD_META_ERR_LIMIT; + } + + profile = metadata->profiles; + while (profile != NULL) { + if (max_profile_size != 0 && profile->size > max_profile_size) { + return GD_META_ERR_LIMIT; + } + profile = profile->next; + } + } + + metadata->max_profile_size = max_profile_size; + metadata->max_total_size = max_total_size; + return GD_META_OK; +} + +BGD_DECLARE(void) +gdImageMetadataGetLimits(const gdImageMetadata *metadata, size_t *max_profile_size, + size_t *max_total_size) +{ + if (max_profile_size != NULL) { + *max_profile_size = metadata != NULL ? metadata->max_profile_size : 0; + } + if (max_total_size != NULL) { + *max_total_size = metadata != NULL ? metadata->max_total_size : 0; + } +} + +BGD_DECLARE(int) +gdImageMetadataSetProfile(gdImageMetadata *metadata, const char *key, const unsigned char *data, + size_t size) +{ + gdImageMetadataProfile *profile; + unsigned char *new_data = NULL; + size_t old_size = 0; + size_t new_total; + + if (metadata == NULL || !gdMetadataKeyIsValid(key) || (data == NULL && size != 0)) { + return GD_META_ERR_INVALID; + } + + if (metadata->max_profile_size != 0 && size > metadata->max_profile_size) { + return GD_META_ERR_LIMIT; + } + + profile = gdMetadataFindProfile(metadata, key); + if (profile != NULL) { + old_size = profile->size; + } + + if ((size_t)-1 - (metadata->total_size - old_size) < size) { + return GD_META_ERR_LIMIT; + } + new_total = metadata->total_size - old_size + size; + if (metadata->max_total_size != 0 && new_total > metadata->max_total_size) { + return GD_META_ERR_LIMIT; + } + + if (size != 0) { + new_data = (unsigned char *)gdMalloc(size); + if (new_data == NULL) { + return GD_META_ERR_NOMEM; + } + memcpy(new_data, data, size); + } + + if (profile == NULL) { + profile = (gdImageMetadataProfile *)gdCalloc(1, sizeof(gdImageMetadataProfile)); + if (profile == NULL) { + if (new_data != NULL) { + gdFree(new_data); + } + return GD_META_ERR_NOMEM; + } + profile->key = gdMetadataStrdup(key); + if (profile->key == NULL) { + if (new_data != NULL) { + gdFree(new_data); + } + gdFree(profile); + return GD_META_ERR_NOMEM; + } + profile->next = metadata->profiles; + metadata->profiles = profile; + metadata->profile_count++; + } else if (profile->data != NULL) { + gdFree(profile->data); + } + + profile->data = new_data; + profile->size = size; + metadata->total_size = new_total; + + return GD_META_OK; +} + +BGD_DECLARE(const unsigned char *) +gdImageMetadataGetProfile(const gdImageMetadata *metadata, const char *key, size_t *size) +{ + gdImageMetadataProfile *profile; + + if (size != NULL) { + *size = 0; + } + + profile = gdMetadataFindProfile(metadata, key); + if (profile == NULL) { + return NULL; + } + + if (size != NULL) { + *size = profile->size; + } + return profile->data; +} + +BGD_DECLARE(int) +gdImageMetadataRemoveProfile(gdImageMetadata *metadata, const char *key) +{ + gdImageMetadataProfile *profile; + gdImageMetadataProfile *previous = NULL; + + if (metadata == NULL || !gdMetadataKeyIsValid(key)) { + return GD_META_ERR_INVALID; + } + + profile = metadata->profiles; + while (profile != NULL) { + if (strcmp(profile->key, key) == 0) { + if (previous == NULL) { + metadata->profiles = profile->next; + } else { + previous->next = profile->next; + } + metadata->profile_count--; + metadata->total_size -= profile->size; + gdMetadataFreeProfile(profile); + return GD_META_OK; + } + previous = profile; + profile = profile->next; + } + + return GD_META_OK; +} + +BGD_DECLARE(size_t) +gdImageMetadataGetProfileCount(const gdImageMetadata *metadata) +{ + return metadata != NULL ? metadata->profile_count : 0; +} + +BGD_DECLARE(int) +gdImageMetadataGetProfileAt(const gdImageMetadata *metadata, size_t index, const char **key, + const unsigned char **data, size_t *size) +{ + gdImageMetadataProfile *profile; + size_t i = 0; + + if (key != NULL) { + *key = NULL; + } + if (data != NULL) { + *data = NULL; + } + if (size != NULL) { + *size = 0; + } + + if (metadata == NULL) { + return GD_META_ERR_INVALID; + } + + profile = metadata->profiles; + while (profile != NULL) { + if (i == index) { + if (key != NULL) { + *key = profile->key; + } + if (data != NULL) { + *data = profile->data; + } + if (size != NULL) { + *size = profile->size; + } + return GD_META_OK; + } + i++; + profile = profile->next; + } + + return GD_META_ERR_INVALID; +} diff --git a/ext/gd/libgd/gd_nnquant.c b/ext/gd/libgd/gd_nnquant.c new file mode 100644 index 000000000000..e4f83fa5d8bb --- /dev/null +++ b/ext/gd/libgd/gd_nnquant.c @@ -0,0 +1,633 @@ +/* NeuQuant Neural-Net Quantization Algorithm + * ------------------------------------------ + * + * Copyright (c) 1994 Anthony Dekker + * + * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. + * See "Kohonen neural networks for optimal colour quantization" + * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367. + * for a discussion of the algorithm. + * See also http://members.ozemail.com.au/~dekker/NEUQUANT.HTML + * + * Any party obtaining a copy of these files from the author, directly or + * indirectly, is granted, free of charge, a full and unrestricted irrevocable, + * world-wide, paid up, royalty-free, nonexclusive right and license to deal + * in this software and documentation files (the "Software"), including without + * limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons who + * receive copies from any such party to do so, with the only requirement being + * that this copyright notice remain intact. + * + * + * Modified to process 32bit RGBA images. + * Stuart Coyle 2004-2007 + * From: http://pngnq.sourceforge.net/ + * + * Ported to libgd by Pierre A. Joye + * (and make it thread safety by droping static and global variables) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "gd.h" +#include "gd_errors.h" +#include "gdhelpers.h" +#include +#include +#include + +#include "gd_nnquant.h" + +/* Network Definitions + ------------------- */ + +#define maxnetpos (MAXNETSIZE - 1) +#define netbiasshift 4 /* bias for colour values */ +#define ncycles 100 /* no. of learning cycles */ + +/* defs for freq and bias */ +#define intbiasshift 16 /* bias for fractions */ +#define intbias (((int)1) << intbiasshift) +#define gammashift 10 /* gamma = 1024 */ +#define gamma (((int)1) << gammashift) +#define betashift 10 +#define beta (intbias >> betashift) /* beta = 1/1024 */ +#define betagamma (intbias << (gammashift - betashift)) + +/* defs for decreasing radius factor */ +#define initrad (MAXNETSIZE >> 3) /* for 256 cols, radius starts */ +#define radiusbiasshift 6 /* at 32.0 biased by 6 bits */ +#define radiusbias (((int)1) << radiusbiasshift) +#define initradius (initrad * radiusbias) /* and decreases by a */ +#define radiusdec 30 /* factor of 1/30 each cycle */ + +/* defs for decreasing alpha factor */ +#define alphabiasshift 10 /* alpha starts at 1.0 */ +#define initalpha (((int)1) << alphabiasshift) + +/* radbias and alpharadbias used for radpower calculation */ +#define radbiasshift 8 +#define radbias (((int)1) << radbiasshift) +#define alpharadbshift (alphabiasshift + radbiasshift) +#define alpharadbias (((int)1) << alpharadbshift) + +#define ALPHA 0 +#define RED 1 +#define BLUE 2 +#define GREEN 3 + +typedef int nq_pixel[5]; + +typedef struct { + /* biased by 10 bits */ + int alphadec; + + /* lengthcount = H*W*3 */ + int lengthcount; + + /* sampling factor 1..30 */ + int samplefac; + + /* Number of colours to use. Made a global instead of #define */ + int netsize; + + /* for network lookup - really 256 */ + int netindex[256]; + + /* ABGRc */ + /* the network itself */ + nq_pixel network[MAXNETSIZE]; + + /* bias and freq arrays for learning */ + int bias[MAXNETSIZE]; + int freq[MAXNETSIZE]; + + /* radpower for precomputation */ + int radpower[initrad]; + + /* the input image itself */ + unsigned char *thepicture; +} nn_quant; + +/* Initialise network in range (0,0,0,0) to (255,255,255,255) and set parameters + ----------------------------------------------------------------------- */ +static void initnet(nn_quant *nnq, unsigned char *thepic, int len, int sample, int colours) +{ + register int i; + register int *p; + + /* Clear out network from previous runs */ + /* thanks to Chen Bin for this fix */ + memset((void *)nnq->network, 0, sizeof(nq_pixel) * MAXNETSIZE); + + nnq->thepicture = thepic; + nnq->lengthcount = len; + nnq->samplefac = sample; + nnq->netsize = colours; + + for (i = 0; i < nnq->netsize; i++) { + p = nnq->network[i]; + p[0] = p[1] = p[2] = p[3] = (i << (netbiasshift + 8)) / nnq->netsize; + nnq->freq[i] = intbias / nnq->netsize; /* 1/netsize */ + nnq->bias[i] = 0; + } +} + +/* -------------------------- */ + +/* Unbias network to give byte values 0..255 and record + * position i to prepare for sort + */ +/* -------------------------- */ + +static void unbiasnet(nn_quant *nnq) +{ + int i, j, temp; + + for (i = 0; i < nnq->netsize; i++) { + for (j = 0; j < 4; j++) { + /* OLD CODE: network[i][j] >>= netbiasshift; */ + /* Fix based on bug report by Juergen Weigert jw@suse.de */ + temp = (nnq->network[i][j] + (1 << (netbiasshift - 1))) >> netbiasshift; + if (temp > 255) + temp = 255; + nnq->network[i][j] = temp; + } + nnq->network[i][4] = i; /* record colour no */ + } +} + +/* Output colormap to unsigned char ptr in RGBA format */ +static void getcolormap(nn_quant *nnq, unsigned char *map) +{ + int i, j; + for (j = 0; j < nnq->netsize; j++) { + for (i = 3; i >= 0; i--) { + *map = nnq->network[j][i]; + map++; + } + } +} + +/* Insertion sort of network and building of netindex[0..255] (to do after + unbias) + ------------------------------------------------------------------------------- + */ +static void inxbuild(nn_quant *nnq) +{ + register int i, j, smallpos, smallval; + register int *p, *q; + int previouscol, startpos; + + previouscol = 0; + startpos = 0; + for (i = 0; i < nnq->netsize; i++) { + p = nnq->network[i]; + smallpos = i; + smallval = p[2]; /* index on g */ + /* find smallest in i..netsize-1 */ + for (j = i + 1; j < nnq->netsize; j++) { + q = nnq->network[j]; + if (q[2] < smallval) { /* index on g */ + smallpos = j; + smallval = q[2]; /* index on g */ + } + } + q = nnq->network[smallpos]; + /* swap p (i) and q (smallpos) entries */ + if (i != smallpos) { + j = q[0]; + q[0] = p[0]; + p[0] = j; + j = q[1]; + q[1] = p[1]; + p[1] = j; + j = q[2]; + q[2] = p[2]; + p[2] = j; + j = q[3]; + q[3] = p[3]; + p[3] = j; + j = q[4]; + q[4] = p[4]; + p[4] = j; + } + /* smallval entry is now in position i */ + if (smallval != previouscol) { + nnq->netindex[previouscol] = (startpos + i) >> 1; + for (j = previouscol + 1; j < smallval; j++) + nnq->netindex[j] = i; + previouscol = smallval; + startpos = i; + } + } + nnq->netindex[previouscol] = (startpos + maxnetpos) >> 1; + for (j = previouscol + 1; j < 256; j++) + nnq->netindex[j] = maxnetpos; /* really 256 */ +} + +/* Search for ABGR values 0..255 (after net is unbiased) and return colour index + ---------------------------------------------------------------------------- + */ +static unsigned int inxsearch(nn_quant *nnq, int al, int b, int g, int r) +{ + register int i, j, dist, a, bestd; + register int *p; + unsigned int best; + + bestd = 1000; /* biggest possible dist is 256*3 */ + best = 0; + i = nnq->netindex[g]; /* index on g */ + j = i - 1; /* start at netindex[g] and work outwards */ + + while ((i < nnq->netsize) || (j >= 0)) { + if (i < nnq->netsize) { + p = nnq->network[i]; + dist = p[2] - g; /* inx key */ + if (dist >= bestd) + i = nnq->netsize; /* stop iter */ + else { + i++; + if (dist < 0) + dist = -dist; + a = p[1] - b; + if (a < 0) + a = -a; + dist += a; + if (dist < bestd) { + a = p[3] - r; + if (a < 0) + a = -a; + dist += a; + } + if (dist < bestd) { + a = p[0] - al; + if (a < 0) + a = -a; + dist += a; + } + if (dist < bestd) { + bestd = dist; + best = p[4]; + } + } + } + + if (j >= 0) { + p = nnq->network[j]; + dist = g - p[2]; /* inx key - reverse dif */ + if (dist >= bestd) + j = -1; /* stop iter */ + else { + j--; + if (dist < 0) + dist = -dist; + a = p[1] - b; + if (a < 0) + a = -a; + dist += a; + if (dist < bestd) { + a = p[3] - r; + if (a < 0) + a = -a; + dist += a; + } + if (dist < bestd) { + a = p[0] - al; + if (a < 0) + a = -a; + dist += a; + } + if (dist < bestd) { + bestd = dist; + best = p[4]; + } + } + } + } + + return (best); +} + +/* Search for biased ABGR values + ---------------------------- */ +static int contest(nn_quant *nnq, int al, int b, int g, int r) +{ + /* finds closest neuron (min dist) and updates freq */ + /* finds best neuron (min dist-bias) and returns position */ + /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ + /* bias[i] = gamma*((1/netsize)-freq[i]) */ + + register int i, dist, a, biasdist, betafreq; + unsigned int bestpos, bestbiaspos; + double bestd, bestbiasd; + register int *p, *f, *n; + + bestd = INT_MAX; + bestbiasd = bestd; + bestpos = 0; + bestbiaspos = bestpos; + p = nnq->bias; + f = nnq->freq; + + for (i = 0; i < nnq->netsize; i++) { + n = nnq->network[i]; + dist = n[0] - al; + if (dist < 0) + dist = -dist; + a = n[1] - b; + if (a < 0) + a = -a; + dist += a; + a = n[2] - g; + if (a < 0) + a = -a; + dist += a; + a = n[3] - r; + if (a < 0) + a = -a; + dist += a; + if (dist < bestd) { + bestd = dist; + bestpos = i; + } + biasdist = dist - ((*p) >> (intbiasshift - netbiasshift)); + if (biasdist < bestbiasd) { + bestbiasd = biasdist; + bestbiaspos = i; + } + betafreq = (*f >> betashift); + *f++ -= betafreq; + *p++ += (betafreq << gammashift); + } + nnq->freq[bestpos] += beta; + nnq->bias[bestpos] -= betagamma; + return (bestbiaspos); +} + +/* Move neuron i towards biased (a,b,g,r) by factor alpha + ---------------------------------------------------- */ + +static void altersingle(nn_quant *nnq, int alpha, int i, int al, int b, int g, int r) +{ + register int *n; + + n = nnq->network[i]; /* alter hit neuron */ + *n -= (alpha * (*n - al)) / initalpha; + n++; + *n -= (alpha * (*n - b)) / initalpha; + n++; + *n -= (alpha * (*n - g)) / initalpha; + n++; + *n -= (alpha * (*n - r)) / initalpha; +} + +/* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in + radpower[|i-j|] + --------------------------------------------------------------------------------- + */ + +static void alterneigh(nn_quant *nnq, int rad, int i, int al, int b, int g, int r) +{ + register int j, k, lo, hi, a; + register int *p, *q; + + lo = i - rad; + if (lo < -1) + lo = -1; + hi = i + rad; + if (hi > nnq->netsize) + hi = nnq->netsize; + + j = i + 1; + k = i - 1; + q = nnq->radpower; + while ((j < hi) || (k > lo)) { + a = (*(++q)); + if (j < hi) { + p = nnq->network[j]; + *p -= (a * (*p - al)) / alpharadbias; + p++; + *p -= (a * (*p - b)) / alpharadbias; + p++; + *p -= (a * (*p - g)) / alpharadbias; + p++; + *p -= (a * (*p - r)) / alpharadbias; + j++; + } + if (k > lo) { + p = nnq->network[k]; + *p -= (a * (*p - al)) / alpharadbias; + p++; + *p -= (a * (*p - b)) / alpharadbias; + p++; + *p -= (a * (*p - g)) / alpharadbias; + p++; + *p -= (a * (*p - r)) / alpharadbias; + k--; + } + } +} + +/* Main Learning Loop + ------------------ */ + +static void learn(nn_quant *nnq, int verbose) /* Stu: N.B. added parameter so that main() could + control verbosity. */ +{ + register int i, j, al, b, g, r; + int radius, rad, alpha, step, delta, samplepixels; + register unsigned char *p; + unsigned char *lim; + + nnq->alphadec = 30 + ((nnq->samplefac - 1) / 3); + p = nnq->thepicture; + lim = nnq->thepicture + nnq->lengthcount; + samplepixels = nnq->lengthcount / (4 * nnq->samplefac); + /* here's a problem with small images: samplepixels < ncycles => delta = 0 + */ + delta = samplepixels / ncycles; + /* kludge to fix */ + if (delta == 0) + delta = 1; + alpha = initalpha; + radius = initradius; + + rad = radius >> radiusbiasshift; + + for (i = 0; i < rad; i++) + nnq->radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); + + if (verbose) + gd_error_ex(GD_NOTICE, "beginning 1D learning: initial radius=%d\n", rad); + + if ((nnq->lengthcount % prime1) != 0) + step = 4 * prime1; + else { + if ((nnq->lengthcount % prime2) != 0) + step = 4 * prime2; + else { + if ((nnq->lengthcount % prime3) != 0) + step = 4 * prime3; + else + step = 4 * prime4; + } + } + + i = 0; + while (i < samplepixels) { + al = p[ALPHA] << netbiasshift; + b = p[BLUE] << netbiasshift; + g = p[GREEN] << netbiasshift; + r = p[RED] << netbiasshift; + j = contest(nnq, al, b, g, r); + + altersingle(nnq, alpha, j, al, b, g, r); + if (rad) + alterneigh(nnq, rad, j, al, b, g, r); /* alter neighbours */ + + p += step; + while (p >= lim) + p -= nnq->lengthcount; + + i++; + if (i % delta == 0) { /* FPE here if delta=0*/ + alpha -= alpha / nnq->alphadec; + radius -= radius / radiusdec; + rad = radius >> radiusbiasshift; + if (rad <= 1) + rad = 0; + for (j = 0; j < rad; j++) + nnq->radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); + } + } + if (verbose) + gd_error_ex(GD_NOTICE, "finished 1D learning: final alpha=%f !\n", + ((float)alpha) / initalpha); +} + +/** + * Function: gdImageNeuQuant + * + * Creates a new palette image from a truecolor image + * + * This is the same as calling with the + * quantization method . + * + * Parameters: + * im - The image. + * max_color - The number of desired palette entries. + * sample_factor - The quantization precision between 1 (highest quality) and + * 10 (fastest). + * + * Returns: + * A newly create palette image; NULL on failure. + */ +BGD_DECLARE(gdImagePtr) +gdImageNeuQuant(gdImagePtr im, const int max_color, int sample_factor) +{ + const int newcolors = max_color; + const int verbose = 1; + + int i, x; + + unsigned char map[MAXNETSIZE][4] = {{0}}; + unsigned char *d; + + nn_quant *nnq = NULL; + + int row; + unsigned char *rgba = NULL; + gdImagePtr dst = NULL; + + if (newcolors <= 0 || newcolors > MAXNETSIZE) { + gd_error("neuquant: max_color must be between 1 and %d\n", MAXNETSIZE); + goto done; + } + + /* Default it to 3 */ + if (sample_factor < 1) { + sample_factor = 3; + } + /* Start neuquant */ + /* Pierre: + * This implementation works with aligned contiguous buffer only + * Upcoming new buffers are contiguous and will be much faster. + * let don't bloat this code to support our good "old" 31bit format. + * It also lets us convert palette image, if one likes to reduce + * a palette + */ + if (overflow2(gdImageSX(im), gdImageSY(im)) || overflow2(gdImageSX(im) * gdImageSY(im), 4)) { + goto done; + } + rgba = (unsigned char *)gdMalloc(gdImageSX(im) * gdImageSY(im) * 4); + if (!rgba) { + goto done; + } + + d = rgba; + for (row = 0; row < gdImageSY(im); row++) { + int *p = im->tpixels[row]; + register int c; + + for (i = 0; i < gdImageSX(im); i++) { + c = *p; + *d++ = gdImageAlpha(im, c); + *d++ = gdImageRed(im, c); + *d++ = gdImageBlue(im, c); + *d++ = gdImageGreen(im, c); + p++; + } + } + + nnq = (nn_quant *)gdMalloc(sizeof(nn_quant)); + if (!nnq) { + goto done; + } + + initnet(nnq, rgba, gdImageSY(im) * gdImageSX(im) * 4, sample_factor, newcolors); + + learn(nnq, verbose); + unbiasnet(nnq); + getcolormap(nnq, (unsigned char *)map); + inxbuild(nnq); + + dst = gdImageCreate(gdImageSX(im), gdImageSY(im)); + if (!dst) { + goto done; + } + + for (x = 0; x < newcolors; ++x) { + dst->red[x] = map[x][0]; + dst->green[x] = map[x][1]; + dst->blue[x] = map[x][2]; + dst->alpha[x] = map[x][3]; + dst->open[x] = 0; + dst->colorsTotal++; + } + + /* Do each image row */ + for (row = 0; row < gdImageSY(im); ++row) { + int offset; + unsigned char *p = dst->pixels[row]; + + /* Assign the new colors */ + offset = row * gdImageSX(im) * 4; + for (i = 0; i < gdImageSX(im); i++) { + p[i] = inxsearch(nnq, rgba[i * 4 + offset + ALPHA], rgba[i * 4 + offset + BLUE], + rgba[i * 4 + offset + GREEN], rgba[i * 4 + offset + RED]); + } + } + +done: + if (rgba) { + gdFree(rgba); + } + + if (nnq) { + gdFree(nnq); + } + return dst; +} diff --git a/ext/gd/libgd/gd_nnquant.h b/ext/gd/libgd/gd_nnquant.h new file mode 100644 index 000000000000..0b7a17889609 --- /dev/null +++ b/ext/gd/libgd/gd_nnquant.h @@ -0,0 +1,15 @@ +/* maximum number of colours that can be used. + actual number is now passed to initcolors */ +#define MAXNETSIZE 256 + +/* For 256 colours, fixed arrays need 8kb, plus space for the image + ---------------------------------------------------------------- */ + +/* four primes near 500 - assume no image has a length so large */ +/* that it is divisible by all four primes */ +#define prime1 499 +#define prime2 491 +#define prime3 487 +#define prime4 503 + +#define minpicturebytes (4 * prime4) /* minimum size for input image */ diff --git a/ext/gd/libgd/gd_path.c b/ext/gd/libgd/gd_path.c new file mode 100644 index 000000000000..85fe5fe775ad --- /dev/null +++ b/ext/gd/libgd/gd_path.c @@ -0,0 +1,726 @@ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "gd_compositor.h" +#include "gd_gradient.h" +#include "gd_path.h" +#include "gd_path_arc.h" +#include "gd_path_dash.h" +#include "gd_path_matrix.h" +#include "gd_span_rle.h" + +static gdSurfacePtr gdSurfaceSnapshotImage(gdImagePtr image) +{ + gdSurfacePtr surface; + int x, y; + + if (!image) + return NULL; + surface = gdSurfaceCreate(image->sx, image->sy, GD_SURFACE_ARGB32); + if (!surface) + return NULL; + for (y = 0; y < image->sy; y++) { + uint32_t *row = (uint32_t *)(surface->data + y * surface->stride); + for (x = 0; x < image->sx; x++) { + int pixel = gdImageGetTrueColorPixel(image, x, y); + row[x] = gdCompositePixelToArgb32(gdCompositePixelFromGd(pixel)); + } + } + return surface; +} + +gdPaintPtr gdPaintAddRef(gdPaintPtr paint) +{ + if (paint == NULL) + return NULL; + + ++paint->ref; + return paint; +} + +BGD_DECLARE(void) gdPaintDestroy(gdPaintPtr paint) +{ + if (paint == NULL) + return; + paint->ref--; + + if (paint->ref == 0) { + switch (paint->type) { + case gdPaintTypeColor: + gdFree(paint->color); + break; + case gdPaintTypeGradient: + gdGradientDestroy(paint->gradient); + break; + case gdPaintTypeSurface: + // Not implemented + break; + case gdPaintTypePattern: + gdPathPatternDestroy(paint->pattern); + break; + default: { + } + } + gdFree(paint); + } +} + +void gdColorInitRgba(gdColorPtr color, double r, double g, double b, double a) +{ + color->r = CLAMP(r, 0.0, 1.0); + color->g = CLAMP(g, 0.0, 1.0); + color->b = CLAMP(b, 0.0, 1.0); + color->a = CLAMP(a, 0.0, 1.0); +} + +gdPathPatternPtr gdPaintGetPattern(const gdPaintPtr paint) +{ + return paint->type == gdPaintTypePattern ? paint->pattern : NULL; +} + +GD_VECTOR2D_INTERNAL gdPathPatternPtr gdPathPatternCreate(gdSurfacePtr surface) +{ + if (!surface) + return NULL; + gdPathPatternPtr pattern = gdMalloc(sizeof(gdPathPattern)); + if (!pattern) + return NULL; + pattern->ref = 1; + pattern->extend = GD_EXTEND_NONE; + pattern->surface = surface; + gdSurfaceAddRef(surface); + pattern->opacity = 1.0; + gdPathMatrixInitIdentity(&pattern->matrix); + return pattern; +} + +BGD_DECLARE(gdPathPatternPtr) gdPathPatternCreateForImage(gdImagePtr image) +{ + gdSurfacePtr snapshot = gdSurfaceSnapshotImage(image); + gdPathPatternPtr pattern; + + if (!snapshot) + return NULL; + pattern = gdPathPatternCreate(snapshot); + gdSurfaceDestroy(snapshot); + return pattern; +} + +BGD_DECLARE(void) gdPathPatternDestroy(gdPathPatternPtr pattern) +{ + if (pattern == NULL) + return; + + if (--pattern->ref == 0) { + gdSurfaceDestroy(pattern->surface); + gdFree(pattern); + } +} + +void gdPathPatternAddRef(gdPathPatternPtr pattern) +{ + if (pattern == NULL) + return; + pattern->ref++; +} + +BGD_DECLARE(gdPaintPtr) gdPaintCreateFromPattern(gdPathPatternPtr pattern) +{ + gdPaintPtr paint = gdMalloc(sizeof(gdPaint)); + if (!paint) + return NULL; + paint->ref = 1; + paint->type = gdPaintTypePattern; + paint->pattern = pattern; + gdPathPatternAddRef(pattern); + return paint; +} + +gdPaintPtr gdPaintCreateForSurface(gdSurfacePtr surface) +{ + gdPathPatternPtr pattern = gdPathPatternCreate(surface); + gdPaintPtr paint = gdPaintCreateFromPattern(pattern); + gdPathPatternDestroy(pattern); + return paint; +} + +BGD_DECLARE(void) gdPathPatternSetExtend(gdPathPatternPtr pattern, gdExtendMode extend) +{ + pattern->extend = extend; +} + +BGD_DECLARE(void) gdPathPatternSetMatrix(gdPathPatternPtr pattern, gdPathMatrixPtr matrix) +{ + memcpy(&pattern->matrix, matrix, sizeof(gdPathMatrix)); +} + +BGD_DECLARE(void) gdPathPatternSetOpacity(gdPathPatternPtr pattern, double opacity) +{ + if (!pattern || !isfinite(opacity)) + return; + pattern->opacity = CLAMP(opacity, 0.0, 1.0); +} + +void gdPaintSetSourceSurface(gdContextPtr context, gdSurfacePtr surface, double x, double y) +{ + gdPathMatrix matrix; + gdPaintPtr paint; + gdPathPatternPtr pattern = gdPathPatternCreate(surface); + gdPathPatternSetExtend(pattern, GD_EXTEND_NONE); + gdPathMatrixMultiply(&matrix, &matrix, &context->state->matrix); + gdPathMatrixInitTranslate(&matrix, x, y); + gdPathPatternSetMatrix(pattern, &matrix); + paint = gdPaintCreateFromPattern(pattern); + gdContextSetSource(context, paint); +} + +gdPaintPtr gdPaintCreateRgba(double r, double g, double b, double a) +{ + gdPaintPtr paint = gdMalloc(sizeof(gdPaint)); + if (!paint) { + return NULL; + } + paint->color = gdMalloc(sizeof(gdColor)); + if (!paint->color) { + gdFree(paint); + return NULL; + } + + paint->ref = 1; + paint->type = gdPaintTypeColor; + gdColorInitRgba(paint->color, r, g, b, a); + return paint; +} + +gdPaintPtr gdPaintCreateRgb(double r, double g, double b) +{ + return gdPaintCreateRgba(r, g, b, 1.0); +} + +BGD_DECLARE(void) gdContextSetSource(gdContextPtr context, gdPaintPtr source) +{ + source = gdPaintAddRef(source); + gdPaintDestroy(context->state->source); + context->state->source = source; +} + +void gdContextSetSourceColorRgb(gdContextPtr context, double r, double g, double b) +{ + gdContextSetSourceRgba(context, r, g, b, 1.0); +} + +gdStatePtr gdStateCreate() +{ + gdStatePtr state = gdMalloc(sizeof(gdState)); + if (!state) { + return NULL; + } + // state->font = NULL; + state->source = gdPaintCreateRgba(0, 0, 0, 1.0); + if (!state->source) { + gdFree(state); + return NULL; + } + gdPathMatrixInitIdentity(&state->matrix); + state->winding = gdFillRuleNonZero; + state->stroke.width = 1.0; + state->stroke.miterlimit = 4.0; + state->stroke.cap = gdLineCapButt; + state->stroke.join = gdLineJoinMiter; + state->stroke.dash = NULL; + state->op = GD_OP_OVER; + // state->fontsize = 12.0; + state->opacity = 1.0; + state->clippath = NULL; + state->next = NULL; + return state; +} + +void gdStateDestroy(gdStatePtr state) +{ + // state->font + gdSpanRleDestroy(state->clippath); + gdPaintDestroy(state->source); + gdPathDashDestroy(state->stroke.dash); + gdFree(state); +} + +BGD_DECLARE(gdPathPtr) gdPathCreate() +{ + gdPathPtr path = gdMalloc(sizeof(gdPath)); + if (!path) + return NULL; + path->ref = 1; + path->contours = 0; + path->start.x = 0.0; + path->start.y = 0.0; + gdArrayInit(&path->elements, sizeof(gdPathOps)); + gdArrayInit(&path->points, sizeof(gdPointF)); + return path; +} + +BGD_DECLARE(gdPathPtr) gdPathDuplicate(const gdPathPtr path) +{ + unsigned int num_elements; + unsigned int num_points; + gdPathPtr result; + + if (!path) + return NULL; + result = gdPathCreate(); + if (!result) + return NULL; + + num_elements = gdArrayNumElements(&path->elements); + num_points = gdArrayNumElements(&path->points); + if ((num_elements && !gdArrayAppendMultiple(&result->elements, gdArrayGetData(&path->elements), + num_elements)) || + (num_points && + !gdArrayAppendMultiple(&result->points, gdArrayGetData(&path->points), num_points))) { + gdPathDestroy(result); + return NULL; + } + + result->contours = path->contours; + result->start = path->start; + + return result; +} + +BGD_DECLARE(void) gdPathAppendPath(gdPathPtr path, const gdPathPtr source) +{ + gdArrayAppendMultiple(&path->elements, gdArrayGetData(&source->elements), + gdArrayNumElements(&source->elements)); + gdArrayAppendMultiple(&path->points, gdArrayGetData(&source->points), + gdArrayNumElements(&source->points)); + + path->contours += source->contours; + path->start = source->start; +} + +BGD_DECLARE(void) gdPathTransform(gdPathPtr path, const gdPathMatrixPtr matrix) +{ + gdPointFPtr p; + unsigned int numElements = gdArrayNumElements(&path->points); + unsigned int i; + + // memset(p, 0, sizeof(gdPointF) * 3); + for (i = 0; i < numElements; i++) { + p = (gdPointFPtr)gdArrayIndex(&path->points, i); + gdPathMatrixMapPoint(matrix, p, p); + } +} + +static inline void _path_get_current_point(const gdPathPtr path, double *x, double *y) +{ + unsigned int numElems = gdArrayNumElements(&path->points); + if (x) + *x = 0.0; + if (y) + *y = 0.0; + if (numElems < 1) + return; + gdPointFPtr point = gdArrayIndex(&path->points, numElems - 1); + if (x) { + *x = point->x; + } + if (y) { + *y = point->y; + } +} + +typedef struct { + double x1; + double y1; + double x2; + double y2; + double x3; + double y3; + double x4; + double y4; +} cubic_points; + +static inline void split(const cubic_points *b, cubic_points *first, cubic_points *second) +{ + double c = (b->x2 + b->x3) * 0.5; + first->x2 = (b->x1 + b->x2) * 0.5; + second->x3 = (b->x3 + b->x4) * 0.5; + first->x1 = b->x1; + second->x4 = b->x4; + first->x3 = (first->x2 + c) * 0.5; + second->x2 = (second->x3 + c) * 0.5; + first->x4 = second->x1 = (first->x3 + second->x2) * 0.5; + + c = (b->y2 + b->y3) * 0.5; + first->y2 = (b->y1 + b->y2) * 0.5; + second->y3 = (b->y3 + b->y4) * 0.5; + first->y1 = b->y1; + second->y4 = b->y4; + first->y3 = (first->y2 + c) * 0.5; + second->y2 = (second->y3 + c) * 0.5; + first->y4 = second->y1 = (first->y3 + second->y2) * 0.5; +} + +/* See http://agg.sourceforge.net/antigrain.com/research/adaptive_bezier/index.html */ +static void _cubic_flatten(gdPathPtr path, const gdPointFPtr p0, const gdPointFPtr p1, + const gdPointFPtr p2, const gdPointFPtr p3) +{ + cubic_points beziers[32]; + beziers[0].x1 = p0->x; + beziers[0].y1 = p0->y; + beziers[0].x2 = p1->x; + beziers[0].y2 = p1->y; + beziers[0].x3 = p2->x; + beziers[0].y3 = p2->y; + beziers[0].x4 = p3->x; + beziers[0].y4 = p3->y; + + /* tolerance for the distance t to the line + 0.1 is a common accepted value + */ + const double tolerance = 0.1; + + cubic_points *b = beziers; + while (b >= beziers) { + double y4y1 = b->y4 - b->y1; + double x4x1 = b->x4 - b->x1; + double l = fabs(x4x1) + fabs(y4y1); + double d; + if (l > 1.0) { + d = fabs((x4x1) * (b->y1 - b->y2) - (y4y1) * (b->x1 - b->x2)) + + fabs((x4x1) * (b->y1 - b->y3) - (y4y1) * (b->x1 - b->x3)); + } else { + d = fabs(b->x1 - b->x2) + fabs(b->y1 - b->y2) + fabs(b->x1 - b->x3) + + fabs(b->y1 - b->y3); + l = 1.0; + } + + if (d < tolerance * l || b == beziers + 31) { + gdPathLineTo(path, b->x4, b->y4); + --b; + } else { + split(b, b + 1, b); + ++b; + } + } +} + +gdPathPtr gdPathDuplicateFlattened(const gdPathPtr path) +{ + gdPathPtr result = gdPathCreate(); + + gdArrayReallocBy(&result->elements, gdArrayNumElements(&path->elements)); + gdArrayReallocBy(&result->points, gdArrayNumElements(&path->points)); + + gdPointFPtr points = gdArrayGetData(&path->points); + for (unsigned int i = 0; i < gdArrayNumElements(&path->elements); i++) { + const gdPathOpsPtr cur_elem = gdArrayIndex(&path->elements, i); + switch (*cur_elem) { + case gdPathOpsMoveTo: + gdPathMoveTo(result, points[0].x, points[0].y); + points += 1; + break; + case gdPathOpsLineTo: + case gdPathOpsClose: + gdPathLineTo(result, points[0].x, points[0].y); + points += 1; + break; + case gdPathOpsCubicTo: { + gdPointF p0; + _path_get_current_point(result, &p0.x, &p0.y); + _cubic_flatten(result, &p0, points, points + 1, points + 2); + points += 3; + break; + } + default: + // Only to silent compiler + break; + } + } + return result; +} + +gdPathPtr gdPathAddRef(gdPathPtr path) +{ + if (path == NULL) + return NULL; + path->ref++; + return path; +} + +BGD_DECLARE(void) gdPathDestroy(gdPathPtr path) +{ + if (path == NULL) + return; + path->ref--; + if (path->ref == 0) { + gdArrayDestroy(&path->elements); + gdArrayDestroy(&path->points); + gdFree(path); + } +} + +void gdPathClear(gdPathPtr path) +{ + gdArrayTruncate(&path->elements, 0); + gdArrayTruncate(&path->points, 0); + path->contours = 0; + path->start.x = 0.0; + path->start.y = 0.0; +} + +/* dump a path, could be nicer but good enough for now +Not exported, only for debugging purposes here */ +void gdPathDumpPathTransform(const gdPathPtr path, const gdPathMatrixPtr matrix) +{ + // GD_FT_Outline* outline = gd_ft_outline_create(path->points.size, path->contours); + gdPointF p[3]; + unsigned int numElements = gdArrayNumElements(&path->elements); + unsigned int pointsIndex = 0; + unsigned int i; + + memset(p, 0, sizeof(gdPointF) * 3); + printf("NEWOUTLINE CONVERT\n"); + for (i = 0; i < numElements; i++) { + gdPathOpsPtr element = (gdPathOpsPtr)gdArrayIndex(&path->elements, i); + gdPointFPtr point = gdArrayIndex(&path->points, pointsIndex); + printf("-------\n"); + switch (*element) { + case gdPathOpsMoveTo: + if (matrix) + gdPathMatrixMapPoint(matrix, point, &p[0]); + printf("MoveTo(%f, %f)", point->x, point->y); + printf("(%f, %f)", p[0].x, p[0].y); + pointsIndex += 1; + break; + case gdPathOpsLineTo: + if (matrix) + gdPathMatrixMapPoint(matrix, point, &p[0]); + printf("LineTo(%f, %f)", point->x, point->y); + printf("(%f, %f)", p[0].x, p[0].y); + pointsIndex += 1; + break; + case gdPathOpsCubicTo: + printf("CubicTo(%f, %f)", point->x, point->y); + if (matrix) + gdPathMatrixMapPoint(matrix, point, &p[0]); + printf("(%f, %f)", p[0].x, p[0].y); + + point = gdArrayIndex(&path->points, pointsIndex + 1); + if (matrix) + gdPathMatrixMapPoint(matrix, point, &p[1]); + printf("(%f, %f)", p[1].x, p[1].y); + + point = gdArrayIndex(&path->points, pointsIndex + 2); + if (matrix) + gdPathMatrixMapPoint(matrix, point, &p[2]); + printf("(%f, %f)", p[2].x, p[2].y); + pointsIndex += 3; + break; + case gdPathOpsClose: + printf("Outline close"); + pointsIndex += 1; + break; + default: + break; + } + printf("\n-------\n"); + } +} + +static inline void _relativeTo(const gdPathPtr path, double *x, double *y) +{ + double _x = -1, _y = -1; + _path_get_current_point(path, &_x, &_y); + *x += _x; + *y += _y; +} + +BGD_DECLARE(void) gdPathMoveTo(gdPathPtr path, double x, double y) +{ + const gdPathOps op = gdPathOpsMoveTo; + gdPointF point; + + gdArrayAppend(&path->elements, &op); + path->contours += 1; + + point.x = x; + point.y = y; + gdArrayAppend(&path->points, &point); + path->start.x = x; + path->start.y = y; +} + +BGD_DECLARE(void) gdPathRelMoveTo(gdPathPtr path, double dx, double dy) +{ + _relativeTo(path, &dx, &dy); + gdPathMoveTo(path, dx, dy); +} + +BGD_DECLARE(void) gdPathLineTo(gdPathPtr path, double x, double y) +{ + const gdPathOps op = gdPathOpsLineTo; + gdPointF point; + + gdArrayAppend(&path->elements, &op); + + point.x = x; + point.y = y; + gdArrayAppend(&path->points, &point); +} +BGD_DECLARE(void) gdPathRelLineTo(gdPathPtr path, double dx, double dy) +{ + _relativeTo(path, &dx, &dy); + gdPathLineTo(path, dx, dy); +} + +BGD_DECLARE(void) +gdPathCurveTo(gdPathPtr path, double x1, double y1, double x2, double y2, double x3, double y3) +{ + const gdPathOps op = gdPathOpsCubicTo; + gdPointF points[3]; + + gdArrayAppend(&path->elements, &op); + points[0].x = x1; + points[0].y = y1; + points[1].x = x2; + points[1].y = y2; + points[2].x = x3; + points[2].y = y3; + gdArrayAppend(&path->points, &points[0]); + gdArrayAppend(&path->points, &points[1]); + gdArrayAppend(&path->points, &points[2]); +} + +BGD_DECLARE(void) +gdPathRelCurveTo(gdPathPtr path, double dx1, double dy1, double dx2, double dy2, double dx3, + double dy3) +{ + _relativeTo(path, &dx1, &dy1); + _relativeTo(path, &dx2, &dy2); + _relativeTo(path, &dx3, &dy3); + gdPathCurveTo(path, dx1, dy1, dx2, dy2, dx3, dy3); +} + +BGD_DECLARE(void) gdPathQuadTo(gdPathPtr path, double x1, double y1, double x2, double y2) +{ + const gdPathOps op = gdPathOpsQuadTo; + gdPointF points[2] = {{x1, y1}, {x2, y2}}; + + gdArrayAppend(&path->elements, &op); + gdArrayAppend(&path->points, &points[0]); + gdArrayAppend(&path->points, &points[1]); +} + +BGD_DECLARE(void) gdPathRelQuadTo(gdPathPtr path, double dx1, double dy1, double dx2, double dy2) +{ + _relativeTo(path, &dx1, &dy1); + _relativeTo(path, &dx2, &dy2); + gdPathQuadTo(path, dx1, dy1, dx2, dy2); +} + +/* +Based on http://www.whizkidtech.redprince.net/bezier/circle/kappa/ +*/ +static void _gdPathArc(gdPathPtr path, double cx, double cy, double radius, double angle1, + double angle2, int ccw) +{ + if (ccw) + _gd_arc_path_negative(path, cx, cy, radius, angle1, angle2); + else + _gd_arc_path(path, cx, cy, radius, angle1, angle2); +} + +BGD_DECLARE(void) +gdPathArc(gdPathPtr path, double cx, double cy, double radius, double angle1, double angle2) +{ + _gdPathArc(path, cx, cy, radius, angle1, angle2, 0); +} + +BGD_DECLARE(void) +gdPathNegativeArc(gdPathPtr path, double cx, double cy, double radius, double angle1, double angle2) +{ + _gdPathArc(path, cx, cy, radius, angle1, angle2, 1); +} + +BGD_DECLARE(void) +gdPathArcTo(gdPathPtr path, double x1, double y1, double x2, double y2, double radius) +{ + double x0, y0; + _path_get_current_point(path, &x0, &y0); + if ((x0 == x1 && y0 == y1) || (x1 == x2 && y1 == y2) || radius <= 0.0) { + gdPathLineTo(path, x1, y1); + return; + } + + double dir = (x2 - x1) * (y0 - y1) + (y2 - y1) * (x1 - x0); + if (dir == 0.0) { + gdPathLineTo(path, x1, y1); + return; + } + + double a2 = (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1); + double b2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + double c2 = (x0 - x2) * (x0 - x2) + (y0 - y2) * (y0 - y2); + + double cosx = (a2 + b2 - c2) / (2 * sqrt(a2 * b2)); + double sinx = sqrt(1 - cosx * cosx); + double d = radius / ((1 - cosx) / sinx); + + double anx = (x1 - x0) / sqrt(a2); + double any = (y1 - y0) / sqrt(a2); + double bnx = (x1 - x2) / sqrt(b2); + double bny = (y1 - y2) / sqrt(b2); + + double x3 = x1 - anx * d; + double y3 = y1 - any * d; + double x4 = x1 - bnx * d; + double y4 = y1 - bny * d; + + int ccw = dir < 0.0; + double cx = x3 + any * radius * (ccw ? 1 : -1); + double cy = y3 - anx * radius * (ccw ? 1 : -1); + double a0 = atan2(y3 - cy, x3 - cx); + double a1 = atan2(y4 - cy, x4 - cx); + + gdPathLineTo(path, x3, y3); + _gdPathArc(path, cx, cy, radius, a0, a1, ccw); +} + +BGD_DECLARE(void) gdPathRectangle(gdPathPtr path, double x, double y, double w, double h) +{ + gdPathMoveTo(path, x, y); + gdPathLineTo(path, x + w, y); + gdPathLineTo(path, x + w, y + h); + gdPathLineTo(path, x, y + h); + gdPathLineTo(path, x, y); + gdPathClose(path); +} + +BGD_DECLARE(void) gdPathClose(gdPathPtr path) +{ + const int numElements = gdArrayNumElements(&path->elements); + const gdPathOps OpClose = gdPathOpsClose; + if (numElements == 0) + return; + const gdPathOpsPtr lastOpPtr = gdArrayIndex(&path->elements, (unsigned int)numElements - 1); + if (*lastOpPtr == gdPathOpsClose) + return; + + gdPointF point; + point.x = path->start.x; + point.y = path->start.y; + gdArrayAppend(&path->elements, &OpClose); + gdArrayAppend(&path->points, &point); +} + +void gdPathBlend(gdContextPtr context, const gdSpanRlePtr rle); diff --git a/ext/gd/libgd/gd_path.h b/ext/gd/libgd/gd_path.h new file mode 100644 index 000000000000..e84f84e8e30b --- /dev/null +++ b/ext/gd/libgd/gd_path.h @@ -0,0 +1,43 @@ +#ifndef GD_PATH_H +#define GD_PATH_H + +#include "gd_vector2d_private.h" + +#include + +gdPathPtr gdPathAddRef(gdPathPtr path); + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI2 +#define M_PI2 M_PI * 2 +#endif + +#define EPSILON_DOUBLE 0.000000000001f +#define MAX_FULL_CIRCLES 65536 +#define PATH_KAPPA 0.5522847498 +#define DEFAULT_TOLERANCE 0.1 +static inline int _doubleEqualsEpsilon(double p1, double p2) +{ + return (fabs(p1 - p2) < EPSILON_DOUBLE); +} + +static inline int _doubleIsZero(double f) { return (fabs(f) <= EPSILON_DOUBLE); } + +#define ARRAY_LENGTH(__array) ((int)(sizeof(__array) / sizeof(__array[0]))) + +gdPaintPtr gdPaintCreateRgba(double r, double g, double b, double a); +gdPaintPtr gdPaintAddRef(gdPaintPtr paint); + +gdStatePtr gdStateCreate(); +void gdStateDestroy(gdStatePtr state); + +gdPathPtr gdPathDuplicateFlattened(const gdPathPtr path); +void gdPathClear(gdPathPtr path); + +gdPaintPtr gdPaintCreateForSurface(gdSurfacePtr surface); +gdPathPatternPtr gdPaintGetPattern(const gdPaintPtr paint); +void gdPaintSetSourceSurface(gdContextPtr context, gdSurfacePtr surface, double x, double y); +void gdPathDumpPathTransform(const gdPathPtr path, const gdPathMatrixPtr matrix); +#endif // GD_PATH_H diff --git a/ext/gd/libgd/gd_path_arc.c b/ext/gd/libgd/gd_path_arc.c new file mode 100644 index 000000000000..6b635168fbb1 --- /dev/null +++ b/ext/gd/libgd/gd_path_arc.c @@ -0,0 +1,250 @@ + +/* This is a port of Carl's amazing work on this. The license chosen + * here is the MPL 1.1, applying to this file only. + * + * Copyright © 2004 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Kristian Høgsberg + * Carl Worth + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_errors.h" +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "gd_array.h" +#include "gd_path.h" +#include "gd_path_arc.h" +#include "gd_path_matrix.h" +#include "gd_span_rle.h" +#include "gd_surface.h" + +/* Spline deviation from the circle in radius would be given by: + + error = sqrt (x**2 + y**2) - 1 + + A simpler error function to work with is: + + e = x**2 + y**2 - 1 + + From "Good approximation of circles by curvature-continuous Bezier + curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric + Design 8 (1990) 22-41, we learn: + + abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) + + and + abs (error) =~ 1/2 * e + + Of course, this error value applies only for the particular spline + approximation that is used in _gd_arc_segment. + + Detailed explanation https://itc.ktu.lt/index.php/ITC/article/view/11812 +*/ +static double _arc_error_normalized(double angle) +{ + return 2.0 / 27.0 * pow(sin(angle / 4), 6) / pow(cos(angle / 4), 2); +} + +static double _arc_max_angle_for_tolerance_normalized(double tolerance) +{ + double angle, error; + int i; + + /* Use table lookup to reduce search time in most cases. */ + struct { + double angle; + double error; + } table[] = { + {M_PI / 1.0, 0.0185185185185185036127}, {M_PI / 2.0, 0.000272567143730179811158}, + {M_PI / 3.0, 2.38647043651461047433e-05}, {M_PI / 4.0, 4.2455377443222443279e-06}, + {M_PI / 5.0, 1.11281001494389081528e-06}, {M_PI / 6.0, 3.72662000942734705475e-07}, + {M_PI / 7.0, 1.47783685574284411325e-07}, {M_PI / 8.0, 6.63240432022601149057e-08}, + {M_PI / 9.0, 3.2715520137536980553e-08}, {M_PI / 10.0, 1.73863223499021216974e-08}, + {M_PI / 11.0, 9.81410988043554039085e-09}, + }; + int table_size = ARRAY_LENGTH(table); + + for (i = 0; i < table_size; i++) + if (table[i].error < tolerance) + return table[i].angle; + + ++i; + do { + angle = M_PI / i++; + error = _arc_error_normalized(angle); + } while (error > tolerance); + + return angle; +} + +static int _arc_segments_needed(double angle, double radius, double tolerance) +{ + double major_axis, max_angle; + + major_axis = radius; + max_angle = _arc_max_angle_for_tolerance_normalized(tolerance / major_axis); + + return ceil(fabs(angle) / max_angle); +} + +/* We want to draw a single spline approximating a circular arc radius + R from angle A to angle B. Since we want a symmetric spline that + matches the endpoints of the arc in position and slope, we know + that the spline control points must be: + + (R * cos(A), R * sin(A)) + (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) + (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) + (R * cos(B), R * sin(B)) + + for some value of h. + + "Approximation of circular arcs by cubic polynomials", Michael + Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides + various values of h along with error analysis for each. + + From that paper, a very practical value of h is: + + h = 4/3 * R * tan(angle/4) + + This value does not give the spline with minimal error, but it does + provide a very good approximation, (6th-order convergence), and the + error expression is quite simple, (see the comment for + _arc_error_normalized). +*/ +static void _gd_arc_segment(gdPathPtr path, double xc, double yc, double radius, double angle_A, + double angle_B) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin(angle_A); + r_cos_A = radius * cos(angle_A); + r_sin_B = radius * sin(angle_B); + r_cos_B = radius * cos(angle_B); + + h = 4.0 / 3.0 * tan((angle_B - angle_A) / 4.0); + + gdPathCurveTo(path, xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A, + xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B, xc + r_cos_B, + yc + r_sin_B); +} + +static void _gd_arc_in_direction(gdPathPtr path, double xc, double yc, double radius, + double angle_min, double angle_max, arcDirectionType dir) +{ + if (!path) + return; + + if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { + angle_max = fmod(angle_max - angle_min, 2 * M_PI); + angle_min = fmod(angle_min, 2 * M_PI); + angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES; + } + + /* Recurse if drawing arc larger than pi */ + if (angle_max - angle_min > M_PI) { + double angle_mid = angle_min + (angle_max - angle_min) / 2.0; + if (dir == ARC_CW) { + _gd_arc_in_direction(path, xc, yc, radius, angle_min, angle_mid, dir); + + _gd_arc_in_direction(path, xc, yc, radius, angle_mid, angle_max, dir); + } else { + _gd_arc_in_direction(path, xc, yc, radius, angle_mid, angle_max, dir); + + _gd_arc_in_direction(path, xc, yc, radius, angle_min, angle_mid, dir); + } + } else if (angle_max != angle_min) { + int i, segments; + double step; + segments = _arc_segments_needed(angle_max - angle_min, radius, DEFAULT_TOLERANCE); + step = (angle_max - angle_min) / segments; + segments -= 1; + + if (dir == ARC_CCW) { + double t; + + t = angle_min; + angle_min = angle_max; + angle_max = t; + + step = -step; + } + gdPathMoveTo(path, xc + radius * cos(angle_min), yc + radius * sin(angle_min)); + + for (i = 0; i < segments; i++, angle_min += step) { + _gd_arc_segment(path, xc, yc, radius, angle_min, angle_min + step); + } + + _gd_arc_segment(path, xc, yc, radius, angle_min, angle_max); + } else { + gdPathLineTo(path, xc + radius * cos(angle_min), yc + radius * sin(angle_min)); + } +} + +void _gd_arc_path(gdPathPtr path, double xc, double yc, double radius, double angle_min, + double angle_max) +{ + if (angle_min > M_PI2) { + angle_min = fmod(angle_min, M_PI2); + } + + if (angle_max > M_PI2) { + printf(" fmod max -"); + angle_max = fmod(angle_max, M_PI2); + } + + if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { + angle_max = fmod(angle_max - angle_min, 2 * M_PI); + angle_min = fmod(angle_min, 2 * M_PI); + angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES; + } + _gd_arc_in_direction(path, xc, yc, radius, angle_min, angle_max, ARC_CW); +} + +void _gd_arc_path_negative(gdPathPtr path, double xc, double yc, double radius, double angle1, + double angle2) +{ + _gd_arc_in_direction(path, xc, yc, radius, angle2, angle1, ARC_CCW); +} diff --git a/ext/gd/libgd/gd_path_arc.h b/ext/gd/libgd/gd_path_arc.h new file mode 100644 index 000000000000..2e6a6e010c74 --- /dev/null +++ b/ext/gd/libgd/gd_path_arc.h @@ -0,0 +1,13 @@ +#ifndef GD_PATH_ARC_H +#define GD_PATH_ARC_H + +#include "gd_vector2d.h" + +void _gd_arc_path(gdPathPtr path, double xc, double yc, double radius, double angle1, + double angle2); + +void _gd_arc_path_negative(gdPathPtr path, double xc, double yc, double radius, double angle1, + double angle2); + +typedef enum arcDirectionTypeStruct { ARC_CW, ARC_CCW } arcDirectionType; +#endif // GD_PATH_ARC_H diff --git a/ext/gd/libgd/gd_path_dash.c b/ext/gd/libgd/gd_path_dash.c new file mode 100644 index 000000000000..a661de894e10 --- /dev/null +++ b/ext/gd/libgd/gd_path_dash.c @@ -0,0 +1,124 @@ +#include +#include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_errors.h" +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "gd_array.h" +#include "gd_path.h" +#include "gd_path_dash.h" +#include "gd_path_matrix.h" + +gdPathDashPtr gdPathDashCreate(const double *data, int size, double offset) +{ + if (data == NULL || size == 0) + return NULL; + + gdPathDashPtr dash = gdMalloc(sizeof(gdPathDash)); + if (!dash) { + return NULL; + } + dash->offset = offset; + dash->data = gdMalloc((size_t)size * sizeof(double)); + if (!dash->data) { + gdFree(dash); + return NULL; + } + dash->size = size; + memcpy(dash->data, data, (size_t)size * sizeof(double)); + return dash; +} + +void gdPathDashDestroy(gdPathDashPtr dash) +{ + if (dash == NULL) + return; + gdFree(dash->data); + gdFree(dash); +} + +gdPathDashPtr gdPathDashClone(const gdPathDashPtr dash) +{ + if (dash == NULL) + return NULL; + + return gdPathDashCreate(dash->data, dash->size, dash->offset); +} + +gdPathPtr gdPathApplyDash(const gdPathDashPtr dash, const gdPathPtr path) +{ + gdPathPtr flat = gdPathDuplicateFlattened(path); + gdPathPtr result = gdPathCreate(); + + gdArrayReallocBy(&result->elements, gdArrayNumElements(&flat->elements)); + gdArrayReallocBy(&result->points, gdArrayNumElements(&flat->points)); + + int toggle = 1; + int offset = 0; + double phase = dash->offset; + while (phase >= dash->data[offset]) { + toggle = !toggle; + phase -= dash->data[offset]; + if (++offset == dash->size) + offset = 0; + } + + gdPathOpsPtr elements = (gdPathOpsPtr)gdArrayGetData(&flat->elements); + gdPathOpsPtr end = elements + gdArrayNumElements(&flat->elements); + gdPointFPtr points = (gdPointFPtr)gdArrayGetData(&flat->points); + + while (elements < end) { + int itoggle = toggle; + int ioffset = offset; + double iphase = phase; + + double x0 = points->x; + double y0 = points->y; + + if (itoggle) + gdPathMoveTo(result, x0, y0); + ++elements; + ++points; + while (elements < end && *elements == gdPathOpsLineTo) { + double dx = points->x - x0; + double dy = points->y - y0; + double dist0 = sqrt(dx * dx + dy * dy); + double dist1 = 0; + while (dist0 - dist1 > dash->data[ioffset] - iphase) { + dist1 += dash->data[ioffset] - iphase; + double a = dist1 / dist0; + double x = x0 + a * dx; + double y = y0 + a * dy; + + if (itoggle) + gdPathLineTo(result, x, y); + else { + gdPathMoveTo(result, x, y); + } + + itoggle = !itoggle; + iphase = 0; + if (++ioffset == dash->size) + ioffset = 0; + } + + iphase += dist0 - dist1; + + x0 = points->x; + y0 = points->y; + + if (itoggle) + gdPathLineTo(result, x0, y0); + + ++elements; + ++points; + } + } + + gdPathDestroy(flat); + return result; +} diff --git a/ext/gd/libgd/gd_path_dash.h b/ext/gd/libgd/gd_path_dash.h new file mode 100644 index 000000000000..07d6c7126250 --- /dev/null +++ b/ext/gd/libgd/gd_path_dash.h @@ -0,0 +1,30 @@ +#ifndef GD_PATH_DASH_H +#define GD_PATH_DASH_H + +#include "gd_vector2d_private.h" + +#define _dash_init(dash) \ + do { \ + dash.data = NULL; \ + dash.size = 0; \ + dash.capacity = 0; \ + } while (0) + +#define _dash_allocate(dash, count) \ + do { \ + if (dash.size + count > dash.capacity) { \ + int capacity = dash.size + count; \ + int newcapacity = dash.capacity == 0 ? 8 : dash.capacity; \ + while (newcapacity < capacity) { \ + newcapacity *= 2; \ + } \ + dash.data = gdRealloc(dash.data, (size_t)newcapacity * sizeof(dash.data[0])); \ + dash.capacity = newcapacity; \ + } \ + } while (0) + +gdPathDashPtr gdPathDashCreate(const double *data, int size, double offset); +gdPathDashPtr gdPathDashClone(const gdPathDashPtr dash); +void gdPathDashDestroy(gdPathDashPtr dash); +gdPathPtr gdPathApplyDash(const gdPathDashPtr dash, const gdPathPtr path); +#endif // GD_PATH_DASH_H diff --git a/ext/gd/libgd/gd_path_matrix.c b/ext/gd/libgd/gd_path_matrix.c new file mode 100644 index 000000000000..ff932254badf --- /dev/null +++ b/ext/gd/libgd/gd_path_matrix.c @@ -0,0 +1,222 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd_path_matrix.h" +#include "gd_vector2d_private.h" + +BGD_DECLARE(void) +gdPathMatrixInit(gdPathMatrixPtr matrix, double m00, double m10, double m01, double m11, double m02, + double m12) +{ + matrix->m00 = m00; + matrix->m10 = m10; + matrix->m01 = m01; + matrix->m11 = m11; + matrix->m02 = m02; + matrix->m12 = m12; +} + +BGD_DECLARE(void) gdPathMatrixInitIdentity(gdPathMatrixPtr matrix) +{ + matrix->m00 = 1.0; + matrix->m10 = 0.0; + matrix->m01 = 0.0; + matrix->m11 = 1.0; + matrix->m02 = 0.0; + matrix->m12 = 0.0; +} + +BGD_DECLARE(void) gdPathMatrixInitTranslate(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrixInit(matrix, 1.0, 0.0, 0.0, 1.0, x, y); +} + +BGD_DECLARE(void) gdPathMatrixInitScale(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrixInit(matrix, x, 0.0, 0.0, y, 0.0, 0.0); +} + +BGD_DECLARE(void) gdPathMatrixInitShear(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrixInit(matrix, 1.0, tan(y), tan(x), 1.0, 0.0, 0.0); +} + +BGD_DECLARE(void) gdPathMatrixInitRotate(gdPathMatrixPtr matrix, double radians) +{ + double c = cos(radians); + double s = sin(radians); + + gdPathMatrixInit(matrix, c, s, -s, c, 0.0, 0.0); +} + +BGD_DECLARE(void) +gdPathMatrixInitRotateTranslate(gdPathMatrixPtr matrix, double radians, double x, double y) +{ + double c = cos(radians); + double s = sin(radians); + + double cx = x * (1 - c) + y * s; + double cy = y * (1 - c) - x * s; + + gdPathMatrixInit(matrix, c, s, -s, c, cx, cy); +} + +BGD_DECLARE(void) gdPathMatrixTranslate(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrix m; + gdPathMatrixInitTranslate(&m, x, y); + gdPathMatrixMultiply(matrix, &m, matrix); +} + +BGD_DECLARE(void) gdPathMatrixScale(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrix m; + gdPathMatrixInitScale(&m, x, y); + gdPathMatrixMultiply(matrix, &m, matrix); +} + +BGD_DECLARE(void) gdPathMatrixShear(gdPathMatrixPtr matrix, double x, double y) +{ + gdPathMatrix m; + gdPathMatrixInitShear(&m, x, y); + gdPathMatrixMultiply(matrix, &m, matrix); +} + +BGD_DECLARE(void) gdPathMatrixRotate(gdPathMatrixPtr matrix, double radians) +{ + gdPathMatrix m; + gdPathMatrixInitRotate(&m, radians); + gdPathMatrixMultiply(matrix, &m, matrix); +} + +BGD_DECLARE(void) +gdPathMatrixRotateTranslate(gdPathMatrixPtr matrix, double radians, double x, double y) +{ + gdPathMatrix m; + gdPathMatrixInitRotateTranslate(&m, radians, x, y); + gdPathMatrixMultiply(matrix, &m, matrix); +} + +BGD_DECLARE(void) +gdPathMatrixMultiply(gdPathMatrixPtr matrix, const gdPathMatrixPtr a, const gdPathMatrixPtr b) +{ + double m00 = a->m00 * b->m00 + a->m10 * b->m01; + double m10 = a->m00 * b->m10 + a->m10 * b->m11; + double m01 = a->m01 * b->m00 + a->m11 * b->m01; + double m11 = a->m01 * b->m10 + a->m11 * b->m11; + double m02 = a->m02 * b->m00 + a->m12 * b->m01 + b->m02; + double m12 = a->m02 * b->m10 + a->m12 * b->m11 + b->m12; + + gdPathMatrixInit(matrix, m00, m10, m01, m11, m02, m12); +} + +BGD_DECLARE(int) gdPathMatrixInvert(gdPathMatrixPtr matrix) +{ + double det = (matrix->m00 * matrix->m11 - matrix->m10 * matrix->m01); + if (det == 0.0) + return 0; + + double inv_det = 1.0 / det; + double m00 = matrix->m00 * inv_det; + double m10 = matrix->m10 * inv_det; + double m01 = matrix->m01 * inv_det; + double m11 = matrix->m11 * inv_det; + double m02 = (matrix->m01 * matrix->m12 - matrix->m11 * matrix->m02) * inv_det; + double m12 = (matrix->m10 * matrix->m02 - matrix->m00 * matrix->m12) * inv_det; + + gdPathMatrixInit(matrix, m11, -m10, -m01, m00, m02, m12); + return 1; +} + +BGD_DECLARE(void) +gdPathMatrixMap(const gdPathMatrixPtr matrix, double x, double y, double *result_x, + double *result_y) +{ + *result_x = x * matrix->m00 + y * matrix->m01 + matrix->m02; + *result_y = x * matrix->m10 + y * matrix->m11 + matrix->m12; +} + +BGD_DECLARE(void) +gdPathMatrixMapPoint(const gdPathMatrixPtr matrix, const gdPointFPtr src, gdPointFPtr dst) +{ + gdPathMatrixMap(matrix, src->x, src->y, &dst->x, &dst->y); +} + +BGD_DECLARE(void) +gdPathMatrixMapRect(const gdPathMatrixPtr matrix, const gdRectFPtr src, gdRectFPtr dst) +{ + gdPointF p[4]; + p[0].x = src->x; + p[0].y = src->y; + p[1].x = src->x + src->w; + p[1].y = src->y; + p[2].x = src->x + src->w; + p[2].y = src->y + src->h; + p[3].x = src->x; + p[3].y = src->y + src->h; + + gdPathMatrixMapPoint(matrix, &p[0], &p[0]); + gdPathMatrixMapPoint(matrix, &p[1], &p[1]); + gdPathMatrixMapPoint(matrix, &p[2], &p[2]); + gdPathMatrixMapPoint(matrix, &p[3], &p[3]); + + double l = p[0].x; + double t = p[0].y; + double r = p[0].x; + double b = p[0].y; + + for (int i = 1; i < 4; i++) { + if (p[i].x < l) + l = p[i].x; + if (p[i].x > r) + r = p[i].x; + if (p[i].y < t) + t = p[i].y; + if (p[i].y > b) + b = p[i].y; + } + + dst->x = l; + dst->y = t; + dst->w = r - l; + dst->h = b - t; +} + +void _matrix_get_affine(const gdPathMatrixPtr matrix, double *m00, double *m10, double *m01, + double *m11, double *m02, double *m12) +{ + *m00 = matrix->m00; + *m10 = matrix->m10; + + *m01 = matrix->m01; + *m11 = matrix->m11; + + if (m02) + *m02 = matrix->m02; + if (m12) + *m12 = matrix->m12; +} + +double _gd_matrix_transformed_circle_major_axis(const gdPathMatrixPtr matrix, double radius) +{ + double a, b, c, d, f, g, h, i, j; + + _matrix_get_affine(matrix, &a, &b, &c, &d, NULL, NULL); + + i = a * a + b * b; + j = c * c + d * d; + + f = 0.5 * (i + j); + g = 0.5 * (i - j); + h = a * c + b * d; + + return radius * sqrt(f + hypot(g, h)); + + /* + * we don't need the minor axis length, which is + * double min = radius * sqrt (f - sqrt (g*g+h*h)); + */ +} diff --git a/ext/gd/libgd/gd_path_matrix.h b/ext/gd/libgd/gd_path_matrix.h new file mode 100644 index 000000000000..c7049240bd8c --- /dev/null +++ b/ext/gd/libgd/gd_path_matrix.h @@ -0,0 +1,10 @@ +#ifndef GD_PATH_MATRIX_H +#define GD_PATH_MATRIX_H + +#include "gd_vector2d.h" + +/* Largest semiaxis after transforming a circle: radius times the largest + * singular value of the matrix's linear component. Translation is ignored. */ +double _gd_matrix_transformed_circle_major_axis(const gdPathMatrixPtr matrix, double radius); + +#endif /* GD_PATH_MATRIX_H */ diff --git a/ext/gd/libgd/gd_path_outline.h b/ext/gd/libgd/gd_path_outline.h new file mode 100644 index 000000000000..c22ba127498c --- /dev/null +++ b/ext/gd/libgd/gd_path_outline.h @@ -0,0 +1,19 @@ +#ifndef GD_PATH_OUTLINE_H +#define GD_PATH_OUTLINE_H + +#include "ftraster/gd_ft_raster.h" +#include "gd_vector2d_private.h" + +/* Internal gdPath <-> FreeType outline bridge. */ +GD_FT_Outline *gd_ft_outline_create(int points, int contours); +void gd_ft_outline_close(GD_FT_Outline *outline); +void gd_ft_outline_end(GD_FT_Outline *outline); +void gd_ft_outline_move_to(GD_FT_Outline *outline, double x, double y); +void gd_ft_outline_line_to(GD_FT_Outline *outline, double x, double y); +void gd_ft_outline_cubic_to(GD_FT_Outline *outline, double x1, double y1, double x2, double y2, + double x3, double y3); +void gd_ft_outline_conic_to(GD_FT_Outline *outline, double x1, double y1, double x2, double y2); +GD_FT_Outline *gd_ft_outline_convert(const gdPathPtr path, const gdPathMatrixPtr matrix); +void gd_ft_outline_destroy(GD_FT_Outline *outline); + +#endif diff --git a/ext/gd/libgd/gd_path_stroke.c b/ext/gd/libgd/gd_path_stroke.c new file mode 100644 index 000000000000..f3c74ae12228 --- /dev/null +++ b/ext/gd/libgd/gd_path_stroke.c @@ -0,0 +1,185 @@ +#include "ftraster/gd_ft_raster.h" +#include "ftraster/gd_ft_stroker.h" +#include "gd_path.h" +#include "gd_path_matrix.h" +#include "gd_path_outline.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" +#include + +static gdPathPtr ft_outline_to_gdpath(const GD_FT_Outline *outline) +{ + if (!outline || outline->n_points == 0) + return NULL; + + gdPathPtr path = gdPathCreate(); + if (!path) + return NULL; + + int first = 0; + for (int contour = 0; contour < outline->n_contours; contour++) { + int last = outline->contours[contour]; + int point = first; + GD_FT_Vector start; + + if (last < first) + goto invalid_outline; + + char first_tag = GD_FT_CURVE_TAG(outline->tags[first]); + char last_tag = GD_FT_CURVE_TAG(outline->tags[last]); + + if (first_tag == GD_FT_CURVE_TAG_ON) { + start = outline->points[point++]; + } else if (first_tag == GD_FT_CURVE_TAG_CONIC) { + if (last_tag == GD_FT_CURVE_TAG_ON) { + start = outline->points[last--]; + } else if (last_tag == GD_FT_CURVE_TAG_CONIC) { + start.x = (outline->points[first].x + outline->points[last].x) / 2; + start.y = (outline->points[first].y + outline->points[last].y) / 2; + } else { + goto invalid_outline; + } + } else { + goto invalid_outline; + } + + gdPathMoveTo(path, start.x / 64.0, start.y / 64.0); + + while (point <= last) { + char tag = GD_FT_CURVE_TAG(outline->tags[point]); + GD_FT_Vector current = outline->points[point++]; + + if (tag == GD_FT_CURVE_TAG_ON) { + gdPathLineTo(path, current.x / 64.0, current.y / 64.0); + continue; + } + + if (tag == GD_FT_CURVE_TAG_CONIC) { + GD_FT_Vector control = current; + for (;;) { + if (point > last) { + gdPathQuadTo(path, control.x / 64.0, control.y / 64.0, start.x / 64.0, + start.y / 64.0); + break; + } + + tag = GD_FT_CURVE_TAG(outline->tags[point]); + current = outline->points[point++]; + if (tag == GD_FT_CURVE_TAG_ON) { + gdPathQuadTo(path, control.x / 64.0, control.y / 64.0, current.x / 64.0, + current.y / 64.0); + break; + } + if (tag != GD_FT_CURVE_TAG_CONIC) + goto invalid_outline; + + GD_FT_Vector middle = {(control.x + current.x) / 2, + (control.y + current.y) / 2}; + gdPathQuadTo(path, control.x / 64.0, control.y / 64.0, middle.x / 64.0, + middle.y / 64.0); + control = current; + } + continue; + } + + if (tag != GD_FT_CURVE_TAG_CUBIC || point > last || + GD_FT_CURVE_TAG(outline->tags[point]) != GD_FT_CURVE_TAG_CUBIC) + goto invalid_outline; + + GD_FT_Vector control1 = current; + GD_FT_Vector control2 = outline->points[point++]; + GD_FT_Vector end = start; + if (point <= last) { + if (GD_FT_CURVE_TAG(outline->tags[point]) != GD_FT_CURVE_TAG_ON) + goto invalid_outline; + end = outline->points[point++]; + } + gdPathCurveTo(path, control1.x / 64.0, control1.y / 64.0, control2.x / 64.0, + control2.y / 64.0, end.x / 64.0, end.y / 64.0); + } + + gdPathClose(path); + first = outline->contours[contour] + 1; + } + + return path; + +invalid_outline: + gdPathDestroy(path); + return NULL; +} + +GD_VECTOR2D_INTERNAL gdPathPtr gdPathStrokeToPath(const gdPathPtr path, const gdStrokePtr stroke, + const gdPathMatrixPtr matrix) +{ + if (!path || !stroke || stroke->width <= 0) + return NULL; + + GD_FT_Outline *outline = gd_ft_outline_convert(path, matrix); + if (!outline) + return NULL; + + GD_FT_Stroker stroker; + GD_FT_Stroker_New(&stroker); + + double radius = stroke->width / 2.0; + GD_FT_Fixed ftWidth = (GD_FT_Fixed)(radius * 64); + GD_FT_Fixed ftMiterLimit = (GD_FT_Fixed)(stroke->miterlimit * 65536); + + GD_FT_Stroker_LineCap ftCap; + switch (stroke->cap) { + case gdLineCapSquare: + ftCap = GD_FT_STROKER_LINECAP_SQUARE; + break; + case gdLineCapRound: + ftCap = GD_FT_STROKER_LINECAP_ROUND; + break; + case gdLineCapButt: + default: + ftCap = GD_FT_STROKER_LINECAP_BUTT; + break; + } + + GD_FT_Stroker_LineJoin ftJoin; + switch (stroke->join) { + case gdLineJoinBevel: + ftJoin = GD_FT_STROKER_LINEJOIN_BEVEL; + break; + case gdLineJoinRound: + ftJoin = GD_FT_STROKER_LINEJOIN_ROUND; + break; + case gdLineJoinMiter: + default: + ftJoin = GD_FT_STROKER_LINEJOIN_MITER_FIXED; + break; + } + + GD_FT_Stroker_Set(stroker, ftWidth, ftCap, ftJoin, ftMiterLimit); + + GD_FT_Stroker_ParseOutline(stroker, outline); + + GD_FT_UInt points, contours; + GD_FT_Stroker_GetCounts(stroker, &points, &contours); + + GD_FT_Outline *strokeOutline = gd_ft_outline_create((int)points, (int)contours); + if (!strokeOutline) { + GD_FT_Stroker_Done(stroker); + gd_ft_outline_destroy(outline); + return NULL; + } + + // Use combined export (both borders + caps) - works for both open and closed paths + GD_FT_Stroker_Export(stroker, strokeOutline); + GD_FT_Stroker_Done(stroker); + + gdPathPtr strokePath = ft_outline_to_gdpath(strokeOutline); + + gd_ft_outline_destroy(outline); + gd_ft_outline_destroy(strokeOutline); + + if (!strokePath) { + return NULL; + } + + return strokePath; +} diff --git a/ext/gd/libgd/gd_perceptual_diff.c b/ext/gd/libgd/gd_perceptual_diff.c new file mode 100644 index 000000000000..f4b68babd190 --- /dev/null +++ b/ext/gd/libgd/gd_perceptual_diff.c @@ -0,0 +1,149 @@ +#include "gd.h" + +#include +#include +#include + +#define GD_PERCEPTUAL_MAX_DELTA 35215.0 + +static uint8_t gd_alpha_to_opacity(int alpha) +{ + return (uint8_t)(((127 - alpha) * 255 + 63) / 127); +} + +static uint8_t blend(uint8_t color, uint8_t alpha, uint8_t background) +{ + return (uint8_t)((color * alpha + background * (255 - alpha) + 127) / 255); +} + +static double pixel_diff_yiq(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2) +{ + double dr = r1 - r2; + double dg = g1 - g2; + double db = b1 - b2; + double y = dr * 0.29889531 + dg * 0.58662247 + db * 0.11448223; + double i = dr * 0.59597799 - dg * 0.27417610 - db * 0.32180189; + double q = dr * 0.21147017 - dg * 0.52261711 + db * 0.31114694; + + return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q; +} + +BGD_DECLARE(int) gdImagePerceptualDiff(gdImagePtr image1, gdImagePtr image2, double threshold, + const gdImagePerceptualDiffOptions *options, gdImagePtr *diff_image, + gdImagePerceptualDiffResult *result) +{ + gdImageDiffMode mode = GD_IMAGE_DIFF_OVERLAY; + int highlight_color = gdTrueColorAlpha(255, 0, 0, 0); + gdImagePtr diff = NULL; + double maximum_delta = 0.0; + double max_yiq_delta; + int alpha_threshold; + int x, y; + + if (diff_image != NULL) { + *diff_image = NULL; + } + if (result != NULL) { + result->pixels_changed = 0; + result->maximum_delta = 0.0; + } + + if (image1 == NULL || image2 == NULL || result == NULL || + !(threshold >= 0.0 && threshold <= 1.0) || image1->sx != image2->sx || + image1->sy != image2->sy) { + return 0; + } + + if (options != NULL) { + mode = options->mode; + highlight_color = options->highlight_color; + } + if (mode < GD_IMAGE_DIFF_NONE || mode > GD_IMAGE_DIFF_MASK) { + return 0; + } + + if (mode != GD_IMAGE_DIFF_NONE && diff_image != NULL) { + diff = gdImageCreateTrueColor(image1->sx, image1->sy); + if (diff == NULL) { + return 0; + } + gdImageAlphaBlending(diff, 0); + gdImageSaveAlpha(diff, 1); + } + + max_yiq_delta = GD_PERCEPTUAL_MAX_DELTA * threshold * threshold; + alpha_threshold = (int)(threshold * 127); + + for (y = 0; y < image1->sy; y++) { + for (x = 0; x < image1->sx; x++) { + int p1 = gdImageGetTrueColorPixel(image1, x, y); + int p2 = gdImageGetTrueColorPixel(image2, x, y); + double delta = 0.0; + int is_diff = 0; + + if (p1 != p2) { + uint8_t r1 = gdTrueColorGetRed(p1); + uint8_t g1 = gdTrueColorGetGreen(p1); + uint8_t b1 = gdTrueColorGetBlue(p1); + int ga1 = gdTrueColorGetAlpha(p1); + uint8_t r2 = gdTrueColorGetRed(p2); + uint8_t g2 = gdTrueColorGetGreen(p2); + uint8_t b2 = gdTrueColorGetBlue(p2); + int ga2 = gdTrueColorGetAlpha(p2); + int alpha_delta = abs(ga1 - ga2); + + delta = (double)alpha_delta / 127.0; + if (alpha_delta > alpha_threshold) { + is_diff = 1; + } else { + uint8_t a1 = gd_alpha_to_opacity(ga1); + uint8_t a2 = gd_alpha_to_opacity(ga2); + double yiq_delta = 0.0; + + if (a1 == 0 && a2 == 0) { + delta = 0.0; + } else if (a1 > 250 && a2 > 250) { + yiq_delta = pixel_diff_yiq(r1, g1, b1, r2, g2, b2); + } else { + double delta_white = pixel_diff_yiq(blend(r1, a1, 255), blend(g1, a1, 255), + blend(b1, a1, 255), blend(r2, a2, 255), + blend(g2, a2, 255), blend(b2, a2, 255)); + double delta_black = + pixel_diff_yiq(blend(r1, a1, 0), blend(g1, a1, 0), blend(b1, a1, 0), + blend(r2, a2, 0), blend(g2, a2, 0), blend(b2, a2, 0)); + yiq_delta = delta_white < delta_black ? delta_white : delta_black; + } + if (yiq_delta > 0.0) { + double color_delta = sqrt(yiq_delta / GD_PERCEPTUAL_MAX_DELTA); + if (color_delta > delta) { + delta = color_delta; + } + is_diff = yiq_delta > max_yiq_delta; + } + } + } + + if (delta > maximum_delta) { + maximum_delta = delta; + } + if (is_diff) { + result->pixels_changed++; + if (diff != NULL) { + gdImageSetPixel(diff, x, y, highlight_color); + } + } else if (diff != NULL && mode == GD_IMAGE_DIFF_OVERLAY) { + gdImageSetPixel(diff, x, y, + gdTrueColorAlpha(gdTrueColorGetRed(p1), gdTrueColorGetGreen(p1), + gdTrueColorGetBlue(p1), 102)); + } else if (diff != NULL) { + gdImageSetPixel(diff, x, y, gdTrueColorAlpha(0, 0, 0, 127)); + } + } + } + + result->maximum_delta = maximum_delta; + if (diff_image != NULL) { + *diff_image = diff; + } + return 1; +} diff --git a/ext/gd/libgd/gd_png.c b/ext/gd/libgd/gd_png.c index 655eee77b906..a07c1ca143c8 100644 --- a/ext/gd/libgd/gd_png.c +++ b/ext/gd/libgd/gd_png.c @@ -1,783 +1,1822 @@ -#include -#include -#include -#include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "gd.h" #include "gd_errors.h" +#include "gd_intern.h" +#include +#include +#include +#include +#include /* JCE: Arrange HAVE_LIBPNG so that it can be set in gd.h */ #ifdef HAVE_LIBPNG -#include "png.h" /* includes zlib.h and setjmp.h */ #include "gdhelpers.h" +#include "png.h" /* includes zlib.h and setjmp.h */ +#include "zlib.h" #define TRUE 1 #define FALSE 0 -/*--------------------------------------------------------------------------- +static const unsigned char gdPngSignature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - gd_png.c Copyright 1999 Greg Roelofs and Thomas Boutell +BGD_DECLARE(const char *) gdPngGetVersionString(void) { return PNG_LIBPNG_VER_STRING; } - The routines in this file, gdImagePng*() and gdImageCreateFromPng*(), - are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(), - except that these functions are noisier in the case of errors (comment - out all fprintf() statements to disable that). +static unsigned int gdPngGetUint32(const unsigned char *data) +{ + return ((unsigned int)data[0] << 24) | ((unsigned int)data[1] << 16) | + ((unsigned int)data[2] << 8) | (unsigned int)data[3]; +} - GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs. - GD 2.0 supports 8 bits of color resolution per channel and - 7 bits of alpha channel resolution. Images with more than 8 bits - per channel are reduced to 8 bits. Images with an alpha channel are - only able to resolve down to '1/128th opaque' instead of '1/256th', - and this conversion is also automatic. I very much doubt you can see it. - Both tRNS and true alpha are supported. +static void gdPngPutUint32(unsigned char *data, size_t value) +{ + data[0] = (unsigned char)((value >> 24) & 0xff); + data[1] = (unsigned char)((value >> 16) & 0xff); + data[2] = (unsigned char)((value >> 8) & 0xff); + data[3] = (unsigned char)(value & 0xff); +} - Gamma is ignored, and there is no support for text annotations. +static int gdPngChunkIs(const unsigned char *type, const char *name) +{ + return memcmp(type, name, 4) == 0; +} - Last updated: 9 February 2001 +static int gdPngIsXmpItxt(const unsigned char *data, size_t size) +{ + static const char keyword[] = "XML:com.adobe.xmp"; + size_t keyword_size = sizeof(keyword) - 1; - ---------------------------------------------------------------------------*/ + return size > keyword_size && memcmp(data, keyword, keyword_size) == 0 && + data[keyword_size] == 0; +} -const char * gdPngGetVersionString() +static int gdPngIsRawProfile(const unsigned char *data, size_t size, const char *profile_type) { - return PNG_LIBPNG_VER_STRING; + static const char prefix[] = "Raw profile type "; + size_t prefix_size = sizeof(prefix) - 1; + size_t profile_type_size; + + if (data == NULL) { + return FALSE; + } + + profile_type_size = strlen(profile_type); + return size > prefix_size + profile_type_size && memcmp(data, prefix, prefix_size) == 0 && + memcmp(data + prefix_size, profile_type, profile_type_size) == 0 && + data[prefix_size + profile_type_size] == 0; } -#ifdef PNG_SETJMP_SUPPORTED -typedef struct _jmpbuf_wrapper +static int gdPngSetTextProfile(gdImageMetadata *metadata, const unsigned char *data, size_t size) { - jmp_buf jmpbuf; -} jmpbuf_wrapper; + const unsigned char *nul; + size_t keyword_size; + char *key; + int status; + + nul = (const unsigned char *)memchr(data, 0, size); + if (nul == NULL || nul == data) { + return GD_META_OK; + } + + keyword_size = (size_t)(nul - data); + key = (char *)gdMalloc(sizeof("png:text:") + keyword_size); + if (key == NULL) { + return GD_META_ERR_NOMEM; + } + memcpy(key, "png:text:", sizeof("png:text:") - 1); + memcpy(key + sizeof("png:text:") - 1, data, keyword_size); + key[sizeof("png:text:") - 1 + keyword_size] = '\0'; -static void gdPngErrorHandler (png_structp png_ptr, png_const_charp msg) + status = gdImageMetadataSetProfile(metadata, key, data, size); + gdFree(key); + return status; +} + +static int gdPngReadMetadataFromMemory(const unsigned char *png, size_t png_size, + gdImageMetadata *metadata) { - jmpbuf_wrapper *jmpbuf_ptr; + size_t pos = 8; - /* This function, aside from the extra step of retrieving the "error - * pointer" (below) and the fact that it exists within the application - * rather than within libpng, is essentially identical to libpng's - * default error handler. The second point is critical: since both - * setjmp() and longjmp() are called from the same code, they are - * guaranteed to have compatible notions of how big a jmp_buf is, - * regardless of whether _BSD_SOURCE or anything else has (or has not) - * been defined. - */ + if (metadata == NULL) { + return GD_META_OK; + } + if (png == NULL || png_size < 8 || memcmp(png, gdPngSignature, 8) != 0) { + return GD_META_ERR_FORMAT; + } - gd_error_ex(GD_WARNING, "gd-png: fatal libpng error: %s", msg); + while (pos + 12 <= png_size) { + unsigned int chunk_size = gdPngGetUint32(png + pos); + const unsigned char *type = png + pos + 4; + const unsigned char *chunk_data = png + pos + 8; + int status = GD_META_OK; + + if ((size_t)chunk_size > png_size - pos - 12) { + return GD_META_ERR_PARSE; + } + + if (gdPngChunkIs(type, "eXIf")) { + status = gdImageMetadataSetProfile(metadata, "exif", chunk_data, chunk_size); + } else if (gdPngChunkIs(type, "zTXt") && + gdPngIsRawProfile(chunk_data, chunk_size, "exif")) { + status = gdImageMetadataSetProfile(metadata, "exif", chunk_data, chunk_size); + } else if (gdPngChunkIs(type, "iCCP")) { + status = gdImageMetadataSetProfile(metadata, "icc", chunk_data, chunk_size); + } else if (gdPngChunkIs(type, "iTXt") && gdPngIsXmpItxt(chunk_data, chunk_size)) { + status = gdImageMetadataSetProfile(metadata, "xmp", chunk_data, chunk_size); + } else if (gdPngChunkIs(type, "zTXt") && gdPngIsRawProfile(chunk_data, chunk_size, "xmp")) { + status = gdImageMetadataSetProfile(metadata, "xmp", chunk_data, chunk_size); + } else if (gdPngChunkIs(type, "tEXt")) { + status = gdPngSetTextProfile(metadata, chunk_data, chunk_size); + } + if (status != GD_META_OK) { + return status; + } + + pos += (size_t)chunk_size + 12; + if (gdPngChunkIs(type, "IEND")) { + return GD_META_OK; + } + } - jmpbuf_ptr = png_get_error_ptr (png_ptr); - if (jmpbuf_ptr == NULL) { /* we are completely hosed now */ - gd_error_ex(GD_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating."); - } + return GD_META_ERR_PARSE; +} + +static int gdPngAppendChunk(unsigned char *out, size_t *out_pos, const char *type, + const unsigned char *data, size_t size) +{ + uLong crc; + + if (data == NULL || size > UINT32_MAX) { + return GD_META_ERR_INVALID; + } - longjmp (jmpbuf_ptr->jmpbuf, 1); + gdPngPutUint32(out + *out_pos, size); + memcpy(out + *out_pos + 4, type, 4); + if (size != 0) { + memcpy(out + *out_pos + 8, data, size); + } + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, out + *out_pos + 4, (uInt)(size + 4)); + gdPngPutUint32(out + *out_pos + 8 + size, crc); + *out_pos += size + 12; + return GD_META_OK; } -static void gdPngWarningHandler (png_structp png_ptr, png_const_charp msg) +static int gdPngMetadataChunkSize(size_t *total, const unsigned char *data, size_t size) { - gd_error_ex(GD_WARNING, "gd-png: libpng warning: %s", msg); + if (data == NULL) { + return GD_META_OK; + } + if (size > UINT32_MAX || (size_t)-1 - *total < size + 12) { + return GD_META_ERR_LIMIT; + } + *total += size + 12; + return GD_META_OK; +} + +static int gdPngShouldSkipChunk(const unsigned char *type, const unsigned char *data, size_t size, + const gdImageMetadata *metadata) +{ + return (gdImageMetadataGetProfile(metadata, "exif", NULL) != NULL && + (gdPngChunkIs(type, "eXIf") || + (gdPngChunkIs(type, "zTXt") && gdPngIsRawProfile(data, size, "exif")))) || + (gdImageMetadataGetProfile(metadata, "icc", NULL) != NULL && + gdPngChunkIs(type, "iCCP")) || + (gdImageMetadataGetProfile(metadata, "xmp", NULL) != NULL && + ((gdPngChunkIs(type, "iTXt") && gdPngIsXmpItxt(data, size)) || + (gdPngChunkIs(type, "zTXt") && gdPngIsRawProfile(data, size, "xmp")))); +} + +static void *gdPngReadCtxToMemory(gdIOCtx *infile, int *size) +{ + enum { GD_PNG_ALLOC_STEP = 8192 }; + unsigned char *data = NULL; + int logical_size = 0; + int real_size = 0; + + if (size != NULL) { + *size = 0; + } + if (infile == NULL || size == NULL) { + return NULL; + } + + for (;;) { + int n; + + if (real_size - logical_size < GD_PNG_ALLOC_STEP) { + unsigned char *temp; + int new_size; + + if (real_size > INT_MAX - GD_PNG_ALLOC_STEP) { + gdFree(data); + return NULL; + } + new_size = real_size + GD_PNG_ALLOC_STEP; + temp = (unsigned char *)gdRealloc(data, new_size); + if (temp == NULL) { + gdFree(data); + return NULL; + } + data = temp; + real_size = new_size; + } + + n = gdGetBuf(data + logical_size, GD_PNG_ALLOC_STEP, infile); + if (n <= 0) { + break; + } + logical_size += n; + } + + *size = logical_size; + return data; +} + +/*--------------------------------------------------------------------------- + + gd_png.c Copyright 1999 Greg Roelofs and Thomas Boutell + + The routines in this file, gdImagePng*() and gdImageCreateFromPng*(), + are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(), + except that these functions are noisier in the case of errors (comment + out all fprintf() statements to disable that). + + GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs. + GD 2.0 supports 8 bits of color resolution per channel and + 7 bits of alpha channel resolution. Images with more than 8 bits + per channel are reduced to 8 bits. Images with an alpha channel are + only able to resolve down to '1/128th opaque' instead of '1/256th', + and this conversion is also automatic. I very much doubt you can see it. + Both tRNS and true alpha are supported. + + Gamma is ignored, and there is no support for text annotations. + + Last updated: 9 February 2001 + + ---------------------------------------------------------------------------*/ + +/** + * File: PNG IO + * + * Read and write PNG images. + */ + +#ifdef PNG_SETJMP_SUPPORTED +typedef struct _jmpbuf_wrapper { + jmp_buf jmpbuf; +} jmpbuf_wrapper; + +static void gdPngErrorHandler(png_structp png_ptr, png_const_charp msg) +{ + jmpbuf_wrapper *jmpbuf_ptr; + + /* This function, aside from the extra step of retrieving the "error + * pointer" (below) and the fact that it exists within the application + * rather than within libpng, is essentially identical to libpng's + * default error handler. The second point is critical: since both + * setjmp() and longjmp() are called from the same code, they are + * guaranteed to have compatible notions of how big a jmp_buf is, + * regardless of whether _BSD_SOURCE or anything else has (or has not) + * been defined. */ + + gd_error_ex(GD_WARNING, "gd-png: fatal libpng error: %s\n", msg); + + jmpbuf_ptr = png_get_error_ptr(png_ptr); + if (jmpbuf_ptr == NULL) { /* we are completely hosed now */ + gd_error_ex(GD_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf " + "unrecoverable; terminating.\n"); + exit(99); + } + + longjmp(jmpbuf_ptr->jmpbuf, 1); +} + +static void gdPngWarningHandler(UNUSED_PARAM(png_structp png_ptr), png_const_charp msg) +{ + gd_error_ex(GD_WARNING, "gd-png: libpng warning: %s", msg); } #endif -static void gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length) +static void gdPngReadData(png_structp png_ptr, png_bytep data, png_size_t length) { - int check; - check = gdGetBuf(data, length, (gdIOCtx *) png_get_io_ptr(png_ptr)); - if (check != length) { - png_error(png_ptr, "Read Error: truncated data"); - } + int check; + check = gdGetBuf(data, length, (gdIOCtx *)png_get_io_ptr(png_ptr)); + if (check != (int)length) { + png_error(png_ptr, "Read Error: truncated data"); + } } -static void gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length) +static void gdPngWriteData(png_structp png_ptr, png_bytep data, png_size_t length) +{ + gdPutBuf(data, length, (gdIOCtx *)png_get_io_ptr(png_ptr)); +} + +static void gdPngFlushData(png_structp png_ptr) { (void)png_ptr; } + +/* + Function: gdImageCreateFromPng + + is called to load images from PNG format + files. Invoke with an already opened + pointer to a FILE containing the desired + image. returns a to the new + image, or NULL if unable to load the image (most often because the + file is corrupt or does not contain a PNG + image). does not close the file. You can + inspect the sx and sy members of the image to determine its + size. The image must eventually be destroyed using + gdImageDestroy(). + + If the PNG image being loaded is a truecolor image, the resulting + gdImagePtr will refer to a truecolor image. If the PNG image being + loaded is a palette or grayscale image, the resulting gdImagePtr + will refer to a palette image. gd retains only 8 bits of + resolution for each of the red, green and blue channels, and only + 7 bits of resolution for the alpha channel. The former restriction + affects only a handful of very rare 48-bit color and 16-bit + grayscale PNG images. The second restriction affects all + semitransparent PNG images, but the difference is essentially + invisible to the eye. 7 bits of alpha channel resolution is, in + practice, quite a lot. + + Variants: + + creates an image from PNG data (i.e. the + contents of a PNG file) already in memory. + + reads in an image using the functions in + a struct. + + is similar to + but uses the old interface. + It is *obsolete*. + + Parameters: + + infile - The input FILE pointer. + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + (start code) + + gdImagePtr im; + ... inside a function ... + FILE *in; + in = fopen("mypng.png", "rb"); + im = gdImageCreateFromPng(in); + fclose(in); + // ... Use the image ... + gdImageDestroy(im); + + (end code) + */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPng(FILE *inFile) { - gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr(png_ptr)); + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromPngCtx(in); + in->gd_free(in); + return im; } -static void gdPngFlushData (png_structp png_ptr) +/* + Function: gdImageCreateFromPngPtr + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngPtr(int size, void *data) { + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromPngCtx(in); + in->gd_free(in); + return im; } -gdImagePtr gdImageCreateFromPng (FILE * inFile) +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngPtrWithMetadata(int size, void *data, gdImageMetadata *metadata) { - gdImagePtr im; - gdIOCtx *in = gdNewFileCtx(inFile); - im = gdImageCreateFromPngCtx(in); - in->gd_free(in); + gdImagePtr im; + gdIOCtx *in; + + if (metadata != NULL) { + int status = gdPngReadMetadataFromMemory((const unsigned char *)data, size, metadata); + if (status != GD_META_OK) { + return NULL; + } + } - return im; + in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromPngCtx(in); + in->gd_free(in); + return im; } -gdImagePtr gdImageCreateFromPngPtr (int size, void *data) +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngCtxWithMetadata(gdIOCtx *infile, gdImageMetadata *metadata) { - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromPngCtx(in); - in->gd_free(in); - return im; + void *data; + int size; + gdImagePtr im; + + data = gdPngReadCtxToMemory(infile, &size); + if (data == NULL) { + return NULL; + } + + im = gdImageCreateFromPngPtrWithMetadata(size, data, metadata); + gdFree(data); + return im; } -/* This routine is based in part on the Chapter 13 demo code in "PNG: The - * Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html). +/* This routine is based in part on the Chapter 13 demo code in + * "PNG: The Definitive Guide" (http://www.libpng.org/pub/png/book/). */ -gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile) + +/* + Function: gdImageCreateFromPngCtx + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngCtx(gdIOCtx *infile) { - png_byte sig[8]; + png_byte sig[8]; #ifdef PNG_SETJMP_SUPPORTED - jmpbuf_wrapper jbw; + jmpbuf_wrapper jbw; #endif - png_structp png_ptr; - png_infop info_ptr; - png_uint_32 width, height, rowbytes, w, h, res_x, res_y; - int bit_depth, color_type, interlace_type, unit_type; - int num_palette, num_trans; - png_colorp palette; - png_color_16p trans_gray_rgb; - png_color_16p trans_color_rgb; - png_bytep trans; - volatile png_bytep image_data = NULL; - volatile png_bytepp row_pointers = NULL; - gdImagePtr im = NULL; - int i, j, *open = NULL; - volatile int transparent = -1; - volatile int palette_allocated = FALSE; - - - /* Make sure the signature can't match by dumb luck -- TBB */ - /* GRR: isn't sizeof(infile) equal to the size of the pointer? */ - memset (sig, 0, sizeof(sig)); - - /* first do a quick check that the file really is a PNG image; could - * have used slightly more general png_sig_cmp() function instead - */ - if (gdGetBuf(sig, 8, infile) < 8) { - return NULL; - } - - if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */ - return NULL; - } + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height, rowbytes, w, h, res_x, res_y; + int bit_depth, color_type, interlace_type, unit_type; + int num_palette = 0, num_trans; + png_colorp palette; + png_color_16p trans_gray_rgb; + png_color_16p trans_color_rgb; + png_bytep trans; + png_bytep image_data = NULL; + png_bytepp row_pointers = NULL; + gdImagePtr im = NULL; + int i, j, *open = NULL; + volatile int transparent = -1; + volatile int palette_allocated = FALSE; + + /* Make sure the signature can't match by dumb luck -- TBB */ + /* GRR: isn't sizeof(infile) equal to the size of the pointer? */ + memset(sig, 0, sizeof(sig)); + + /* first do a quick check that the file really is a PNG image; could + * have used slightly more general png_sig_cmp() function instead */ + if (gdGetBuf(sig, 8, infile) < 8) { + return NULL; + } + + if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */ + return NULL; /* bad signature */ + } #ifdef PNG_SETJMP_SUPPORTED - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, gdPngWarningHandler); + png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, gdPngWarningHandler); #else - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif - if (png_ptr == NULL) { - gd_error("gd-png error: cannot allocate libpng main struct"); - return NULL; - } - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - gd_error("gd-png error: cannot allocate libpng info struct"); - png_destroy_read_struct (&png_ptr, NULL, NULL); - - return NULL; - } - - /* we could create a second info struct here (end_info), but it's only - * useful if we want to keep pre- and post-IDAT chunk info separated - * (mainly for PNG-aware image editors and converters) - */ - - /* setjmp() must be called in every non-callback function that calls a - * PNG-reading libpng function - */ + if (png_ptr == NULL) { + gd_error("gd-png error: cannot allocate libpng main struct\n"); + return NULL; + } + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + gd_error("gd-png error: cannot allocate libpng info struct\n"); + png_destroy_read_struct(&png_ptr, NULL, NULL); + + return NULL; + } + + /* we could create a second info struct here (end_info), but it's only + * useful if we want to keep pre- and post-IDAT chunk info separated + * (mainly for PNG-aware image editors and converters) + */ + + /* setjmp() must be called in every non-callback function that calls a + * PNG-reading libpng function. We must reset it everytime we get a + * new allocation that we save in a stack variable. + */ #ifdef PNG_SETJMP_SUPPORTED - if (setjmp(jbw.jmpbuf)) { - gd_error("gd-png error: setjmp returns error condition"); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (setjmp(jbw.jmpbuf)) { + gd_error("gd-png error: setjmp returns error condition 1\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return NULL; - } + return NULL; + } #endif - png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */ - - png_set_read_fn(png_ptr, (void *) infile, gdPngReadData); - png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ - - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); - if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA) - || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { - im = gdImageCreateTrueColor((int) width, (int) height); - } else { - im = gdImageCreate((int) width, (int) height); - } - if (im == NULL) { - gd_error("gd-png error: cannot allocate gdImage struct"); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - return NULL; - } - - if (bit_depth == 16) { - png_set_strip_16(png_ptr); - } else if (bit_depth < 8) { - png_set_packing (png_ptr); /* expand to 1 byte per pixel */ - } - - /* setjmp() must be called in every non-callback function that calls a - * PNG-reading libpng function - */ + png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */ + + png_set_read_fn(png_ptr, (void *)infile, gdPngReadData); + png_set_user_limits(png_ptr, 0x7fffffffL, 0x7fffffffL); + png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, + NULL); + if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA) || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + im = gdImageCreateTrueColor((int)width, (int)height); + } else { + im = gdImageCreate((int)width, (int)height); + } + if (im == NULL) { + gd_error("gd-png error: cannot allocate gdImage struct\n"); + goto error; + } + + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } else if (bit_depth < 8) { + png_set_packing(png_ptr); /* expand to 1 byte per pixel */ + } + + /* setjmp() must be called in every non-callback function that calls a + * PNG-reading libpng function. We must reset it everytime we get a + * new allocation that we save in a stack variable. + */ #ifdef PNG_SETJMP_SUPPORTED - if (setjmp(jbw.jmpbuf)) { - gd_error("gd-png error: setjmp returns error condition"); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - gdFree(image_data); - gdFree(row_pointers); - if (im) { - gdImageDestroy(im); - } - return NULL; - } + if (setjmp(jbw.jmpbuf)) { + gd_error("gd-png error: setjmp returns error condition 2\n"); + goto error; + } #endif #ifdef PNG_pHYs_SUPPORTED - /* check if the resolution is specified */ - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { - if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type)) { - switch (unit_type) { - case PNG_RESOLUTION_METER: - im->res_x = DPM2DPI(res_x); - im->res_y = DPM2DPI(res_y); - break; - } - } - } + /* check if the resolution is specified */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { + if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type)) { + switch (unit_type) { + case PNG_RESOLUTION_METER: + im->res_x = DPM2DPI(res_x); + im->res_y = DPM2DPI(res_y); + break; + } + } + } #endif - switch (color_type) { - case PNG_COLOR_TYPE_PALETTE: - png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + switch (color_type) { + case PNG_COLOR_TYPE_PALETTE: + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); #ifdef DEBUG - gd_error("gd-png color_type is palette, colors: %d", num_palette); + gd_error("gd-png color_type is palette, colors: %d\n", num_palette); #endif /* DEBUG */ - if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) { - /* gd 2.0: we support this rather thoroughly now. Grab the - * first fully transparent entry, if any, as the value of - * the simple-transparency index, mostly for backwards - * binary compatibility. The alpha channel is where it's - * really at these days. - */ - int firstZero = 1; - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); - for (i = 0; i < num_trans; ++i) { - im->alpha[i] = gdAlphaMax - (trans[i] >> 1); - if ((trans[i] == 0) && (firstZero)) { - transparent = i; - firstZero = 0; - } - } - } - break; - case PNG_COLOR_TYPE_GRAY: - /* create a fake palette and check for single-shade transparency */ - if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) { - gd_error("gd-png error: cannot allocate gray palette"); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - return NULL; - } - palette_allocated = TRUE; - if (bit_depth < 8) { - num_palette = 1 << bit_depth; - for (i = 0; i < 256; ++i) { - j = (255 * i) / (num_palette - 1); - palette[i].red = palette[i].green = palette[i].blue = j; - } - } else { - num_palette = 256; - for (i = 0; i < 256; ++i) { - palette[i].red = palette[i].green = palette[i].blue = i; - } - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb); - if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */ - transparent = trans_gray_rgb->gray >> 8; - } else { - transparent = trans_gray_rgb->gray; - } - /* Note slight error in 16-bit case: up to 256 16-bit shades - * may get mapped to a single 8-bit shade, and only one of them - * is supposed to be transparent. IOW, both opaque pixels and - * transparent pixels will be mapped into the transparent entry. - * There is no particularly good way around this in the case - * that all 256 8-bit shades are used, but one could write some - * custom 16-bit code to handle the case where there are gdFree - * palette entries. This error will be extremely rare in - * general, though. (Quite possibly there is only one such - * image in existence.) - */ - } - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - png_set_gray_to_rgb(png_ptr); - ZEND_FALLTHROUGH; - case PNG_COLOR_TYPE_RGB: - case PNG_COLOR_TYPE_RGB_ALPHA: - /* gd 2.0: we now support truecolor. See the comment above - * for a rare situation in which the transparent pixel may not - * work properly with 16-bit channels. - */ - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb); - if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */ - transparent = gdTrueColor(trans_color_rgb->red >> 8, - trans_color_rgb->green >> 8, - trans_color_rgb->blue >> 8); - } else { - transparent = gdTrueColor(trans_color_rgb->red, - trans_color_rgb->green, - trans_color_rgb->blue); - } - } - break; - } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + /* gd 2.0: we support this rather thoroughly now. Grab the + * first fully transparent entry, if any, as the value of + * the simple-transparency index, mostly for backwards + * binary compatibility. The alpha channel is where it's + * really at these days. + */ + int firstZero = 1; + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); + for (i = 0; i < num_trans; ++i) { + im->alpha[i] = gdAlphaMax - (trans[i] >> 1); + if ((trans[i] == 0) && (firstZero)) { + /* 2.0.5: long-forgotten patch from Wez Furlong */ + transparent = i; + firstZero = 0; + } + } + } + break; + + case PNG_COLOR_TYPE_GRAY: + /* create a fake palette and check for single-shade transparency */ + if ((palette = (png_colorp)gdMalloc(256 * sizeof(png_color))) == NULL) { + gd_error("gd-png error: cannot allocate gray palette\n"); + goto error; + } + palette_allocated = TRUE; + if (bit_depth < 8) { + num_palette = 1 << bit_depth; + for (i = 0; i < 256; ++i) { + j = (255 * i) / (num_palette - 1); + palette[i].red = palette[i].green = palette[i].blue = j; + } + } else { + num_palette = 256; + for (i = 0; i < 256; ++i) { + palette[i].red = palette[i].green = palette[i].blue = i; + } + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb); + if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */ + transparent = trans_gray_rgb->gray >> 8; + } else { + transparent = trans_gray_rgb->gray; + } + /* Note slight error in 16-bit case: up to 256 16-bit shades + * may get mapped to a single 8-bit shade, and only one of them + * is supposed to be transparent. IOW, both opaque pixels and + * transparent pixels will be mapped into the transparent entry. + * There is no particularly good way around this in the case + * that all 256 8-bit shades are used, but one could write some + * custom 16-bit code to handle the case where there are gdFree + * palette entries. This error will be extremely rare in + * general, though. (Quite possibly there is only one such + * image in existence.) */ + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_set_gray_to_rgb(png_ptr); + // fall through + // Keep above comment, gcc recognizes it and silent its warning about + // fall through case here + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + /* gd 2.0: we now support truecolor. See the comment above + for a rare situation in which the transparent pixel may not + work properly with 16-bit channels. */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb); + if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */ + transparent = gdTrueColor(trans_color_rgb->red >> 8, trans_color_rgb->green >> 8, + trans_color_rgb->blue >> 8); + } else { + transparent = gdTrueColor(trans_color_rgb->red, trans_color_rgb->green, + trans_color_rgb->blue); + } + } + break; + default: + gd_error("gd-png color_type is unknown: %d\n", color_type); + goto error; + } - /* enable the interlace transform if supported */ + /* enable the interlace transform if supported */ #ifdef PNG_READ_INTERLACING_SUPPORTED - (void)png_set_interlace_handling(png_ptr); + (void)png_set_interlace_handling(png_ptr); #endif - png_read_update_info(png_ptr, info_ptr); - - /* allocate space for the PNG image data */ - rowbytes = png_get_rowbytes(png_ptr, info_ptr); - image_data = (png_bytep) safe_emalloc(rowbytes, height, 0); - - row_pointers = (png_bytepp) safe_emalloc(height, sizeof(png_bytep), 0); - - /* set the individual row_pointers to point at the correct offsets */ - for (h = 0; h < height; ++h) { - row_pointers[h] = image_data + h * rowbytes; - } - - png_read_image(png_ptr, row_pointers); /* read whole image... */ - png_read_end(png_ptr, NULL); /* ...done! */ - - if (!im->trueColor) { - im->colorsTotal = num_palette; - /* load the palette and mark all entries "open" (unused) for now */ - open = im->open; - for (i = 0; i < num_palette; ++i) { - im->red[i] = palette[i].red; - im->green[i] = palette[i].green; - im->blue[i] = palette[i].blue; - open[i] = 1; - } - for (i = num_palette; i < gdMaxColors; ++i) { - open[i] = 1; - } - } - - /* 2.0.12: Slaven Rezic: palette images are not the only images - * with a simple transparent color setting. - */ - im->transparent = transparent; - im->interlace = (interlace_type == PNG_INTERLACE_ADAM7); - - /* can't nuke structs until done with palette */ - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - switch (color_type) { - case PNG_COLOR_TYPE_RGB: - for (h = 0; h < height; h++) { - int boffset = 0; - for (w = 0; w < width; w++) { - register png_byte r = row_pointers[h][boffset++]; - register png_byte g = row_pointers[h][boffset++]; - register png_byte b = row_pointers[h][boffset++]; - im->tpixels[h][w] = gdTrueColor (r, g, b); - } - } - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - case PNG_COLOR_TYPE_RGB_ALPHA: - for (h = 0; h < height; h++) { - int boffset = 0; - for (w = 0; w < width; w++) { - register png_byte r = row_pointers[h][boffset++]; - register png_byte g = row_pointers[h][boffset++]; - register png_byte b = row_pointers[h][boffset++]; - - /* gd has only 7 bits of alpha channel resolution, and - * 127 is transparent, 0 opaque. A moment of convenience, - * a lifetime of compatibility. - */ - - register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1); - im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a); - } - } - break; - - default: - /* Palette image, or something coerced to be one */ - for (h = 0; h < height; ++h) { - for (w = 0; w < width; ++w) { - register png_byte idx = row_pointers[h][w]; - im->pixels[h][w] = idx; - open[idx] = 0; - } - } - } + png_read_update_info(png_ptr, info_ptr); + + /* allocate space for the PNG image data */ + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + if (overflow2(rowbytes, height)) + goto error; + image_data = (png_bytep)gdMalloc(rowbytes * height); + if (!image_data) { + gd_error("gd-png error: cannot allocate image data\n"); + goto error; + } + if (overflow2(height, sizeof(png_bytep))) + goto error; + + row_pointers = (png_bytepp)gdMalloc(height * sizeof(png_bytep)); + if (!row_pointers) { + gd_error("gd-png error: cannot allocate row pointers\n"); + goto error; + } + + /* setjmp() must be called in every non-callback function that calls a + * PNG-reading libpng function. We must reset it everytime we get a + * new allocation that we save in a stack variable. + */ +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp(jbw.jmpbuf)) { + gd_error("gd-png error: setjmp returns error condition 3\n"); + goto error; + } +#endif + + /* set the individual row_pointers to point at the correct offsets */ + for (h = 0; h < height; ++h) { + row_pointers[h] = image_data + h * rowbytes; + } + + png_read_image(png_ptr, row_pointers); /* read whole image... */ + png_read_end(png_ptr, NULL); /* ...done! */ + + if (!im->trueColor) { + im->colorsTotal = num_palette; + /* load the palette and mark all entries "open" (unused) for now */ + open = im->open; + for (i = 0; i < num_palette; ++i) { + im->red[i] = palette[i].red; + im->green[i] = palette[i].green; + im->blue[i] = palette[i].blue; + open[i] = 1; + } + for (i = num_palette; i < gdMaxColors; ++i) { + open[i] = 1; + } + } + /* 2.0.12: Slaven Rezic: palette images are not the only images + with a simple transparent color setting */ + im->transparent = transparent; + im->interlace = (interlace_type == PNG_INTERLACE_ADAM7); + + /* can't nuke structs until done with palette */ + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + switch (color_type) { + case PNG_COLOR_TYPE_RGB: + for (h = 0; h < height; h++) { + int boffset = 0; + for (w = 0; w < width; w++) { + register png_byte r = row_pointers[h][boffset++]; + register png_byte g = row_pointers[h][boffset++]; + register png_byte b = row_pointers[h][boffset++]; + im->tpixels[h][w] = gdTrueColor(r, g, b); + } + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB_ALPHA: + for (h = 0; h < height; h++) { + int boffset = 0; + for (w = 0; w < width; w++) { + register png_byte r = row_pointers[h][boffset++]; + register png_byte g = row_pointers[h][boffset++]; + register png_byte b = row_pointers[h][boffset++]; + + /* gd has only 7 bits of alpha channel resolution, and + * 127 is transparent, 0 opaque. A moment of convenience, + * a lifetime of compatibility. + */ + + register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1); + im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a); + } + } + break; + default: + if (!im->trueColor) { + /* Palette image, or something coerced to be one */ + for (h = 0; h < height; ++h) { + for (w = 0; w < width; ++w) { + register png_byte idx = row_pointers[h][w]; + im->pixels[h][w] = idx; + open[idx] = 0; + } + } + } + } #ifdef DEBUG - if (!im->trueColor) { - for (i = num_palette; i < gdMaxColors; ++i) { - if (!open[i]) { - gd_error("gd-png warning: image data references out-of-range color index (%d)", i); - } - } - } + if (!im->trueColor) { + for (i = num_palette; i < gdMaxColors; ++i) { + if (!open[i]) { + fprintf(stderr, + "gd-png warning: image data references out-of-range" + " color index (%d)\n", + i); + } + } + } #endif - if (palette_allocated) { - gdFree(palette); - } - gdFree(image_data); - gdFree(row_pointers); +done: + if (palette_allocated) { + gdFree(palette); + } + if (image_data) + gdFree(image_data); + if (row_pointers) + gdFree(row_pointers); + + return im; + +error: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (im) { + gdImageDestroy(im); + im = NULL; + } + goto done; +} + +/* + Function: gdImagePngEx + + outputs the specified image to the specified file in + PNG format. The file must be open for writing. Under MSDOS and all + versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + In addition, allows the level of compression to be + specified. A compression level of 0 means "no compression." A + compression level of 1 means "compressed, but as quickly as + possible." A compression level of 9 means "compressed as much as + possible to produce the smallest possible file." A compression level + of -1 will use the default compression level at the time zlib was + compiled on your system. + + Variants: + + is equivalent to calling with + compression of -1. + + and write via a + instead of a file handle. + + and store the image file to + memory. + + Parameters: + + im - the image to write + outFile - the output FILE* object. + level - compression level: 0 -> none, 1-9 -> level, -1 -> default + + Returns: + + Nothing. + + Example: + (start code) + + gdImagePtr im; + int black, white; + FILE *out; + + im = gdImageCreate(100, 100); // Create the image + white = gdImageColorAllocate(im, 255, 255, 255); // Alloc background + black = gdImageColorAllocate(im, 0, 0, 0); // Allocate drawing color + gdImageRectangle(im, 0, 0, 99, 99, black); // Draw rectangle + out = fopen("rect.png", "wb"); // Open output file (binary) + gdImagePngEx(im, out, 9); // Write PNG, max compression + fclose(out); // Close file + gdImageDestroy(im); // Destroy image + + (end code) +*/ +BGD_DECLARE(void) gdImagePngEx(gdImagePtr im, FILE *outFile, int level) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImagePngCtxEx(im, out, level); + out->gd_free(out); +} + +/* + Function: gdImagePng + + Equivalent to calling with compression of -1. + + Parameters: + + im - the image to save. + outFile - the output FILE*. + + Returns: - return im; + Nothing. +*/ +BGD_DECLARE(void) gdImagePng(gdImagePtr im, FILE *outFile) { gdImagePngEx(im, outFile, -1); } + +static int _gdImagePngCtxWithOptions(gdImagePtr im, gdIOCtx *outfile, + const gdPngWriteOptions *options); + +BGD_DECLARE(void) gdPngWriteOptionsInit(gdPngWriteOptions *options) +{ + if (options == NULL) + return; + memset(options, 0, sizeof(*options)); + options->struct_size = sizeof(*options); + options->compression_level = -1; + options->filters = GD_PNG_FILTER_AUTO; + options->compression_strategy = GD_PNG_COMPRESSION_STRATEGY_DEFAULT; } -void gdImagePngEx (gdImagePtr im, FILE * outFile, int level, int basefilter) +static int gdPngWriteOptionsValid(const gdPngWriteOptions *options) { - gdIOCtx *out = gdNewFileCtx(outFile); - gdImagePngCtxEx(im, out, level, basefilter); - out->gd_free(out); + if (options->struct_size < sizeof(*options)) { + gd_error("gd-png error: invalid options structure size\n"); + return FALSE; + } + if (options->compression_level < -1 || options->compression_level > 9) { + gd_error("gd-png error: compression level must be -1 through 9\n"); + return FALSE; + } + if ((options->filters & ~GD_PNG_FILTER_ALL) != 0) { + gd_error("gd-png error: invalid filter mask\n"); + return FALSE; + } + if (options->compression_strategy < GD_PNG_COMPRESSION_STRATEGY_DEFAULT || + options->compression_strategy > GD_PNG_COMPRESSION_STRATEGY_FIXED) { + gd_error("gd-png error: invalid compression strategy\n"); + return FALSE; + } + return TRUE; } -void gdImagePng (gdImagePtr im, FILE * outFile) +BGD_DECLARE(int) +gdImagePngWithOptions(gdImagePtr im, FILE *outFile, const gdPngWriteOptions *options) { - gdIOCtx *out = gdNewFileCtx(outFile); - gdImagePngCtxEx(im, out, -1, -1); - out->gd_free(out); + gdIOCtx *out; + int status; + if (im == NULL || outFile == NULL) + return 1; + out = gdNewFileCtx(outFile); + if (out == NULL) + return 1; + status = gdImagePngCtxWithOptions(im, out, options); + out->gd_free(out); + return status; } -void * gdImagePngPtr (gdImagePtr im, int *size) +/* + Function: gdImagePngPtr + + Equivalent to calling with compression of -1. + + See for more information. + + Parameters: + + im - the image to save. + size - Output: size in bytes of the result. + + Returns: + + A pointer to memory containing the image data or NULL on error. + +*/ +BGD_DECLARE(void *) gdImagePngPtr(gdImagePtr im, int *size) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - gdImagePngCtxEx(im, out, -1, -1); - rv = gdDPExtractData(out, size); - out->gd_free(out); + return gdImagePngPtrEx(im, size, -1); +} + +/* + Function: gdImagePngPtrEx + + Identical to except that it returns a pointer to a + memory area with the PNG data. This memory must be freed by the + caller when it is no longer needed. **The caller must invoke + gdFree(), not free()** + + The 'size' parameter receives the total size of the block of + memory. + + See for more information. + + Parameters: + + im - the image to save. + size - Output: size in bytes of the result. + level - compression level: 0 -> none, 1-9 -> level, -1 -> default + + Returns: - return rv; + A pointer to memory containing the image data or NULL on error. + +*/ +BGD_DECLARE(void *) gdImagePngPtrEx(gdImagePtr im, int *size, int level) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + gdPngWriteOptions options; + if (out == NULL) + return NULL; + gdPngWriteOptionsInit(&options); + options.compression_level = level; + if (!_gdImagePngCtxWithOptions(im, out, &options)) + rv = gdDPExtractData(out, size); + else + rv = NULL; + out->gd_free(out); + return rv; } -void * gdImagePngPtrEx (gdImagePtr im, int *size, int level, int basefilter) +BGD_DECLARE(void *) +gdImagePngPtrWithOptions(gdImagePtr im, int *size, const gdPngWriteOptions *options) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - gdImagePngCtxEx(im, out, level, basefilter); - rv = gdDPExtractData(out, size); - out->gd_free(out); - return rv; + gdPngWriteOptions defaults; + gdIOCtx *out; + void *rv = NULL; + if (size != NULL) + *size = 0; + if (im == NULL || size == NULL) + return NULL; + if (options == NULL) { + gdPngWriteOptionsInit(&defaults); + options = &defaults; + } + if (!gdPngWriteOptionsValid(options)) + return NULL; + out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImagePngCtxWithOptions(im, out, options)) + rv = gdDPExtractData(out, size); + out->gd_free(out); + if (rv != NULL && options->metadata != NULL && + gdImageMetadataInjectPng(&rv, size, options->metadata) != GD_META_OK) { + gdFree(rv); + *size = 0; + return NULL; + } + return rv; } -void gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile) +BGD_DECLARE(void *) +gdImagePngPtrWithMetadata(gdImagePtr im, int *size, const gdImageMetadata *metadata) { - gdImagePngCtxEx(im, outfile, -1, -1); + return gdImagePngPtrExWithMetadata(im, size, -1, metadata); +} + +BGD_DECLARE(void *) +gdImagePngPtrExWithMetadata(gdImagePtr im, int *size, int level, const gdImageMetadata *metadata) +{ + void *rv = gdImagePngPtrEx(im, size, level); + if (rv == NULL) + return NULL; + if (gdImageMetadataInjectPng(&rv, size, metadata) != GD_META_OK) { + gdFree(rv); + if (size != NULL) + *size = 0; + return NULL; + } + return rv; +} + +BGD_DECLARE(int) +gdImageMetadataInjectPng(void **data, int *size, const gdImageMetadata *metadata) +{ + const unsigned char *png; + unsigned char *out; + const unsigned char *profile; + size_t profile_size; + size_t png_size; + size_t pos; + size_t out_pos; + size_t out_size; + size_t extra_size = 0; + size_t i; + int wrote_after_ihdr = FALSE; + + if (data == NULL || size == NULL || *data == NULL || *size < 0) { + return GD_META_ERR_INVALID; + } + if (metadata == NULL) { + return GD_META_OK; + } + + png = (const unsigned char *)*data; + png_size = (size_t)*size; + if (png_size < 8 || memcmp(png, gdPngSignature, 8) != 0) { + return GD_META_ERR_FORMAT; + } + + profile = gdImageMetadataGetProfile(metadata, "exif", &profile_size); + if (gdPngMetadataChunkSize(&extra_size, profile, profile_size) != GD_META_OK) { + return GD_META_ERR_LIMIT; + } + profile = gdImageMetadataGetProfile(metadata, "icc", &profile_size); + if (gdPngMetadataChunkSize(&extra_size, profile, profile_size) != GD_META_OK) { + return GD_META_ERR_LIMIT; + } + profile = gdImageMetadataGetProfile(metadata, "xmp", &profile_size); + if (gdPngMetadataChunkSize(&extra_size, profile, profile_size) != GD_META_OK) { + return GD_META_ERR_LIMIT; + } + for (i = 0; i < gdImageMetadataGetProfileCount(metadata); i++) { + const char *key = NULL; + const unsigned char *text_data = NULL; + size_t text_size = 0; + if (gdImageMetadataGetProfileAt(metadata, i, &key, &text_data, &text_size) == GD_META_OK && + key != NULL && strncmp(key, "png:text:", 9) == 0) { + if (gdPngMetadataChunkSize(&extra_size, text_data, text_size) != GD_META_OK) { + return GD_META_ERR_LIMIT; + } + } + } + + if ((size_t)-1 - png_size < extra_size || png_size + extra_size > INT_MAX) { + return GD_META_ERR_LIMIT; + } + out_size = png_size + extra_size; + out = (unsigned char *)gdMalloc(out_size); + if (out == NULL) { + return GD_META_ERR_NOMEM; + } + + memcpy(out, png, 8); + pos = 8; + out_pos = 8; + while (pos + 12 <= png_size) { + unsigned int chunk_size = gdPngGetUint32(png + pos); + const unsigned char *type = png + pos + 4; + const unsigned char *chunk_data = png + pos + 8; + size_t chunk_total; + + if ((size_t)chunk_size > png_size - pos - 12) { + gdFree(out); + return GD_META_ERR_PARSE; + } + chunk_total = (size_t)chunk_size + 12; + + if (!gdPngShouldSkipChunk(type, chunk_data, chunk_size, metadata)) { + memcpy(out + out_pos, png + pos, chunk_total); + out_pos += chunk_total; + } + pos += chunk_total; + + if (gdPngChunkIs(type, "IHDR") && !wrote_after_ihdr) { + int status; + profile = gdImageMetadataGetProfile(metadata, "exif", &profile_size); + if (profile != NULL && + (status = gdPngAppendChunk( + out, &out_pos, + gdPngIsRawProfile(profile, profile_size, "exif") ? "zTXt" : "eXIf", profile, + profile_size)) != GD_META_OK) { + gdFree(out); + return status; + } + profile = gdImageMetadataGetProfile(metadata, "icc", &profile_size); + if (profile != NULL && (status = gdPngAppendChunk(out, &out_pos, "iCCP", profile, + profile_size)) != GD_META_OK) { + gdFree(out); + return status; + } + profile = gdImageMetadataGetProfile(metadata, "xmp", &profile_size); + if (profile != NULL && + (status = gdPngAppendChunk(out, &out_pos, + gdPngIsRawProfile(profile, profile_size, "xmp") ? "zTXt" + : "iTXt", + profile, profile_size)) != GD_META_OK) { + gdFree(out); + return status; + } + for (i = 0; i < gdImageMetadataGetProfileCount(metadata); i++) { + const char *key = NULL; + const unsigned char *text_data = NULL; + size_t text_size = 0; + if (gdImageMetadataGetProfileAt(metadata, i, &key, &text_data, &text_size) == + GD_META_OK && + key != NULL && strncmp(key, "png:text:", 9) == 0) { + status = gdPngAppendChunk(out, &out_pos, "tEXt", text_data, text_size); + if (status != GD_META_OK) { + gdFree(out); + return status; + } + } + } + wrote_after_ihdr = TRUE; + } + + if (gdPngChunkIs(type, "IEND")) { + gdFree(*data); + *data = out; + *size = (int)out_pos; + return GD_META_OK; + } + } + + gdFree(out); + return GD_META_ERR_PARSE; +} + +/* + Function: gdImagePngCtx + + Equivalent to calling with compression of -1. + See for more information. + + Parameters: + + im - the image to save. + outfile - the to write to. + + Returns: + + Nothing. + +*/ +BGD_DECLARE(void) gdImagePngCtx(gdImagePtr im, gdIOCtx *outfile) +{ + /* 2.0.13: 'return' here was an error, thanks to Kevin Smith */ + gdImagePngCtxEx(im, outfile, -1); +} + +BGD_DECLARE(void) +gdImagePngCtxWithMetadata(gdImagePtr im, gdIOCtx *outfile, const gdImageMetadata *metadata) +{ + gdImagePngCtxExWithMetadata(im, outfile, -1, metadata); +} + +/* + Function: gdImagePngCtxEx + + Outputs the given image as PNG data, but using a instead + of a file. See . + + Parameters: + + im - the image to save. + outfile - the to write to. + level - compression level: 0 -> none, 1-9 -> level, -1 -> default + + Returns: + + Nothing. + +*/ +BGD_DECLARE(void) gdImagePngCtxEx(gdImagePtr im, gdIOCtx *outfile, int level) +{ + gdPngWriteOptions options; + gdPngWriteOptionsInit(&options); + options.compression_level = level; + (void)_gdImagePngCtxWithOptions(im, outfile, &options); +} + +BGD_DECLARE(void) +gdImagePngCtxExWithMetadata(gdImagePtr im, gdIOCtx *outfile, int level, + const gdImageMetadata *metadata) +{ + void *data; + int size = 0; + data = gdImagePngPtrExWithMetadata(im, &size, level, metadata); + if (data == NULL) + return; + gdPutBuf(data, size, outfile); + gdFree(data); +} + +BGD_DECLARE(int) +gdImagePngCtxWithOptions(gdImagePtr im, gdIOCtx *outfile, const gdPngWriteOptions *options) +{ + gdPngWriteOptions defaults; + void *data; + int size = 0; + + if (im == NULL || outfile == NULL) + return 1; + if (options == NULL) { + gdPngWriteOptionsInit(&defaults); + options = &defaults; + } + if (!gdPngWriteOptionsValid(options)) + return 1; + if (options->metadata == NULL) + return _gdImagePngCtxWithOptions(im, outfile, options); + data = gdImagePngPtrWithOptions(im, &size, options); + if (data == NULL) + return 1; + if (gdPutBuf(data, size, outfile) != size) { + gdFree(data); + return 1; + } + gdFree(data); + return 0; } /* This routine is based in part on code from Dale Lutz (Safe Software Inc.) * and in part on demo code from Chapter 15 of "PNG: The Definitive Guide" - * (http://www.cdrom.com/pub/png/pngbook.html). + * (http://www.libpng.org/pub/png/book/). */ -void gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level, int basefilter) -{ - int i, j, bit_depth = 0, interlace_type; - int width = im->sx; - int height = im->sy; - int colors = im->colorsTotal; - int *open = im->open; - int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */ - png_byte trans_values[256]; - png_color_16 trans_rgb_value; - png_color palette[gdMaxColors]; - png_structp png_ptr; - png_infop info_ptr; - volatile int transparent = im->transparent; - volatile int remap = FALSE; +/* returns 0 on success, 1 on failure */ +static int _gdImagePngCtxWithOptions(gdImagePtr im, gdIOCtx *outfile, + const gdPngWriteOptions *options) +{ + int i, j, bit_depth = 0, interlace_type; + int width = im->sx; + int height = im->sy; + int colors = im->colorsTotal; + int *open = im->open; + int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */ + png_byte trans_values[256]; + png_color_16 trans_rgb_value; + png_color palette[gdMaxColors]; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers = NULL; + volatile int transparent = im->transparent; + volatile int remap = FALSE; #ifdef PNG_SETJMP_SUPPORTED - jmpbuf_wrapper jbw; + jmpbuf_wrapper jbw; +#endif + int ret = 0; + + /* width or height of value 0 is invalid in IHDR; + see http://www.w3.org/TR/PNG-Chunks.html */ + if (width == 0 || height == 0) + return 1; - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, gdPngWarningHandler); +#ifdef PNG_SETJMP_SUPPORTED + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &jbw, gdPngErrorHandler, + gdPngWarningHandler); #else - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif - if (png_ptr == NULL) { - gd_error("gd-png error: cannot allocate libpng main struct"); - return; - } - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - gd_error("gd-png error: cannot allocate libpng info struct"); - png_destroy_write_struct (&png_ptr, (png_infopp) NULL); + if (png_ptr == NULL) { + gd_error("gd-png error: cannot allocate libpng main struct\n"); + return 1; + } - return; + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + gd_error("gd-png error: cannot allocate libpng info struct\n"); + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return 1; } #ifdef PNG_SETJMP_SUPPORTED - if (setjmp(jbw.jmpbuf)) { - gd_error("gd-png error: setjmp returns error condition"); - png_destroy_write_struct (&png_ptr, &info_ptr); - - return; - } -#endif + if (setjmp(jbw.jmpbuf)) { + gd_error("gd-png error: setjmp returns error condition\n"); + png_destroy_write_struct(&png_ptr, &info_ptr); - png_set_write_fn(png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData); + if (row_pointers) { + for (i = 0; i < height; ++i) + gdFree(row_pointers[i]); + gdFree(row_pointers); + } - /* This is best for palette images, and libpng defaults to it for - * palette images anyway, so we don't need to do it explicitly. - * What to ideally do for truecolor images depends, alas, on the image. - * gd is intentionally imperfect and doesn't spend a lot of time - * fussing with such things. - */ - - /* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */ + return 1; + } +#endif - /* 2.0.12: this is finally a parameter */ - if (level != -1 && (level < 0 || level > 9)) { - gd_error("gd-png error: compression level must be 0 through 9"); - return; - } - png_set_compression_level(png_ptr, level); - if (basefilter >= 0) { - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, basefilter); - } + png_set_write_fn(png_ptr, (void *)outfile, gdPngWriteData, gdPngFlushData); + + png_set_user_limits(png_ptr, 0x7fffffffL, 0x7fffffffL); + + /* This is best for palette images, and libpng defaults to it for + palette images anyway, so we don't need to do it explicitly. + What to ideally do for truecolor images depends, alas, on the image. + gd is intentionally imperfect and doesn't spend a lot of time + fussing with such things. */ + + /* 2.0.12: this is finally a parameter */ + png_set_compression_level(png_ptr, options->compression_level); + if (options->filters != GD_PNG_FILTER_AUTO) { + int filters = 0; + if (options->filters & GD_PNG_FILTER_NONE) + filters |= PNG_FILTER_NONE; + if (options->filters & GD_PNG_FILTER_SUB) + filters |= PNG_FILTER_SUB; + if (options->filters & GD_PNG_FILTER_UP) + filters |= PNG_FILTER_UP; + if (options->filters & GD_PNG_FILTER_AVERAGE) + filters |= PNG_FILTER_AVG; + if (options->filters & GD_PNG_FILTER_PAETH) + filters |= PNG_FILTER_PAETH; + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters); + } + if (options->compression_strategy != GD_PNG_COMPRESSION_STRATEGY_DEFAULT) { + int strategy = Z_DEFAULT_STRATEGY; + switch (options->compression_strategy) { + case GD_PNG_COMPRESSION_STRATEGY_FILTERED: + strategy = Z_FILTERED; + break; + case GD_PNG_COMPRESSION_STRATEGY_HUFFMAN_ONLY: + strategy = Z_HUFFMAN_ONLY; + break; + case GD_PNG_COMPRESSION_STRATEGY_RLE: + strategy = Z_RLE; + break; + case GD_PNG_COMPRESSION_STRATEGY_FIXED: + strategy = Z_FIXED; + break; + } + png_set_compression_strategy(png_ptr, strategy); + } #ifdef PNG_pHYs_SUPPORTED - /* 2.1.0: specify the resolution */ - png_set_pHYs(png_ptr, info_ptr, DPI2DPM(im->res_x), DPI2DPM(im->res_y), - PNG_RESOLUTION_METER); + /* 2.1.0: specify the resolution */ + png_set_pHYs(png_ptr, info_ptr, DPI2DPM(im->res_x), DPI2DPM(im->res_y), PNG_RESOLUTION_METER); #endif - /* can set this to a smaller value without compromising compression if all - * image data is 16K or less; will save some decoder memory [min == 8] - */ - - /* png_set_compression_window_bits(png_ptr, 15); */ + /* can set this to a smaller value without compromising compression if all + * image data is 16K or less; will save some decoder memory [min == 8] */ + /* png_set_compression_window_bits(png_ptr, 15); */ - if (!im->trueColor) { - if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) { - transparent = -1; - } - - for (i = 0; i < gdMaxColors; ++i) { - mapping[i] = -1; - } - - /* count actual number of colors used (colorsTotal == high-water mark) */ - colors = 0; - for (i = 0; i < im->colorsTotal; ++i) { - if (!open[i]) { - mapping[i] = colors; - ++colors; - } - } - if (colors == 0) { - gd_error("gd-png error: no colors in palette"); - goto bail; - } - if (colors < im->colorsTotal) { - remap = TRUE; - } - if (colors <= 2) { - bit_depth = 1; - } else if (colors <= 4) { - bit_depth = 2; - } else if (colors <= 16) { - bit_depth = 4; - } else { - bit_depth = 8; - } - } - - interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; - - if (im->trueColor) { - if (im->saveAlphaFlag) { - png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, interlace_type, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - } else { - png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - } - } else { - png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - } - - if (im->trueColor && !im->saveAlphaFlag && (transparent >= 0)) { - /* 2.0.9: fixed by Thomas Winzig */ - trans_rgb_value.red = gdTrueColorGetRed (im->transparent); - trans_rgb_value.green = gdTrueColorGetGreen (im->transparent); - trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent); - png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value); - } - - if (!im->trueColor) { - /* Oy veh. Remap the PNG palette to put the entries with interesting alpha channel - * values first. This minimizes the size of the tRNS chunk and thus the size - * of the PNG file as a whole. - */ - - int tc = 0; - int i; - int j; - int k; - - for (i = 0; (i < im->colorsTotal); i++) { - if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) { - tc++; - } - } - if (tc) { + if (!im->trueColor) { + if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) + transparent = -1; + } + if (!im->trueColor) { + for (i = 0; i < gdMaxColors; ++i) + mapping[i] = -1; + } + if (!im->trueColor) { + /* count actual number of colors used (colorsTotal == high-water mark) + */ + colors = 0; + for (i = 0; i < im->colorsTotal; ++i) { + if (!open[i]) { + mapping[i] = colors; + ++colors; + } + } + if (colors == 0) { + gd_error("gd-png error: no colors in palette\n"); + ret = 1; + goto bail; + } + if (colors < im->colorsTotal) { + remap = TRUE; + } + if (colors <= 2) + bit_depth = 1; + else if (colors <= 4) + bit_depth = 2; + else if (colors <= 16) + bit_depth = 4; + else + bit_depth = 8; + } + interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; + + if (im->trueColor) { + if (im->saveAlphaFlag) { + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, + interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + } else { + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + } + } else { + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, + interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + } + if (im->trueColor && (!im->saveAlphaFlag) && (transparent >= 0)) { + /* 2.0.9: fixed by Thomas Winzig */ + trans_rgb_value.red = gdTrueColorGetRed(im->transparent); + trans_rgb_value.green = gdTrueColorGetGreen(im->transparent); + trans_rgb_value.blue = gdTrueColorGetBlue(im->transparent); + png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value); + } + if (!im->trueColor) { + /* Oy veh. Remap the PNG palette to put the + entries with interesting alpha channel + values first. This minimizes the size + of the tRNS chunk and thus the size + of the PNG file as a whole. */ + int tc = 0; + int i; + int j; + int k; + for (i = 0; (i < im->colorsTotal); i++) { + if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) { + tc++; + } + } + if (tc) { #if 0 for (i = 0; (i < im->colorsTotal); i++) { - trans_values[i] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6)); + trans_values[i] = 255 - + ((im->alpha[i] << 1) + (im->alpha[i] >> 6)); } png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL); #endif - if (!remap) { - remap = TRUE; - } + if (!remap) { + remap = TRUE; + } + /* (Semi-)transparent indexes come up from the bottom + of the list of real colors; opaque + indexes come down from the top */ + j = 0; + k = colors - 1; + for (i = 0; (i < im->colorsTotal); i++) { + if (!im->open[i]) { + if (im->alpha[i] != gdAlphaOpaque) { + /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */ + trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6)); + mapping[i] = j++; + } else { + mapping[i] = k--; + } + } + } + png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL); + } + } - /* (Semi-)transparent indexes come up from the bottom of the list of real colors; opaque - * indexes come down from the top - */ - j = 0; - k = colors - 1; - - for (i = 0; i < im->colorsTotal; i++) { - if (!im->open[i]) { - if (im->alpha[i] != gdAlphaOpaque) { - /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */ - trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6)); - mapping[i] = j++; - } else { - mapping[i] = k--; - } - } - } - png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL); - } - } - - /* convert palette to libpng layout */ - if (!im->trueColor) { - if (remap) { - for (i = 0; i < im->colorsTotal; ++i) { - if (mapping[i] < 0) { - continue; - } - - palette[mapping[i]].red = im->red[i]; - palette[mapping[i]].green = im->green[i]; - palette[mapping[i]].blue = im->blue[i]; - } - } else { - for (i = 0; i < colors; ++i) { - palette[i].red = im->red[i]; - palette[i].green = im->green[i]; - palette[i].blue = im->blue[i]; - } - } - png_set_PLTE(png_ptr, info_ptr, palette, colors); - } - - /* write out the PNG header info (everything up to first IDAT) */ - png_write_info(png_ptr, info_ptr); - - /* make sure < 8-bit images are packed into pixels as tightly as possible */ - png_set_packing(png_ptr); - - /* This code allocates a set of row buffers and copies the gd image data - * into them only in the case that remapping is necessary; in gd 1.3 and - * later, the im->pixels array is laid out identically to libpng's row - * pointers and can be passed to png_write_image() function directly. - * The remapping case could be accomplished with less memory for non- - * interlaced images, but interlacing causes some serious complications. - */ - - if (im->trueColor) { - /* performance optimizations by Phong Tran */ - int channels = im->saveAlphaFlag ? 4 : 3; - /* Our little 7-bit alpha channel trick costs us a bit here. */ - png_bytep *row_pointers; - unsigned char* pOutputRow; - int **ptpixels = im->tpixels; - int *pThisRow; - unsigned char a; - int thisPixel; - png_bytep *prow_pointers; - int saveAlphaFlag = im->saveAlphaFlag; - - row_pointers = safe_emalloc(sizeof(png_bytep), height, 0); - prow_pointers = row_pointers; - for (j = 0; j < height; ++j) { - *prow_pointers = (png_bytep) safe_emalloc(width, channels, 0); - pOutputRow = *prow_pointers++; - pThisRow = *ptpixels++; - for (i = 0; i < width; ++i) { - thisPixel = *pThisRow++; - *pOutputRow++ = gdTrueColorGetRed(thisPixel); - *pOutputRow++ = gdTrueColorGetGreen(thisPixel); - *pOutputRow++ = gdTrueColorGetBlue(thisPixel); - if (saveAlphaFlag) { - /* convert the 7-bit alpha channel to an 8-bit alpha channel. - * We do a little bit-flipping magic, repeating the MSB - * as the LSB, to ensure that 0 maps to 0 and - * 127 maps to 255. We also have to invert to match - * PNG's convention in which 255 is opaque. - */ - a = gdTrueColorGetAlpha(thisPixel); - /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */ - *pOutputRow++ = 255 - ((a << 1) + (a >> 6)); - } - } - } - - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); - - for (j = 0; j < height; ++j) { - gdFree(row_pointers[j]); - } - - gdFree(row_pointers); - } else { - if (remap) { - png_bytep *row_pointers; - row_pointers = safe_emalloc(height, sizeof(png_bytep), 0); - for (j = 0; j < height; ++j) { - row_pointers[j] = (png_bytep) gdMalloc(width); - for (i = 0; i < width; ++i) { - row_pointers[j][i] = mapping[im->pixels[j][i]]; - } - } + /* convert palette to libpng layout */ + if (!im->trueColor) { + if (remap) + for (i = 0; i < im->colorsTotal; ++i) { + if (mapping[i] < 0) + continue; + palette[mapping[i]].red = im->red[i]; + palette[mapping[i]].green = im->green[i]; + palette[mapping[i]].blue = im->blue[i]; + } + else + for (i = 0; i < colors; ++i) { + palette[i].red = im->red[i]; + palette[i].green = im->green[i]; + palette[i].blue = im->blue[i]; + } + png_set_PLTE(png_ptr, info_ptr, palette, colors); + } + + /* write out the PNG header info (everything up to first IDAT) */ + png_write_info(png_ptr, info_ptr); + + /* make sure < 8-bit images are packed into pixels as tightly as possible */ + png_set_packing(png_ptr); + + /* This code allocates a set of row buffers and copies the gd image data + * into them only in the case that remapping is necessary; in gd 1.3 and + * later, the im->pixels array is laid out identically to libpng's row + * pointers and can be passed to png_write_image() function directly. + * The remapping case could be accomplished with less memory for non- + * interlaced images, but interlacing causes some serious complications. */ + if (im->trueColor) { + /* performance optimizations by Phong Tran */ + int channels = im->saveAlphaFlag ? 4 : 3; + /* Our little 7-bit alpha channel trick costs us a bit here. */ + unsigned char *pOutputRow; + int **ptpixels = im->tpixels; + int *pThisRow; + unsigned char a; + int thisPixel; + png_bytep *prow_pointers; + int saveAlphaFlag = im->saveAlphaFlag; + if (overflow2(sizeof(png_bytep), height)) { + ret = 1; + goto bail; + } + /* Need to use calloc so we can clean it up sanely in the error handler. + */ + row_pointers = gdCalloc(height, sizeof(png_bytep)); + if (row_pointers == NULL) { + gd_error("gd-png error: unable to allocate row_pointers\n"); + ret = 1; + goto bail; + } + prow_pointers = row_pointers; + for (j = 0; j < height; ++j) { + if (overflow2(width, channels) || + ((*prow_pointers = (png_bytep)gdMalloc(width * channels)) == NULL)) { + gd_error("gd-png error: unable to allocate rows\n"); + for (i = 0; i < j; ++i) + gdFree(row_pointers[i]); + /* 2.0.29: memory leak TBB */ + gdFree(row_pointers); + ret = 1; + goto bail; + } + pOutputRow = *prow_pointers++; + pThisRow = *ptpixels++; + for (i = 0; i < width; ++i) { + thisPixel = *pThisRow++; + *pOutputRow++ = gdTrueColorGetRed(thisPixel); + *pOutputRow++ = gdTrueColorGetGreen(thisPixel); + *pOutputRow++ = gdTrueColorGetBlue(thisPixel); + + if (saveAlphaFlag) { + /* convert the 7-bit alpha channel to an 8-bit alpha + channel. We do a little bit-flipping magic, repeating the + MSB as the LSB, to ensure that 0 maps to 0 and 127 maps + to 255. We also have to invert to match PNG's convention + in which 255 is opaque. */ + a = gdTrueColorGetAlpha(thisPixel); + /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */ + *pOutputRow++ = 255 - ((a << 1) + (a >> 6)); + } + } + } + + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + + for (j = 0; j < height; ++j) + gdFree(row_pointers[j]); + gdFree(row_pointers); + } else { + if (remap) { + png_bytep *row_pointers; + if (overflow2(sizeof(png_bytep), height)) { + ret = 1; + goto bail; + } + row_pointers = gdMalloc(sizeof(png_bytep) * height); + if (row_pointers == NULL) { + gd_error("gd-png error: unable to allocate row_pointers\n"); + ret = 1; + goto bail; + } + for (j = 0; j < height; ++j) { + if ((row_pointers[j] = (png_bytep)gdMalloc(width)) == NULL) { + gd_error("gd-png error: unable to allocate rows\n"); + for (i = 0; i < j; ++i) + gdFree(row_pointers[i]); + /* TBB: memory leak */ + gdFree(row_pointers); + ret = 1; + goto bail; + } + for (i = 0; i < width; ++i) + row_pointers[j][i] = mapping[im->pixels[j][i]]; + } + + png_write_image(png_ptr, row_pointers); + + for (j = 0; j < height; ++j) + gdFree(row_pointers[j]); + gdFree(row_pointers); + + png_write_end(png_ptr, info_ptr); + } else { + png_write_image(png_ptr, im->pixels); + png_write_end(png_ptr, info_ptr); + } + } + /* 1.6.3: maybe we should give that memory BACK! TBB */ +bail: + png_destroy_write_struct(&png_ptr, &info_ptr); + return ret; +} - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); +#else /* !HAVE_LIBPNG */ - for (j = 0; j < height; ++j) { - gdFree(row_pointers[j]); - } +static void _noPngError(void) { gd_error("PNG image support has been disabled\n"); } + +BGD_DECLARE(void) gdPngWriteOptionsInit(gdPngWriteOptions *options) +{ + if (options == NULL) + return; + memset(options, 0, sizeof(*options)); + options->struct_size = sizeof(*options); + options->compression_level = -1; +} + +BGD_DECLARE(int) +gdImagePngWithOptions(gdImagePtr im, FILE *outFile, const gdPngWriteOptions *options) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + ARG_NOT_USED(options); + _noPngError(); + return 1; +} - gdFree(row_pointers); - } else { - png_write_image(png_ptr, im->pixels); - png_write_end(png_ptr, info_ptr); - } - } - /* 1.6.3: maybe we should give that memory BACK! TBB */ - bail: - png_destroy_write_struct(&png_ptr, &info_ptr); +BGD_DECLARE(int) +gdImagePngCtxWithOptions(gdImagePtr im, gdIOCtx *outfile, const gdPngWriteOptions *options) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(options); + _noPngError(); + return 1; +} + +BGD_DECLARE(void *) +gdImagePngPtrWithOptions(gdImagePtr im, int *size, const gdPngWriteOptions *options) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(options); + if (size != NULL) + *size = 0; + _noPngError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromPng(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noPngError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngPtrWithMetadata(int size, void *data, gdImageMetadata *metadata) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + ARG_NOT_USED(metadata); + return NULL; +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromPngCtxWithMetadata(gdIOCtx *infile, gdImageMetadata *metadata) +{ + ARG_NOT_USED(infile); + ARG_NOT_USED(metadata); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngCtx(gdIOCtx *infile) +{ + ARG_NOT_USED(infile); + return NULL; +} + +BGD_DECLARE(void) gdImagePngEx(gdImagePtr im, FILE *outFile, int level) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + ARG_NOT_USED(level); + _noPngError(); +} + +BGD_DECLARE(void) gdImagePng(gdImagePtr im, FILE *outFile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + _noPngError(); +} + +BGD_DECLARE(void *) gdImagePngPtr(gdImagePtr im, int *size) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + return NULL; +} + +BGD_DECLARE(void *) gdImagePngPtrEx(gdImagePtr im, int *size, int level) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(level); + return NULL; +} + +BGD_DECLARE(void *) +gdImagePngPtrWithMetadata(gdImagePtr im, int *size, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(metadata); + return NULL; +} + +BGD_DECLARE(void *) +gdImagePngPtrExWithMetadata(gdImagePtr im, int *size, int level, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(level); + ARG_NOT_USED(metadata); + return NULL; +} + +BGD_DECLARE(int) +gdImageMetadataInjectPng(void **data, int *size, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(data); + ARG_NOT_USED(size); + ARG_NOT_USED(metadata); + return GD_META_ERR_UNSUPPORTED; +} + +BGD_DECLARE(void) gdImagePngCtx(gdImagePtr im, gdIOCtx *outfile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + _noPngError(); +} + +BGD_DECLARE(void) +gdImagePngCtxWithMetadata(gdImagePtr im, gdIOCtx *outfile, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(metadata); + _noPngError(); +} + +BGD_DECLARE(void) gdImagePngCtxEx(gdImagePtr im, gdIOCtx *outfile, int level) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(level); + _noPngError(); +} + +BGD_DECLARE(void) +gdImagePngCtxExWithMetadata(gdImagePtr im, gdIOCtx *outfile, int level, + const gdImageMetadata *metadata) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(level); + ARG_NOT_USED(metadata); + _noPngError(); } #endif /* HAVE_LIBPNG */ diff --git a/ext/gd/libgd/gd_qoi.c b/ext/gd/libgd/gd_qoi.c new file mode 100644 index 000000000000..e3a155bb1c2d --- /dev/null +++ b/ext/gd/libgd/gd_qoi.c @@ -0,0 +1,372 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "gd.h" +#include "gd_errors.h" +#include "gdhelpers.h" + +#define QOI_IMPLEMENTATION +#include "gd_qoi.h" + +#define GD_QOI_ALLOC_STEP 8192 + +static unsigned char alpha7BitTo8Bit(int alpha7Bit) +{ + return (unsigned char)(alpha7Bit == gdAlphaTransparent + ? 0 + : 255 - ((alpha7Bit << 1) + (alpha7Bit >> 6))); +} + +static int alpha8BitTo7Bit(unsigned char alpha8Bit) { return gdAlphaMax - (alpha8Bit >> 1); } + +static void *gdQoiReadCtxToMemory(gdIOCtx *infile, int *size) +{ + unsigned char *data = NULL; + int logical_size = 0; + int real_size = 0; + + if (size != NULL) { + *size = 0; + } + if (infile == NULL || size == NULL) { + return NULL; + } + + for (;;) { + int n; + + if (real_size - logical_size < GD_QOI_ALLOC_STEP) { + unsigned char *temp; + int new_size; + + if (real_size > INT_MAX - GD_QOI_ALLOC_STEP) { + gdFree(data); + return NULL; + } + new_size = real_size + GD_QOI_ALLOC_STEP; + temp = (unsigned char *)gdRealloc(data, new_size); + if (temp == NULL) { + gdFree(data); + return NULL; + } + data = temp; + real_size = new_size; + } + + n = gdGetBuf(data + logical_size, GD_QOI_ALLOC_STEP, infile); + if (n <= 0) { + break; + } + logical_size += n; + } + + *size = logical_size; + return data; +} + +static gdImagePtr gdImageCreateFromQoiBytes(const void *data, int size) +{ + qoi_desc desc; + unsigned char *pixels; + gdImagePtr im = NULL; + unsigned int x, y; + unsigned char *p; + + if (data == NULL || size <= 0) { + return NULL; + } + + memset(&desc, 0, sizeof(desc)); + pixels = (unsigned char *)gdQoiDecode(data, size, &desc, 4); + if (pixels == NULL) { + return NULL; + } + + if (desc.width == 0 || desc.height == 0 || desc.width > INT_MAX || desc.height > INT_MAX) { + gdFree(pixels); + return NULL; + } + + im = gdImageCreateTrueColor((int)desc.width, (int)desc.height); + if (im == NULL) { + gdFree(pixels); + return NULL; + } + im->saveAlphaFlag = 1; + im->alphaBlendingFlag = 0; + + p = pixels; + for (y = 0; y < desc.height; y++) { + for (x = 0; x < desc.width; x++) { + unsigned char r = *(p++); + unsigned char g = *(p++); + unsigned char b = *(p++); + unsigned char a = *(p++); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, alpha8BitTo7Bit(a)); + } + } + + gdFree(pixels); + return im; +} + +static unsigned char *gdImageToQoiPixels(gdImagePtr im) +{ + unsigned char *pixels, *p; + int x, y; + + if (im == NULL || gdImageSX(im) <= 0 || gdImageSY(im) <= 0) { + return NULL; + } + if (overflow2(gdImageSX(im), gdImageSY(im)) || overflow2(gdImageSX(im) * gdImageSY(im), 4)) { + return NULL; + } + + pixels = (unsigned char *)gdMalloc((size_t)gdImageSX(im) * gdImageSY(im) * 4); + if (pixels == NULL) { + return NULL; + } + + p = pixels; + if (im->trueColor) { + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + int c = im->tpixels[y][x]; + *(p++) = gdTrueColorGetRed(c); + *(p++) = gdTrueColorGetGreen(c); + *(p++) = gdTrueColorGetBlue(c); + *(p++) = alpha7BitTo8Bit(gdTrueColorGetAlpha(c)); + } + } + } else { + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + int c = im->pixels[y][x]; + *(p++) = (unsigned char)im->red[c]; + *(p++) = (unsigned char)im->green[c]; + *(p++) = (unsigned char)im->blue[c]; + *(p++) = alpha7BitTo8Bit(im->alpha[c]); + } + } + } + + return pixels; +} + +static int gdQoiNormalizeColorspace(int colorspace) +{ + return colorspace == GD_QOI_LINEAR ? QOI_LINEAR : QOI_SRGB; +} + +static int _gdImageQoiCtx(gdImagePtr im, gdIOCtx *outfile, int colorspace) +{ + qoi_desc desc; + unsigned char *pixels; + void *encoded; + int encoded_size = 0; + int result = 0; + + if (outfile == NULL) { + return 0; + } + + pixels = gdImageToQoiPixels(im); + if (pixels == NULL) { + return 0; + } + + desc.width = (unsigned int)gdImageSX(im); + desc.height = (unsigned int)gdImageSY(im); + desc.channels = 4; + desc.colorspace = (unsigned char)gdQoiNormalizeColorspace(colorspace); + + encoded = gdQoiEncode(pixels, &desc, &encoded_size); + gdFree(pixels); + if (encoded == NULL || encoded_size <= 0) { + gdFree(encoded); + return 0; + } + + if (gdPutBuf(encoded, encoded_size, outfile) == encoded_size) { + result = 1; + } else { + gd_error("gd-qoi write error\n"); + } + + gdFree(encoded); + return result; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoi(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) { + return NULL; + } + im = gdImageCreateFromQoiCtx(in); + in->gd_free(in); + return im; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiPtr(int size, void *data) +{ + return gdImageCreateFromQoiPtrWithMetadata(size, data, NULL); +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromQoiPtrWithMetadata(int size, void *data, gdImageMetadata *metadata) +{ + ARG_NOT_USED(metadata); + return gdImageCreateFromQoiBytes(data, size); +} + +BGD_DECLARE(gdImagePtr) +gdImageCreateFromQoiCtxWithMetadata(gdIOCtx *infile, gdImageMetadata *metadata) +{ + void *data; + int size = 0; + gdImagePtr im; + + ARG_NOT_USED(metadata); + data = gdQoiReadCtxToMemory(infile, &size); + if (data == NULL) { + return NULL; + } + im = gdImageCreateFromQoiBytes(data, size); + gdFree(data); + return im; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiCtx(gdIOCtx *infile) +{ + return gdImageCreateFromQoiCtxWithMetadata(infile, NULL); +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromQoiSource(gdSourcePtr inSource) +{ + gdImagePtr im; + gdIOCtx *in; + if (inSource == NULL) { + return NULL; + } + in = gdNewSSCtx(inSource, NULL); + if (in == NULL) { + return NULL; + } + im = gdImageCreateFromQoiCtx(in); + in->gd_free(in); + return im; +} + +BGD_DECLARE(void) gdImageQoiEx(gdImagePtr im, FILE *outFile, int colorspace) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + gdImageQoiCtxEx(im, out, colorspace); + out->gd_free(out); +} + +BGD_DECLARE(void) gdImageQoi(gdImagePtr im, FILE *outFile) +{ + gdImageQoiEx(im, outFile, GD_QOI_SRGB); +} + +BGD_DECLARE(void *) gdImageQoiPtr(gdImagePtr im, int *size) +{ + return gdImageQoiPtrEx(im, size, GD_QOI_SRGB); +} + +BGD_DECLARE(void *) gdImageQoiPtrEx(gdImagePtr im, int *size, int colorspace) +{ + gdIOCtx *out; + void *rv = NULL; + + if (size != NULL) { + *size = 0; + } + if (size == NULL) { + return NULL; + } + + out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageQoiCtx(im, out, colorspace)) { + rv = gdDPExtractData(out, size); + } + out->gd_free(out); + return rv; +} + +BGD_DECLARE(void *) +gdImageQoiPtrWithMetadata(gdImagePtr im, int *size, const gdImageMetadata *metadata) +{ + return gdImageQoiPtrExWithMetadata(im, size, GD_QOI_SRGB, metadata); +} + +BGD_DECLARE(void *) +gdImageQoiPtrExWithMetadata(gdImagePtr im, int *size, int colorspace, + const gdImageMetadata *metadata) +{ + ARG_NOT_USED(metadata); + return gdImageQoiPtrEx(im, size, colorspace); +} + +BGD_DECLARE(int) +gdImageMetadataInjectQoi(void **data, int *size, const gdImageMetadata *metadata) +{ + ARG_NOT_USED(data); + ARG_NOT_USED(size); + ARG_NOT_USED(metadata); + return GD_META_OK; +} + +BGD_DECLARE(void) gdImageQoiCtx(gdImagePtr im, gdIOCtx *outfile) +{ + gdImageQoiCtxEx(im, outfile, GD_QOI_SRGB); +} + +BGD_DECLARE(void) +gdImageQoiCtxWithMetadata(gdImagePtr im, gdIOCtx *outfile, const gdImageMetadata *metadata) +{ + gdImageQoiCtxExWithMetadata(im, outfile, GD_QOI_SRGB, metadata); +} + +BGD_DECLARE(void) +gdImageQoiCtxEx(gdImagePtr im, gdIOCtx *outfile, int colorspace) +{ + _gdImageQoiCtx(im, outfile, colorspace); +} + +BGD_DECLARE(void) +gdImageQoiCtxExWithMetadata(gdImagePtr im, gdIOCtx *outfile, int colorspace, + const gdImageMetadata *metadata) +{ + ARG_NOT_USED(metadata); + _gdImageQoiCtx(im, outfile, colorspace); +} + +BGD_DECLARE(void) gdImageQoiToSink(gdImagePtr im, gdSinkPtr outSink) +{ + gdIOCtx *out; + if (outSink == NULL) { + return; + } + out = gdNewSSCtx(NULL, outSink); + if (out == NULL) { + return; + } + gdImageQoiCtx(im, out); + out->gd_free(out); +} diff --git a/ext/gd/libgd/gd_qoi.h b/ext/gd/libgd/gd_qoi.h new file mode 100644 index 000000000000..7cb2d4360a97 --- /dev/null +++ b/ext/gd/libgd/gd_qoi.h @@ -0,0 +1,629 @@ +/* + +Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org +SPDX-License-Identifier: MIT + +This copy is vendored for libgd's QOI codec. The upstream MIT license is +also included in docs/QOI-LICENSE. + + +QOI - The "Quite OK Image" format for fast, lossless image compression + +-- About + +QOI encodes and decodes images in a lossless format. Compared to stb_image and +stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and +20% better compression. + + +-- Synopsis + +// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this +// library to create the implementation. + +#define QOI_IMPLEMENTATION +#include "qoi.h" + +// Encode and store an RGBA buffer to the file system. The qoi_desc describes +// the input pixel data. +qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ + .width = 1920, + .height = 1080, + .channels = 4, + .colorspace = QOI_SRGB +}); + +// Load and decode a QOI image from the file system into a 32bbp RGBA buffer. +// The qoi_desc struct will be filled with the width, height, number of channels +// and colorspace read from the file header. +qoi_desc desc; +void *rgba_pixels = qoi_read("image.qoi", &desc, 4); + + + +-- Documentation + +This library provides the following functions; +- qoi_read -- read and decode a QOI file +- qoi_decode -- decode the raw bytes of a QOI image from memory +- qoi_write -- encode and write a QOI file +- qoi_encode -- encode an rgba buffer into a QOI image in memory + +See the function declaration below for the signature and more information. + +If you don't want/need the qoi_read and qoi_write functions, you can define +QOI_NO_STDIO before including this library. + +This library uses malloc() and free(). To supply your own malloc implementation +you can define QOI_MALLOC and QOI_FREE before including this library. + +This library uses memset() to zero-initialize the index. To supply your own +implementation you can define QOI_ZEROARR before including this library. + + +-- Data Format + +A QOI file has a 14 byte header, followed by any number of data "chunks" and an +8-byte end marker. + +struct qoi_header_t { + char magic[4]; // magic bytes "qoif" + uint32_t width; // image width in pixels (BE) + uint32_t height; // image height in pixels (BE) + uint8_t channels; // 3 = RGB, 4 = RGBA + uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear +}; + +Images are encoded row by row, left to right, top to bottom. The decoder and +encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An +image is complete when all pixels specified by width * height have been covered. + +Pixels are encoded as + - a run of the previous pixel + - an index into an array of previously seen pixels + - a difference to the previous pixel value in r,g,b + - full r,g,b or r,g,b,a values + +The color channels are assumed to not be premultiplied with the alpha channel +("un-premultiplied alpha"). + +A running array[64] (zero-initialized) of previously seen pixel values is +maintained by the encoder and decoder. Each pixel that is seen by the encoder +and decoder is put into this array at the position formed by a hash function of +the color value. In the encoder, if the pixel value at the index matches the +current pixel, this index position is written to the stream as QOI_OP_INDEX. +The hash function for the index is: + + index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 + +Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The +bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All +values encoded in these data bits have the most significant bit on the left. + +The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the +presence of an 8-bit tag first. + +The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte. + + +The possible chunks are: + + +.- QOI_OP_INDEX ----------. +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------+-----------------| +| 0 0 | index | +`-------------------------` +2-bit tag b00 +6-bit index into the color index array: 0..63 + +A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the +same index. QOI_OP_RUN should be used instead. + + +.- QOI_OP_DIFF -----------. +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------+-----+-----+-----| +| 0 1 | dr | dg | db | +`-------------------------` +2-bit tag b01 +2-bit red channel difference from the previous pixel between -2..1 +2-bit green channel difference from the previous pixel between -2..1 +2-bit blue channel difference from the previous pixel between -2..1 + +The difference to the current channel values are using a wraparound operation, +so "1 - 2" will result in 255, while "255 + 1" will result in 0. + +Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as +0 (b00). 1 is stored as 3 (b11). + +The alpha value remains unchanged from the previous pixel. + + +.- QOI_OP_LUMA -------------------------------------. +| Byte[0] | Byte[1] | +| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | +|-------+-----------------+-------------+-----------| +| 1 0 | green diff | dr - dg | db - dg | +`---------------------------------------------------` +2-bit tag b10 +6-bit green channel difference from the previous pixel -32..31 +4-bit red channel difference minus green channel difference -8..7 +4-bit blue channel difference minus green channel difference -8..7 + +The green channel is used to indicate the general direction of change and is +encoded in 6 bits. The red and blue channels (dr and db) base their diffs off +of the green channel difference and are encoded in 4 bits. I.e.: + dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) + db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) + +The difference to the current channel values are using a wraparound operation, +so "10 - 13" will result in 253, while "250 + 7" will result in 1. + +Values are stored as unsigned integers with a bias of 32 for the green channel +and a bias of 8 for the red and blue channel. + +The alpha value remains unchanged from the previous pixel. + + +.- QOI_OP_RUN ------------. +| Byte[0] | +| 7 6 5 4 3 2 1 0 | +|-------+-----------------| +| 1 1 | run | +`-------------------------` +2-bit tag b11 +6-bit run-length repeating the previous pixel: 1..62 + +The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 +(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and +QOI_OP_RGBA tags. + + +.- QOI_OP_RGB ------------------------------------------. +| Byte[0] | Byte[1] | Byte[2] | Byte[3] | +| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | +|-------------------------+---------+---------+---------| +| 1 1 1 1 1 1 1 0 | red | green | blue | +`-------------------------------------------------------` +8-bit tag b11111110 +8-bit red channel value +8-bit green channel value +8-bit blue channel value + +The alpha value remains unchanged from the previous pixel. + + +.- QOI_OP_RGBA ---------------------------------------------------. +| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | +| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | +|-------------------------+---------+---------+---------+---------| +| 1 1 1 1 1 1 1 1 | red | green | blue | alpha | +`-----------------------------------------------------------------` +8-bit tag b11111111 +8-bit red channel value +8-bit green channel value +8-bit blue channel value +8-bit alpha channel value + +*/ + +/* ----------------------------------------------------------------------------- +Header - Public functions */ + +#ifndef GD_QOI_H +#define GD_QOI_H + +#define QOI_NO_STDIO +#define QOI_MALLOC(sz) gdMalloc(sz) +#define QOI_FREE(p) gdFree(p) +#define qoi_encode gdQoiEncode +#define qoi_decode gdQoiDecode + +#ifdef __cplusplus +extern "C" { +#endif + +/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. +It describes either the input format (for qoi_write and qoi_encode), or is +filled with the description read from the file header (for qoi_read and +qoi_decode). + +The colorspace in this qoi_desc is an enum where + 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel + 1 = all channels are linear +You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely +informative. It will be saved to the file header, but does not affect +how chunks are en-/decoded. */ + +#define QOI_SRGB 0 +#define QOI_LINEAR 1 + +typedef struct { + unsigned int width; + unsigned int height; + unsigned char channels; + unsigned char colorspace; +} qoi_desc; + +#ifndef QOI_NO_STDIO + +/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file +system. The qoi_desc struct must be filled with the image width, height, +number of channels (3 = RGB, 4 = RGBA) and the colorspace. + +The function returns 0 on failure (invalid parameters, or fopen or malloc +failed) or the number of bytes written on success. */ + +int qoi_write(const char *filename, const void *data, const qoi_desc *desc); + +/* Read and decode a QOI image from the file system. If channels is 0, the +number of channels from the file header is used. If channels is 3 or 4 the +output format will be forced into this number of channels. + +The function either returns NULL on failure (invalid data, or malloc or fopen +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +will be filled with the description from the file header. + +The returned pixel data should be free()d after use. */ + +void *qoi_read(const char *filename, qoi_desc *desc, int channels); + +#endif /* QOI_NO_STDIO */ + +/* Encode raw RGB or RGBA pixels into a QOI image in memory. + +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the encoded data on success. On success the out_len +is set to the size in bytes of the encoded data. + +The returned qoi data should be free()d after use. */ + +void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); + +/* Decode a QOI image from memory. + +The function either returns NULL on failure (invalid parameters or malloc +failed) or a pointer to the decoded pixels. On success, the qoi_desc struct +is filled with the description from the file header. + +The returned pixel data should be free()d after use. */ + +void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); + +#ifdef __cplusplus +} +#endif +#endif /* GD_QOI_H */ + +/* ----------------------------------------------------------------------------- +Implementation */ + +#ifdef QOI_IMPLEMENTATION +#include +#include + +#ifndef QOI_MALLOC +#define QOI_MALLOC(sz) malloc(sz) +#define QOI_FREE(p) free(p) +#endif +#ifndef QOI_ZEROARR +#define QOI_ZEROARR(a) memset((a), 0, sizeof(a)) +#endif + +#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ +#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ +#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ +#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ +#define QOI_OP_RGB 0xfe /* 11111110 */ +#define QOI_OP_RGBA 0xff /* 11111111 */ + +#define QOI_MASK_2 0xc0 /* 11000000 */ + +#define QOI_COLOR_HASH(C) (C.rgba.r * 3 + C.rgba.g * 5 + C.rgba.b * 7 + C.rgba.a * 11) +#define QOI_MAGIC \ + (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | ((unsigned int)'i') << 8 | \ + ((unsigned int)'f')) +#define QOI_HEADER_SIZE 14 + +/* 2GB is the max file size that this implementation can safely handle. We guard +against anything larger than that, assuming the worst case with 5 bytes per +pixel, rounded down to a nice clean value. 400 million pixels ought to be +enough for anybody. */ +#define QOI_PIXELS_MAX ((unsigned int)400000000) + +typedef union { + struct { + unsigned char r, g, b, a; + } rgba; + unsigned int v; +} qoi_rgba_t; + +static const unsigned char qoi_padding[8] = {0, 0, 0, 0, 0, 0, 0, 1}; + +static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) +{ + bytes[(*p)++] = (0xff000000 & v) >> 24; + bytes[(*p)++] = (0x00ff0000 & v) >> 16; + bytes[(*p)++] = (0x0000ff00 & v) >> 8; + bytes[(*p)++] = (0x000000ff & v); +} + +static unsigned int qoi_read_32(const unsigned char *bytes, int *p) +{ + unsigned int a = bytes[(*p)++]; + unsigned int b = bytes[(*p)++]; + unsigned int c = bytes[(*p)++]; + unsigned int d = bytes[(*p)++]; + return a << 24 | b << 16 | c << 8 | d; +} + +void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) +{ + int i, max_size, p, run; + int px_len, px_end, px_pos, channels; + unsigned char *bytes; + const unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px, px_prev; + + if (data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || + desc->channels < 3 || desc->channels > 4 || desc->colorspace > 1 || + desc->height >= QOI_PIXELS_MAX / desc->width) { + return NULL; + } + + max_size = + desc->width * desc->height * (desc->channels + 1) + QOI_HEADER_SIZE + sizeof(qoi_padding); + + p = 0; + bytes = (unsigned char *)QOI_MALLOC(max_size); + if (!bytes) { + return NULL; + } + + qoi_write_32(bytes, &p, QOI_MAGIC); + qoi_write_32(bytes, &p, desc->width); + qoi_write_32(bytes, &p, desc->height); + bytes[p++] = desc->channels; + bytes[p++] = desc->colorspace; + + pixels = (const unsigned char *)data; + + QOI_ZEROARR(index); + + run = 0; + px_prev.rgba.r = 0; + px_prev.rgba.g = 0; + px_prev.rgba.b = 0; + px_prev.rgba.a = 255; + px = px_prev; + + px_len = desc->width * desc->height * desc->channels; + px_end = px_len - desc->channels; + channels = desc->channels; + + for (px_pos = 0; px_pos < px_len; px_pos += channels) { + px.rgba.r = pixels[px_pos + 0]; + px.rgba.g = pixels[px_pos + 1]; + px.rgba.b = pixels[px_pos + 2]; + + if (channels == 4) { + px.rgba.a = pixels[px_pos + 3]; + } + + if (px.v == px_prev.v) { + run++; + if (run == 62 || px_pos == px_end) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } + } else { + int index_pos; + + if (run > 0) { + bytes[p++] = QOI_OP_RUN | (run - 1); + run = 0; + } + + index_pos = QOI_COLOR_HASH(px) & (64 - 1); + + if (index[index_pos].v == px.v) { + bytes[p++] = QOI_OP_INDEX | index_pos; + } else { + index[index_pos] = px; + + if (px.rgba.a == px_prev.rgba.a) { + signed char vr = px.rgba.r - px_prev.rgba.r; + signed char vg = px.rgba.g - px_prev.rgba.g; + signed char vb = px.rgba.b - px_prev.rgba.b; + + signed char vg_r = vr - vg; + signed char vg_b = vb - vg; + + if (vr > -3 && vr < 2 && vg > -3 && vg < 2 && vb > -3 && vb < 2) { + bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); + } else if (vg_r > -9 && vg_r < 8 && vg > -33 && vg < 32 && vg_b > -9 && + vg_b < 8) { + bytes[p++] = QOI_OP_LUMA | (vg + 32); + bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); + } else { + bytes[p++] = QOI_OP_RGB; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + } + } else { + bytes[p++] = QOI_OP_RGBA; + bytes[p++] = px.rgba.r; + bytes[p++] = px.rgba.g; + bytes[p++] = px.rgba.b; + bytes[p++] = px.rgba.a; + } + } + } + px_prev = px; + } + + for (i = 0; i < (int)sizeof(qoi_padding); i++) { + bytes[p++] = qoi_padding[i]; + } + + *out_len = p; + return bytes; +} + +void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) +{ + const unsigned char *bytes; + unsigned int header_magic; + unsigned char *pixels; + qoi_rgba_t index[64]; + qoi_rgba_t px; + int px_len, chunks_len, px_pos; + int p = 0, run = 0; + + if (data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) || + size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)) { + return NULL; + } + + bytes = (const unsigned char *)data; + + header_magic = qoi_read_32(bytes, &p); + desc->width = qoi_read_32(bytes, &p); + desc->height = qoi_read_32(bytes, &p); + desc->channels = bytes[p++]; + desc->colorspace = bytes[p++]; + + if (desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || + desc->colorspace > 1 || header_magic != QOI_MAGIC || + desc->height >= QOI_PIXELS_MAX / desc->width) { + return NULL; + } + + if (channels == 0) { + channels = desc->channels; + } + + px_len = desc->width * desc->height * channels; + pixels = (unsigned char *)QOI_MALLOC(px_len); + if (!pixels) { + return NULL; + } + + QOI_ZEROARR(index); + px.rgba.r = 0; + px.rgba.g = 0; + px.rgba.b = 0; + px.rgba.a = 255; + + chunks_len = size - (int)sizeof(qoi_padding); + for (px_pos = 0; px_pos < px_len; px_pos += channels) { + if (run > 0) { + run--; + } else if (p < chunks_len) { + int b1 = bytes[p++]; + + if (b1 == QOI_OP_RGB) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + } else if (b1 == QOI_OP_RGBA) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + px.rgba.a = bytes[p++]; + } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { + px = index[b1]; + } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { + px.rgba.r += ((b1 >> 4) & 0x03) - 2; + px.rgba.g += ((b1 >> 2) & 0x03) - 2; + px.rgba.b += (b1 & 0x03) - 2; + } else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { + int b2 = bytes[p++]; + int vg = (b1 & 0x3f) - 32; + px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); + px.rgba.g += vg; + px.rgba.b += vg - 8 + (b2 & 0x0f); + } else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { + run = (b1 & 0x3f); + } + + index[QOI_COLOR_HASH(px) & (64 - 1)] = px; + } + + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; + + if (channels == 4) { + pixels[px_pos + 3] = px.rgba.a; + } + } + + return pixels; +} + +#ifndef QOI_NO_STDIO +#include + +int qoi_write(const char *filename, const void *data, const qoi_desc *desc) +{ + FILE *f = fopen(filename, "wb"); + int size, err; + void *encoded; + + if (!f) { + return 0; + } + + encoded = qoi_encode(data, desc, &size); + if (!encoded) { + fclose(f); + return 0; + } + + fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); + fclose(f); + + QOI_FREE(encoded); + return err ? 0 : size; +} + +void *qoi_read(const char *filename, qoi_desc *desc, int channels) +{ + FILE *f = fopen(filename, "rb"); + int size, bytes_read; + void *pixels, *data; + + if (!f) { + return NULL; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return NULL; + } + + data = QOI_MALLOC(size); + if (!data) { + fclose(f); + return NULL; + } + + bytes_read = fread(data, 1, size, f); + fclose(f); + pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); + QOI_FREE(data); + return pixels; +} + +#endif /* QOI_NO_STDIO */ +#endif /* QOI_IMPLEMENTATION */ diff --git a/ext/gd/libgd/gd_readimage.c b/ext/gd/libgd/gd_readimage.c new file mode 100644 index 000000000000..65db6368f51b --- /dev/null +++ b/ext/gd/libgd/gd_readimage.c @@ -0,0 +1,289 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd.h" +#include "gd_errors.h" +#include +#include + +#define GD_READIMAGE_PROBE_SIZE 32 + +typedef gdImagePtr(BGD_STDCALL *ReadCtxFn)(gdIOCtxPtr); + +struct FormatEntry { + const char *name; + size_t offset; + const unsigned char *sig; + size_t sig_len; + ReadCtxFn reader; + int enabled; +}; + +#ifdef HAVE_LIBPNG +#define GD_READIMAGE_PNG_READER gdImageCreateFromPngCtx +#define GD_READIMAGE_PNG_ENABLED 1 +#else +#define GD_READIMAGE_PNG_READER NULL +#define GD_READIMAGE_PNG_ENABLED 0 +#endif + +#ifdef HAVE_LIBJXL +#define GD_READIMAGE_JXL_READER gdImageCreateFromJxlCtx +#define GD_READIMAGE_JXL_ENABLED 1 +#else +#define GD_READIMAGE_JXL_READER NULL +#define GD_READIMAGE_JXL_ENABLED 0 +#endif + +#ifdef HAVE_LIBJPEG +#define GD_READIMAGE_JPEG_READER gdImageCreateFromJpegCtx +#define GD_READIMAGE_JPEG_ENABLED 1 +#else +#define GD_READIMAGE_JPEG_READER NULL +#define GD_READIMAGE_JPEG_ENABLED 0 +#endif + +#ifdef HAVE_LIBTIFF +#define GD_READIMAGE_TIFF_READER gdImageCreateFromTiffCtx +#define GD_READIMAGE_TIFF_ENABLED 1 +#else +#define GD_READIMAGE_TIFF_READER NULL +#define GD_READIMAGE_TIFF_ENABLED 0 +#endif + +#ifdef HAVE_LIBWEBP +#define GD_READIMAGE_WEBP_READER gdImageCreateFromWebpCtx +#define GD_READIMAGE_WEBP_ENABLED 1 +#else +#define GD_READIMAGE_WEBP_READER NULL +#define GD_READIMAGE_WEBP_ENABLED 0 +#endif + +#ifdef HAVE_LIBAVIF +#define GD_READIMAGE_AVIF_READER gdImageCreateFromAvifCtx +#define GD_READIMAGE_AVIF_ENABLED 1 +#else +#define GD_READIMAGE_AVIF_READER NULL +#define GD_READIMAGE_AVIF_ENABLED 0 +#endif + +#ifdef HAVE_LIBHEIF +#define GD_READIMAGE_HEIF_READER gdImageCreateFromHeifCtx +#define GD_READIMAGE_HEIF_ENABLED 1 +#else +#define GD_READIMAGE_HEIF_READER NULL +#define GD_READIMAGE_HEIF_ENABLED 0 +#endif + +#if ENABLE_GD_FORMATS +#define GD_READIMAGE_GD_READER gdImageCreateFromGdCtx +#define GD_READIMAGE_GD_ENABLED 1 +#else +#define GD_READIMAGE_GD_READER NULL +#define GD_READIMAGE_GD_ENABLED 0 +#endif + +#if ENABLE_GD_FORMATS && defined(HAVE_LIBZ) +#define GD_READIMAGE_GD2_READER gdImageCreateFromGd2Ctx +#define GD_READIMAGE_GD2_ENABLED 1 +#else +#define GD_READIMAGE_GD2_READER NULL +#define GD_READIMAGE_GD2_ENABLED 0 +#endif + +#ifdef HAVE_LIBXPM +#define GD_READIMAGE_XPM_ENABLED 1 +#else +#define GD_READIMAGE_XPM_ENABLED 0 +#endif + +#define ENTRY(n, off, reader_fn, is_enabled, ...) \ + { \ + (n), (off), (const unsigned char[]){__VA_ARGS__}, \ + sizeof((const unsigned char[]){__VA_ARGS__}) / sizeof(unsigned char), (reader_fn), \ + (is_enabled) \ + } + +static const struct FormatEntry format_table[] = { + ENTRY("PNG", 0, GD_READIMAGE_PNG_READER, GD_READIMAGE_PNG_ENABLED, 0x89, 0x50, 0x4E, 0x47, 0x0D, + 0x0A, 0x1A, 0x0A), + ENTRY("JXL", 0, GD_READIMAGE_JXL_READER, GD_READIMAGE_JXL_ENABLED, 0x00, 0x00, 0x00, 0x0C, 0x4A, + 0x58, 0x4C, 0x20, 0x0D, 0x0A, 0x87, 0x0A), + ENTRY("JXL", 0, GD_READIMAGE_JXL_READER, GD_READIMAGE_JXL_ENABLED, 0xFF, 0x0A), + ENTRY("JPEG", 0, GD_READIMAGE_JPEG_READER, GD_READIMAGE_JPEG_ENABLED, 0xFF, 0xD8, 0xFF), + ENTRY("GIF", 0, gdImageCreateFromGifCtx, 1, 0x47, 0x49, 0x46, 0x38, 0x39, 0x61), + ENTRY("GIF", 0, gdImageCreateFromGifCtx, 1, 0x47, 0x49, 0x46, 0x38, 0x37, 0x61), + ENTRY("BMP", 0, gdImageCreateFromBmpCtx, 1, 0x42, 0x4D), + ENTRY("TIFF", 0, GD_READIMAGE_TIFF_READER, GD_READIMAGE_TIFF_ENABLED, 0x4D, 0x4D, 0x00, 0x2A), + ENTRY("TIFF", 0, GD_READIMAGE_TIFF_READER, GD_READIMAGE_TIFF_ENABLED, 0x49, 0x49, 0x2A, 0x00), + ENTRY("WEBP", 8, GD_READIMAGE_WEBP_READER, GD_READIMAGE_WEBP_ENABLED, 0x57, 0x45, 0x42, 0x50), + ENTRY("AVIF", 4, GD_READIMAGE_AVIF_READER, GD_READIMAGE_AVIF_ENABLED, 0x66, 0x74, 0x79, 0x70, + 0x61, 0x76, 0x69, 0x66), + ENTRY("HEIC", 4, GD_READIMAGE_HEIF_READER, GD_READIMAGE_HEIF_ENABLED, 0x66, 0x74, 0x79, 0x70, + 0x68, 0x65, 0x69, 0x63), + ENTRY("GD2", 0, GD_READIMAGE_GD2_READER, GD_READIMAGE_GD2_ENABLED, 0x67, 0x64, 0x32, 0x00), + ENTRY("GD", 0, GD_READIMAGE_GD_READER, GD_READIMAGE_GD_ENABLED, 0xFF, 0xFE), + ENTRY("GD", 0, GD_READIMAGE_GD_READER, GD_READIMAGE_GD_ENABLED, 0xFF, 0xFF), + ENTRY("QOI", 0, gdImageCreateFromQoiCtx, 1, 0x71, 0x6F, 0x69, 0x66), + ENTRY("XPM", 0, NULL, GD_READIMAGE_XPM_ENABLED, 0x2F, 0x2A, 0x20, 0x58, 0x50, 0x4D, 0x20, 0x2A), + ENTRY("XBM", 0, NULL, 1, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x20), + {NULL, 0, NULL, 0, NULL, 0}}; + +static int sig_match(const unsigned char *probe, size_t probe_len, const struct FormatEntry *e) +{ + if (e->offset + e->sig_len > probe_len) + return 0; + return memcmp(probe + e->offset, e->sig, e->sig_len) == 0; +} + +static const struct FormatEntry *find_format(const unsigned char *probe, size_t probe_len) +{ + size_t i; + + for (i = 0; format_table[i].name != NULL; i++) { + const struct FormatEntry *e = &format_table[i]; + if (sig_match(probe, probe_len, e)) + return e; + } + + return NULL; +} + +static void report_unreadable_format(const struct FormatEntry *e, const char *source) +{ + if (e == NULL) { + gd_error("gd-readimage: unknown image format"); + return; + } + + if (!e->enabled) { + gd_error("gd-readimage: %s image support has been disabled", e->name); + return; + } + + gd_error("gd-readimage: %s image does not support reading from %s", e->name, source); +} + +/* + Function: gdImageReadCtx + + Read an image from a , auto-detecting the format by + magic-byte signatures. + + reads the first bytes from the ctx, determines + the image format by matching known magic-byte signatures, and + dispatches to the appropriate codec's Ctx reader. The ctx must + support seeking so it can be rewound after probing. + + Formats without a Ctx reader (XPM, XBM) are not supported by + this function. Use for those formats. + + NULL is returned on error or if the format is not recognized. + + Parameters: + + ctx - the input + + Returns: + + A pointer to the new image or NULL if an error occurred. + +*/ +BGD_DECLARE(gdImagePtr) gdImageReadCtx(gdIOCtxPtr ctx) +{ + unsigned char probe[GD_READIMAGE_PROBE_SIZE]; + size_t probe_len; + const struct FormatEntry *e; + + if (ctx == NULL) + return NULL; + + probe_len = (size_t)gdGetBuf(probe, GD_READIMAGE_PROBE_SIZE, ctx); + gdSeek(ctx, 0); + + e = find_format(probe, probe_len); + if (e != NULL && e->reader != NULL) + return e->reader(ctx); + + report_unreadable_format(e, "gdIOCtx"); + return NULL; +} + +/* + Function: gdImageReadFile + + Read an image file, auto-detecting the format by magic-byte + signatures rather than filename extension. + + opens the file, probes its header bytes to + determine the format, and calls the appropriate + _gdImageCreateFrom*Ctx_ function. If the format has no Ctx + reader (XPM, XBM), it falls back to the filename-based or + FILE*-based reader. + + NULL is returned on error or if the format is not recognized. + + Parameters: + + filename - the input file name + + Returns: + + A pointer to the new image or NULL if an error occurred. + +*/ +BGD_DECLARE(gdImagePtr) gdImageReadFile(const char *filename) +{ + FILE *fh; + gdIOCtxPtr ctx; + unsigned char probe[GD_READIMAGE_PROBE_SIZE]; + size_t probe_len; + gdImagePtr im; + const struct FormatEntry *e; + + if (filename == NULL) + return NULL; + + fh = fopen(filename, "rb"); + if (!fh) + return NULL; + + ctx = gdNewFileCtx(fh); + if (!ctx) { + fclose(fh); + return NULL; + } + + probe_len = (size_t)gdGetBuf(probe, GD_READIMAGE_PROBE_SIZE, ctx); + gdSeek(ctx, 0); + + e = find_format(probe, probe_len); + if (e != NULL && e->reader != NULL) { + im = e->reader(ctx); + ctx->gd_free(ctx); + fclose(fh); + return im; + } + + ctx->gd_free(ctx); + fclose(fh); + + if (e != NULL && e->enabled) { +#ifdef HAVE_LIBXPM + if (strcmp(e->name, "XPM") == 0) + return gdImageCreateFromXpm((char *)filename); +#endif + if (strcmp(e->name, "XBM") == 0) { + FILE *xbm_fh = fopen(filename, "rb"); + if (!xbm_fh) + return NULL; + im = gdImageCreateFromXbm(xbm_fh); + fclose(xbm_fh); + return im; + } + } + + report_unreadable_format(e, "file"); + return NULL; +} diff --git a/ext/gd/libgd/gd_rotate.c b/ext/gd/libgd/gd_rotate.c index 53c6c9470db7..82f9c8a745b9 100644 --- a/ext/gd/libgd/gd_rotate.c +++ b/ext/gd/libgd/gd_rotate.c @@ -11,331 +11,333 @@ #ifdef ROTATE_PI #undef ROTATE_PI #endif /* ROTATE_PI */ - -#define ROTATE_DEG2RAD 3.1415926535897932384626433832795/180 -void gdImageSkewX (gdImagePtr dst, gdImagePtr src, int uRow, int iOffset, double dWeight, int clrBack, int ignoretransparent) +typedef int(BGD_STDCALL *FuncPtr)(gdImagePtr, int, int); +#define ROTATE_DEG2RAD 3.1415926535897932384626433832795 / 180 +void gdImageSkewX(gdImagePtr dst, gdImagePtr src, int uRow, int iOffset, double dWeight, + int clrBack, int ignoretransparent) { - typedef int (*FuncPtr)(gdImagePtr, int, int); - int i, r, g, b, a, clrBackR, clrBackG, clrBackB, clrBackA; - FuncPtr f; - - int pxlOldLeft, pxlLeft=0, pxlSrc; - - /* Keep clrBack as color index if required */ - if (src->trueColor) { - pxlOldLeft = clrBack; - f = gdImageGetTrueColorPixel; - } else { - pxlOldLeft = clrBack; - clrBackR = gdImageRed(src, clrBack); - clrBackG = gdImageGreen(src, clrBack); - clrBackB = gdImageBlue(src, clrBack); - clrBackA = gdImageAlpha(src, clrBack); - clrBack = gdTrueColorAlpha(clrBackR, clrBackG, clrBackB, clrBackA); - f = gdImageGetPixel; - } - - for (i = 0; i < iOffset; i++) { - gdImageSetPixel (dst, i, uRow, clrBack); - } - - if (i < dst->sx) { - gdImageSetPixel (dst, i, uRow, clrBack); - } - - for (i = 0; i < src->sx; i++) { - pxlSrc = f (src,i,uRow); - - r = (int)(gdImageRed(src,pxlSrc) * dWeight); - g = (int)(gdImageGreen(src,pxlSrc) * dWeight); - b = (int)(gdImageBlue(src,pxlSrc) * dWeight); - a = (int)(gdImageAlpha(src,pxlSrc) * dWeight); - - pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a); - - if (pxlLeft == -1) { - pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a); - } - - r = gdImageRed(src,pxlSrc) - (gdImageRed(src,pxlLeft) - gdImageRed(src,pxlOldLeft)); - g = gdImageGreen(src,pxlSrc) - (gdImageGreen(src,pxlLeft) - gdImageGreen(src,pxlOldLeft)); - b = gdImageBlue(src,pxlSrc) - (gdImageBlue(src,pxlLeft) - gdImageBlue(src,pxlOldLeft)); - a = gdImageAlpha(src,pxlSrc) - (gdImageAlpha(src,pxlLeft) - gdImageAlpha(src,pxlOldLeft)); - - if (r>255) { - r = 255; + int i, r, g, b, a, clrBackR, clrBackG, clrBackB, clrBackA; + FuncPtr f; + + int pxlOldLeft, pxlLeft = 0, pxlSrc; + + /* Keep clrBack as color index if required */ + if (src->trueColor) { + pxlOldLeft = clrBack; + f = gdImageGetTrueColorPixel; + } else { + pxlOldLeft = clrBack; + clrBackR = gdImageRed(src, clrBack); + clrBackG = gdImageGreen(src, clrBack); + clrBackB = gdImageBlue(src, clrBack); + clrBackA = gdImageAlpha(src, clrBack); + clrBack = gdTrueColorAlpha(clrBackR, clrBackG, clrBackB, clrBackA); + f = gdImageGetPixel; + } + + for (i = 0; i < iOffset; i++) { + gdImageSetPixel(dst, i, uRow, clrBack); + } + + if (i < dst->sx) { + gdImageSetPixel(dst, i, uRow, clrBack); + } + + for (i = 0; i < src->sx; i++) { + pxlSrc = f(src, i, uRow); + + r = (int)(gdImageRed(src, pxlSrc) * dWeight); + g = (int)(gdImageGreen(src, pxlSrc) * dWeight); + b = (int)(gdImageBlue(src, pxlSrc) * dWeight); + a = (int)(gdImageAlpha(src, pxlSrc) * dWeight); + + pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a); + + if (pxlLeft == -1) { + pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a); } - if (g>255) { - g = 255; - } + r = gdImageRed(src, pxlSrc) - (gdImageRed(src, pxlLeft) - gdImageRed(src, pxlOldLeft)); + g = gdImageGreen(src, pxlSrc) - + (gdImageGreen(src, pxlLeft) - gdImageGreen(src, pxlOldLeft)); + b = gdImageBlue(src, pxlSrc) - (gdImageBlue(src, pxlLeft) - gdImageBlue(src, pxlOldLeft)); + a = gdImageAlpha(src, pxlSrc) - + (gdImageAlpha(src, pxlLeft) - gdImageAlpha(src, pxlOldLeft)); - if (b>255) { - b = 255; - } + if (r > 255) { + r = 255; + } - if (a>127) { - a = 127; - } + if (g > 255) { + g = 255; + } - if (ignoretransparent && pxlSrc == dst->transparent) { - pxlSrc = dst->transparent; - } else { - pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a); + if (b > 255) { + b = 255; + } - if (pxlSrc == -1) { - pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a); - } - } + if (a > 127) { + a = 127; + } - if ((i + iOffset >= 0) && (i + iOffset < dst->sx)) { - gdImageSetPixel (dst, i+iOffset, uRow, pxlSrc); - } + if (ignoretransparent && pxlSrc == dst->transparent) { + pxlSrc = dst->transparent; + } else { + pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a); + + if (pxlSrc == -1) { + pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a); + } + } + + if ((i + iOffset >= 0) && (i + iOffset < dst->sx)) { + gdImageSetPixel(dst, i + iOffset, uRow, pxlSrc); + } - pxlOldLeft = pxlLeft; - } + pxlOldLeft = pxlLeft; + } - i += iOffset; + i += iOffset; - if (i < dst->sx) { - gdImageSetPixel (dst, i, uRow, pxlLeft); - } + if (i < dst->sx) { + gdImageSetPixel(dst, i, uRow, pxlLeft); + } - gdImageSetPixel (dst, iOffset, uRow, clrBack); + gdImageSetPixel(dst, iOffset, uRow, clrBack); - i--; + i--; - while (++i < dst->sx) { - gdImageSetPixel (dst, i, uRow, clrBack); - } + while (++i < dst->sx) { + gdImageSetPixel(dst, i, uRow, clrBack); + } } -void gdImageSkewY (gdImagePtr dst, gdImagePtr src, int uCol, int iOffset, double dWeight, int clrBack, int ignoretransparent) +void gdImageSkewY(gdImagePtr dst, gdImagePtr src, int uCol, int iOffset, double dWeight, + int clrBack, int ignoretransparent) { - typedef int (*FuncPtr)(gdImagePtr, int, int); - int i, iYPos=0, r, g, b, a; - FuncPtr f; - int pxlOldLeft, pxlLeft=0, pxlSrc; - - if (src->trueColor) { - f = gdImageGetTrueColorPixel; - } else { - f = gdImageGetPixel; - } - - for (i = 0; i<=iOffset; i++) { - gdImageSetPixel (dst, uCol, i, clrBack); - } - r = (int)((double)gdImageRed(src,clrBack) * dWeight); - g = (int)((double)gdImageGreen(src,clrBack) * dWeight); - b = (int)((double)gdImageBlue(src,clrBack) * dWeight); - a = (int)((double)gdImageAlpha(src,clrBack) * dWeight); - - pxlOldLeft = gdImageColorAllocateAlpha(dst, r, g, b, a); - - for (i = 0; i < src->sy; i++) { - pxlSrc = f (src, uCol, i); - iYPos = i + iOffset; - - r = (int)((double)gdImageRed(src,pxlSrc) * dWeight); - g = (int)((double)gdImageGreen(src,pxlSrc) * dWeight); - b = (int)((double)gdImageBlue(src,pxlSrc) * dWeight); - a = (int)((double)gdImageAlpha(src,pxlSrc) * dWeight); - - pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a); - - if (pxlLeft == -1) { - pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a); - } - - r = gdImageRed(src,pxlSrc) - (gdImageRed(src,pxlLeft) - gdImageRed(src,pxlOldLeft)); - g = gdImageGreen(src,pxlSrc) - (gdImageGreen(src,pxlLeft) - gdImageGreen(src,pxlOldLeft)); - b = gdImageBlue(src,pxlSrc) - (gdImageBlue(src,pxlLeft) - gdImageBlue(src,pxlOldLeft)); - a = gdImageAlpha(src,pxlSrc) - (gdImageAlpha(src,pxlLeft) - gdImageAlpha(src,pxlOldLeft)); - - if (r>255) { - r = 255; - } - - if (g>255) { - g = 255; - } - - if (b>255) { - b = 255; - } - - if (a>127) { - a = 127; - } - - if (ignoretransparent && pxlSrc == dst->transparent) { - pxlSrc = dst->transparent; - } else { - pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a); - - if (pxlSrc == -1) { - pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a); - } - } - - if ((iYPos >= 0) && (iYPos < dst->sy)) { - gdImageSetPixel (dst, uCol, iYPos, pxlSrc); - } - - pxlOldLeft = pxlLeft; - } - - i = iYPos; - if (i < dst->sy) { - gdImageSetPixel (dst, uCol, i, pxlLeft); - } - - i--; - while (++i < dst->sy) { - gdImageSetPixel (dst, uCol, i, clrBack); - } + int i, iYPos = 0, r, g, b, a; + FuncPtr f; + int pxlOldLeft, pxlLeft = 0, pxlSrc; + + if (src->trueColor) { + f = gdImageGetTrueColorPixel; + } else { + f = gdImageGetPixel; + } + + for (i = 0; i <= iOffset; i++) { + gdImageSetPixel(dst, uCol, i, clrBack); + } + r = (int)((double)gdImageRed(src, clrBack) * dWeight); + g = (int)((double)gdImageGreen(src, clrBack) * dWeight); + b = (int)((double)gdImageBlue(src, clrBack) * dWeight); + a = (int)((double)gdImageAlpha(src, clrBack) * dWeight); + + pxlOldLeft = gdImageColorAllocateAlpha(dst, r, g, b, a); + + for (i = 0; i < src->sy; i++) { + pxlSrc = f(src, uCol, i); + iYPos = i + iOffset; + + r = (int)((double)gdImageRed(src, pxlSrc) * dWeight); + g = (int)((double)gdImageGreen(src, pxlSrc) * dWeight); + b = (int)((double)gdImageBlue(src, pxlSrc) * dWeight); + a = (int)((double)gdImageAlpha(src, pxlSrc) * dWeight); + + pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a); + + if (pxlLeft == -1) { + pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a); + } + + r = gdImageRed(src, pxlSrc) - (gdImageRed(src, pxlLeft) - gdImageRed(src, pxlOldLeft)); + g = gdImageGreen(src, pxlSrc) - + (gdImageGreen(src, pxlLeft) - gdImageGreen(src, pxlOldLeft)); + b = gdImageBlue(src, pxlSrc) - (gdImageBlue(src, pxlLeft) - gdImageBlue(src, pxlOldLeft)); + a = gdImageAlpha(src, pxlSrc) - + (gdImageAlpha(src, pxlLeft) - gdImageAlpha(src, pxlOldLeft)); + + if (r > 255) { + r = 255; + } + + if (g > 255) { + g = 255; + } + + if (b > 255) { + b = 255; + } + + if (a > 127) { + a = 127; + } + + if (ignoretransparent && pxlSrc == dst->transparent) { + pxlSrc = dst->transparent; + } else { + pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a); + + if (pxlSrc == -1) { + pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a); + } + } + + if ((iYPos >= 0) && (iYPos < dst->sy)) { + gdImageSetPixel(dst, uCol, iYPos, pxlSrc); + } + + pxlOldLeft = pxlLeft; + } + + i = iYPos; + if (i < dst->sy) { + gdImageSetPixel(dst, uCol, i, pxlLeft); + } + + i--; + while (++i < dst->sy) { + gdImageSetPixel(dst, uCol, i, clrBack); + } } /* Rotates an image by 90 degrees (counter clockwise) */ -gdImagePtr gdImageRotate90 (gdImagePtr src, int ignoretransparent) +gdImagePtr gdImageRotate90(gdImagePtr src, int ignoretransparent) { - int uY, uX; - int c,r,g,b,a; - gdImagePtr dst; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src->trueColor) { - f = gdImageGetTrueColorPixel; - } else { - f = gdImageGetPixel; - } - dst = gdImageCreateTrueColor(src->sy, src->sx); - - if (dst != NULL) { - int old_blendmode = dst->alphaBlendingFlag; - dst->alphaBlendingFlag = 0; - dst->saveAlphaFlag = 1; - dst->transparent = src->transparent; - - gdImagePaletteCopy (dst, src); - - for (uY = 0; uYsy; uY++) { - for (uX = 0; uXsx; uX++) { - c = f (src, uX, uY); - if (!src->trueColor) { - r = gdImageRed(src,c); - g = gdImageGreen(src,c); - b = gdImageBlue(src,c); - a = gdImageAlpha(src,c); - c = gdTrueColorAlpha(r, g, b, a); - } - if (ignoretransparent && c == dst->transparent) { - gdImageSetPixel(dst, uY, (dst->sy - uX - 1), dst->transparent); - } else { - gdImageSetPixel(dst, uY, (dst->sy - uX - 1), c); - } - } - } - dst->alphaBlendingFlag = old_blendmode; - } - - return dst; + int uY, uX; + int c, r, g, b, a; + gdImagePtr dst; + typedef int (*FuncPtr)(gdImagePtr, int, int); + FuncPtr f; + + if (src->trueColor) { + f = gdImageGetTrueColorPixel; + } else { + f = gdImageGetPixel; + } + dst = gdImageCreateTrueColor(src->sy, src->sx); + + if (dst != NULL) { + int old_blendmode = dst->alphaBlendingFlag; + dst->alphaBlendingFlag = 0; + dst->saveAlphaFlag = 1; + dst->transparent = src->transparent; + + gdImagePaletteCopy(dst, src); + + for (uY = 0; uY < src->sy; uY++) { + for (uX = 0; uX < src->sx; uX++) { + c = f(src, uX, uY); + if (!src->trueColor) { + r = gdImageRed(src, c); + g = gdImageGreen(src, c); + b = gdImageBlue(src, c); + a = gdImageAlpha(src, c); + c = gdTrueColorAlpha(r, g, b, a); + } + if (ignoretransparent && c == dst->transparent) { + gdImageSetPixel(dst, uY, (dst->sy - uX - 1), dst->transparent); + } else { + gdImageSetPixel(dst, uY, (dst->sy - uX - 1), c); + } + } + } + dst->alphaBlendingFlag = old_blendmode; + } + + return dst; } /* Rotates an image by 180 degrees (counter clockwise) */ -gdImagePtr gdImageRotate180 (gdImagePtr src, int ignoretransparent) +gdImagePtr gdImageRotate180(gdImagePtr src, int ignoretransparent) { - int uY, uX; - int c,r,g,b,a; - gdImagePtr dst; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src->trueColor) { - f = gdImageGetTrueColorPixel; - } else { - f = gdImageGetPixel; - } - dst = gdImageCreateTrueColor(src->sx, src->sy); - - if (dst != NULL) { - int old_blendmode = dst->alphaBlendingFlag; - dst->alphaBlendingFlag = 0; - dst->saveAlphaFlag = 1; - dst->transparent = src->transparent; - - gdImagePaletteCopy (dst, src); - - for (uY = 0; uYsy; uY++) { - for (uX = 0; uXsx; uX++) { - c = f (src, uX, uY); - if (!src->trueColor) { - r = gdImageRed(src,c); - g = gdImageGreen(src,c); - b = gdImageBlue(src,c); - a = gdImageAlpha(src,c); - c = gdTrueColorAlpha(r, g, b, a); - } - - if (ignoretransparent && c == dst->transparent) { - gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), dst->transparent); - } else { - gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), c); - } - } - } - dst->alphaBlendingFlag = old_blendmode; - } - - return dst; + int uY, uX; + int c, r, g, b, a; + gdImagePtr dst; + FuncPtr f; + + if (src->trueColor) { + f = gdImageGetTrueColorPixel; + } else { + f = gdImageGetPixel; + } + dst = gdImageCreateTrueColor(src->sx, src->sy); + + if (dst != NULL) { + int old_blendmode = dst->alphaBlendingFlag; + dst->alphaBlendingFlag = 0; + dst->saveAlphaFlag = 1; + dst->transparent = src->transparent; + + gdImagePaletteCopy(dst, src); + + for (uY = 0; uY < src->sy; uY++) { + for (uX = 0; uX < src->sx; uX++) { + c = f(src, uX, uY); + if (!src->trueColor) { + r = gdImageRed(src, c); + g = gdImageGreen(src, c); + b = gdImageBlue(src, c); + a = gdImageAlpha(src, c); + c = gdTrueColorAlpha(r, g, b, a); + } + + if (ignoretransparent && c == dst->transparent) { + gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), dst->transparent); + } else { + gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), c); + } + } + } + dst->alphaBlendingFlag = old_blendmode; + } + + return dst; } /* Rotates an image by 270 degrees (counter clockwise) */ -gdImagePtr gdImageRotate270 (gdImagePtr src, int ignoretransparent) +gdImagePtr gdImageRotate270(gdImagePtr src, int ignoretransparent) { - int uY, uX; - int c,r,g,b,a; - gdImagePtr dst; - typedef int (*FuncPtr)(gdImagePtr, int, int); - FuncPtr f; - - if (src->trueColor) { - f = gdImageGetTrueColorPixel; - } else { - f = gdImageGetPixel; - } - dst = gdImageCreateTrueColor (src->sy, src->sx); - - if (dst != NULL) { - int old_blendmode = dst->alphaBlendingFlag; - dst->alphaBlendingFlag = 0; - dst->saveAlphaFlag = 1; - dst->transparent = src->transparent; - - gdImagePaletteCopy (dst, src); - - for (uY = 0; uYsy; uY++) { - for (uX = 0; uXsx; uX++) { - c = f (src, uX, uY); - if (!src->trueColor) { - r = gdImageRed(src,c); - g = gdImageGreen(src,c); - b = gdImageBlue(src,c); - a = gdImageAlpha(src,c); - c = gdTrueColorAlpha(r, g, b, a); - } - - if (ignoretransparent && c == dst->transparent) { - gdImageSetPixel(dst, (dst->sx - uY - 1), uX, dst->transparent); - } else { - gdImageSetPixel(dst, (dst->sx - uY - 1), uX, c); - } - } - } - dst->alphaBlendingFlag = old_blendmode; - } - - return dst; + int uY, uX; + int c, r, g, b, a; + gdImagePtr dst; + FuncPtr f; + + if (src->trueColor) { + f = gdImageGetTrueColorPixel; + } else { + f = gdImageGetPixel; + } + dst = gdImageCreateTrueColor(src->sy, src->sx); + + if (dst != NULL) { + int old_blendmode = dst->alphaBlendingFlag; + dst->alphaBlendingFlag = 0; + dst->saveAlphaFlag = 1; + dst->transparent = src->transparent; + + gdImagePaletteCopy(dst, src); + + for (uY = 0; uY < src->sy; uY++) { + for (uX = 0; uX < src->sx; uX++) { + c = f(src, uX, uY); + if (!src->trueColor) { + r = gdImageRed(src, c); + g = gdImageGreen(src, c); + b = gdImageBlue(src, c); + a = gdImageAlpha(src, c); + c = gdTrueColorAlpha(r, g, b, a); + } + + if (ignoretransparent && c == dst->transparent) { + gdImageSetPixel(dst, (dst->sx - uY - 1), uX, dst->transparent); + } else { + gdImageSetPixel(dst, (dst->sx - uY - 1), uX, c); + } + } + } + dst->alphaBlendingFlag = old_blendmode; + } + + return dst; } diff --git a/ext/gd/libgd/gd_security.c b/ext/gd/libgd/gd_security.c index cbc4872d2fce..5e4d7da04826 100644 --- a/ext/gd/libgd/gd_security.c +++ b/ext/gd/libgd/gd_security.c @@ -1,50 +1,61 @@ /* - * gd_security.c - * - * Implements buffer overflow check routines. - * - * Written 2004, Phil Knirsch. - * Based on netpbm fixes by Alan Cox. - * + * gd_security.c + * + * Implements buffer overflow check routines. + * + * Written 2004, Phil Knirsch. + * Based on netpbm fixes by Alan Cox. + * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include -#include #include "gd.h" #include "gd_errors.h" +#include +#include +#include +#include int overflow2(int a, int b) { - if(a <= 0 || b <= 0) { - gd_error("One parameter to a memory allocation multiplication is negative or zero, failing operation gracefully\n"); - return 1; - } - if(a > INT_MAX / b) { - gd_error("Product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully\n"); - return 1; - } - return 0; + if (a <= 0 || b <= 0) { + gd_error_ex(GD_WARNING, "one parameter to a memory allocation multiplication is " + "negative or zero, failing operation gracefully\n"); + return 1; + } + if (a > INT_MAX / b) { + gd_error_ex(GD_WARNING, "Product of memory allocation multiplication would exceed " + "INT_MAX, failing operation gracefully\n"); + return 1; + } + return 0; } int overflowMul3(int a, int b, int c) { - if (a < 0 || b < 0 || c < 0) { - return 1; - } - if (a == 0 || b == 0 || c == 0) { - return 0; - } - if (a > INT_MAX / b) { - return 1; - } - if ((int64_t)a * b > INT64_MAX / c) { - return 1; - } - return 0; + if (a < 0 || b < 0 || c < 0) { + return 1; + } + if (a == 0 || b == 0 || c == 0) { + return 0; + } + /* check a*b fits in int first */ + if (a > INT_MAX / b) { + return 1; + } +#ifdef HAVE_INT64_T + /* check a*b*c fits in int64 */ + if ((int64_t)a * b > INT64_MAX / c) { + return 1; + } +#else + /* no 64-bit type available, check against INT_MAX */ + if (a > INT_MAX / b / c) { + return 1; + } +#endif + return 0; } diff --git a/ext/gd/libgd/gd_span_rle.c b/ext/gd/libgd/gd_span_rle.c new file mode 100644 index 000000000000..23b46de4a825 --- /dev/null +++ b/ext/gd/libgd/gd_span_rle.c @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gd_errors.h" +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "ftraster/gd_ft_math.h" +#include "ftraster/gd_ft_raster.h" +#include "ftraster/gd_ft_stroker.h" +#include "ftraster/gd_ft_types.h" +#include "gd_array.h" +#include "gd_path.h" +#include "gd_path_dash.h" +#include "gd_path_matrix.h" +#include "gd_path_outline.h" +#include "gd_span_rle.h" +#include "gd_surface.h" + +#define SQRT2 1.41421356237309504880 + +gdSpanRlePtr gdSpanRleCreate() +{ + gdSpanRlePtr rle = gdMalloc(sizeof(gdSpanRle)); + if (!rle) + return NULL; + rle->ref = 1; + _rle_spans_init(rle->spans); + rle->x = 0; + rle->y = 0; + rle->w = 0; + rle->h = 0; + return rle; +} + +gdSpanRlePtr gdSpanRleRetain(gdSpanRlePtr rle) +{ + if (rle != NULL) + rle->ref++; + return rle; +} + +void gdSpanRleDestroy(gdSpanRlePtr rle) +{ + if (rle == NULL) + return; + if (--rle->ref > 0) + return; + gdFree(rle->spans.data); + gdFree(rle); +} + +void gdSpanRleClear(gdSpanRlePtr rle) +{ + rle->spans.size = 0; + rle->x = 0; + rle->y = 0; + rle->w = 0; + rle->h = 0; +} + +gdSpanRlePtr gdSpanRleClone(gdSpanRlePtr rle) +{ + if (rle == NULL) + return NULL; + + gdSpanRlePtr clone = gdMalloc(sizeof(gdSpanRle)); + if (!clone) + return NULL; + + clone->ref = 1; + _rle_spans_init(clone->spans); + if (rle->spans.size > 0) { + clone->spans.data = gdMalloc((size_t)rle->spans.size * sizeof(gdSpan)); + if (!clone->spans.data) { + gdFree(clone); + return NULL; + } + clone->spans.capacity = rle->spans.size; + memcpy(clone->spans.data, rle->spans.data, (size_t)rle->spans.size * sizeof(gdSpan)); + } + clone->spans.size = rle->spans.size; + clone->x = rle->x; + clone->y = rle->y; + clone->w = rle->w; + clone->h = rle->h; + return clone; +} + +#define DIV255(x) (((x) + ((x) >> 8) + 0x80) >> 8) +gdSpanRlePtr gdSpanHorizontalClip(const gdSpanRlePtr a, const gdSpanRlePtr b) +{ + gdSpanRlePtr result = gdMalloc(sizeof(gdSpanRle)); + if (!result) { + return NULL; + } + + result->ref = 1; + _rle_spans_init(result->spans); + if (a->spans.size == 0 || b->spans.size == 0) { + result->x = 0; + result->y = 0; + result->w = 0; + result->h = 0; + return result; + } + int capacity = MAX(a->spans.size, b->spans.size); + if (capacity > 0) { + result->spans.data = gdMalloc((size_t)capacity * sizeof(gdSpan)); + if (!result->spans.data) { + gdFree(result); + return NULL; + } + result->spans.capacity = capacity; + } + + gdSpanPtr a_spans = a->spans.data; + gdSpanPtr a_end = a_spans + a->spans.size; + + gdSpanPtr b_spans = b->spans.data; + gdSpanPtr b_end = b_spans + b->spans.size; + + while (a_spans < a_end && b_spans < b_end) { + if (b_spans->y > a_spans->y) { + ++a_spans; + continue; + } + + if (a_spans->y != b_spans->y) { + ++b_spans; + continue; + } + + int ax1 = a_spans->x; + int ax2 = ax1 + a_spans->len; + int bx1 = b_spans->x; + int bx2 = bx1 + b_spans->len; + + if (bx1 < ax1 && bx2 < ax1) { + ++b_spans; + continue; + } + + if (ax1 < bx1 && ax2 < bx1) { + ++a_spans; + continue; + } + + int x = MAX(ax1, bx1); + int len = MIN(ax2, bx2) - x; + if (len) { + gdSpanPtr span = result->spans.data + result->spans.size; + span->x = (short)x; + span->len = (unsigned short)len; + span->y = a_spans->y; + span->coverage = DIV255(a_spans->coverage * b_spans->coverage); + result->spans.size += 1; + } + + if (ax2 < bx2) { + ++a_spans; + } else { + ++b_spans; + } + } + + if (result->spans.size == 0) { + result->x = 0; + result->y = 0; + result->w = 0; + result->h = 0; + return result; + } + + gdSpanPtr spans = (result->spans.data); + int x1 = INT_MAX; + int y1 = spans[0].y; + int x2 = 0; + int y2 = spans[result->spans.size - 1].y; + for (int i = 0; i < result->spans.size; i++) { + if (spans[i].x < x1) + x1 = spans[i].x; + if (spans[i].x + spans[i].len > x2) + x2 = spans[i].x + spans[i].len; + } + + result->x = x1; + result->y = y1; + result->w = x2 - x1; + result->h = y2 - y1 + 1; + return result; +} + +void gdSpanRlePathClip(gdSpanRlePtr rle, const gdSpanRlePtr clip) +{ + if (rle == NULL || clip == NULL) + return; + + gdSpanRlePtr result = gdSpanHorizontalClip(rle, clip); + if (!result) { + return; + } + _rle_spans_allocate(rle->spans, result->spans.size); + if (result->spans.size > 0) { + memcpy(rle->spans.data, result->spans.data, (size_t)result->spans.size * sizeof(gdSpan)); + } + rle->spans.size = result->spans.size; + rle->x = result->x; + rle->y = result->y; + rle->w = result->w; + rle->h = result->h; + gdSpanRleDestroy(result); +} + +GD_FT_Outline *gd_ft_outline_create(int points, int contours) +{ + GD_FT_Outline *ft = gdMalloc(sizeof(GD_FT_Outline)); + if (!ft) + return NULL; + ft->points = malloc((size_t)(points + contours) * sizeof(GD_FT_Vector)); + ft->tags = malloc((size_t)(points + contours) * sizeof(char)); + ft->contours = malloc((size_t)contours * sizeof(short)); + ft->contours_flag = malloc((size_t)contours * sizeof(char)); + ft->n_points = ft->n_contours = 0; + ft->flags = 0x0; + return ft; +} + +void gd_ft_outline_close(GD_FT_Outline *ft) +{ + ft->contours_flag[ft->n_contours] = 0; + int index = ft->n_contours ? ft->contours[ft->n_contours - 1] + 1 : 0; + if (index == ft->n_points) + return; + + ft->points[ft->n_points].x = ft->points[index].x; + ft->points[ft->n_points].y = ft->points[index].y; + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_ON; + ft->n_points++; +} + +void gd_ft_outline_end(GD_FT_Outline *ft) +{ + if (ft->n_points) { + ft->contours[ft->n_contours] = ft->n_points - 1; + ft->n_contours++; + } +} + +#define FT_COORD(x) (GD_FT_Pos)((x) * 64) +void gd_ft_outline_move_to(GD_FT_Outline *ft, double x, double y) +{ + ft->points[ft->n_points].x = FT_COORD(x); + ft->points[ft->n_points].y = FT_COORD(y); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_ON; + if (ft->n_points) { + ft->contours[ft->n_contours] = ft->n_points - 1; + ft->n_contours++; + } + + ft->contours_flag[ft->n_contours] = 1; + ft->n_points++; +} + +void gd_ft_outline_line_to(GD_FT_Outline *ft, double x, double y) +{ + ft->points[ft->n_points].x = FT_COORD(x); + ft->points[ft->n_points].y = FT_COORD(y); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_ON; + ft->n_points++; +} + +void gd_ft_outline_cubic_to(GD_FT_Outline *ft, double x1, double y1, double x2, double y2, + double x3, double y3) +{ + ft->points[ft->n_points].x = FT_COORD(x1); + ft->points[ft->n_points].y = FT_COORD(y1); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_CUBIC; + ft->n_points++; + + ft->points[ft->n_points].x = FT_COORD(x2); + ft->points[ft->n_points].y = FT_COORD(y2); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_CUBIC; + ft->n_points++; + + ft->points[ft->n_points].x = FT_COORD(x3); + ft->points[ft->n_points].y = FT_COORD(y3); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_ON; + ft->n_points++; +} + +void gd_ft_outline_conic_to(GD_FT_Outline *ft, double x1, double y1, double x2, double y2) +{ + ft->points[ft->n_points].x = FT_COORD(x1); + ft->points[ft->n_points].y = FT_COORD(y1); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_CONIC; + ft->n_points++; + + ft->points[ft->n_points].x = FT_COORD(x2); + ft->points[ft->n_points].y = FT_COORD(y2); + ft->tags[ft->n_points] = GD_FT_CURVE_TAG_ON; + ft->n_points++; +} + +GD_FT_Outline *gd_ft_outline_convert(const gdPathPtr path, const gdPathMatrixPtr matrix) +{ + /* A path may begin with LineTo/CurveTo. The outline bridge treats that + prefix as an implicit contour before the first explicit MoveTo. */ + int contour_capacity = path->contours + 1; + GD_FT_Outline *outline = + gd_ft_outline_create(gdArrayNumElements(&path->points), contour_capacity); + gdPointF p[3]; + unsigned int numElements = gdArrayNumElements(&path->elements); + unsigned int pointsIndex = 0; + unsigned int i = 0; + int contour_open = 0; + + memset(p, 0, sizeof(gdPointF) * 3); + if (!outline) + return NULL; + for (i = 0; i < numElements; i++) { + gdPathOpsPtr element = (gdPathOpsPtr)gdArrayIndex(&path->elements, i); + gdPointFPtr point = gdArrayIndex(&path->points, pointsIndex); + switch (*element) { + case gdPathOpsMoveTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + gd_ft_outline_move_to(outline, p[0].x, p[0].y); + contour_open = 1; + pointsIndex += 1; + break; + case gdPathOpsLineTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + if (contour_open) + gd_ft_outline_line_to(outline, p[0].x, p[0].y); + else { + gd_ft_outline_move_to(outline, p[0].x, p[0].y); + contour_open = 1; + } + pointsIndex += 1; + break; + case gdPathOpsCubicTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + point = gdArrayIndex(&path->points, pointsIndex + 2); + gdPathMatrixMapPoint(matrix, point, &p[2]); + if (contour_open) + gd_ft_outline_cubic_to(outline, p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y); + else { + gd_ft_outline_move_to(outline, p[2].x, p[2].y); + contour_open = 1; + } + pointsIndex += 3; + break; + case gdPathOpsClose: + if (contour_open) { + gd_ft_outline_close(outline); + contour_open = 0; + } + pointsIndex += 1; + break; + case gdPathOpsQuadTo: + gdPathMatrixMapPoint(matrix, point, &p[0]); + point = gdArrayIndex(&path->points, pointsIndex + 1); + gdPathMatrixMapPoint(matrix, point, &p[1]); + if (contour_open) + gd_ft_outline_conic_to(outline, p[0].x, p[0].y, p[1].x, p[1].y); + else { + gd_ft_outline_move_to(outline, p[1].x, p[1].y); + contour_open = 1; + } + pointsIndex += 2; + break; + } + } + + gd_ft_outline_end(outline); + return outline; +} + +void gd_ft_outline_destroy(GD_FT_Outline *ft) +{ + gdFree(ft->points); + gdFree(ft->tags); + gdFree(ft->contours); + gdFree(ft->contours_flag); + gdFree(ft); +} + +static void generation_callback(int count, const GD_FT_Span *spans, void *user) +{ + gdSpanRlePtr rle = user; + _rle_spans_allocate(rle->spans, count); + gdSpanPtr data = rle->spans.data + rle->spans.size; + memcpy(data, spans, (size_t)count * sizeof(gdSpan)); + rle->spans.size += count; +} + +static void bbox_callback(int x, int y, int w, int h, void *user) +{ + gdSpanRlePtr rle = user; + rle->x = x; + rle->y = y; + rle->w = w; + rle->h = h; +} + +static void _rasterize_fill(gdSpanRlePtr rle, const gdPathPtr path, const gdPathMatrixPtr matrix, + const gdRectFPtr clip, gdFillRule winding) +{ + static gdPathMatrix identity_matrix = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0}; + GD_FT_Raster_Params params = {0}; + params.flags = GD_FT_RASTER_FLAG_DIRECT | GD_FT_RASTER_FLAG_AA; + params.gray_spans = generation_callback; + params.bbox_cb = bbox_callback; + params.user = rle; + + if (clip) { + params.flags |= GD_FT_RASTER_FLAG_CLIP; + params.clip_box.xMin = (GD_FT_Pos)clip->x; + params.clip_box.yMin = (GD_FT_Pos)clip->y; + params.clip_box.xMax = (GD_FT_Pos)(clip->x + clip->w); + params.clip_box.yMax = (GD_FT_Pos)(clip->y + clip->h); + } + + gd_ft_raster_render_path(path, matrix ? matrix : &identity_matrix, ¶ms, + winding == gdFillRuleEvenOdd ? GD_FT_OUTLINE_EVEN_ODD_FILL + : GD_FT_OUTLINE_NONE); +} + +void gdSpanRleRasterize(gdSpanRlePtr rle, const gdPathPtr path, const gdPathMatrixPtr matrix, + const gdRectFPtr clip, const gdStrokePtr stroke, gdFillRule winding) +{ + if (stroke && stroke->width > 0) { + gdPathPtr pathToStroke = (gdPathPtr)path; + + // Apply dash pattern to original path BEFORE stroke conversion + if (stroke->dash) { + pathToStroke = gdPathApplyDash(stroke->dash, path); + if (!pathToStroke) + return; + } + + gdPathPtr strokePath = gdPathStrokeToPath(pathToStroke, stroke, matrix); + if (!strokePath) { + if (pathToStroke != path) + gdPathDestroy(pathToStroke); + return; + } + + if (pathToStroke != path) + gdPathDestroy(pathToStroke); + + _rasterize_fill(rle, strokePath, NULL, clip, winding); + gdPathDestroy(strokePath); + } else { + _rasterize_fill(rle, path, matrix, clip, winding); + } +} diff --git a/ext/gd/libgd/gd_span_rle.h b/ext/gd/libgd/gd_span_rle.h new file mode 100644 index 000000000000..99eb94c3f70c --- /dev/null +++ b/ext/gd/libgd/gd_span_rle.h @@ -0,0 +1,35 @@ +#ifndef GD_SPAN_RLE_H +#define GD_SPAN_RLE_H + +#include "gd_vector2d_private.h" + +#define _rle_spans_init(rle_s) \ + do { \ + rle_s.data = NULL; \ + rle_s.size = 0; \ + rle_s.capacity = 0; \ + } while (0) + +#define _rle_spans_allocate(rle_s, count) \ + do { \ + if (rle_s.size + count > rle_s.capacity) { \ + int capacity = rle_s.size + count; \ + int newcapacity = rle_s.capacity == 0 ? 8 : rle_s.capacity; \ + while (newcapacity < capacity) { \ + newcapacity *= 2; \ + } \ + rle_s.data = gdRealloc(rle_s.data, (size_t)newcapacity * sizeof(rle_s.data[0])); \ + rle_s.capacity = newcapacity; \ + } \ + } while (0) + +gdSpanRlePtr gdSpanRleCreate(); +gdSpanRlePtr gdSpanRleRetain(gdSpanRlePtr rle); +void gdSpanRleDestroy(gdSpanRlePtr rle); +void gdSpanRleClear(gdSpanRlePtr rle); +void gdSpanRleRasterize(gdSpanRlePtr rle, const gdPathPtr path, const gdPathMatrixPtr matrix, + const gdRectFPtr clip, const gdStrokePtr stroke, gdFillRule winding); +void gdSpanRlePathClip(gdSpanRlePtr rle, const gdSpanRlePtr clip); +gdSpanRlePtr gdSpanRleClone(gdSpanRlePtr rle); +gdSpanRlePtr gdSpanHorizontalClip(const gdSpanRlePtr a, const gdSpanRlePtr b); +#endif // GD_SPAN_RLE_H diff --git a/ext/gd/libgd/gd_ss.c b/ext/gd/libgd/gd_ss.c index 048ac0a70085..affbaec3e873 100644 --- a/ext/gd/libgd/gd_ss.c +++ b/ext/gd/libgd/gd_ss.c @@ -1,15 +1,16 @@ -#include +#include "gd.h" +#include "gd_errors.h" #include -#include +#include #include -#include "gd.h" +#include #define TRUE 1 #define FALSE 0 /* Exported functions: */ -extern void gdImagePngToSink (gdImagePtr im, gdSinkPtr out); -extern gdImagePtr gdImageCreateFromPngSource (gdSourcePtr inSource); +extern void gdImagePngToSink(gdImagePtr im, gdSinkPtr out); +extern gdImagePtr gdImageCreateFromPngSource(gdSourcePtr inSource); /* Use this for commenting out debug-print statements. */ /* Just use the first '#define' to allow all the prints... */ @@ -17,33 +18,45 @@ extern gdImagePtr gdImageCreateFromPngSource (gdSourcePtr inSource); #define GD_SS_DBG(s) #ifdef HAVE_LIBPNG -void gdImagePngToSink (gdImagePtr im, gdSinkPtr outSink) +/* + Function: gdImagePngToSink +*/ +BGD_DECLARE(void) gdImagePngToSink(gdImagePtr im, gdSinkPtr outSink) { - gdIOCtx *out = gdNewSSCtx(NULL, outSink); - gdImagePngCtx(im, out); - out->gd_free(out); + gdIOCtx *out = gdNewSSCtx(NULL, outSink); + gdImagePngCtx(im, out); + out->gd_free(out); } -gdImagePtr gdImageCreateFromPngSource (gdSourcePtr inSource) +/* + Function: gdImageCreateFromPngSource + + See for documentation. This is obsolete; use + instead. + */ +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngSource(gdSourcePtr inSource) { - gdIOCtx *in = gdNewSSCtx(inSource, NULL); - gdImagePtr im; + gdIOCtx *in = gdNewSSCtx(inSource, NULL); + gdImagePtr im; - im = gdImageCreateFromPngCtx(in); + im = gdImageCreateFromPngCtx(in); - in->gd_free(in); + in->gd_free(in); - return im; + return im; } -#else /* no HAVE_LIBPNG */ -void gdImagePngToSink (gdImagePtr im, gdSinkPtr outSink) +#else /* no HAVE_LIBPNG */ +BGD_DECLARE(void) gdImagePngToSink(gdImagePtr im, gdSinkPtr outSink) { - gd_error("PNG support is not available"); + (void)im; + (void)outSink; + gd_error("PNG support is not available\n"); } -gdImagePtr gdImageCreateFromPngSource (gdSourcePtr inSource) + +BGD_DECLARE(gdImagePtr) gdImageCreateFromPngSource(gdSourcePtr inSource) { - gd_error("PNG support is not available"); - return NULL; + (void)inSource; + gd_error("PNG support is not available\n"); + return NULL; } #endif /* HAVE_LIBPNG */ - diff --git a/ext/gd/libgd/gd_surface.c b/ext/gd/libgd/gd_surface.c new file mode 100644 index 000000000000..fe157032aab9 --- /dev/null +++ b/ext/gd/libgd/gd_surface.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd_errors.h" +#include "gd_intern.h" +#include "gd_vector2d_private.h" +#include "gdhelpers.h" + +#include "gd_array.h" +#include "gd_surface.h" + +static int checkOverflowAndType(int width, int height, unsigned type) +{ + int bytes_per_pixel; + if (type == GD_SURFACE_NONE || type >= GD_SURFACE_COUNT || width <= 0 || height <= 0) { + gd_error("gdSurface: invalid dimensions or surface type\n"); + return 1; + } + bytes_per_pixel = type == GD_SURFACE_A8 ? 1 : 4; + if (overflow2(width, bytes_per_pixel) || overflow2(width * bytes_per_pixel, height)) { + return 1; + } + return 0; +} + +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceCreate(int width, int height, unsigned int type) +{ + gdSurfacePtr surface; + + if (type >= GD_SURFACE_COUNT) { + return NULL; + } + if (checkOverflowAndType(width, height, type)) { + return NULL; + } + surface = gdMalloc(sizeof(gdSurface)); + if (!surface) { + return NULL; + } + + const int bytes_per_pixel = type == GD_SURFACE_A8 ? 1 : 4; + const size_t size = (size_t)width * (size_t)height * (size_t)bytes_per_pixel; + surface->data = gdCalloc(1, size); + if (!surface->data) { + gdFree(surface); + return NULL; + } + surface->type = type; + surface->ref = 1; + surface->gdOwned = 1; + surface->width = width; + surface->height = height; + surface->stride = width * bytes_per_pixel; + return surface; +} + +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceCreateForData(unsigned char *data, int width, int height, + int stride, unsigned int type) +{ + gdSurfacePtr surface; + + if (!data) { + return NULL; + } + if (checkOverflowAndType(width, height, type)) { + return NULL; + } + if (stride < width * (type == GD_SURFACE_A8 ? 1 : 4)) { + return NULL; + } + + surface = gdMalloc(sizeof(gdSurface)); + if (!surface) { + return NULL; + } + surface->ref = 1; + surface->gdOwned = 0; + surface->data = data; + surface->width = width; + surface->height = height; + surface->stride = stride; + surface->type = type; + return surface; +} + +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceAddRef(gdSurfacePtr surface) +{ + if (surface == NULL) { + return NULL; + } + surface->ref++; + return surface; +} + +GD_VECTOR2D_INTERNAL unsigned char *gdSurfaceGetData(const gdSurfacePtr surface) +{ + if (surface == NULL) { + return NULL; + } + return surface->data; +} + +GD_VECTOR2D_INTERNAL gdSurfaceType gdSurfaceGetType(const gdSurfacePtr surface) +{ + if (surface == NULL) { + return GD_SURFACE_NONE; + } + return surface->type; +} + +GD_VECTOR2D_INTERNAL int gdSurfaceGetWidth(const gdSurfacePtr surface) { return surface->width; } + +GD_VECTOR2D_INTERNAL int gdSurfaceGetHeight(const gdSurfacePtr surface) { return surface->height; } + +GD_VECTOR2D_INTERNAL int gdSurfaceGetStride(const gdSurfacePtr surface) { return surface->stride; } + +GD_VECTOR2D_INTERNAL void gdSurfaceDestroy(gdSurfacePtr surface) +{ + if (!surface) { + return; + } + if (--surface->ref == 0) { + if (surface->gdOwned) { + gdFree(surface->data); + } + gdFree(surface); + } +} diff --git a/ext/gd/libgd/gd_surface.h b/ext/gd/libgd/gd_surface.h new file mode 100644 index 000000000000..85446d49dccc --- /dev/null +++ b/ext/gd/libgd/gd_surface.h @@ -0,0 +1,6 @@ +#ifndef GD_SURFACE_H +#define GD_SURFACE_H + +#include "gd_vector2d_private.h" + +#endif // GD_SURFACE_H diff --git a/ext/gd/libgd/gd_tga.c b/ext/gd/libgd/gd_tga.c index f888f9ea6fbe..14aefcdeae0b 100644 --- a/ext/gd/libgd/gd_tga.c +++ b/ext/gd/libgd/gd_tga.c @@ -8,356 +8,550 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#include #include +#include #include #include -#include "gd_tga.h" #include "gd.h" #include "gd_errors.h" +#include "gd_io.h" +#include "gd_tga.h" #include "gdhelpers.h" +static int tga_is_rle(uint8_t imagetype); +static int tga_pixel_size(uint8_t bits); +static int tga_read_color_map(gdIOCtx *ctx, oTga *tga); +static int tga_read_pixel(gdIOCtx *ctx, oTga *tga, int *pixel); +static void tga_apply_attribute_type(gdIOCtx *ctx, oTga *tga, int pixel_count); +static int tga_decode_color(const unsigned char *buf, int bits, int alpha_bits, int *has_alpha); +static int tga_decode_16(unsigned int value, int alpha_bits, int *has_alpha); +static int tga_scale_5_to_8(int c); +static int tga_alpha_8_to_gd(int a); +static void tga_strip_alpha(oTga *tga, int pixel_count); + /* - Function: gdImageCreateFromTga + Function: gdImageCreateFromTga - Creates a gdImage from a TGA file + Creates a gdImage from a TGA file - Parameters: + Parameters: - infile - Pointer to TGA binary file + infile - Pointer to TGA binary file */ -gdImagePtr gdImageCreateFromTga(FILE *fp) +BGD_DECLARE(gdImagePtr) gdImageCreateFromTga(FILE *fp) { - gdImagePtr image; - gdIOCtx* in = gdNewFileCtx(fp); - if (in == NULL) return NULL; - image = gdImageCreateFromTgaCtx(in); - in->gd_free( in ); - return image; + gdImagePtr image; + gdIOCtx *in = gdNewFileCtx(fp); + if (in == NULL) + return NULL; + image = gdImageCreateFromTgaCtx(in); + in->gd_free(in); + return image; } /* - Function: gdImageCreateFromTgaPtr + Function: gdImageCreateFromTgaPtr */ -gdImagePtr gdImageCreateFromTgaPtr(int size, void *data) +BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaPtr(int size, void *data) { - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx (size, data, 0); - if (in == NULL) return NULL; - im = gdImageCreateFromTgaCtx(in); - in->gd_free(in); - return im; + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) + return NULL; + im = gdImageCreateFromTgaCtx(in); + in->gd_free(in); + return im; } - /* - Function: gdImageCreateFromTgaCtx + Function: gdImageCreateFromTgaCtx - Creates a gdImage from a gdIOCtx referencing a TGA binary file. + Creates a gdImage from a gdIOCtx referencing a TGA binary file. - Parameters: - ctx - Pointer to a gdIOCtx structure + Parameters: + ctx - Pointer to a gdIOCtx structure */ -gdImagePtr gdImageCreateFromTgaCtx(gdIOCtx* ctx) +BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaCtx(gdIOCtx *ctx) { - int bitmap_caret = 0; - oTga *tga = NULL; - /* int pixel_block_size = 0; - int image_block_size = 0; */ - volatile gdImagePtr image = NULL; - int x = 0; - int y = 0; - - tga = (oTga *) gdMalloc(sizeof(oTga)); - if (!tga) { - return NULL; - } - - tga->bitmap = NULL; - tga->ident = NULL; - - if (read_header_tga(ctx, tga) < 0) { - free_tga(tga); - return NULL; - } - - /*TODO: Will this be used? - pixel_block_size = tga->bits / 8; - image_block_size = (tga->width * tga->height) * pixel_block_size; - */ - - if (read_image_tga(ctx, tga) < 0) { - free_tga(tga); - return NULL; - } - - image = gdImageCreateTrueColor((int)tga->width, (int)tga->height ); - - if (image == 0) { - free_tga( tga ); - return NULL; - } - - /*! \brief Populate GD image object - * Copy the pixel data from our tga bitmap buffer into the GD image - * Disable blending and save the alpha channel per default - */ - if (tga->alphabits) { - gdImageAlphaBlending(image, 0); - gdImageSaveAlpha(image, 1); - } - - /* TODO: use alphabits as soon as we support 24bit and other alpha bps (ie != 8bits) */ - for (y = 0; y < tga->height; y++) { - register int *tpix = image->tpixels[y]; - for ( x = 0; x < tga->width; x++, tpix++) { - if (tga->bits == TGA_BPP_24) { - *tpix = gdTrueColor(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret]); - bitmap_caret += 3; - } else if (tga->bits == TGA_BPP_32 && tga->alphabits) { - register int a = tga->bitmap[bitmap_caret + 3]; - - *tpix = gdTrueColorAlpha(tga->bitmap[bitmap_caret + 2], tga->bitmap[bitmap_caret + 1], tga->bitmap[bitmap_caret], gdAlphaMax - (a >> 1)); - bitmap_caret += 4; - } - } - } - - if (tga->flipv && tga->fliph) { - gdImageFlipBoth(image); - } else if (tga->flipv) { - gdImageFlipVertical(image); - } else if (tga->fliph) { - gdImageFlipHorizontal(image); - } - - free_tga(tga); - - return image; + int bitmap_caret = 0; + oTga *tga = NULL; + volatile gdImagePtr image = NULL; + int x = 0; + int y = 0; + + if (ctx == NULL) { + return NULL; + } + + tga = (oTga *)gdMalloc(sizeof(oTga)); + if (!tga) { + return NULL; + } + + tga->bitmap = NULL; + tga->colormap = NULL; + tga->ident = NULL; + tga->has_alpha = 0; + + if (read_header_tga(ctx, tga) < 0) { + free_tga(tga); + return NULL; + } + + if (read_image_tga(ctx, tga) < 0) { + free_tga(tga); + return NULL; + } + + image = gdImageCreateTrueColor((int)tga->width, (int)tga->height); + + if (image == 0) { + free_tga(tga); + return NULL; + } + + /*! \brief Populate GD image object + * Copy the pixel data from our tga bitmap buffer into the GD image + * Disable blending and save the alpha channel per default + */ + if (tga->has_alpha) { + gdImageAlphaBlending(image, 0); + gdImageSaveAlpha(image, 1); + } + + for (y = 0; y < tga->height; y++) { + register int *tpix = image->tpixels[y]; + for (x = 0; x < tga->width; x++, tpix++) { + *tpix = tga->bitmap[bitmap_caret++]; + } + } + + if (tga->flipv && tga->fliph) { + gdImageFlipBoth(image); + } else if (tga->flipv) { + gdImageFlipVertical(image); + } else if (tga->fliph) { + gdImageFlipHorizontal(image); + } + + free_tga(tga); + + return image; } /*! \brief Reads a TGA header. - * Reads the header block from a binary TGA file populating the referenced TGA structure. - * \param ctx Pointer to TGA binary file - * \param tga Pointer to TGA structure - * \return int 1 on success, -1 on failure + * Reads the header block from a binary TGA file populating the referenced TGA + *structure. \param ctx Pointer to TGA binary file \param tga Pointer to TGA + *structure \return int 1 on sucess, -1 on failure */ int read_header_tga(gdIOCtx *ctx, oTga *tga) { - unsigned char header[18]; - - if (gdGetBuf(header, sizeof(header), ctx) < 18) { - gd_error("Fail to read header"); - return -1; - } - - tga->identsize = header[0]; - tga->colormaptype = header[1]; - tga->imagetype = header[2]; - tga->colormapstart = header[3] + (header[4] << 8); - tga->colormaplength = header[5] + (header[6] << 8); - tga->colormapbits = header[7]; - tga->xstart = header[8] + (header[9] << 8); - tga->ystart = header[10] + (header[11] << 8); - tga->width = header[12] + (header[13] << 8); - tga->height = header[14] + (header[15] << 8); - tga->bits = header[16]; - tga->alphabits = header[17] & 0x0f; - tga->fliph = (header[17] & 0x10) ? 1 : 0; - tga->flipv = (header[17] & 0x20) ? 0 : 1; + unsigned char header[18]; + + if (gdGetBuf(header, sizeof(header), ctx) < 18) { + gd_error("fail to read header"); + return -1; + } + + tga->identsize = header[0]; + tga->colormaptype = header[1]; + tga->imagetype = header[2]; + tga->colormapstart = header[3] + (header[4] << 8); + tga->colormaplength = header[5] + (header[6] << 8); + tga->colormapbits = header[7]; + tga->xstart = header[8] + (header[9] << 8); + tga->ystart = header[10] + (header[11] << 8); + tga->width = header[12] + (header[13] << 8); + tga->height = header[14] + (header[15] << 8); + tga->bits = header[16]; + tga->alphabits = header[17] & 0x0f; + tga->fliph = (header[17] & 0x10) ? 1 : 0; + tga->flipv = (header[17] & 0x20) ? 0 : 1; + tga->has_alpha = 0; #ifdef DEBUG - printf("format bps: %i\n", tga->bits); - printf("flip h/v: %i / %i\n", tga->fliph, tga->flipv); - printf("alpha: %i\n", tga->alphabits); - printf("wxh: %i %i\n", tga->width, tga->height); + printf("format bps: %i\n", tga->bits); + printf("flip h/v: %i / %i\n", tga->fliph, tga->flipv); + printf("alpha: %i\n", tga->alphabits); + printf("wxh: %i %i\n", tga->width, tga->height); #endif - if (!((tga->bits == TGA_BPP_24 && tga->alphabits == 0) - || (tga->bits == TGA_BPP_32 && tga->alphabits == 8))) - { - gd_error_ex(GD_WARNING, "gd-tga: %u bits per pixel with %u alpha bits not supported\n", - tga->bits, tga->alphabits); - return -1; - } - - tga->ident = NULL; - - if (tga->identsize > 0) { - tga->ident = (char *) gdMalloc(tga->identsize * sizeof(char)); - if(tga->ident == NULL) { - return -1; - } - - if (gdGetBuf(tga->ident, tga->identsize, ctx) != tga->identsize) { - gd_error("fail to read header ident"); - return -1; - } - } - - return 1; + if (tga->width <= 0 || tga->height <= 0) { + gd_error("gd-tga: invalid image dimensions\n"); + return -1; + } + + if (tga->colormaptype > 1) { + gd_error_ex(GD_WARNING, "gd-tga: unsupported color map type %u\n", tga->colormaptype); + return -1; + } + + switch (tga->imagetype) { + case TGA_TYPE_INDEXED: + case TGA_TYPE_INDEXED_RLE: + if (tga->colormaptype != 1 || tga->bits != TGA_BPP_8 || + !(tga->colormapbits == 15 || tga->colormapbits == 16 || tga->colormapbits == 24 || + tga->colormapbits == 32)) { + gd_error_ex(GD_WARNING, "gd-tga: unsupported color mapped image\n"); + return -1; + } + break; + + case TGA_TYPE_RGB: + case TGA_TYPE_RGB_RLE: + if (!(tga->bits == TGA_BPP_16 || tga->bits == TGA_BPP_24 || + (tga->bits == TGA_BPP_32 && tga->alphabits == 8))) { + gd_error_ex(GD_WARNING, "gd-tga: unsupported truecolor depth %u\n", tga->bits); + return -1; + } + break; + + case TGA_TYPE_GREYSCALE: + case TGA_TYPE_GREYSCALE_RLE: + if (tga->bits != TGA_BPP_8) { + gd_error_ex(GD_WARNING, "gd-tga: unsupported grayscale depth %u\n", tga->bits); + return -1; + } + break; + + default: + gd_error_ex(GD_WARNING, "gd-tga: unsupported image type %u\n", tga->imagetype); + return -1; + } + + tga->ident = NULL; + + if (tga->identsize > 0) { + tga->ident = (char *)gdMalloc(tga->identsize * sizeof(char)); + if (tga->ident == NULL) { + return -1; + } + + if (gdGetBuf(tga->ident, tga->identsize, ctx) != tga->identsize) { + gd_error("fail to read header ident"); + return -1; + } + } + + return 1; } /*! \brief Reads a TGA image data into buffer. - * Reads the image data block from a binary TGA file populating the referenced TGA structure. - * \param ctx Pointer to TGA binary file - * \param tga Pointer to TGA structure - * \return int 0 on success, -1 on failure + * Reads the image data block from a binary TGA file populating the referenced + *TGA structure. \param ctx Pointer to TGA binary file \param tga Pointer to TGA + *structure \return int 0 on sucess, -1 on failure */ -int read_image_tga( gdIOCtx *ctx, oTga *tga ) +int read_image_tga(gdIOCtx *ctx, oTga *tga) +{ + int pixel_count; + int bitmap_caret = 0; + + if (overflow2(tga->width, tga->height)) { + return -1; + } + + pixel_count = tga->width * tga->height; + if (overflow2(pixel_count, sizeof(int))) { + return -1; + } + + if (tga_read_color_map(ctx, tga) < 0) { + return -1; + } + + /*! \brief Allocate memory for image block + * Allocate a chunk of memory for the image block to be passed into. + */ + tga->bitmap = (int *)gdMalloc(pixel_count * sizeof(int)); + if (tga->bitmap == NULL) + return -1; + + if (tga_is_rle(tga->imagetype)) { + while (bitmap_caret < pixel_count) { + int packet = gdGetC(ctx); + int encoded_pixels; + int pixel; + int i; + + if (packet == EOF) { + gd_error("gd-tga: premature end of image data\n"); + return -1; + } + + encoded_pixels = (packet & ~TGA_RLE_FLAG) + 1; + if (encoded_pixels > pixel_count - bitmap_caret) { + return -1; + } + + if ((packet & TGA_RLE_FLAG) == TGA_RLE_FLAG) { + if (tga_read_pixel(ctx, tga, &pixel) < 0) { + return -1; + } + for (i = 0; i < encoded_pixels; i++) { + tga->bitmap[bitmap_caret++] = pixel; + } + } else { + for (i = 0; i < encoded_pixels; i++) { + if (tga_read_pixel(ctx, tga, &pixel) < 0) { + return -1; + } + tga->bitmap[bitmap_caret++] = pixel; + } + } + } + } else { + while (bitmap_caret < pixel_count) { + if (tga_read_pixel(ctx, tga, &tga->bitmap[bitmap_caret]) < 0) { + return -1; + } + bitmap_caret++; + } + } + + tga_apply_attribute_type(ctx, tga, pixel_count); + + return 1; +} + +static int tga_is_rle(uint8_t imagetype) +{ + return imagetype == TGA_TYPE_INDEXED_RLE || imagetype == TGA_TYPE_RGB_RLE || + imagetype == TGA_TYPE_GREYSCALE_RLE; +} + +static int tga_pixel_size(uint8_t bits) { return (bits + 7) / 8; } + +static int tga_read_color_map(gdIOCtx *ctx, oTga *tga) +{ + int i; + int has_alpha = 0; + int entry_size; + + if (tga->colormaptype == 0) { + return 1; + } + + if (tga->colormaplength <= 0) { + return -1; + } + + if (overflow2(tga->colormaplength, sizeof(int))) { + return -1; + } + + entry_size = tga_pixel_size(tga->colormapbits); + tga->colormap = (int *)gdMalloc(tga->colormaplength * sizeof(int)); + if (tga->colormap == NULL) { + return -1; + } + + for (i = 0; i < tga->colormaplength; i++) { + unsigned char buf[4] = {0, 0, 0, 0}; + + if (gdGetBuf(buf, entry_size, ctx) != entry_size) { + gd_error("gd-tga: premature end of color map data\n"); + return -1; + } + + tga->colormap[i] = + tga_decode_color(buf, tga->colormapbits, tga->colormapbits == 32 ? 8 : 0, &has_alpha); + } + + if (has_alpha) { + tga->has_alpha = 1; + } + + return 1; +} + +static int tga_read_pixel(gdIOCtx *ctx, oTga *tga, int *pixel) +{ + unsigned char buf[4] = {0, 0, 0, 0}; + int size = tga_pixel_size(tga->bits); + int has_alpha = 0; + + if (gdGetBuf(buf, size, ctx) != size) { + gd_error("gd-tga: premature end of image data\n"); + return -1; + } + + switch (tga->imagetype) { + case TGA_TYPE_INDEXED: + case TGA_TYPE_INDEXED_RLE: { + int index = buf[0] - tga->colormapstart; + + if (index < 0 || index >= tga->colormaplength || tga->colormap == NULL) { + return -1; + } + *pixel = tga->colormap[index]; + break; + } + + case TGA_TYPE_RGB: + case TGA_TYPE_RGB_RLE: + *pixel = tga_decode_color(buf, tga->bits, tga->alphabits, &has_alpha); + if (has_alpha) { + tga->has_alpha = 1; + } + break; + + case TGA_TYPE_GREYSCALE: + case TGA_TYPE_GREYSCALE_RLE: + *pixel = gdTrueColor(buf[0], buf[0], buf[0]); + break; + + default: + return -1; + } + + return 1; +} + +static void tga_apply_attribute_type(gdIOCtx *ctx, oTga *tga, int pixel_count) +{ + static const unsigned char signature[18] = {'T', 'R', 'U', 'E', 'V', 'I', 'S', 'I', 'O', + 'N', '-', 'X', 'F', 'I', 'L', 'E', '.', '\0'}; + unsigned char footer_ring[26]; + unsigned char footer[26]; + size_t trailing_size = 0; + size_t i; + int c; + long start; + uint32_t extension_offset; + uint32_t attr_type_offset; + uint32_t end_offset; + int attr_type; + + if (!tga->has_alpha) { + return; + } + + if (ctx->tell == NULL || ctx->seek == NULL) { + return; + } + + start = gdTell(ctx); + if (start < 0 || start > INT_MAX) { + return; + } + + while ((c = gdGetC(ctx)) != EOF) { + footer_ring[trailing_size % sizeof(footer_ring)] = (unsigned char)c; + if (trailing_size == SIZE_MAX) { + return; + } + trailing_size++; + } + + if (trailing_size < sizeof(footer)) { + return; + } + + for (i = 0; i < sizeof(footer); i++) { + footer[i] = footer_ring[(trailing_size + i) % sizeof(footer_ring)]; + } + if (memcmp(footer + 8, signature, sizeof(signature)) != 0) { + return; + } + + extension_offset = (uint32_t)footer[0] | ((uint32_t)footer[1] << 8) | + ((uint32_t)footer[2] << 16) | ((uint32_t)footer[3] << 24); + if (extension_offset == 0 || extension_offset < (uint32_t)start || + extension_offset > (uint32_t)INT_MAX - 494) { + return; + } + + attr_type_offset = extension_offset + 494; + if (trailing_size > (size_t)INT_MAX - (size_t)start || + attr_type_offset >= (uint32_t)start + (uint32_t)trailing_size - sizeof(footer)) { + return; + } + end_offset = (uint32_t)start + (uint32_t)trailing_size; + + if (!gdSeek(ctx, (int)attr_type_offset)) { + return; + } + attr_type = gdGetC(ctx); + gdSeek(ctx, (int)end_offset); + if (attr_type == EOF) { + return; + } + if (attr_type != 3 && attr_type != 4) { + tga_strip_alpha(tga, pixel_count); + } +} + +static int tga_decode_color(const unsigned char *buf, int bits, int alpha_bits, int *has_alpha) +{ + switch (bits) { + case 15: + case 16: + return tga_decode_16((unsigned int)buf[0] | ((unsigned int)buf[1] << 8), alpha_bits, + has_alpha); + + case 24: + return gdTrueColor(buf[2], buf[1], buf[0]); + + case 32: + if (has_alpha) { + *has_alpha = 1; + } + return gdTrueColorAlpha(buf[2], buf[1], buf[0], tga_alpha_8_to_gd(buf[3])); + } + + return 0; +} + +static int tga_decode_16(unsigned int value, int alpha_bits, int *has_alpha) { - int pixel_block_size = (tga->bits / 8); - int image_block_size; - int* decompression_buffer = NULL; - unsigned char* conversion_buffer = NULL; - int buffer_caret = 0; - int bitmap_caret = 0; - int i = 0; - int encoded_pixels; - int rle_size; - - if(overflow2(tga->width, tga->height)) { - return -1; - } - - if(overflow2(tga->width * tga->height, pixel_block_size)) { - return -1; - } - - image_block_size = (tga->width * tga->height) * pixel_block_size; - if(overflow2(image_block_size, sizeof(int))) { - return -1; - } - - /*! \todo Add more image type support. - */ - if (tga->imagetype != TGA_TYPE_RGB && tga->imagetype != TGA_TYPE_RGB_RLE) - return -1; - - /*! \brief Allocate memmory for image block - * Allocate a chunk of memory for the image block to be passed into. - */ - tga->bitmap = (int *) gdMalloc(image_block_size * sizeof(int)); - if (tga->bitmap == NULL) - return -1; - - switch (tga->imagetype) { - case TGA_TYPE_RGB: - /*! \brief Read in uncompressed RGB TGA - * Chunk load the pixel data from an uncompressed RGB type TGA. - */ - conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char)); - if (conversion_buffer == NULL) { - return -1; - } - - if (gdGetBuf(conversion_buffer, image_block_size, ctx) != image_block_size) { - gd_error("gd-tga: premature end of image data\n"); - gdFree(conversion_buffer); - return -1; - } - - while (buffer_caret < image_block_size) { - tga->bitmap[buffer_caret] = (int) conversion_buffer[buffer_caret]; - buffer_caret++; - } - - gdFree(conversion_buffer); - break; - - case TGA_TYPE_RGB_RLE: - /*! \brief Read in RLE compressed RGB TGA - * Chunk load the pixel data from an RLE compressed RGB type TGA. - */ - decompression_buffer = (int*) gdMalloc(image_block_size * sizeof(int)); - if (decompression_buffer == NULL) { - return -1; - } - conversion_buffer = (unsigned char *) gdMalloc(image_block_size * sizeof(unsigned char)); - if (conversion_buffer == NULL) { - gd_error("gd-tga: premature end of image data\n"); - gdFree( decompression_buffer ); - return -1; - } - - rle_size = gdGetBuf(conversion_buffer, image_block_size, ctx); - if (rle_size <= 0) { - gdFree(conversion_buffer); - gdFree(decompression_buffer); - return -1; - } - - buffer_caret = 0; - - while( buffer_caret < rle_size) { - decompression_buffer[buffer_caret] = (int)conversion_buffer[buffer_caret]; - buffer_caret++; - } - - buffer_caret = 0; - - while( bitmap_caret < image_block_size ) { - - if (buffer_caret + pixel_block_size > rle_size) { - gdFree( decompression_buffer ); - gdFree( conversion_buffer ); - return -1; - } - - if ((decompression_buffer[buffer_caret] & TGA_RLE_FLAG) == TGA_RLE_FLAG) { - encoded_pixels = ( ( decompression_buffer[ buffer_caret ] & ~TGA_RLE_FLAG ) + 1 ); - buffer_caret++; - - if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size - || buffer_caret + pixel_block_size > rle_size) { - gdFree( decompression_buffer ); - gdFree( conversion_buffer ); - return -1; - } - - for (i = 0; i < encoded_pixels; i++) { - memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, pixel_block_size * sizeof(int)); - bitmap_caret += pixel_block_size; - } - buffer_caret += pixel_block_size; - - } else { - encoded_pixels = decompression_buffer[ buffer_caret ] + 1; - buffer_caret++; - - if ((bitmap_caret + (encoded_pixels * pixel_block_size)) > image_block_size - || buffer_caret + (encoded_pixels * pixel_block_size) > rle_size) { - gdFree( decompression_buffer ); - gdFree( conversion_buffer ); - return -1; - } - - memcpy(tga->bitmap + bitmap_caret, decompression_buffer + buffer_caret, encoded_pixels * pixel_block_size * sizeof(int)); - bitmap_caret += (encoded_pixels * pixel_block_size); - buffer_caret += (encoded_pixels * pixel_block_size); - } - } - gdFree( decompression_buffer ); - gdFree( conversion_buffer ); - break; - } - - return 1; + int b = tga_scale_5_to_8(value & 0x1f); + int g = tga_scale_5_to_8((value >> 5) & 0x1f); + int r = tga_scale_5_to_8((value >> 10) & 0x1f); + + if (alpha_bits > 0) { + int a = (value & 0x8000) ? gdAlphaOpaque : gdAlphaTransparent; + + if (has_alpha) { + *has_alpha = 1; + } + return gdTrueColorAlpha(r, g, b, a); + } + + return gdTrueColor(r, g, b); +} + +static int tga_scale_5_to_8(int c) { return (c * 255) / 31; } + +static int tga_alpha_8_to_gd(int a) { return gdAlphaMax - (a >> 1); } + +static void tga_strip_alpha(oTga *tga, int pixel_count) +{ + int i; + + for (i = 0; i < pixel_count; i++) { + int pixel = tga->bitmap[i]; + + tga->bitmap[i] = gdTrueColor(gdTrueColorGetRed(pixel), gdTrueColorGetGreen(pixel), + gdTrueColorGetBlue(pixel)); + } + tga->has_alpha = 0; } /*! \brief Cleans up a TGA structure. - * Dereferences the bitmap referenced in a TGA structure, then the structure itself - * \param tga Pointer to TGA structure + * Dereferences the bitmap referenced in a TGA structure, then the structure + *itself \param tga Pointer to TGA structure */ -void free_tga(oTga * tga) +void free_tga(oTga *tga) { - if (tga) { - if (tga->ident) - gdFree(tga->ident); - if (tga->bitmap) - gdFree(tga->bitmap); - gdFree(tga); - } + if (tga) { + if (tga->ident) + gdFree(tga->ident); + if (tga->bitmap) + gdFree(tga->bitmap); + if (tga->colormap) + gdFree(tga->colormap); + gdFree(tga); + } } diff --git a/ext/gd/libgd/gd_tga.h b/ext/gd/libgd/gd_tga.h index 297f3dc99d6d..539500f07407 100644 --- a/ext/gd/libgd/gd_tga.h +++ b/ext/gd/libgd/gd_tga.h @@ -1,5 +1,5 @@ #ifndef __TGA_H -#define __TGA_H 1 +#define __TGA_H 1 #include "gd.h" #include "gdhelpers.h" @@ -7,46 +7,49 @@ #include "gd_intern.h" typedef struct oTga_ { - uint8_t identsize; // size of ID field that follows 18 uint8_t header (0 usually) - uint8_t colormaptype; // type of colour map 0=none, 1=has palette [IGNORED] Adrian requested no support - uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed - - int colormapstart; // first colour map entry in palette [IGNORED] Adrian requested no support - int colormaplength; // number of colours in palette [IGNORED] Adrian requested no support - uint8_t colormapbits; // number of bits per palette entry 15,16,24,32 [IGNORED] Adrian requested no support - - int xstart; // image x origin - int ystart; // image y origin - int width; // image width in pixels - int height; // image height in pixels - uint8_t bits; // image bits per pixel 8,16,24,32 - uint8_t alphabits; // alpha bits (low 4bits of header 17) - uint8_t fliph; // horizontal or vertical - uint8_t flipv; // flip - char *ident; // identifcation tag string - int *bitmap; // bitmap data + uint8_t identsize; // size of ID field that follows 18 uint8_t header (0 + // usually) + uint8_t colormaptype; // type of colour map 0=none, 1=has palette + uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed + + int colormapstart; // first colour map entry in palette + int colormaplength; // number of colours in palette + uint8_t colormapbits; // number of bits per palette entry 15,16,24,32 + + int xstart; // image x origin + int ystart; // image y origin + int width; // image width in pixels + int height; // image height in pixels + uint8_t bits; // image bits per pixel 8,16,24,32 + uint8_t alphabits; // alpha bits (low 4bits of header 17) + uint8_t fliph; // horizontal or vertical + uint8_t flipv; // flip + uint8_t has_alpha; // decoded image contains alpha + char *ident; // identifcation tag string + int *bitmap; // bitmap data + int *colormap; // decoded color map } oTga; -#define TGA_TYPE_NO_IMAGE 0 -#define TGA_TYPE_INDEXED 1 -#define TGA_TYPE_RGB 2 -#define TGA_TYPE_GREYSCALE 3 -#define TGA_TYPE_INDEXED_RLE 9 -#define TGA_TYPE_RGB_RLE 10 -#define TGA_TYPE_GREYSCALE_RLE 11 -#define TGA_TYPE_INDEXED_HUFFMAN_DELTA_RLE 32 -#define TGA_TYPE_RGB_HUFFMAN_DELTA_QUADTREE_RLE 33 - -#define TGA_BPP_8 8 -#define TGA_BPP_16 16 -#define TGA_BPP_24 24 -#define TGA_BPP_32 32 - -#define TGA_RLE_FLAG 128 - -int read_header_tga(gdIOCtx *ctx, oTga *tga); -int read_image_tga(gdIOCtx *ctx, oTga *tga); +#define TGA_TYPE_NO_IMAGE 0 +#define TGA_TYPE_INDEXED 1 +#define TGA_TYPE_RGB 2 +#define TGA_TYPE_GREYSCALE 3 +#define TGA_TYPE_INDEXED_RLE 9 +#define TGA_TYPE_RGB_RLE 10 +#define TGA_TYPE_GREYSCALE_RLE 11 +#define TGA_TYPE_INDEXED_HUFFMAN_DELTA_RLE 32 +#define TGA_TYPE_RGB_HUFFMAN_DELTA_QUADTREE_RLE 33 + +#define TGA_BPP_8 8 +#define TGA_BPP_16 16 +#define TGA_BPP_24 24 +#define TGA_BPP_32 32 + +#define TGA_RLE_FLAG 128 + +int read_header_tga(gdIOCtxPtr ctx, oTga *tga); +int read_image_tga(gdIOCtxPtr ctx, oTga *tga); void free_tga(oTga *tga); #endif //__TGA_H diff --git a/ext/gd/libgd/gd_tiff.c b/ext/gd/libgd/gd_tiff.c new file mode 100644 index 000000000000..3b40b15c5faa --- /dev/null +++ b/ext/gd/libgd/gd_tiff.c @@ -0,0 +1,2422 @@ +/* + TIFF - Tagged Image File Format Encapsulation for GD Library + + gd_tiff.c + Copyright (C) Pierre-A. Joye, M. Retallack + + --------------------------------------------------------------------------- + ** + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. + ** + --------------------------------------------------------------------------- + Ctx code written by M. Retallack + + Todo: + + If we fail - cleanup + Writer: Use gd error function, overflow check may not be necessary as + we write our own data (check already done) + + Implement 2 color black/white saving using group4 fax compression + Implement function to specify encoding to use when writing tiff data + + ---------------------------------------------------------------------------- + */ + +/** + * File: TIFF IO + * + * Read and write TIFF images. + * + * Multi-page TIFF reading is supported via the gdTiffRead* API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd.h" +#include "gd_errors.h" +#include "gd_intern.h" +#include "gdfonts.h" +#include +#include +#include +#include + +#include "gdhelpers.h" + +#ifdef HAVE_LIBTIFF + +#include "tiff.h" +#include "tiffio.h" + +#define GD_SUCCESS 1 +#define GD_FAILURE 0 + +#define TRUE 1 +#define FALSE 0 + +/* I define those here until the new formats + * are commited. We can then rely on the global + * def + */ +#define GD_PALETTE 1 +#define GD_TRUECOLOR 2 +#define GD_GRAY 3 +#define GD_INDEXED 4 +#define GD_RGB 5 + +typedef struct tiff_handle { + int size; + int pos; + gdIOCtx *ctx; + int written; +} tiff_handle; + +/* + Functions for reading, writing and seeking in gdIOCtx + This allows for non-file i/o operations with no + explicit use of libtiff fileio wrapper functions + + Note: because libtiff requires random access, but gdIOCtx + only supports streams, all writes are buffered + into memory and written out on close, also all + reads are done from a memory mapped version of the + tiff (assuming one already exists) +*/ + +static tiff_handle *new_tiff_handle(gdIOCtx *g, int initial_size) +{ + tiff_handle *t; + + if (!g) { + gd_error("Cannot create a new tiff handle, missing Ctx argument"); + return NULL; + } + + t = (tiff_handle *)gdMalloc(sizeof(tiff_handle)); + if (!t) { + gd_error("Failed to allocate a new tiff handle"); + return NULL; + } + + t->size = initial_size; + t->pos = 0; + t->ctx = g; + t->written = 0; + + return t; +} + +/* TIFFReadWriteProc tiff_readproc - Will use gdIOCtx procs to read required + (previously written) TIFF file content */ +static tsize_t tiff_readproc(thandle_t clientdata, tdata_t data, tsize_t size) +{ + tiff_handle *th = (tiff_handle *)clientdata; + gdIOCtx *ctx = th->ctx; + + size = (ctx->getBuf)(ctx, data, size); + if (size > 0) { + th->pos += size; + } + + return size; +} + +/* TIFFReadWriteProc tiff_writeproc - Will use gdIOCtx procs to write out + TIFF data */ +static tsize_t tiff_writeproc(thandle_t clientdata, tdata_t data, tsize_t size) +{ + tiff_handle *th = (tiff_handle *)clientdata; + gdIOCtx *ctx = th->ctx; + + size = (ctx->putBuf)(ctx, data, size); + if (size > 0) { + th->pos += size; + } + if (th->pos > th->size) { + th->size = th->pos; + } + + return size; +} + +/* TIFFSeekProc tiff_seekproc + * used to move around the partially written TIFF */ +static toff_t tiff_seekproc(thandle_t clientdata, toff_t offset, int from) +{ + tiff_handle *th = (tiff_handle *)clientdata; + gdIOCtx *ctx = th->ctx; + int result; + + switch (from) { + default: + case SEEK_SET: + /* just use offset */ + break; + + case SEEK_END: + /* invert offset, so that it is from start, not end as supplied */ + offset = th->size + offset; + break; + + case SEEK_CUR: + /* add current position to translate it to 'from start', + * not from durrent as supplied + */ + offset += th->pos; + break; + } + + /* now, move pos in both io context and buf */ + if ((result = (ctx->seek)(ctx, offset))) { + th->pos = offset; + } + + return result ? offset : (toff_t)-1; +} + +/* TIFFCloseProc tiff_closeproc - used to finally close the TIFF file */ +static int tiff_closeproc(thandle_t clientdata) +{ + (void)clientdata; + /*tiff_handle *th = (tiff_handle *)clientdata; + gdIOCtx *ctx = th->ctx; + + (ctx->gd_free)(ctx);*/ + + return 0; +} + +/* TIFFSizeProc tiff_sizeproc */ +static toff_t tiff_sizeproc(thandle_t clientdata) +{ + tiff_handle *th = (tiff_handle *)clientdata; + return th->size; +} + +/* TIFFMapFileProc tiff_mapproc() */ +static int tiff_mapproc(thandle_t h, tdata_t *d, toff_t *o) +{ + (void)h; + (void)d; + (void)o; + return 0; +} + +/* TIFFUnmapFileProc tiff_unmapproc */ +static void tiff_unmapproc(thandle_t h, tdata_t d, toff_t o) +{ + (void)h; + (void)d; + (void)o; +} + +static int tiff_file_size(FILE *fp) +{ + long current_pos; + long end_pos; + + if (!fp) { + return 0; + } + + current_pos = ftell(fp); + if (current_pos < 0) { + return 0; + } + + if (fseek(fp, 0, SEEK_END) != 0) { + (void)fseek(fp, current_pos, SEEK_SET); + return 0; + } + + end_pos = ftell(fp); + (void)fseek(fp, current_pos, SEEK_SET); + + if (end_pos < 0) { + return 0; + } + + if (end_pos > INT_MAX) { + return INT_MAX; + } + + return (int)end_pos; +} + +static int tiff_ctx_size(gdIOCtx *ctx) +{ + unsigned char buffer[4096]; + long current_pos; + long end_pos; + + if (ctx == NULL || ctx->tell == NULL || ctx->seek == NULL) + return 0; + + current_pos = ctx->tell(ctx); + if (current_pos < 0) + return 0; + + while (ctx->getBuf(ctx, buffer, sizeof(buffer)) > 0) + ; + end_pos = ctx->tell(ctx); + (void)ctx->seek(ctx, (int)current_pos); + + if (end_pos < 0) + return 0; + if (end_pos > INT_MAX) + return INT_MAX; + + return (int)end_pos; +} + +/* tiffWriter + * ---------- + * Write the gd image as a tiff file (called by gdImageTiffCtx) + * Parameters are: + * image: gd image structure; + * out: the stream where to write + * bitDepth: depth in bits of each pixel + */ +static void tiffWriter(gdImagePtr image, gdIOCtx *out, int bitDepth) +{ + int x, y; + int i; + int r, g, b, a; + TIFF *tiff; + int width, height; + int color; + char *scan; + int samplesPerPixel = 3; + int bitsPerSample; + int transparentColorR = -1; + int transparentColorG = -1; + int transparentColorB = -1; + uint16_t extraSamples[1]; + uint16_t *colorMapRed = NULL; + uint16_t *colorMapGreen = NULL; + uint16_t *colorMapBlue = NULL; + size_t colorMapSize; + + tiff_handle *th; + + th = new_tiff_handle(out, 0); + if (!th) { + return; + } + extraSamples[0] = EXTRASAMPLE_ASSOCALPHA; + + /* read in the width/height of gd image */ + width = gdImageSX(image); + height = gdImageSY(image); + + /* reset clip region to whole image */ + gdImageSetClip(image, 0, 0, width, height); + + /* handle old-style single-colour mapping to 100% transparency */ + if (image->transparent != -1) { + /* set our 100% transparent colour value */ + transparentColorR = gdImageRed(image, image->transparent); + transparentColorG = gdImageGreen(image, image->transparent); + transparentColorB = gdImageBlue(image, image->transparent); + } + + /* Open tiff file writing routines, but use special read/write/seek + * functions so that tiff lib writes correct bits of tiff content to + * correct areas of file opened and modifieable by the gdIOCtx functions + */ + tiff = TIFFClientOpen("", "w", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + + TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); + TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, + (bitDepth == 24) ? PHOTOMETRIC_RGB : PHOTOMETRIC_PALETTE); + + bitsPerSample = (bitDepth == 24 || bitDepth == 8) ? 8 : 1; + TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bitsPerSample); + + TIFFSetField(tiff, TIFFTAG_XRESOLUTION, (float)image->res_x); + TIFFSetField(tiff, TIFFTAG_YRESOLUTION, (float)image->res_y); + + /* build the color map for 8 bit images */ + if (bitDepth != 24) { + if (overflow2(1 << bitsPerSample, sizeof(uint16_t))) { + gdFree(th); + return; + } + colorMapSize = (size_t)(1 << bitsPerSample) * sizeof(uint16_t); + colorMapRed = (uint16_t *)gdMalloc(colorMapSize); + if (!colorMapRed) { + gdFree(th); + return; + } + colorMapGreen = (uint16_t *)gdMalloc(colorMapSize); + if (!colorMapGreen) { + gdFree(colorMapRed); + gdFree(th); + return; + } + colorMapBlue = (uint16_t *)gdMalloc(colorMapSize); + if (!colorMapBlue) { + gdFree(colorMapRed); + gdFree(colorMapGreen); + gdFree(th); + return; + } + + for (i = 0; i < image->colorsTotal; i++) { + colorMapRed[i] = gdImageRed(image, i) + (gdImageRed(image, i) * 256); + colorMapGreen[i] = gdImageGreen(image, i) + (gdImageGreen(image, i) * 256); + colorMapBlue[i] = gdImageBlue(image, i) + (gdImageBlue(image, i) * 256); + } + + TIFFSetField(tiff, TIFFTAG_COLORMAP, colorMapRed, colorMapGreen, colorMapBlue); + samplesPerPixel = 1; + } + + /* here, we check if the 'save alpha' flag is set on the source gd image */ + if ((bitDepth == 24) && (image->saveAlphaFlag || image->transparent != -1)) { + /* so, we need to store the alpha values too! + * Also, tell TIFF what the extra sample means (associated alpha) */ + samplesPerPixel = 4; + TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel); + TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, extraSamples); + } else { + TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel); + } + + TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1); + + if (overflow2(width, samplesPerPixel)) { + if (colorMapRed) + gdFree(colorMapRed); + if (colorMapGreen) + gdFree(colorMapGreen); + if (colorMapBlue) + gdFree(colorMapBlue); + gdFree(th); + return; + } + + if (!(scan = (char *)gdMalloc(width * samplesPerPixel))) { + if (colorMapRed) + gdFree(colorMapRed); + if (colorMapGreen) + gdFree(colorMapGreen); + if (colorMapBlue) + gdFree(colorMapBlue); + gdFree(th); + return; + } + + /* loop through y-coords, and x-coords */ + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + /* generate scan line for writing to tiff */ + color = gdImageGetPixel(image, x, y); + + a = (127 - gdImageAlpha(image, color)) * 2; + a = (a == 0xfe) ? 0xff : a & 0xff; + b = gdImageBlue(image, color); + g = gdImageGreen(image, color); + r = gdImageRed(image, color); + + /* if this pixel has the same RGB as the transparent colour, + * then set alpha fully transparent */ + if (transparentColorR == r && transparentColorG == g && transparentColorB == b) { + a = 0x00; + } + + if (bitDepth != 24) { + /* write out 1 or 8 bit value in 1 byte + * (currently treats 1bit as 8bit) */ + scan[(x * samplesPerPixel) + 0] = color; + } else { + /* write out 24 bit value in 3 (or 4 if transparent) bytes */ + if (image->saveAlphaFlag || image->transparent != -1) { + scan[(x * samplesPerPixel) + 3] = a; + } + + scan[(x * samplesPerPixel) + 2] = b; + scan[(x * samplesPerPixel) + 1] = g; + scan[(x * samplesPerPixel) + 0] = r; + } + } + + /* Write the scan line to the tiff */ + if (TIFFWriteEncodedStrip(tiff, y, scan, width * samplesPerPixel) == -1) { + if (colorMapRed) + gdFree(colorMapRed); + if (colorMapGreen) + gdFree(colorMapGreen); + if (colorMapBlue) + gdFree(colorMapBlue); + gdFree(th); + /* error handler here */ + gd_error("Could not create TIFF\n"); + return; + } + } + + /* now cloase and free up resources */ + TIFFClose(tiff); + gdFree(scan); + gdFree(th); + + if (bitDepth != 24) { + gdFree(colorMapRed); + gdFree(colorMapGreen); + gdFree(colorMapBlue); + } +} + +/* + Function: gdImageTiffCtx + + Write the gd image as a tiff file. + + Parameters: + + image - gd image structure; + out - the stream where to write +*/ +BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out) +{ + int clipx1P, clipy1P, clipx2P, clipy2P; + int bitDepth = 24; + + /* First, switch off clipping, or we'll not get all the image! */ + gdImageGetClip(image, &clipx1P, &clipy1P, &clipx2P, &clipy2P); + + /* use the appropriate routine depending on the bit depth of the image */ + if (image->trueColor) { + bitDepth = 24; + } else if (image->colorsTotal == 2) { + bitDepth = 1; + } else { + bitDepth = 8; + } + + tiffWriter(image, out, bitDepth); + + /* reset clipping area to the gd image's original values */ + gdImageSetClip(image, clipx1P, clipy1P, clipx2P, clipy2P); +} + +/* Check if we are really in 8bit mode */ +static int checkColorMap(int n, uint16_t *r, uint16_t *g, uint16_t *b) +{ + while (n-- > 0) + if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) + return (16); + return (8); +} + +/* Read and convert a TIFF colormap */ +static int readTiffColorMap(gdImagePtr im, TIFF *tif, char is_bw, int photometric) +{ + uint16_t *redcmap, *greencmap, *bluecmap; + uint16_t bps; + int i; + + if (is_bw) { + if (photometric == PHOTOMETRIC_MINISWHITE) { + gdImageColorAllocate(im, 255, 255, 255); + gdImageColorAllocate(im, 0, 0, 0); + } else { + gdImageColorAllocate(im, 0, 0, 0); + gdImageColorAllocate(im, 255, 255, 255); + } + } else { + uint16_t min_sample_val, max_sample_val; + + if (!TIFFGetField(tif, TIFFTAG_MINSAMPLEVALUE, &min_sample_val)) { + min_sample_val = 0; + } + if (!TIFFGetField(tif, TIFFTAG_MAXSAMPLEVALUE, &max_sample_val)) { + max_sample_val = 255; + } + + if (photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE) { + /* TODO: use TIFFTAG_MINSAMPLEVALUE and TIFFTAG_MAXSAMPLEVALUE */ + /* Gray level palette */ + for (i = min_sample_val; i <= max_sample_val; i++) { + gdImageColorAllocate(im, i, i, i); + } + return GD_SUCCESS; + + } else if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap)) { + gd_error("Cannot read the color map"); + return GD_FAILURE; + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + +#define CVT(x) (((x) * 255) / ((1L << 16) - 1)) + if (checkColorMap(1 << bps, redcmap, greencmap, bluecmap) == 16) { + for (i = (1 << bps) - 1; i > 0; i--) { + redcmap[i] = CVT(redcmap[i]); + greencmap[i] = CVT(greencmap[i]); + bluecmap[i] = CVT(bluecmap[i]); + } + } + for (i = 0; i < 256; i++) { + gdImageColorAllocate(im, redcmap[i], greencmap[i], bluecmap[i]); + } +#undef CVT + } + return GD_SUCCESS; +} + +static void readTiffBw(const unsigned char *src, gdImagePtr im, uint16_t photometric, int startx, + int starty, int width, int height, char has_alpha, int extra, int align) +{ + int x = startx, y = starty; + + (void)has_alpha; + (void)extra; + (void)align; + + for (y = starty; y < starty + height; y++) { + for (x = startx; x < startx + width;) { + register unsigned char curr = *src++; + register unsigned char mask; + + if (photometric == PHOTOMETRIC_MINISWHITE) { + curr = ~curr; + } + for (mask = 0x80; mask != 0 && x < startx + width; x++, mask >>= 1) { + gdImageSetPixel(im, x, y, ((curr & mask) != 0) ? 0 : 1); + } + } + } +} + +static void readTiff8bit(const unsigned char *src, gdImagePtr im, uint16_t photometric, int startx, + int starty, int width, int height, char has_alpha, int extra, int align) +{ + int red, green, blue, alpha; + int x, y; + + (void)extra; + (void)align; + + switch (photometric) { + case PHOTOMETRIC_PALETTE: + /* Palette has no alpha (see TIFF specs for more details */ + for (y = starty; y < starty + height; y++) { + for (x = startx; x < startx + width; x++) { + gdImageSetPixel(im, x, y, *(src++)); + } + } + break; + + case PHOTOMETRIC_RGB: + if (has_alpha) { + gdImageAlphaBlending(im, 0); + gdImageSaveAlpha(im, 1); + + for (y = starty; y < starty + height; y++) { + for (x = startx; x < startx + width; x++) { + red = *src++; + green = *src++; + blue = *src++; + alpha = *src++; + red = MIN(red, alpha); + blue = MIN(blue, alpha); + green = MIN(green, alpha); + + if (alpha) { + gdImageSetPixel(im, x, y, + gdTrueColorAlpha(red * 255 / alpha, green * 255 / alpha, + blue * 255 / alpha, + gdAlphaMax - (alpha >> 1))); + } else { + gdImageSetPixel( + im, x, y, + gdTrueColorAlpha(red, green, blue, gdAlphaMax - (alpha >> 1))); + } + } + } + + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + register unsigned char r = *src++; + register unsigned char g = *src++; + register unsigned char b = *src++; + + gdImageSetPixel(im, x, y, gdTrueColor(r, g, b)); + } + } + } + break; + + case PHOTOMETRIC_MINISWHITE: + if (has_alpha) { + /* We don't process the extra yet */ + } else { + for (y = starty; y < starty + height; y++) { + for (x = startx; x < startx + width; x++) { + gdImageSetPixel(im, x, y, ~(*src++)); + } + } + } + break; + + case PHOTOMETRIC_MINISBLACK: + if (has_alpha) { + /* We don't process the extra yet */ + } else { + for (y = starty; y < height; y++) { + for (x = 0; x < width; x++) { + gdImageSetPixel(im, x, y, *src++); + } + } + } + break; + } +} + +static int createFromTiffTiles(TIFF *tif, gdImagePtr im, uint16_t bps, uint16_t photometric, + char has_alpha, char is_bw, int extra) +{ + uint16_t planar; + int im_width, im_height; + int tile_width, tile_height; + int x, y, height, width; + unsigned char *buffer; + tmsize_t tile_size; + int success = GD_SUCCESS; + + if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) { + planar = PLANARCONFIG_CONTIG; + } + if (TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &im_width) == 0 || + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &im_height) == 0 || + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) == 0 || + TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height) == 0) { + return FALSE; + } + if (tile_width <= 0 || tile_height <= 0) { + return FALSE; + } + + tile_size = TIFFTileSize(tif); + if (tile_size <= 0) { + return FALSE; + } + buffer = (unsigned char *)gdMalloc((size_t)tile_size); + if (!buffer) { + return FALSE; + } + + for (y = 0; y < im_height; y += tile_height) { + for (x = 0; x < im_width; x += tile_width) { + if (TIFFReadTile(tif, buffer, x, y, 0, 0) < 0) { + success = GD_FAILURE; + goto end; + } + width = MIN(im_width - x, tile_width); + height = MIN(im_height - y, tile_height); + if (bps == 8) { + readTiff8bit(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0); + } else if (is_bw) { + readTiffBw(buffer, im, photometric, x, y, width, height, has_alpha, extra, 0); + } else { + gd_error("TIFF error, unsupported tiled image format in direct " + "reader"); + success = GD_FAILURE; + goto end; + } + } + } +end: + gdFree(buffer); + return success; +} + +static int createFromTiffLines(TIFF *tif, gdImagePtr im, uint16_t bps, uint16_t photometric, + char has_alpha, char is_bw, int extra) +{ + uint16_t planar; + uint32_t im_height, im_width, y; + + unsigned char *buffer; + int success = GD_SUCCESS; + + if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) { + planar = PLANARCONFIG_CONTIG; + } + + if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &im_height)) { + gd_error("Can't fetch TIFF height\n"); + return FALSE; + } + + if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &im_width)) { + gd_error("Can't fetch TIFF width \n"); + return FALSE; + } + + if (im_width > INT_MAX || overflow2((int)im_width, 4)) { + return GD_FAILURE; + } + buffer = (unsigned char *)gdMalloc((size_t)im_width * 4); + if (!buffer) { + return GD_FAILURE; + } + if (planar == PLANARCONFIG_CONTIG) { + switch (bps) { + case 8: + for (y = 0; y < im_height; y++) { + if (TIFFReadScanline(tif, buffer, y, 0) < 0) { + gd_error("Error while reading scanline %i", y); + success = GD_FAILURE; + break; + } + /* reading one line at a time */ + readTiff8bit(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0); + } + break; + + default: + if (is_bw) { + for (y = 0; y < im_height; y++) { + if (TIFFReadScanline(tif, buffer, y, 0) < 0) { + gd_error("Error while reading scanline %i", y); + success = GD_FAILURE; + break; + } + /* reading one line at a time */ + readTiffBw(buffer, im, photometric, 0, y, im_width, 1, has_alpha, extra, 0); + } + } else { + gd_error("TIFF error, unsupported scanline image format in " + "direct reader"); + success = GD_FAILURE; + } + break; + } + } else { + gd_error("TIFF error, unsupported separate planar image in direct reader"); + success = GD_FAILURE; + } + + gdFree(buffer); + return success; +} + +static int createFromTiffRgba(TIFF *tif, gdImagePtr im) +{ + int a; + int x, y; + int alphaBlendingFlag = 0; + int color; + int width = im->sx; + int height = im->sy; + uint32_t *buffer; + uint32_t rgba; + int success; + + buffer = (uint32_t *)gdCalloc(sizeof(uint32_t), width * height); + if (!buffer) { + return GD_FAILURE; + } + + /* switch off colour merging on target gd image just while we write out + * content - we want to preserve the alpha data until the user chooses + * what to do with the image */ + alphaBlendingFlag = im->alphaBlendingFlag; + gdImageAlphaBlending(im, 0); + + success = TIFFReadRGBAImage(tif, width, height, buffer, 1); + + if (success) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + /* if it doesn't already exist, allocate a new colour, + * else use existing one */ + rgba = buffer[(y * width + x)]; + a = (0xff - TIFFGetA(rgba)) / 2; + color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a); + + /* set pixel colour to this colour */ + gdImageSetPixel(im, x, height - y - 1, color); + } + } + } + + gdFree(buffer); + + /* now reset colour merge for alpha blending routines */ + gdImageAlphaBlending(im, alphaBlendingFlag); + return success; +} + +/* + Function: gdImageCreateFromTiffCtx + + Create a gdImage from a TIFF file input from an gdIOCtx. +*/ +static gdImagePtr TiffDecodeCurrentDirectory(TIFF *tif) +{ + uint16_t bps, spp, photometric; + uint16_t orientation; + int width, height; + uint16_t extra, *extra_types; + uint16_t planar; + char has_alpha, is_bw, is_gray; + char force_rgba = FALSE; + char save_transparent; + int image_type; + int ret; + float res_float; + + gdImagePtr im = NULL; + + if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width)) { + gd_error("TIFF error, Cannot read image width"); + return NULL; + } + + if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) { + gd_error("TIFF error, Cannot read image height"); + return NULL; + } + + if (width <= 0 || height <= 0) { + gd_error("TIFF error, image dimensions must be greater than 0"); + return NULL; + } + + if (overflow2(width, height)) { + gd_error("TIFF error, image dimensions are too large"); + return NULL; + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + + if (bps == 0 || bps > 32) { + gd_error("TIFF error, invalid bits per sample: %u", (unsigned)bps); + return NULL; + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + + if (spp == 0 || spp > 4) { + gd_error("TIFF error, invalid samples per pixel: %u", (unsigned)spp); + return NULL; + } + + if (!TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) { + extra = 0; + } + + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { + uint16_t compression; + if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression) && + (compression == COMPRESSION_CCITTFAX3 || compression == COMPRESSION_CCITTFAX4 || + compression == COMPRESSION_CCITTRLE || compression == COMPRESSION_CCITTRLEW)) { + gd_error("Could not get photometric. " + "Image is CCITT compressed, assuming min-is-white"); + photometric = PHOTOMETRIC_MINISWHITE; + } else { + gd_error("Could not get photometric. " + "Assuming min-is-black"); + + photometric = PHOTOMETRIC_MINISBLACK; + } + } + save_transparent = FALSE; + + /* test if the extrasample represents an associated alpha channel... */ + if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) { + has_alpha = TRUE; + save_transparent = FALSE; + --extra; + } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA)) { + has_alpha = TRUE; + save_transparent = TRUE; + --extra; + } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)) { + /* assuming unassociated alpha if unspecified */ + gd_error("alpha channel type not defined, assuming alpha is not " + "premultiplied"); + has_alpha = TRUE; + save_transparent = TRUE; + --extra; + } else { + has_alpha = FALSE; + } + + if (photometric == PHOTOMETRIC_RGB && spp > 3 + extra) { + has_alpha = TRUE; + extra = spp - 4; + } else if (photometric != PHOTOMETRIC_RGB && spp > 1 + extra) { + has_alpha = TRUE; + extra = spp - 2; + } + + is_bw = FALSE; + is_gray = FALSE; + + switch (photometric) { + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + if (!has_alpha && bps == 1 && spp == 1) { + image_type = GD_INDEXED; + is_bw = TRUE; + } else { + image_type = GD_GRAY; + } + break; + + case PHOTOMETRIC_RGB: + image_type = GD_RGB; + break; + + case PHOTOMETRIC_PALETTE: + image_type = GD_INDEXED; + break; + + default: + force_rgba = TRUE; + break; + } + + if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) { + planar = PLANARCONFIG_CONTIG; + } + + /* The direct scanline/tile readers only implement contiguous 1-bit BW + * images. */ + if (!is_bw || bps != 1 || spp != 1 || has_alpha || planar != PLANARCONFIG_CONTIG) { + force_rgba = TRUE; + } + + /* Force rgba if image planes are not contiguous or the format is otherwise + * unsupported. */ + if (force_rgba) { + image_type = GD_RGB; + } + + if (!force_rgba && + (image_type == GD_PALETTE || image_type == GD_INDEXED || image_type == GD_GRAY)) { + im = gdImageCreate(width, height); + if (!im) + return NULL; + readTiffColorMap(im, tif, is_bw, photometric); + } else { + im = gdImageCreateTrueColor(width, height); + if (!im) + return NULL; + } + +#ifdef DEBUG + printf("force rgba: %i\n", force_rgba); + printf("has_alpha: %i\n", has_alpha); + printf("save trans: %i\n", save_transparent); + printf("is_bw: %i\n", is_bw); + printf("is_gray: %i\n", is_gray); + printf("type: %i\n", image_type); +#else + (void)is_gray; + (void)save_transparent; +#endif + + if (force_rgba) { + ret = createFromTiffRgba(tif, im); + } else if (TIFFIsTiled(tif)) { + ret = createFromTiffTiles(tif, im, bps, photometric, has_alpha, is_bw, extra); + } else { + ret = createFromTiffLines(tif, im, bps, photometric, has_alpha, is_bw, extra); + } + + if (!ret) { + gdImageDestroy(im); + return NULL; + } + + if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_float)) { + im->res_x = (unsigned int)res_float; // truncate + } + if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res_float)) { + im->res_y = (unsigned int)res_float; // truncate + } + + if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &orientation)) { + switch (orientation) { + case ORIENTATION_TOPLEFT: + case ORIENTATION_TOPRIGHT: + case ORIENTATION_BOTRIGHT: + case ORIENTATION_BOTLEFT: + break; + + default: + gd_error("Orientation %d not handled yet!", orientation); + break; + } + } + + return im; +} + +static gdImagePtr gdImageCreateFromTiffCtxEx(gdIOCtx *infile, int initial_size) +{ + TIFF *tif; + tiff_handle *th; + gdImagePtr im = NULL; + + th = new_tiff_handle(infile, initial_size); + if (!th) { + return NULL; + } + + tif = TIFFClientOpen("", "rb", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + + if (!tif) { + gd_error("Cannot open TIFF image"); + gdFree(th); + return NULL; + } + + im = TiffDecodeCurrentDirectory(tif); + + TIFFClose(tif); + gdFree(th); + return im; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile) +{ + return gdImageCreateFromTiffCtxEx(infile, tiff_ctx_size(infile)); +} + +/* + Function: gdImageCreateFromTIFF +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + int initial_size = tiff_file_size(inFile); + + if (in == NULL) + return NULL; + im = gdImageCreateFromTiffCtxEx(in, initial_size); + in->gd_free(in); + return im; +} + +/* + Function: gdImageCreateFromTiffPtr +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) + return NULL; + im = gdImageCreateFromTiffCtxEx(in, size); + in->gd_free(in); + return im; +} + +/* + Function: gdImageTiff +*/ +BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageTiffCtx(im, out); /* what's an fg again? */ + out->gd_free(out); +} + +/* + Function: gdImageTiffPtr +*/ +BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + gdImageTiffCtx(im, out); /* what's an fg again? */ + rv = gdDPExtractData(out, size); + out->gd_free(out); + return rv; +} + +#define GD_TIFF_ALLOC_STEP (4 * 1024) + +typedef struct gdTiffReadStruct { + uint8_t *data; + size_t size; + TIFF *tif; + tiff_handle *th; + gdIOCtx *memCtx; + int pageCount; + int currentPage; + gdImagePtr image; +} gdTiffRead; + +static uint8_t *TiffReadCtxData(gdIOCtx *infile, size_t *size) +{ + uint8_t *filedata = NULL, *temp, *read; + ssize_t n; + + *size = 0; + do { + temp = gdRealloc(filedata, *size + GD_TIFF_ALLOC_STEP); + if (temp == NULL) { + gdFree(filedata); + gd_error("TIFF decode: realloc failed"); + return NULL; + } + filedata = temp; + read = temp + *size; + n = gdGetBuf(read, GD_TIFF_ALLOC_STEP, infile); + if (n > 0 && n != EOF) { + *size += n; + } + } while (n > 0 && n != EOF); + + if (*size == 0) { + gdFree(filedata); + return NULL; + } + + return filedata; +} + +static void TiffFillInfo(TIFF *tif, gdTiffInfo *info, int pageCount) +{ + uint16_t bps, spp, photometric, compression; + float res_float; + uint16_t resUnit; + + if (info == NULL || tif == NULL) { + return; + } + memset(info, 0, sizeof(*info)); + info->pageCount = pageCount; + + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, (uint32_t *)&info->width); + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, (uint32_t *)&info->height); + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + info->bitsPerSample = bps; + info->samplesPerPixel = spp; + + if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression)) { + info->compression = compression; + } + if (TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { + info->photometric = photometric; + } + if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_float)) { + info->xResolution = res_float; + } + if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res_float)) { + info->yResolution = res_float; + } + if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resUnit)) { + info->resolutionUnit = resUnit; + } else { + info->resolutionUnit = 2; + } +} + +static void TiffFillPageInfo(TIFF *tif, gdTiffPageInfo *info, int pageIndex) +{ + uint16_t bps, spp, photometric, compression, planar; + uint16_t extra, *extra_types; + uint16_t resUnit; + float res_float; + + if (info == NULL || tif == NULL) { + return; + } + memset(info, 0, sizeof(*info)); + info->pageIndex = pageIndex; + + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, (uint32_t *)&info->width); + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, (uint32_t *)&info->height); + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + info->bitsPerSample = bps; + info->samplesPerPixel = spp; + info->isTiled = TIFFIsTiled(tif); + + if (TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression)) { + info->compression = compression; + } + if (TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { + info->photometric = photometric; + } + if (TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar)) { + info->planar = planar; + } else { + info->planar = PLANARCONFIG_CONTIG; + } + + if (!TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) { + extra = 0; + } + info->hasAlpha = (extra > 0); + + if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res_float)) { + info->xResolution = res_float; + } + if (TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res_float)) { + info->yResolution = res_float; + } + if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resUnit)) { + info->resolutionUnit = resUnit; + } else { + info->resolutionUnit = 2; + } +} + +static gdTiffReadPtr TiffReadOpenFromData(uint8_t *data, size_t size) +{ + gdTiffReadPtr tiff; + gdIOCtx *memCtx; + tiff_handle *th; + TIFF *tif; + + tiff = (gdTiffReadPtr)gdCalloc(1, sizeof(gdTiffRead)); + if (tiff == NULL) { + return NULL; + } + + tiff->data = data; + tiff->size = size; + + memCtx = gdNewDynamicCtxEx((int)size, data, 0); + if (memCtx == NULL) { + gdFree(tiff); + return NULL; + } + tiff->memCtx = memCtx; + + th = new_tiff_handle(memCtx, (int)size); + if (th == NULL) { + memCtx->gd_free(memCtx); + gdFree(tiff); + return NULL; + } + tiff->th = th; + + tif = TIFFClientOpen("", "rb", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + if (tif == NULL) { + gdFree(th); + memCtx->gd_free(memCtx); + gdFree(tiff); + return NULL; + } + tiff->tif = tif; + + tiff->pageCount = (int)TIFFNumberOfDirectories(tif); + if (tiff->pageCount <= 0) { + gd_error("TIFF error, invalid page count: %d", tiff->pageCount); + TIFFClose(tif); + tiff->tif = NULL; + gdFree(th); + tiff->th = NULL; + memCtx->gd_free(memCtx); + tiff->memCtx = NULL; + gdFree(tiff); + return NULL; + } + tiff->currentPage = -1; + tiff->image = NULL; + + return tiff; +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpen(FILE *fd) +{ + gdIOCtx *in; + gdTiffReadPtr tiff; + uint8_t *data; + size_t size; + + if (fd == NULL) { + return NULL; + } + in = gdNewFileCtx(fd); + if (in == NULL) { + return NULL; + } + data = TiffReadCtxData(in, &size); + in->gd_free(in); + if (data == NULL) { + return NULL; + } + tiff = TiffReadOpenFromData(data, size); + if (tiff == NULL) { + gdFree(data); + } + return tiff; +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenCtx(gdIOCtxPtr in) +{ + uint8_t *data; + size_t size; + + if (in == NULL) { + return NULL; + } + data = TiffReadCtxData(in, &size); + if (data == NULL) { + return NULL; + } + return TiffReadOpenFromData(data, size); +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenPtr(int size, void *data) +{ + uint8_t *buf; + + if (size <= 0 || data == NULL) { + return NULL; + } + buf = (uint8_t *)gdMalloc((size_t)size); + if (buf == NULL) { + return NULL; + } + memcpy(buf, data, (size_t)size); + return TiffReadOpenFromData(buf, (size_t)size); +} + +BGD_DECLARE(void) gdTiffReadClose(gdTiffReadPtr tiff) +{ + if (tiff == NULL) { + return; + } + if (tiff->image != NULL) { + gdImageDestroy(tiff->image); + } + if (tiff->tif != NULL) { + TIFFClose(tiff->tif); + } + if (tiff->th != NULL) { + gdFree(tiff->th); + } + if (tiff->memCtx != NULL) { + tiff->memCtx->gd_free(tiff->memCtx); + } + if (tiff->data != NULL) { + gdFree(tiff->data); + } + gdFree(tiff); +} + +BGD_DECLARE(int) gdTiffReadGetInfo(gdTiffReadPtr tiff, gdTiffInfo *info) +{ + tdir_t savedDir; + + if (tiff == NULL || info == NULL || tiff->tif == NULL) { + return 0; + } + savedDir = TIFFCurrentDirectory(tiff->tif); + TIFFSetDirectory(tiff->tif, 0); + TiffFillInfo(tiff->tif, info, tiff->pageCount); + TIFFSetDirectory(tiff->tif, savedDir); + return 1; +} + +BGD_DECLARE(int) +gdTiffReadNextImage(gdTiffReadPtr tiff, gdTiffPageInfo *info, gdImagePtr *image) +{ + int ok; + + if (image != NULL) { + *image = NULL; + } + if (tiff == NULL || tiff->tif == NULL || tiff->pageCount <= 0) { + return -1; + } + + if (tiff->currentPage >= tiff->pageCount - 1 && tiff->currentPage >= 0) { + return 0; + } + + if (tiff->currentPage < 0) { + if (!TIFFSetDirectory(tiff->tif, 0)) { + return 0; + } + tiff->currentPage = 0; + } else { + if (!TIFFReadDirectory(tiff->tif)) { + return 0; + } + tiff->currentPage++; + } + + TiffFillPageInfo(tiff->tif, info, tiff->currentPage); + + if (tiff->image != NULL) { + gdImageDestroy(tiff->image); + tiff->image = NULL; + } + + tiff->image = TiffDecodeCurrentDirectory(tiff->tif); + if (tiff->image == NULL) { + ok = TIFFSetDirectory(tiff->tif, TIFFCurrentDirectory(tiff->tif)); + (void)ok; + return -1; + } + + if (image != NULL) { + *image = tiff->image; + } + return 1; +} + +BGD_DECLARE(gdImagePtr) gdTiffReadCloneImage(gdTiffReadPtr tiff) +{ + gdImagePtr dst; + int x, y; + + if (tiff == NULL || tiff->image == NULL) { + return NULL; + } + dst = gdImageCreateTrueColor(gdImageSX(tiff->image), gdImageSY(tiff->image)); + if (dst == NULL) { + return NULL; + } + gdImageAlphaBlending(dst, 0); + gdImageSaveAlpha(dst, tiff->image->saveAlphaFlag); + for (y = 0; y < gdImageSY(tiff->image); y++) { + for (x = 0; x < gdImageSX(tiff->image); x++) { + dst->tpixels[y][x] = gdImageGetPixel(tiff->image, x, y); + } + } + return dst; +} + +BGD_DECLARE(int) gdTiffIsMultiPage(FILE *fd) +{ + gdIOCtx *in; + uint8_t *data; + size_t size; + gdIOCtx *memCtx; + tiff_handle *th; + TIFF *tif; + tdir_t dirCount; + int pos; + + if (fd == NULL) { + return -1; + } + in = gdNewFileCtx(fd); + if (in == NULL) { + return -1; + } + pos = (int)gdTell(in); + if (pos < 0) { + in->gd_free(in); + return -1; + } + data = TiffReadCtxData(in, &size); + if (data == NULL) { + in->gd_free(in); + return -1; + } + memCtx = gdNewDynamicCtxEx((int)size, data, 0); + if (memCtx == NULL) { + gdFree(data); + in->gd_free(in); + return -1; + } + th = new_tiff_handle(memCtx, (int)size); + if (th == NULL) { + memCtx->gd_free(memCtx); + gdFree(data); + in->gd_free(in); + return -1; + } + tif = TIFFClientOpen("", "rb", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + if (tif == NULL) { + gdFree(th); + memCtx->gd_free(memCtx); + gdFree(data); + in->gd_free(in); + return -1; + } + dirCount = TIFFNumberOfDirectories(tif); + TIFFClose(tif); + gdFree(th); + memCtx->gd_free(memCtx); + gdFree(data); + if (!gdSeek(in, pos)) { + in->gd_free(in); + return -1; + } + in->gd_free(in); + return dirCount > 1 ? 1 : 0; +} + +BGD_DECLARE(int) gdTiffIsMultiPageCtx(gdIOCtxPtr in) +{ + uint8_t *data; + size_t size; + gdIOCtx *memCtx; + tiff_handle *th; + TIFF *tif; + tdir_t dirCount; + int pos; + + if (in == NULL || in->tell == NULL || in->seek == NULL) { + return -1; + } + pos = (int)gdTell(in); + if (pos < 0) { + return -1; + } + data = TiffReadCtxData(in, &size); + if (data == NULL) { + return -1; + } + memCtx = gdNewDynamicCtxEx((int)size, data, 0); + if (memCtx == NULL) { + gdFree(data); + return -1; + } + th = new_tiff_handle(memCtx, (int)size); + if (th == NULL) { + memCtx->gd_free(memCtx); + gdFree(data); + return -1; + } + tif = TIFFClientOpen("", "rb", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + if (tif == NULL) { + gdFree(th); + memCtx->gd_free(memCtx); + gdFree(data); + return -1; + } + dirCount = TIFFNumberOfDirectories(tif); + TIFFClose(tif); + gdFree(th); + memCtx->gd_free(memCtx); + gdFree(data); + if (!gdSeek(in, pos)) { + return -1; + } + return dirCount > 1 ? 1 : 0; +} + +BGD_DECLARE(int) gdTiffIsMultiPagePtr(int size, void *data) +{ + gdIOCtx *in; + int result; + + if (size <= 0 || data == NULL) { + return -1; + } + in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) { + return -1; + } + result = gdTiffIsMultiPageCtx(in); + in->gd_free(in); + return result; +} + +/* ========== TIFF Write API ========== */ + +struct gdTiffWriteStruct { + gdIOCtx *out; + int ownsCtx; + int memoryWriter; + TIFF *tif; + tiff_handle *th; + gdTiffWriteOptions options; + int pageCount; + int finalized; +}; + +static int TiffWriteValidateOptions(const gdTiffWriteOptions *opts) +{ + if (opts == NULL) { + gd_error("gd-tiff write: options is NULL"); + return 0; + } + switch (opts->colorspace) { + case GD_TIFF_RGB: + case GD_TIFF_RGBA: + case GD_TIFF_GRAY: + case GD_TIFF_BILEVEL: + break; + default: + gd_error("gd-tiff write: invalid colorspace %d", opts->colorspace); + return 0; + } + switch (opts->bitDepth) { + case 1: + case 8: + case 16: + break; + default: + gd_error("gd-tiff write: invalid bit depth %d", opts->bitDepth); + return 0; + } + if (opts->bitDepth == 1 && opts->colorspace != GD_TIFF_BILEVEL) { + gd_error("gd-tiff write: 1-bit depth requires GD_TIFF_BILEVEL colorspace"); + return 0; + } + if (opts->colorspace == GD_TIFF_BILEVEL && opts->bitDepth != 1) { + gd_error("gd-tiff write: GD_TIFF_BILEVEL requires 1-bit depth"); + return 0; + } + switch (opts->compression) { + case COMPRESSION_NONE: + break; + case COMPRESSION_LZW: + case COMPRESSION_ADOBE_DEFLATE: + case COMPRESSION_DEFLATE: + case COMPRESSION_PACKBITS: + if (opts->bitDepth == 1 && opts->colorspace != GD_TIFF_BILEVEL) { + gd_error("gd-tiff write: LZW/Deflate/PackBits at 1-bit requires " + "BILEVEL"); + return 0; + } + break; + case COMPRESSION_JPEG: + if (opts->bitDepth != 8) { + gd_error("gd-tiff write: JPEG compression requires 8-bit depth"); + return 0; + } + break; + case COMPRESSION_CCITTFAX3: + case COMPRESSION_CCITTFAX4: + if (opts->bitDepth != 1) { + gd_error("gd-tiff write: CCITT compression requires 1-bit depth"); + return 0; + } + break; + default: + gd_error("gd-tiff write: unsupported compression %d", opts->compression); + return 0; + } + if (opts->colorspace == GD_TIFF_RGB && opts->bitDepth == 1) { + gd_error("gd-tiff write: RGB at 1-bit is not valid"); + return 0; + } + if (opts->colorspace == GD_TIFF_RGBA && opts->bitDepth == 1) { + gd_error("gd-tiff write: RGBA at 1-bit is not valid"); + return 0; + } + return 1; +} + +static void TiffWriteSetDefaults(gdTiffWriteOptions *opts) +{ + if (opts->bitDepth == 0) + opts->bitDepth = 8; + if (opts->colorspace == 0) + opts->colorspace = GD_TIFF_RGBA; + if (opts->compression == 0) + opts->compression = COMPRESSION_ADOBE_DEFLATE; + if (opts->jpegQuality == 0 && opts->compression == COMPRESSION_JPEG) + opts->jpegQuality = 75; + if (opts->resolutionUnit == 0) + opts->resolutionUnit = GD_TIFF_RESUNIT_INCH; + if (opts->xResolution == 0) + opts->xResolution = 72.0f; + if (opts->yResolution == 0) + opts->yResolution = 72.0f; + if (opts->alphaType == 0) + opts->alphaType = GD_TIFF_ALPHA_UNASSOCIATED; +} + +static int TiffWriteSamplesPerPixel(const gdTiffWriteOptions *opts) +{ + switch (opts->colorspace) { + case GD_TIFF_RGBA: + return 4; + case GD_TIFF_RGB: + return 3; + case GD_TIFF_GRAY: + case GD_TIFF_BILEVEL: + return 1; + default: + return 3; + } +} + +static int TiffWritePhotometric(const gdTiffWriteOptions *opts) +{ + switch (opts->colorspace) { + case GD_TIFF_RGB: + case GD_TIFF_RGBA: + return PHOTOMETRIC_RGB; + case GD_TIFF_GRAY: + return opts->minIsWhite ? PHOTOMETRIC_MINISWHITE : PHOTOMETRIC_MINISBLACK; + case GD_TIFF_BILEVEL: + return opts->minIsWhite ? PHOTOMETRIC_MINISWHITE : PHOTOMETRIC_MINISBLACK; + default: + return PHOTOMETRIC_RGB; + } +} + +static int TiffWriteBitsPerSample(const gdTiffWriteOptions *opts) +{ + if (opts->colorspace == GD_TIFF_BILEVEL) + return 1; + return opts->bitDepth; +} + +static void TiffWriteConvertRowRGBA8(gdImagePtr im, int y, uint8_t *buf, int width) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int a = gdImageAlpha(im, c); + a = (127 - a) * 2; + if (a > 255) + a = 255; + if (a == 254) + a = 255; + buf[x * 4 + 0] = (uint8_t)r; + buf[x * 4 + 1] = (uint8_t)g; + buf[x * 4 + 2] = (uint8_t)b; + buf[x * 4 + 3] = (uint8_t)a; + } +} + +static void TiffWriteConvertRowRGB8(gdImagePtr im, int y, uint8_t *buf, int width) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int a = gdImageAlpha(im, c); + a = (127 - a) * 2; + if (a > 255) + a = 255; + if (a == 254) + a = 255; + if (a < 255) { + int af = a + 1; + r = (r * af + 127) / 255; + g = (g * af + 127) / 255; + b = (b * af + 127) / 255; + } + buf[x * 3 + 0] = (uint8_t)r; + buf[x * 3 + 1] = (uint8_t)g; + buf[x * 3 + 2] = (uint8_t)b; + } +} + +static void TiffWriteConvertRowGray8(gdImagePtr im, int y, uint8_t *buf, int width, int minIsWhite) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int gray = (int)(0.2126f * r + 0.7152f * g + 0.0722f * b + 0.5f); + if (gray > 255) + gray = 255; + if (minIsWhite) + gray = 255 - gray; + buf[x] = (uint8_t)gray; + } +} + +static void TiffWriteConvertRowBilevel(gdImagePtr im, int y, uint8_t *buf, int width, + int minIsWhite) +{ + int x; + memset(buf, 0, (width + 7) / 8); + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int gray = (int)(0.2126f * r + 0.7152f * g + 0.0722f * b + 0.5f); + int bit; + if (minIsWhite) + bit = (gray < 128) ? 1 : 0; + else + bit = (gray >= 128) ? 1 : 0; + if (bit) + buf[x / 8] |= (uint8_t)(0x80 >> (x & 7)); + } +} + +static void TiffWriteConvertRowRGBA16(gdImagePtr im, int y, uint16_t *buf, int width) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int a = gdImageAlpha(im, c); + a = (127 - a) * 2; + if (a > 255) + a = 255; + if (a == 254) + a = 255; + buf[x * 4 + 0] = (uint16_t)(r * 257); + buf[x * 4 + 1] = (uint16_t)(g * 257); + buf[x * 4 + 2] = (uint16_t)(b * 257); + buf[x * 4 + 3] = (uint16_t)(a * 257); + } +} + +static void TiffWriteConvertRowRGB16(gdImagePtr im, int y, uint16_t *buf, int width) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int a = gdImageAlpha(im, c); + a = (127 - a) * 2; + if (a > 255) + a = 255; + if (a == 254) + a = 255; + if (a < 255) { + int af = a + 1; + r = (r * af + 127) / 255; + g = (g * af + 127) / 255; + b = (b * af + 127) / 255; + } + buf[x * 3 + 0] = (uint16_t)(r * 257); + buf[x * 3 + 1] = (uint16_t)(g * 257); + buf[x * 3 + 2] = (uint16_t)(b * 257); + } +} + +static void TiffWriteConvertRowGray16(gdImagePtr im, int y, uint16_t *buf, int width, + int minIsWhite) +{ + int x; + for (x = 0; x < width; x++) { + int c = im->tpixels[y][x]; + int r = gdImageRed(im, c); + int g = gdImageGreen(im, c); + int b = gdImageBlue(im, c); + int gray = (int)(0.2126f * r + 0.7152f * g + 0.0722f * b + 0.5f); + if (gray > 255) + gray = 255; + if (minIsWhite) + gray = 255 - gray; + buf[x] = (uint16_t)(gray * 257); + } +} + +static int TiffWriteWritePage(gdTiffWritePtr write, gdImagePtr im) +{ + TIFF *tif = write->tif; + gdTiffWriteOptions *opts = &write->options; + int width, height; + int nsamples, bps, photometric; + int y; + uint8_t *scanbuf = NULL; + + width = gdImageSX(im); + height = gdImageSY(im); + nsamples = TiffWriteSamplesPerPixel(opts); + bps = TiffWriteBitsPerSample(opts); + photometric = TiffWritePhotometric(opts); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, nsamples); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric); + TIFFSetField(tif, TIFFTAG_COMPRESSION, opts->compression); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); + + if (opts->colorspace == GD_TIFF_RGBA) { + uint16_t extra = (opts->alphaType == GD_TIFF_ALPHA_ASSOCIATED) ? EXTRASAMPLE_ASSOCALPHA + : EXTRASAMPLE_UNASSALPHA; + TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, &extra); + } + + if (opts->compression == COMPRESSION_LZW || opts->compression == COMPRESSION_ADOBE_DEFLATE || + opts->compression == COMPRESSION_DEFLATE) { + TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL); + } + + if (opts->compression == COMPRESSION_JPEG && opts->jpegQuality > 0) { + TIFFSetField(tif, TIFFTAG_JPEGQUALITY, opts->jpegQuality); + } + + if (opts->colorspace == GD_TIFF_BILEVEL && (opts->compression == COMPRESSION_CCITTFAX3 || + opts->compression == COMPRESSION_CCITTFAX4)) { + uint32_t g3opts = 0; + if (opts->compression == COMPRESSION_CCITTFAX3) { + TIFFSetField(tif, TIFFTAG_GROUP3OPTIONS, g3opts); + } + if (opts->compression == COMPRESSION_CCITTFAX4) { + TIFFSetField(tif, TIFFTAG_GROUP4OPTIONS, 0); + } + TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + } + + { + uint16_t resUnit; + switch (opts->resolutionUnit) { + case GD_TIFF_RESUNIT_NONE: + resUnit = RESUNIT_NONE; + break; + case GD_TIFF_RESUNIT_INCH: + resUnit = RESUNIT_INCH; + break; + case GD_TIFF_RESUNIT_CENTIMETER: + resUnit = RESUNIT_CENTIMETER; + break; + default: + resUnit = RESUNIT_INCH; + break; + } + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, resUnit); + TIFFSetField(tif, TIFFTAG_XRESOLUTION, opts->xResolution); + TIFFSetField(tif, TIFFTAG_YRESOLUTION, opts->yResolution); + } + + if (opts->colorspace == GD_TIFF_BILEVEL) { + size_t scanline_size = (size_t)((width + 7) / 8); + if (opts->compression == COMPRESSION_CCITTFAX3 || + opts->compression == COMPRESSION_CCITTFAX4) { + scanline_size = (size_t)TIFFScanlineSize(tif); + if (scanline_size == 0) + scanline_size = (size_t)((width + 7) / 8); + } + scanbuf = (uint8_t *)gdMalloc(scanline_size); + if (scanbuf == NULL) { + gd_error("gd-tiff write: could not allocate scanline buffer"); + return 0; + } + for (y = 0; y < height; y++) { + TiffWriteConvertRowBilevel(im, y, scanbuf, width, opts->minIsWhite); + if (TIFFWriteScanline(tif, scanbuf, y, 0) < 0) { + gdFree(scanbuf); + gd_error("gd-tiff write: could not write scanline"); + return 0; + } + } + } else if (opts->bitDepth == 16) { + size_t scanline_size = (size_t)TIFFScanlineSize(tif); + scanbuf = (uint8_t *)gdMalloc(scanline_size); + if (scanbuf == NULL) { + gd_error("gd-tiff write: could not allocate scanline buffer"); + return 0; + } + for (y = 0; y < height; y++) { + uint16_t *buf16 = (uint16_t *)scanbuf; + switch (opts->colorspace) { + case GD_TIFF_RGBA: + TiffWriteConvertRowRGBA16(im, y, buf16, width); + break; + case GD_TIFF_RGB: + TiffWriteConvertRowRGB16(im, y, buf16, width); + break; + case GD_TIFF_GRAY: + TiffWriteConvertRowGray16(im, y, buf16, width, opts->minIsWhite); + break; + default: + break; + } + if (TIFFWriteScanline(tif, scanbuf, y, 0) < 0) { + gdFree(scanbuf); + gd_error("gd-tiff write: could not write scanline"); + return 0; + } + } + } else { + size_t scanline_size = (size_t)TIFFScanlineSize(tif); + scanbuf = (uint8_t *)gdMalloc(scanline_size); + if (scanbuf == NULL) { + gd_error("gd-tiff write: could not allocate scanline buffer"); + return 0; + } + for (y = 0; y < height; y++) { + switch (opts->colorspace) { + case GD_TIFF_RGBA: + TiffWriteConvertRowRGBA8(im, y, scanbuf, width); + break; + case GD_TIFF_RGB: + TiffWriteConvertRowRGB8(im, y, scanbuf, width); + break; + case GD_TIFF_GRAY: + TiffWriteConvertRowGray8(im, y, scanbuf, width, opts->minIsWhite); + break; + default: + break; + } + if (TIFFWriteScanline(tif, scanbuf, y, 0) < 0) { + gdFree(scanbuf); + gd_error("gd-tiff write: could not write scanline"); + return 0; + } + } + } + + gdFree(scanbuf); + + if (!TIFFWriteDirectory(tif)) { + gd_error("gd-tiff write: could not write directory for page %d", write->pageCount); + return 0; + } + + write->pageCount++; + return 1; +} + +static void TiffWriteFree(gdTiffWritePtr write) +{ + if (write == NULL) + return; + if (write->tif) { + TIFFClose(write->tif); + write->tif = NULL; + } + if (write->th) { + gdFree(write->th); + write->th = NULL; + } + if (write->ownsCtx && write->out) { + write->out->gd_free(write->out); + write->out = NULL; + } + gdFree(write); +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpen(FILE *outFile, const gdTiffWriteOptions *options) +{ + gdIOCtx *out; + gdTiffWritePtr write; + + if (outFile == NULL) + return NULL; + out = gdNewFileCtx(outFile); + if (out == NULL) + return NULL; + write = gdTiffWriteOpenCtx(out, options); + if (write == NULL) { + out->gd_free(out); + return NULL; + } + write->ownsCtx = 1; + return write; +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenCtx(gdIOCtxPtr out, const gdTiffWriteOptions *options) +{ + gdTiffWritePtr write; + tiff_handle *th; + TIFF *tif; + + if (out == NULL) + return NULL; + + write = (gdTiffWritePtr)gdCalloc(1, sizeof(struct gdTiffWriteStruct)); + if (write == NULL) + return NULL; + + write->out = out; + write->ownsCtx = 0; + + if (options != NULL) { + write->options = *options; + } else { + memset(&write->options, 0, sizeof(write->options)); + } + TiffWriteSetDefaults(&write->options); + + if (!TiffWriteValidateOptions(&write->options)) { + gdFree(write); + return NULL; + } + + th = new_tiff_handle(out, 0); + if (th == NULL) { + gdFree(write); + return NULL; + } + write->th = th; + + tif = TIFFClientOpen("", "w", th, tiff_readproc, tiff_writeproc, tiff_seekproc, tiff_closeproc, + tiff_sizeproc, tiff_mapproc, tiff_unmapproc); + if (tif == NULL) { + gdFree(th); + gdFree(write); + gd_error("gd-tiff write: could not open TIFF for writing"); + return NULL; + } + write->tif = tif; + + return write; +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenPtr(const gdTiffWriteOptions *options) +{ + gdIOCtx *out; + gdTiffWritePtr write; + + out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + write = gdTiffWriteOpenCtx(out, options); + if (write == NULL) { + out->gd_free(out); + return NULL; + } + write->ownsCtx = 1; + write->memoryWriter = 1; + return write; +} + +BGD_DECLARE(int) gdTiffWriteAddImage(gdTiffWritePtr write, gdImagePtr image) +{ + if (write == NULL || image == NULL || write->finalized) + return 0; + if (!image->trueColor) { + gd_error("gd-tiff write: only truecolor images are supported by the " + "new write API"); + return 0; + } + return TiffWriteWritePage(write, image); +} + +BGD_DECLARE(void) gdTiffWriteClose(gdTiffWritePtr write) +{ + if (write == NULL) + return; + write->finalized = 1; + TiffWriteFree(write); +} + +BGD_DECLARE(void *) gdTiffWritePtrFinish(gdTiffWritePtr write, int *size) +{ + void *rv = NULL; + + if (size != NULL) + *size = 0; + if (write == NULL || !write->memoryWriter) { + TiffWriteFree(write); + return NULL; + } + write->finalized = 1; + if (write->tif != NULL) { + TIFFClose(write->tif); + write->tif = NULL; + } + if (write->out != NULL) { + rv = gdDPExtractData(write->out, size); + } + TiffWriteFree(write); + return rv; +} + +#else + +static void _noTiffError(void) { gd_error("TIFF image support has been disabled\n"); } + +BGD_DECLARE(void) gdImageTiffCtx(gdImagePtr image, gdIOCtx *out) +{ + ARG_NOT_USED(image); + ARG_NOT_USED(out); + _noTiffError(); +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffCtx(gdIOCtx *infile) +{ + ARG_NOT_USED(infile); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiff(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromTiffPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(void) gdImageTiff(gdImagePtr im, FILE *outFile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + _noTiffError(); +} + +BGD_DECLARE(void *) gdImageTiffPtr(gdImagePtr im, int *size) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpen(FILE *fd) +{ + ARG_NOT_USED(fd); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenCtx(gdIOCtxPtr in) +{ + ARG_NOT_USED(in); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdTiffReadPtr) gdTiffReadOpenPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(void) gdTiffReadClose(gdTiffReadPtr tiff) +{ + ARG_NOT_USED(tiff); + _noTiffError(); +} + +BGD_DECLARE(int) gdTiffReadGetInfo(gdTiffReadPtr tiff, gdTiffInfo *info) +{ + ARG_NOT_USED(tiff); + ARG_NOT_USED(info); + _noTiffError(); + return 0; +} + +BGD_DECLARE(int) +gdTiffReadNextImage(gdTiffReadPtr tiff, gdTiffPageInfo *info, gdImagePtr *image) +{ + ARG_NOT_USED(tiff); + ARG_NOT_USED(info); + ARG_NOT_USED(image); + _noTiffError(); + return -1; +} + +BGD_DECLARE(gdImagePtr) gdTiffReadCloneImage(gdTiffReadPtr tiff) +{ + ARG_NOT_USED(tiff); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(int) gdTiffIsMultiPage(FILE *fd) +{ + ARG_NOT_USED(fd); + _noTiffError(); + return -1; +} + +BGD_DECLARE(int) gdTiffIsMultiPageCtx(gdIOCtxPtr in) +{ + ARG_NOT_USED(in); + _noTiffError(); + return -1; +} + +BGD_DECLARE(int) gdTiffIsMultiPagePtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noTiffError(); + return -1; +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpen(FILE *outFile, const gdTiffWriteOptions *options) +{ + ARG_NOT_USED(outFile); + ARG_NOT_USED(options); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenCtx(gdIOCtxPtr out, const gdTiffWriteOptions *options) +{ + ARG_NOT_USED(out); + ARG_NOT_USED(options); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(gdTiffWritePtr) +gdTiffWriteOpenPtr(const gdTiffWriteOptions *options) +{ + ARG_NOT_USED(options); + _noTiffError(); + return NULL; +} + +BGD_DECLARE(int) gdTiffWriteAddImage(gdTiffWritePtr write, gdImagePtr image) +{ + ARG_NOT_USED(write); + ARG_NOT_USED(image); + _noTiffError(); + return 0; +} + +BGD_DECLARE(void) gdTiffWriteClose(gdTiffWritePtr write) +{ + ARG_NOT_USED(write); + _noTiffError(); +} + +BGD_DECLARE(void *) gdTiffWritePtrFinish(gdTiffWritePtr write, int *size) +{ + ARG_NOT_USED(write); + ARG_NOT_USED(size); + _noTiffError(); + return NULL; +} + +#endif diff --git a/ext/gd/libgd/gd_topal.c b/ext/gd/libgd/gd_topal.c index 2a9fb3d608dc..7beefbc10b7e 100644 --- a/ext/gd/libgd/gd_topal.c +++ b/ext/gd/libgd/gd_topal.c @@ -2,12 +2,12 @@ correct use of stub (sigh). */ /* 2.0.12: a new adaptation from the same original, this time - by Barend Gehrels. My attempt to incorporate alpha channel - into the result worked poorly and degraded the quality of - palette conversion even when the source contained no - alpha channel data. This version does not attempt to produce - an output file with transparency in some of the palette - indexes, which, in practice, doesn't look so hot anyway. TBB */ + by Barend Gehrels. My attempt to incorporate alpha channel + into the result worked poorly and degraded the quality of + palette conversion even when the source contained no + alpha channel data. This version does not attempt to produce + an output file with transparency in some of the palette + indexes, which, in practice, doesn't look so hot anyway. TBB */ /* * gd_topal, adapted from jquant2.c @@ -28,34 +28,42 @@ * fundamental assumptions even hold with an irregularly spaced color map. */ +/** + * File: Color Quantization + * + * Functions for truecolor to palette conversion + */ + /* * THOMAS BOUTELL & BAREND GEHRELS, february 2003 * adapted the code to work within gd rather than within libjpeg. * If it is not working, it's not Thomas G. Lane's fault. */ - -#include #include "gd.h" #include "gdhelpers.h" +#include + +#ifdef HAVE_LIBIMAGEQUANT +#include +#endif /* (Re)define some defines known by libjpeg */ #define QUANT_2PASS_SUPPORTED -#define RGB_RED 0 -#define RGB_GREEN 1 -#define RGB_BLUE 2 +#define RGB_RED 0 +#define RGB_GREEN 1 +#define RGB_BLUE 2 #define JSAMPLE unsigned char -#define MAXJSAMPLE (gdMaxColors-1) +#define MAXJSAMPLE (gdMaxColors - 1) #define BITS_IN_JSAMPLE 8 -#define JSAMPROW int* +#define JSAMPROW int * #define JDIMENSION int #define METHODDEF(type) static type -#define LOCAL(type) static type - +#define LOCAL(type) static type /* We assume that right shift corresponds to signed division by 2 with * rounding towards minus infinity. This is correct for typical "arithmetic @@ -68,22 +76,25 @@ */ #ifdef RIGHT_SHIFT_IS_UNSIGNED -#define SHIFT_TEMPS INT32 shift_temp; -#define RIGHT_SHIFT(x,shft) \ - ((shift_temp = (x)) < 0 ? \ - (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ - (shift_temp >> (shft))) +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x, shft) \ + ((shift_temp = (x)) < 0 ? (shift_temp >> (shft)) | ((~((INT32)0)) << (32 - (shft))) \ + : (shift_temp >> (shft))) #else #define SHIFT_TEMPS -#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#define RIGHT_SHIFT(x, shft) ((x) >> (shft)) #endif - -#define range_limit(x) { if(x<0) x=0; if (x>255) x=255; } - +#define range_limit(x) \ + { \ + if (x < 0) \ + x = 0; \ + if (x > 255) \ + x = 255; \ + } #ifndef INT16 -#define INT16 short +#define INT16 short #endif #ifndef UINT16 @@ -98,8 +109,6 @@ #define FAR #endif - - #ifndef boolean #define boolean int #endif @@ -112,13 +121,11 @@ #define FALSE 0 #endif - #define input_buf (oim->tpixels) #define output_buf (nim->pixels) #ifdef QUANT_2PASS_SUPPORTED - /* * This module implements the well-known Heckbert paradigm for color * quantization. Most of the ideas used here can be traced back to @@ -165,9 +172,9 @@ * probably need to change these scale factors. */ -#define R_SCALE 2 /* scale R distances by this much */ -#define G_SCALE 3 /* scale G distances by this much */ -#define B_SCALE 1 /* and B by this much */ +#define R_SCALE 2 /* scale R distances by this much */ +#define G_SCALE 3 /* scale G distances by this much */ +#define B_SCALE 1 /* and B by this much */ /* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B @@ -192,7 +199,6 @@ #define C2_SCALE B_SCALE #endif - /* * First we have the histogram data structure and routines for creating it. * @@ -219,34 +225,32 @@ * arrays are in far memory (same arrangement as we use for image arrays). */ -#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ +#define MAXNUMCOLORS (MAXJSAMPLE + 1) /* maximum size of colormap */ /* These will do the right thing for either R,G,B or B,G,R color order, * but you may not like the results for other color orders. */ -#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ -#define HIST_C1_BITS 6 /* bits of precision in G histogram */ -#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ /* Number of elements along histogram axes. */ -#define HIST_C0_ELEMS (1<histogram; - int row; - JDIMENSION col; - int width = oim->sx; - int num_rows = oim->sy; - - for (row = 0; row < num_rows; row++) - { - ptr = input_buf[row]; - for (col = width; col > 0; col--) - { - int r = gdTrueColorGetRed (*ptr) >> C0_SHIFT; - int g = gdTrueColorGetGreen (*ptr) >> C1_SHIFT; - int b = gdTrueColorGetBlue (*ptr) >> C2_SHIFT; - /* 2.0.12: Steven Brown: support a single totally transparent - color in the original. */ - if ((oim->transparent >= 0) && (*ptr == oim->transparent)) - { - ptr++; - continue; - } - /* get pixel value and index into the histogram */ - histp = &histogram[r][g][b]; - /* increment, check for overflow and undo increment if so. */ - if (++(*histp) == 0) - (*histp)--; - ptr++; - } + register JSAMPROW ptr; + register histptr histp; + register hist3d histogram = cquantize->histogram; + int row; + JDIMENSION col; + int width = oim->sx; + int num_rows = oim->sy; + + (void)nim; + + for (row = 0; row < num_rows; row++) { + ptr = input_buf[row]; + for (col = width; col > 0; col--) { + int r = gdTrueColorGetRed(*ptr) >> C0_SHIFT; + int g = gdTrueColorGetGreen(*ptr) >> C1_SHIFT; + int b = gdTrueColorGetBlue(*ptr) >> C2_SHIFT; + /* 2.0.12: Steven Brown: support a single totally transparent + color in the original. */ + if ((oim->transparent >= 0) && (*ptr == oim->transparent)) { + ptr++; + continue; + } + /* get pixel value and index into the histogram */ + histp = &histogram[r][g][b]; + /* increment, check for overflow and undo increment if so. */ + if (++(*histp) == 0) + (*histp)--; + ptr++; + } } } - /* * Next we have the really interesting routines: selection of a colormap * given the completed histogram. @@ -356,394 +353,357 @@ prescan_quantize (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) * subset of the input color space (to histogram precision). */ -typedef struct -{ - /* The bounds of the box (inclusive); expressed as histogram indexes */ - int c0min, c0max; - int c1min, c1max; - int c2min, c2max; - /* The volume (actually 2-norm) of the box */ - INT32 volume; - /* The number of nonzero histogram cells within this box */ - long colorcount; -} -box; +typedef struct { + /* The bounds of the box (inclusive); expressed as histogram indexes */ + int c0min, c0max; + int c1min, c1max; + int c2min, c2max; + /* The volume (actually 2-norm) of the box */ + INT32 volume; + /* The number of nonzero histogram cells within this box */ + long colorcount; +} box; typedef box *boxptr; - -LOCAL (boxptr) find_biggest_color_pop (boxptr boxlist, int numboxes) +LOCAL(boxptr) +find_biggest_color_pop(boxptr boxlist, int numboxes) /* Find the splittable box with the largest color population */ /* Returns NULL if no splittable boxes remain */ { - register boxptr boxp; - register int i; - register long maxc = 0; - boxptr which = NULL; - - for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) - { - if (boxp->colorcount > maxc && boxp->volume > 0) - { - which = boxp; - maxc = boxp->colorcount; - } + register boxptr boxp; + register int i; + register long maxc = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->colorcount > maxc && boxp->volume > 0) { + which = boxp; + maxc = boxp->colorcount; + } } - return which; + return which; } - -LOCAL (boxptr) find_biggest_volume (boxptr boxlist, int numboxes) +LOCAL(boxptr) +find_biggest_volume(boxptr boxlist, int numboxes) /* Find the splittable box with the largest (scaled) volume */ /* Returns NULL if no splittable boxes remain */ { - register boxptr boxp; - register int i; - register INT32 maxv = 0; - boxptr which = NULL; - - for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) - { - if (boxp->volume > maxv) - { - which = boxp; - maxv = boxp->volume; - } + register boxptr boxp; + register int i; + register INT32 maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->volume > maxv) { + which = boxp; + maxv = boxp->volume; + } } - return which; + return which; } - -LOCAL (void) - update_box (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, boxptr boxp) +LOCAL(void) +update_box(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, boxptr boxp) { - hist3d histogram = cquantize->histogram; - histptr histp; - int c0, c1, c2; - int c0min, c0max, c1min, c1max, c2min, c2max; - INT32 dist0, dist1, dist2; - long ccount; - - c0min = boxp->c0min; - c0max = boxp->c0max; - c1min = boxp->c1min; - c1max = boxp->c1max; - c2min = boxp->c2min; - c2max = boxp->c2max; - - if (c0max > c0min) - for (c0 = c0min; c0 <= c0max; c0++) - for (c1 = c1min; c1 <= c1max; c1++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++) - if (*histp++ != 0) - { - boxp->c0min = c0min = c0; - goto have_c0min; - } - } + hist3d histogram = cquantize->histogram; + histptr histp; + int c0, c1, c2; + int c0min, c0max, c1min, c1max, c2min, c2max; + INT32 dist0, dist1, dist2; + long ccount; + (void)oim; + (void)nim; + + c0min = boxp->c0min; + c0max = boxp->c0max; + c1min = boxp->c1min; + c1max = boxp->c1max; + c2min = boxp->c2min; + c2max = boxp->c2max; + + if (c0max > c0min) + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0min = c0min = c0; + goto have_c0min; + } + } have_c0min: - if (c0max > c0min) - for (c0 = c0max; c0 >= c0min; c0--) - for (c1 = c1min; c1 <= c1max; c1++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++) - if (*histp++ != 0) - { - boxp->c0max = c0max = c0; - goto have_c0max; - } - } + if (c0max > c0min) + for (c0 = c0max; c0 >= c0min; c0--) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0max = c0max = c0; + goto have_c0max; + } + } have_c0max: - if (c1max > c1min) - for (c1 = c1min; c1 <= c1max; c1++) - for (c0 = c0min; c0 <= c0max; c0++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++) - if (*histp++ != 0) - { - boxp->c1min = c1min = c1; - goto have_c1min; - } - } + if (c1max > c1min) + for (c1 = c1min; c1 <= c1max; c1++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1min = c1min = c1; + goto have_c1min; + } + } have_c1min: - if (c1max > c1min) - for (c1 = c1max; c1 >= c1min; c1--) - for (c0 = c0min; c0 <= c0max; c0++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++) - if (*histp++ != 0) - { - boxp->c1max = c1max = c1; - goto have_c1max; - } - } + if (c1max > c1min) + for (c1 = c1max; c1 >= c1min; c1--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1max = c1max = c1; + goto have_c1max; + } + } have_c1max: - if (c2max > c2min) - for (c2 = c2min; c2 <= c2max; c2++) - for (c0 = c0min; c0 <= c0max; c0++) - { - histp = &histogram[c0][c1min][c2]; - for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) - if (*histp != 0) - { - boxp->c2min = c2min = c2; - goto have_c2min; - } - } + if (c2max > c2min) + for (c2 = c2min; c2 <= c2max; c2++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = &histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2min = c2min = c2; + goto have_c2min; + } + } have_c2min: - if (c2max > c2min) - for (c2 = c2max; c2 >= c2min; c2--) - for (c0 = c0min; c0 <= c0max; c0++) - { - histp = &histogram[c0][c1min][c2]; - for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) - if (*histp != 0) - { - boxp->c2max = c2max = c2; - goto have_c2max; - } - } + if (c2max > c2min) + for (c2 = c2max; c2 >= c2min; c2--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = &histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2max = c2max = c2; + goto have_c2max; + } + } have_c2max: - /* Update box volume. - * We use 2-norm rather than real volume here; this biases the method - * against making long narrow boxes, and it has the side benefit that - * a box is splittable iff norm > 0. - * Since the differences are expressed in histogram-cell units, - * we have to shift back to JSAMPLE units to get consistent distances; - * after which, we scale according to the selected distance scale factors. - */ - dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; - dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; - dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; - boxp->volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2; - - /* Now scan remaining volume of box and compute population */ - ccount = 0; - for (c0 = c0min; c0 <= c0max; c0++) - for (c1 = c1min; c1 <= c1max; c1++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++, histp++) - if (*histp != 0) - { - ccount++; - } - } - boxp->colorcount = ccount; + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + boxp->volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++, histp++) + if (*histp != 0) { + ccount++; + } + } + boxp->colorcount = ccount; } - -LOCAL (int) -median_cut (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, - boxptr boxlist, int numboxes, int desired_colors) +LOCAL(int) +median_cut(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, boxptr boxlist, int numboxes, + int desired_colors) /* Repeatedly select and split the largest box until we have enough boxes */ { - int n, lb; - int c0, c1, c2, cmax; - register boxptr b1, b2; - - while (numboxes < desired_colors) - { - /* Select box to split. - * Current algorithm: by population for first half, then by volume. - */ - if (numboxes * 2 <= desired_colors) - { - b1 = find_biggest_color_pop (boxlist, numboxes); - } - else - { - b1 = find_biggest_volume (boxlist, numboxes); - } - if (b1 == NULL) /* no splittable boxes left! */ - break; - b2 = &boxlist[numboxes]; /* where new box will go */ - /* Copy the color bounds to the new box. */ - b2->c0max = b1->c0max; - b2->c1max = b1->c1max; - b2->c2max = b1->c2max; - b2->c0min = b1->c0min; - b2->c1min = b1->c1min; - b2->c2min = b1->c2min; - /* Choose which axis to split the box on. - * Current algorithm: longest scaled axis. - * See notes in update_box about scaling distances. - */ - c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; - c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; - c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; - /* We want to break any ties in favor of green, then red, blue last. - * This code does the right thing for R,G,B or B,G,R color orders only. - */ + int n, lb; + int c0, c1, c2, cmax; + register boxptr b1, b2; + + while (numboxes < desired_colors) { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + if (numboxes * 2 <= desired_colors) { + b1 = find_biggest_color_pop(boxlist, numboxes); + } else { + b1 = find_biggest_volume(boxlist, numboxes); + } + if (b1 == NULL) /* no splittable boxes left! */ + break; + b2 = &boxlist[numboxes]; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->c0max = b1->c0max; + b2->c1max = b1->c1max; + b2->c2max = b1->c2max; + b2->c0min = b1->c0min; + b2->c1min = b1->c1min; + b2->c2min = b1->c2min; + /* Choose which axis to split the box on. + * Current algorithm: longest scaled axis. + * See notes in update_box about scaling distances. + */ + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + /* We want to break any ties in favor of green, then red, blue last. + * This code does the right thing for R,G,B or B,G,R color orders only. + */ #if RGB_RED == 0 - cmax = c1; - n = 1; - if (c0 > cmax) - { - cmax = c0; - n = 0; - } - if (c2 > cmax) - { - n = 2; - } + cmax = c1; + n = 1; + if (c0 > cmax) { + cmax = c0; + n = 0; + } + if (c2 > cmax) { + n = 2; + } #else - cmax = c1; - n = 1; - if (c2 > cmax) - { - cmax = c2; - n = 2; - } - if (c0 > cmax) - { - n = 0; - } + cmax = c1; + n = 1; + if (c2 > cmax) { + cmax = c2; + n = 2; + } + if (c0 > cmax) { + n = 0; + } #endif - /* Choose split point along selected axis, and update box bounds. - * Current algorithm: split at halfway point. - * (Since the box has been shrunk to minimum volume, - * any split will produce two nonempty subboxes.) - * Note that lb value is max for lower box, so must be < old max. - */ - switch (n) - { - case 0: - lb = (b1->c0max + b1->c0min) / 2; - b1->c0max = lb; - b2->c0min = lb + 1; - break; - case 1: - lb = (b1->c1max + b1->c1min) / 2; - b1->c1max = lb; - b2->c1min = lb + 1; - break; - case 2: - lb = (b1->c2max + b1->c2min) / 2; - b1->c2max = lb; - b2->c2min = lb + 1; - break; - } - /* Update stats for boxes */ - update_box (oim, nim, cquantize, b1); - update_box (oim, nim, cquantize, b2); - numboxes++; + /* Choose split point along selected axis, and update box bounds. + * Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + switch (n) { + case 0: + lb = (b1->c0max + b1->c0min) / 2; + b1->c0max = lb; + b2->c0min = lb + 1; + break; + case 1: + lb = (b1->c1max + b1->c1min) / 2; + b1->c1max = lb; + b2->c1min = lb + 1; + break; + case 2: + lb = (b1->c2max + b1->c2min) / 2; + b1->c2max = lb; + b2->c2min = lb + 1; + break; + } + /* Update stats for boxes */ + update_box(oim, nim, cquantize, b1); + update_box(oim, nim, cquantize, b2); + numboxes++; } - return numboxes; + return numboxes; } - -LOCAL (void) - compute_color (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, - boxptr boxp, int icolor) -/* Compute representative color for a box, put it in colormap[icolor] */ +LOCAL(void) +compute_color(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, boxptr boxp, int icolor) { - /* Current algorithm: mean weighted by pixels (not colors) */ - /* Note it is important to get the rounding correct! */ - hist3d histogram = cquantize->histogram; - histptr histp; - int c0, c1, c2; - int c0min, c0max, c1min, c1max, c2min, c2max; - long count = 0; /* 2.0.28: = 0 */ - long total = 0; - long c0total = 0; - long c1total = 0; - long c2total = 0; - - c0min = boxp->c0min; - c0max = boxp->c0max; - c1min = boxp->c1min; - c1max = boxp->c1max; - c2min = boxp->c2min; - c2max = boxp->c2max; - - for (c0 = c0min; c0 <= c0max; c0++) - for (c1 = c1min; c1 <= c1max; c1++) - { - histp = &histogram[c0][c1][c2min]; - for (c2 = c2min; c2 <= c2max; c2++) - { - if ((count = *histp++) != 0) - { - total += count; - c0total += - ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count; - c1total += - ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count; - c2total += - ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count; - } - } - } - - /* 2.0.16: Paul den Dulk found an occasion where total can be 0 */ - if (total) - { - nim->red[icolor] = (int) ((c0total + (total >> 1)) / total); - nim->green[icolor] = (int) ((c1total + (total >> 1)) / total); - nim->blue[icolor] = (int) ((c2total + (total >> 1)) / total); - } - else - { - nim->red[icolor] = 255; - nim->green[icolor] = 255; - nim->blue[icolor] = 255; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0, c1, c2; + int c0min, c0max, c1min, c1max, c2min, c2max; + long count = 0; /* 2.0.28: = 0 */ + long total = 0; + long c0total = 0; + long c1total = 0; + long c2total = 0; + (void)oim; + + c0min = boxp->c0min; + c0max = boxp->c0max; + c1min = boxp->c1min; + c1max = boxp->c1max; + c2min = boxp->c2min; + c2max = boxp->c2max; + + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = &histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) { + if ((count = *histp++) != 0) { + total += count; + c0total += ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count; + c1total += ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count; + c2total += ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count; + } + } + } + + /* 2.0.16: Paul den Dulk found an occasion where total can be 0 */ + if (total) { + nim->red[icolor] = (int)((c0total + (total >> 1)) / total); + nim->green[icolor] = (int)((c1total + (total >> 1)) / total); + nim->blue[icolor] = (int)((c2total + (total >> 1)) / total); + } else { + nim->red[icolor] = 255; + nim->green[icolor] = 255; + nim->blue[icolor] = 255; } - nim->open[icolor] = 0; + nim->open[icolor] = 0; } - -LOCAL (void) -select_colors (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int desired_colors) +LOCAL(void) +select_colors(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int desired_colors) /* Master routine for color selection */ { - boxptr boxlist; - int numboxes; - int i; - - /* Allocate workspace for box list */ - boxlist = (boxptr) safe_emalloc(desired_colors, sizeof (box), 1); - /* Initialize one box containing whole space */ - numboxes = 1; - boxlist[0].c0min = 0; - boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; - boxlist[0].c1min = 0; - boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; - boxlist[0].c2min = 0; - boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; - /* Shrink it to actually-used volume and set its statistics */ - update_box (oim, nim, cquantize, &boxlist[0]); - /* Perform median-cut to produce final box list */ - numboxes = median_cut (oim, nim, cquantize, boxlist, numboxes, desired_colors); - /* Compute the representative color for each box, fill colormap */ - for (i = 0; i < numboxes; i++) - compute_color (oim, nim, cquantize, &boxlist[i], i); - nim->colorsTotal = numboxes; - - /* If we had a pure transparency color, add it as the last palette entry. - * Skip incrementing the color count so that the dither / matching phase - * won't use it on pixels that shouldn't have been transparent. We'll - * increment it after all that finishes. */ - if (oim->transparent >= 0) - { - /* Save the transparent color. */ - nim->red[nim->colorsTotal] = gdTrueColorGetRed (oim->transparent); - nim->green[nim->colorsTotal] = gdTrueColorGetGreen (oim->transparent); - nim->blue[nim->colorsTotal] = gdTrueColorGetBlue (oim->transparent); - nim->alpha[nim->colorsTotal] = gdAlphaTransparent; - nim->open[nim->colorsTotal] = 0; + boxptr boxlist; + int numboxes; + int i; + + /* Allocate workspace for box list */ + /* This can't happen because we clamp desired_colors at gdMaxColors, + but anyway */ + if (overflow2(desired_colors, sizeof(box))) { + return; + } + boxlist = (boxptr)gdMalloc(desired_colors * sizeof(box)); + if (!boxlist) { + return; + } + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].c0min = 0; + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c1min = 0; + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c2min = 0; + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + /* Shrink it to actually-used volume and set its statistics */ + update_box(oim, nim, cquantize, &boxlist[0]); + /* Perform median-cut to produce final box list */ + numboxes = median_cut(oim, nim, cquantize, boxlist, numboxes, desired_colors); + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color(oim, nim, cquantize, &boxlist[i], i); + nim->colorsTotal = numboxes; + + /* If we had a pure transparency color, add it as the last palette entry. + * Skip incrementing the color count so that the dither / matching phase + * won't use it on pixels that shouldn't have been transparent. We'll + * increment it after all that finishes. */ + if (oim->transparent >= 0) { + /* Save the transparent color. */ + nim->red[nim->colorsTotal] = gdTrueColorGetRed(oim->transparent); + nim->green[nim->colorsTotal] = gdTrueColorGetGreen(oim->transparent); + nim->blue[nim->colorsTotal] = gdTrueColorGetBlue(oim->transparent); + nim->alpha[nim->colorsTotal] = gdAlphaTransparent; + nim->open[nim->colorsTotal] = 0; } - gdFree (boxlist); + gdFree(boxlist); } - /* * These routines are concerned with the time-critical task of mapping input * colors to the nearest color in the selected colormap. @@ -797,20 +757,18 @@ select_colors (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int d * it might not be any faster, and it's certainly more complicated. */ - /* log2(histogram cells in update box) for each axis; this can be adjusted */ -#define BOX_C0_LOG (HIST_C0_BITS-3) -#define BOX_C1_LOG (HIST_C1_BITS-3) -#define BOX_C2_LOG (HIST_C2_BITS-3) - -#define BOX_C0_ELEMS (1<colorsTotal; - int maxc0, maxc1, maxc2; - int centerc0, centerc1, centerc2; - int i, x, ncolors; - INT32 minmaxdist, min_dist, max_dist, tdist; - INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ - - /* Compute true coordinates of update box's upper corner and center. - * Actually we compute the coordinates of the center of the upper-corner - * histogram cell, which are the upper bounds of the volume we care about. - * Note that since ">>" rounds down, the "center" values may be closer to - * min than to max; hence comparisons to them must be "<=", not "<". - */ - maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); - centerc0 = (minc0 + maxc0) >> 1; - maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); - centerc1 = (minc1 + maxc1) >> 1; - maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); - centerc2 = (minc2 + maxc2) >> 1; - - /* For each color in colormap, find: - * 1. its minimum squared-distance to any point in the update box - * (zero if color is within update box); - * 2. its maximum squared-distance to any point in the update box. - * Both of these can be found by considering only the corners of the box. - * We save the minimum distance for each color in mindist[]; - * only the smallest maximum distance is of interest. - */ - minmaxdist = 0x7FFFFFFFL; - - for (i = 0; i < numcolors; i++) - { - /* We compute the squared-c0-distance term, then add in the other two. */ - x = nim->red[i]; - if (x < minc0) - { - tdist = (x - minc0) * C0_SCALE; - min_dist = tdist * tdist; - tdist = (x - maxc0) * C0_SCALE; - max_dist = tdist * tdist; - } - else if (x > maxc0) - { - tdist = (x - maxc0) * C0_SCALE; - min_dist = tdist * tdist; - tdist = (x - minc0) * C0_SCALE; - max_dist = tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - min_dist = 0; - if (x <= centerc0) - { - tdist = (x - maxc0) * C0_SCALE; - max_dist = tdist * tdist; - } - else - { - tdist = (x - minc0) * C0_SCALE; - max_dist = tdist * tdist; - } - } - - x = nim->green[i]; - if (x < minc1) - { - tdist = (x - minc1) * C1_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc1) * C1_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc1) - { - tdist = (x - maxc1) * C1_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc1) * C1_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc1) - { - tdist = (x - maxc1) * C1_SCALE; - max_dist += tdist * tdist; - } - else - { - tdist = (x - minc1) * C1_SCALE; - max_dist += tdist * tdist; - } - } - - x = nim->blue[i]; - if (x < minc2) - { - tdist = (x - minc2) * C2_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc2) * C2_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc2) - { - tdist = (x - maxc2) * C2_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc2) * C2_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc2) - { - tdist = (x - maxc2) * C2_SCALE; - max_dist += tdist * tdist; - } - else - { - tdist = (x - minc2) * C2_SCALE; - max_dist += tdist * tdist; - } - } - - mindist[i] = min_dist; /* save away the results */ - if (max_dist < minmaxdist) - minmaxdist = max_dist; + int numcolors = nim->colorsTotal; + int maxc0, maxc1, maxc2; + int centerc0, centerc1, centerc2; + int i, x, ncolors; + INT32 minmaxdist, min_dist, max_dist, tdist; + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + (void)oim; + (void)cquantize; + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + centerc0 = (minc0 + maxc0) >> 1; + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + centerc1 = (minc1 + maxc1) >> 1; + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + centerc2 = (minc2 + maxc2) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) { + /* We compute the squared-c0-distance term, then add in the other two. + */ + x = nim->red[i]; + if (x < minc0) { + tdist = (x - minc0) * C0_SCALE; + min_dist = tdist * tdist; + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist * tdist; + } else if (x > maxc0) { + tdist = (x - maxc0) * C0_SCALE; + min_dist = tdist * tdist; + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist * tdist; + } else { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerc0) { + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist * tdist; + } else { + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist * tdist; + } + } + + x = nim->green[i]; + if (x < minc1) { + tdist = (x - minc1) * C1_SCALE; + min_dist += tdist * tdist; + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist * tdist; + } else if (x > maxc1) { + tdist = (x - maxc1) * C1_SCALE; + min_dist += tdist * tdist; + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist * tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc1) { + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist * tdist; + } else { + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist * tdist; + } + } + + x = nim->blue[i]; + if (x < minc2) { + tdist = (x - minc2) * C2_SCALE; + min_dist += tdist * tdist; + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist * tdist; + } else if (x > maxc2) { + tdist = (x - maxc2) * C2_SCALE; + min_dist += tdist * tdist; + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist * tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc2) { + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist * tdist; + } else { + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist * tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; } - /* Now we know that no cell in the update box is more than minmaxdist - * away from some colormap entry. Therefore, only colors that are - * within minmaxdist of some part of the box need be considered. - */ - ncolors = 0; - for (i = 0; i < numcolors; i++) - { - if (mindist[i] <= minmaxdist) - colorlist[ncolors++] = (JSAMPLE) i; + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = (JSAMPLE)i; } - return ncolors; + return ncolors; } - -LOCAL (void) find_best_colors ( - gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, - int minc0, int minc1, int minc2, - int numcolors, JSAMPLE colorlist[], - JSAMPLE bestcolor[]) +LOCAL(void) +find_best_colors(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int minc0, int minc1, + int minc2, int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) /* Find the closest colormap entry for each cell in the update box, * given the list of candidate colors prepared by find_nearby_colors. * Return the indexes of the closest entries in the bestcolor[] array. @@ -988,369 +919,350 @@ LOCAL (void) find_best_colors ( * find the distance from a colormap entry to successive cells in the box. */ { - int ic0, ic1, ic2; - int i, icolor; - register INT32 *bptr; /* pointer into bestdist[] array */ - JSAMPLE *cptr; /* pointer into bestcolor[] array */ - INT32 dist0, dist1; /* initial distance values */ - register INT32 dist2; /* current distance in inner loop */ - INT32 xx0, xx1; /* distance increments */ - register INT32 xx2; - INT32 inc0, inc1, inc2; /* initial values for increments */ - /* This array holds the distance to the nearest-so-far color for each cell */ - INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - - /* Initialize best-distance for each cell of the update box */ - bptr = bestdist; - for (i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS - 1; i >= 0; i--) - *bptr++ = 0x7FFFFFFFL; - - /* For each color selected by find_nearby_colors, - * compute its distance to the center of each cell in the box. - * If that's less than best-so-far, update best distance and color number. - */ - - /* Nominal steps between cell centers ("x" in Thomas article) */ -#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) -#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) -#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) - - for (i = 0; i < numcolors; i++) - { - int r, g, b; - icolor = colorlist[i]; - r = nim->red[icolor]; - g = nim->green[icolor]; - b = nim->blue[icolor]; - - /* Compute (square of) distance from minc0/c1/c2 to this color */ - inc0 = (minc0 - r) * C0_SCALE; - dist0 = inc0 * inc0; - inc1 = (minc1 - g) * C1_SCALE; - dist0 += inc1 * inc1; - inc2 = (minc2 - b) * C2_SCALE; - dist0 += inc2 * inc2; - /* Form the initial difference increments */ - inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; - inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; - inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; - /* Now loop over all cells in box, updating distance per Thomas method */ - bptr = bestdist; - cptr = bestcolor; - xx0 = inc0; - for (ic0 = BOX_C0_ELEMS - 1; ic0 >= 0; ic0--) - { - dist1 = dist0; - xx1 = inc1; - for (ic1 = BOX_C1_ELEMS - 1; ic1 >= 0; ic1--) - { - dist2 = dist1; - xx2 = inc2; - for (ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) - { - if (dist2 < *bptr) - { - *bptr = dist2; - *cptr = (JSAMPLE) icolor; - } - dist2 += xx2; - xx2 += 2 * STEP_C2 * STEP_C2; - bptr++; - cptr++; - } - dist1 += xx1; - xx1 += 2 * STEP_C1 * STEP_C1; - } - dist0 += xx0; - xx0 += 2 * STEP_C0 * STEP_C0; - } + int ic0, ic1, ic2; + int i, icolor; + register INT32 *bptr; /* pointer into bestdist[] array */ + JSAMPLE *cptr; /* pointer into bestcolor[] array */ + INT32 dist0, dist1; /* initial distance values */ + register INT32 dist2; /* current distance in inner loop */ + INT32 xx0, xx1; /* distance increments */ + register INT32 xx2; + INT32 inc0, inc1, inc2; /* initial values for increments */ + /* This array holds the distance to the nearest-so-far color for each cell + */ + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + (void)oim; + (void)cquantize; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS - 1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color + * number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + for (i = 0; i < numcolors; i++) { + int r, g, b; + icolor = colorlist[i]; + r = nim->red[icolor]; + g = nim->green[icolor]; + b = nim->blue[icolor]; + + /* Compute (square of) distance from minc0/c1/c2 to this color */ + inc0 = (minc0 - r) * C0_SCALE; + dist0 = inc0 * inc0; + inc1 = (minc1 - g) * C1_SCALE; + dist0 += inc1 * inc1; + inc2 = (minc2 - b) * C2_SCALE; + dist0 += inc2 * inc2; + /* Form the initial difference increments */ + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + /* Now loop over all cells in box, updating distance per Thomas method + */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inc0; + for (ic0 = BOX_C0_ELEMS - 1; ic0 >= 0; ic0--) { + dist1 = dist0; + xx1 = inc1; + for (ic1 = BOX_C1_ELEMS - 1; ic1 >= 0; ic1--) { + dist2 = dist1; + xx2 = inc2; + for (ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) { + if (dist2 < *bptr) { + *bptr = dist2; + *cptr = (JSAMPLE)icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_C2 * STEP_C2; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_C1 * STEP_C1; + } + dist0 += xx0; + xx0 += 2 * STEP_C0 * STEP_C0; + } } } - -LOCAL (void) -fill_inverse_cmap ( - gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, - int c0, int c1, int c2) +LOCAL(void) +fill_inverse_cmap(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int c0, int c1, + int c2) /* Fill the inverse-colormap entries in the update box that contains */ /* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ /* we can fill as many others as we wish.) */ { - hist3d histogram = cquantize->histogram; - int minc0, minc1, minc2; /* lower left corner of update box */ - int ic0, ic1, ic2; - register JSAMPLE *cptr; /* pointer into bestcolor[] array */ - register histptr cachep; /* pointer into main cache array */ - /* This array lists the candidate colormap indexes. */ - JSAMPLE colorlist[MAXNUMCOLORS]; - int numcolors; /* number of candidate colors */ - /* This array holds the actually closest colormap index for each cell. */ - JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - - /* Convert cell coordinates to update box ID */ - c0 >>= BOX_C0_LOG; - c1 >>= BOX_C1_LOG; - c2 >>= BOX_C2_LOG; - - /* Compute true coordinates of update box's origin corner. - * Actually we compute the coordinates of the center of the corner - * histogram cell, which are the lower bounds of the volume we care about. - */ - minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); - minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); - minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); - - /* Determine which colormap entries are close enough to be candidates - * for the nearest entry to some cell in the update box. - */ - numcolors = - find_nearby_colors (oim, nim, cquantize, minc0, minc1, minc2, colorlist); - find_best_colors (oim, nim, cquantize, minc0, minc1, minc2, numcolors, - colorlist, bestcolor); - - /* Save the best color numbers (plus 1) in the main cache array */ - c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ - c1 <<= BOX_C1_LOG; - c2 <<= BOX_C2_LOG; - cptr = bestcolor; - for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) - { - for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) - { - cachep = &histogram[c0 + ic0][c1 + ic1][c2]; - for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) - { - *cachep++ = (histcell) ((*cptr++) + 1); - } - } + hist3d histogram = cquantize->histogram; + int minc0, minc1, minc2; /* lower left corner of update box */ + int ic0, ic1, ic2; + register JSAMPLE *cptr; /* pointer into bestcolor[] array */ + register histptr cachep; /* pointer into main cache array */ + /* This array lists the candidate colormap indexes. */ + JSAMPLE colorlist[MAXNUMCOLORS]; + int numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Convert cell coordinates to update box ID */ + c0 >>= BOX_C0_LOG; + c1 >>= BOX_C1_LOG; + c2 >>= BOX_C2_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors(oim, nim, cquantize, minc0, minc1, minc2, colorlist); + find_best_colors(oim, nim, cquantize, minc0, minc1, minc2, numcolors, colorlist, bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + c1 <<= BOX_C1_LOG; + c2 <<= BOX_C2_LOG; + cptr = bestcolor; + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + cachep = &histogram[c0 + ic0][c1 + ic1][c2]; + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + *cachep++ = (histcell)((*cptr++) + 1); + } + } } } - /* * Map some rows of pixels to the output colormapped representation. */ -METHODDEF (void) -pass2_no_dither (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) +METHODDEF(void) +pass2_no_dither(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) { - register int *inptr; - register unsigned char *outptr; - int width = oim->sx; - int num_rows = oim->sy; - hist3d histogram = cquantize->histogram; - register int c0, c1, c2; - int row; - JDIMENSION col; - register histptr cachep; - - - for (row = 0; row < num_rows; row++) - { - inptr = input_buf[row]; - outptr = output_buf[row]; - for (col = width; col > 0; col--) - { - /* get pixel value and index into the cache */ - int r, g, b; - r = gdTrueColorGetRed (*inptr); - g = gdTrueColorGetGreen (*inptr); - /* - 2.0.24: inptr must not be incremented until after - transparency check, if any. Thanks to "Super Pikeman." - */ - b = gdTrueColorGetBlue (*inptr); - - /* If the pixel is transparent, we assign it the palette index that - * will later be added at the end of the palette as the transparent - * index. */ - if ((oim->transparent >= 0) && (oim->transparent == *inptr)) - { - *outptr++ = nim->colorsTotal; - inptr++; - continue; - } - inptr++; - c0 = r >> C0_SHIFT; - c1 = g >> C1_SHIFT; - c2 = b >> C2_SHIFT; - cachep = &histogram[c0][c1][c2]; - /* If we have not seen this color before, find nearest colormap entry */ - /* and update the cache */ - if (*cachep == 0) - fill_inverse_cmap (oim, nim, cquantize, c0, c1, c2); - /* Now emit the colormap index for this cell */ - *outptr++ = (*cachep - 1); - } + register int *inptr; + register unsigned char *outptr; + int width = oim->sx; + int num_rows = oim->sy; + hist3d histogram = cquantize->histogram; + register int c0, c1, c2; + int row; + JDIMENSION col; + register histptr cachep; + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the cache */ + int r, g, b; + r = gdTrueColorGetRed(*inptr); + g = gdTrueColorGetGreen(*inptr); + /* + 2.0.24: inptr must not be incremented until after + transparency check, if any. Thanks to "Super Pikeman." + */ + b = gdTrueColorGetBlue(*inptr); + + /* If the pixel is transparent, we assign it the palette index that + * will later be added at the end of the palette as the transparent + * index. */ + if ((oim->transparent >= 0) && (oim->transparent == *inptr)) { + *outptr++ = nim->colorsTotal; + inptr++; + continue; + } + inptr++; + c0 = r >> C0_SHIFT; + c1 = g >> C1_SHIFT; + c2 = b >> C2_SHIFT; + cachep = &histogram[c0][c1][c2]; + /* If we have not seen this color before, find nearest colormap + * entry */ + /* and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(oim, nim, cquantize, c0, c1, c2); + /* Now emit the colormap index for this cell */ + *outptr++ = (*cachep - 1); + } } } - -METHODDEF (void) -pass2_fs_dither (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) +METHODDEF(void) +pass2_fs_dither(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) { - hist3d histogram = cquantize->histogram; - register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ - LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ - LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ - register FSERRPTR errorptr; /* => fserrors[] at column before current */ - histptr cachep; - int dir; /* +1 or -1 depending on direction */ - int dir3; /* 3*dir, for advancing inptr & errorptr */ - int row; - JDIMENSION col; - int *inptr; /* => current input pixel */ - unsigned char *outptr; /* => current output pixel */ - int width = oim->sx; - int num_rows = oim->sy; - int *colormap0 = nim->red; - int *colormap1 = nim->green; - int *colormap2 = nim->blue; - int *error_limit = cquantize->error_limiter; - - - SHIFT_TEMPS for (row = 0; row < num_rows; row++) + hist3d histogram = cquantize->histogram; + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + histptr cachep; + int dir; /* +1 or -1 depending on direction */ + int dir3; /* 3*dir, for advancing inptr & errorptr */ + int row; + JDIMENSION col; + int *inptr; /* => current input pixel */ + unsigned char *outptr; /* => current output pixel */ + int width = oim->sx; + int num_rows = oim->sy; + int *colormap0 = nim->red; + int *colormap1 = nim->green; + int *colormap2 = nim->blue; + int *error_limit = cquantize->error_limiter; + + SHIFT_TEMPS for (row = 0; row < num_rows; row++) { - inptr = input_buf[row]; - outptr = output_buf[row]; - if (cquantize->on_odd_row) - { - /* work right to left in this row */ - inptr += (width - 1) * 3; /* so point to rightmost pixel */ - outptr += width - 1; - dir = -1; - dir3 = -3; - errorptr = cquantize->fserrors + (width + 1) * 3; /* => entry after last column */ - } - else - { - /* work left to right in this row */ - dir = 1; - dir3 = 3; - errorptr = cquantize->fserrors; /* => entry before first real column */ - } - /* Preset error values: no error propagated to first pixel from left */ - cur0 = cur1 = cur2 = 0; - /* and no error propagated to row below yet */ - belowerr0 = belowerr1 = belowerr2 = 0; - bpreverr0 = bpreverr1 = bpreverr2 = 0; - - for (col = width; col > 0; col--) - { - - /* If this pixel is transparent, we want to assign it to the special - * transparency color index past the end of the palette rather than - * go through matching / dithering. */ - if ((oim->transparent >= 0) && (*inptr == oim->transparent)) - { - *outptr = nim->colorsTotal; - errorptr[0] = 0; - errorptr[1] = 0; - errorptr[2] = 0; - errorptr[3] = 0; - inptr += dir; - outptr += dir; - errorptr += dir3; - continue; - } - /* curN holds the error propagated from the previous pixel on the - * current line. Add the error propagated from the previous line - * to form the complete error correction term for this pixel, and - * round the error term (which is expressed * 16) to an integer. - * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct - * for either sign of the error value. - * Note: errorptr points to *previous* column's array entry. - */ - cur0 = RIGHT_SHIFT (cur0 + errorptr[dir3 + 0] + 8, 4); - cur1 = RIGHT_SHIFT (cur1 + errorptr[dir3 + 1] + 8, 4); - cur2 = RIGHT_SHIFT (cur2 + errorptr[dir3 + 2] + 8, 4); - /* Limit the error using transfer function set by init_error_limit. - * See comments with init_error_limit for rationale. - */ - cur0 = error_limit[cur0]; - cur1 = error_limit[cur1]; - cur2 = error_limit[cur2]; - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE (or less with error limiting); - * this sets the required size of the range_limit array. - */ - cur0 += gdTrueColorGetRed (*inptr); - cur1 += gdTrueColorGetGreen (*inptr); - cur2 += gdTrueColorGetBlue (*inptr); - range_limit (cur0); - range_limit (cur1); - range_limit (cur2); - - /* Index into the cache with adjusted pixel value */ - cachep = - &histogram[cur0 >> C0_SHIFT][cur1 >> C1_SHIFT][cur2 >> C2_SHIFT]; - /* If we have not seen this color before, find nearest colormap */ - /* entry and update the cache */ - if (*cachep == 0) - fill_inverse_cmap (oim, nim, cquantize, cur0 >> C0_SHIFT, - cur1 >> C1_SHIFT, cur2 >> C2_SHIFT); - /* Now emit the colormap index for this cell */ - { - register int pixcode = *cachep - 1; - *outptr = (JSAMPLE) pixcode; - /* Compute representation error for this pixel */ + inptr = input_buf[row]; + outptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + inptr += (width - 1) * 3; /* so point to rightmost pixel */ + outptr += width - 1; + dir = -1; + dir3 = -3; + errorptr = cquantize->fserrors + (width + 1) * 3; /* => entry after last column */ + } else { + /* work left to right in this row */ + dir = 1; + dir3 = 3; + errorptr = cquantize->fserrors; /* => entry before first real column */ + } + /* Preset error values: no error propagated to first pixel from left */ + cur0 = cur1 = cur2 = 0; + /* and no error propagated to row below yet */ + belowerr0 = belowerr1 = belowerr2 = 0; + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + for (col = width; col > 0; col--) { + + /* If this pixel is transparent, we want to assign it to the special + * transparency color index past the end of the palette rather than + * go through matching / dithering. */ + if ((oim->transparent >= 0) && (*inptr == oim->transparent)) { + *outptr = nim->colorsTotal; + errorptr[0] = 0; + errorptr[1] = 0; + errorptr[2] = 0; + errorptr[3] = 0; + inptr += dir; + outptr += dir; + errorptr += dir3; + continue; + } + /* curN holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3 + 0] + 8, 4); + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3 + 1] + 8, 4); + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3 + 2] + 8, 4); + /* Limit the error using transfer function set by init_error_limit. + * See comments with init_error_limit for rationale. + */ + cur0 = error_limit[cur0]; + cur1 = error_limit[cur1]; + cur2 = error_limit[cur2]; + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + * this sets the required size of the range_limit array. + */ + cur0 += gdTrueColorGetRed(*inptr); + cur1 += gdTrueColorGetGreen(*inptr); + cur2 += gdTrueColorGetBlue(*inptr); + range_limit(cur0); + range_limit(cur1); + range_limit(cur2); + + /* Index into the cache with adjusted pixel value */ + cachep = &histogram[cur0 >> C0_SHIFT][cur1 >> C1_SHIFT][cur2 >> C2_SHIFT]; + /* If we have not seen this color before, find nearest colormap */ + /* entry and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(oim, nim, cquantize, cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, + cur2 >> C2_SHIFT); + /* Now emit the colormap index for this cell */ + { + register int pixcode = *cachep - 1; + *outptr = (JSAMPLE)pixcode; + /* Compute representation error for this pixel */ #define GETJSAMPLE - cur0 -= GETJSAMPLE (colormap0[pixcode]); - cur1 -= GETJSAMPLE (colormap1[pixcode]); - cur2 -= GETJSAMPLE (colormap2[pixcode]); + cur0 -= GETJSAMPLE(colormap0[pixcode]); + cur1 -= GETJSAMPLE(colormap1[pixcode]); + cur2 -= GETJSAMPLE(colormap2[pixcode]); #undef GETJSAMPLE - } - /* Compute error fractions to be propagated to adjacent pixels. - * Add these into the running sums, and simultaneously shift the - * next-line error sums left by 1 column. - */ - { - register LOCFSERROR bnexterr, delta; - - bnexterr = cur0; /* Process component 0 */ - delta = cur0 * 2; - cur0 += delta; /* form error * 3 */ - errorptr[0] = (FSERROR) (bpreverr0 + cur0); - cur0 += delta; /* form error * 5 */ - bpreverr0 = belowerr0 + cur0; - belowerr0 = bnexterr; - cur0 += delta; /* form error * 7 */ - bnexterr = cur1; /* Process component 1 */ - delta = cur1 * 2; - cur1 += delta; /* form error * 3 */ - errorptr[1] = (FSERROR) (bpreverr1 + cur1); - cur1 += delta; /* form error * 5 */ - bpreverr1 = belowerr1 + cur1; - belowerr1 = bnexterr; - cur1 += delta; /* form error * 7 */ - bnexterr = cur2; /* Process component 2 */ - delta = cur2 * 2; - cur2 += delta; /* form error * 3 */ - errorptr[2] = (FSERROR) (bpreverr2 + cur2); - cur2 += delta; /* form error * 5 */ - bpreverr2 = belowerr2 + cur2; - belowerr2 = bnexterr; - cur2 += delta; /* form error * 7 */ - } - /* At this point curN contains the 7/16 error value to be propagated - * to the next pixel on the current line, and all the errors for the - * next line have been shifted over. We are therefore ready to move on. - */ - inptr += dir; /* Advance pixel pointers to next column */ - outptr += dir; - errorptr += dir3; /* advance errorptr to current column */ - } - /* Post-loop cleanup: we must unload the final error values into the - * final fserrors[] entry. Note we need not unload belowerrN because - * it is for the dummy column before or after the actual array. - */ - errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ - errorptr[1] = (FSERROR) bpreverr1; - errorptr[2] = (FSERROR) bpreverr2; + } + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + { + register LOCFSERROR bnexterr, delta; + + bnexterr = cur0; /* Process component 0 */ + delta = cur0 * 2; + cur0 += delta; /* form error * 3 */ + errorptr[0] = (FSERROR)(bpreverr0 + cur0); + cur0 += delta; /* form error * 5 */ + bpreverr0 = belowerr0 + cur0; + belowerr0 = bnexterr; + cur0 += delta; /* form error * 7 */ + bnexterr = cur1; /* Process component 1 */ + delta = cur1 * 2; + cur1 += delta; /* form error * 3 */ + errorptr[1] = (FSERROR)(bpreverr1 + cur1); + cur1 += delta; /* form error * 5 */ + bpreverr1 = belowerr1 + cur1; + belowerr1 = bnexterr; + cur1 += delta; /* form error * 7 */ + bnexterr = cur2; /* Process component 2 */ + delta = cur2 * 2; + cur2 += delta; /* form error * 3 */ + errorptr[2] = (FSERROR)(bpreverr2 + cur2); + cur2 += delta; /* form error * 5 */ + bpreverr2 = belowerr2 + cur2; + belowerr2 = bnexterr; + cur2 += delta; /* form error * 7 */ + } + /* At this point curN contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move + * on. + */ + inptr += dir; /* Advance pixel pointers to next column */ + outptr += dir; + errorptr += dir3; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error values into the + * final fserrors[] entry. Note we need not unload belowerrN because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR)bpreverr0; /* unload prev errs into array */ + errorptr[1] = (FSERROR)bpreverr1; + errorptr[2] = (FSERROR)bpreverr2; } } - /* * Initialize the error-limiting transfer function (lookup table). * The raw F-S error computation can potentially compute error values of up to @@ -1368,312 +1280,567 @@ pass2_fs_dither (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) * to Aaron Giles for this idea. */ -LOCAL (void) -init_error_limit (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) +LOCAL(void) +init_error_limit(gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize) /* Allocate and fill in the error_limiter table */ { - int *table; - int in, out; - - cquantize->error_limiter_storage = - (int *) safe_emalloc ((MAXJSAMPLE * 2 + 1), sizeof (int), 0); - if (!cquantize->error_limiter_storage) - { - return; + int *table; + int in, out; + (void)oim; + (void)nim; + + cquantize->error_limiter_storage = (int *)gdMalloc((MAXJSAMPLE * 2 + 1) * sizeof(int)); + if (!cquantize->error_limiter_storage) { + return; } - table = cquantize->error_limiter_storage; + table = cquantize->error_limiter_storage; - table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ - cquantize->error_limiter = table; + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + cquantize->error_limiter = table; -#define STEPSIZE ((MAXJSAMPLE+1)/16) - /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ - out = 0; - for (in = 0; in < STEPSIZE; in++, out++) - { - table[in] = out; - table[-in] = -out; +#define STEPSIZE ((MAXJSAMPLE + 1) / 16) + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + out = 0; + for (in = 0; in < STEPSIZE; in++, out++) { + table[in] = out; + table[-in] = -out; } - /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ - for (; in < STEPSIZE * 3; in++, out += (in & 1) ? 0 : 1) - { - table[in] = out; - table[-in] = -out; + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + for (; in < STEPSIZE * 3; in++, out += (in & 1) ? 0 : 1) { + table[in] = out; + table[-in] = -out; } - /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ - for (; in <= MAXJSAMPLE; in++) - { - table[in] = out; - table[-in] = -out; + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + for (; in <= MAXJSAMPLE; in++) { + table[in] = out; + table[-in] = -out; } #undef STEPSIZE } - /* * Finish up at the end of each pass. */ -static void -zeroHistogram (hist3d histogram) +static void zeroHistogram(hist3d histogram) { - int i; - /* Zero the histogram or inverse color map */ - for (i = 0; i < HIST_C0_ELEMS; i++) - { - memset (histogram[i], - 0, HIST_C1_ELEMS * HIST_C2_ELEMS * sizeof (histcell)); + int i; + /* Zero the histogram or inverse color map */ + for (i = 0; i < HIST_C0_ELEMS; i++) { + memset(histogram[i], 0, HIST_C1_ELEMS * HIST_C2_ELEMS * sizeof(histcell)); + } +} + +/** + * Function: gdImageTrueColorToPaletteSetMethod + * + * Selects the quantization method + * + * That quantization method is used for all subsequent + * and calls. + * + * Parameters: + * im - The image. + * method - The quantization method, see . + * speed - The quantization speed between 1 (highest quality) and + * 10 (fastest). 0 selects a method-specific default (recommended). + * + * Returns: + * Zero if the given method is invalid or not available; non-zero otherwise. + * + * See also: + * - + */ +BGD_DECLARE(int) +gdImageTrueColorToPaletteSetMethod(gdImagePtr im, int method, int speed) +{ +#ifndef HAVE_LIBIMAGEQUANT + if (method == GD_QUANT_LIQ) { + return FALSE; + } +#endif + + if (method >= GD_QUANT_DEFAULT && method <= GD_QUANT_LIQ) { + im->paletteQuantizationMethod = method; + + if (speed < 0 || speed > 10) { + speed = 0; + } + im->paletteQuantizationSpeed = speed; + } + return TRUE; +} + +/** + * Function: gdImageTrueColorToPaletteSetQuality + * + * Chooses a quality range for quantization + * + * That quality range is used in all subsequent calls to + * and + * if the quantization method is . + * + * Parameters: + * im - The image. + * min_quality - The minimum quality in range 1-100 (1 = ugly, 100 = perfect). + * If the palette cannot represent the image with at least + * min_quality, then no conversion is done. + * max_quality - The maximum quality in range 1-100 (1 = ugly, 100 = perfect), + * which must be higher than the min_quality. If the palette can + * represent the image with a quality better than max_quality, + * then fewer colors than requested will be used. + */ +BGD_DECLARE(void) +gdImageTrueColorToPaletteSetQuality(gdImagePtr im, int min_quality, int max_quality) +{ + if (min_quality >= 0 && min_quality <= 100 && max_quality >= 0 && max_quality <= 100 && + min_quality <= max_quality) { + im->paletteQuantizationMinQuality = min_quality; + im->paletteQuantizationMaxQuality = max_quality; + } +} + +static int gdImageTrueColorToPaletteBody(gdImagePtr oim, int dither, int colorsWanted, + gdImagePtr *cimP); + +/** + * Function: gdImageCreatePaletteFromTrueColor + * + * Creates a new palette image from a truecolor image + * + * Parameters: + * im - The image. + * dither - Whether dithering should be applied. + * colorsWanted - The number of desired palette entries. + * + * Returns: + * A newly create palette image; NULL on failure. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(gdImagePtr) +gdImageCreatePaletteFromTrueColor(gdImagePtr im, int dither, int colorsWanted) +{ + gdImagePtr nim; + if (TRUE == gdImageTrueColorToPaletteBody(im, dither, colorsWanted, &nim)) { + return nim; } + return NULL; } -static int gdImageTrueColorToPaletteBody (gdImagePtr oim, int dither, int colorsWanted, gdImagePtr *cimP); +/** + * Function: gdImageTrueColorToPalette + * + * Converts a truecolor image to a palette image + * + * Parameters: + * im - The image. + * dither - Whether dithering should be applied. + * colorsWanted - The number of desired palette entries. + * + * Returns: + * Non-zero if the conversion succeeded, zero otherwise. + * + * See also: + * - + * - + * - + */ +BGD_DECLARE(int) +gdImageTrueColorToPalette(gdImagePtr im, int dither, int colorsWanted) +{ + return gdImageTrueColorToPaletteBody(im, dither, colorsWanted, 0); +} -gdImagePtr gdImageCreatePaletteFromTrueColor (gdImagePtr im, int dither, int colorsWanted) +#ifdef HAVE_LIBIMAGEQUANT +/** + LIQ library needs pixels in RGBA order with alpha 0-255 (opaque 255). + This callback is run whenever source rows need to be converted from GD's + format. +*/ +static void convert_gdpixel_to_rgba(liq_color output_row[], int y, int width, void *userinfo) { - gdImagePtr nim; - if (TRUE == gdImageTrueColorToPaletteBody(im, dither, colorsWanted, &nim)) { - return nim; - } - return NULL; + gdImagePtr oim = userinfo; + int x; + for (x = 0; x < width; x++) { + int pixel = input_buf[y][x]; + output_row[x].r = gdTrueColorGetRed(pixel) * 255 / gdRedMax; + output_row[x].g = gdTrueColorGetGreen(pixel) * 255 / gdGreenMax; + output_row[x].b = gdTrueColorGetBlue(pixel) * 255 / gdBlueMax; + if (oim->transparent >= 0 && pixel == oim->transparent) { + output_row[x].a = 0; + } else { + int alpha = gdTrueColorGetAlpha(pixel); + if (gdAlphaOpaque < gdAlphaTransparent) { + alpha = gdAlphaTransparent - alpha; + } + output_row[x].a = alpha * 255 / gdAlphaMax; + } + } } +#endif -int gdImageTrueColorToPalette (gdImagePtr im, int dither, int colorsWanted) +static int ensure_transparent_palette_entry(gdImagePtr im, int transparentColor) { - return gdImageTrueColorToPaletteBody(im, dither, colorsWanted, 0); + int transparent; + + if (transparentColor < 0) { + return -1; + } + + transparent = im->transparent; + if (transparent >= 0 && transparent < im->colorsTotal) { + im->alpha[transparent] = gdAlphaTransparent; + return transparent; + } + + if (im->colorsTotal >= gdMaxColors) { + return -1; + } + + transparent = im->colorsTotal; + im->red[transparent] = gdTrueColorGetRed(transparentColor); + im->green[transparent] = gdTrueColorGetGreen(transparentColor); + im->blue[transparent] = gdTrueColorGetBlue(transparentColor); + im->alpha[transparent] = gdAlphaTransparent; + im->open[transparent] = 0; + im->transparent = transparent; + im->colorsTotal++; + + return transparent; +} + +static void remap_transparent_pixels(gdImagePtr src, gdImagePtr dst, int transparentColor, + int transparentIndex) +{ + int x, y; + + if (transparentColor < 0 || transparentIndex < 0) { + return; + } + + for (y = 0; y < src->sy; y++) { + for (x = 0; x < src->sx; x++) { + if (src->tpixels[y][x] == transparentColor) { + dst->pixels[y][x] = transparentIndex; + } + } + } +} + +static void copy_palette_image_data(gdImagePtr dst, gdImagePtr src) +{ + int i, y; + + for (y = 0; y < src->sy; y++) { + memcpy(dst->pixels[y], src->pixels[y], src->sx); + } + + dst->trueColor = 0; + dst->colorsTotal = src->colorsTotal; + dst->transparent = src->transparent; + for (i = 0; i < gdMaxColors; i++) { + dst->red[i] = src->red[i]; + dst->green[i] = src->green[i]; + dst->blue[i] = src->blue[i]; + dst->alpha[i] = src->alpha[i]; + dst->open[i] = src->open[i]; + } } static void free_truecolor_image_data(gdImagePtr oim) { - int i; - oim->trueColor = 0; - /* Junk the truecolor pixels */ - for (i = 0; i < oim->sy; i++) - { - gdFree (oim->tpixels[i]); + int i; + oim->trueColor = 0; + /* Junk the truecolor pixels */ + for (i = 0; i < oim->sy; i++) { + gdFree(oim->tpixels[i]); } - gdFree (oim->tpixels); - oim->tpixels = 0; + gdFree(oim->tpixels); + oim->tpixels = 0; } +#ifdef HAVE_LIBIMAGEQUANT +/* liq requires 16 byte aligned heap memory */ +static void *malloc16(size_t size) +{ +#ifndef _WIN32 + void *p; + return posix_memalign(&p, 16, size) == 0 ? p : NULL; +#else + return _aligned_malloc(size, 16); +#endif +} +static void free16(void *ptr) +{ +#ifndef _WIN32 + free(ptr); +#else + _aligned_free(ptr); +#endif +} +#endif + /* * Module initialization routine for 2-pass color quantization. */ -static int gdImageTrueColorToPaletteBody (gdImagePtr oim, int dither, int colorsWanted, gdImagePtr *cimP) +static int gdImageTrueColorToPaletteBody(gdImagePtr oim, int dither, int colorsWanted, + gdImagePtr *cimP) { - my_cquantize_ptr cquantize = NULL; - int i, conversionSucceeded=0; - - /* Allocate the JPEG palette-storage */ - size_t arraysize; - int maxColors = gdMaxColors; - gdImagePtr nim; - if (cimP) { - nim = gdImageCreate(oim->sx, oim->sy); - *cimP = nim; - if (!nim) { - return FALSE; - } - } else { - nim = oim; - } - if (!oim->trueColor) - { - /* (Almost) nothing to do! */ - if (cimP) { - gdImageCopy(nim, oim, 0, 0, 0, 0, oim->sx, oim->sy); + my_cquantize_ptr cquantize = NULL; + int i, conversionSucceeded = 0; + int transparentColor; + + /* Allocate the JPEG palette-storage */ + size_t arraysize; + int maxColors = gdMaxColors; + gdImagePtr nim; + + if (cimP) { + nim = gdImageCreate(oim->sx, oim->sy); *cimP = nim; - } - return TRUE; + if (!nim) { + return FALSE; + } + } else { + nim = oim; } - /* If we have a transparent color (the alphaless mode of transparency), we - * must reserve a palette entry for it at the end of the palette. */ - if (oim->transparent >= 0) - { - maxColors--; + if (!oim->trueColor) { + /* (Almost) nothing to do! */ + if (cimP) { + gdImageCopy(nim, oim, 0, 0, 0, 0, oim->sx, oim->sy); + *cimP = nim; + } + return TRUE; } - if (colorsWanted > maxColors) - { - colorsWanted = maxColors; + + transparentColor = oim->transparent; + + /* If we have a transparent color (the alphaless mode of transparency), we + * must reserve a palette entry for it at the end of the palette. */ + if (transparentColor >= 0) { + maxColors--; } - if (!cimP) { - nim->pixels = gdCalloc (oim->sy, sizeof (unsigned char *)); - if (!nim->pixels) - { - /* No can do */ - goto outOfMemory; - } - for (i = 0; (i < nim->sy); i++) - { - nim->pixels[i] = gdCalloc (oim->sx, sizeof (unsigned char *)); - if (!nim->pixels[i]) - { - goto outOfMemory; - } - } - } - - cquantize = (my_cquantize_ptr) gdCalloc (1, sizeof (my_cquantizer)); - if (!cquantize) - { - /* No can do */ - goto outOfMemory; + if (colorsWanted > maxColors) { + colorsWanted = maxColors; + } + if (!cimP) { + nim->pixels = gdCalloc(sizeof(unsigned char *), oim->sy); + if (!nim->pixels) { + /* No can do */ + goto outOfMemory; + } + for (i = 0; (i < nim->sy); i++) { + nim->pixels[i] = (unsigned char *)gdCalloc(sizeof(unsigned char), oim->sx); + if (!nim->pixels[i]) { + goto outOfMemory; + } + } } - cquantize->fserrors = NULL; /* flag optional arrays not allocated */ - cquantize->error_limiter = NULL; - - /* Allocate the histogram/inverse colormap storage */ - cquantize->histogram = (hist3d) safe_emalloc (HIST_C0_ELEMS, sizeof (hist2d), 0); - for (i = 0; i < HIST_C0_ELEMS; i++) - { - cquantize->histogram[i] = - (hist2d) safe_emalloc (HIST_C1_ELEMS * HIST_C2_ELEMS, sizeof (histcell), 0); - if (!cquantize->histogram[i]) - { - goto outOfMemory; - } + if (oim->paletteQuantizationMethod == GD_QUANT_NEUQUANT) { + if (cimP) { /* NeuQuant always creates a copy, so the new blank image + can't be used */ + gdImageDestroy(nim); + } + nim = gdImageNeuQuant(oim, colorsWanted, + oim->paletteQuantizationSpeed ? oim->paletteQuantizationSpeed : 2); + if (!nim) { + return FALSE; + } + if (transparentColor >= 0) { + int transparent = ensure_transparent_palette_entry(nim, transparentColor); + if (transparent < 0) { + gdImageDestroy(nim); + return FALSE; + } + remap_transparent_pixels(oim, nim, transparentColor, transparent); + } + if (cimP) { + *cimP = nim; + return TRUE; + } + free_truecolor_image_data(oim); + copy_palette_image_data(oim, nim); + gdImageDestroy(nim); + return TRUE; } - cquantize->fserrors = (FSERRPTR) safe_emalloc (3, sizeof (FSERROR), 0); - init_error_limit (oim, nim, cquantize); - arraysize = (size_t) ((nim->sx + 2) * (3 * sizeof (FSERROR))); - /* Allocate Floyd-Steinberg workspace. */ - cquantize->fserrors = gdRealloc(cquantize->fserrors, arraysize); - memset(cquantize->fserrors, 0, arraysize); - if (!cquantize->fserrors) - { - goto outOfMemory; +#ifdef HAVE_LIBIMAGEQUANT + if (oim->paletteQuantizationMethod == GD_QUANT_DEFAULT || + oim->paletteQuantizationMethod == GD_QUANT_LIQ) { + liq_attr *attr = liq_attr_create_with_allocator(malloc16, free16); + liq_image *image; + liq_result *remap; + int remapped_ok = 0; + + liq_set_max_colors(attr, colorsWanted); + + /* by default make it fast to match speed of previous implementation */ + liq_set_speed(attr, oim->paletteQuantizationSpeed ? oim->paletteQuantizationSpeed : 9); + if (oim->paletteQuantizationMaxQuality) { + liq_set_quality(attr, oim->paletteQuantizationMinQuality, + oim->paletteQuantizationMaxQuality); + } + image = liq_image_create_custom(attr, convert_gdpixel_to_rgba, oim, oim->sx, oim->sy, 0); + remap = liq_quantize_image(attr, image); + if (!remap) { /* minimum quality not met, leave image unmodified */ + liq_image_destroy(image); + liq_attr_destroy(attr); + goto outOfMemory; + } + + liq_set_dithering_level(remap, dither ? 1 : 0); + if (LIQ_OK == liq_write_remapped_image_rows(remap, image, output_buf)) { + remapped_ok = 1; + const liq_palette *pal = liq_get_palette(remap); + nim->transparent = -1; + unsigned int icolor; + for (icolor = 0; icolor < pal->count; icolor++) { + nim->open[icolor] = 0; + nim->red[icolor] = pal->entries[icolor].r * gdRedMax / 255; + nim->green[icolor] = pal->entries[icolor].g * gdGreenMax / 255; + nim->blue[icolor] = pal->entries[icolor].b * gdBlueMax / 255; + int alpha = pal->entries[icolor].a * gdAlphaMax / 255; + if (gdAlphaOpaque < gdAlphaTransparent) { + alpha = gdAlphaTransparent - alpha; + } + nim->alpha[icolor] = alpha; + if (nim->transparent == -1 && alpha == gdAlphaTransparent) { + nim->transparent = icolor; + } + } + nim->colorsTotal = pal->count; + if (transparentColor >= 0) { + int transparent = ensure_transparent_palette_entry(nim, transparentColor); + if (transparent >= 0) { + remap_transparent_pixels(oim, nim, transparentColor, transparent); + } else { + remapped_ok = 0; + } + } + } + liq_result_destroy(remap); + liq_image_destroy(image); + liq_attr_destroy(attr); + + if (remapped_ok) { + if (!cimP) { + free_truecolor_image_data(oim); + } + return TRUE; + } } - cquantize->on_odd_row = FALSE; - - /* Do the work! */ - zeroHistogram (cquantize->histogram); - prescan_quantize (oim, nim, cquantize); - /* TBB 2.0.5: pass colorsWanted, not 256! */ - select_colors (oim, nim, cquantize, colorsWanted); - zeroHistogram (cquantize->histogram); - if (dither) - { - pass2_fs_dither (oim, nim, cquantize); +#endif + + cquantize = (my_cquantize_ptr)gdCalloc(sizeof(my_cquantizer), 1); + if (!cquantize) { + /* No can do */ + goto outOfMemory; } - else - { - pass2_no_dither (oim, nim, cquantize); + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + cquantize->error_limiter = NULL; + + /* Allocate the histogram/inverse colormap storage */ + cquantize->histogram = (hist3d)gdMalloc(HIST_C0_ELEMS * sizeof(hist2d)); + for (i = 0; i < HIST_C0_ELEMS; i++) { + cquantize->histogram[i] = + (hist2d)gdMalloc(HIST_C1_ELEMS * HIST_C2_ELEMS * sizeof(histcell)); + if (!cquantize->histogram[i]) { + goto outOfMemory; + } } -#if 0 /* 2.0.12; we no longer attempt full alpha in palettes */ - if (cquantize->transparentIsPresent) - { - int mt = -1; - int mtIndex = -1; - for (i = 0; (i < im->colorsTotal); i++) - { - if (im->alpha[i] > mt) - { - mtIndex = i; - mt = im->alpha[i]; - } - } - for (i = 0; (i < im->colorsTotal); i++) - { - if (im->alpha[i] == mt) - { - im->alpha[i] = gdAlphaTransparent; - } - } + + cquantize->fserrors = (FSERRPTR)gdMalloc(3 * sizeof(FSERROR)); + init_error_limit(oim, nim, cquantize); + arraysize = (size_t)((nim->sx + 2) * (3 * sizeof(FSERROR))); + /* Allocate Floyd-Steinberg workspace. */ + cquantize->fserrors = gdReallocEx(cquantize->fserrors, arraysize); + if (!cquantize->fserrors) { + goto outOfMemory; } - if (cquantize->opaqueIsPresent) - { - int mo = 128; - int moIndex = -1; - for (i = 0; (i < im->colorsTotal); i++) - { - if (im->alpha[i] < mo) - { - moIndex = i; - mo = im->alpha[i]; - } - } - for (i = 0; (i < im->colorsTotal); i++) - { - if (im->alpha[i] == mo) - { - im->alpha[i] = gdAlphaOpaque; - } - } + memset(cquantize->fserrors, 0, arraysize); + cquantize->on_odd_row = FALSE; + + /* Do the work! */ + zeroHistogram(cquantize->histogram); + prescan_quantize(oim, nim, cquantize); + /* TBB 2.0.5: pass colorsWanted, not 256! */ + select_colors(oim, nim, cquantize, colorsWanted); + zeroHistogram(cquantize->histogram); + if (dither) { + if (cquantize->error_limiter == NULL) { + goto outOfMemory; + } + pass2_fs_dither(oim, nim, cquantize); + } else { + pass2_no_dither(oim, nim, cquantize); } -#endif - /* If we had a 'transparent' color, increment the color count so it's - * officially in the palette and convert the transparent variable to point to - * an index rather than a color (Its data already exists and transparent - * pixels have already been mapped to it by this point, it is done late as to - * avoid color matching / dithering with it). */ - if (oim->transparent >= 0) - { - nim->transparent = nim->colorsTotal; - nim->colorsTotal++; + /* If we had a 'transparent' color, increment the color count so it's + * officially in the palette and convert the transparent variable to point + * to an index rather than a color (Its data already exists and transparent + * pixels have already been mapped to it by this point, it is done late as + * to avoid color matching / dithering with it). */ + if (transparentColor >= 0) { + nim->transparent = nim->colorsTotal; + nim->colorsTotal++; } - /* Success! Get rid of the truecolor image data. */ - conversionSucceeded = TRUE; - if (!cimP) - { - free_truecolor_image_data(oim); + /* Success! Get rid of the truecolor image data. */ + conversionSucceeded = TRUE; + if (!cimP) { + free_truecolor_image_data(oim); } - goto freeQuantizeData; - /* Tediously free stuff. */ + goto freeQuantizeData; + /* Tediously free stuff. */ outOfMemory: - conversionSucceeded = FALSE; - if (oim->trueColor) - { - if (!cimP) { - /* On failure only */ - for (i = 0; i < nim->sy; i++) - { - if (nim->pixels[i]) - { - gdFree (nim->pixels[i]); - } - } - if (nim->pixels) - { - gdFree (nim->pixels); - } - nim->pixels = 0; - } else { - gdImageDestroy(nim); - *cimP = 0; - } + conversionSucceeded = FALSE; + if (oim->trueColor) { + if (!cimP) { + /* On failure only */ + if (nim->pixels) { + for (i = 0; i < nim->sy; i++) { + if (nim->pixels[i]) { + gdFree(nim->pixels[i]); + } + } + gdFree(nim->pixels); + } + nim->pixels = NULL; + } else { + gdImageDestroy(nim); + *cimP = 0; + } } + freeQuantizeData: - for (i = 0; i < HIST_C0_ELEMS; i++) - { - if (cquantize->histogram[i]) - { - gdFree (cquantize->histogram[i]); - } - } - if (cquantize->histogram) - { - gdFree (cquantize->histogram); - } - if (cquantize->fserrors) - { - gdFree (cquantize->fserrors); + if (cquantize) { + if (cquantize->histogram) { + for (i = 0; i < HIST_C0_ELEMS; i++) { + if (cquantize->histogram[i]) { + gdFree(cquantize->histogram[i]); + } + } + } + if (cquantize->histogram) { + gdFree(cquantize->histogram); + } + if (cquantize->fserrors) { + gdFree(cquantize->fserrors); + } + if (cquantize->error_limiter_storage) { + gdFree(cquantize->error_limiter_storage); + } + gdFree(cquantize); } - if (cquantize->error_limiter_storage) - { - gdFree (cquantize->error_limiter_storage); - } - if (cquantize) - { - gdFree (cquantize); - } - return conversionSucceeded; + return conversionSucceeded; } - #endif diff --git a/ext/gd/libgd/gd_transform.c b/ext/gd/libgd/gd_transform.c index 9051525eecca..0b125398b88c 100644 --- a/ext/gd/libgd/gd_transform.c +++ b/ext/gd/libgd/gd_transform.c @@ -1,73 +1,121 @@ +/** + * File: Transformations + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + #include "gd.h" -void gdImageFlipVertical(gdImagePtr im) +/** + * Function: gdImageFlipVertical + * + * Flip an image vertically + * + * The image is mirrored upside-down. + * + * Parameters: + * im - The image. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) gdImageFlipVertical(gdImagePtr im) { - register int x, y; + register int x, y; - if (im->trueColor) { - for (y = 0; y < im->sy / 2; y++) { - int *row_dst = im->tpixels[y]; - int *row_src = im->tpixels[im->sy - 1 - y]; - for (x = 0; x < im->sx; x++) { - register int p; - p = row_dst[x]; - row_dst[x] = im->tpixels[im->sy - 1 - y][x]; - row_src[x] = p; - } - } - } else { - unsigned char p; - for (y = 0; y < im->sy / 2; y++) { - for (x = 0; x < im->sx; x++) { - p = im->pixels[y][x]; - im->pixels[y][x] = im->pixels[im->sy - 1 - y][x]; - im->pixels[im->sy - 1 - y][x] = p; - } - } - } - return; + if (im->trueColor) { + for (y = 0; y < im->sy / 2; y++) { + int *row_dst = im->tpixels[y]; + int *row_src = im->tpixels[im->sy - 1 - y]; + for (x = 0; x < im->sx; x++) { + register int p; + p = row_dst[x]; + row_dst[x] = im->tpixels[im->sy - 1 - y][x]; + row_src[x] = p; + } + } + } else { + unsigned char p; + for (y = 0; y < im->sy / 2; y++) { + for (x = 0; x < im->sx; x++) { + p = im->pixels[y][x]; + im->pixels[y][x] = im->pixels[im->sy - 1 - y][x]; + im->pixels[im->sy - 1 - y][x] = p; + } + } + } + return; } -void gdImageFlipHorizontal(gdImagePtr im) +/** + * Function: gdImageFlipHorizontal + * + * Flip an image horizontally + * + * The image is mirrored left-right. + * + * Parameters: + * im - The image. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) gdImageFlipHorizontal(gdImagePtr im) { - int x, y; + int x, y; - if (im->trueColor) { - int *px1, *px2, tmp; + if (im->trueColor) { + int *px1, *px2, tmp; - for (y = 0; y < im->sy; y++) { - px1 = im->tpixels[y]; - px2 = im->tpixels[y] + im->sx - 1; - for (x = 0; x < (im->sx >> 1); x++) { - tmp = *px1; - *px1 = *px2; - *px2 = tmp; - px1++; - px2--; - } - } - } else { - unsigned char *px1, *px2, tmp; + for (y = 0; y < im->sy; y++) { + px1 = im->tpixels[y]; + px2 = im->tpixels[y] + im->sx - 1; + for (x = 0; x < (im->sx >> 1); x++) { + tmp = *px1; + *px1 = *px2; + *px2 = tmp; + px1++; + px2--; + } + } + } else { + unsigned char *px1, *px2, tmp; - for (y = 0; y < im->sy; y++) { - px1 = im->pixels[y]; - px2 = im->pixels[y] + im->sx - 1; - for (x = 0; x < (im->sx >> 1); x++) { - tmp = *px1; - *px1 = *px2; - *px2 = tmp; - px1++; - px2--; - } - } - } + for (y = 0; y < im->sy; y++) { + px1 = im->pixels[y]; + px2 = im->pixels[y] + im->sx - 1; + for (x = 0; x < (im->sx >> 1); x++) { + tmp = *px1; + *px1 = *px2; + *px2 = tmp; + px1++; + px2--; + } + } + } } -void gdImageFlipBoth(gdImagePtr im) +/** + * Function: gdImageFlipBoth + * + * Flip an image vertically and horizontally + * + * The image is mirrored upside-down and left-right. + * + * Parameters: + * im - The image. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) gdImageFlipBoth(gdImagePtr im) { - gdImageFlipVertical(im); - gdImageFlipHorizontal(im); + gdImageFlipVertical(im); + gdImageFlipHorizontal(im); } - - diff --git a/ext/gd/libgd/gd_uhdr.c b/ext/gd/libgd/gd_uhdr.c new file mode 100644 index 000000000000..8417032ff9b9 --- /dev/null +++ b/ext/gd/libgd/gd_uhdr.c @@ -0,0 +1,1321 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/** + * File: UltraHDR IO + * + * Read and write UltraHDR images with gain map preservation. + */ + +#include +#include +#include +#include + +#include "gd.h" +#include "gd_errors.h" +#include "gd_intern.h" +#include "gdhelpers.h" + +#ifdef HAVE_LIBUHDR +#include +#endif + +typedef enum { + GD_UHDR_OP_RESIZE = 0, + GD_UHDR_OP_CROP, + GD_UHDR_OP_ROTATE, + GD_UHDR_OP_MIRROR +} gdUhdrOpType; + +typedef struct { + gdUhdrOpType type; + int p1; + int p2; + int p3; + int p4; +} gdUhdrOp; + +struct gdUhdrImageStruct { + int format; + int width; + int height; + int has_gain_map; + void *blob; + int blob_size; + gdUhdrOp *ops; + int op_count; + int op_capacity; +}; + +static void gdUhdrSetError(gdUhdrErrorPtr err, int code, int provider_code, const char *message) +{ + if (!err) { + return; + } + + err->code = code; + err->provider_code = provider_code; + err->message[0] = '\0'; + if (message) { + strncpy(err->message, message, sizeof(err->message) - 1); + err->message[sizeof(err->message) - 1] = '\0'; + } +} + +#ifdef HAVE_LIBUHDR + +static int gdUhdrIsSupportedFormat(int format) { return format == GD_UHDR_FORMAT_JPEG; } + +static int gdUhdrIntAddOverflow(int a, int b) +{ + if (a < 0 || b < 0) { + return 1; + } + + if (a > INT_MAX - b) { + return 1; + } + + return 0; +} + +static int gdUhdrReadAllFromCtx(gdIOCtxPtr ctx, void **out_data, int *out_size) +{ + const int step = 4096; + unsigned char buf[4096]; + unsigned char *data = NULL; + int size = 0; + + if (!ctx || !out_data || !out_size) { + return GD_UHDR_E_INVALID; + } + + for (;;) { + int n = gdGetBuf(buf, step, ctx); + int new_size; + if (n <= 0) { + break; + } + + if (gdUhdrIntAddOverflow(size, n)) { + gd_error("gd-uhdr read size overflow: size=%d n=%d\n", size, n); + gdFree(data); + return GD_UHDR_E_INVALID; + } + + new_size = size + n; + + { + unsigned char *tmp = (unsigned char *)gdRealloc(data, (size_t)new_size); + if (!tmp) { + gd_error("gd-uhdr realloc failed: requested=%d\n", new_size); + gdFree(data); + return GD_UHDR_E_DECODE; + } + data = tmp; + } + + memcpy(data + size, buf, (size_t)n); + size = new_size; + } + + if (!data || size <= 0) { + gdFree(data); + return GD_UHDR_E_DECODE; + } + + *out_data = data; + *out_size = size; + return GD_UHDR_SUCCESS; +} + +static void gdUhdrInitCompressedImage(uhdr_compressed_image_t *image, void *data, int size) +{ + memset(image, 0, sizeof(*image)); + image->data = data; + image->data_sz = (size_t)size; + image->capacity = (size_t)size; + image->cg = UHDR_CG_UNSPECIFIED; + image->ct = UHDR_CT_UNSPECIFIED; + image->range = UHDR_CR_FULL_RANGE; +} + +static int gdUhdrCopyMetadataProfile(gdImageMetadata **dst, const gdImageMetadata *src, + const char *key, gdUhdrErrorPtr err) +{ + const unsigned char *profile; + size_t profile_size; + int status; + + profile = gdImageMetadataGetProfile(src, key, &profile_size); + if (!profile) { + return GD_UHDR_SUCCESS; + } + + if (!*dst) { + *dst = gdImageMetadataCreate(); + if (!*dst) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Out of memory copying JPEG metadata"); + return GD_UHDR_E_ENCODE; + } + } + + status = gdImageMetadataSetProfile(*dst, key, profile, profile_size); + if (status != GD_META_OK) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, status, "Failed to copy JPEG metadata"); + return GD_UHDR_E_ENCODE; + } + + return GD_UHDR_SUCCESS; +} + +static int gdUhdrCreateJpegMetadataFromBlock(uhdr_mem_block_t *block, int copy_exif, int copy_icc, + gdImageMetadata **out, gdUhdrErrorPtr err) +{ + gdImageMetadata *src_metadata; + gdImagePtr decoded; + int status; + + *out = NULL; + if (!block || !block->data || block->data_sz == 0) { + return GD_UHDR_SUCCESS; + } + if (block->data_sz > (size_t)INT_MAX) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "JPEG metadata block is too large"); + return GD_UHDR_E_INVALID; + } + + src_metadata = gdImageMetadataCreate(); + if (!src_metadata) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Out of memory reading JPEG metadata"); + return GD_UHDR_E_ENCODE; + } + + decoded = gdImageCreateFromJpegPtrWithMetadata((int)block->data_sz, block->data, src_metadata); + if (!decoded) { + gdImageMetadataFree(src_metadata); + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to read JPEG metadata"); + return GD_UHDR_E_DECODE; + } + gdImageDestroy(decoded); + + if (copy_exif) { + status = gdUhdrCopyMetadataProfile(out, src_metadata, "exif", err); + if (status != GD_UHDR_SUCCESS) { + gdImageMetadataFree(src_metadata); + return status; + } + } + if (copy_icc) { + status = gdUhdrCopyMetadataProfile(out, src_metadata, "icc", err); + if (status != GD_UHDR_SUCCESS) { + gdImageMetadataFree(src_metadata); + return status; + } + } + + gdImageMetadataFree(src_metadata); + return GD_UHDR_SUCCESS; +} + +static gdImagePtr gdUhdrCreateGdImageFromJpegBlock(uhdr_mem_block_t *block, const char *label, + gdUhdrErrorPtr err) +{ + gdImagePtr image; + + if (!block || !block->data || block->data_sz == 0 || block->data_sz > (size_t)INT_MAX) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Invalid compressed UltraHDR component"); + return NULL; + } + + image = gdImageCreateFromJpegPtr((int)block->data_sz, block->data); + if (!image) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, + label && label[0] ? label : "Failed to decode UltraHDR JPEG component"); + return NULL; + } + + return image; +} + +static int gdUhdrScaleValue(int value, int from_extent, int to_extent, int *out) +{ + long long scaled; + + if (!out || value < 0 || from_extent <= 0 || to_extent <= 0) { + return 0; + } + + scaled = ((long long)value * (long long)to_extent + from_extent / 2) / from_extent; + if (scaled < 0 || scaled > INT_MAX) { + return 0; + } + + *out = (int)scaled; + return 1; +} + +static int gdUhdrReplaceImage(gdImagePtr *image, gdImagePtr replacement, gdUhdrErrorPtr err) +{ + if (!replacement) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to transform UltraHDR image component"); + return GD_UHDR_E_ENCODE; + } + + gdImageDestroy(*image); + *image = replacement; + return GD_UHDR_SUCCESS; +} + +static gdImagePtr gdUhdrScaleImage(gdImagePtr image, unsigned int width, unsigned int height) +{ + /* Mitchell uses GD's two-pass scaler and reduces downscale aliasing. */ + if (!gdImageSetInterpolationMethod(image, GD_MITCHELL)) { + return NULL; + } + + return gdImageScale(image, width, height); +} + +static int gdUhdrApplyGdOps(gdImagePtr *base_image, gdImagePtr *gainmap_image, gdUhdrImagePtr im, + gdUhdrErrorPtr err) +{ + int i; + + if (!base_image || !*base_image || !gainmap_image || !*gainmap_image || !im) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR transform state"); + return GD_UHDR_E_INVALID; + } + + for (i = 0; i < im->op_count; i++) { + gdUhdrOp *op = &im->ops[i]; + int base_w = gdImageSX(*base_image); + int base_h = gdImageSY(*base_image); + int gain_w = gdImageSX(*gainmap_image); + int gain_h = gdImageSY(*gainmap_image); + int status; + + switch (op->type) { + case GD_UHDR_OP_RESIZE: { + int scaled_gain_w; + int scaled_gain_h; + + if (!gdUhdrScaleValue(op->p1, base_w, gain_w, &scaled_gain_w) || + !gdUhdrScaleValue(op->p2, base_h, gain_h, &scaled_gain_h)) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR resize operation"); + return GD_UHDR_E_INVALID; + } + if (scaled_gain_w <= 0) { + scaled_gain_w = 1; + } + if (scaled_gain_h <= 0) { + scaled_gain_h = 1; + } + + status = gdUhdrReplaceImage( + base_image, + gdUhdrScaleImage(*base_image, (unsigned int)op->p1, (unsigned int)op->p2), err); + if (status != GD_UHDR_SUCCESS) { + return status; + } + status = + gdUhdrReplaceImage(gainmap_image, + gdUhdrScaleImage(*gainmap_image, (unsigned int)scaled_gain_w, + (unsigned int)scaled_gain_h), + err); + if (status != GD_UHDR_SUCCESS) { + return status; + } + break; + } + case GD_UHDR_OP_CROP: { + gdRect base_crop; + gdRect gain_crop; + int gain_left; + int gain_right; + int gain_top; + int gain_bottom; + + if (op->p1 < 0 || op->p3 < 0 || op->p2 <= op->p1 || op->p4 <= op->p3 || + op->p2 > base_w || op->p4 > base_h) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR crop operation"); + return GD_UHDR_E_INVALID; + } + if (!gdUhdrScaleValue(op->p1, base_w, gain_w, &gain_left) || + !gdUhdrScaleValue(op->p2, base_w, gain_w, &gain_right) || + !gdUhdrScaleValue(op->p3, base_h, gain_h, &gain_top) || + !gdUhdrScaleValue(op->p4, base_h, gain_h, &gain_bottom) || + gain_right <= gain_left || gain_bottom <= gain_top) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, + "Invalid UltraHDR gain map crop operation"); + return GD_UHDR_E_INVALID; + } + + base_crop.x = op->p1; + base_crop.y = op->p3; + base_crop.width = op->p2 - op->p1; + base_crop.height = op->p4 - op->p3; + gain_crop.x = gain_left; + gain_crop.y = gain_top; + gain_crop.width = gain_right - gain_left; + gain_crop.height = gain_bottom - gain_top; + + status = gdUhdrReplaceImage(base_image, gdImageCrop(*base_image, &base_crop), err); + if (status != GD_UHDR_SUCCESS) { + return status; + } + status = + gdUhdrReplaceImage(gainmap_image, gdImageCrop(*gainmap_image, &gain_crop), err); + if (status != GD_UHDR_SUCCESS) { + return status; + } + break; + } + case GD_UHDR_OP_ROTATE: { + gdImagePtr rotated_base = NULL; + gdImagePtr rotated_gainmap = NULL; + + if (op->p1 == 90) { + rotated_base = gdImageRotate90(*base_image, 0); + rotated_gainmap = gdImageRotate90(*gainmap_image, 0); + } else if (op->p1 == 180) { + rotated_base = gdImageRotate180(*base_image, 0); + rotated_gainmap = gdImageRotate180(*gainmap_image, 0); + } else if (op->p1 == 270) { + rotated_base = gdImageRotate270(*base_image, 0); + rotated_gainmap = gdImageRotate270(*gainmap_image, 0); + } + + status = gdUhdrReplaceImage(base_image, rotated_base, err); + if (status != GD_UHDR_SUCCESS) { + if (rotated_gainmap) { + gdImageDestroy(rotated_gainmap); + } + return status; + } + status = gdUhdrReplaceImage(gainmap_image, rotated_gainmap, err); + if (status != GD_UHDR_SUCCESS) { + return status; + } + break; + } + case GD_UHDR_OP_MIRROR: + if (op->p1 == GD_UHDR_MIRROR_HORIZONTAL) { + gdImageFlipHorizontal(*base_image); + gdImageFlipHorizontal(*gainmap_image); + } else { + gdImageFlipVertical(*base_image); + gdImageFlipVertical(*gainmap_image); + } + break; + default: + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Unknown queued UltraHDR operation"); + return GD_UHDR_E_INVALID; + } + } + + return GD_UHDR_SUCCESS; +} + +static int gdUhdrEncodeJpegComponent(gdImagePtr image, int quality, const gdImageMetadata *metadata, + int no_subsampling, void **out_data, int *out_size, + gdUhdrErrorPtr err) +{ + void *jpeg; + int jpeg_size = 0; + + if (no_subsampling) { + jpeg = gdImageJpegPtrWithMetadataNoSubsampling(image, &jpeg_size, quality, metadata); + } else { + jpeg = gdImageJpegPtrWithMetadata(image, &jpeg_size, quality, metadata); + } + if (!jpeg || jpeg_size <= 0) { + gdFree(jpeg); + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to encode UltraHDR JPEG component"); + return GD_UHDR_E_ENCODE; + } + + *out_data = jpeg; + *out_size = jpeg_size; + return GD_UHDR_SUCCESS; +} + +static int gdUhdrQueueOp(gdUhdrImagePtr im, gdUhdrOpType type, int p1, int p2, int p3, int p4) +{ + if (im->op_count == im->op_capacity) { + int new_cap = im->op_capacity == 0 ? 8 : im->op_capacity * 2; + gdUhdrOp *tmp; + + if (overflow2(new_cap, (int)sizeof(gdUhdrOp))) { + return GD_UHDR_E_INVALID; + } + + tmp = (gdUhdrOp *)gdRealloc(im->ops, (size_t)new_cap * sizeof(gdUhdrOp)); + if (!tmp) { + return GD_UHDR_E_INVALID; + } + + im->ops = tmp; + im->op_capacity = new_cap; + } + + im->ops[im->op_count].type = type; + im->ops[im->op_count].p1 = p1; + im->ops[im->op_count].p2 = p2; + im->ops[im->op_count].p3 = p3; + im->ops[im->op_count].p4 = p4; + im->op_count++; + + return GD_UHDR_SUCCESS; +} + +static gdUhdrImagePtr gdUhdrImageCreateFromData(void *data, int size, int format, + gdUhdrErrorPtr err) +{ + gdUhdrImagePtr im; + uhdr_codec_private_t *dec; + uhdr_compressed_image_t input; + uhdr_error_info_t rc; + + if (!data || size <= 0 || !gdUhdrIsSupportedFormat(format)) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR input"); + return NULL; + } + + if (!is_uhdr_image(data, size)) { + gdUhdrSetError(err, GD_UHDR_E_UNSUPPORTED, 0, "Input is not a valid UltraHDR image"); + return NULL; + } + + dec = uhdr_create_decoder(); + if (!dec) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to create UltraHDR decoder"); + return NULL; + } + + input.data = data; + input.data_sz = (size_t)size; + input.capacity = (size_t)size; + input.cg = UHDR_CG_UNSPECIFIED; + input.ct = UHDR_CT_UNSPECIFIED; + input.range = UHDR_CR_FULL_RANGE; + + rc = uhdr_dec_set_image(dec, &input); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_set_image failed"); + uhdr_release_decoder(dec); + return NULL; + } + + rc = uhdr_dec_probe(dec); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_probe failed"); + uhdr_release_decoder(dec); + return NULL; + } + + im = (gdUhdrImagePtr)gdCalloc(1, sizeof(*im)); + if (!im) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Out of memory"); + uhdr_release_decoder(dec); + return NULL; + } + + im->blob = gdMalloc((size_t)size); + if (!im->blob) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Out of memory"); + gdFree(im); + uhdr_release_decoder(dec); + return NULL; + } + + memcpy(im->blob, data, (size_t)size); + im->blob_size = size; + im->format = format; + im->width = uhdr_dec_get_image_width(dec); + im->height = uhdr_dec_get_image_height(dec); + im->has_gain_map = + (uhdr_dec_get_gainmap_width(dec) > 0 && uhdr_dec_get_gainmap_height(dec) > 0) ? 1 : 0; + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + uhdr_release_decoder(dec); + return im; +} + +#endif + +#ifndef HAVE_LIBUHDR + +static int gdUhdrUnavailableCode(void) { return GD_UHDR_NOT_AVAILABLE; } + +static const char *gdUhdrUnavailableMessage(void) +{ + return "UltraHDR support is not enabled in this build"; +} + +#endif + +/* + Function: gdUhdrIsAvailable + + Returns whether UltraHDR support is available in this libgd build. + + Returns: + + 1 if available, 0 otherwise. +*/ +BGD_DECLARE(int) gdUhdrIsAvailable(void) +{ +#ifdef HAVE_LIBUHDR + return 1; +#else + return 0; +#endif +} + +/* + Function: gdUhdrImageCreateFromFile + + Loads an UltraHDR image from a file. + + Variants: + + loads from a . + + loads from memory. + + Parameters: + + filename - input file path. + format - input format (currently ). + err - optional output error details. + + Returns: + + A new on success, or NULL on error. +*/ +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromFile(const char *filename, int format, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + FILE *fp; + gdIOCtxPtr in; + gdUhdrImagePtr im; + + if (!filename) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "filename must not be NULL"); + return NULL; + } + + fp = fopen(filename, "rb"); + if (!fp) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to open input file"); + return NULL; + } + + in = gdNewFileCtx(fp); + if (!in) { + fclose(fp); + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to create IO context"); + return NULL; + } + + im = gdUhdrImageCreateFromCtx(in, format, err); + in->gd_free(in); + fclose(fp); + return im; +#else + ARG_NOT_USED(filename); + ARG_NOT_USED(format); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return NULL; +#endif +} + +/* + Function: gdUhdrImageCreateFromCtx + + See . +*/ +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromCtx(gdIOCtxPtr ctx, int format, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + void *data = NULL; + int size = 0; + int rc; + gdUhdrImagePtr im; + + if (!gdUhdrIsSupportedFormat(format)) { + gdUhdrSetError(err, GD_UHDR_E_UNSUPPORTED, 0, "Unsupported UltraHDR format"); + return NULL; + } + + rc = gdUhdrReadAllFromCtx(ctx, &data, &size); + if (rc != GD_UHDR_SUCCESS) { + gdUhdrSetError(err, rc, 0, "Failed to read UltraHDR stream"); + return NULL; + } + + im = gdUhdrImageCreateFromData(data, size, format, err); + gdFree(data); + return im; +#else + ARG_NOT_USED(ctx); + ARG_NOT_USED(format); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return NULL; +#endif +} + +/* + Function: gdUhdrImageCreateFromPtr + + See . +*/ +BGD_DECLARE(gdUhdrImagePtr) +gdUhdrImageCreateFromPtr(int size, void *data, int format, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + if (!gdUhdrIsSupportedFormat(format)) { + gdUhdrSetError(err, GD_UHDR_E_UNSUPPORTED, 0, "Unsupported UltraHDR format"); + return NULL; + } + + return gdUhdrImageCreateFromData(data, size, format, err); +#else + ARG_NOT_USED(size); + ARG_NOT_USED(data); + ARG_NOT_USED(format); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return NULL; +#endif +} + +/* + Function: gdUhdrImageDestroy + + Releases an UltraHDR image created by or its + variants. +*/ +BGD_DECLARE(void) gdUhdrImageDestroy(gdUhdrImagePtr im) +{ + if (!im) { + return; + } + + gdFree(im->ops); + gdFree(im->blob); + gdFree(im); +} + +/* + Function: gdUhdrImageWidth + + Returns the image width in pixels. +*/ +BGD_DECLARE(int) gdUhdrImageWidth(gdUhdrImagePtr im) +{ + if (!im) { + return 0; + } + return im->width; +} + +/* + Function: gdUhdrImageHeight + + Returns the image height in pixels. +*/ +BGD_DECLARE(int) gdUhdrImageHeight(gdUhdrImagePtr im) +{ + if (!im) { + return 0; + } + return im->height; +} + +/* + Function: gdUhdrImageHasGainMap + + Returns nonzero when the loaded image contains a gain map. +*/ +BGD_DECLARE(int) gdUhdrImageHasGainMap(gdUhdrImagePtr im) +{ + if (!im) { + return 0; + } + return im->has_gain_map; +} + +/* + Function: gdUhdrImageResize + + Queues a resize operation to be applied at save time. +*/ +BGD_DECLARE(int) +gdUhdrImageResize(gdUhdrImagePtr im, int width, int height, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + int rc; + if (!im || width <= 0 || height <= 0) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid resize arguments"); + return GD_UHDR_E_INVALID; + } + + rc = gdUhdrQueueOp(im, GD_UHDR_OP_RESIZE, width, height, 0, 0); + if (rc != GD_UHDR_SUCCESS) { + gdUhdrSetError(err, rc, 0, "Failed to queue resize effect"); + return rc; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return GD_UHDR_SUCCESS; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(width); + ARG_NOT_USED(height); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageCrop + + Queues a crop operation to be applied at save time. +*/ +BGD_DECLARE(int) +gdUhdrImageCrop(gdUhdrImagePtr im, int left, int top, int width, int height, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + int rc; + int right; + int bottom; + + if (!im || left < 0 || top < 0 || width <= 0 || height <= 0) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid crop arguments"); + return GD_UHDR_E_INVALID; + } + + if (gdUhdrIntAddOverflow(left, width) || gdUhdrIntAddOverflow(top, height)) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid crop arguments"); + return GD_UHDR_E_INVALID; + } + + right = left + width; + bottom = top + height; + + rc = gdUhdrQueueOp(im, GD_UHDR_OP_CROP, left, right, top, bottom); + if (rc != GD_UHDR_SUCCESS) { + gdUhdrSetError(err, rc, 0, "Failed to queue crop effect"); + return rc; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return GD_UHDR_SUCCESS; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(left); + ARG_NOT_USED(top); + ARG_NOT_USED(width); + ARG_NOT_USED(height); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageRotate + + Queues a clockwise rotation (90, 180, or 270 degrees) to be applied at + save time. +*/ +BGD_DECLARE(int) +gdUhdrImageRotate(gdUhdrImagePtr im, int degrees, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + int rc; + if (!im || (degrees != 90 && degrees != 180 && degrees != 270)) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid rotation angle"); + return GD_UHDR_E_INVALID; + } + + rc = gdUhdrQueueOp(im, GD_UHDR_OP_ROTATE, degrees, 0, 0, 0); + if (rc != GD_UHDR_SUCCESS) { + gdUhdrSetError(err, rc, 0, "Failed to queue rotation effect"); + return rc; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return GD_UHDR_SUCCESS; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(degrees); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageMirror + + Queues a mirror operation to be applied at save time. +*/ +BGD_DECLARE(int) +gdUhdrImageMirror(gdUhdrImagePtr im, int axis, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + int rc; + if (!im || (axis != GD_UHDR_MIRROR_HORIZONTAL && axis != GD_UHDR_MIRROR_VERTICAL)) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid mirror axis"); + return GD_UHDR_E_INVALID; + } + + rc = gdUhdrQueueOp(im, GD_UHDR_OP_MIRROR, axis, 0, 0, 0); + if (rc != GD_UHDR_SUCCESS) { + gdUhdrSetError(err, rc, 0, "Failed to queue mirror effect"); + return rc; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return GD_UHDR_SUCCESS; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(axis); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageFile + + Saves an UltraHDR image to a file path. + + Variants: + + writes via . + + writes to memory. +*/ +BGD_DECLARE(int) +gdUhdrImageFile(gdUhdrImagePtr im, const char *filename, int format, int quality, + gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + FILE *fp; + gdIOCtxPtr out; + int rc; + + if (!filename) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "filename must not be NULL"); + return GD_UHDR_E_INVALID; + } + + fp = fopen(filename, "wb"); + if (!fp) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to open output file"); + return GD_UHDR_E_ENCODE; + } + + out = gdNewFileCtx(fp); + if (!out) { + fclose(fp); + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to create IO context"); + return GD_UHDR_E_ENCODE; + } + + rc = gdUhdrImageCtx(im, out, format, quality, err); + out->gd_free(out); + fclose(fp); + return rc; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(filename); + ARG_NOT_USED(format); + ARG_NOT_USED(quality); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageCtx + + See . +*/ +BGD_DECLARE(int) +gdUhdrImageCtx(gdUhdrImagePtr im, gdIOCtxPtr ctx, int format, int quality, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + uhdr_codec_private_t *dec = NULL; + uhdr_codec_private_t *enc = NULL; + uhdr_compressed_image_t source; + uhdr_compressed_image_t base_jpeg; + uhdr_compressed_image_t gainmap_jpeg; + uhdr_error_info_t rc; + uhdr_compressed_image_t *encoded; + uhdr_gainmap_metadata_t *source_metadata; + uhdr_gainmap_metadata_t metadata; + uhdr_mem_block_t *base_block = NULL; + uhdr_mem_block_t *gainmap_block = NULL; + gdImageMetadata *base_metadata = NULL; + gdImageMetadata *gainmap_metadata = NULL; + gdImagePtr base_image = NULL; + gdImagePtr gainmap_image = NULL; + void *base_jpeg_data = NULL; + void *gainmap_jpeg_data = NULL; + int base_jpeg_size = 0; + int gainmap_jpeg_size = 0; + int write_result; + int status = GD_UHDR_SUCCESS; + + if (!im || !ctx || !im->blob || im->blob_size <= 0) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR image or output context"); + return GD_UHDR_E_INVALID; + } + + if (!gdUhdrIsSupportedFormat(format)) { + gdUhdrSetError(err, GD_UHDR_E_UNSUPPORTED, 0, "Unsupported UltraHDR output format"); + return GD_UHDR_E_UNSUPPORTED; + } + + if (quality < 1 || quality > 95) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "quality must be in [1..95]"); + return GD_UHDR_E_INVALID; + } + + if (im->op_count == 0) { + write_result = gdPutBuf(im->blob, im->blob_size, ctx); + if (write_result != im->blob_size) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to write UltraHDR output"); + return GD_UHDR_E_ENCODE; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return GD_UHDR_SUCCESS; + } + + dec = uhdr_create_decoder(); + enc = uhdr_create_encoder(); + if (!dec || !enc) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to create UltraHDR codec contexts"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + gdUhdrInitCompressedImage(&source, im->blob, im->blob_size); + + rc = uhdr_dec_set_image(dec, &source); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_set_image failed"); + status = GD_UHDR_E_DECODE; + goto cleanup; + } + + rc = uhdr_dec_probe(dec); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_probe failed"); + status = GD_UHDR_E_DECODE; + goto cleanup; + } + + source_metadata = uhdr_dec_get_gainmap_metadata(dec); + if (!source_metadata) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "UltraHDR gain map metadata is missing"); + status = GD_UHDR_E_DECODE; + goto cleanup; + } + metadata = *source_metadata; + + base_block = uhdr_dec_get_base_image(dec); + gainmap_block = uhdr_dec_get_gainmap_image(dec); + + /* + * Geometry transforms make source EXIF dimensions, orientation, and + * thumbnails stale. Preserve ICC, but copy EXIF only for pass-through + * writes unless GD learns to rewrite those tags. + */ + status = + gdUhdrCreateJpegMetadataFromBlock(base_block, im->op_count == 0, 1, &base_metadata, err); + if (status != GD_UHDR_SUCCESS) { + goto cleanup; + } + + if (!metadata.use_base_cg) { + status = gdUhdrCreateJpegMetadataFromBlock(gainmap_block, 0, 1, &gainmap_metadata, err); + if (status != GD_UHDR_SUCCESS) { + goto cleanup; + } + } + + base_image = + gdUhdrCreateGdImageFromJpegBlock(base_block, "Failed to decode UltraHDR base image", err); + if (!base_image) { + status = GD_UHDR_E_DECODE; + goto cleanup; + } + + gainmap_image = gdUhdrCreateGdImageFromJpegBlock( + gainmap_block, "Failed to decode UltraHDR gain map image", err); + if (!gainmap_image) { + status = GD_UHDR_E_DECODE; + goto cleanup; + } + + status = gdUhdrApplyGdOps(&base_image, &gainmap_image, im, err); + if (status != GD_UHDR_SUCCESS) { + goto cleanup; + } + + status = gdUhdrEncodeJpegComponent(base_image, quality, base_metadata, 0, &base_jpeg_data, + &base_jpeg_size, err); + if (status != GD_UHDR_SUCCESS) { + goto cleanup; + } + + status = gdUhdrEncodeJpegComponent(gainmap_image, quality, gainmap_metadata, 1, + &gainmap_jpeg_data, &gainmap_jpeg_size, err); + if (status != GD_UHDR_SUCCESS) { + goto cleanup; + } + + gdUhdrInitCompressedImage(&base_jpeg, base_jpeg_data, base_jpeg_size); + gdUhdrInitCompressedImage(&gainmap_jpeg, gainmap_jpeg_data, gainmap_jpeg_size); + + rc = uhdr_enc_set_output_format(enc, UHDR_CODEC_JPG); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_enc_set_output_format failed"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + rc = uhdr_enc_set_compressed_image(enc, &base_jpeg, UHDR_BASE_IMG); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_enc_set_compressed_image(base) failed"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + rc = uhdr_enc_set_gainmap_image(enc, &gainmap_jpeg, &metadata); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_enc_set_gainmap_image failed"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + rc = uhdr_encode(enc); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_encode failed"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + encoded = uhdr_get_encoded_stream(enc); + if (!encoded || !encoded->data || encoded->data_sz == 0) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Encoded UltraHDR stream is empty"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + write_result = gdPutBuf(encoded->data, (int)encoded->data_sz, ctx); + if (write_result != (int)encoded->data_sz) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to write UltraHDR output"); + status = GD_UHDR_E_ENCODE; + goto cleanup; + } + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + status = GD_UHDR_SUCCESS; + +cleanup: + gdFree(gainmap_jpeg_data); + gdFree(base_jpeg_data); + if (gainmap_image) { + gdImageDestroy(gainmap_image); + } + if (base_image) { + gdImageDestroy(base_image); + } + if (gainmap_metadata) { + gdImageMetadataFree(gainmap_metadata); + } + if (base_metadata) { + gdImageMetadataFree(base_metadata); + } + if (enc) { + uhdr_release_encoder(enc); + } + if (dec) { + uhdr_release_decoder(dec); + } + return status; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(ctx); + ARG_NOT_USED(format); + ARG_NOT_USED(quality); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return gdUhdrUnavailableCode(); +#endif +} + +/* + Function: gdUhdrImageWritePtr + + See . +*/ +BGD_DECLARE(void *) +gdUhdrImageWritePtr(gdUhdrImagePtr im, int *size, int format, int quality, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + gdIOCtxPtr out; + void *rv; + + if (size) { + *size = 0; + } + + out = gdNewDynamicCtx(2048, NULL); + if (!out) { + gdUhdrSetError(err, GD_UHDR_E_ENCODE, 0, "Failed to create dynamic output context"); + return NULL; + } + + if (gdUhdrImageCtx(im, out, format, quality, err) != GD_UHDR_SUCCESS) { + out->gd_free(out); + return NULL; + } + + rv = gdDPExtractData(out, size); + out->gd_free(out); + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + return rv; +#else + ARG_NOT_USED(im); + ARG_NOT_USED(format); + ARG_NOT_USED(quality); + if (size) { + *size = 0; + } + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return NULL; +#endif +} + +/* + Function: gdUhdrImageGetSdr + + Decodes and returns the SDR base image as a . +*/ +BGD_DECLARE(gdImagePtr) +gdUhdrImageGetSdr(gdUhdrImagePtr im, gdUhdrErrorPtr err) +{ +#ifdef HAVE_LIBUHDR + uhdr_codec_private_t *dec; + uhdr_compressed_image_t input; + uhdr_error_info_t rc; + uhdr_raw_image_t *raw; + gdImagePtr out; + int x, y; + unsigned int stride; + unsigned char *row; + + if (!im || !im->blob || im->blob_size <= 0) { + gdUhdrSetError(err, GD_UHDR_E_INVALID, 0, "Invalid UltraHDR image"); + return NULL; + } + + dec = uhdr_create_decoder(); + if (!dec) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to create UltraHDR decoder"); + return NULL; + } + + input.data = im->blob; + input.data_sz = (size_t)im->blob_size; + input.capacity = (size_t)im->blob_size; + input.cg = UHDR_CG_UNSPECIFIED; + input.ct = UHDR_CT_UNSPECIFIED; + input.range = UHDR_CR_FULL_RANGE; + + rc = uhdr_dec_set_image(dec, &input); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_set_image failed"); + uhdr_release_decoder(dec); + return NULL; + } + + rc = uhdr_dec_set_out_img_format(dec, UHDR_IMG_FMT_32bppRGBA8888); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_set_out_img_format failed"); + uhdr_release_decoder(dec); + return NULL; + } + + rc = uhdr_dec_set_out_color_transfer(dec, UHDR_CT_SRGB); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_dec_set_out_color_transfer failed"); + uhdr_release_decoder(dec); + return NULL; + } + + rc = uhdr_decode(dec); + if (rc.error_code != UHDR_CODEC_OK) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, rc.error_code, + rc.has_detail ? rc.detail : "uhdr_decode failed"); + uhdr_release_decoder(dec); + return NULL; + } + + raw = uhdr_get_decoded_image(dec); + if (!raw || !raw->planes[UHDR_PLANE_PACKED] || raw->w == 0 || raw->h == 0) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Decoded SDR image unavailable"); + uhdr_release_decoder(dec); + return NULL; + } + + out = gdImageCreateTrueColor((int)raw->w, (int)raw->h); + if (!out) { + gdUhdrSetError(err, GD_UHDR_E_DECODE, 0, "Failed to allocate SDR gdImage"); + uhdr_release_decoder(dec); + return NULL; + } + + stride = raw->stride[UHDR_PLANE_PACKED] ? raw->stride[UHDR_PLANE_PACKED] : raw->w; + for (y = 0; y < (int)raw->h; y++) { + row = (unsigned char *)raw->planes[UHDR_PLANE_PACKED] + ((size_t)y * stride * 4); + for (x = 0; x < (int)raw->w; x++) { + unsigned char r = row[(size_t)x * 4 + 0]; + unsigned char g = row[(size_t)x * 4 + 1]; + unsigned char b = row[(size_t)x * 4 + 2]; + unsigned char a8 = row[(size_t)x * 4 + 3]; + int a7 = gdAlphaMax - (a8 >> 1); + out->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a7); + } + } + out->saveAlphaFlag = 1; + + gdUhdrSetError(err, GD_UHDR_SUCCESS, 0, NULL); + uhdr_release_decoder(dec); + return out; +#else + ARG_NOT_USED(im); + gdUhdrSetError(err, gdUhdrUnavailableCode(), 0, gdUhdrUnavailableMessage()); + return NULL; +#endif +} diff --git a/ext/gd/libgd/gd_vector2d.h b/ext/gd/libgd/gd_vector2d.h new file mode 100644 index 000000000000..e21cb02761a5 --- /dev/null +++ b/ext/gd/libgd/gd_vector2d.h @@ -0,0 +1,631 @@ +#ifndef GD_VECTOR2D_H +#define GD_VECTOR2D_H + +#include "gd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Experimental API: source and ABI compatibility are not yet guaranteed. */ +#define GD_VECTOR2D_EXPERIMENTAL 1 +#define GD_VECTOR2D_VERSION 1 + +typedef struct gdContextStruct gdContext; +typedef gdContext *gdContextPtr; +typedef struct gdPathStruct gdPath; +typedef gdPath *gdPathPtr; +typedef struct gdPaintStruct gdPaint; +typedef gdPaint *gdPaintPtr; +typedef struct gdPathPatternStruct gdPathPattern; +typedef gdPathPattern *gdPathPatternPtr; + +typedef struct gdPathMatrixStruct { + double m00, m10, m01, m11, m02, m12; +} gdPathMatrix; +typedef gdPathMatrix *gdPathMatrixPtr; + +typedef struct gdRectFStruct { + double x, y, w, h; +} gdRectF; +typedef gdRectF *gdRectFPtr; + +typedef enum { GD_EXTEND_NONE, GD_EXTEND_REPEAT, GD_EXTEND_REFLECT, GD_EXTEND_PAD } gdExtendMode; +typedef enum { gdLineCapButt, gdLineCapRound, gdLineCapSquare } gdLineCap; +typedef enum { gdLineJoinMiter, gdLineJoinRound, gdLineJoinBevel } gdLineJoin; +typedef enum { gdFillRuleNonZero, gdFillRuleEvenOdd } gdFillRule; + +typedef enum { + GD_OP_CLEAR, + GD_OP_SOURCE, + GD_OP_OVER, + GD_OP_IN, + GD_OP_OUT, + GD_OP_ATOP, + GD_OP_DEST, + GD_OP_DEST_OVER, + GD_OP_DEST_IN, + GD_OP_DEST_OUT, + GD_OP_DEST_ATOP, + GD_OP_XOR, + GD_OP_ADD, + GD_OP_SATURATE, + GD_OP_MULTIPLY, + GD_OP_SCREEN, + GD_OP_OVERLAY, + GD_OP_DARKEN, + GD_OP_LIGHTEN, + GD_OP_COLOR_DODGE, + GD_OP_COLOR_BURN, + GD_OP_HARD_LIGHT, + GD_OP_SOFT_LIGHT, + GD_OP_DIFFERENCE, + GD_OP_EXCLUSION, + GD_OP_HSL_HUE, + GD_OP_HSL_SATURATION, + GD_OP_HSL_COLOR, + GD_OP_HSL_LUMINOSITY, + GD_OP_COUNT +} gdCompositeOperator; +typedef gdCompositeOperator gdImageOp; +#define gdImageOpsSrc GD_OP_SOURCE +#define gdImageOpsSrcOver GD_OP_OVER +#define gdImageOpsDstIn GD_OP_DEST_IN +#define gdImageOpsDstOut GD_OP_DEST_OUT + +BGD_DECLARE(gdContextPtr) gdContextCreateForImage(gdImagePtr image); +BGD_DECLARE(void) gdContextFlushImage(gdContextPtr context); +BGD_DECLARE(gdImagePtr) gdContextGetImage(gdContextPtr context); +BGD_DECLARE(void) gdContextDestroy(gdContextPtr context); +BGD_DECLARE(int) gdContextSave(gdContextPtr context); +BGD_DECLARE(int) gdContextRestore(gdContextPtr context); +BGD_DECLARE(int) gdContextClip(gdContextPtr context); +BGD_DECLARE(int) gdContextClipPreserve(gdContextPtr context); +BGD_DECLARE(void) gdContextNewPath(gdContextPtr context); +BGD_DECLARE(void) gdContextAppendPath(gdContextPtr context, gdPathPtr path); +BGD_DECLARE(void) +gdContextSetSourceRgba(gdContextPtr context, double r, double g, double b, double a); +BGD_DECLARE(void) gdContextSetSourceRgb(gdContextPtr context, double r, double g, double b); +BGD_DECLARE(void) +gdContextSetSourceImage(gdContextPtr context, gdImagePtr image, double x, double y); +BGD_DECLARE(void) gdContextSetSource(gdContextPtr context, gdPaintPtr source); +BGD_DECLARE(void) gdContextSetOperator(gdContextPtr context, gdCompositeOperator op); +BGD_DECLARE(void) gdContextSetOpacity(gdContextPtr context, double opacity); +BGD_DECLARE(void) gdContextMoveTo(gdContextPtr context, double x, double y); +BGD_DECLARE(void) gdContextRelMoveTo(gdContextPtr context, double dx, double dy); +BGD_DECLARE(void) gdContextLineTo(gdContextPtr context, double x, double y); +BGD_DECLARE(void) gdContextRelLineTo(gdContextPtr context, double dx, double dy); +BGD_DECLARE(void) +gdContextCurveTo(gdContextPtr context, double x1, double y1, double x2, double y2, double x3, + double y3); +BGD_DECLARE(void) +gdContextRelCurveTo(gdContextPtr context, double dx1, double dy1, double dx2, double dy2, + double dx3, double dy3); +BGD_DECLARE(void) gdContextQuadTo(gdContextPtr context, double x1, double y1, double x2, double y2); +BGD_DECLARE(void) +gdContextRelQuadTo(gdContextPtr context, double dx1, double dy1, double dx2, double dy2); +BGD_DECLARE(void) +gdContextArc(gdContextPtr context, double cx, double cy, double radius, double a0, double a1); +BGD_DECLARE(void) +gdContextNegativeArc(gdContextPtr context, double cx, double cy, double radius, double a0, + double a1); +BGD_DECLARE(void) +gdContextRectangle(gdContextPtr context, double x, double y, double width, double height); +BGD_DECLARE(void) gdContextClosePath(gdContextPtr context); +BGD_DECLARE(void) gdContextScale(gdContextPtr context, double x, double y); +BGD_DECLARE(void) gdContextTranslate(gdContextPtr context, double x, double y); +BGD_DECLARE(void) gdContextRotate(gdContextPtr context, double radians); +BGD_DECLARE(void) gdContextTransform(gdContextPtr context, const gdPathMatrixPtr matrix); +BGD_DECLARE(void) gdContextSetLineWidth(gdContextPtr context, double width); +BGD_DECLARE(void) gdContextSetLineCap(gdContextPtr context, gdLineCap cap); +BGD_DECLARE(void) gdContextSetLineJoin(gdContextPtr context, gdLineJoin join); +BGD_DECLARE(void) +gdContextSetDash(gdContextPtr context, double offset, const double *data, int size); +BGD_DECLARE(void) gdContextSetFillRule(gdContextPtr context, gdFillRule rule); +BGD_DECLARE(void) gdContextStroke(gdContextPtr context); +BGD_DECLARE(void) gdContextStrokePreserve(gdContextPtr context); +BGD_DECLARE(void) gdContextFill(gdContextPtr context); +BGD_DECLARE(void) gdContextFillPreserve(gdContextPtr context); +BGD_DECLARE(void) gdContextPaint(gdContextPtr context); + +BGD_DECLARE(gdPaintPtr) gdPaintCreateFromPattern(gdPathPatternPtr pattern); +BGD_DECLARE(gdPaintPtr) gdPaintCreateLinear(double x0, double y0, double x1, double y1); +BGD_DECLARE(gdPaintPtr) +gdPaintCreateRadial(double x0, double y0, double r0, double x1, double y1, double r1); +BGD_DECLARE(int) +gdPaintAddColorStopRgb(gdPaintPtr paint, double offset, double r, double g, double b); +BGD_DECLARE(int) +gdPaintAddColorStopRgba(gdPaintPtr paint, double offset, double r, double g, double b, double a); +BGD_DECLARE(int) gdPaintSetExtend(gdPaintPtr paint, gdExtendMode extend); +BGD_DECLARE(int) gdPaintSetMatrix(gdPaintPtr paint, const gdPathMatrixPtr matrix); +BGD_DECLARE(void) gdPaintDestroy(gdPaintPtr paint); + +BGD_DECLARE(gdPathPatternPtr) gdPathPatternCreateForImage(gdImagePtr image); +BGD_DECLARE(void) gdPathPatternDestroy(gdPathPatternPtr pattern); +BGD_DECLARE(void) gdPathPatternSetExtend(gdPathPatternPtr pattern, gdExtendMode extend); +BGD_DECLARE(void) gdPathPatternSetMatrix(gdPathPatternPtr pattern, gdPathMatrixPtr matrix); +BGD_DECLARE(void) gdPathPatternSetOpacity(gdPathPatternPtr pattern, double opacity); + +/** + * Function: gdPathMatrixInit + * + * Initialize an affine transformation matrix from its six coefficients. + * + * A point is mapped to (x * m00 + y * m01 + m02, + * x * m10 + y * m11 + m12). + * + * Parameters: + * matrix - The matrix to initialize. + * m00 - The horizontal x coefficient. + * m10 - The vertical x coefficient. + * m01 - The horizontal y coefficient. + * m11 - The vertical y coefficient. + * m02 - The horizontal translation. + * m12 - The vertical translation. + * + * See also: + * - + * - + */ +BGD_DECLARE(void) +gdPathMatrixInit(gdPathMatrixPtr matrix, double m00, double m10, double m01, double m11, double m02, + double m12); +/** + * Function: gdPathMatrixInitIdentity + * + * Initialize a matrix to the identity transformation. + * + * Parameters: + * matrix - The matrix to initialize. + */ +BGD_DECLARE(void) gdPathMatrixInitIdentity(gdPathMatrixPtr matrix); + +/** + * Function: gdPathMatrixInitTranslate + * + * Initialize a translation matrix. + * + * Parameters: + * matrix - The matrix to initialize. + * x - The horizontal translation. + * y - The vertical translation. + */ +BGD_DECLARE(void) gdPathMatrixInitTranslate(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixInitScale + * + * Initialize a scaling matrix. + * + * Parameters: + * matrix - The matrix to initialize. + * x - The horizontal scale factor. + * y - The vertical scale factor. + */ +BGD_DECLARE(void) gdPathMatrixInitScale(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixInitShear + * + * Initialize a shear matrix. The shear factors are the tangents of the + * supplied angles. + * + * Parameters: + * matrix - The matrix to initialize. + * x - The horizontal shear angle, in radians. + * y - The vertical shear angle, in radians. + */ +BGD_DECLARE(void) gdPathMatrixInitShear(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixInitRotate + * + * Initialize a rotation matrix about the origin. + * + * Parameters: + * matrix - The matrix to initialize. + * radians - The rotation angle, in radians. + */ +BGD_DECLARE(void) gdPathMatrixInitRotate(gdPathMatrixPtr matrix, double radians); + +/** + * Function: gdPathMatrixInitRotateTranslate + * + * Initialize a rotation matrix about a specified point. The point (x, y) + * remains unchanged by the resulting transformation. + * + * Parameters: + * matrix - The matrix to initialize. + * radians - The rotation angle, in radians. + * x - The horizontal coordinate of the rotation center. + * y - The vertical coordinate of the rotation center. + */ +BGD_DECLARE(void) +gdPathMatrixInitRotateTranslate(gdPathMatrixPtr matrix, double radians, double x, double y); + +/** + * Function: gdPathMatrixTranslate + * + * Apply a translation before the transformation already in a matrix. + * + * Parameters: + * matrix - The matrix to modify. + * x - The horizontal translation. + * y - The vertical translation. + */ +BGD_DECLARE(void) gdPathMatrixTranslate(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixScale + * + * Apply scaling before the transformation already in a matrix. + * + * Parameters: + * matrix - The matrix to modify. + * x - The horizontal scale factor. + * y - The vertical scale factor. + */ +BGD_DECLARE(void) gdPathMatrixScale(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixShear + * + * Apply a shear before the transformation already in a matrix. + * + * Parameters: + * matrix - The matrix to modify. + * x - The horizontal shear angle, in radians. + * y - The vertical shear angle, in radians. + */ +BGD_DECLARE(void) gdPathMatrixShear(gdPathMatrixPtr matrix, double x, double y); + +/** + * Function: gdPathMatrixRotate + * + * Apply a rotation about the origin before the transformation already in a + * matrix. + * + * Parameters: + * matrix - The matrix to modify. + * radians - The rotation angle, in radians. + */ +BGD_DECLARE(void) gdPathMatrixRotate(gdPathMatrixPtr matrix, double radians); + +/** + * Function: gdPathMatrixRotateTranslate + * + * Apply a rotation about a specified point before the transformation already + * in a matrix. + * + * Parameters: + * matrix - The matrix to modify. + * radians - The rotation angle, in radians. + * x - The horizontal coordinate of the rotation center. + * y - The vertical coordinate of the rotation center. + */ +BGD_DECLARE(void) +gdPathMatrixRotateTranslate(gdPathMatrixPtr matrix, double radians, double x, double y); + +/** + * Function: gdPathMatrixMultiply + * + * Compose two affine transformations. The result maps through a first and + * then through b. matrix may alias a or b. + * + * Parameters: + * matrix - The destination matrix. + * a - The transformation applied first. + * b - The transformation applied second. + */ +BGD_DECLARE(void) +gdPathMatrixMultiply(gdPathMatrixPtr matrix, const gdPathMatrixPtr a, const gdPathMatrixPtr b); + +/** + * Function: gdPathMatrixInvert + * + * Invert an affine transformation in place. A singular matrix is left + * unchanged. + * + * Parameters: + * matrix - The matrix to invert. + * + * Returns: + * Non-zero on success, or zero if the matrix is singular. + */ +BGD_DECLARE(int) gdPathMatrixInvert(gdPathMatrixPtr matrix); + +/** + * Function: gdPathMatrixMap + * + * Transform a pair of coordinates. + * + * Parameters: + * matrix - The transformation matrix. + * x - The source horizontal coordinate. + * y - The source vertical coordinate. + * result_x - Where to store the transformed horizontal coordinate. + * result_y - Where to store the transformed vertical coordinate. + */ +BGD_DECLARE(void) +gdPathMatrixMap(const gdPathMatrixPtr matrix, double x, double y, double *result_x, + double *result_y); + +/** + * Function: gdPathMatrixMapPoint + * + * Transform a point. src and dst may point to the same object. + * + * Parameters: + * matrix - The transformation matrix. + * src - The source point. + * dst - Where to store the transformed point. + */ +BGD_DECLARE(void) +gdPathMatrixMapPoint(const gdPathMatrixPtr matrix, const gdPointFPtr src, gdPointFPtr dst); + +/** + * Function: gdPathMatrixMapRect + * + * Transform all four corners of a rectangle and calculate their axis-aligned + * bounding box. src and dst may point to the same object. + * + * Parameters: + * matrix - The transformation matrix. + * src - The source rectangle. + * dst - Where to store the transformed bounding rectangle. + */ +BGD_DECLARE(void) +gdPathMatrixMapRect(const gdPathMatrixPtr matrix, const gdRectFPtr src, gdRectFPtr dst); + +/** + * Function: gdPathCreate + * + * Create an empty path. + * + * Returns: + * A new path, or NULL if allocation fails. Destroy it with . + */ +BGD_DECLARE(gdPathPtr) gdPathCreate(void); + +/** + * Function: gdPathDuplicate + * + * Create an independent copy of a path. + * + * Parameters: + * path - The path to copy. Must not be NULL. + * + * Returns: + * A new path, or NULL if path is NULL or allocation fails. Destroy the copy + * with . + */ +BGD_DECLARE(gdPathPtr) gdPathDuplicate(const gdPathPtr path); + +/** + * Function: gdPathDestroy + * + * Release a path. Passing NULL has no effect. + * + * Parameters: + * path - The path to release. + */ +BGD_DECLARE(void) gdPathDestroy(gdPathPtr path); + +/** + * Function: gdPathAppendPath + * + * Append all contours from one path to another. + * + * Parameters: + * path - The destination path. + * source - The path to append. + */ +BGD_DECLARE(void) gdPathAppendPath(gdPathPtr path, const gdPathPtr source); + +/** + * Function: gdPathTransform + * + * Transform every point in a path in place. + * + * Parameters: + * path - The path to transform. + * matrix - The transformation matrix. + */ +BGD_DECLARE(void) gdPathTransform(gdPathPtr path, const gdPathMatrixPtr matrix); + +/** + * Function: gdPathMoveTo + * + * Start a new contour at an absolute position. + * + * Parameters: + * path - The path to modify. + * x - The horizontal coordinate. + * y - The vertical coordinate. + */ +BGD_DECLARE(void) gdPathMoveTo(gdPathPtr path, double x, double y); + +/** + * Function: gdPathRelMoveTo + * + * Start a new contour at an offset from the current point. For an empty path, + * the offset is relative to (0, 0). + * + * Parameters: + * path - The path to modify. + * dx - The horizontal offset. + * dy - The vertical offset. + */ +BGD_DECLARE(void) gdPathRelMoveTo(gdPathPtr path, double dx, double dy); + +/** + * Function: gdPathLineTo + * + * Add a straight line to an absolute position. + * + * Parameters: + * path - The path to modify. + * x - The endpoint's horizontal coordinate. + * y - The endpoint's vertical coordinate. + */ +BGD_DECLARE(void) gdPathLineTo(gdPathPtr path, double x, double y); + +/** + * Function: gdPathRelLineTo + * + * Add a straight line using an offset from the current point. + * + * Parameters: + * path - The path to modify. + * dx - The horizontal offset. + * dy - The vertical offset. + */ +BGD_DECLARE(void) gdPathRelLineTo(gdPathPtr path, double dx, double dy); + +/** + * Function: gdPathQuadTo + * + * Add a quadratic Bezier curve. + * + * Parameters: + * path - The path to modify. + * x1 - The control point's horizontal coordinate. + * y1 - The control point's vertical coordinate. + * x2 - The endpoint's horizontal coordinate. + * y2 - The endpoint's vertical coordinate. + */ +BGD_DECLARE(void) gdPathQuadTo(gdPathPtr path, double x1, double y1, double x2, double y2); + +/** + * Function: gdPathRelQuadTo + * + * Add a quadratic Bezier curve using offsets from the current point. + * + * Parameters: + * path - The path to modify. + * dx1 - The control point's horizontal offset. + * dy1 - The control point's vertical offset. + * dx2 - The endpoint's horizontal offset. + * dy2 - The endpoint's vertical offset. + */ +BGD_DECLARE(void) gdPathRelQuadTo(gdPathPtr path, double dx1, double dy1, double dx2, double dy2); + +/** + * Function: gdPathCurveTo + * + * Add a cubic Bezier curve. + * + * Parameters: + * path - The path to modify. + * x1 - The first control point's horizontal coordinate. + * y1 - The first control point's vertical coordinate. + * x2 - The second control point's horizontal coordinate. + * y2 - The second control point's vertical coordinate. + * x3 - The endpoint's horizontal coordinate. + * y3 - The endpoint's vertical coordinate. + */ +BGD_DECLARE(void) +gdPathCurveTo(gdPathPtr path, double x1, double y1, double x2, double y2, double x3, double y3); + +/** + * Function: gdPathRelCurveTo + * + * Add a cubic Bezier curve using offsets from the current point. + * + * Parameters: + * path - The path to modify. + * dx1 - The first control point's horizontal offset. + * dy1 - The first control point's vertical offset. + * dx2 - The second control point's horizontal offset. + * dy2 - The second control point's vertical offset. + * dx3 - The endpoint's horizontal offset. + * dy3 - The endpoint's vertical offset. + */ +BGD_DECLARE(void) +gdPathRelCurveTo(gdPathPtr path, double dx1, double dy1, double dx2, double dy2, double dx3, + double dy3); + +/** + * Function: gdPathArc + * + * Add a circular arc in the positive-angle direction. Angles are in radians. + * A line is added from the current point to the beginning of the arc when + * necessary. + * + * Parameters: + * path - The path to modify. + * cx - The center's horizontal coordinate. + * cy - The center's vertical coordinate. + * radius - The arc radius. + * angle1 - The starting angle in radians. + * angle2 - The ending angle in radians. + */ +BGD_DECLARE(void) +gdPathArc(gdPathPtr path, double cx, double cy, double radius, double angle1, double angle2); + +/** + * Function: gdPathNegativeArc + * + * Add a circular arc in the negative-angle direction. Angles are in radians. + * A line is added from the current point to the beginning of the arc when + * necessary. + * + * Parameters: + * path - The path to modify. + * cx - The center's horizontal coordinate. + * cy - The center's vertical coordinate. + * radius - The arc radius. + * angle1 - The starting angle in radians. + * angle2 - The ending angle in radians. + */ +BGD_DECLARE(void) +gdPathNegativeArc(gdPathPtr path, double cx, double cy, double radius, double angle1, + double angle2); + +/** + * Function: gdPathArcTo + * + * Connect the current point to (x1, y1) and (x2, y2) with a circular arc + * tangent to both line segments. Degenerate geometry or a non-positive radius + * adds a line to (x1, y1). + * + * Parameters: + * path - The path to modify. + * x1 - The corner's horizontal coordinate. + * y1 - The corner's vertical coordinate. + * x2 - The second tangent line's horizontal endpoint. + * y2 - The second tangent line's vertical endpoint. + * radius - The arc radius. + */ +BGD_DECLARE(void) +gdPathArcTo(gdPathPtr path, double x1, double y1, double x2, double y2, double radius); + +/** + * Function: gdPathRectangle + * + * Add a closed rectangular contour. + * + * Parameters: + * path - The path to modify. + * x - The rectangle's left coordinate. + * y - The rectangle's top coordinate. + * width - The rectangle width. + * height - The rectangle height. + */ +BGD_DECLARE(void) gdPathRectangle(gdPathPtr path, double x, double y, double width, double height); + +/** + * Function: gdPathClose + * + * Close the current contour with a line to its starting point. An empty path + * or an already closed contour is unchanged. + * + * Parameters: + * path - The path to modify. + */ +BGD_DECLARE(void) gdPathClose(gdPathPtr path); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ext/gd/libgd/gd_vector2d_private.h b/ext/gd/libgd/gd_vector2d_private.h new file mode 100644 index 000000000000..3a98aa452c54 --- /dev/null +++ b/ext/gd/libgd/gd_vector2d_private.h @@ -0,0 +1,134 @@ +#ifndef GD_VECTOR2D_PRIVATE_H +#define GD_VECTOR2D_PRIVATE_H + +#include "gd_array.h" +#include "gd_vector2d.h" + +#if defined(__GNUC__) && !defined(_WIN32) +#define GD_VECTOR2D_INTERNAL __attribute__((visibility("hidden"))) +#else +#define GD_VECTOR2D_INTERNAL +#endif + +typedef enum { + GD_SURFACE_NONE, + GD_SURFACE_ARGB32, + GD_SURFACE_XRGB32, + GD_SURFACE_A8, + GD_SURFACE_COUNT +} gdSurfaceType; +typedef struct gdSurfaceStruct { + int ref; + unsigned char *data; + gdSurfaceType type; + int gdOwned; + int width, height, stride; +} gdSurface; +typedef gdSurface *gdSurfacePtr; + +typedef struct gdSpanStruct { + short x, y; + unsigned short len; + unsigned char coverage; +} gdSpan; +typedef gdSpan *gdSpanPtr; +typedef struct gdSpanRleStruct { + int ref; + struct { + gdSpanPtr data; + int size, capacity; + } spans; + int x, y, w, h; +} gdSpanRle; +typedef gdSpanRle *gdSpanRlePtr; + +typedef enum { + gdPaintTypeColor, + gdPaintTypeGradient, + gdPaintTypeSurface, + gdPaintTypePattern +} gdPaintType; +typedef struct gdColorStruct { + double r, g, b, a; +} gdColor; +typedef gdColor *gdColorPtr; +struct gdPathPatternStruct { + int ref; + gdExtendMode extend; + gdSurfacePtr surface; + gdPathMatrix matrix; + double opacity; +}; +typedef struct gdGradientStruct gdGradient; +struct gdPaintStruct { + int ref; + gdPaintType type; + union { + gdColorPtr color; + gdSurfacePtr surface; + gdPathPatternPtr pattern; + gdGradient *gradient; + }; +}; +typedef struct gdPathDashStruct { + double *data; + int size; + double offset; +} gdPathDash; +typedef gdPathDash *gdPathDashPtr; +typedef struct gdStrokeStruct { + double width, miterlimit; + gdLineCap cap; + gdLineJoin join; + gdPathDashPtr dash; +} gdStroke; +typedef gdStroke *gdStrokePtr; +typedef struct gdStateStruct { + gdSpanRlePtr clippath; + gdPaintPtr source; + gdPathMatrix matrix; + gdFillRule winding; + gdStroke stroke; + gdCompositeOperator op; + double opacity; + struct gdStateStruct *next; +} gdState; +typedef gdState *gdStatePtr; +typedef enum { + gdPathOpsMoveTo, + gdPathOpsLineTo, + gdPathOpsCubicTo, + gdPathOpsQuadTo, + gdPathOpsClose +} gdPathOps; +typedef gdPathOps *gdPathOpsPtr; +struct gdPathStruct { + int ref, contours; + gdPointF start; + gdArray elements; + gdArray points; +}; +struct gdContextStruct { + int ref; + gdSurfacePtr surface; + gdPathPtr path; + gdStatePtr state; + gdSpanRlePtr rle, clippath; + gdRectF clip; + gdImagePtr image; + int imageOwned; +}; + +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceCreate(int width, int height, unsigned int type); +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceCreateForData(unsigned char *data, int width, int height, + int stride, unsigned int type); +GD_VECTOR2D_INTERNAL gdSurfacePtr gdSurfaceAddRef(gdSurfacePtr surface); +GD_VECTOR2D_INTERNAL void gdSurfaceDestroy(gdSurfacePtr surface); +GD_VECTOR2D_INTERNAL gdContextPtr gdContextCreate(gdSurfacePtr surface); +GD_VECTOR2D_INTERNAL gdPathPatternPtr gdPathPatternCreate(gdSurfacePtr surface); +GD_VECTOR2D_INTERNAL void gdContextSetSourceSurface(gdContextPtr context, gdSurfacePtr surface, + double x, double y); +GD_VECTOR2D_INTERNAL gdPathPtr gdPathStrokeToPath(const gdPathPtr path, const gdStrokePtr stroke, + const gdPathMatrixPtr matrix); + +#endif diff --git a/ext/gd/libgd/gd_version.c b/ext/gd/libgd/gd_version.c new file mode 100644 index 000000000000..1573a47dff8b --- /dev/null +++ b/ext/gd/libgd/gd_version.c @@ -0,0 +1,34 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "gd.h" + +/* These functions return the version information. We use functions + * so that changes in the shared library will automatically be + * reflected in executables using it without recompiling them. */ + +/* + Function: gdMajorVersion +*/ +BGD_DECLARE(int) gdMajorVersion() { return GD_MAJOR_VERSION; } + +/* + Function: gdMinorVersion +*/ +BGD_DECLARE(int) gdMinorVersion() { return GD_MINOR_VERSION; } + +/* + Function: gdReleaseVersion +*/ +BGD_DECLARE(int) gdReleaseVersion() { return GD_RELEASE_VERSION; } + +/* + Function: gdExtraVersion +*/ +BGD_DECLARE(const char *) gdExtraVersion() { return GD_EXTRA_VERSION; } + +/* + Function: gdVersionString +*/ +BGD_DECLARE(const char *) gdVersionString() { return GD_VERSION_STRING; } diff --git a/ext/gd/libgd/gd_wbmp.c b/ext/gd/libgd/gd_wbmp.c index 22d1c4f4c66d..aab120ac7389 100644 --- a/ext/gd/libgd/gd_wbmp.c +++ b/ext/gd/libgd/gd_wbmp.c @@ -1,227 +1,288 @@ /* - WBMP: Wireless Bitmap Type 0: B/W, Uncompressed Bitmap - Specification of the WBMP format can be found in the file: - SPEC-WAESpec-19990524.pdf - You can download the WAP specification on: http://www.wapforum.com/ - - gd_wbmp.c - - Copyright (C) Johan Van den Brande (johan@vandenbrande.com) - - Fixed: gdImageWBMPPtr, gdImageWBMP - - Recoded: gdImageWBMPCtx for use with my wbmp library - (wbmp library included, but you can find the latest distribution - at http://www.vandenbrande.com/wbmp) - - Implemented: gdImageCreateFromWBMPCtx, gdImageCreateFromWBMP - - --------------------------------------------------------------------------- - - Parts of this code are from Maurice Smurlo. - - - ** Copyright (C) Maurice Szmurlo --- T-SIT --- January 2000 - ** (Maurice.Szmurlo@info.unicaen.fr) - - ** Permission to use, copy, modify, and distribute this software and its - ** documentation for any purpose and without fee is hereby granted, provided - ** that the above copyright notice appear in all copies and that both that - ** copyright notice and this permission notice appear in supporting - ** documentation. This software is provided "as is" without express or - ** implied warranty. - - --------------------------------------------------------------------------- - Parts od this code are inspired by 'pbmtowbmp.c' and 'wbmptopbm.c' by - Terje Sannum . - ** - ** Permission to use, copy, modify, and distribute this software and its - ** documentation for any purpose and without fee is hereby granted, provided - ** that the above copyright notice appear in all copies and that both that - ** copyright notice and this permission notice appear in supporting - ** documentation. This software is provided "as is" without express or - ** implied warranty. - ** - --------------------------------------------------------------------------- - - Todo: - - gdCreateFromWBMP function for reading WBMP files + * WBMP: Wireless Bitmap Type 0: B/W, Uncompressed Bitmap + * Specification of the WBMP format can be found in the file: + * SPEC-WAESpec-19990524.pdf + * You can download the WAP specification on: http://www.wapforum.com/ + * + * gd_wbmp.c + * + * Copyright (C) Johan Van den Brande (johan@vandenbrande.com) + * + * Fixed: gdImageWBMPPtr, gdImageWBMP + * + * Recoded: gdImageWBMPCtx for use with my wbmp library + * (wbmp library included, but you can find the latest distribution + * at http://www.vandenbrande.com/wbmp) + * + * Implemented: gdImageCreateFromWBMPCtx, gdImageCreateFromWBMP + * + *-------------------------------------------------------------------------- + * + * Parts of this code are from Maurice Smurlo. + * + ** Copyright (C) Maurice Szmurlo --- T-SIT --- January 2000 + ** (Maurice.Szmurlo@info.unicaen.fr) + ** + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. + * + *-------------------------------------------------------------------------- + * + * Parts of this code are inspired by 'pbmtowbmp.c' and 'wbmptopbm.c' by + * Terje Sannum . + * + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. + * + *-------------------------------------------------------------------------- + * + * Todo: + * + * gdCreateFromWBMP function for reading WBMP files + * + *-------------------------------------------------------------------------- + */ - ---------------------------------------------------------------------------- +/** + * File: WBMP IO + * + * Read and write WBMP images. */ -#include -#include -#include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include "gd.h" -#include "gdfonts.h" #include "gd_errors.h" -#include "wbmp.h" +#include "gdfonts.h" +#include +#include +#include +#include "wbmp.h" /* gd_putout - ** --------- - ** Wrapper around gdPutC for use with writewbmp - ** + * --------- + * Wrapper around gdPutC for use with writewbmp */ -void gd_putout (int i, void *out) -{ - gdPutC(i, (gdIOCtx *) out); -} - +static void gd_putout(int i, void *out) { gdPutC(i, (gdIOCtx *)out); } /* gd_getin - ** -------- - ** Wrapper around gdGetC for use with readwbmp - ** + * -------- + * Wrapper around gdGetC for use with readwbmp */ -int gd_getin (void *in) -{ - return (gdGetC((gdIOCtx *) in)); -} +static int gd_getin(void *in) { return (gdGetC((gdIOCtx *)in)); } static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out); -/* gdImageWBMPCtx - ** -------------- - ** Write the image as a wbmp file - ** Parameters are: - ** image: gd image structure; - ** fg: the index of the foreground color. any other value will be - ** considered as background and will not be written - ** out: the stream where to write +/* + Function: gdImageWBMPCtx + + Write the image as a wbmp file + + Parameters: + image - gd image structure + fg - the index of the foreground color. any other value will be + considered as background and will not be written + out - the stream where to write */ -void gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out) +BGD_DECLARE(void) gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) { - _gdImageWBMPCtx(image, fg, out); + _gdImageWBMPCtx(image, fg, out); } /* returns 0 on success, 1 on failure */ static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) { - int x, y, pos; - Wbmp *wbmp; - - /* create the WBMP */ - if ((wbmp = createwbmp (gdImageSX (image), gdImageSY (image), WBMP_WHITE)) == NULL) { - gd_error("Could not create WBMP"); - return 1; - } - - /* fill up the WBMP structure */ - pos = 0; - for (y = 0; y < gdImageSY(image); y++) { - for (x = 0; x < gdImageSX(image); x++) { - if (gdImageGetPixel (image, x, y) == fg) { - wbmp->bitmap[pos] = WBMP_BLACK; - } - pos++; - } - } - - /* write the WBMP to a gd file descriptor */ - if (writewbmp (wbmp, &gd_putout, out)) { - freewbmp(wbmp); - gd_error("Could not save WBMP"); - return 1; - } - - /* des submitted this bugfix: gdFree the memory. */ - freewbmp(wbmp); - - return 0; + int x, y, pos; + Wbmp *wbmp; + + /* create the WBMP */ + if ((wbmp = createwbmp(gdImageSX(image), gdImageSY(image), WBMP_WHITE)) == NULL) { + gd_error("Could not create WBMP\n"); + return 1; + } + + /* fill up the WBMP structure */ + pos = 0; + for (y = 0; y < gdImageSY(image); y++) { + for (x = 0; x < gdImageSX(image); x++) { + if (gdImageGetPixel(image, x, y) == fg) { + wbmp->bitmap[pos] = WBMP_BLACK; + } + pos++; + } + } + + /* write the WBMP to a gd file descriptor */ + if (writewbmp(wbmp, &gd_putout, out)) { + freewbmp(wbmp); + gd_error("Could not save WBMP\n"); + return 1; + } + + /* des submitted this bugfix: gdFree the memory. */ + freewbmp(wbmp); + + return 0; } -/* gdImageCreateFromWBMPCtx - ** ------------------------ - ** Create a gdImage from a WBMP file input from an gdIOCtx +/* + Function: gdImageCreateFromWBMPCtx + + Reads in a WBMP image via a struct. See + . */ -gdImagePtr gdImageCreateFromWBMPCtx (gdIOCtx * infile) +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMPCtx(gdIOCtx *infile) { - /* FILE *wbmp_file; */ - Wbmp *wbmp; - gdImagePtr im = NULL; - int black, white; - int col, row, pos; - - if (readwbmp (&gd_getin, infile, &wbmp)) { - return NULL; - } - - if (!(im = gdImageCreate (wbmp->width, wbmp->height))) { - freewbmp (wbmp); - return NULL; - } - - /* create the background color */ - white = gdImageColorAllocate(im, 255, 255, 255); - /* create foreground color */ - black = gdImageColorAllocate(im, 0, 0, 0); - - /* fill in image (in a wbmp 1 = white/ 0 = black) */ - pos = 0; - for (row = 0; row < wbmp->height; row++) { - for (col = 0; col < wbmp->width; col++) { - if (wbmp->bitmap[pos++] == WBMP_WHITE) { - gdImageSetPixel(im, col, row, white); - } else { - gdImageSetPixel(im, col, row, black); - } - } - } - - freewbmp(wbmp); - - return im; + Wbmp *wbmp; + gdImagePtr im = NULL; + int black, white; + int col, row, pos; + + if (readwbmp(&gd_getin, infile, &wbmp)) { + return (NULL); + } + + if (!(im = gdImageCreate(wbmp->width, wbmp->height))) { + freewbmp(wbmp); + return NULL; + } + + /* create the background color */ + white = gdImageColorAllocate(im, 255, 255, 255); + /* create foreground color */ + black = gdImageColorAllocate(im, 0, 0, 0); + + /* fill in image (in a wbmp 1 = white/ 0 = black) */ + pos = 0; + for (row = 0; row < wbmp->height; row++) { + for (col = 0; col < wbmp->width; col++) { + if (wbmp->bitmap[pos++] == WBMP_WHITE) { + gdImageSetPixel(im, col, row, white); + } else { + gdImageSetPixel(im, col, row, black); + } + } + } + + freewbmp(wbmp); + + return im; } -/* gdImageCreateFromWBMP - ** --------------------- +/* + Function: gdImageCreateFromWBMP + + is called to load images from WBMP format + files. Invoke with an already opened + pointer to a file containing the desired + image. returns a gdImagePtr to the new + image, or NULL if unable to load the image (most often because the + file is corrupt or does not contain a WBMP + image). does not close the file. You can + inspect the sx and sy members of the image to determine its + size. The image must eventually be destroyed using + . + + Variants: + + creates an image from WBMP data (i.e. the + contents of a WBMP file) already in memory. + + reads in an image using the functions in + a struct. + + Parameters: + + infile - The input FILE pointer + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + (start code) + + gdImagePtr im; + FILE *in; + in = fopen("mywbmp.wbmp", "rb"); + im = gdImageCreateFromWBMP(in); + fclose(in); + // ... Use the image ... + gdImageDestroy(im); + + (end code) */ -gdImagePtr gdImageCreateFromWBMP (FILE * inFile) -{ - gdImagePtr im; - gdIOCtx *in = gdNewFileCtx(inFile); - im = gdImageCreateFromWBMPCtx(in); - in->gd_free(in); - return im; +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMP(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (in == NULL) + return NULL; + im = gdImageCreateFromWBMPCtx(in); + in->gd_free(in); + return im; } -gdImagePtr gdImageCreateFromWBMPPtr (int size, void *data) +/* + Function: gdImageCreateFromWBMPPtr + + Parameters: + + size - size of WBMP data in bytes. + data - WBMP data (i.e. contents of a WBMP file). + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWBMPPtr(int size, void *data) { - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - im = gdImageCreateFromWBMPCtx(in); - in->gd_free(in); - return im; + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) { + return 0; + } + im = gdImageCreateFromWBMPCtx(in); + in->gd_free(in); + return im; } -/* gdImageWBMP - ** ----------- +/* + Function: gdImageWBMP */ -void gdImageWBMP (gdImagePtr im, int fg, FILE * outFile) +BGD_DECLARE(void) gdImageWBMP(gdImagePtr im, int fg, FILE *outFile) { - gdIOCtx *out = gdNewFileCtx(outFile); - gdImageWBMPCtx(im, fg, out); - out->gd_free(out); + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) + return; + gdImageWBMPCtx(im, fg, out); + out->gd_free(out); } -/* gdImageWBMPPtr - ** -------------- +/* + Function: gdImageWBMPPtr */ -void * gdImageWBMPPtr (gdImagePtr im, int *size, int fg) +BGD_DECLARE(void *) gdImageWBMPPtr(gdImagePtr im, int *size, int fg) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - if (!_gdImageWBMPCtx(im, fg, out)) { - rv = gdDPExtractData(out, size); - } else { - rv = NULL; - } - out->gd_free(out); - - return rv; + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) + return NULL; + if (!_gdImageWBMPCtx(im, fg, out)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } + out->gd_free(out); + return rv; } diff --git a/ext/gd/libgd/gd_webp.c b/ext/gd/libgd/gd_webp.c index 6277e09710f9..2045cbe84afb 100644 --- a/ext/gd/libgd/gd_webp.c +++ b/ext/gd/libgd/gd_webp.c @@ -1,207 +1,1322 @@ -#include -#include -#include -#include +/** + * File: WebP IO + * + * Read and write WebP images. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + #include "gd.h" #include "gd_errors.h" +#include "gd_intern.h" #include "gdhelpers.h" +#include +#include +#include +#include #ifdef HAVE_LIBWEBP #include "webp/decode.h" +#include "webp/demux.h" #include "webp/encode.h" +#include "webp/mux.h" + +#define GD_WEBP_ALLOC_STEP (4 * 1024) + +struct gdWebpReadStruct { + uint8_t *data; + size_t size; + WebPDemuxer *demux; + WebPAnimDecoder *decoder; + WebPIterator iter; + int haveIter; + int rawIndex; + int imageIndex; + int rawTimestamp; + int imageTimestamp; + gdImagePtr rawFrame; + gdImagePtr canvas; +}; + +struct gdWebpWriteStruct { + gdIOCtxPtr out; + int ownsCtx; + int memoryWriter; + WebPAnimEncoder *encoder; + gdWebpWriteOptions options; + int canvasWidth; + int canvasHeight; + int timestamp; + int frameCount; + int finalized; +}; + +static uint8_t *WebpReadCtxData(gdIOCtx *infile, size_t *size) +{ + uint8_t *filedata = NULL, *temp, *read; + ssize_t n; + + *size = 0; + do { + temp = gdRealloc(filedata, *size + GD_WEBP_ALLOC_STEP); + if (temp == NULL) { + gdFree(filedata); + gd_error("WebP decode: realloc failed"); + return NULL; + } + filedata = temp; + read = temp + *size; + n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile); + if (n > 0 && n != EOF) { + *size += n; + } + } while (n > 0 && n != EOF); + + if (*size == 0) { + gdFree(filedata); + return NULL; + } + + return filedata; +} + +static int WebpGdAlphaToWebp(int alpha) +{ + if (alpha == gdAlphaTransparent) { + return 0; + } + return 255 - ((alpha << 1) + (alpha >> 6)); +} + +static int WebpWebpAlphaToGd(uint8_t alpha) { return gdAlphaMax - (alpha >> 1); } + +static gdImagePtr WebpImageFromRGBA(const uint8_t *rgba, int width, int height) +{ + gdImagePtr im; + const uint8_t *p; + int x, y; + + if (rgba == NULL || width <= 0 || height <= 0) { + return NULL; + } + im = gdImageCreateTrueColor(width, height); + if (im == NULL) { + return NULL; + } + gdImageAlphaBlending(im, 0); + gdImageSaveAlpha(im, 1); + for (y = 0, p = rgba; y < height; y++) { + for (x = 0; x < width; x++) { + uint8_t r = *(p++); + uint8_t g = *(p++); + uint8_t b = *(p++); + uint8_t a = *(p++); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, WebpWebpAlphaToGd(a)); + } + } + return im; +} + +static gdImagePtr WebpImageFromARGB(const uint8_t *argb, int width, int height) +{ + gdImagePtr im; + const uint8_t *p; + int x, y; + + if (argb == NULL || width <= 0 || height <= 0) { + return NULL; + } + im = gdImageCreateTrueColor(width, height); + if (im == NULL) { + return NULL; + } + gdImageAlphaBlending(im, 0); + gdImageSaveAlpha(im, 1); + for (y = 0, p = argb; y < height; y++) { + for (x = 0; x < width; x++) { + uint8_t a = *(p++); + uint8_t r = *(p++); + uint8_t g = *(p++); + uint8_t b = *(p++); + im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, WebpWebpAlphaToGd(a)); + } + } + return im; +} + +static gdImagePtr WebpCloneImage(gdImagePtr src) +{ + gdImagePtr dst; + int x, y; + + if (src == NULL) { + return NULL; + } + dst = gdImageCreateTrueColor(gdImageSX(src), gdImageSY(src)); + if (dst == NULL) { + return NULL; + } + gdImageAlphaBlending(dst, 0); + gdImageSaveAlpha(dst, src->saveAlphaFlag); + for (y = 0; y < gdImageSY(src); y++) { + for (x = 0; x < gdImageSX(src); x++) { + dst->tpixels[y][x] = gdImageGetPixel(src, x, y); + } + } + return dst; +} + +static void WebpFillInfo(const WebPDemuxer *demux, gdWebpInfo *info) +{ + if (info == NULL) { + return; + } + info->width = (int)WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); + info->height = (int)WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); + info->frameCount = (int)WebPDemuxGetI(demux, WEBP_FF_FRAME_COUNT); + info->loopCount = (int)WebPDemuxGetI(demux, WEBP_FF_LOOP_COUNT); + info->backgroundColor = (int)WebPDemuxGetI(demux, WEBP_FF_BACKGROUND_COLOR); + info->formatFlags = (int)WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); +} + +static void WebpFillFrameInfo(const WebPIterator *iter, int frameIndex, int timestamp, + gdWebpFrameInfo *info) +{ + if (info == NULL || iter == NULL) { + return; + } + info->frameIndex = frameIndex; + info->x = iter->x_offset; + info->y = iter->y_offset; + info->width = iter->width; + info->height = iter->height; + info->duration = iter->duration; + info->timestamp = timestamp; + info->dispose = iter->dispose_method; + info->blend = iter->blend_method; + info->hasAlpha = iter->has_alpha; + info->complete = iter->complete; +} + +static int WebpProbeData(const uint8_t *data, size_t size) +{ + WebPData webpData; + WebPDemuxer *demux; + uint32_t flags; + int frameCount; + + if (data == NULL || size == 0) { + return -1; + } + webpData.bytes = data; + webpData.size = size; + demux = WebPDemux(&webpData); + if (demux == NULL) { + return -1; + } + flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + frameCount = (int)WebPDemuxGetI(demux, WEBP_FF_FRAME_COUNT); + WebPDemuxDelete(demux); + return ((flags & ANIMATION_FLAG) != 0 || frameCount > 1) ? 1 : 0; +} + +static gdImagePtr WebpDecodeFirstImage(const uint8_t *filedata, size_t size) +{ + WebPData webpData; + WebPAnimDecoderOptions options; + WebPAnimDecoder *decoder; + uint8_t *rgba = NULL; + int timestamp = 0; + gdImagePtr im; + + if (!WebPAnimDecoderOptionsInit(&options)) { + return NULL; + } + options.color_mode = MODE_RGBA; + webpData.bytes = filedata; + webpData.size = size; + decoder = WebPAnimDecoderNew(&webpData, &options); + if (decoder == NULL) { + return NULL; + } + if (!WebPAnimDecoderGetNext(decoder, &rgba, ×tamp)) { + WebPAnimDecoderDelete(decoder); + return NULL; + } + { + WebPAnimInfo info; + if (!WebPAnimDecoderGetInfo(decoder, &info)) { + WebPAnimDecoderDelete(decoder); + return NULL; + } + im = WebpImageFromRGBA(rgba, (int)info.canvas_width, (int)info.canvas_height); + } + WebPAnimDecoderDelete(decoder); + return im; +} + +/* + Function: gdImageCreateFromWebp + + is called to load truecolor images from + WebP format files. Invoke with an + already opened pointer to a file containing the desired + image. returns a to the new + truecolor image, or NULL if unable to load the image (most often + because the file is corrupt or does not contain a WebP + image). does not close the file. + + You can inspect the sx and sy members of the image to determine + its size. The image must eventually be destroyed using + . + + *The returned image is always a truecolor image.* + + Variants: + + creates an image from WebP data + already in memory. + + reads its data via the function + pointers in a structure. + + Parameters: + + infile - The input FILE pointer. + + Returns: + + A pointer to the new *truecolor* image. This will need to be + destroyed with once it is no longer needed. + + On error, returns NULL. +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebp(FILE *inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx(inFile); + if (!in) { + return 0; + } + im = gdImageCreateFromWebpCtx(in); + in->gd_free(in); + + return im; +} + +/* + Function: gdImageCreateFromWebpPtr + + See . + + Parameters: -#define GD_WEBP_ALLOC_STEP (4*1024) - -gdImagePtr gdImageCreateFromWebp (FILE * inFile) -{ - gdImagePtr im; - gdIOCtx *in = gdNewFileCtx(inFile); - if (!in) - return 0; - im = gdImageCreateFromWebpCtx(in); - in->gd_free(in); - - return im; -} - - -gdImagePtr gdImageCreateFromWebpPtr (int size, void *data) -{ - gdImagePtr im; - gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); - if (!in) - return 0; - im = gdImageCreateFromWebpCtx(in); - in->gd_free(in); - return im; -} - -gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile) -{ - int width, height; - uint8_t *filedata = NULL; - uint8_t *argb = NULL; - size_t size = 0, n; - gdImagePtr im; - int x, y; - uint8_t *p; - - do { - unsigned char *read, *temp; - - temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP); - if (temp) { - filedata = temp; - read = temp + size; - } else { - if (filedata) { - gdFree(filedata); - } - gd_error("WebP decode: realloc failed"); - return NULL; - } - - n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile); - if (n>0 && n!=EOF) { - size += n; - } - } while (n>0 && n!=EOF); - - if (WebPGetInfo(filedata,size, &width, &height) == 0) { - gd_error("gd-webp cannot get webp info"); - gdFree(filedata); - return NULL; - } - - im = gdImageCreateTrueColor(width, height); - if (!im) { - gdFree(filedata); - return NULL; - } - argb = WebPDecodeARGB(filedata, size, &width, &height); - if (!argb) { - gd_error("gd-webp cannot allocate temporary buffer"); - gdFree(filedata); - gdImageDestroy(im); - return NULL; - } - for (y = 0, p = argb; y < height; y++) { - for (x = 0; x < width; x++) { - register uint8_t a = gdAlphaMax - (*(p++) >> 1); - register uint8_t r = *(p++); - register uint8_t g = *(p++); - register uint8_t b = *(p++); - im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a); - } - } - gdFree(filedata); - /* do not use gdFree here, in case gdFree/alloc is mapped to something else than libc */ - free(argb); - im->saveAlphaFlag = 1; - return im; -} - -void gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality) -{ - uint8_t *argb; - int x, y; - uint8_t *p; - uint8_t *out; - size_t out_size; - - if (im == NULL) { - return; - } - - if (!gdImageTrueColor(im)) { - gd_error("Palette image not supported by webp"); - return; - } - - if (quality == -1) { - quality = 80; - } - - if (overflow2(gdImageSX(im), 4)) { - return; - } - - if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) { - return; - } - - argb = (uint8_t *)gdMalloc(gdImageSX(im) * 4 * gdImageSY(im)); - if (!argb) { - return; - } - p = argb; - for (y = 0; y < gdImageSY(im); y++) { - for (x = 0; x < gdImageSX(im); x++) { - register int c; - register char a; - c = im->tpixels[y][x]; - a = gdTrueColorGetAlpha(c); - if (a == 127) { - a = 0; - } else { - a = 255 - ((a << 1) + (a >> 6)); - } - *(p++) = gdTrueColorGetRed(c); - *(p++) = gdTrueColorGetGreen(c); - *(p++) = gdTrueColorGetBlue(c); - *(p++) = a; - } - } - - if (quality >= gdWebpLossless) { - out_size = WebPEncodeLosslessRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, &out); - } else { - out_size = WebPEncodeRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, quality, &out); - } - - if (out_size == 0) { - gd_error("gd-webp encoding failed"); - goto freeargb; - } - gdPutBuf(out, out_size, outfile); - free(out); + size - size of WebP data in bytes. + data - pointer to WebP data. +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpPtr(int size, void *data) +{ + gdImagePtr im; + gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0); + if (!in) + return 0; + im = gdImageCreateFromWebpCtx(in); + in->gd_free(in); + return im; +} + +/* + Function: gdImageCreateFromWebpCtx + + See . +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpCtx(gdIOCtx *infile) +{ + int width, height; + uint8_t *filedata = NULL; + uint8_t *argb = NULL; + size_t size = 0; + gdImagePtr im; + + filedata = WebpReadCtxData(infile, &size); + if (filedata == NULL) { + gd_error("gd-webp cannot get webp info"); + return NULL; + } + + if (WebPGetInfo(filedata, size, &width, &height) == 0) { + im = WebpDecodeFirstImage(filedata, size); + if (im == NULL) { + gd_error("gd-webp cannot get webp info"); + } + gdFree(filedata); + return im; + } + argb = WebPDecodeARGB(filedata, size, &width, &height); + if (!argb) { + im = WebpDecodeFirstImage(filedata, size); + if (im == NULL) { + gd_error("gd-webp cannot allocate temporary buffer"); + } + gdFree(filedata); + return im; + } + im = WebpImageFromARGB(argb, width, height); + /* do not use gdFree here, in case gdFree/alloc is mapped to something else + * than libc */ + WebPFree(argb); + gdFree(filedata); + return im; +} + +BGD_DECLARE(int) gdWebpIsAnimated(FILE *fdFile) +{ + gdIOCtx *fd; + uint8_t *data; + size_t size; + int result, pos; + + if (fdFile == NULL) { + return -1; + } + fd = gdNewFileCtx(fdFile); + if (fd == NULL) { + return -1; + } + pos = (int)gdTell(fd); + if (pos < 0) { + fd->gd_free(fd); + return -1; + } + data = WebpReadCtxData(fd, &size); + result = WebpProbeData(data, size); + gdFree(data); + if (!gdSeek(fd, pos)) { + result = -1; + } + fd->gd_free(fd); + return result; +} + +BGD_DECLARE(int) gdWebpIsAnimatedCtx(gdIOCtxPtr in) +{ + uint8_t *data; + size_t size; + int result, pos; + + if (in == NULL || in->tell == NULL || in->seek == NULL) { + return -1; + } + pos = (int)gdTell(in); + if (pos < 0) { + return -1; + } + data = WebpReadCtxData(in, &size); + result = WebpProbeData(data, size); + gdFree(data); + if (!gdSeek(in, pos)) { + return -1; + } + return result; +} + +BGD_DECLARE(int) gdWebpIsAnimatedPtr(int size, void *data) +{ + if (size <= 0 || data == NULL) { + return -1; + } + return WebpProbeData((const uint8_t *)data, (size_t)size); +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpen(FILE *fdFile) +{ + gdIOCtx *fd; + gdWebpReadPtr webp; + + if (fdFile == NULL) { + return NULL; + } + fd = gdNewFileCtx(fdFile); + if (fd == NULL) { + return NULL; + } + webp = gdWebpReadOpenCtx(fd); + fd->gd_free(fd); + return webp; +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenPtr(int size, void *data) +{ + gdIOCtx *in; + gdWebpReadPtr webp; + + if (size <= 0 || data == NULL) { + return NULL; + } + in = gdNewDynamicCtxEx(size, data, 0); + if (in == NULL) { + return NULL; + } + webp = gdWebpReadOpenCtx(in); + in->gd_free(in); + return webp; +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenCtx(gdIOCtxPtr in) +{ + gdWebpReadPtr webp; + WebPData webpData; + WebPAnimDecoderOptions decOptions; + + if (in == NULL) { + return NULL; + } + webp = (gdWebpReadPtr)gdCalloc(1, sizeof(struct gdWebpReadStruct)); + if (webp == NULL) { + return NULL; + } + webp->data = WebpReadCtxData(in, &webp->size); + if (webp->data == NULL) { + gdFree(webp); + return NULL; + } + webpData.bytes = webp->data; + webpData.size = webp->size; + webp->demux = WebPDemux(&webpData); + if (webp->demux == NULL || !WebPAnimDecoderOptionsInit(&decOptions)) { + gdWebpReadClose(webp); + return NULL; + } + decOptions.color_mode = MODE_RGBA; + webp->decoder = WebPAnimDecoderNew(&webpData, &decOptions); + if (webp->decoder == NULL) { + gdWebpReadClose(webp); + return NULL; + } + return webp; +} + +BGD_DECLARE(void) gdWebpReadClose(gdWebpReadPtr webp) +{ + if (webp == NULL) { + return; + } + if (webp->haveIter) { + WebPDemuxReleaseIterator(&webp->iter); + } + if (webp->decoder != NULL) { + WebPAnimDecoderDelete(webp->decoder); + } + if (webp->demux != NULL) { + WebPDemuxDelete(webp->demux); + } + if (webp->rawFrame != NULL) { + gdImageDestroy(webp->rawFrame); + } + if (webp->canvas != NULL) { + gdImageDestroy(webp->canvas); + } + gdFree(webp->data); + gdFree(webp); +} + +BGD_DECLARE(int) gdWebpReadGetInfo(gdWebpReadPtr webp, gdWebpInfo *info) +{ + if (webp == NULL || info == NULL || webp->demux == NULL) { + return 0; + } + WebpFillInfo(webp->demux, info); + return 1; +} + +BGD_DECLARE(int) +gdWebpReadNextFrame(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *frame) +{ + uint8_t *rgba; + int width, height; + + if (frame != NULL) { + *frame = NULL; + } + if (webp == NULL || webp->demux == NULL) { + return -1; + } + if (webp->haveIter) { + if (!WebPDemuxNextFrame(&webp->iter)) { + return 0; + } + } else { + if (!WebPDemuxGetFrame(webp->demux, 1, &webp->iter)) { + return 0; + } + webp->haveIter = 1; + } + if (webp->rawFrame != NULL) { + gdImageDestroy(webp->rawFrame); + webp->rawFrame = NULL; + } + rgba = WebPDecodeRGBA(webp->iter.fragment.bytes, webp->iter.fragment.size, &width, &height); + if (rgba == NULL) { + return -1; + } + webp->rawFrame = WebpImageFromRGBA(rgba, width, height); + WebPFree(rgba); + if (webp->rawFrame == NULL) { + return -1; + } + WebpFillFrameInfo(&webp->iter, webp->rawIndex, webp->rawTimestamp, info); + webp->rawTimestamp += webp->iter.duration; + webp->rawIndex++; + if (frame != NULL) { + *frame = webp->rawFrame; + } + return 1; +} + +BGD_DECLARE(int) +gdWebpReadNextImage(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *image) +{ + uint8_t *rgba; + int timestamp, duration; + WebPIterator iter; + int haveFrameInfo = 0; + WebPAnimInfo animInfo; + + if (image != NULL) { + *image = NULL; + } + if (webp == NULL || webp->decoder == NULL) { + return -1; + } + if (!WebPAnimDecoderGetNext(webp->decoder, &rgba, ×tamp)) { + return 0; + } + if (!WebPAnimDecoderGetInfo(webp->decoder, &animInfo)) { + return -1; + } + duration = timestamp - webp->imageTimestamp; + if (duration < 0) { + duration = 0; + } + if (WebPDemuxGetFrame(webp->demux, webp->imageIndex + 1, &iter)) { + haveFrameInfo = 1; + WebpFillFrameInfo(&iter, webp->imageIndex, webp->imageTimestamp, info); + if (info != NULL) { + info->duration = duration; + } + WebPDemuxReleaseIterator(&iter); + } else if (info != NULL) { + memset(info, 0, sizeof(*info)); + info->frameIndex = webp->imageIndex; + info->width = (int)animInfo.canvas_width; + info->height = (int)animInfo.canvas_height; + info->duration = duration; + info->timestamp = webp->imageTimestamp; + } + if (webp->canvas != NULL) { + gdImageDestroy(webp->canvas); + webp->canvas = NULL; + } + webp->canvas = WebpImageFromRGBA(rgba, (int)animInfo.canvas_width, (int)animInfo.canvas_height); + if (webp->canvas == NULL) { + return -1; + } + if (!haveFrameInfo && info != NULL) { + info->complete = 1; + } + webp->imageTimestamp = timestamp; + webp->imageIndex++; + if (image != NULL) { + *image = webp->canvas; + } + return 1; +} + +BGD_DECLARE(gdImagePtr) gdWebpReadCloneImage(gdWebpReadPtr webp) +{ + if (webp == NULL || webp->canvas == NULL) { + return NULL; + } + return WebpCloneImage(webp->canvas); +} + +/* returns 0 on success, 1 on failure */ +static int _gdImageWebpCtx(gdImagePtr im, gdIOCtx *outfile, int quality) +{ + uint8_t *argb; + int x, y; + uint8_t *p; + uint8_t *out; + size_t out_size; + size_t ret = 0; + + if (im == NULL) { + return 1; + } + + if (!gdImageTrueColor(im)) { + gd_error("Palette image not supported by webp"); + return 1; + } + + if (quality == -1) { + quality = 80; + } + + if (overflow2(gdImageSX(im), 4)) { + return 1; + } + + if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) { + return 1; + } + + argb = (uint8_t *)gdMalloc(gdImageSX(im) * 4 * gdImageSY(im)); + if (!argb) { + return 1; + } + p = argb; + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + register int c; + register char a; + c = im->tpixels[y][x]; + a = gdTrueColorGetAlpha(c); + if (a == 127) { + a = 0; + } else { + a = 255 - ((a << 1) + (a >> 6)); + } + *(p++) = gdTrueColorGetRed(c); + *(p++) = gdTrueColorGetGreen(c); + *(p++) = gdTrueColorGetBlue(c); + *(p++) = a; + } + } + if (quality >= gdWebpLossless) { + out_size = + WebPEncodeLosslessRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, &out); + } else { + out_size = + WebPEncodeRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, quality, &out); + } + if (out_size == 0) { + gd_error("gd-webp encoding failed"); + ret = 1; + goto freeargb; + } + + int res = gdPutBuf(out, out_size, outfile); + WebPFree(out); + if (res < 0 || (size_t)res != out_size) { + gd_error("gd-webp write error\n"); + ret = 1; + } freeargb: - gdFree(argb); + gdFree(argb); + + return ret; } -void gdImageWebpEx (gdImagePtr im, FILE * outFile, int quality) +/* + Function: gdImageWebpCtx + + Write the image as WebP data via a . See + for more details. + + Parameters: + + im - The image to write. + outfile - The output sink. + quality - Image quality. + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) gdImageWebpCtx(gdImagePtr im, gdIOCtx *outfile, int quality) { - gdIOCtx *out = gdNewFileCtx(outFile); - gdImageWebpCtx(im, out, quality); - out->gd_free(out); + _gdImageWebpCtx(im, outfile, quality); } -void gdImageWebp (gdImagePtr im, FILE * outFile) +/* + Function: gdImageWebpEx + + outputs the specified image to the specified file in + WebP format. The file must be open for writing. Under MSDOS and + all versions of Windows, it is important to use "wb" as opposed to + simply "w" as the mode when opening the file, and under Unix there + is no penalty for doing so. does not close the file; + your code must do so. + + If _quality_ is -1, a reasonable quality value (which should yield a + good general quality / size tradeoff for most situations) is used. Otherwise + _quality_ should be a value in the range 0-100, higher quality values + usually implying both higher quality and larger image sizes. + + If _quality_ is greater than or equal to then the image + will be written in the lossless WebP format. + + Variants: + + stores the image using a struct. + + stores the image to RAM. + + Parameters: + + im - The image to save. + outFile - The FILE pointer to write to. + quality - Compression quality (0-100). + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) gdImageWebpEx(gdImagePtr im, FILE *outFile, int quality) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageWebpCtx(im, out, quality); + out->gd_free(out); +} + +/* + Function: gdImageWebp + + Variant of which uses the default quality (-1). + + Parameters: + + im - The image to save + outFile - The FILE pointer to write to. + + Returns: + + Nothing. +*/ +BGD_DECLARE(void) gdImageWebp(gdImagePtr im, FILE *outFile) +{ + gdIOCtx *out = gdNewFileCtx(outFile); + if (out == NULL) { + return; + } + _gdImageWebpCtx(im, out, -1); + out->gd_free(out); +} + +/* + Function: gdImageWebpPtr + + See . +*/ +BGD_DECLARE(void *) gdImageWebpPtr(gdImagePtr im, int *size) { - gdIOCtx *out = gdNewFileCtx(outFile); - gdImageWebpCtx(im, out, -1); - out->gd_free(out); + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageWebpCtx(im, out, -1)) { + rv = NULL; + } else { + rv = gdDPExtractData(out, size); + } + out->gd_free(out); + + return rv; } -void * gdImageWebpPtr (gdImagePtr im, int *size) +/* + Function: gdImageWebpPtrEx + + See . +*/ +BGD_DECLARE(void *) gdImageWebpPtrEx(gdImagePtr im, int *size, int quality) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - gdImageWebpCtx(im, out, -1); - rv = gdDPExtractData(out, size); - out->gd_free(out); + void *rv; - return rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + if (_gdImageWebpCtx(im, out, quality)) { + rv = NULL; + } else { + rv = gdDPExtractData(out, size); + } + out->gd_free(out); + return rv; } -void * gdImageWebpPtrEx (gdImagePtr im, int *size, int quality) +static void WebpWriteFree(gdWebpWritePtr webp) { - void *rv; - gdIOCtx *out = gdNewDynamicCtx(2048, NULL); - gdImageWebpCtx(im, out, quality); - rv = gdDPExtractData(out, size); - out->gd_free(out); - return rv; + if (webp == NULL) { + return; + } + if (webp->encoder != NULL) { + WebPAnimEncoderDelete(webp->encoder); + } + if (webp->ownsCtx && webp->out != NULL) { + webp->out->gd_free(webp->out); + } + gdFree(webp); } + +static int WebpWriteEnsureEncoder(gdWebpWritePtr webp, gdImagePtr image) +{ + WebPAnimEncoderOptions encOptions; + + if (webp->encoder != NULL) { + return 1; + } + if (image == NULL) { + return 0; + } + webp->canvasWidth = + webp->options.canvasWidth > 0 ? webp->options.canvasWidth : gdImageSX(image); + webp->canvasHeight = + webp->options.canvasHeight > 0 ? webp->options.canvasHeight : gdImageSY(image); + if (webp->canvasWidth <= 0 || webp->canvasHeight <= 0) { + return 0; + } + if (!WebPAnimEncoderOptionsInit(&encOptions)) { + return 0; + } + encOptions.anim_params.loop_count = webp->options.loopCount; + encOptions.anim_params.bgcolor = (uint32_t)webp->options.backgroundColor; + if (webp->options.minimizeSize) { + encOptions.minimize_size = webp->options.minimizeSize; + } + if (webp->options.kmin || webp->options.kmax) { + encOptions.kmin = webp->options.kmin; + encOptions.kmax = webp->options.kmax; + } + if (webp->options.allowMixed) { + encOptions.allow_mixed = webp->options.allowMixed; + } + webp->encoder = WebPAnimEncoderNew(webp->canvasWidth, webp->canvasHeight, &encOptions); + return webp->encoder != NULL; +} + +static int WebpImageToRGBA(gdImagePtr im, uint8_t **rgba) +{ + uint8_t *p; + int x, y; + + *rgba = NULL; + if (im == NULL || !gdImageTrueColor(im)) { + gd_error("Palette image not supported by webp"); + return 0; + } + if (overflow2(gdImageSX(im), 4) || overflow2(gdImageSX(im) * 4, gdImageSY(im))) { + return 0; + } + *rgba = (uint8_t *)gdMalloc(gdImageSX(im) * 4 * gdImageSY(im)); + if (*rgba == NULL) { + return 0; + } + p = *rgba; + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + int c = im->tpixels[y][x]; + *(p++) = gdTrueColorGetRed(c); + *(p++) = gdTrueColorGetGreen(c); + *(p++) = gdTrueColorGetBlue(c); + *(p++) = WebpGdAlphaToWebp(gdTrueColorGetAlpha(c)); + } + } + return 1; +} + +static int WebpWriteAssemble(gdWebpWritePtr webp) +{ + WebPData webpData; + int ok = 0; + + if (webp == NULL || webp->out == NULL || webp->finalized) { + return 0; + } + if (webp->encoder == NULL || webp->frameCount == 0) { + gd_error("gd-webp animation has no frames"); + return 0; + } + WebPDataInit(&webpData); + if (!WebPAnimEncoderAdd(webp->encoder, NULL, webp->timestamp, NULL)) { + gd_error("gd-webp animation flush failed: %s", WebPAnimEncoderGetError(webp->encoder)); + goto done; + } + if (!WebPAnimEncoderAssemble(webp->encoder, &webpData)) { + gd_error("gd-webp animation assembly failed: %s", WebPAnimEncoderGetError(webp->encoder)); + goto done; + } + if ((size_t)gdPutBuf(webpData.bytes, webpData.size, webp->out) != webpData.size) { + gd_error("gd-webp animation write error"); + goto done; + } + webp->finalized = 1; + ok = 1; +done: + WebPDataClear(&webpData); + return ok; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpen(FILE *outFile, const gdWebpWriteOptions *options) +{ + gdIOCtx *out; + gdWebpWritePtr webp; + + if (outFile == NULL) { + return NULL; + } + out = gdNewFileCtx(outFile); + if (out == NULL) { + return NULL; + } + webp = gdWebpWriteOpenCtx(out, options); + if (webp == NULL) { + out->gd_free(out); + return NULL; + } + webp->ownsCtx = 1; + return webp; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenCtx(gdIOCtxPtr out, const gdWebpWriteOptions *options) +{ + gdWebpWritePtr webp; + + if (out == NULL) { + return NULL; + } + webp = (gdWebpWritePtr)gdCalloc(1, sizeof(struct gdWebpWriteStruct)); + if (webp == NULL) { + return NULL; + } + webp->out = out; + webp->ownsCtx = 0; + if (options != NULL) { + webp->options = *options; + } + if (webp->options.quality == 0) { + webp->options.quality = -1; + } + return webp; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenPtr(const gdWebpWriteOptions *options) +{ + gdIOCtx *out; + gdWebpWritePtr webp; + + out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) { + return NULL; + } + webp = gdWebpWriteOpenCtx(out, options); + if (webp == NULL) { + out->gd_free(out); + return NULL; + } + webp->ownsCtx = 1; + webp->memoryWriter = 1; + return webp; +} + +BGD_DECLARE(int) +gdWebpWriteAddImage(gdWebpWritePtr webp, gdImagePtr image, int durationMs) +{ + WebPConfig config; + WebPPicture picture; + uint8_t *rgba = NULL; + int ok = 0; + + if (webp == NULL || image == NULL || durationMs < 0 || webp->finalized) { + return 0; + } + if (!WebpWriteEnsureEncoder(webp, image)) { + return 0; + } + if (gdImageSX(image) != webp->canvasWidth || gdImageSY(image) != webp->canvasHeight) { + gd_error("gd-webp animation frames must match canvas size"); + return 0; + } + if (!WebpImageToRGBA(image, &rgba)) { + return 0; + } + if (!WebPConfigInit(&config) || !WebPPictureInit(&picture)) { + goto done; + } + config.quality = webp->options.quality == -1 ? 80.0f : (float)webp->options.quality; + if (webp->options.lossless || webp->options.quality >= gdWebpLossless) { + config.lossless = 1; + if (config.quality > 100.0f) { + config.quality = 100.0f; + } + } + if (webp->options.method >= 0) { + config.method = webp->options.method; + } + if (!WebPValidateConfig(&config)) { + gd_error("gd-webp invalid animation encoder configuration"); + goto done; + } + picture.width = gdImageSX(image); + picture.height = gdImageSY(image); + picture.use_argb = 1; + if (!WebPPictureImportRGBA(&picture, rgba, gdImageSX(image) * 4)) { + goto free_picture; + } + if (!WebPAnimEncoderAdd(webp->encoder, &picture, webp->timestamp, &config)) { + gd_error("gd-webp animation add frame failed: %s", WebPAnimEncoderGetError(webp->encoder)); + goto free_picture; + } + webp->timestamp += durationMs; + webp->frameCount++; + ok = 1; +free_picture: + WebPPictureFree(&picture); +done: + gdFree(rgba); + return ok; +} + +BGD_DECLARE(void) gdWebpWriteClose(gdWebpWritePtr webp) +{ + if (webp == NULL) { + return; + } + if (!webp->memoryWriter) { + WebpWriteAssemble(webp); + } + WebpWriteFree(webp); +} + +BGD_DECLARE(void *) gdWebpWritePtrFinish(gdWebpWritePtr webp, int *size) +{ + void *rv = NULL; + + if (size != NULL) { + *size = 0; + } + if (webp == NULL || !webp->memoryWriter) { + WebpWriteFree(webp); + return NULL; + } + if (WebpWriteAssemble(webp)) { + rv = gdDPExtractData(webp->out, size); + } + WebpWriteFree(webp); + return rv; +} + +#else /* !HAVE_LIBWEBP */ + +static void _noWebpError(void) { gd_error("WEBP image support has been disabled\n"); } + +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebp(FILE *inFile) +{ + ARG_NOT_USED(inFile); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpCtx(gdIOCtx *infile) +{ + ARG_NOT_USED(infile); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(void) gdImageWebpCtx(gdImagePtr im, gdIOCtx *outfile, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outfile); + ARG_NOT_USED(quality); + _noWebpError(); +} + +BGD_DECLARE(void) gdImageWebpEx(gdImagePtr im, FILE *outFile, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + ARG_NOT_USED(quality); + _noWebpError(); +} + +BGD_DECLARE(void) gdImageWebp(gdImagePtr im, FILE *outFile) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(outFile); + _noWebpError(); +} + +BGD_DECLARE(void *) gdImageWebpPtr(gdImagePtr im, int *size) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(void *) gdImageWebpPtrEx(gdImagePtr im, int *size, int quality) +{ + ARG_NOT_USED(im); + ARG_NOT_USED(size); + ARG_NOT_USED(quality); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(int) gdWebpIsAnimated(FILE *fd) +{ + ARG_NOT_USED(fd); + _noWebpError(); + return -1; +} + +BGD_DECLARE(int) gdWebpIsAnimatedCtx(gdIOCtxPtr in) +{ + ARG_NOT_USED(in); + _noWebpError(); + return -1; +} + +BGD_DECLARE(int) gdWebpIsAnimatedPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noWebpError(); + return -1; +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpen(FILE *fd) +{ + ARG_NOT_USED(fd); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenCtx(gdIOCtxPtr in) +{ + ARG_NOT_USED(in); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdWebpReadPtr) gdWebpReadOpenPtr(int size, void *data) +{ + ARG_NOT_USED(size); + ARG_NOT_USED(data); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(void) gdWebpReadClose(gdWebpReadPtr webp) +{ + ARG_NOT_USED(webp); + _noWebpError(); +} + +BGD_DECLARE(int) gdWebpReadGetInfo(gdWebpReadPtr webp, gdWebpInfo *info) +{ + ARG_NOT_USED(webp); + ARG_NOT_USED(info); + _noWebpError(); + return 0; +} + +BGD_DECLARE(int) +gdWebpReadNextFrame(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *frame) +{ + ARG_NOT_USED(webp); + ARG_NOT_USED(info); + ARG_NOT_USED(frame); + _noWebpError(); + return -1; +} + +BGD_DECLARE(int) +gdWebpReadNextImage(gdWebpReadPtr webp, gdWebpFrameInfo *info, gdImagePtr *image) +{ + ARG_NOT_USED(webp); + ARG_NOT_USED(info); + ARG_NOT_USED(image); + _noWebpError(); + return -1; +} + +BGD_DECLARE(gdImagePtr) gdWebpReadCloneImage(gdWebpReadPtr webp) +{ + ARG_NOT_USED(webp); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpen(FILE *outFile, const gdWebpWriteOptions *options) +{ + ARG_NOT_USED(outFile); + ARG_NOT_USED(options); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenCtx(gdIOCtxPtr out, const gdWebpWriteOptions *options) +{ + ARG_NOT_USED(out); + ARG_NOT_USED(options); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(gdWebpWritePtr) +gdWebpWriteOpenPtr(const gdWebpWriteOptions *options) +{ + ARG_NOT_USED(options); + _noWebpError(); + return NULL; +} + +BGD_DECLARE(int) +gdWebpWriteAddImage(gdWebpWritePtr webp, gdImagePtr image, int durationMs) +{ + ARG_NOT_USED(webp); + ARG_NOT_USED(image); + ARG_NOT_USED(durationMs); + _noWebpError(); + return 0; +} + +BGD_DECLARE(void) gdWebpWriteClose(gdWebpWritePtr webp) +{ + ARG_NOT_USED(webp); + _noWebpError(); +} + +BGD_DECLARE(void *) gdWebpWritePtrFinish(gdWebpWritePtr webp, int *size) +{ + ARG_NOT_USED(webp); + ARG_NOT_USED(size); + _noWebpError(); + return NULL; +} + #endif /* HAVE_LIBWEBP */ diff --git a/ext/gd/libgd/gd_xbm.c b/ext/gd/libgd/gd_xbm.c index bd81b8685b20..3e8a75a18054 100644 --- a/ext/gd/libgd/gd_xbm.c +++ b/ext/gd/libgd/gd_xbm.c @@ -1,233 +1,303 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Author: Marcus Boerger | - +----------------------------------------------------------------------+ +/** + * File: XBM IO + * + * Read and write XBM images. */ /* $Id$ */ -#include -#include -#include -#include #include "gd.h" -#include "gdhelpers.h" #include "gd_errors.h" - +#include "gd_intern.h" +#include "gdhelpers.h" +#include +#include +#include +#include +#include +#include #include "php.h" - #define MAX_XBM_LINE_SIZE 255 -/* {{{ gdImagePtr gdImageCreateFromXbm */ -gdImagePtr gdImageCreateFromXbm(FILE * fd) +/* + Function: gdImageCreateFromXbm + + is called to load images from X bitmap + format files. Invoke with an already opened + pointer to a file containing the desired + image. returns a to the new + image, or NULL if unable to load the image (most often because the + file is corrupt or does not contain an X bitmap format + image). does not close the file. + + You can inspect the sx and sy members of the image to determine + its size. The image must eventually be destroyed using + . + + X11 X bitmaps (which define a char[]) as well as X10 X bitmaps (which define + a short[]) are supported. + + Parameters: + + fd - The input FILE pointer + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + (start code) + + gdImagePtr im; + FILE *in; + in = fopen("myxbm.xbm", "rb"); + im = gdImageCreateFromXbm(in); + fclose(in); + // ... Use the image ... + gdImageDestroy(im); + + (end code) +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromXbm(FILE *fd) { - char fline[MAX_XBM_LINE_SIZE]; - char iname[MAX_XBM_LINE_SIZE]; - char *type; - int value; - unsigned int width = 0, height = 0; - int fail = 0; - int max_bit = 0; - - gdImagePtr im; - int bytes = 0, i; - int bit, x = 0, y = 0; - int ch; - char h[8]; - unsigned int b; - - rewind(fd); - while (fgets(fline, MAX_XBM_LINE_SIZE, fd)) { - fline[MAX_XBM_LINE_SIZE-1] = '\0'; - if (strlen(fline) == MAX_XBM_LINE_SIZE-1) { - return 0; - } - if (sscanf(fline, "#define %s %d", iname, &value) == 2) { - if (!(type = strrchr(iname, '_'))) { - type = iname; - } else { - type++; - } - - if (!strcmp("width", type)) { - width = (unsigned int) value; - } - if (!strcmp("height", type)) { - height = (unsigned int) value; - } - } else { - if ( sscanf(fline, "static unsigned char %s = {", iname) == 1 - || sscanf(fline, "static char %s = {", iname) == 1) - { - max_bit = 128; - } else if (sscanf(fline, "static unsigned short %s = {", iname) == 1 - || sscanf(fline, "static short %s = {", iname) == 1) - { - max_bit = 32768; - } - if (max_bit) { - bytes = (width + 7) / 8 * height; - if (!bytes) { - return 0; - } - if (!(type = strrchr(iname, '_'))) { - type = iname; - } else { - type++; - } - if (!strcmp("bits[]", type)) { - break; - } - } - } - } - if (!bytes || !max_bit) { - return 0; - } - - if(!(im = gdImageCreate(width, height))) { - return 0; - } - gdImageColorAllocate(im, 255, 255, 255); - gdImageColorAllocate(im, 0, 0, 0); - h[2] = '\0'; - h[4] = '\0'; - for (i = 0; i < bytes; i++) { - while (1) { - if ((ch=getc(fd)) == EOF) { - fail = 1; - break; - } - if (ch == 'x') { - break; - } - } - if (fail) { - break; - } - /* Get hex value */ - if ((ch=getc(fd)) == EOF) { - break; - } - h[0] = ch; - if ((ch=getc(fd)) == EOF) { - break; - } - h[1] = ch; - if (max_bit == 32768) { - if ((ch=getc(fd)) == EOF) { - break; - } - h[2] = ch; - if ((ch=getc(fd)) == EOF) { - break; - } - h[3] = ch; - } - if (sscanf(h, "%x", &b) != 1) { - gd_error("Invalid XBM"); - gdImageDestroy(im); - return 0; - } - for (bit = 1; bit <= max_bit; bit = bit << 1) { - gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0); - if (x == im->sx) { - x = 0; - y++; - if (y == im->sy) { - return im; - } - break; - } - } - } - - gd_error("EOF before image was complete"); - gdImageDestroy(im); - return 0; + char fline[MAX_XBM_LINE_SIZE]; + char iname[MAX_XBM_LINE_SIZE]; + char *type; + int value; + unsigned int width = 0, height = 0; + int fail = 0; + int max_bit = 0; + + gdImagePtr im; + int bytes = 0, i; + int bit, x = 0, y = 0; + int ch; + char h[8]; + unsigned int b; + + rewind(fd); + while (fgets(fline, MAX_XBM_LINE_SIZE, fd)) { + fline[MAX_XBM_LINE_SIZE - 1] = '\0'; + if (strlen(fline) == MAX_XBM_LINE_SIZE - 1) { + return 0; + } + if (sscanf(fline, "#define %s %d", iname, &value) == 2) { + if (!(type = strrchr(iname, '_'))) { + type = iname; + } else { + type++; + } + + if (!strcmp("width", type)) { + width = (unsigned int)value; + } + if (!strcmp("height", type)) { + height = (unsigned int)value; + } + } else { + if (sscanf(fline, "static unsigned char %s = {", iname) == 1 || + sscanf(fline, "static char %s = {", iname) == 1) { + max_bit = 128; + } else if (sscanf(fline, "static unsigned short %s = {", iname) == 1 || + sscanf(fline, "static short %s = {", iname) == 1) { + max_bit = 32768; + } + if (max_bit) { + bytes = (width + 7) / 8 * height; + if (!bytes) { + return 0; + } + if (!(type = strrchr(iname, '_'))) { + type = iname; + } else { + type++; + } + if (!strcmp("bits[]", type)) { + break; + } + } + } + } + if (!bytes || !max_bit) { + return 0; + } + + if (!(im = gdImageCreate(width, height))) { + return 0; + } + gdImageColorAllocate(im, 255, 255, 255); + gdImageColorAllocate(im, 0, 0, 0); + h[2] = '\0'; + h[4] = '\0'; + for (i = 0; i < bytes; i++) { + while (1) { + if ((ch = getc(fd)) == EOF) { + fail = 1; + break; + } + if (ch == 'x') { + break; + } + } + if (fail) { + break; + } + /* Get hex value */ + if ((ch = getc(fd)) == EOF) { + break; + } + h[0] = ch; + if ((ch = getc(fd)) == EOF) { + break; + } + h[1] = ch; + if (max_bit == 32768) { + if ((ch = getc(fd)) == EOF) { + break; + } + h[2] = ch; + if ((ch = getc(fd)) == EOF) { + break; + } + h[3] = ch; + } + if (sscanf(h, "%x", &b) != 1) { + gd_error("Invalid XBM"); + gdImageDestroy(im); + return 0; + } + for (bit = 1; bit <= max_bit; bit = bit << 1) { + gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0); + if (x == im->sx) { + x = 0; + y++; + if (y == im->sy) { + return im; + } + break; + } + } + } + + gd_error("EOF before image was complete"); + gdImageDestroy(im); + return 0; } -/* }}} */ /* {{{ gdCtxPrintf */ -void gdCtxPrintf(gdIOCtx * out, const char *format, ...) +static void gdCtxPrintf(gdIOCtx *out, const char *format, ...) { - char *buf; - int len; - va_list args; - - va_start(args, format); - len = vspprintf(&buf, 0, format, args); - va_end(args); - out->putBuf(out, buf, len); - efree(buf); + char *buf; + int len; + va_list args; + + va_start(args, format); + len = vspprintf(&buf, 0, format, args); + va_end(args); + out->putBuf(out, buf, len); + gdFree(buf); } /* }}} */ -/* {{{ gdImageXbmCtx */ -void gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOCtx * out) +/* The compiler will optimize strlen(constant) to a constant number. */ +#define gdCtxPuts(out, s) out->putBuf(out, s, strlen(s)) + +/** + * Function: gdImageXbmCtx + * + * Writes an image to an IO context in X11 bitmap format. + * + * Parameters: + * + * image - The to write. + * file_name - The prefix of the XBM's identifiers. Illegal characters are + * automatically stripped. + * gd - Which color to use as forground color. All pixels with another + * color are unset. + * out - The to write the image file to. + * + */ +BGD_DECLARE(void) +gdImageXbmCtx(gdImagePtr image, char *file_name, int fg, gdIOCtx *out) { - int x, y, c, b, sx, sy, p; - char *name, *f; - size_t i, l; - - name = file_name; - if ((f = strrchr(name, '/')) != NULL) name = f+1; - if ((f = strrchr(name, '\\')) != NULL) name = f+1; - name = estrdup(name); - if ((f = strrchr(name, '.')) != NULL && !strcasecmp(f, ".XBM")) *f = '\0'; - if ((l = strlen(name)) == 0) { - efree(name); - name = estrdup("image"); - } else { - for (i=0; i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || + (c == '_'))) { + + // Anything else (spaces, punctuation, accents, UTF-8 bytes) + // becomes '_' + name[i] = '_'; + } + } + } + + /* Since "name" comes from the user, run it through a direct puts. + * Trying to printf it into a local buffer means we'd need a large + * or dynamic buffer to hold it all. */ + + /* #define _width 1234 */ + gdCtxPuts(out, "#define "); + gdCtxPuts(out, name); + gdCtxPuts(out, "_width "); + gdCtxPrintf(out, "%d\n", gdImageSX(image)); + + /* #define _height 1234 */ + gdCtxPuts(out, "#define "); + gdCtxPuts(out, name); + gdCtxPuts(out, "_height "); + gdCtxPrintf(out, "%d\n", gdImageSY(image)); + + /* static unsigned char _bits[] = {\n */ + gdCtxPuts(out, "static unsigned char "); + gdCtxPuts(out, name); + gdCtxPuts(out, "_bits[] = {\n "); + + gdFree(name); + + b = 1; + p = 0; + c = 0; + sx = gdImageSX(image); + sy = gdImageSY(image); + for (y = 0; y < sy; y++) { + for (x = 0; x < sx; x++) { + if (gdImageGetPixel(image, x, y) == fg) { + c |= b; + } + if ((b == 128) || (x == sx - 1)) { + b = 1; + if (p) { + gdCtxPuts(out, ", "); + if (!(p % 12)) { + gdCtxPuts(out, "\n "); + p = 12; + } + } + p++; + gdCtxPrintf(out, "0x%02X", c); + c = 0; + } else { + b <<= 1; + } + } + } + gdCtxPuts(out, "};\n"); } -/* }}} */ diff --git a/ext/gd/libgd/gdcache.c b/ext/gd/libgd/gdcache.c index 0d6e65194a8f..85675865e3bd 100644 --- a/ext/gd/libgd/gdcache.c +++ b/ext/gd/libgd/gdcache.c @@ -17,7 +17,9 @@ * John Ellson (ellson@graphviz.org) Oct 31, 1997 * * Test this with: - * gcc -o gdcache -g -Wall -DTEST gdcache.c + * gcc -o gdcache -g -Wall -DTEST -DNEED_CACHE gdcache.c -lgd + * or + * gcc -o gdcache -g -Wall -DTEST -DNEED_CACHE gdcache.c libgd.a * * The cache is implemented by a singly-linked list of elements * each containing a pointer to a user struct that is being managed by @@ -51,159 +53,163 @@ /* implementation */ /*********************************************************/ - /* create a new cache */ -gdCache_head_t * -gdCacheCreate ( - int size, - gdCacheTestFn_t gdCacheTest, - gdCacheFetchFn_t gdCacheFetch, - gdCacheReleaseFn_t gdCacheRelease) +gdCache_head_t *gdCacheCreate(int size, gdCacheTestFn_t gdCacheTest, gdCacheFetchFn_t gdCacheFetch, + gdCacheReleaseFn_t gdCacheRelease) { - gdCache_head_t *head; - - head = (gdCache_head_t *) gdPMalloc(sizeof (gdCache_head_t)); - head->mru = NULL; - head->size = size; - head->gdCacheTest = gdCacheTest; - head->gdCacheFetch = gdCacheFetch; - head->gdCacheRelease = gdCacheRelease; - return head; + gdCache_head_t *head; + + head = (gdCache_head_t *)gdMalloc(sizeof(gdCache_head_t)); + if (!head) { + return NULL; + } + + head->mru = NULL; + head->size = size; + head->gdCacheTest = gdCacheTest; + head->gdCacheFetch = gdCacheFetch; + head->gdCacheRelease = gdCacheRelease; + + return head; } -void -gdCacheDelete (gdCache_head_t * head) +void gdCacheDelete(gdCache_head_t *head) { - gdCache_element_t *elem, *prev; - - elem = head->mru; - while (elem) - { - (*(head->gdCacheRelease)) (elem->userdata); - prev = elem; - elem = elem->next; - gdPFree ((char *) prev); + gdCache_element_t *elem, *prev; + + elem = head->mru; + while (elem) { + (*(head->gdCacheRelease))(elem->userdata); + prev = elem; + elem = elem->next; + gdFree((char *)prev); } - gdPFree ((char *) head); + gdFree((char *)head); } -void * -gdCacheGet (gdCache_head_t * head, void *keydata) +void *gdCacheGet(gdCache_head_t *head, void *keydata) { - int i = 0; - gdCache_element_t *elem, *prev = NULL, *prevprev = NULL; - void *userdata; - - elem = head->mru; - while (elem) - { - if ((*(head->gdCacheTest)) (elem->userdata, keydata)) - { - if (i) - { /* if not already most-recently-used */ - /* relink to top of list */ - prev->next = elem->next; - elem->next = head->mru; - head->mru = elem; - } - return elem->userdata; - } - prevprev = prev; - prev = elem; - elem = elem->next; - i++; - } - userdata = (*(head->gdCacheFetch)) (&(head->error), keydata); - if (!userdata) - { - /* if there was an error in the fetch then don't cache */ - return NULL; + int i = 0; + gdCache_element_t *elem, *prev = NULL, *prevprev = NULL; + void *userdata; + + elem = head->mru; + while (elem) { + if ((*(head->gdCacheTest))(elem->userdata, keydata)) { + if (i) { + /* if not already most-recently-used */ + /* relink to top of list */ + prev->next = elem->next; + elem->next = head->mru; + head->mru = elem; + } + return elem->userdata; + } + prevprev = prev; + prev = elem; + elem = elem->next; + i++; } - if (i < head->size) - { /* cache still growing - add new elem */ - elem = (gdCache_element_t *) gdPMalloc(sizeof (gdCache_element_t)); + userdata = (*(head->gdCacheFetch))(&(head->error), keydata); + if (!userdata) { + /* if there was an error in the fetch then don't cache */ + return NULL; } - else - { /* cache full - replace least-recently-used */ - /* preveprev becomes new end of list */ - prevprev->next = NULL; - elem = prev; - (*(head->gdCacheRelease)) (elem->userdata); + + if (i < head->size) { + /* cache still growing - add new elem */ + elem = (gdCache_element_t *)gdMalloc(sizeof(gdCache_element_t)); + if (!elem) { + (*(head->gdCacheRelease))(userdata); + return NULL; + } + } else { + /* cache full - replace least-recently-used */ + if (!prevprev) { + /* cache size is 1 */ + head->mru = NULL; + } else { + /* prevprev becomes new end of list */ + prevprev->next = NULL; + } + elem = prev; + if (!elem) { + /* Happen only with an invalid cache size (<= 0). Bad state but + * still worth handling it here before deref */ + (*(head->gdCacheRelease))(userdata); + return NULL; + } + (*(head->gdCacheRelease))(elem->userdata); } - /* relink to top of list */ - elem->next = head->mru; - head->mru = elem; - elem->userdata = userdata; - return userdata; -} + /* relink to top of list */ + elem->next = head->mru; + head->mru = elem; + elem->userdata = userdata; + return userdata; +} /*********************************************************/ /* test stub */ /*********************************************************/ - #ifdef TEST #include -typedef struct -{ - int key; - int value; -} -key_value_t; +typedef struct { + int key; + int value; +} key_value_t; -static int -cacheTest (void *map, void *key) -{ - return (((key_value_t *) map)->key == *(int *) key); -} +static int cacheTest(void *map, void *key) { return (((key_value_t *)map)->key == *(int *)key); } -static void * -cacheFetch (char **error, void *key) +static void *cacheFetch(char **error, void *key) { - key_value_t *map; + key_value_t *map; - map = (key_value_t *) gdMalloc (sizeof (key_value_t)); - map->key = *(int *) key; - map->value = 3; + map = (key_value_t *)gdMalloc(sizeof(key_value_t)); + if (!map) { + *error = "gdMalloc failed"; + return NULL; + } + map->key = *(int *)key; + map->value = 3; - *error = NULL; - return (void *) map; -} + *error = NULL; -static void -cacheRelease (void *map) -{ - gdFree ((char *) map); + return (void *)map; } -int -main (char *argv[], int argc) +static void cacheRelease(void *map) { gdFree((char *)map); } + +int main(int argc, char **argv) { - gdCache_head_t *cacheTable; - int elem, key; - - cacheTable = gdCacheCreate (3, cacheTest, cacheFetch, cacheRelease); - - key = 20; - elem = *(int *) gdCacheGet (cacheTable, &key); - key = 30; - elem = *(int *) gdCacheGet (cacheTable, &key); - key = 40; - elem = *(int *) gdCacheGet (cacheTable, &key); - key = 50; - elem = *(int *) gdCacheGet (cacheTable, &key); - key = 30; - elem = *(int *) gdCacheGet (cacheTable, &key); - key = 30; - elem = *(int *) gdCacheGet (cacheTable, &key); - - gdCacheDelete (cacheTable); - - return 0; + gdCache_head_t *cacheTable; + int elem, key; + + cacheTable = gdCacheCreate(3, cacheTest, cacheFetch, cacheRelease); + if (!cacheTable) { + exit(1); + } + + key = 20; + elem = *(int *)gdCacheGet(cacheTable, &key); + key = 30; + elem = *(int *)gdCacheGet(cacheTable, &key); + key = 40; + elem = *(int *)gdCacheGet(cacheTable, &key); + key = 50; + elem = *(int *)gdCacheGet(cacheTable, &key); + key = 30; + elem = *(int *)gdCacheGet(cacheTable, &key); + key = 30; + elem = *(int *)gdCacheGet(cacheTable, &key); + + gdCacheDelete(cacheTable); + + return 0; } #endif /* TEST */ diff --git a/ext/gd/libgd/gdcache.h b/ext/gd/libgd/gdcache.h index dd262e3e98da..4531d6c33e66 100644 --- a/ext/gd/libgd/gdcache.h +++ b/ext/gd/libgd/gdcache.h @@ -1,3 +1,7 @@ +#ifdef __cplusplus +extern "C" { +#endif + /* * gdcache.h * @@ -8,7 +12,9 @@ * John Ellson (ellson@graphviz.org) Oct 31, 1997 * * Test this with: - * gcc -o gdcache -g -Wall -DTEST gdcache.c + * gcc -o gdcache -g -Wall -DTEST -DNEED_CACHE gdcache.c -lgd + * or + * gcc -o gdcache -g -Wall -DTEST -DNEED_CACHE gdcache.c libgd.a * * The cache is implemented by a singly-linked list of elements * each containing a pointer to a user struct that is being managed by @@ -53,31 +59,29 @@ typedef void (*gdCacheReleaseFn_t)(void *userdata); /* element structure */ typedef struct gdCache_element_s gdCache_element_t; struct gdCache_element_s { - gdCache_element_t *next; - void *userdata; + gdCache_element_t *next; + void *userdata; }; /* head structure */ typedef struct gdCache_head_s gdCache_head_t; struct gdCache_head_s { - gdCache_element_t *mru; - int size; - char *error; - gdCacheTestFn_t gdCacheTest; - gdCacheFetchFn_t gdCacheFetch; - gdCacheReleaseFn_t gdCacheRelease; + gdCache_element_t *mru; + int size; + char *error; + gdCacheTestFn_t gdCacheTest; + gdCacheFetchFn_t gdCacheFetch; + gdCacheReleaseFn_t gdCacheRelease; }; /* function templates */ -gdCache_head_t * -gdCacheCreate( - int size, - gdCacheTestFn_t gdCacheTest, - gdCacheFetchFn_t gdCacheFetch, - gdCacheReleaseFn_t gdCacheRelease ); +gdCache_head_t *gdCacheCreate(int size, gdCacheTestFn_t gdCacheTest, gdCacheFetchFn_t gdCacheFetch, + gdCacheReleaseFn_t gdCacheRelease); -void -gdCacheDelete( gdCache_head_t *head ); +void gdCacheDelete(gdCache_head_t *head); -void * -gdCacheGet( gdCache_head_t *head, void *keydata ); +void *gdCacheGet(gdCache_head_t *head, void *keydata); + +#ifdef __cplusplus +} +#endif diff --git a/ext/gd/libgd/gddemo.c b/ext/gd/libgd/gddemo.c index a5ceeb53b86b..b7f7184c88fa 100644 --- a/ext/gd/libgd/gddemo.c +++ b/ext/gd/libgd/gddemo.c @@ -1,118 +1,107 @@ -#include #include "gd.h" #include "gdfontg.h" #include "gdfonts.h" +#include -int -main (void) +int main(void) { - /* Input and output files */ - FILE *in; - FILE *out; + /* Input and output files */ + FILE *in; + FILE *out; - /* Input and output images */ - gdImagePtr im_in = 0, im_out = 0; + /* Input and output images */ + gdImagePtr im_in = 0, im_out = 0; - /* Brush image */ - gdImagePtr brush; + /* Brush image */ + gdImagePtr brush; - /* Color indexes */ - int white; - int blue; - int red; - int green; + /* Color indexes */ + int white; + int blue; + int red; + int green; - /* Points for polygon */ - gdPoint points[3]; + /* Points for polygon */ + gdPoint points[3]; - /* Create output image, 256 by 256 pixels, true color. */ - im_out = gdImageCreateTrueColor (256, 256); - /* First color allocated is background. */ - white = gdImageColorAllocate (im_out, 255, 255, 255); + /* Create output image, 256 by 256 pixels, true color. */ + im_out = gdImageCreateTrueColor(256, 256); + /* First color allocated is background. */ + white = gdImageColorAllocate(im_out, 255, 255, 255); - /* Set transparent color. */ - gdImageColorTransparent (im_out, white); + /* Set transparent color. */ + gdImageColorTransparent(im_out, white); - /* Try to load demoin.png and paste part of it into the - output image. */ - in = fopen ("demoin.png", "rb"); - if (!in) - { - fprintf (stderr, "Can't load source image; this demo\n"); - fprintf (stderr, "is much more impressive if demoin.png\n"); - fprintf (stderr, "is available.\n"); - im_in = 0; - } - else - { - im_in = gdImageCreateFromPng (in); - fclose (in); - /* Now copy, and magnify as we do so */ - gdImageCopyResized (im_out, im_in, - 32, 32, 0, 0, 192, 192, 255, 255); + /* Try to load demoin.png and paste part of it into the + output image. */ + in = fopen("demoin.png", "rb"); + if (!in) { + fprintf(stderr, "Can't load source image; this demo\n"); + fprintf(stderr, "is much more impressive if demoin.png\n"); + fprintf(stderr, "is available.\n"); + im_in = 0; + } else { + im_in = gdImageCreateFromPng(in); + fclose(in); + /* Now copy, and magnify as we do so */ + gdImageCopyResized(im_out, im_in, 32, 32, 0, 0, 192, 192, 255, 255); } - red = gdImageColorAllocate (im_out, 255, 0, 0); - green = gdImageColorAllocate (im_out, 0, 255, 0); - blue = gdImageColorAllocate (im_out, 0, 0, 255); - /* Rectangle */ - gdImageLine (im_out, 16, 16, 240, 16, green); - gdImageLine (im_out, 240, 16, 240, 240, green); - gdImageLine (im_out, 240, 240, 16, 240, green); - gdImageLine (im_out, 16, 240, 16, 16, green); - /* Circle */ - gdImageArc (im_out, 128, 128, 60, 20, 0, 720, blue); - /* Arc */ - gdImageArc (im_out, 128, 128, 40, 40, 90, 270, blue); - /* Flood fill: doesn't do much on a continuously - variable tone jpeg original. */ - gdImageFill (im_out, 8, 8, blue); - /* Polygon */ - points[0].x = 64; - points[0].y = 0; - points[1].x = 0; - points[1].y = 128; - points[2].x = 128; - points[2].y = 128; - gdImageFilledPolygon (im_out, points, 3, green); - /* Brush. A fairly wild example also involving a line style! */ - if (im_in) - { - int style[8]; - brush = gdImageCreateTrueColor (16, 16); - gdImageCopyResized (brush, im_in, - 0, 0, 0, 0, - gdImageSX (brush), gdImageSY (brush), - gdImageSX (im_in), gdImageSY (im_in)); - gdImageSetBrush (im_out, brush); - /* With a style, so they won't overprint each other. - Normally, they would, yielding a fat-brush effect. */ - style[0] = 0; - style[1] = 0; - style[2] = 0; - style[3] = 0; - style[4] = 0; - style[5] = 0; - style[6] = 0; - style[7] = 1; - gdImageSetStyle (im_out, style, 8); - /* Draw the styled, brushed line */ - gdImageLine (im_out, 0, 255, 255, 0, gdStyledBrushed); + red = gdImageColorAllocate(im_out, 255, 0, 0); + green = gdImageColorAllocate(im_out, 0, 255, 0); + blue = gdImageColorAllocate(im_out, 0, 0, 255); + /* Rectangle */ + gdImageLine(im_out, 16, 16, 240, 16, green); + gdImageLine(im_out, 240, 16, 240, 240, green); + gdImageLine(im_out, 240, 240, 16, 240, green); + gdImageLine(im_out, 16, 240, 16, 16, green); + /* Circle */ + gdImageArc(im_out, 128, 128, 60, 20, 0, 720, blue); + /* Arc */ + gdImageArc(im_out, 128, 128, 40, 40, 90, 270, blue); + /* Flood fill: doesn't do much on a continuously + variable tone jpeg original. */ + gdImageFill(im_out, 8, 8, blue); + /* Polygon */ + points[0].x = 64; + points[0].y = 0; + points[1].x = 0; + points[1].y = 128; + points[2].x = 128; + points[2].y = 128; + gdImageFilledPolygon(im_out, points, 3, green); + /* Brush. A fairly wild example also involving a line style! */ + if (im_in) { + int style[8]; + brush = gdImageCreateTrueColor(16, 16); + gdImageCopyResized(brush, im_in, 0, 0, 0, 0, gdImageSX(brush), gdImageSY(brush), + gdImageSX(im_in), gdImageSY(im_in)); + gdImageSetBrush(im_out, brush); + /* With a style, so they won't overprint each other. + Normally, they would, yielding a fat-brush effect. */ + style[0] = 0; + style[1] = 0; + style[2] = 0; + style[3] = 0; + style[4] = 0; + style[5] = 0; + style[6] = 0; + style[7] = 1; + gdImageSetStyle(im_out, style, 8); + /* Draw the styled, brushed line */ + gdImageLine(im_out, 0, 255, 255, 0, gdStyledBrushed); } - /* Text */ - gdImageString (im_out, gdFontGiant, 32, 32, - (unsigned char *) "hi", red); - gdImageStringUp (im_out, gdFontSmall, 64, 64, - (unsigned char *) "hi", red); - /* Make output image interlaced (progressive, in the case of JPEG) */ - gdImageInterlace (im_out, 1); - out = fopen ("demoout.png", "wb"); - /* Write PNG */ - gdImagePng (im_out, out); - fclose (out); - gdImageDestroy (im_out); - if (im_in) - { - gdImageDestroy (im_in); + /* Text */ + gdImageString(im_out, gdFontGiant, 32, 32, (unsigned char *)"hi", red); + gdImageStringUp(im_out, gdFontSmall, 64, 64, (unsigned char *)"hi", red); + /* Make output image interlaced (progressive, in the case of JPEG) */ + gdImageInterlace(im_out, 1); + out = fopen("demoout.png", "wb"); + /* Write PNG */ + gdImagePng(im_out, out); + fclose(out); + gdImageDestroy(im_out); + if (im_in) { + gdImageDestroy(im_in); } - return 0; + return 0; } diff --git a/ext/gd/libgd/gdfontg.c b/ext/gd/libgd/gdfontg.c index edddd0990d8e..c7306fc89f8f 100644 --- a/ext/gd/libgd/gdfontg.c +++ b/ext/gd/libgd/gdfontg.c @@ -1,5 +1,3 @@ - - /* This is a header file for gd font, generated using bdftogd version 0.51 by Jan Pazdziora, adelton@fi.muni.cz @@ -10,4380 +8,35100 @@ "Libor Skarvada, libor@informatics.muni.cz" */ - +/** + * File: Giant Font + * + * A very large ISO-8859-2 raster font (9x15 pixels). + * + * The font is supposed to be used with and + * and their variants. + */ #include "gdfontg.h" -static const char gdFontGiantData[] = -{ -/* Char 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 1, 0, - -/* Char 3 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 4 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 5 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 6 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 7 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 8 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 9 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 10 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 11 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 12 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 13 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 14 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 15 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 16 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 17 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 18 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 19 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 20 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 21 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 22 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 23 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 24 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 25 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 26 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 27 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 28 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 29 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 30 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 31 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 32 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 33 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 34 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 35 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 36 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 37 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 38 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 39 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 40 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 41 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 42 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 43 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 44 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 45 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 46 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 47 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 48 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 49 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 50 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 51 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 52 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 53 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 54 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 55 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 56 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 57 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 58 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 59 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 60 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 61 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 62 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 63 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 64 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 65 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 66 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 67 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 68 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 69 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 70 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 71 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 72 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 73 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 74 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 75 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 76 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 77 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 78 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 79 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 80 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 81 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 82 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 83 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 84 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 85 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 86 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 87 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 88 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 89 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 90 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 91 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 92 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 93 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 94 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 95 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 96 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 97 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 98 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 99 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 100 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 101 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 102 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 103 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - -/* Char 104 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 105 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 106 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, 0, - -/* Char 107 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 108 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 109 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 110 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 111 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 112 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - -/* Char 113 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - -/* Char 114 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 115 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 116 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 117 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 118 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 119 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 120 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 121 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - -/* Char 122 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 123 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 124 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 125 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 126 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 127 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 128 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 129 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 130 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 131 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 132 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 133 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 134 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 135 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 136 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 137 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 138 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 139 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 140 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 141 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 142 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 143 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 144 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 145 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 146 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 147 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 148 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 149 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 150 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 151 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 152 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 153 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 154 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 155 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 156 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 157 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 158 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 159 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 160 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 161 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - -/* Char 162 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 163 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 164 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 165 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 166 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 167 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 168 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 169 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 170 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - -/* Char 171 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 172 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 173 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 174 */ - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 175 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 176 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 177 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, - -/* Char 178 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, - -/* Char 179 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 180 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 181 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 182 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 183 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 184 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - -/* Char 185 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 186 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - -/* Char 187 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 188 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 189 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 190 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 191 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 192 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 193 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 194 */ - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 195 */ - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 196 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 197 */ - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 198 */ - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 199 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - -/* Char 200 */ - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 201 */ - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 202 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - -/* Char 203 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 204 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 205 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 206 */ - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 207 */ - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 208 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 209 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 210 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 211 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 212 */ - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 213 */ - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 214 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 215 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 216 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 217 */ - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 218 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 219 */ - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 220 */ - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 221 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 222 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - -/* Char 223 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 224 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 225 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 226 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 227 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 228 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 229 */ - 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 230 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 231 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - -/* Char 232 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 233 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 234 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - -/* Char 235 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 236 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 237 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 238 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 239 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 240 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 241 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 242 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 243 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 244 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 245 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 246 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 247 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 248 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 249 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 250 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 251 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 252 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 253 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, - -/* Char 254 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, - -/* Char 255 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - +char gdFontGiantData[] = { + /* Char 0 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 1 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 2 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + + /* Char 3 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 4 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 5 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 6 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 7 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 8 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 9 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 10 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 11 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 12 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 13 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 14 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 15 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 16 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 17 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 18 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 19 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 20 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 21 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 22 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 23 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 24 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 25 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 26 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 27 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 28 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 29 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 30 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 31 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 32 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 33 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 34 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 35 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 36 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 37 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 38 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 39 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 40 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 41 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 42 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 43 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 44 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 45 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 46 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 47 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 48 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 49 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 50 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 51 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 52 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 53 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 54 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 55 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 56 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 57 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 58 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 59 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 60 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 61 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 62 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 63 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 64 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 65 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 66 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 67 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 68 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 69 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 70 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 71 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 72 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 73 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 74 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 75 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 76 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 77 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 78 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 79 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 80 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 81 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 82 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 83 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 84 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 85 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 86 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 87 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 88 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 89 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 90 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 91 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 92 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 93 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 94 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 95 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 96 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 97 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 98 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 99 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 100 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 101 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 102 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 103 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 104 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 105 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 106 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 107 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 108 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 109 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 110 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 111 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 112 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 113 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 114 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 115 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 116 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 117 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 118 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 119 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 120 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 121 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 122 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 123 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 124 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 125 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 126 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 127 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 128 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 129 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 130 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 131 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 132 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 133 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 134 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 135 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 136 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 137 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 138 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 139 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 140 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 141 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 142 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 143 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 144 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 145 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 146 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 147 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 148 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 149 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 150 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 151 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 152 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 153 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 154 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 155 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 156 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 157 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 158 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 159 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 160 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 161 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 162 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 163 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 164 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 165 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 166 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 167 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 168 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 169 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 170 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + + /* Char 171 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 172 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 173 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 174 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 175 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 176 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 177 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + + /* Char 178 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + + /* Char 179 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 180 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 181 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 182 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 183 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 184 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 185 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 186 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + + /* Char 187 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 188 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 189 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 190 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 191 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 192 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 193 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 194 */ + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 195 */ + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 196 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 197 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 198 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 199 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 200 */ + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 201 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 202 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + + /* Char 203 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 204 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 205 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 206 */ + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 207 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 208 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 209 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 210 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 211 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 212 */ + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 213 */ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 214 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 215 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 216 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 217 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 218 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 219 */ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 220 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 221 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 222 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 223 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 224 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 225 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 226 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 227 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 228 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 229 */ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 230 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 231 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 232 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 233 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 234 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + + /* Char 235 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 236 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 237 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 238 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 239 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 240 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 241 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 242 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 243 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 244 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 245 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 246 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 247 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 248 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 249 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 250 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 251 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 252 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 253 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 254 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 255 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; -gdFont gdFontGiantRep = -{ - 256, - 0, - 9, - 15, - (char*)gdFontGiantData -}; +gdFont gdFontGiantRep = {256, 0, 9, 15, gdFontGiantData}; -gdFontPtr gdFontGiant = &gdFontGiantRep; +BGD_EXPORT_DATA_PROT gdFontPtr gdFontGiant = &gdFontGiantRep; -gdFontPtr gdFontGetGiant(void) -{ - return gdFontGiant; -} +/** + * Function: gdFontGetGiant + * + * Returns the built-in giant font. + */ +BGD_DECLARE(gdFontPtr) gdFontGetGiant(void) { return gdFontGiant; } /* This file has not been truncated. */ diff --git a/ext/gd/libgd/gdfontg.h b/ext/gd/libgd/gdfontg.h index 8a3e95e8bc82..9b3a3e5213ef 100644 --- a/ext/gd/libgd/gdfontg.h +++ b/ext/gd/libgd/gdfontg.h @@ -1,4 +1,3 @@ - #ifndef _GDFONTG_H_ #define _GDFONTG_H_ 1 @@ -7,24 +6,22 @@ extern "C" { #endif /* - This is a header file for gd font, generated using - bdftogd version 0.51 by Jan Pazdziora, adelton@fi.muni.cz - from bdf font - -Misc-Fixed-Bold-R-Normal-Sans-15-140-75-75-C-90-ISO8859-2 - at Mon Jan 26 14:45:58 1998. - The original bdf was holding following copyright: - "Libor Skarvada, libor@informatics.muni.cz" + This is a header file for gd font, generated using + bdftogd version 0.51 by Jan Pazdziora, adelton@fi.muni.cz + from bdf font + -Misc-Fixed-Bold-R-Normal-Sans-15-140-75-75-C-90-ISO8859-2 + at Mon Jan 26 14:45:58 1998. + The original bdf was holding following copyright: + "Libor Skarvada, libor@informatics.muni.cz" */ - #include "gd.h" -extern gdFontPtr gdFontGiant; -extern gdFontPtr gdFontGetGiant(void); +extern BGD_EXPORT_DATA_PROT gdFontPtr gdFontGiant; +BGD_DECLARE(gdFontPtr) gdFontGetGiant(void); #ifdef __cplusplus } #endif #endif - diff --git a/ext/gd/libgd/gdfontl.c b/ext/gd/libgd/gdfontl.c index 093954ba3c61..758adfb1cdc9 100644 --- a/ext/gd/libgd/gdfontl.c +++ b/ext/gd/libgd/gdfontl.c @@ -11,4636 +11,33309 @@ "Libor Skarvada, libor@informatics.muni.cz" */ +/** + * File: Large Font + * + * A large ISO-8859-2 raster font (8x16 pixels). + * + * The font is supposed to be used with and + * and their variants. + */ #include "gdfontl.h" -static const char gdFontLargeData[] = -{ -/* Char 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 2 */ - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 1, - -/* Char 3 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 4 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 5 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 6 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 7 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 8 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 9 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 0, 0, 0, - 1, 0, 1, 0, 1, 0, 0, 0, - 1, 0, 0, 1, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 10 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 11 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 12 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 13 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 14 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 15 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 16 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 17 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 18 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 19 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 20 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 21 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 22 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 23 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 24 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 25 */ - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 26 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 27 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 28 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 1, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 29 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 30 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 31 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 32 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 33 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 34 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 35 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 36 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 37 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 1, 0, 0, - 1, 0, 0, 1, 0, 1, 0, 0, - 0, 1, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 1, 1, 0, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 38 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 0, - 1, 0, 0, 0, 1, 0, 1, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 39 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 40 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 41 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 42 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 1, 0, 1, 0, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 43 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 44 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 45 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 46 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 47 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 48 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 49 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 50 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 51 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 52 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 53 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 54 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 55 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 56 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 57 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 58 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 59 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 60 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 61 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 62 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 63 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 64 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, 1, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 65 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 66 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 67 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 68 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 69 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 70 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 71 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 72 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 73 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 74 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 75 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 76 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 77 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 1, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 78 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 79 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 80 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 81 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 1, 0, 1, 0, - 0, 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 82 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 83 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 84 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 85 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 86 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 87 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 88 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 89 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 90 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 91 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 92 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 93 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 94 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 95 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 96 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 97 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 98 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 99 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 100 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 101 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 102 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 103 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - -/* Char 104 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 105 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 106 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - -/* Char 107 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 108 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 109 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 1, 1, 0, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 110 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 111 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 112 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - -/* Char 113 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - -/* Char 114 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 115 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 116 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 117 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 118 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 119 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 120 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 121 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - -/* Char 122 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 123 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 124 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 125 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 126 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 0, 0, 1, 0, 0, 1, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 127 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 128 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 129 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 130 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 131 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 132 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 133 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 134 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 135 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 136 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 137 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 138 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 139 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 140 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 141 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 142 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 143 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 144 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 145 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 146 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 147 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 148 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 149 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 150 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 151 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 152 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 153 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 154 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 155 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 156 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 157 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 158 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 159 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 160 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 161 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - -/* Char 162 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 163 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 164 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 165 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 166 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 167 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 168 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 169 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 170 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 171 */ - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 172 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 173 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 174 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 175 */ - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 176 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 177 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - -/* Char 178 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - -/* Char 179 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 180 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 181 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 182 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 183 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 184 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - -/* Char 185 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 186 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 187 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, 1, 0, - 1, 1, 1, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 188 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 189 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 190 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 191 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 192 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 193 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 194 */ - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 195 */ - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 196 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 197 */ - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 198 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 199 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - -/* Char 200 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 201 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 202 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - -/* Char 203 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 204 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 205 */ - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 206 */ - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 207 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 208 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 1, 1, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 209 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 210 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 1, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 211 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 212 */ - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 213 */ - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 214 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 215 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 216 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 217 */ - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 218 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 219 */ - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 220 */ - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 221 */ - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 222 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 223 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 1, 1, 0, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 224 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 225 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 226 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 227 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 228 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 229 */ - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 230 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 231 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - -/* Char 232 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 233 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 234 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - -/* Char 235 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 236 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 237 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 238 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 239 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 1, 0, 1, - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 240 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 241 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 242 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 243 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 244 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 245 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 1, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 246 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 247 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 248 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 249 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 250 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 251 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 1, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 252 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -/* Char 253 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, - -/* Char 254 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, - -/* Char 255 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - +char gdFontLargeData[] = { + /* Char 0 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 1 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 2 */ + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + + /* Char 3 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 4 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 5 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 6 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 7 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 8 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 9 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 10 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 11 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 12 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 13 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 14 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 15 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 16 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 17 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 18 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 19 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 20 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 21 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 22 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 23 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 24 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 25 */ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 26 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 27 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 28 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 29 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 30 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 31 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 32 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 33 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 34 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 35 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 36 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 37 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 38 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 39 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 40 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 41 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 42 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 43 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 44 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 45 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 46 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 47 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 48 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 49 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 50 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 51 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 52 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 53 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 54 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 55 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 56 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 57 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 58 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 59 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 60 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 61 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 62 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 63 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 64 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 65 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 66 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 67 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 68 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 69 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 70 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 71 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 72 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 73 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 74 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 75 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 76 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 77 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 78 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 79 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 80 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 81 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 82 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 83 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 84 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 85 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 86 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 87 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 88 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 89 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 90 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 91 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 92 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 93 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 94 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 95 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 96 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 97 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 98 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 99 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 100 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 101 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 102 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 103 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 104 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 105 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 106 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 107 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 108 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 109 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 110 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 111 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 112 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 113 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + + /* Char 114 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 115 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 116 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 117 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 118 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 119 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 120 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 121 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 122 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 123 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 124 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 125 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 126 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 127 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 128 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 129 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 130 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 131 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 132 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 133 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 134 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 135 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 136 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 137 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 138 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 139 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 140 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 141 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 142 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 143 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 144 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 145 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 146 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 147 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 148 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 149 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 150 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 151 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 152 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 153 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 154 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 155 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 156 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 157 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 158 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 159 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 160 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 161 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 162 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 163 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 164 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 165 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 166 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 167 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 168 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 169 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 170 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 171 */ + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 172 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 173 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 174 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 175 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 176 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 177 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 178 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + + /* Char 179 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 180 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 181 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 182 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 183 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 184 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 185 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 186 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 187 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 188 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 189 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 190 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 191 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 192 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 193 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 194 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 195 */ + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 196 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 197 */ + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 198 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 199 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 200 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 201 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 202 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 203 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 204 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 205 */ + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 206 */ + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 207 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 208 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 209 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 210 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 211 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 212 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 213 */ + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 214 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 215 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 216 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 217 */ + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 218 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 219 */ + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 220 */ + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 221 */ + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 222 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 223 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 224 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 225 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 226 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 227 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 228 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 229 */ + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 230 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 231 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 232 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 233 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 234 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + + /* Char 235 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 236 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 237 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 238 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 239 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 240 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 241 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 242 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 243 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 244 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 245 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 246 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 247 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 248 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 249 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 250 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 251 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 252 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 253 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 254 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + + /* Char 255 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; +gdFont gdFontLargeRep = {256, 0, 8, 16, gdFontLargeData}; -gdFont gdFontLargeRep = -{ - 256, - 0, - 8, - 16, - (char*)gdFontLargeData -}; +BGD_EXPORT_DATA_PROT gdFontPtr gdFontLarge = &gdFontLargeRep; -gdFontPtr gdFontLarge = &gdFontLargeRep; - -gdFontPtr gdFontGetLarge(void) -{ - return gdFontLarge; -} +/** + * Function: gdFontGetLarge + * + * Returns the built-in large font. + */ +BGD_DECLARE(gdFontPtr) +gdFontGetLarge(void) { return gdFontLarge; } /* This file has not been truncated. */ diff --git a/ext/gd/libgd/gdfontl.h b/ext/gd/libgd/gdfontl.h index 92fee14ff334..a1980083e407 100644 --- a/ext/gd/libgd/gdfontl.h +++ b/ext/gd/libgd/gdfontl.h @@ -1,4 +1,3 @@ - #ifndef _GDFONTL_H_ #define _GDFONTL_H_ 1 @@ -7,25 +6,23 @@ extern "C" { #endif /* - This is a header file for gd font, generated using - bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz - from bdf font - -misc-fixed-medium-r-normal--16-140-75-75-c-80-iso8859-2 - at Tue Jan 6 19:39:27 1998. - - The original bdf was holding following copyright: - "Libor Skarvada, libor@informatics.muni.cz" + This is a header file for gd font, generated using + bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz + from bdf font + -misc-fixed-medium-r-normal--16-140-75-75-c-80-iso8859-2 + at Tue Jan 6 19:39:27 1998. + + The original bdf was holding following copyright: + "Libor Skarvada, libor@informatics.muni.cz" */ - #include "gd.h" -extern gdFontPtr gdFontLarge; -extern gdFontPtr gdFontGetLarge(void); +extern BGD_EXPORT_DATA_PROT gdFontPtr gdFontLarge; +BGD_DECLARE(gdFontPtr) gdFontGetLarge(void); #ifdef __cplusplus } #endif #endif - diff --git a/ext/gd/libgd/gdfontmb.c b/ext/gd/libgd/gdfontmb.c index b60110507251..d10ea9e05e5f 100644 --- a/ext/gd/libgd/gdfontmb.c +++ b/ext/gd/libgd/gdfontmb.c @@ -1,5 +1,3 @@ - - /* This is a header file for gd font, generated using bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz @@ -9,3868 +7,23838 @@ No copyright info was found in the original bdf. */ +/** + * File: Medium Bold Font + * + * A medium bold ISO-8859-2 raster font (7x13 pixels). + * + * The font is supposed to be used with and + * and their variants. + */ #include "gdfontmb.h" -static const char gdFontMediumBoldData[] = -{ -/* Char 0 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 1 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 2 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 3 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 4 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 5 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 0, - 0, 0, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 6 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 7 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 8 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 9 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 10 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 11 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 12 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 13 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 14 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 15 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 16 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 17 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 18 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 19 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 20 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 21 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 22 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 23 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 24 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 25 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 26 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 27 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 28 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 29 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 30 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 1, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 31 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 32 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 33 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 34 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 35 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 36 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 0, 1, 1, 0, 1, 0, - 1, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 1, 0, - 1, 0, 1, 1, 0, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 37 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 0, - 1, 0, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 1, 0, 1, 0, - 1, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 38 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 0, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 39 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 40 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 41 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 42 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 43 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 44 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 45 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 46 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 47 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 48 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 49 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 50 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 51 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 52 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 53 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 54 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 55 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 56 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 57 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 58 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 59 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 60 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 61 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 62 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 63 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 64 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 0, 0, 0, 1, 1, 0, - 1, 0, 1, 1, 1, 1, 0, - 1, 0, 1, 0, 1, 1, 0, - 1, 0, 1, 1, 1, 1, 0, - 1, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 65 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 66 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 67 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 68 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 69 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 70 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 71 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 72 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 73 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 74 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 75 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 76 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 77 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 78 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 79 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 80 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 81 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 82 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 83 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 84 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 85 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 86 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 87 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 88 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 89 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 90 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 91 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 92 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 93 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 94 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 95 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 96 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 97 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 98 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 99 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 100 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 101 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 102 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 103 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - -/* Char 104 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 105 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 106 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - -/* Char 107 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 108 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 109 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 110 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 111 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 112 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - -/* Char 113 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - -/* Char 114 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 115 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 116 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 117 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 118 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 119 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 120 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 121 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - -/* Char 122 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 123 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 124 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 125 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 126 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 127 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 128 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 129 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 130 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 131 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 132 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 133 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 134 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 135 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 136 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 137 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 138 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 139 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 140 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 141 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 142 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 143 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 144 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 145 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 146 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 147 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 148 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 149 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 150 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 151 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 152 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 153 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 154 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 155 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 156 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 157 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 158 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 159 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 160 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 161 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - -/* Char 162 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 163 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 164 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 165 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 166 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 167 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 168 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 169 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 170 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - -/* Char 171 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 172 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 173 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 174 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 175 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 176 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 177 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - -/* Char 178 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - -/* Char 179 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 1, 0, - 0, 0, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 180 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 181 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 182 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 183 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 184 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - -/* Char 185 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 186 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - -/* Char 187 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 188 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 189 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 190 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 191 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 192 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 193 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 194 */ - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 195 */ - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 196 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 197 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 198 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 199 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - -/* Char 200 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 201 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 202 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - -/* Char 203 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 204 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 205 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 206 */ - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 207 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 208 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 209 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 210 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 211 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 212 */ - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 213 */ - 0, 1, 1, 0, 0, 1, 1, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 214 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 215 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 216 */ - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 217 */ - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 218 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 219 */ - 0, 1, 1, 0, 0, 1, 1, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 220 */ - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 221 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 222 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - -/* Char 223 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 224 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 225 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 226 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 227 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 228 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 229 */ - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 230 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 231 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - -/* Char 232 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 233 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 234 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 1, 1, 0, 0, - -/* Char 235 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 236 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 237 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 238 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 239 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, - 0, 0, 0, 1, 1, 0, 1, - 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 1, 1, 0, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 240 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 1, 1, 1, 1, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 241 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 242 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 243 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 244 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 245 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 246 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 247 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 248 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 249 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 250 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 251 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 1, 1, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 252 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - -/* Char 253 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 1, 1, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 0, - 0, 1, 1, 1, 1, 0, 0, - -/* Char 254 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - -/* Char 255 */ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - +char gdFontMediumBoldData[] = { + /* Char 0 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 1 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 2 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 3 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 4 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 5 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 6 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 7 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 8 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 9 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 10 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 11 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 12 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 13 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 14 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 15 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 16 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 17 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 18 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 19 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 20 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 21 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 22 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 23 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 24 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 25 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 26 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 27 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 28 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 29 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 30 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 31 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 32 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 33 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 34 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 35 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 36 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 37 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 38 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 39 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 40 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 41 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 42 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 43 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 44 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 45 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 46 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 47 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 48 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 49 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 50 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 51 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 52 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 53 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 54 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 55 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 56 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 57 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 58 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 59 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 60 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 61 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 62 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 63 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 64 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 65 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 66 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 67 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 68 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 69 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 70 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 71 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 72 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 73 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 74 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 75 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 76 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 77 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 78 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 79 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 80 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 81 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 82 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 83 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 84 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 85 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 86 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 87 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 88 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 89 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 90 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 91 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 92 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 93 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 94 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 95 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 96 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 97 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 98 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 99 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 100 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 101 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 102 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 103 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 104 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 105 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 106 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 107 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 108 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 109 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 110 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 111 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 112 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + + /* Char 113 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 114 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 115 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 116 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 117 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 118 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 119 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 120 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 121 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 122 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 123 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 124 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 125 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 126 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 127 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 128 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 129 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 130 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 131 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 132 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 133 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 134 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 135 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 136 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 137 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 138 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 139 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 140 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 141 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 142 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 143 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 144 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 145 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 146 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 147 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 148 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 149 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 150 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 151 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 152 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 153 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 154 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 155 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 156 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 157 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 158 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 159 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 160 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 161 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 162 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 163 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 164 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 165 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 166 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 167 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 168 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 169 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 170 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 171 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 172 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 173 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 174 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 175 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 176 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 177 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 178 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + + /* Char 179 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 180 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 181 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 182 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 183 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 184 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + + /* Char 185 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 186 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 187 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 188 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 189 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 190 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 191 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 192 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 193 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 194 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 195 */ + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 196 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 197 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 198 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 199 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 200 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 201 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 202 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + + /* Char 203 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 204 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 205 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 206 */ + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 207 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 208 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 209 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 210 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 211 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 212 */ + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 213 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 214 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 215 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 216 */ + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 217 */ + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 218 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 219 */ + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 220 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 221 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 222 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + + /* Char 223 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 224 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 225 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 226 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 227 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 228 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 229 */ + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 230 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 231 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 232 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 233 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 234 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + + /* Char 235 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 236 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 237 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 238 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 239 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 240 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 241 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 242 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 243 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 244 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 245 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 246 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 247 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 248 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 249 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 250 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 251 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 252 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + /* Char 253 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + + /* Char 254 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + + /* Char 255 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; -gdFont gdFontMediumBoldRep = -{ - 256, - 0, - 7, - 13, - (char*)gdFontMediumBoldData -}; +gdFont gdFontMediumBoldRep = {256, 0, 7, 13, gdFontMediumBoldData}; -gdFontPtr gdFontMediumBold = &gdFontMediumBoldRep; +BGD_EXPORT_DATA_PROT gdFontPtr gdFontMediumBold = &gdFontMediumBoldRep; -gdFontPtr gdFontGetMediumBold(void) -{ - return gdFontMediumBold; -} +/** + * Function: gdFontGetMediumBold + * + * Returns the built-in medium bold font. + */ +BGD_DECLARE(gdFontPtr) +gdFontGetMediumBold(void) { return gdFontMediumBold; } /* This file has not been truncated. */ diff --git a/ext/gd/libgd/gdfontmb.h b/ext/gd/libgd/gdfontmb.h index 2e2f0cbe4798..6239286cc212 100644 --- a/ext/gd/libgd/gdfontmb.h +++ b/ext/gd/libgd/gdfontmb.h @@ -1,4 +1,3 @@ - #ifndef _GDFONTMB_H_ #define _GDFONTMB_H_ 1 @@ -7,23 +6,21 @@ extern "C" { #endif /* - This is a header file for gd font, generated using - bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz - from bdf font - -misc-fixed-bold-r-normal-sans-13-94-100-100-c-70-iso8859-2 - at Thu Jan 8 13:54:57 1998. - No copyright info was found in the original bdf. + This is a header file for gd font, generated using + bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz + from bdf font + -misc-fixed-bold-r-normal-sans-13-94-100-100-c-70-iso8859-2 + at Thu Jan 8 13:54:57 1998. + No copyright info was found in the original bdf. */ - #include "gd.h" -extern gdFontPtr gdFontMediumBold; -extern gdFontPtr gdFontGetMediumBold(void); +extern BGD_EXPORT_DATA_PROT gdFontPtr gdFontMediumBold; +BGD_DECLARE(gdFontPtr) gdFontGetMediumBold(void); #ifdef __cplusplus } #endif #endif - diff --git a/ext/gd/libgd/gdfonts.c b/ext/gd/libgd/gdfonts.c index bcc0717cafe4..3f23c0d669a0 100644 --- a/ext/gd/libgd/gdfonts.c +++ b/ext/gd/libgd/gdfonts.c @@ -1,5 +1,3 @@ - - /* This is a header file for gd font, generated using bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz @@ -9,11 +7,19 @@ No copyright info was found in the original bdf. */ +/** + * File: Small Font + * + * A small ISO-8859-2 raster font (6x13 pixels). + * + * The font is supposed to be used with and + * and their variants. + */ #include "gdfonts.h" -static const char gdFontSmallData[] = -{ +// clang-format off +char gdFontSmallData[] = { /* Char 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3853,24 +3859,19 @@ static const char gdFontSmallData[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - }; +// clang-format on -gdFont gdFontSmallRep = -{ - 256, - 0, - 6, - 13, - (char*)gdFontSmallData -}; +gdFont gdFontSmallRep = {256, 0, 6, 13, gdFontSmallData}; -gdFontPtr gdFontSmall = &gdFontSmallRep; +BGD_EXPORT_DATA_PROT gdFontPtr gdFontSmall = &gdFontSmallRep; -gdFontPtr gdFontGetSmall(void) -{ - return gdFontSmall; -} +/** + * Function: gdFontGetSmall + * + * Returns the built-in small font. + */ +BGD_DECLARE(gdFontPtr) +gdFontGetSmall(void) { return gdFontSmall; } /* This file has not been truncated. */ diff --git a/ext/gd/libgd/gdfonts.h b/ext/gd/libgd/gdfonts.h index 55d0e1f0b2ad..1f762f7a8dc2 100644 --- a/ext/gd/libgd/gdfonts.h +++ b/ext/gd/libgd/gdfonts.h @@ -1,4 +1,3 @@ - #ifndef _GDFONTS_H_ #define _GDFONTS_H_ 1 @@ -7,23 +6,21 @@ extern "C" { #endif /* - This is a header file for gd font, generated using - bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz - from bdf font - -misc-fixed-medium-r-semicondensed-sans-12-116-75-75-c-60-iso8859-2 - at Thu Jan 8 14:13:20 1998. - No copyright info was found in the original bdf. + This is a header file for gd font, generated using + bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz + from bdf font + -misc-fixed-medium-r-semicondensed-sans-12-116-75-75-c-60-iso8859-2 + at Thu Jan 8 14:13:20 1998. + No copyright info was found in the original bdf. */ - #include "gd.h" -extern gdFontPtr gdFontSmall; -extern gdFontPtr gdFontGetSmall(void); +extern BGD_EXPORT_DATA_PROT gdFontPtr gdFontSmall; +BGD_DECLARE(gdFontPtr) gdFontGetSmall(void); #ifdef __cplusplus } #endif #endif - diff --git a/ext/gd/libgd/gdfontt.c b/ext/gd/libgd/gdfontt.c index d4e0cf9573d2..4580f3488c2d 100644 --- a/ext/gd/libgd/gdfontt.c +++ b/ext/gd/libgd/gdfontt.c @@ -1,5 +1,3 @@ - - /* This is a header file for gd font, generated using bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz @@ -10,11 +8,18 @@ "Libor Skarvada, libor@informatics.muni.cz" */ - +/** + * File: Tiny Font + * + * A very small ISO-8859-2 raster font (5x8 pixels). + * + * The font is supposed to be used with and + * and their variants. + */ #include "gdfontt.h" -static const char gdFontTinyData[] = -{ +// clang-format off +char gdFontTinyData[] = { /* Char 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2574,24 +2579,19 @@ static const char gdFontTinyData[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - }; +// clang-format on -gdFont gdFontTinyRep = -{ - 256, - 0, - 5, - 8, - (char*)gdFontTinyData -}; +gdFont gdFontTinyRep = {256, 0, 5, 8, gdFontTinyData}; -gdFontPtr gdFontTiny = &gdFontTinyRep; +BGD_EXPORT_DATA_PROT gdFontPtr gdFontTiny = &gdFontTinyRep; -gdFontPtr gdFontGetTiny(void) -{ - return gdFontTiny; -} +/** + * Function: gdFontGetTiny + * + * Returns the built-in tiny font. + */ +BGD_DECLARE(gdFontPtr) +gdFontGetTiny(void) { return gdFontTiny; } /* This file has not been truncated. */ diff --git a/ext/gd/libgd/gdfontt.h b/ext/gd/libgd/gdfontt.h index 102fec991b4b..9b9ca2085cf2 100644 --- a/ext/gd/libgd/gdfontt.h +++ b/ext/gd/libgd/gdfontt.h @@ -1,4 +1,3 @@ - #ifndef _GDFONTT_H_ #define _GDFONTT_H_ 1 @@ -7,24 +6,22 @@ extern "C" { #endif /* - This is a header file for gd font, generated using - bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz - from bdf font - -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-2 - at Thu Jan 8 13:49:54 1998. - The original bdf was holding following copyright: - "Libor Skarvada, libor@informatics.muni.cz" + This is a header file for gd font, generated using + bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz + from bdf font + -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-2 + at Thu Jan 8 13:49:54 1998. + The original bdf was holding following copyright: + "Libor Skarvada, libor@informatics.muni.cz" */ - #include "gd.h" -extern gdFontPtr gdFontTiny; -extern gdFontPtr gdFontGetTiny(void); +extern BGD_EXPORT_DATA_PROT gdFontPtr gdFontTiny; +BGD_DECLARE(gdFontPtr) gdFontGetTiny(void); #ifdef __cplusplus } #endif #endif - diff --git a/ext/gd/libgd/gdft.c b/ext/gd/libgd/gdft.c index 34a4064f5fcb..59213c3014b8 100644 --- a/ext/gd/libgd/gdft.c +++ b/ext/gd/libgd/gdft.c @@ -1,69 +1,113 @@ - /********************************************/ /* gd interface to freetype library */ /* */ /* John Ellson ellson@graphviz.org */ /********************************************/ +/** + * File: FreeType font rendering + */ + +#include #include #include #include -#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "gd.h" +#include "gd_intern.h" #include "gdhelpers.h" -#ifndef _WIN32 +/* The bundled PHP font cache can outlive the request which populated it. */ +#define GD_FT_CACHE_MALLOC(size) gdPMalloc(size) +#define GD_FT_CACHE_FREE(ptr) gdPFree(ptr) +#define GD_FT_ACCESS(path, mode) VCWD_ACCESS(path, mode) +#define GD_FT_IS_PATH(path) \ + (IS_ABSOLUTE_PATH(path, strlen(path)) || strchr(path, '/') || strchr(path, '\\')) + +/* 2.0.10: WIN32, not MSWIN32 */ +#if !defined(_WIN32) && !defined(_WIN32_WCE) #include -#else +#elif defined(_WIN32_WCE) +#include /* getenv() */ +#include /* access() */ +#define getenv wceex_getenv +#define access wceex_access +#else /* _WIN32_WCE */ #include #ifndef R_OK -# define R_OK 04 /* Needed in Windows */ -#endif -#endif - -#ifdef _WIN32 -#define access _access -#ifndef R_OK -#define R_OK 2 +#define R_OK 04 /* Needed in Windows */ #endif #endif /* number of antialised colors for indexed bitmaps */ -/* overwrite Windows GDI define in case of windows build */ -#ifdef NUMCOLORS -#undef NUMCOLORS -#endif -#define NUMCOLORS 8 - -char * -gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string) -{ - /* 2.0.6: valid return */ - return gdImageStringFT (im, brect, fg, fontlist, ptsize, angle, x, y, string); -} +#define GD_NUMCOLORS 8 #ifndef HAVE_LIBFREETYPE -char * -gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string, - gdFTStringExtraPtr strex) +BGD_DECLARE(char *) +gdImageStringFTEx(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string, gdFTStringExtraPtr strex) { - return "libgd was not built with FreeType font support\n"; + (void)im; + (void)brect; + (void)fg; + (void)fontlist; + (void)ptsize; + (void)angle; + (void)x; + (void)y; + (void)string; + (void)strex; + + return "libgd was not built with FreeType font support\n"; } -char * -gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string) +BGD_DECLARE(char *) +gdImageStringFT(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string) { - return "libgd was not built with FreeType font support\n"; + (void)im; + (void)brect; + (void)fg; + (void)fontlist; + (void)ptsize; + (void)angle; + (void)x; + (void)y; + (void)string; + + return "libgd was not built with FreeType font support\n"; } #else - #include "gdcache.h" +/* 2.0.16 Christophe Thomas: starting with FreeType 2.1.6, this is + mandatory, and it has been supported for a long while. */ +#ifdef HAVE_FT2BUILD_H #include #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_SIZES_H +#else +#include +#include +#include +#endif + +#ifdef HAVE_LIBFONTCONFIG +static int fontConfigFlag = 0; + +/* translate a fontconfig fontpattern into a fontpath. + return NULL if OK, else return error string */ +static char *font_pattern(char **fontpath, char *fontpattern); +#endif + +#ifdef HAVE_LIBFREETYPE +#include "entities.h" +static char *font_path(char **fontpath, char *name_list); +#endif /* number of fonts cached before least recently used is replaced */ #define FONTCACHESIZE 6 @@ -80,7 +124,8 @@ gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist, /* * The character (space) used to separate alternate fonts in the - * fontlist parameter to gdImageStringFT. 2.0.18: space was a oor choice for this. + * fontlist parameter to gdImageStringFT. 2.0.18: space was a + * poor choice for this. */ #define LISTSEPARATOR ";" @@ -92,69 +137,73 @@ gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist, */ #ifndef DEFAULT_FONTPATH -# if defined(_WIN32) -# define DEFAULT_FONTPATH "C:\\WINDOWS\\FONTS;C:\\WINNT\\FONTS" -# elif defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh)) -# define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts" -# else - /* default fontpath for unix systems - whatever happened to standards ! */ -# define DEFAULT_FONTPATH "/usr/X11R6/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/truetype:/usr/X11R6/lib/X11/fonts/TTF:/usr/share/fonts/TrueType:/usr/share/fonts/truetype:/usr/openwin/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/Type1:/usr/lib/X11/fonts/Type1:/usr/openwin/lib/X11/fonts/Type1" -# endif +#if defined(_WIN32) +#define DEFAULT_FONTPATH "C:\\WINDOWS\\FONTS;C:\\WINNT\\FONTS" +#elif defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh)) +#define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts" +#else +/* default fontpath for unix systems - whatever happened to standards ! */ +#define DEFAULT_FONTPATH \ + "/usr/X11R6/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/truetype:/" \ + "usr/X11R6/lib/X11/fonts/TTF:/usr/share/fonts/TrueType:/usr/share/fonts/" \ + "truetype:/usr/openwin/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/" \ + "Type1:/usr/lib/X11/fonts/Type1:/usr/openwin/lib/X11/fonts/Type1" +#endif #endif #ifndef PATHSEPARATOR -# if defined(_WIN32) -# define PATHSEPARATOR ";" -# else -# define PATHSEPARATOR ":" -# endif +#if defined(_WIN32) +#define PATHSEPARATOR ";" +#else +#define PATHSEPARATOR ":" +#endif #endif - #ifndef TRUE #define FALSE 0 #define TRUE !FALSE #endif -#ifndef MAX -#define MAX(a,b) ((a)>(b)?(a):(b)) -#endif - -#ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -#endif - -typedef struct +/** + * Function: gdImageStringTTF + * + * Alias of . + */ +BGD_DECLARE(char *) +gdImageStringTTF(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string) { - char *fontlist; /* key */ - FT_Library *library; - FT_Face face; - FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis, have_char_map_apple_roman; - gdCache_head_t *glyphCache; + /* 2.0.6: valid return */ + return gdImageStringFT(im, brect, fg, fontlist, ptsize, angle, x, y, string); +} + +typedef struct { + char *fontlist; /* key */ + int flags; /* key */ + char *fontpath; + FT_Library *library; + FT_Face face; } font_t; -typedef struct -{ - char *fontlist; /* key */ - int preferred_map; - FT_Library *library; +typedef struct { + const char *fontlist; /* key */ + int flags; /* key */ + FT_Library *library; } fontkey_t; -typedef struct -{ - int pixel; /* key */ - int bgcolor; /* key */ - int fgcolor; /* key *//* -ve means no antialias */ - gdImagePtr im; /* key */ - int tweencolor; +typedef struct { + int pixel; /* key */ + int bgcolor; /* key */ + int fgcolor; /* key */ /* -ve means no antialias */ + gdImagePtr im; /* key */ + int tweencolor; } tweencolor_t; -typedef struct -{ - int pixel; /* key */ - int bgcolor; /* key */ - int fgcolor; /* key *//* -ve means no antialias */ - gdImagePtr im; /* key */ +typedef struct { + int pixel; /* key */ + int bgcolor; /* key */ + int fgcolor; /* key */ /* -ve means no antialias */ + gdImagePtr im; /* key */ } tweencolorkey_t; /******************************************************************** @@ -203,7 +252,14 @@ typedef struct #include "jisx0208.h" #endif -extern int any2eucjp (char *, char *, unsigned int); +static int comp_entities(const void *e1, const void *e2) +{ + struct entities_s *en1 = (struct entities_s *)e1; + struct entities_s *en2 = (struct entities_s *)e2; + return strcmp(en1->name, en2->name); +} + +extern int any2eucjp(char *, const char *, unsigned int); /* Persistent font cache until explicitly cleared */ /* Fonts can be used across multiple images */ @@ -215,1026 +271,1624 @@ static FT_Library library; #define Tcl_UniChar int #define TCL_UTF_MAX 3 -static int gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr) +static int gdTcl_UtfToUniChar(const char *str, Tcl_UniChar *chPtr) /* str is the UTF8 next character pointer */ /* chPtr is the int for the result */ { - int byte; - - /* HTML4.0 entities in decimal form, e.g. Å */ - byte = *((unsigned char *) str); - if (byte == '&') { - int i, n = 0; - - byte = *((unsigned char *) (str + 1)); - if (byte == '#') { - byte = *((unsigned char *) (str + 2)); - if (byte == 'x' || byte == 'X') { - for (i = 3; i < 8; i++) { - byte = *((unsigned char *) (str + i)); - if (byte >= 'A' && byte <= 'F') - byte = byte - 'A' + 10; - else if (byte >= 'a' && byte <= 'f') - byte = byte - 'a' + 10; - else if (byte >= '0' && byte <= '9') - byte = byte - '0'; - else - break; - n = (n * 16) + byte; - } - } else { - for (i = 2; i < 8; i++) { - byte = *((unsigned char *) (str + i)); - if (byte >= '0' && byte <= '9') { - n = (n * 10) + (byte - '0'); - } else { - break; - } - } - } - if (byte == ';') { - *chPtr = (Tcl_UniChar) n; - return ++i; - } - } - } - - /* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */ - - byte = *((unsigned char *) str); + int byte; + char entity_name_buf[ENTITY_NAME_LENGTH_MAX + 1]; + char *p; + struct entities_s key, *res; + + /* HTML4.0 entities in decimal form, e.g. Å */ + /* or in hexadecimal form, e.g. 水 */ + byte = *((unsigned char *)str); + if (byte == '&') { + int i, n = 0; + + byte = *((unsigned char *)(str + 1)); + if (byte == '#') { + byte = *((unsigned char *)(str + 2)); + if (byte == 'x' || byte == 'X') { + for (i = 3; i < 8; i++) { + byte = *((unsigned char *)(str + i)); + if (byte >= 'A' && byte <= 'F') + byte = byte - 'A' + 10; + else if (byte >= 'a' && byte <= 'f') + byte = byte - 'a' + 10; + else if (byte >= '0' && byte <= '9') + byte = byte - '0'; + else + break; + n = (n * 16) + byte; + } + } else { + for (i = 2; i < 8; i++) { + byte = *((unsigned char *)(str + i)); + if (byte >= '0' && byte <= '9') { + n = (n * 10) + (byte - '0'); + } else { + break; + } + } + } + if (byte == ';') { + *chPtr = (Tcl_UniChar)n; + return ++i; + } + } else { + key.name = p = entity_name_buf; + for (i = 1; i <= 1 + ENTITY_NAME_LENGTH_MAX; i++) { + byte = *((unsigned char *)(str + i)); + if (byte == '\0') + break; + if (byte == ';') { + *p++ = '\0'; + res = bsearch(&key, entities, NR_OF_ENTITIES, sizeof(entities[0]), + *comp_entities); + if (res) { + *chPtr = (Tcl_UniChar)res->value; + return ++i; + } + break; + } + *p++ = byte; + } + } + } + + /* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */ + byte = *((unsigned char *)str); #ifdef JISX0208 - if (0xA1 <= byte && byte <= 0xFE) { - int ku, ten; - - ku = (byte & 0x7F) - 0x20; - ten = (str[1] & 0x7F) - 0x20; - if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) { - *chPtr = (Tcl_UniChar) byte; - return 1; - } - - *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1]; - return 2; - } else + if (0xA1 <= byte && byte <= 0xFE) { + int ku, ten; + + ku = (byte & 0x7F) - 0x20; + ten = (str[1] & 0x7F) - 0x20; + if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) { + *chPtr = (Tcl_UniChar)byte; + return 1; + } + + *chPtr = (Tcl_UniChar)UnicodeTbl[ku - 1][ten - 1]; + return 2; + } else #endif /* JISX0208 */ - if (byte < 0xC0) { - /* Handles properly formed UTF-8 characters between - * 0x01 and 0x7F. Also treats \0 and naked trail - * bytes 0x80 to 0xBF as valid characters representing - * themselves. - */ - - *chPtr = (Tcl_UniChar) byte; - return 1; - } else if (byte < 0xE0) { - if ((str[1] & 0xC0) == 0x80) { - /* Two-byte-character lead-byte followed by a trail-byte. */ - - *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F)); - return 2; - } - /* - * A two-byte-character lead-byte not followed by trail-byte - * represents itself. - */ - - *chPtr = (Tcl_UniChar) byte; - return 1; - } else if (byte < 0xF0) { - if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) { - /* Three-byte-character lead byte followed by two trail bytes. */ - - *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F)); - return 3; - } - /* A three-byte-character lead-byte not followed by two trail-bytes represents itself. */ - - *chPtr = (Tcl_UniChar) byte; - return 1; - } + if (byte < 0xC0) { + /* Handles properly formed UTF-8 characters between + * 0x01 and 0x7F. Also treats \0 and naked trail + * bytes 0x80 to 0xBF as valid characters representing + * themselves. + */ + + *chPtr = (Tcl_UniChar)byte; + return 1; + } else if (byte < 0xE0) { + if ((str[1] & 0xC0) == 0x80) { + /* Two-byte-character lead-byte followed by a trail-byte. */ + *chPtr = (Tcl_UniChar)(((byte & 0x1F) << 6) | (str[1] & 0x3F)); + return 2; + } + /* + * A two-byte-character lead-byte not followed by trail-byte + * represents itself. + */ + + *chPtr = (Tcl_UniChar)byte; + return 1; + } else if (byte < 0xF0) { + if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) { + /* Three-byte-character lead byte followed by two trail bytes. */ + *chPtr = + (Tcl_UniChar)(((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F)); + return 3; + } + /* A three-byte-character lead-byte not followed by two trail-bytes represents itself. + */ + *chPtr = (Tcl_UniChar)byte; + return 1; + } #if TCL_UTF_MAX > 3 - else { - int ch, total, trail; - - total = totalBytes[byte]; - trail = total - 1; - - if (trail > 0) { - ch = byte & (0x3F >> trail); - do { - str++; - if ((*str & 0xC0) != 0x80) { - *chPtr = byte; - return 1; - } - ch <<= 6; - ch |= (*str & 0x3F); - trail--; - } while (trail > 0); - *chPtr = ch; - return total; - } - } + else { + int ch, total, trail; + + total = totalBytes[byte]; + trail = total - 1; + if (trail > 0) { + ch = byte & (0x3F >> trail); + do { + str++; + if ((*str & 0xC0) != 0x80) { + *chPtr = byte; + return 1; + } + ch <<= 6; + ch |= (*str & 0x3F); + trail--; + } while (trail > 0); + *chPtr = ch; + return total; + } + } +#endif + + *chPtr = (Tcl_UniChar)byte; + return 1; +} + +#ifdef HAVE_LIBRAQM +#include +#else +#define RAQM_VERSION_ATLEAST(a, b, c) 0 +#endif + +typedef struct { + unsigned int index; + FT_Pos x_advance; + FT_Pos y_advance; + FT_Pos x_offset; + FT_Pos y_offset; + uint32_t cluster; +} glyphInfo; + +static ssize_t textLayout(uint32_t *text, size_t len, FT_Face face, gdFTStringExtraPtr strex, + glyphInfo **glyph_info) +{ + size_t count; + glyphInfo *info; + + if (!len) { + return 0; + } + +#ifdef HAVE_LIBRAQM + size_t i; + raqm_glyph_t *glyphs; + raqm_t *rq = raqm_create(); + + if (!rq || !raqm_set_text(rq, text, len) || !raqm_set_freetype_face(rq, face) || + !raqm_set_par_direction(rq, RAQM_DIRECTION_DEFAULT) || !raqm_layout(rq)) { + raqm_destroy(rq); + return -1; + } + + glyphs = raqm_get_glyphs(rq, &count); + if (!glyphs) { + raqm_destroy(rq); + return -1; + } + + info = (glyphInfo *)gdMalloc(sizeof(glyphInfo) * count); + if (!info) { + raqm_destroy(rq); + return -1; + } + + for (i = 0; i < count; i++) { + info[i].index = glyphs[i].index; + info[i].x_offset = glyphs[i].x_offset; + info[i].y_offset = glyphs[i].y_offset; + info[i].x_advance = glyphs[i].x_advance; + info[i].y_advance = glyphs[i].y_advance; + info[i].cluster = glyphs[i].cluster; + } + + raqm_destroy(rq); +#else + FT_UInt glyph_index = 0, previous = 0; + FT_Vector delta; + FT_Error err; + info = (glyphInfo *)gdMalloc(sizeof(glyphInfo) * len); + if (!info) { + return -1; + } + for (count = 0; count < len; count++) { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index(face, text[count]); + + /* retrieve kerning distance */ + if (!(strex && (strex->flags & gdFTEX_DISABLE_KERNING)) && !FT_IS_FIXED_WIDTH(face) && + FT_HAS_KERNING(face) && previous && glyph_index) + FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta); + else + delta.x = delta.y = 0; + + err = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (err) { + gdFree(info); + return -1; + } + info[count].index = glyph_index; + info[count].x_offset = 0; + info[count].y_offset = 0; + if (delta.x != 0) + info[count - 1].x_advance += delta.x; + info[count].x_advance = face->glyph->metrics.horiAdvance; + info[count].y_advance = 0; + info[count].cluster = count; + + /* carriage returns or newlines */ + if (text[count] == '\r' || text[count] == '\n') + previous = 0; /* clear kerning flag */ + else + previous = glyph_index; + } #endif - *chPtr = (Tcl_UniChar) byte; - return 1; + *glyph_info = info; + if (count <= (size_t)SSIZE_MAX) { + return (ssize_t)count; + } + return -1; } /********************************************************************/ /* font cache functions */ -static int fontTest (void *element, void *key) +static int fontTest(void *element, void *key) { - font_t *a = (font_t *) element; - fontkey_t *b = (fontkey_t *) key; - - if (strcmp (a->fontlist, b->fontlist) == 0) { - switch (b->preferred_map) { - case gdFTEX_Unicode: - if (a->have_char_map_unicode) { - return 1; - } - break; - case gdFTEX_Shift_JIS: - if (a->have_char_map_sjis) { - return 1; - } - break; - case gdFTEX_Big5: - if (a->have_char_map_sjis) { - return 1; - } - break; - } - } - return 0; + font_t *a = (font_t *)element; + fontkey_t *b = (fontkey_t *)key; + + return a->flags == b->flags && strcmp(a->fontlist, b->fontlist) == 0; } -static void *fontFetch (char **error, void *key) +#ifdef HAVE_LIBFONTCONFIG +static int useFontConfig(int flag) { - font_t *a; - fontkey_t *b = (fontkey_t *) key; - int n; - int font_found = 0; - unsigned short platform, encoding; - char *fontsearchpath, *fontlist; - char fullname[MAXPATHLEN], cur_dir[MAXPATHLEN]; - char *name, *path=NULL, *dir; - char *strtok_ptr; - FT_Error err; - FT_CharMap found = 0; - FT_CharMap charmap; - - a = (font_t *) gdPMalloc(sizeof(font_t)); - a->fontlist = gdPEstrdup(b->fontlist); - a->library = b->library; - - /* - * Search the pathlist for any of a list of font names. - */ - fontsearchpath = getenv ("GDFONTPATH"); - if (!fontsearchpath) { - fontsearchpath = DEFAULT_FONTPATH; - } - fontlist = gdEstrdup(a->fontlist); - - /* - * Must use gd_strtok_r becasuse strtok() isn't thread safe - */ - for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name; name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr)) { - char *strtok_ptr_path; - /* make a fresh copy each time - strtok corrupts it. */ - path = gdEstrdup (fontsearchpath); - - /* if name is an absolute filename then test directly */ - /* Actual length doesn't matter, just the minimum does up to length 2. */ - unsigned int min_length = 0; - if (name[0] != '\0') { - if (name[1] != '\0') { - min_length = 2; - } else { - min_length = 1; - } - } - ZEND_IGNORE_VALUE(min_length); /* On Posix systems this may be unused */ - if (IS_ABSOLUTE_PATH(name, min_length)) { - snprintf(fullname, sizeof(fullname) - 1, "%s", name); - if (access(fullname, R_OK) == 0) { - font_found++; - break; - } - } - for (dir = gd_strtok_r (path, PATHSEPARATOR, &strtok_ptr_path); dir; - dir = gd_strtok_r (0, PATHSEPARATOR, &strtok_ptr_path)) { - if (!strcmp(dir, ".")) { - #ifdef HAVE_GETCWD - dir = VCWD_GETCWD(cur_dir, MAXPATHLEN); -#elif defined(HAVE_GETWD) - dir = VCWD_GETWD(cur_dir); + if (fontConfigFlag) { + return (!(flag & gdFTEX_FONTPATHNAME)); + } + return flag & gdFTEX_FONTCONFIG; +} #endif - if (!dir) { - continue; - } - } -#define GD_CHECK_FONT_PATH(ext) \ - snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext); \ - if (access(fullname, R_OK) == 0) { \ - font_found++; \ - break; \ - } \ - - GD_CHECK_FONT_PATH(""); - GD_CHECK_FONT_PATH(".ttf"); - GD_CHECK_FONT_PATH(".pfa"); - GD_CHECK_FONT_PATH(".pfb"); - GD_CHECK_FONT_PATH(".dfont"); - } - gdFree(path); - path = NULL; - if (font_found) { - break; - } - } - - if (path) { - gdFree(path); - } - - gdFree(fontlist); - - if (!font_found) { - gdPFree(a->fontlist); - gdPFree(a); - *error = "Could not find/open font"; - return NULL; - } - - err = FT_New_Face (*b->library, fullname, 0, &a->face); - if (err) { - gdPFree(a->fontlist); - gdPFree(a); - *error = "Could not read font"; - return NULL; - } - - /* FIXME - This mapping stuff is incomplete - where is the spec? */ - /* EAM - It's worse than that. It's pointless to match character encodings here. - * As currently written, the stored a->face->charmap only matches one of - * the actual charmaps and we cannot know at this stage if it is the right - * one. We should just skip all this stuff, and check in gdImageStringFTEx - * if some particular charmap is preferred and if so whether it is held in - * one of the a->face->charmaps[0..num_charmaps]. - * And why is it so bad not to find any recognized charmap? The user may - * still know what mapping to use, even if we do not. In that case we can - * just use the map in a->face->charmaps[num_charmaps] and be done with it. - */ - - for (n = 0; n < a->face->num_charmaps; n++) { - charmap = a->face->charmaps[n]; - platform = charmap->platform_id; - encoding = charmap->encoding_id; - - /* Whatever is the last value is what should be set */ - a->have_char_map_unicode = 0; - a->have_char_map_big5 = 0; - a->have_char_map_sjis = 0; - a->have_char_map_apple_roman = 0; - -/* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */ -#if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2))) - if (charmap->encoding == FT_ENCODING_MS_SYMBOL - || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM - || charmap->encoding == FT_ENCODING_ADOBE_STANDARD) { - a->have_char_map_unicode = 1; - found = charmap; - a->face->charmap = charmap; - return (void *)a; - } -#endif /* Freetype 2.1.3 or better */ -/* EAM DEBUG */ - - if ((platform == 3 && encoding == 1) /* Windows Unicode */ - || (platform == 3 && encoding == 0) /* Windows Symbol */ - || (platform == 2 && encoding == 1) /* ISO Unicode */ - || (platform == 0)) - { /* Apple Unicode */ - a->have_char_map_unicode = 1; - found = charmap; - if (b->preferred_map == gdFTEX_Unicode) { - break; - } - } else if (platform == 3 && encoding == 4) { /* Windows Big5 */ - a->have_char_map_big5 = 1; - found = charmap; - if (b->preferred_map == gdFTEX_Big5) { - break; - } - } else if (platform == 3 && encoding == 2) { /* Windows Sjis */ - a->have_char_map_sjis = 1; - found = charmap; - if (b->preferred_map == gdFTEX_Shift_JIS) { - break; - } - } else if ((platform == 1 && encoding == 0) /* Apple Roman */ - || (platform == 2 && encoding == 0)) - { /* ISO ASCII */ - a->have_char_map_apple_roman = 1; - found = charmap; - if (b->preferred_map == gdFTEX_MacRoman) { - break; - } - } - } - if (!found) { - gdPFree(a->fontlist); - gdPFree(a); - *error = "Unable to find a CharMap that I can handle"; - return NULL; - } - - /* 2.0.5: we should actually return this */ - a->face->charmap = found; - return (void *) a; +static void *fontFetch(char **error, void *key) +{ + font_t *a; + fontkey_t *b = (fontkey_t *)key; + char *fontpath = NULL; + char *suffix; + FT_Error err; + const unsigned int b_font_list_len = strlen(b->fontlist); + + *error = NULL; + + a = (font_t *)GD_FT_CACHE_MALLOC(sizeof(font_t)); + if (!a) { + *error = "could not alloc font cache entry"; + return NULL; + } + + a->fontlist = (char *)GD_FT_CACHE_MALLOC(b_font_list_len + 1); + if (a->fontlist == NULL) { + GD_FT_CACHE_FREE(a); + *error = "could not alloc full list of fonts"; + return NULL; + } + memcpy(a->fontlist, b->fontlist, b_font_list_len); + a->fontlist[b_font_list_len] = 0; + + a->flags = b->flags; + a->library = b->library; + a->fontpath = NULL; + +#ifdef HAVE_LIBFONTCONFIG + if (!useFontConfig(b->flags)) + *error = font_path(&fontpath, a->fontlist); + else + *error = font_pattern(&fontpath, a->fontlist); +#else + *error = font_path(&fontpath, a->fontlist); +#endif /* HAVE_LIBFONTCONFIG */ + if (*error || !fontpath || !fontpath[0]) { + GD_FT_CACHE_FREE(a->fontlist); + gdFree(fontpath); + GD_FT_CACHE_FREE(a); + + if (!*error) + *error = "font_path() returned an empty font pathname"; + + return NULL; + } + a->fontpath = (char *)GD_FT_CACHE_MALLOC(strlen(fontpath) + 1); + if (!a->fontpath) { + GD_FT_CACHE_FREE(a->fontlist); + gdFree(fontpath); + GD_FT_CACHE_FREE(a); + *error = "could not alloc font path"; + return NULL; + } + memcpy(a->fontpath, fontpath, strlen(fontpath) + 1); + gdFree(fontpath); + + err = FT_New_Face(*b->library, a->fontpath, 0, &a->face); + + /* Read kerning metrics for Postscript fonts. */ + if (!err && + ((suffix = strstr(a->fontpath, ".pfa")) || (suffix = strstr(a->fontpath, ".pfb"))) && + ((strcpy(suffix, ".afm") && (GD_FT_ACCESS(a->fontpath, R_OK) == 0)) || + (strcpy(suffix, ".pfm") && (GD_FT_ACCESS(a->fontpath, R_OK) == 0)))) { + err = FT_Attach_File(a->face, a->fontpath); + } + + if (err) { + GD_FT_CACHE_FREE(a->fontlist); + GD_FT_CACHE_FREE(a->fontpath); + GD_FT_CACHE_FREE(a); + *error = "Could not read font"; + return NULL; + } + + return (void *)a; } -static void fontRelease (void *element) +static void fontRelease(void *element) { - font_t *a = (font_t *) element; + font_t *a = (font_t *)element; - FT_Done_Face (a->face); - gdPFree(a->fontlist); - gdPFree((char *) element); + FT_Done_Face(a->face); + GD_FT_CACHE_FREE(a->fontlist); + GD_FT_CACHE_FREE(a->fontpath); + GD_FT_CACHE_FREE((char *)element); } /********************************************************************/ /* tweencolor cache functions */ -static int tweenColorTest (void *element, void *key) +static int tweenColorTest(void *element, void *key) { - tweencolor_t *a = (tweencolor_t *) element; - tweencolorkey_t *b = (tweencolorkey_t *) key; + tweencolor_t *a = (tweencolor_t *)element; + tweencolorkey_t *b = (tweencolorkey_t *)key; - return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im); + return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && + a->im == b->im); } /* * Computes a color in im's color table that is part way between * the background and foreground colors proportional to the gray - * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already + * pixel value in the range 0-GD_NUMCOLORS. The fg and bg colors must already * be in the color table for palette images. For truecolor images the * returned value simply has an alpha component and gdImageAlphaBlend * does the work so that text can be alpha blended across a complex * background (TBB; and for real in 2.0.2). */ -static void * tweenColorFetch (char **error, void *key) +static void *tweenColorFetch(char **error, void *key) { - tweencolor_t *a; - tweencolorkey_t *b = (tweencolorkey_t *) key; - int pixel, npixel, bg, fg; - gdImagePtr im; - - a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t)); - pixel = a->pixel = b->pixel; - bg = a->bgcolor = b->bgcolor; - fg = a->fgcolor = b->fgcolor; - im = a->im = b->im; - - /* if fg is specified by a negative color idx, then don't antialias */ - if (fg < 0) { - if ((pixel + pixel) >= NUMCOLORS) { - a->tweencolor = -fg; - } else { - a->tweencolor = bg; - } - } else { - npixel = NUMCOLORS - pixel; - if (im->trueColor) { - /* 2.0.1: use gdImageSetPixel to do the alpha blending work, - * or to just store the alpha level. All we have to do here - * is incorporate our knowledge of the percentage of this - * pixel that is really "lit" by pushing the alpha value - * up toward transparency in edge regions. - */ - a->tweencolor = gdTrueColorAlpha( - gdTrueColorGetRed(fg), - gdTrueColorGetGreen(fg), - gdTrueColorGetBlue(fg), - gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS)); - } else { - a->tweencolor = gdImageColorResolve(im, - (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS, - (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS, - (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS); - } - } - return (void *) a; + tweencolor_t *a; + tweencolorkey_t *b = (tweencolorkey_t *)key; + int pixel, npixel, bg, fg; + gdImagePtr im; + + (void)error; + + a = (tweencolor_t *)gdMalloc(sizeof(tweencolor_t)); + if (!a) { + return NULL; + } + + pixel = a->pixel = b->pixel; + bg = a->bgcolor = b->bgcolor; + fg = a->fgcolor = b->fgcolor; + im = a->im = b->im; + + /* if fg is specified by a negative color idx, then don't antialias */ + if (fg < 0) { + if ((pixel + pixel) >= GD_NUMCOLORS) + a->tweencolor = -fg; + else + a->tweencolor = bg; + } else { + npixel = GD_NUMCOLORS - pixel; + if (im->trueColor) { + /* 2.0.1: use gdImageSetPixel to do the alpha blending work, + or to just store the alpha level. All we have to do here + is incorporate our knowledge of the percentage of this + pixel that is really "lit" by pushing the alpha value + up toward transparency in edge regions. */ + a->tweencolor = gdTrueColorAlpha( + gdTrueColorGetRed(fg), gdTrueColorGetGreen(fg), gdTrueColorGetBlue(fg), + gdAlphaMax - (gdTrueColorGetAlpha(fg) * pixel / GD_NUMCOLORS)); + } else { + a->tweencolor = + gdImageColorResolve(im, (pixel * im->red[fg] + npixel * im->red[bg]) / GD_NUMCOLORS, + (pixel * im->green[fg] + npixel * im->green[bg]) / GD_NUMCOLORS, + (pixel * im->blue[fg] + npixel * im->blue[bg]) / GD_NUMCOLORS); + } + } + return (void *)a; } -static void tweenColorRelease (void *element) -{ - gdFree((char *) element); -} +static void tweenColorRelease(void *element) { gdFree((char *)element); } /* draw_bitmap - transfers glyph bitmap to GD image */ -static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y) -{ - unsigned char *pixel = NULL; - int *tpixel = NULL; - int opixel; - int x, y, row, col, pc, pcr; - - tweencolor_t *tc_elem; - tweencolorkey_t tc_key; - - /* copy to image, mapping colors */ - tc_key.fgcolor = fg; - tc_key.im = im; - /* Truecolor version; does not require the cache */ - if (im->trueColor) { - for (row = 0; row < bitmap.rows; row++) { - pc = row * bitmap.pitch; - pcr = pc; - y = pen_y + row; - /* clip if out of bounds */ - /* 2.0.16: clipping rectangle, not image bounds */ - if ((y > im->cy2) || (y < im->cy1)) { - continue; - } - for (col = 0; col < bitmap.width; col++, pc++) { - int level; - if (bitmap.pixel_mode == ft_pixel_mode_grays) { - /* Scale to 128 levels of alpha for gd use. - * alpha 0 is opacity, so be sure to invert at the end - */ - level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1)); - } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { - /* 2.0.5: mode_mono fix from Giuliano Pochini */ - level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque; - } else { - return "Unsupported ft_pixel_mode"; - } - if (level == 0) /* if background */ - continue; - if ((fg >= 0) && (im->trueColor)) { - /* Consider alpha in the foreground color itself to be an - * upper bound on how opaque things get, when truecolor is - * available. Without truecolor this results in far too many - * color indexes. - */ - level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax; - } - level = gdAlphaMax - level; - x = pen_x + col; - /* clip if out of bounds */ - /* 2.0.16: clip to clipping rectangle, Matt McNabb */ - if ((x > im->cx2) || (x < im->cx1)) { - continue; - } - /* get pixel location in gd buffer */ - tpixel = &im->tpixels[y][x]; - if (fg < 0) { - if (level < (gdAlphaMax / 2)) { - *tpixel = -fg; - } - } else { - if (im->alphaBlendingFlag) { - opixel = *tpixel; - if (gdTrueColorGetAlpha(opixel) != gdAlphaTransparent) { - *tpixel = gdAlphaBlend (opixel, - (level << 24) + (fg & 0xFFFFFF)); - } else { - *tpixel = (level << 24) + (fg & 0xFFFFFF); - } - } else { - *tpixel = (level << 24) + (fg & 0xFFFFFF); - } - } - } - } - return (char *) NULL; - } - /* Non-truecolor case, restored to its more or less original form */ - for (row = 0; row < bitmap.rows; row++) { - int pcr; - pc = row * bitmap.pitch; - pcr = pc; - if (bitmap.pixel_mode==ft_pixel_mode_mono) { - pc *= 8; /* pc is measured in bits for monochrome images */ - } - y = pen_y + row; - - /* clip if out of bounds */ - if (y > im->cy2 || y < im->cy1) { - continue; - } - - for (col = 0; col < bitmap.width; col++, pc++) { - if (bitmap.pixel_mode == ft_pixel_mode_grays) { - /* - * Round to NUMCOLORS levels of antialiasing for - * index color images since only 256 colors are - * available. - */ - tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1); - } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { - tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0; - /* 2.0.5: mode_mono fix from Giuliano Pochini */ - tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0; - } else { - return "Unsupported ft_pixel_mode"; - } - if (tc_key.pixel > 0) { /* if not background */ - x = pen_x + col; - - /* clip if out of bounds */ - if (x > im->cx2 || x < im->cx1) { - continue; - } - /* get pixel location in gd buffer */ - pixel = &im->pixels[y][x]; - if (tc_key.pixel == NUMCOLORS) { - /* use fg color directly. gd 2.0.2: watch out for - * negative indexes (thanks to David Marwood). - */ - *pixel = (fg < 0) ? -fg : fg; - } else { - /* find antialised color */ - tc_key.bgcolor = *pixel; - tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key); - *pixel = tc_elem->tweencolor; - } - } - } - } - return (char *) NULL; -} - -static int -gdroundupdown (FT_F26Dot6 v1, int roundup) +static char *gdft_draw_bitmap(gdCache_head_t *tc_cache, gdImagePtr im, int fg, FT_Bitmap bitmap, + int pen_x, int pen_y) { - return (!roundup) ? v1 >> 6 : (v1 + 63) >> 6; + unsigned char *pixel = NULL; + int *tpixel = NULL; + int opixel; + int x, y, pc, pcr; + unsigned int col; + unsigned int row; + + tweencolor_t *tc_elem; + tweencolorkey_t tc_key; + + /* copy to image, mapping colors */ + tc_key.fgcolor = fg; + tc_key.im = im; + /* Truecolor version; does not require the cache */ + if (im->trueColor) { + for (row = 0; row < bitmap.rows; row++) { + pc = row * bitmap.pitch; + pcr = pc; + y = pen_y + row; + /* clip if out of bounds */ + /* 2.0.16: clipping rectangle, not image bounds */ + if ((y > im->cy2) || (y < im->cy1)) + continue; + for (col = 0; col < bitmap.width; col++, pc++) { + int level; + if (bitmap.pixel_mode == ft_pixel_mode_grays) { + /* + * Scale to 128 levels of alpha for gd use. + * alpha 0 is opacity, so be sure to invert at the end + */ + level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1)); + } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { + /* 2.0.5: mode_mono fix from Giuliano Pochini */ + level = ((bitmap.buffer[(col >> 3) + pcr]) & (1 << (~col & 0x07))) + ? gdAlphaTransparent + : gdAlphaOpaque; + } else { + return "Unsupported ft_pixel_mode"; + } + if (level == 0) /* if background */ + continue; + + if ((fg >= 0) && (im->trueColor)) { + /* Consider alpha in the foreground color itself to be an + upper bound on how opaque things get, when truecolor is + available. Without truecolor this results in far too many + color indexes. */ + level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax; + } + level = gdAlphaMax - level; /* inverting to get alpha */ + x = pen_x + col; + /* clip if out of bounds */ + /* 2.0.16: clip to clipping rectangle, Matt McNabb */ + if ((x > im->cx2) || (x < im->cx1)) + continue; + /* get pixel location in gd buffer */ + tpixel = &im->tpixels[y][x]; + if (fg < 0) { + if (level < (gdAlphaMax / 2)) { + *tpixel = -fg; + } + } else { + if (im->alphaBlendingFlag) { + opixel = *tpixel; + if (gdTrueColorGetAlpha(opixel) != gdAlphaTransparent) { + *tpixel = gdAlphaBlend(opixel, (level << 24) + (fg & 0xFFFFFF)); + } else { + *tpixel = (level << 24) + (fg & 0xFFFFFF); + } + } else { + *tpixel = (level << 24) + (fg & 0xFFFFFF); + } + } + } + } + return (char *)NULL; + } + /* Non-truecolor case, restored to its more or less original form */ + for (row = 0; row < bitmap.rows; row++) { + int pcr; + pc = row * bitmap.pitch; + pcr = pc; + if (bitmap.pixel_mode == ft_pixel_mode_mono) + pc *= 8; /* pc is measured in bits for monochrome images */ + + y = pen_y + row; + + /* clip if out of bounds */ + if (y > im->cy2 || y < im->cy1) + continue; + + for (col = 0; col < bitmap.width; col++, pc++) { + if (bitmap.pixel_mode == ft_pixel_mode_grays) { + /* + * Round to GD_NUMCOLORS levels of antialiasing for + * index color images since only 256 colors are + * available. + */ + tc_key.pixel = ((bitmap.buffer[pc] * GD_NUMCOLORS) + bitmap.num_grays / 2) / + (bitmap.num_grays - 1); + } else if (bitmap.pixel_mode == ft_pixel_mode_mono) { + /* 2.0.5: mode_mono fix from Giuliano Pochini */ + tc_key.pixel = + ((bitmap.buffer[(col >> 3) + pcr]) & (1 << (~col & 0x07))) ? GD_NUMCOLORS : 0; + } else { + return "Unsupported ft_pixel_mode"; + } + if (tc_key.pixel == 0) /* if background */ + continue; + + x = pen_x + col; + + /* clip if out of bounds */ + if (x > im->cx2 || x < im->cx1) + continue; + /* get pixel location in gd buffer */ + pixel = &im->pixels[y][x]; + if (tc_key.pixel == GD_NUMCOLORS) { + /* use fg color directly. gd 2.0.2: watch out for + negative indexes (thanks to David Marwood). */ + *pixel = (fg < 0) ? -fg : fg; + } else { + /* find antialised color */ + + tc_key.bgcolor = *pixel; + tc_elem = (tweencolor_t *)gdCacheGet(tc_cache, &tc_key); + if (!tc_elem) + return tc_cache->error; + *pixel = tc_elem->tweencolor; + } + } + } + return (char *)NULL; } -void gdFontCacheShutdown() -{ - gdMutexLock(gdFontCacheMutex); +/** + * Function: gdFreeFontCache + * + * Alias of . + */ +BGD_DECLARE(void) gdFreeFontCache() { gdFontCacheShutdown(); } - if (fontCache) { - gdCacheDelete(fontCache); - fontCache = NULL; - FT_Done_FreeType(library); - } +BGD_DECLARE(void) gdFontCacheMutexSetup(void) { gdMutexSetup(gdFontCacheMutex); } - gdMutexUnlock(gdFontCacheMutex); -} +BGD_DECLARE(void) gdFontCacheMutexShutdown(void) { gdMutexShutdown(gdFontCacheMutex); } -void gdFreeFontCache(void) +/** + * Function: gdFontCacheShutdown + * + * Shut down the font cache and free the allocated resources. + * + * Important: + * This function has to be called whenever FreeType operations have been + * invoked, to avoid resource leaks. It doesn't harm to call this function + * multiple times. + */ +BGD_DECLARE(void) gdFontCacheShutdown() { - gdFontCacheShutdown(); + if (fontCache) { + gdMutexLock(gdFontCacheMutex); + gdCacheDelete(fontCache); + /* 2.0.16: Gustavo Scotti: make sure we don't free this twice */ + fontCache = 0; + gdMutexUnlock(gdFontCacheMutex); + FT_Done_FreeType(library); + } } -void gdFontCacheMutexSetup() +/** + * Function: gdImageStringFT + * + * Render an UTF-8 string onto a gd image. + * + * Parameters: + * im - The image to draw onto. + * brect - The bounding rectangle as array of 8 integers where each pair + * represents the x- and y-coordinate of a point. The points + * specify the lower left, lower right, upper right and upper left + * corner. + * fg - The font color. + * fontlist - The semicolon delimited list of font filenames to look for. + * ptsize - The height of the font in typographical points (pt). + * angle - The angle in radian to rotate the font counter-clockwise. + * x - The x-coordinate of the basepoint (roughly the lower left corner) + *of the first letter. y - The y-coordinate of the basepoint (roughly the + *lower left corner) of the first letter. string - The string to render. + * + * Variant: + * - + * + * See also: + * - + */ +BGD_DECLARE(char *) +gdImageStringFT(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string) { - gdMutexSetup(gdFontCacheMutex); + return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0); } -void gdFontCacheMutexShutdown() +/** + * Function: gdFontCacheSetup + * + * Set up the font cache. + * + * This is called automatically from the string rendering functions, if it + * has not already been called. So there's no need to call this function + * explicitly. + */ +BGD_DECLARE(int) gdFontCacheSetup(void) { - gdMutexShutdown(gdFontCacheMutex); + if (fontCache) { + /* Already set up */ + return 0; + } + if (FT_Init_FreeType(&library)) { + return -1; + } + fontCache = gdCacheCreate(FONTCACHESIZE, fontTest, fontFetch, fontRelease); + if (!fontCache) { + return -2; + } + return 0; } -int gdFontCacheSetup(void) -{ - if (fontCache) { - /* Already set up */ - return 0; - } - if (FT_Init_FreeType(&library)) { - return -1; - } - fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease); - return 0; -} +/* + Function: gdImageStringFTEx + gdImageStringFTEx extends the capabilities of gdImageStringFT by + providing a way to pass additional parameters. -/********************************************************************/ -/* gdImageStringFT - render a utf8 string onto a gd image */ + If the strex parameter is not null, it must point to a + gdFTStringExtra structure. As of gd 2.0.5, this structure is defined + as follows: + (start code) -char * -gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist, - double ptsize, double angle, int x, int y, char *string) -{ - return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0); -} + typedef struct { + // logical OR of gdFTEX_ values + int flags; + + // fine tune line spacing for '\n' + double linespacing; + + // Preferred character mapping + int charmap; + + // Rendering resolution + int hdpi; + int vdpi; + char *xshow; + char *fontpath; + } gdFTStringExtra, *gdFTStringExtraPtr; + + (end code) + + To output multiline text with a specific line spacing, include + gdFTEX_LINESPACE in the setting of flags: + + > flags |= gdFTEX_LINESPACE; + + And also set linespacing to the desired spacing, expressed as a + multiple of the font height. Thus a line spacing of 1.0 is the + minimum to guarantee that lines of text do not collide. + + If gdFTEX_LINESPACE is not present, or strex is null, or + gdImageStringFT is called, linespacing defaults to 1.05. + + To specify a preference for Unicode, Shift_JIS Big5 character + encoding, set or To output multiline text with a specific line + spacing, include gdFTEX_CHARMAP in the setting of flags: + + > flags |= gdFTEX_CHARMAP; + + And set charmap to the desired value, which can be any of + gdFTEX_Unicode, gdFTEX_Shift_JIS, gdFTEX_Big5, or + gdFTEX_Adobe_Custom. If you do not specify a preference, Unicode + will be tried first. If the preferred character mapping is not found + in the font, other character mappings are attempted. + + GD operates on the assumption that the output image will be rendered + to a computer screen. By default, gd passes a resolution of 96 dpi + to the freetype text rendering engine. This influences the "hinting" + decisions made by the renderer. To specify a different resolution, + set hdpi and vdpi accordingly (in dots per inch) and add + gdFTEX_RESOLUTION to flags: + + > flags | gdFTEX_RESOLUTION; + + GD 2.0.29 and later will normally attempt to apply kerning tables, + if fontconfig is available, to adjust the relative positions of + consecutive characters more ideally for that pair of + characters. This can be turn off by specifying the + gdFTEX_DISABLE_KERNING flag: + + > flags | gdFTEX_DISABLE_KERNING; -char * -gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex) + GD 2.0.29 and later can return a vector of individual character + position advances, occasionally useful in applications that must + know exactly where each character begins. This is returned in the + xshow element of the gdFTStringExtra structure if the gdFTEX_XSHOW + flag is set: + + > flags | gdFTEX_XSHOW; + + The caller is responsible for calling gdFree() on the xshow element + after the call if gdFTEX_XSHOW is set. + + GD 2.0.29 and later can also return the path to the actual font file + used if the gdFTEX_RETURNFONTPATHNAME flag is set. This is useful + because GD 2.0.29 and above are capable of selecting a font + automatically based on a fontconfig font pattern when fontconfig is + available. This information is returned in the fontpath element of + the gdFTStringExtra structure. + + > flags | gdFTEX_RETURNFONTPATHNAME; + + The caller is responsible for calling gdFree() on the fontpath + element after the call if gdFTEX_RETURNFONTPATHNAME is set. + + GD 2.0.29 and later can use fontconfig to resolve font names, + including fontconfig patterns, if the gdFTEX_FONTCONFIG flag is + set. As a convenience, this behavior can be made the default by + calling with a nonzero value. In that situation it + is not necessary to set the gdFTEX_FONTCONFIG flag on every call; + however explicit font path names can still be used if the + gdFTEX_FONTPATHNAME flag is set: + + > flags | gdFTEX_FONTPATHNAME; + + Unless has been called with a nonzero value, GD + 2.0.29 and later will still expect the fontlist argument to the + freetype text output functions to be a font file name or list + thereof as in previous versions. If you do not wish to make + fontconfig the default, it is still possible to force the use of + fontconfig for a single call to the freetype text output functions + by setting the gdFTEX_FONTCONFIG flag: + + > flags | gdFTEX_FONTCONFIG; + + GD 2.0.29 and above can use fontconfig to resolve font names, + including fontconfig patterns, if the gdFTEX_FONTCONFIG flag is + set. As a convenience, this behavior can be made the default by + calling with a nonzero value. In that situation it + is not necessary to set the gdFTEX_FONTCONFIG flag on every call; + however explicit font path names can still be used if the + gdFTEX_FONTPATHNAME flag is set: + + > flags | gdFTEX_FONTPATHNAME; + + For more information, see . +*/ + +/* the platform-independent resolution used for size and position calculations + */ +/* the size of the error introduced by rounding is affected by this number */ +#define METRIC_RES 300 + +BGD_DECLARE(char *) +gdImageStringFTEx(gdImagePtr im, int *brect, int fg, const char *fontlist, double ptsize, + double angle, int x, int y, const char *string, gdFTStringExtraPtr strex) { - FT_BBox bbox, glyph_bbox; - FT_Matrix matrix; - FT_Vector pen, delta, penf; - FT_Face face; - FT_Glyph image; - FT_GlyphSlot slot; - FT_Bool use_kerning; - FT_UInt glyph_index, previous; - double sin_a = sin (angle); - double cos_a = cos (angle); - int len, i = 0, ch; - int x1 = 0, y1 = 0; - font_t *font; - fontkey_t fontkey; - char *next; - char *tmpstr = NULL; - int render = (im && (im->trueColor || (fg <= 255 && fg >= -255))); - FT_BitmapGlyph bm; - /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */ - int render_mode = FT_LOAD_DEFAULT; - int m, mfound; - /* Now tuneable thanks to Wez Furlong */ - double linespace = LINESPACE; - /* 2.0.6: put this declaration with the other declarations! */ - /* - * make a new tweenColorCache on every call - * because caching colormappings between calls - * is not safe. If the im-pointer points to a - * brand new image, the cache gives out bogus - * colorindexes. -- 27.06.2001 - */ - gdCache_head_t *tc_cache; - /* Tuneable horizontal and vertical resolution in dots per inch */ - int hdpi, vdpi; - - if (strex && ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)) { - linespace = strex->linespacing; - } - tc_cache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease); - - /***** initialize font library and font cache on first call ******/ - - gdMutexLock(gdFontCacheMutex); - if (!fontCache) { - if (gdFontCacheSetup() != 0) { - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Failure to initialize font library"; - } - } - /*****/ - - /* 2.0.12: allow explicit specification of the preferred map; - * but we still fall back if it is not available. - */ - m = gdFTEX_Unicode; - if (strex && (strex->flags & gdFTEX_CHARMAP)) { - m = strex->charmap; - } - - /* get the font (via font cache) */ - fontkey.fontlist = fontlist; - fontkey.library = &library; - fontkey.preferred_map = m; - font = (font_t *) gdCacheGet (fontCache, &fontkey); - if (!font) { - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return fontCache->error; - } - face = font->face; /* shortcut */ - slot = face->glyph; /* shortcut */ - - /* - * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF, - * or 100h x 50v dpi FAX format. 2.0.23. - * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com - */ - hdpi = GD_RESOLUTION; - vdpi = GD_RESOLUTION; - if (strex && (strex->flags & gdFTEX_RESOLUTION)) { - hdpi = strex->hdpi; - vdpi = strex->vdpi; - } - - if (FT_Set_Char_Size(face, 0, (FT_F26Dot6) (ptsize * 64), hdpi, vdpi)) { - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Could not set character size"; - } - - matrix.xx = (FT_Fixed) (cos_a * (1 << 16)); - matrix.yx = (FT_Fixed) (sin_a * (1 << 16)); - matrix.xy = -matrix.yx; - matrix.yy = matrix.xx; - - penf.x = penf.y = 0; /* running position of non-rotated string */ - pen.x = pen.y = 0; /* running position of rotated string */ - bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0; - - use_kerning = FT_HAS_KERNING (face); - previous = 0; - if (fg < 0) { - render_mode |= FT_LOAD_MONOCHROME; - } - - /* Try all three types of maps, but start with the specified one */ - mfound = 0; - for (i = 0; i < 3; i++) { - switch (m) { - case gdFTEX_Unicode: - if (font->have_char_map_unicode) { - mfound = 1; - } - break; - case gdFTEX_Shift_JIS: - if (font->have_char_map_sjis) { - mfound = 1; - } - break; - case gdFTEX_Big5: - /* This was the 'else' case, we can't really 'detect' it */ - mfound = 1; - break; - } - if (mfound) { - break; - } - m++; - m %= 3; - } - if (!mfound) { - /* No character set found! */ - gdMutexUnlock(gdFontCacheMutex); - return "No character set found"; - } + FT_Matrix matrix; + FT_Vector penf, oldpenf, total_min = {0, 0}, total_max = {0, 0}, glyph_min, glyph_max; + FT_Face face; + FT_CharMap charmap = NULL; + FT_CharMap fallback_charmap = NULL; + FT_Glyph image; + FT_GlyphSlot slot; + FT_Error err; + FT_UInt glyph_index; + double sin_a = sin(angle); + double cos_a = cos(angle); + int i, ch; + font_t *font; + fontkey_t fontkey; + const char *next; + char *tmpstr = 0; + uint32_t *text; + glyphInfo *info = NULL; + ssize_t count; + int render = (im && (im->trueColor || (fg <= 255 && fg >= -255))); + FT_BitmapGlyph bm; + /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing + freetype and doesn't look as good */ + int render_mode = FT_LOAD_DEFAULT; + int encoding, encodingfound; + /* Now tuneable thanks to Wez Furlong */ + double linespace = LINESPACE; + /* 2.0.6: put this declaration with the other declarations! */ + /* + * make a new tweenColorCache on every call + * because caching colormappings between calls + * is not safe. If the im-pointer points to a + * brand new image, the cache gives out bogus + * colorindexes. -- 27.06.2001 + */ + gdCache_head_t *tc_cache; + /* Tuneable horizontal and vertical resolution in dots per inch */ + int hdpi, vdpi, horiAdvance, vertAdvance, xshow_alloc = 0, xshow_pos = 0; + FT_Size platform_specific = NULL, platform_independent; + + if (strex) { + if ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE) { + linespace = strex->linespacing; + } + } + tc_cache = + gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease); + + /***** initialize font library and font cache on first call ******/ + if (!fontCache) { + if (gdFontCacheSetup() != 0) { + gdCacheDelete(tc_cache); + return "Failure to initialize font library"; + } + } + /*****/ + gdMutexLock(gdFontCacheMutex); + /* get the font (via font cache) */ + fontkey.fontlist = fontlist; + if (strex) + fontkey.flags = strex->flags & (gdFTEX_FONTPATHNAME | gdFTEX_FONTCONFIG); + else + fontkey.flags = 0; + fontkey.library = &library; + font = (font_t *)gdCacheGet(fontCache, &fontkey); + if (!font) { + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return fontCache->error; + } + face = font->face; /* shortcut */ + slot = face->glyph; /* shortcut */ + + if (brect) { + total_min.x = total_min.y = 0; + total_max.x = total_max.y = 0; + } + + /* + * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 + * dpi TIFF, or 100h x 50v dpi FAX format. 2.0.23. 2004/02/27 Mark + * Shackelford, mark.shackelford@acs-inc.com + */ + hdpi = GD_RESOLUTION; + vdpi = GD_RESOLUTION; + encoding = gdFTEX_Unicode; + if (strex) { + if (strex->flags & gdFTEX_RESOLUTION) { + hdpi = strex->hdpi; + vdpi = strex->vdpi; + } + if (strex->flags & gdFTEX_XSHOW) { + strex->xshow = NULL; + } + /* 2.0.12: allow explicit specification of the preferred map; + but we still fall back if it is not available. */ + if (strex->flags & gdFTEX_CHARMAP) { + encoding = strex->charmap; + } + /* 2.0.29: we can return the font path if desired */ + if (strex->flags & gdFTEX_RETURNFONTPATHNAME) { + const unsigned int fontpath_len = strlen(font->fontpath); + + strex->fontpath = (char *)gdMalloc(fontpath_len + 1); + if (strex->fontpath == NULL) { + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return "could not alloc full list of fonts"; + } + memcpy(strex->fontpath, font->fontpath, fontpath_len); + strex->fontpath[fontpath_len] = 0; + } + } + + matrix.xx = (FT_Fixed)(cos_a * (1 << 16)); + matrix.yx = (FT_Fixed)(sin_a * (1 << 16)); + matrix.xy = -matrix.yx; + matrix.yy = matrix.xx; + + /* Keep RAQM layout in text space; apply transform later only for + * the render glyph load. */ +#ifdef HAVE_LIBRAQM + FT_Set_Transform(face, NULL, NULL); +#else + /* set rotation transform */ + FT_Set_Transform(face, &matrix, NULL); +#endif + + FT_New_Size(face, &platform_independent); + FT_Activate_Size(platform_independent); + if (FT_Set_Char_Size(face, 0, (FT_F26Dot6)(ptsize * 64), METRIC_RES, METRIC_RES)) { + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return "Could not set character size"; + } + + if (render) { + FT_New_Size(face, &platform_specific); + FT_Activate_Size(platform_specific); + if (FT_Set_Char_Size(face, 0, (FT_F26Dot6)(ptsize * 64), hdpi, vdpi)) { + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return "Could not set character size"; + } + } + + if (fg < 0) + render_mode |= FT_LOAD_MONOCHROME; + + /* find requested charmap */ + encodingfound = 0; + for (i = 0; i < face->num_charmaps; i++) { + charmap = face->charmaps[i]; + + if (encoding == gdFTEX_Unicode) { + if (charmap->encoding == FT_ENCODING_UNICODE) { + encodingfound++; + break; + } + if (!fallback_charmap && (charmap->encoding == FT_ENCODING_MS_SYMBOL || + charmap->encoding == FT_ENCODING_ADOBE_CUSTOM || + charmap->encoding == FT_ENCODING_ADOBE_STANDARD)) { + fallback_charmap = charmap; + } + } else if (encoding == gdFTEX_Adobe_Custom) { + if (charmap->encoding == FT_ENCODING_ADOBE_CUSTOM) { + encodingfound++; + break; + } + if (!fallback_charmap && charmap->encoding == FT_ENCODING_APPLE_ROMAN) { + fallback_charmap = charmap; + } + } else if (encoding == gdFTEX_Big5) { + /* renamed sometime after freetype-2.1.4 */ +#ifndef FT_ENCODING_BIG5 +#define FT_ENCODING_BIG5 FT_ENCODING_MS_BIG5 +#endif + if (charmap->encoding == FT_ENCODING_BIG5) { + encodingfound++; + break; + } + } else if (encoding == gdFTEX_Shift_JIS) { + /* renamed sometime after freetype-2.1.4 */ +#ifndef FT_ENCODING_SJIS +#define FT_ENCODING_SJIS FT_ENCODING_MS_SJIS +#endif + if (charmap->encoding == FT_ENCODING_SJIS) { + encodingfound++; + break; + } + } + } + if (!encodingfound && fallback_charmap) { + charmap = fallback_charmap; + encodingfound = 1; + } + if (encodingfound) { + FT_Set_Charmap(face, charmap); + } else { + /* No character set found! */ + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return "No character set found"; + } #ifndef JISX0208 - if (font->have_char_map_sjis) { + if (encoding == gdFTEX_Shift_JIS) { #endif - tmpstr = (char *) gdMalloc(BUFSIZ); - any2eucjp(tmpstr, string, BUFSIZ); - next = tmpstr; + if ((tmpstr = (char *)gdMalloc(BUFSIZ))) { + any2eucjp(tmpstr, string, BUFSIZ); + next = tmpstr; + } else { + next = string; + } #ifndef JISX0208 - } else { - next = string; - } + } else { + next = string; + } #endif - i = 0; - while (*next) { - ch = *next; - - /* carriage returns */ - if (ch == '\r') { - penf.x = 0; - x1 = (int)(- penf.y * sin_a + 32) / 64; - y1 = (int)(- penf.y * cos_a + 32) / 64; - pen.x = pen.y = 0; - previous = 0; /* clear kerning flag */ - next++; - continue; - } - /* newlines */ - if (ch == '\n') { - if (!*(++next)) break; - /* 2.0.13: reset penf.x. Christopher J. Grayce */ - penf.x = 0; - penf.y -= (long)(face->size->metrics.height * linespace); - penf.y = (penf.y - 32) & -64; /* round to next pixel row */ - x1 = (int)(- penf.y * sin_a + 32) / 64; - y1 = (int)(- penf.y * cos_a + 32) / 64; - pen.x = pen.y = 0; - previous = 0; /* clear kerning flag */ - continue; - } - -/* EAM DEBUG */ -#if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2))) - if (font->face->family_name && font->face->charmap->encoding && - font->face->charmap->encoding == FT_ENCODING_MS_SYMBOL && strcmp(font->face->family_name, "Symbol") == 0) { - /* I do not know the significance of the constant 0xf000. - * It was determined by inspection of the character codes - * stored in Microsoft font symbol. - * Added by Pierre (pajoye@php.net): - * Convert to the Symbol glyph range only for a Symbol family member - */ - len = gdTcl_UtfToUniChar (next, &ch); - ch |= 0xf000; - next += len; - } else -#endif /* Freetype 2.1 or better */ -/* EAM DEBUG */ - - switch (m) { - case gdFTEX_Unicode: - if (font->have_char_map_unicode) { - /* use UTF-8 mapping from ASCII */ - len = gdTcl_UtfToUniChar(next, &ch); - next += len; - } - break; - case gdFTEX_Shift_JIS: - if (font->have_char_map_sjis) { - unsigned char c; - int jiscode; - c = *next; - if (0xA1 <= c && c <= 0xFE) { - next++; - jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F); - - ch = (jiscode >> 8) & 0xFF; - jiscode &= 0xFF; - - if (ch & 1) { - jiscode += 0x40 - 0x21; - } else { - jiscode += 0x9E - 0x21; - } - - if (jiscode >= 0x7F) { - jiscode++; - } - ch = (ch - 0x21) / 2 + 0x81; - if (ch >= 0xA0) { - ch += 0x40; - } - - ch = (ch << 8) + jiscode; - } else { - ch = c & 0xFF; /* don't extend sign */ - } - if (*next) next++; - } - break; - case gdFTEX_Big5: { - /* - * Big 5 mapping: - * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref: - * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs - */ - ch = (*next) & 0xFF; /* don't extend sign */ - next++; - if (ch >= 161 /* first code of JIS-8 pair */ - && *next) { /* don't advance past '\0' */ - /* TBB: Fix from Kwok Wah On: & 255 needed */ - ch = (ch * 256) + ((*next) & 255); - next++; - } - } - break; - } - - /* set rotation transform */ - FT_Set_Transform(face, &matrix, NULL); - /* Convert character code to glyph index */ - glyph_index = FT_Get_Char_Index(face, ch); - - /* retrieve kerning distance and move pen position */ - if (use_kerning && previous && glyph_index) { - FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta); - pen.x += (int)(delta.x * cos_a); - pen.y -= (int)(delta.x * sin_a); - penf.x += delta.x; - } - - if (brect) { /* only if need brect */ - /* load glyph image into the slot (erase previous one) */ - if (FT_Load_Glyph(face, glyph_index, render_mode | FT_LOAD_IGNORE_TRANSFORM)) { - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Problem loading glyph"; - } - - /* transform glyph image */ - if (FT_Get_Glyph(slot, &image)) { - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Problem loading glyph"; - } + oldpenf.x = oldpenf.y = 0; /* for postscript xshow operator */ + penf.x = penf.y = 0; /* running position of non-rotated glyphs */ + text = (uint32_t *)gdCalloc(sizeof(uint32_t), strlen(next)); + i = 0; + while (*next) { + int len; + ch = *next; + switch (encoding) { + case gdFTEX_Unicode: { + /* use UTF-8 mapping from ASCII */ + len = gdTcl_UtfToUniChar(next, &ch); + /* EAM DEBUG */ + /* TBB: get this exactly right: 2.1.3 *or better*, all possible + * cases. */ + /* 2.0.24: David R. Morrison: use the more complete ifdef here. */ + if (charmap->encoding == FT_ENCODING_MS_SYMBOL) { + /* I do not know the significance of the constant 0xf000. */ + /* It was determined by inspection of the character codes */ + /* stored in Microsoft font symbol.ttf */ + ch |= 0xf000; + } + /* EAM DEBUG */ + next += len; + } break; + case gdFTEX_Shift_JIS: { + unsigned char c; + int jiscode; + c = *next; + if (0xA1 <= c && c <= 0xFE) { + next++; + jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F); + + ch = (jiscode >> 8) & 0xFF; + jiscode &= 0xFF; + + if (ch & 1) + jiscode += 0x40 - 0x21; + else + jiscode += 0x9E - 0x21; + + if (jiscode >= 0x7F) + jiscode++; + ch = (ch - 0x21) / 2 + 0x81; + if (ch >= 0xA0) + ch += 0x40; + + ch = (ch << 8) + jiscode; + } else { + ch = c & 0xFF; /* don't extend sign */ + } + if (*next) + next++; + } break; + case gdFTEX_Big5: { + /* + * Big 5 mapping: + * use "JIS-8 half-width katakana" coding from 8-bit characters. + * Ref: + * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs + */ + ch = (*next) & 0xFF; /* don't extend sign */ + next++; + if (ch >= 161 /* first code of JIS-8 pair */ + && *next) { + /* don't advance past '\0' */ + /* TBB: Fix from Kwok Wah On: & 255 needed */ + ch = (ch * 256) + ((*next) & 255); + next++; + } + } break; + + case gdFTEX_Adobe_Custom: + default: + ch &= 0xFF; + next++; + break; + } + text[i] = ch; + i++; + } + + FT_Activate_Size(platform_independent); + + count = textLayout(text, i, face, strex, &info); + + if (count < 0) { + gdFree(text); + gdFree(tmpstr); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + if (info) { + gdFree(info); + } + return "Problem doing text layout"; + } + + for (i = 0; i < count; i++) { + FT_Activate_Size(platform_independent); + + ch = text[info[i].cluster]; + + /* carriage returns */ + if (ch == '\r') { + penf.x = 0; + continue; + } + + /* newlines */ + if (ch == '\n') { + /* 2.0.13: reset penf.x. Christopher J. Grayce */ + penf.x = 0; + penf.y += linespace * ptsize * 64 * METRIC_RES / 72; + penf.y &= ~63; /* round down to 1/METRIC_RES */ + continue; + } + + glyph_index = info[i].index; + /* When we know the position of the second or subsequent character, + save the (kerned) advance from the preceeding character in the + xshow vector */ + if (i && strex && (strex->flags & gdFTEX_XSHOW)) { + /* make sure we have enough allocation for two numbers + so we don't have to recheck for the terminating number */ + if (!xshow_alloc) { + xshow_alloc = 100; + strex->xshow = gdMalloc(xshow_alloc); + if (!strex->xshow) { + if (tmpstr) + gdFree(tmpstr); + gdFree(text); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + gdFree(info); + return "Problem allocating memory"; + } + xshow_pos = 0; + } else if (xshow_pos + 20 > xshow_alloc) { + xshow_alloc += 100; + strex->xshow = gdReallocEx(strex->xshow, xshow_alloc); + if (!strex->xshow) { + if (tmpstr) + gdFree(tmpstr); + gdFree(text); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + gdFree(info); + return "Problem allocating memory"; + } + } + xshow_pos += sprintf(strex->xshow + xshow_pos, "%g ", + (double)(penf.x - oldpenf.x) * hdpi / (64 * METRIC_RES)); + } + oldpenf.x = penf.x; + +#ifdef HAVE_LIBRAQM + /* Keep RAQM brect/layout metrics in unrotated text space. */ + FT_Set_Transform(face, NULL, NULL); +#endif - FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox); - glyph_bbox.xMin += penf.x; - glyph_bbox.yMin += penf.y; - glyph_bbox.xMax += penf.x; - glyph_bbox.yMax += penf.y; - if (ch == ' ') { /* special case for trailing space */ - glyph_bbox.xMax += slot->metrics.horiAdvance; - } - if (!i) { /* if first character, init BB corner values */ - bbox.xMin = glyph_bbox.xMin; - bbox.yMin = glyph_bbox.yMin; - bbox.xMax = glyph_bbox.xMax; - bbox.yMax = glyph_bbox.yMax; + /* load glyph image into the slot (erase previous one) */ + err = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (err) { + if (tmpstr) + gdFree(tmpstr); + gdFree(text); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + gdFree(info); + return "Problem loading glyph"; + } + + horiAdvance = info[i].x_advance; + vertAdvance = info[i].y_advance; + + if (brect) { + /* only if need brect */ + + glyph_min.x = penf.x + slot->metrics.horiBearingX; + glyph_min.y = penf.y - slot->metrics.horiBearingY; + +#if 0 + if (ch == ' ') { /* special case for trailing space */ + glyph_max.x = penf.x + horiAdvance; } else { - if (bbox.xMin > glyph_bbox.xMin) { - bbox.xMin = glyph_bbox.xMin; - } - if (bbox.yMin > glyph_bbox.yMin) { - bbox.yMin = glyph_bbox.yMin; - } - if (bbox.xMax < glyph_bbox.xMax) { - bbox.xMax = glyph_bbox.xMax; - } - if (bbox.yMax < glyph_bbox.yMax) { - bbox.yMax = glyph_bbox.yMax; - } - } - i++; - } - - /* increment (unrotated) pen position */ - penf.x += slot->metrics.horiAdvance; - - if (render) { - if (!brect || angle != 0) { - /* reload the rotated glyph (for bbox we needed FT_LOAD_IGNORE_TRANSFORM - bbox is rotated later) */ - FT_Done_Glyph(image); - - /* load glyph image into the slot (erase previous one) */ - if (FT_Load_Glyph(face, glyph_index, render_mode)) { - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Problem loading glyph"; - } - - /* transform glyph image */ - if (FT_Get_Glyph(slot, &image)) { - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Problem loading glyph"; - } - } - - if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) { - FT_Done_Glyph(image); - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return "Problem rendering glyph"; + glyph_max.x = glyph_min.x + slot->metrics.width; } +#else + glyph_max.x = penf.x + horiAdvance; +#endif + glyph_max.y = glyph_min.y + slot->metrics.height; + + if (i == 0) { + total_min = glyph_min; + total_max = glyph_max; + } else { + if (glyph_min.x < total_min.x) + total_min.x = glyph_min.x; + if (glyph_min.y < total_min.y) + total_min.y = glyph_min.y; + if (glyph_max.x > total_max.x) + total_max.x = glyph_max.x; + if (glyph_max.y > total_max.y) + total_max.y = glyph_max.y; + } + } + + if (render) { + FT_Activate_Size(platform_specific); + +#ifdef HAVE_LIBRAQM + /* Render rotated glyph bitmaps while RAQM pen advances remain in + * layout space. */ + FT_Set_Transform(face, &matrix, NULL); +#endif - /* now, draw to our target surface */ - bm = (FT_BitmapGlyph) image; - gdft_draw_bitmap(tc_cache, im, fg, bm->bitmap, x + x1 + ((pen.x + 31) >> 6) + bm->left, y + y1 + ((pen.y + 31) >> 6) - bm->top); - } - - /* record current glyph index for kerning */ - previous = glyph_index; - - /* increment pen position */ - pen.x += image->advance.x >> 10; - pen.y -= image->advance.y >> 10; - - FT_Done_Glyph(image); - } - - if (brect) { /* only if need brect */ - /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */ - double d1 = sin (angle + 0.78539816339744830962); - double d2 = sin (angle - 0.78539816339744830962); - - /* rotate bounding rectangle (at 0, 0) */ - brect[0] = (int) (bbox.xMin * cos_a - bbox.yMin * sin_a); - brect[1] = (int) (bbox.xMin * sin_a + bbox.yMin * cos_a); - brect[2] = (int) (bbox.xMax * cos_a - bbox.yMin * sin_a); - brect[3] = (int) (bbox.xMax * sin_a + bbox.yMin * cos_a); - brect[4] = (int) (bbox.xMax * cos_a - bbox.yMax * sin_a); - brect[5] = (int) (bbox.xMax * sin_a + bbox.yMax * cos_a); - brect[6] = (int) (bbox.xMin * cos_a - bbox.yMax * sin_a); - brect[7] = (int) (bbox.xMin * sin_a + bbox.yMax * cos_a); - - /* scale, round and offset brect */ - brect[0] = x + gdroundupdown(brect[0], d2 > 0); - brect[1] = y - gdroundupdown(brect[1], d1 < 0); - brect[2] = x + gdroundupdown(brect[2], d1 > 0); - brect[3] = y - gdroundupdown(brect[3], d2 > 0); - brect[4] = x + gdroundupdown(brect[4], d2 < 0); - brect[5] = y - gdroundupdown(brect[5], d1 > 0); - brect[6] = x + gdroundupdown(brect[6], d1 < 0); - brect[7] = y - gdroundupdown(brect[7], d2 < 0); - } - - if (tmpstr) { - gdFree(tmpstr); - } - gdCacheDelete(tc_cache); - gdMutexUnlock(gdFontCacheMutex); - return (char *) NULL; + /* load glyph again into the slot (erase previous one) - this time + * with scaling */ + err = FT_Load_Glyph(face, glyph_index, render_mode); + if (err) { + if (tmpstr) + gdFree(tmpstr); + gdFree(text); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + gdFree(info); + return "Problem loading glyph"; + } + + /* load and transform glyph image */ + FT_Get_Glyph(slot, &image); + + if (image->format != ft_glyph_format_bitmap) { + err = FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1); + if (err) { + FT_Done_Glyph(image); + if (tmpstr) + gdFree(tmpstr); + gdFree(text); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + gdFree(info); + return "Problem rendering glyph"; + } + } + + /* now, draw to our target surface */ + bm = (FT_BitmapGlyph)image; + /* position rounded down to nearest pixel at current dpi + (the estimate was rounded up to next 1/METRIC_RES, so this should + fit) */ + FT_Pos pen_x = penf.x + info[i].x_offset; + FT_Pos pen_y = penf.y - info[i].y_offset; + gdft_draw_bitmap( + tc_cache, im, fg, bm->bitmap, + (int)(x + (pen_x * cos_a + pen_y * sin_a) * hdpi / (METRIC_RES * 64) + bm->left), + (int)(y - (pen_x * sin_a - pen_y * cos_a) * vdpi / (METRIC_RES * 64) - bm->top)); + + FT_Done_Glyph(image); + } + + penf.x += horiAdvance; + penf.y += vertAdvance; + } + + gdFree(text); + if (info) { + gdFree(info); + } + + /* Save the (unkerned) advance from the last character in the xshow vector + */ + if (strex && (strex->flags & gdFTEX_XSHOW) && strex->xshow) { + sprintf(strex->xshow + xshow_pos, "%g", + (double)(penf.x - oldpenf.x) * hdpi / (64 * METRIC_RES)); + } + + if (brect) { + /* only if need brect */ + double scalex = (double)hdpi / (64 * METRIC_RES); + double scaley = (double)vdpi / (64 * METRIC_RES); + + /* rotate bounding rectangle, scale and round to int pixels, and + * translate */ + brect[0] = x + (total_min.x * cos_a + total_max.y * sin_a) * scalex; + brect[1] = y - (total_min.x * sin_a - total_max.y * cos_a) * scaley; + brect[2] = x + (total_max.x * cos_a + total_max.y * sin_a) * scalex; + brect[3] = y - (total_max.x * sin_a - total_max.y * cos_a) * scaley; + brect[4] = x + (total_max.x * cos_a + total_min.y * sin_a) * scalex; + brect[5] = y - (total_max.x * sin_a - total_min.y * cos_a) * scaley; + brect[6] = x + (total_min.x * cos_a + total_min.y * sin_a) * scalex; + brect[7] = y - (total_min.x * sin_a - total_min.y * cos_a) * scaley; + } + + FT_Done_Size(platform_independent); + if (render) + FT_Done_Size(platform_specific); + + if (tmpstr) + gdFree(tmpstr); + gdCacheDelete(tc_cache); + gdMutexUnlock(gdFontCacheMutex); + return (char *)NULL; } #endif /* HAVE_LIBFREETYPE */ + +#ifdef HAVE_LIBFONTCONFIG +/* Code to find font path, with special mapping for Postscript font names. + * + * Dag Lem + */ + +#include + +/* #define NO_POSTSCRIPT_ALIAS 1 */ +#ifndef NO_POSTSCRIPT_ALIAS +typedef struct _PostscriptAlias { + char *name; + char *family; + char *style; +} PostscriptAlias; + +/* This table maps standard Postscript font names to URW Type 1 fonts. + The mapping is converted from Ghostscript (Fontmap.GS) + for use with fontconfig. */ +static PostscriptAlias postscript_alias[] = { + {"AvantGarde-Book", "URW Gothic L", "Book"}, + {"AvantGarde-BookOblique", "URW Gothic L", "Book Oblique"}, + {"AvantGarde-Demi", "URW Gothic L", "Demi"}, + {"AvantGarde-DemiOblique", "URW Gothic L", "Demi Oblique"}, + + {"Bookman-Demi", "URW Bookman L", "Demi Bold"}, + {"Bookman-DemiItalic", "URW Bookman L", "Demi Bold Italic"}, + {"Bookman-Light", "URW Bookman L", "Light"}, + {"Bookman-LightItalic", "URW Bookman L", "Light Italic"}, + + {"Courier", "Nimbus Mono L", "Regular"}, + {"Courier-Oblique", "Nimbus Mono L", "Regular Oblique"}, + {"Courier-Bold", "Nimbus Mono L", "Bold"}, + {"Courier-BoldOblique", "Nimbus Mono L", "Bold Oblique"}, + + {"Helvetica", "Nimbus Sans L", "Regular"}, + {"Helvetica-Oblique", "Nimbus Sans L", "Regular Italic"}, + {"Helvetica-Bold", "Nimbus Sans L", "Bold"}, + {"Helvetica-BoldOblique", "Nimbus Sans L", "Bold Italic"}, + + {"Helvetica-Narrow", "Nimbus Sans L", "Regular Condensed"}, + {"Helvetica-Narrow-Oblique", "Nimbus Sans L", "Regular Condensed Italic"}, + {"Helvetica-Narrow-Bold", "Nimbus Sans L", "Bold Condensed"}, + {"Helvetica-Narrow-BoldOblique", "Nimbus Sans L", "Bold Condensed Italic"}, + + {"NewCenturySchlbk-Roman", "Century Schoolbook L", "Roman"}, + {"NewCenturySchlbk-Italic", "Century Schoolbook L", "Italic"}, + {"NewCenturySchlbk-Bold", "Century Schoolbook L", "Bold"}, + {"NewCenturySchlbk-BoldItalic", "Century Schoolbook L", "Bold Italic"}, + + {"Palatino-Roman", "URW Palladio L", "Roman"}, + {"Palatino-Italic", "URW Palladio L", "Italic"}, + {"Palatino-Bold", "URW Palladio L", "Bold"}, + {"Palatino-BoldItalic", "URW Palladio L", "Bold Italic"}, + + {"Symbol", "Standard Symbols L", "Regular"}, + + {"Times-Roman", "Nimbus Roman No9 L", "Regular"}, + {"Times-Italic", "Nimbus Roman No9 L", "Regular Italic"}, + {"Times-Bold", "Nimbus Roman No9 L", "Medium"}, + {"Times-BoldItalic", "Nimbus Roman No9 L", "Medium Italic"}, + + {"ZapfChancery-MediumItalic", "URW Chancery L", "Medium Italic"}, + + {"ZapfDingbats", "Dingbats", ""}, +}; +#endif + +static FcPattern *find_font(FcPattern *pattern) +{ + FcResult result; + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcConfigSubstitute(0, pattern, FcMatchFont); + FcDefaultSubstitute(pattern); + + return FcFontMatch(0, pattern, &result); +} + +#ifndef NO_POSTSCRIPT_ALIAS +static char *find_postscript_font(FcPattern **fontpattern, char *fontname) +{ + FcPattern *font = NULL; + size_t i; + + *fontpattern = NULL; + for (i = 0; i < sizeof(postscript_alias) / sizeof(*postscript_alias); i++) { + if (strcmp(fontname, postscript_alias[i].name) == 0) { + FcChar8 *family; + + FcPattern *pattern = + FcPatternBuild(0, FC_FAMILY, FcTypeString, postscript_alias[i].family, FC_STYLE, + FcTypeString, postscript_alias[i].style, (char *)0); + font = find_font(pattern); + FcPatternDestroy(pattern); + + if (!font) + return "fontconfig: Couldn't find font."; + if (FcPatternGetString(font, FC_FAMILY, 0, &family) != FcResultMatch) { + FcPatternDestroy(font); + return "fontconfig: Couldn't retrieve font family name."; + } + + /* Check whether we got the font family we wanted. */ + if (strcmp((const char *)family, postscript_alias[i].family) != 0) { + FcPatternDestroy(font); + return "fontconfig: Didn't find expected font family. Perhaps " + "URW Type 1 fonts need installing?"; + } + break; + } + } + + *fontpattern = font; + return NULL; +} +#endif + +static char *font_pattern(char **fontpath, char *fontpattern) +{ + FcPattern *font = NULL; + FcChar8 *file; + FcPattern *pattern; +#ifndef NO_POSTSCRIPT_ALIAS + char *error; +#endif + + *fontpath = NULL; +#ifndef NO_POSTSCRIPT_ALIAS + error = find_postscript_font(&font, fontpattern); + + if (!font) { + if (error) + return error; +#endif + pattern = FcNameParse((const FcChar8 *)fontpattern); + font = find_font(pattern); + FcPatternDestroy(pattern); +#ifndef NO_POSTSCRIPT_ALIAS + } +#endif + + if (!font) + return "fontconfig: Couldn't find font."; + if (FcPatternGetString(font, FC_FILE, 0, &file) != FcResultMatch) { + FcPatternDestroy(font); + return "fontconfig: Couldn't retrieve font file name."; + } else { + const unsigned int file_len = strlen((const char *)file); + + *fontpath = (char *)gdMalloc(file_len + 1); + if (*fontpath == NULL) { + return "could not alloc font path"; + } + memcpy(*fontpath, (const char *)file, file_len); + (*fontpath)[file_len] = 0; + } + FcPatternDestroy(font); + + return NULL; +} + +#endif /* HAVE_LIBFONTCONFIG */ + +#ifdef HAVE_LIBFREETYPE +/* Look up font using font names as file names. */ +static char *font_path(char **fontpath, char *name_list) +{ + int font_found = 0; + char *fontsearchpath, *fontlist; + char *fullname = NULL; + char *name, *dir; + char *path; + char *strtok_ptr = NULL; + const unsigned int name_list_len = strlen(name_list); + + /* + * Search the pathlist for any of a list of font names. + */ + *fontpath = NULL; + fontsearchpath = getenv("GDFONTPATH"); + if (!fontsearchpath) + fontsearchpath = DEFAULT_FONTPATH; + path = (char *)gdMalloc(sizeof(char) * strlen(fontsearchpath) + 1); + if (path == NULL) { + return "could not alloc full list of fonts"; + } + path[0] = 0; + + fontlist = (char *)gdMalloc(name_list_len + 1); + if (fontlist == NULL) { + gdFree(path); + return "could not alloc full list of fonts"; + } + memcpy(fontlist, name_list, name_list_len); + fontlist[name_list_len] = 0; + + /* + * Must use gd_strtok_r else pointer corrupted by strtok in nested loop. + */ + for (name = gd_strtok_r(fontlist, LISTSEPARATOR, &strtok_ptr); name; + name = gd_strtok_r(0, LISTSEPARATOR, &strtok_ptr)) { + char *path_ptr = NULL; + + /* make a fresh copy each time - strtok corrupts it. */ + sprintf(path, "%s", fontsearchpath); + /* + * Allocate an oversized buffer that is guaranteed to be + * big enough for all paths to be tested. + */ + /* 2.0.22: Thorben Kundinger: +8 is needed, not +6. */ + fullname = gdReallocEx(fullname, strlen(fontsearchpath) + strlen(name) + 8); + if (!fullname) { + gdFree(fontlist); + gdFree(path); + return "could not alloc full path of font"; + } + /* if name is an absolute or relative pathname then test directly */ + if (GD_FT_IS_PATH(name)) { + sprintf(fullname, "%s", name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } + } + for (dir = gd_strtok_r(path, PATHSEPARATOR, &path_ptr); dir; + dir = gd_strtok_r(0, PATHSEPARATOR, &path_ptr)) { + if (strchr(name, '.')) { + sprintf(fullname, "%s/%s", dir, name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } else { + continue; + } + } + sprintf(fullname, "%s/%s.ttf", dir, name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } + sprintf(fullname, "%s/%s.pfa", dir, name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } + sprintf(fullname, "%s/%s.pfb", dir, name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } + sprintf(fullname, "%s/%s.dfont", dir, name); + if (GD_FT_ACCESS(fullname, R_OK) == 0) { + font_found++; + break; + } + } + + if (font_found) + break; + } + gdFree(path); + if (fontlist != NULL) { + gdFree(fontlist); + fontlist = NULL; + } + if (!font_found) { + gdFree(fullname); + return "Could not find/open font"; + } + + *fontpath = fullname; + return NULL; +} +#endif + +/** + * Function: gdFTUseFontConfig + * + * Enable or disable fontconfig by default. + * + * If GD is built without libfontconfig support, this function is a NOP. + * + * Parameters: + * flag - Zero to disable, nonzero to enable. + * + * See also: + * - + */ +BGD_DECLARE(int) gdFTUseFontConfig(int flag) +{ +#ifdef HAVE_LIBFONTCONFIG + fontConfigFlag = flag; + return 1; +#else + (void)flag; + return 0; +#endif /* HAVE_LIBFONTCONFIG */ +} diff --git a/ext/gd/libgd/gdfx.c b/ext/gd/libgd/gdfx.c new file mode 100644 index 000000000000..3acb0afcc113 --- /dev/null +++ b/ext/gd/libgd/gdfx.c @@ -0,0 +1,472 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "gd.h" +#include "gd_errors.h" +#include "gd_intern.h" +#include + +/* In tests this is sufficient to prevent obvious artifacts */ +#define MAG 4 + +#define PI 3.141592 +#define DEG2RAD(x) ((x) * PI / 180.) + +#define MAX4(x, y, z, w) ((MAX((x), (y))) > (MAX((z), (w))) ? (MAX((x), (y))) : (MAX((z), (w)))) +#define MIN4(x, y, z, w) ((MIN((x), (y))) < (MIN((z), (w))) ? (MIN((x), (y))) : (MIN((z), (w)))) + +#define MAXX(x) MAX4(x[0], x[2], x[4], x[6]) +#define MINX(x) MIN4(x[0], x[2], x[4], x[6]) +#define MAXY(x) MAX4(x[1], x[3], x[5], x[7]) +#define MINY(x) MIN4(x[1], x[3], x[5], x[7]) + +/** + * Function: gdImageStringFTCircle + * + * Draw text curved along the top and bottom of a circular area of an image. + * + * Parameters: + * im - The image to draw onto. + * cx - The x-coordinate of the center of the circular area. + * cy - The y-coordinate of the center of the circular area. + * radius - The radius of the circular area. + * textRadius - The height of each character; if textRadius is 1/2 of radius, + * characters extend halfway from the edge to the center. + * fillPortion - The percentage of the 180 degrees of the circular area + * assigned to each section of text, that is actually occupied + * by text. The value has to be in range 0.0 to 1.0, with useful + * values from about 0.4 to 0.9; 0.9 looks better than 1.0 which + * is rather crowded. + * font - The fontlist that is passed to . + * points - The point size, which functions as a hint. Although the size + * of the text is determined by radius, textRadius and + * fillPortion, a point size that 'hints' appropriately should be + * passed. If it's known that the text will be large, a large + * point size such as 24.0 should be passed to get the best + * results. + * top - The text to draw clockwise at the top of the circular area. + * bottom - The text to draw counterclockwise at the bottom of the + * circular area. + * fgcolor - The font color. + * + * Returns: + * NULL on success, or an error string on failure. + */ +BGD_DECLARE(char *) +gdImageStringFTCircle(gdImagePtr im, int cx, int cy, double radius, double textRadius, + double fillPortion, char *font, double points, char *top, char *bottom, + int fgcolor) +{ + char *err; + int w; + int brect[8]; + int sx1, sx2, sy1, sy2, sx, sy; + int x, y; + int fr, fg, fb, fa; + int ox, oy; + double prop; + gdImagePtr im1; + gdImagePtr im2; + gdImagePtr im3; + /* obtain brect so that we can size the image */ + err = gdImageStringFT((gdImagePtr)NULL, &brect[0], 0, font, points * MAG, 0, 0, 0, bottom); + if (err) { + return err; + } + sx1 = MAXX(brect) - MINX(brect) + 6; + sy1 = MAXY(brect) - MINY(brect) + 6; + err = gdImageStringFT((gdImagePtr)NULL, &brect[0], 0, font, points * MAG, 0, 0, 0, top); + if (err) { + return err; + } + sx2 = MAXX(brect) - MINX(brect) + 6; + sy2 = MAXY(brect) - MINY(brect) + 6; + /* Pad by 4 pixels to allow for slight errors + observed in the bounding box returned by freetype */ + if (sx1 > sx2) { + sx = sx1 * 2 + 4; + } else { + sx = sx2 * 2 + 4; + } + if (sy1 > sy2) { + sy = sy1; + } else { + sy = sy2; + } + im1 = gdImageCreateTrueColor(sx, sy); + if (!im1) { + return "could not create first image"; + } + err = gdImageStringFT(im1, 0, gdTrueColor(255, 255, 255), font, points * MAG, 0, + ((sx / 2) - sx1) / 2, points * MAG, bottom); + if (err) { + gdImageDestroy(im1); + return err; + } + /* We don't know the descent, which would be needed to do this + with the angle parameter. Instead, implement a simple + flip operation ourselves. */ + err = gdImageStringFT(im1, 0, gdTrueColor(255, 255, 255), font, points * MAG, 0, + sx / 2 + ((sx / 2) - sx2) / 2, points * MAG, top); + if (err) { + gdImageDestroy(im1); + return err; + } + /* Flip in place is tricky, be careful not to double-swap things */ + if (sy & 1) { + for (y = 0; (y <= (sy / 2)); y++) { + int xlimit = sx - 2; + if (y == (sy / 2)) { + /* If there is a "middle" row, be careful + not to swap twice! */ + xlimit -= (sx / 4); + } + for (x = (sx / 2) + 2; (x < xlimit); x++) { + int t; + int ox = sx - x + (sx / 2) - 1; + int oy = sy - y - 1; + t = im1->tpixels[oy][ox]; + im1->tpixels[oy][ox] = im1->tpixels[y][x]; + im1->tpixels[y][x] = t; + } + } + } else { + for (y = 0; (y < (sy / 2)); y++) { + int xlimit = sx - 2; + for (x = (sx / 2) + 2; (x < xlimit); x++) { + int t; + int ox = sx - x + (sx / 2) - 1; + int oy = sy - y - 1; + t = im1->tpixels[oy][ox]; + im1->tpixels[oy][ox] = im1->tpixels[y][x]; + im1->tpixels[y][x] = t; + } + } + } +#if STEP_PNGS + { + FILE *out = fopen("gdfx1.png", "wb"); + gdImagePng(im1, out); + fclose(out); + } +#endif /* STEP_PNGS */ + /* Resample taller; the exact proportions of the text depend on the + ratio of textRadius to radius, and the value of fillPortion */ + if (sx > sy * 10) { + w = sx; + } else { + w = sy * 10; + } + im2 = gdImageCreateTrueColor(w, w); + if (!im2) { + gdImageDestroy(im1); + return "could not create resampled image"; + } + prop = textRadius / radius; + gdImageCopyResampled(im2, im1, gdImageSX(im2) * (1.0 - fillPortion) / 4, sy * 10 * (1.0 - prop), + 0, 0, gdImageSX(im2) * fillPortion / 2, sy * 10 * prop, gdImageSX(im1) / 2, + gdImageSY(im1)); + gdImageCopyResampled(im2, im1, (gdImageSX(im2) / 2) + gdImageSX(im2) * (1.0 - fillPortion) / 4, + sy * 10 * (1.0 - prop), gdImageSX(im1) / 2, 0, + gdImageSX(im2) * fillPortion / 2, sy * 10 * prop, gdImageSX(im1) / 2, + gdImageSY(im1)); +#if STEP_PNGS + { + FILE *out = fopen("gdfx2.png", "wb"); + gdImagePng(im2, out); + fclose(out); + } +#endif /* STEP_PNGS */ + + gdImageDestroy(im1); + + /* Ready to produce a circle */ + im3 = gdImageSquareToCircle(im2, radius); + if (im3 == NULL) { + gdImageDestroy(im2); + return 0; + } + gdImageDestroy(im2); + /* Now blend im3 with the destination. Cheat a little. The + source (im3) is white-on-black, so we can use the + red component as a basis for alpha as long as we're + careful to shift off the extra bit and invert + (alpha ranges from 0 to 127 where 0 is OPAQUE). + Also be careful to allow for an alpha component + in the fgcolor parameter itself (gug!) */ + fr = gdTrueColorGetRed(fgcolor); + fg = gdTrueColorGetGreen(fgcolor); + fb = gdTrueColorGetBlue(fgcolor); + fa = gdTrueColorGetAlpha(fgcolor); + ox = cx - (im3->sx / 2); + oy = cy - (im3->sy / 2); + for (y = 0; (y < im3->sy); y++) { + for (x = 0; (x < im3->sx); x++) { + int a = gdTrueColorGetRed(im3->tpixels[y][x]) >> 1; + a *= (127 - fa); + a /= 127; + a = 127 - a; + gdImageSetPixel(im, x + ox, y + oy, gdTrueColorAlpha(fr, fg, fb, a)); + } + } + gdImageDestroy(im3); + return 0; +} + +#if GDFX_MAIN + +int main(int argc, char *argv[]) +{ + FILE *in; + FILE *out; + gdImagePtr im; + int radius; + /* Create an image of text on a circle, with an + alpha channel so that we can copy it onto a + background */ + in = fopen("eleanor.jpg", "rb"); + if (!in) { + im = gdImageCreateTrueColor(300, 300); + } else { + im = gdImageCreateFromJpeg(in); + fclose(in); + } + if (gdImageSX(im) < gdImageSY(im)) { + radius = gdImageSX(im) / 2; + } else { + radius = gdImageSY(im) / 2; + } + gdImageStringFTCircle(im, gdImageSX(im) / 2, gdImageSY(im) / 2, radius, radius / 2, 0.8, + "arial", 24, "top text", "bottom text", + gdTrueColorAlpha(240, 240, 255, 32)); + out = fopen("gdfx.png", "wb"); + if (!out) { + gd_error("Can't create gdfx.png\n"); + return 1; + } + gdImagePng(im, out); + fclose(out); + gdImageDestroy(im); + return 0; +} + +#endif /* GDFX_MAIN */ + +/* Note: don't change these */ +#define SUPER 2 +#define SUPERBITS1 1 +#define SUPERBITS2 2 + +/** + * Function: gdImageSquareToCircle + * + * Apply polar coordinate transformation to an image. + * + * The X axis of the original will be remapped to theta (angle) and the Y axis + * of the original will be remapped to rho (distance from center). + * + * Parameters: + * im - The image, which must be square, i.e. width == height. + * radius - The radius of the new image, i.e. width == height == radius * 2. + * + * Returns: + * The transformed image, or NULL on failure. + */ +BGD_DECLARE(gdImagePtr) +gdImageSquareToCircle(gdImagePtr im, int radius) +{ + int x, y; + double c; + gdImagePtr im2; + if (im->sx != im->sy) { + /* Source image must be square */ + return 0; + } + im2 = gdImageCreateTrueColor(radius * 2, radius * 2); + if (!im2) { + return 0; + } + /* Supersampling for a nicer result */ + c = (im2->sx / 2) * SUPER; + for (y = 0; (y < im2->sy * SUPER); y++) { + for (x = 0; (x < im2->sx * SUPER); x++) { + double rho = sqrt((x - c) * (x - c) + (y - c) * (y - c)); + int pix; + int cpix; + double theta; + double ox; + double oy; + int red, green, blue, alpha; + if (rho > c) { + continue; + } + theta = atan2(x - c, y - c) + PI / 2; + if (theta < 0) { + theta += 2 * PI; + } + /* Undo supersampling */ + oy = (rho * im->sx) / (im2->sx * SUPER / 2); + ox = theta * im->sx / (3.141592653 * 2); + pix = gdImageGetPixel(im, ox, oy); + cpix = im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1]; + red = (gdImageRed(im, pix) >> SUPERBITS2) + gdTrueColorGetRed(cpix); + green = (gdImageGreen(im, pix) >> SUPERBITS2) + gdTrueColorGetGreen(cpix); + blue = (gdImageBlue(im, pix) >> SUPERBITS2) + gdTrueColorGetBlue(cpix); + alpha = (gdImageAlpha(im, pix) >> SUPERBITS2) + gdTrueColorGetAlpha(cpix); + im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1] = + gdTrueColorAlpha(red, green, blue, alpha); + } + } + /* Restore full dynamic range, 0-63 yields 0-252. Replication of + first 2 bits in last 2 bits has the desired effect. Note + slightly different arithmetic for alpha which is 7-bit. + NOTE: only correct for SUPER == 2 */ + for (y = 0; (y < im2->sy); y++) { + for (x = 0; (x < im2->sx); x++) { + /* Copy first 2 bits to last 2 bits, matching the + dynamic range of the original cheaply */ + int cpix = im2->tpixels[y][x]; + + im2->tpixels[y][x] = gdTrueColorAlpha( + (gdTrueColorGetRed(cpix) & 0xFC) + ((gdTrueColorGetRed(cpix) & 0xC0) >> 6), + (gdTrueColorGetGreen(cpix) & 0xFC) + ((gdTrueColorGetGreen(cpix) & 0xC0) >> 6), + (gdTrueColorGetBlue(cpix) & 0xFC) + ((gdTrueColorGetBlue(cpix) & 0xC0) >> 6), + (gdTrueColorGetAlpha(cpix) & 0x7C) + ((gdTrueColorGetAlpha(cpix) & 0x60) >> 6)); + } + } + return im2; +} + +/* 2.0.16: Called by gdImageSharpen to avoid excessive code repetition + Added on 2003-11-19 by + Paul Troughton (paultroughtonieeeorg) + Given filter coefficents and colours of three adjacent pixels, +returns new colour for centre pixel +*/ + +static int gdImageSubSharpen(int pc, int c, int nc, float inner_coeff, float outer_coeff) +{ + float red, green, blue, alpha; + + red = inner_coeff * gdTrueColorGetRed(c) + + outer_coeff * (gdTrueColorGetRed(pc) + gdTrueColorGetRed(nc)); + green = inner_coeff * gdTrueColorGetGreen(c) + + outer_coeff * (gdTrueColorGetGreen(pc) + gdTrueColorGetGreen(nc)); + blue = inner_coeff * gdTrueColorGetBlue(c) + + outer_coeff * (gdTrueColorGetBlue(pc) + gdTrueColorGetBlue(nc)); + alpha = gdTrueColorGetAlpha(c); + + /* Clamping, as can overshoot bounds in either direction */ + if (red > 255.0f) { + red = 255.0f; + } + if (green > 255.0f) { + green = 255.0f; + } + if (blue > 255.0f) { + blue = 255.0f; + } + if (red < 0.0f) { + red = 0.0f; + } + if (green < 0.0f) { + green = 0.0f; + } + if (blue < 0.0f) { + blue = 0.0f; + } + + return gdTrueColorAlpha((int)red, (int)green, (int)blue, (int)alpha); +} + +/** + * Function: gdImageSharpen + * + * Sharpen an image. + * + * Uses a simple 3x3 convolution kernel and makes use of separability. + * It's faster, but less flexible, than full-blown unsharp masking. + * Silently does nothing to non-truecolor images and for pct<0, as it's not a + * useful blurring function. + * + * Parameters: + * pct - The sharpening percentage, which can be greater than 100. + * + * Author: + * Paul Troughton (paultroughtonieeeorg) + */ +BGD_DECLARE(void) +gdImageSharpen(gdImagePtr im, int pct) +{ + int x, y; + int sx, sy; + float inner_coeff, outer_coeff; + + sx = im->sx; + sy = im->sy; + + /* Must sum to 1 to avoid overall change in brightness. + * Scaling chosen so that pct=100 gives 1-D filter [-1 6 -1]/4, + * resulting in a 2-D filter [1 -6 1; -6 36 -6; 1 -6 1]/16, + * which gives noticeable, but not excessive, sharpening + */ + + outer_coeff = -pct / 400.0; + inner_coeff = 1 - 2 * outer_coeff; + + /* Don't try to do anything with non-truecolor images, as + pointless, + * nor for pct<=0, as small kernel size leads to nasty + artefacts when blurring + */ + if ((im->trueColor) && (pct > 0)) { + + /* First pass, 1-D convolution column-wise */ + for (x = 0; x < sx; x++) { + + /* pc is colour of previous pixel; c of the + current pixel and nc of the next */ + int pc, c, nc; + + /* Replicate edge pixel at image boundary */ + pc = gdImageGetPixel(im, x, 0); + + /* Stop looping before last pixel to avoid + conditional within loop */ + for (y = 0; y < sy - 1; y++) { + + c = gdImageGetPixel(im, x, y); + + nc = gdImageGetTrueColorPixel(im, x, y + 1); + + /* Update centre pixel to new colour */ + gdImageSetPixel(im, x, y, gdImageSubSharpen(pc, c, nc, inner_coeff, outer_coeff)); + + /* Save original colour of current + pixel for next time round */ + pc = c; + } + + /* Deal with last pixel, replicating current + pixel at image boundary */ + c = gdImageGetPixel(im, x, y); + gdImageSetPixel(im, x, y, gdImageSubSharpen(pc, c, c, inner_coeff, outer_coeff)); + } + + /* Second pass, 1-D convolution row-wise */ + for (y = 0; y < sy; y++) { + int pc, c; + pc = gdImageGetPixel(im, 0, y); + for (x = 0; x < sx - 1; x++) { + int c, nc; + c = gdImageGetPixel(im, x, y); + nc = gdImageGetTrueColorPixel(im, x + 1, y); + gdImageSetPixel(im, x, y, gdImageSubSharpen(pc, c, nc, inner_coeff, outer_coeff)); + pc = c; + } + c = gdImageGetPixel(im, x, y); + gdImageSetPixel(im, x, y, gdImageSubSharpen(pc, c, c, inner_coeff, outer_coeff)); + } + } +} diff --git a/ext/gd/libgd/gdfx.h b/ext/gd/libgd/gdfx.h new file mode 100644 index 000000000000..0de97cabf37c --- /dev/null +++ b/ext/gd/libgd/gdfx.h @@ -0,0 +1,21 @@ +#ifndef GDFX_H +#define GDFX_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +BGD_DECLARE(gdImagePtr) gdImageSquareToCircle(gdImagePtr im, int radius); + +BGD_DECLARE(char *) +gdImageStringFTCircle(gdImagePtr im, int cx, int cy, double radius, double textRadius, + double fillPortion, char *font, double points, char *top, char *bottom, + int fgcolor); + +BGD_DECLARE(void) gdImageSharpen(gdImagePtr im, int pct); + +#ifdef __cplusplus +} +#endif + +#endif /* GDFX_H */ diff --git a/ext/gd/libgd/gdhelpers.c b/ext/gd/libgd/gdhelpers.c index 44b79ac77f32..121123321ddc 100644 --- a/ext/gd/libgd/gdhelpers.c +++ b/ext/gd/libgd/gdhelpers.c @@ -4,71 +4,71 @@ #include "gd.h" #include "gdhelpers.h" + +#ifdef HAVE_LIBPNG +#include + +#endif #include #include /* TBB: gd_strtok_r is not portable; provide an implementation */ -#define SEP_TEST (separators[*((unsigned char *) s)]) +#define SEP_TEST (separators[*((unsigned char *)s)]) -char * -gd_strtok_r (char *s, char *sep, char **state) +char *gd_strtok_r(char *s, const char *sep, char **state) { - char separators[256]; - char *result = 0; - memset (separators, 0, sizeof (separators)); - while (*sep) - { - separators[*((unsigned char *) sep)] = 1; - sep++; - } - if (!s) - { - /* Pick up where we left off */ - s = *state; + char separators[256]; + char *result = 0; + memset(separators, 0, sizeof(separators)); + while (*sep) { + separators[*((const unsigned char *)sep)] = 1; + sep++; } - /* 1. EOS */ - if (!(*s)) - { - *state = s; - return 0; + if (!s) { + /* Pick up where we left off */ + s = *state; } - /* 2. Leading separators, if any */ - if (SEP_TEST) - { - do - { - s++; - } - while (SEP_TEST); - /* 2a. EOS after separators only */ - if (!(*s)) - { - *state = s; - return 0; - } + /* 1. EOS */ + if (!(*s)) { + *state = s; + return 0; } - /* 3. A token */ - result = s; - do - { - /* 3a. Token at end of string */ - if (!(*s)) - { - *state = s; - return result; - } - s++; + /* 2. Leading separators, if any */ + if (SEP_TEST) { + do { + s++; + } while (SEP_TEST); + /* 2a. EOS after separators only */ + if (!(*s)) { + *state = s; + return 0; + } } - while (!SEP_TEST); - /* 4. Terminate token and skip trailing separators */ - *s = '\0'; - do - { - s++; - } - while (SEP_TEST); - /* 5. Return token */ - *state = s; - return result; + /* 3. A token */ + result = s; + do { + /* 3a. Token at end of string */ + if (!(*s)) { + *state = s; + return result; + } + s++; + } while (!SEP_TEST); + /* 4. Terminate token and skip trailing separators */ + *s = '\0'; + do { + s++; + } while (SEP_TEST); + /* 5. Return token */ + *state = s; + return result; +} + +void *gdReallocEx(void *ptr, size_t size) +{ + void *newPtr = gdRealloc(ptr, size); + if (!newPtr && ptr) + gdFree(ptr); + return newPtr; } diff --git a/ext/gd/libgd/gdhelpers.h b/ext/gd/libgd/gdhelpers.h index 202ccfce7636..931c5fb83a73 100644 --- a/ext/gd/libgd/gdhelpers.h +++ b/ext/gd/libgd/gdhelpers.h @@ -1,30 +1,38 @@ #ifndef GDHELPERS_H #define GDHELPERS_H 1 -#include #include "php.h" +#include /* TBB: strtok_r is not universal; provide an implementation of it. */ -extern char *gd_strtok_r(char *s, char *sep, char **state); +char *gd_strtok_r(char *s, const char *sep, char **state); /* These functions wrap memory management. gdFree is - in gd.h, where callers can utilize it to correctly - free memory allocated by these functions with the - right version of free(). */ -#define gdCalloc(nmemb, size) ecalloc(nmemb, size) -#define gdMalloc(size) emalloc(size) -#define gdRealloc(ptr, size) erealloc(ptr, size) -#define gdEstrdup(ptr) estrdup(ptr) -#define gdFree(ptr) efree(ptr) -#define gdPMalloc(ptr) pemalloc(ptr, 1) -#define gdPFree(ptr) pefree(ptr, 1) -#define gdPEstrdup(ptr) pestrdup(ptr, 1) + in gd.h, where callers can utilize it to correctly + free memory allocated by these functions with the + right version of free(). */ +#define gdCalloc(nmemb, size) ecalloc((nmemb), (size)) +#define gdMalloc(size) emalloc(size) +#define gdRealloc(ptr, size) erealloc(ptr, size) +#define gdEstrdup(ptr) estrdup(ptr) +#define gdFree(ptr) efree(ptr) +#define gdPMalloc(ptr) pemalloc(ptr, 1) +#define gdPFree(ptr) pefree(ptr, 1) +#define gdPEstrdup(ptr) pestrdup(ptr, 1) + +/* The extended version of gdReallocEx will free *ptr if the + * realloc fails. */ +void *gdReallocEx(void *ptr, size_t size); + +/* Internal deterministic allocation hook used by the vector2d failure tests. */ +void *gdVector2dMalloc(size_t size); +void gdVector2dTestSetAllocationFailureCountdown(int countdown); /* Returns nonzero if multiplying the two quantities will - result in integer overflow. Also returns nonzero if - either quantity is negative. By Phil Knirsch based on - netpbm fixes by Alan Cox. */ + result in integer overflow. Also returns nonzero if + either quantity is negative. By Phil Knirsch based on + netpbm fixes by Alan Cox. */ int overflow2(int a, int b); int overflowMul3(int a, int b, int c); @@ -43,10 +51,9 @@ int overflowMul3(int a, int b, int c); #define gdMutexUnlock(x) #endif -#define DPCM2DPI(dpcm) (unsigned int)((dpcm)*2.54 + 0.5) -#define DPM2DPI(dpm) (unsigned int)((dpm)*0.0254 + 0.5) -#define DPI2DPCM(dpi) (unsigned int)((dpi)/2.54 + 0.5) -#define DPI2DPM(dpi) (unsigned int)((dpi)/0.0254 + 0.5) +#define DPCM2DPI(dpcm) (unsigned int)((dpcm) * 2.54 + 0.5) +#define DPM2DPI(dpm) (unsigned int)((dpm) * 0.0254 + 0.5) +#define DPI2DPCM(dpi) (unsigned int)((dpi) / 2.54 + 0.5) +#define DPI2DPM(dpi) (unsigned int)((dpi) / 0.0254 + 0.5) #endif /* GDHELPERS_H */ - diff --git a/ext/gd/libgd/gdkanji.c b/ext/gd/libgd/gdkanji.c index ef769f89badd..1c539310cee5 100644 --- a/ext/gd/libgd/gdkanji.c +++ b/ext/gd/libgd/gdkanji.c @@ -2,25 +2,62 @@ /* gdkanji.c (Kanji code converter) */ /* written by Masahito Yamaga (ma@yama-ga.com) */ -#include -#include -#include #include "gd.h" +#include "gd_errors.h" #include "gdhelpers.h" - +#include #include +#include +#include +#include #if defined(HAVE_ICONV_H) || defined(HAVE_ICONV) -#include #include +#include #endif #if defined(HAVE_ICONV_H) && !defined(HAVE_ICONV) #define HAVE_ICONV 1 #endif +#ifndef HAVE_ICONV_T_DEF +typedef void *iconv_t; +#endif + +#ifndef HAVE_ICONV +#define ICONV_CONST /**/ +iconv_t iconv_open(const char *, const char *); +size_t iconv(iconv_t, ICONV_CONST char **, size_t *, char **, size_t *); +int iconv_close(iconv_t); + +iconv_t iconv_open(const char *tocode, const char *fromcode) +{ + (void)tocode; + (void)fromcode; + return (iconv_t)(-1); +} + +size_t iconv(iconv_t cd, ICONV_CONST char **inbuf, size_t *inbytesleft, char **outbuf, + size_t *outbytesleft) +{ + (void)cd; + (void)inbuf; + (void)inbytesleft; + (void)outbuf; + (void)outbytesleft; + return 0; +} + +int iconv_close(iconv_t cd) +{ + (void)cd; + return 0; +} + +#endif /* !HAVE_ICONV */ #define LIBNAME "any2eucjp()" -#if defined(__MSC__) || defined(__BORLANDC__) || defined(__TURBOC__) || defined(_Windows) || defined(MSDOS) +#if defined(__MSC__) || defined(__BORLANDC__) || defined(__TURBOC__) || defined(_Windows) || \ + defined(MSDOS) #ifndef SJISPRE #define SJISPRE 1 #endif @@ -33,7 +70,7 @@ #undef FALSE #endif -#define TRUE 1 +#define TRUE 1 #define FALSE 0 #define NEW 1 @@ -47,194 +84,144 @@ #define NEWJISSTR "JIS7" #define OLDJISSTR "jis" -#define EUCSTR "eucJP" -#define SJISSTR "SJIS" +#define EUCSTR "eucJP" +#define SJISSTR "SJIS" #define ESC 27 #define SS2 142 -static void -debug (const char *format,...) -{ -#ifdef DEBUG - va_list args; - - va_start (args, format); - fprintf (stdout, "%s: ", LIBNAME); - vfprintf (stdout, format, args); - fprintf (stdout, "\n"); - va_end (args); -#endif -} +/* DetectKanjiCode() derived from DetectCodeType() by Ken Lunde. */ -static void -error (const char *format,...) +static int DetectKanjiCode(const unsigned char *str) { - va_list args; - char *tmp; - - va_start(args, format); - vspprintf(&tmp, 0, format, args); - va_end(args); - php_error_docref(NULL, E_WARNING, "%s: %s", LIBNAME, tmp); - efree(tmp); -} + static int whatcode = ASCII; + int oldcode = ASCII; + int c, i; + char *lang = NULL; -/* DetectKanjiCode() derived from DetectCodeType() by Ken Lunde. */ + c = '\1'; + i = 0; -static int -DetectKanjiCode (unsigned char *str) -{ - static int whatcode = ASCII; - int oldcode = ASCII; - int c, i; - char *lang = NULL; - - c = '\1'; - i = 0; - - if (whatcode != EUCORSJIS && whatcode != ASCII) - { - oldcode = whatcode; - whatcode = ASCII; + if (whatcode != EUCORSJIS && whatcode != ASCII) { + oldcode = whatcode; + whatcode = ASCII; } - while ((whatcode == EUCORSJIS || whatcode == ASCII) && c != '\0') - { - if ((c = str[i++]) != '\0') - { - if (c == ESC) - { - c = str[i++]; - if (c == '$') - { - c = str[i++]; - if (c == 'B') - whatcode = NEW; - else if (c == '@') - whatcode = OLD; - } - else if (c == '(') - { - c = str[i++]; - if (c == 'I') - whatcode = ESCI; - } - else if (c == 'K') - whatcode = NEC; - } - else if ((c >= 129 && c <= 141) || (c >= 143 && c <= 159)) - whatcode = SJIS; - else if (c == SS2) - { - c = str[i++]; - if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160) || (c >= 224 && c <= 252)) - whatcode = SJIS; - else if (c >= 161 && c <= 223) - whatcode = EUCORSJIS; - } - else if (c >= 161 && c <= 223) - { - c = str[i++]; - if (c >= 240 && c <= 254) - whatcode = EUC; - else if (c >= 161 && c <= 223) - whatcode = EUCORSJIS; - else if (c >= 224 && c <= 239) - { - whatcode = EUCORSJIS; - while (c >= 64 && whatcode == EUCORSJIS) - { - if (c >= 129) - { - if (c <= 141 || (c >= 143 && c <= 159)) - whatcode = SJIS; - else if (c >= 253 && c <= 254) - whatcode = EUC; - } - c = str[i++]; - } - } - else if (c <= 159) - whatcode = SJIS; - } - else if (c >= 240 && c <= 254) - whatcode = EUC; - else if (c >= 224 && c <= 239) - { - c = str[i++]; - if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160)) - whatcode = SJIS; - else if (c >= 253 && c <= 254) - whatcode = EUC; - else if (c >= 161 && c <= 252) - whatcode = EUCORSJIS; - } - } + while ((whatcode == EUCORSJIS || whatcode == ASCII) && c != '\0') { + if ((c = str[i++]) != '\0') { + if (c == ESC) { + c = str[i++]; + if (c == '$') { + c = str[i++]; + if (c == 'B') + whatcode = NEW; + else if (c == '@') + whatcode = OLD; + } else if (c == '(') { + c = str[i++]; + if (c == 'I') + whatcode = ESCI; + } else if (c == 'K') + whatcode = NEC; + } else if ((c >= 129 && c <= 141) || (c >= 143 && c <= 159)) + whatcode = SJIS; + else if (c == SS2) { + c = str[i++]; + if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160) || (c >= 224 && c <= 252)) + whatcode = SJIS; + else if (c >= 161 && c <= 223) + whatcode = EUCORSJIS; + } else if (c >= 161 && c <= 223) { + c = str[i++]; + if (c >= 240 && c <= 254) + whatcode = EUC; + else if (c >= 161 && c <= 223) + whatcode = EUCORSJIS; + else if (c >= 224 && c <= 239) { + whatcode = EUCORSJIS; + while (c >= 64 && whatcode == EUCORSJIS) { + if (c >= 129) { + if (c <= 141 || (c >= 143 && c <= 159)) + whatcode = SJIS; + else if (c >= 253 && c <= 254) + whatcode = EUC; + } + c = str[i++]; + } + } else if (c <= 159) + whatcode = SJIS; + } else if (c >= 240 && c <= 254) + whatcode = EUC; + else if (c >= 224 && c <= 239) { + c = str[i++]; + if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160)) + whatcode = SJIS; + else if (c >= 253 && c <= 254) + whatcode = EUC; + else if (c >= 161 && c <= 252) + whatcode = EUCORSJIS; + } + } } #ifdef DEBUG - if (whatcode == ASCII) - debug ("Kanji code not included."); - else if (whatcode == EUCORSJIS) - debug ("Kanji code not detected."); - else - debug ("Kanji code detected at %d byte.", i); + if (whatcode == ASCII) + gd_error_ex(GD_DEBUG, "Kanji code not included."); + else if (whatcode == EUCORSJIS) + gd_error_ex(GD_DEBUG, "Kanji code not detected."); + else + gd_error_ex(GD_DEBUG, "Kanji code detected at %d byte.", i); #endif - if (whatcode == EUCORSJIS && oldcode != ASCII) - whatcode = oldcode; - - if (whatcode == EUCORSJIS) - { - if (getenv ("LC_ALL")) - lang = getenv ("LC_ALL"); - else if (getenv ("LC_CTYPE")) - lang = getenv ("LC_CTYPE"); - else if (getenv ("LANG")) - lang = getenv ("LANG"); - - if (lang) - { - if (strcmp (lang, "ja_JP.SJIS") == 0 || + if (whatcode == EUCORSJIS && oldcode != ASCII) + whatcode = oldcode; + + if (whatcode == EUCORSJIS) { + if (getenv("LC_ALL")) + lang = getenv("LC_ALL"); + else if (getenv("LC_CTYPE")) + lang = getenv("LC_CTYPE"); + else if (getenv("LANG")) + lang = getenv("LANG"); + + if (lang) { + if (strcmp(lang, "ja_JP.SJIS") == 0 || #ifdef hpux - strcmp (lang, "japanese") == 0 || + strcmp(lang, "japanese") == 0 || #endif - strcmp (lang, "ja_JP.mscode") == 0 || - strcmp (lang, "ja_JP.PCK") == 0) - whatcode = SJIS; - else if (strncmp (lang, "ja", 2) == 0) + strcmp(lang, "ja_JP.mscode") == 0 || strcmp(lang, "ja_JP.PCK") == 0) + whatcode = SJIS; + else if (strncmp(lang, "ja", 2) == 0) #ifdef SJISPRE - whatcode = SJIS; + whatcode = SJIS; #else - whatcode = EUC; + whatcode = EUC; #endif - } + } } - if (whatcode == EUCORSJIS) + if (whatcode == EUCORSJIS) #ifdef SJISPRE - whatcode = SJIS; + whatcode = SJIS; #else - whatcode = EUC; + whatcode = EUC; #endif - return whatcode; + return whatcode; } /* SJIStoJIS() is sjis2jis() by Ken Lunde. */ -static void -SJIStoJIS (int *p1, int *p2) +static void SJIStoJIS(int *p1, int *p2) { - register unsigned char c1 = *p1; - register unsigned char c2 = *p2; - register int adjust = c2 < 159; - register int rowOffset = c1 < 160 ? 112 : 176; - register int cellOffset = adjust ? (31 + (c2 > 127)) : 126; - - *p1 = ((c1 - rowOffset) << 1) - adjust; - *p2 -= cellOffset; + register unsigned char c1 = *p1; + register unsigned char c2 = *p2; + register int adjust = c2 < 159; + register int rowOffset = c1 < 160 ? 112 : 176; + register int cellOffset = adjust ? (31 + (c2 > 127)) : 126; + + *p1 = ((c1 - rowOffset) << 1) - adjust; + *p2 -= cellOffset; } /* han2zen() was derived from han2zen() written by Ken Lunde. */ @@ -242,340 +229,250 @@ SJIStoJIS (int *p1, int *p2) #define IS_DAKU(c) ((c >= 182 && c <= 196) || (c >= 202 && c <= 206) || (c == 179)) #define IS_HANDAKU(c) (c >= 202 && c <= 206) -static void -han2zen (int *p1, int *p2) +static void han2zen(int *p1, int *p2) { - int c = *p1; - int daku = FALSE; - int handaku = FALSE; - int mtable[][2] = - { - {129, 66}, - {129, 117}, - {129, 118}, - {129, 65}, - {129, 69}, - {131, 146}, - {131, 64}, - {131, 66}, - {131, 68}, - {131, 70}, - {131, 72}, - {131, 131}, - {131, 133}, - {131, 135}, - {131, 98}, - {129, 91}, - {131, 65}, - {131, 67}, - {131, 69}, - {131, 71}, - {131, 73}, - {131, 74}, - {131, 76}, - {131, 78}, - {131, 80}, - {131, 82}, - {131, 84}, - {131, 86}, - {131, 88}, - {131, 90}, - {131, 92}, - {131, 94}, - {131, 96}, - {131, 99}, - {131, 101}, - {131, 103}, - {131, 105}, - {131, 106}, - {131, 107}, - {131, 108}, - {131, 109}, - {131, 110}, - {131, 113}, - {131, 116}, - {131, 119}, - {131, 122}, - {131, 125}, - {131, 126}, - {131, 128}, - {131, 129}, - {131, 130}, - {131, 132}, - {131, 134}, - {131, 136}, - {131, 137}, - {131, 138}, - {131, 139}, - {131, 140}, - {131, 141}, - {131, 143}, - {131, 147}, - {129, 74}, - {129, 75} - }; - - if (*p2 == 222 && IS_DAKU (*p1)) - daku = TRUE; /* Daku-ten */ - else if (*p2 == 223 && IS_HANDAKU (*p1)) - handaku = TRUE; /* Han-daku-ten */ - - *p1 = mtable[c - 161][0]; - *p2 = mtable[c - 161][1]; - - if (daku) - { - if ((*p2 >= 74 && *p2 <= 103) || (*p2 >= 110 && *p2 <= 122)) - (*p2)++; - else if (*p2 == 131 || *p2 == 69) - *p2 = 148; - } - else if (handaku && *p2 >= 110 && *p2 <= 122) - (*p2) += 2; + int c = *p1; + int daku = FALSE; + int handaku = FALSE; + int mtable[][2] = { + {129, 66}, {129, 117}, {129, 118}, {129, 65}, {129, 69}, {131, 146}, {131, 64}, + {131, 66}, {131, 68}, {131, 70}, {131, 72}, {131, 131}, {131, 133}, {131, 135}, + {131, 98}, {129, 91}, {131, 65}, {131, 67}, {131, 69}, {131, 71}, {131, 73}, + {131, 74}, {131, 76}, {131, 78}, {131, 80}, {131, 82}, {131, 84}, {131, 86}, + {131, 88}, {131, 90}, {131, 92}, {131, 94}, {131, 96}, {131, 99}, {131, 101}, + {131, 103}, {131, 105}, {131, 106}, {131, 107}, {131, 108}, {131, 109}, {131, 110}, + {131, 113}, {131, 116}, {131, 119}, {131, 122}, {131, 125}, {131, 126}, {131, 128}, + {131, 129}, {131, 130}, {131, 132}, {131, 134}, {131, 136}, {131, 137}, {131, 138}, + {131, 139}, {131, 140}, {131, 141}, {131, 143}, {131, 147}, {129, 74}, {129, 75}}; + + if (*p2 == 222 && IS_DAKU(*p1)) + daku = TRUE; /* Daku-ten */ + else if (*p2 == 223 && IS_HANDAKU(*p1)) + handaku = TRUE; /* Han-daku-ten */ + + *p1 = mtable[c - 161][0]; + *p2 = mtable[c - 161][1]; + + if (daku) { + if ((*p2 >= 74 && *p2 <= 103) || (*p2 >= 110 && *p2 <= 122)) + (*p2)++; + else if (*p2 == 131 || *p2 == 69) + *p2 = 148; + } else if (handaku && *p2 >= 110 && *p2 <= 122) + (*p2) += 2; } /* Recast strcpy to handle unsigned chars used below. */ -#define ustrcpy(A,B) (strcpy((char*)(A),(const char*)(B))) - -static void -do_convert (unsigned char *to, unsigned char *from, const char *code) +#define ustrcpy(A, B) (strcpy((char *)(A), (const char *)(B))) +#define ustrncpy(A, B, maxsize) (strncpy((char *)(A), (const char *)(B), maxsize)) +static void do_convert(unsigned char **to, const unsigned char **from, const char *code) { #ifdef HAVE_ICONV - iconv_t cd; - size_t from_len, to_len; - - if ((cd = iconv_open (EUCSTR, code)) == (iconv_t) - 1) - { - error ("iconv_open() error"); - if (errno == EINVAL) - error ("invalid code specification: \"%s\" or \"%s\"", - EUCSTR, code); - strcpy ((char *) to, (const char *) from); - return; + iconv_t cd; + size_t from_len, to_len; + + if ((cd = iconv_open(EUCSTR, code)) == (iconv_t)-1) { + gd_error("iconv_open() error"); + if (errno == EINVAL) + gd_error("invalid code specification: \"%s\" or \"%s\"", EUCSTR, code); + ustrcpy(*to, *from); + return; } - from_len = strlen ((const char *) from) + 1; - to_len = BUFSIZ; - - if ((int) iconv(cd, (char **) &from, &from_len, (char **) &to, &to_len) == -1) - { - if (errno == EINVAL) - error ("invalid end of input string"); - else if (errno == EILSEQ) - error ("invalid code in input string"); - else if (errno == E2BIG) - error ("output buffer overflow at do_convert()"); - else - error ("something happen"); - strcpy ((char *) to, (const char *) from); - if (iconv_close (cd) != 0) - error ("iconv_close() error"); - return; + from_len = strlen((const char *)*from) + 1; + to_len = BUFSIZ; + + if ((int)(iconv(cd, (char **)from, &from_len, (char **)to, &to_len)) == -1) { + if (errno == EINVAL) + gd_error("invalid end of input string"); + else if (errno == EILSEQ) + gd_error("invalid code in input string"); + else if (errno == E2BIG) + gd_error("output buffer overflow at do_convert()"); + else + gd_error("something happen"); + ustrcpy(*to, *from); + if (iconv_close(cd) != 0) + gd_error("iconv_close() error"); + return; } - if (iconv_close (cd) != 0) - { - error ("iconv_close() error"); - } + if (iconv_close(cd) != 0) + gd_error("iconv_close() error"); #else - int p1, p2, i, j; - int jisx0208 = FALSE; - int hankaku = FALSE; - - j = 0; - if (strcmp (code, NEWJISSTR) == 0 || strcmp (code, OLDJISSTR) == 0) - { - for (i = 0; from[i] != '\0' && j < BUFSIZ; i++) - { - if (from[i] == ESC) - { - i++; - if (from[i] == '$') - { - jisx0208 = TRUE; - hankaku = FALSE; - i++; - } - else if (from[i] == '(') - { - jisx0208 = FALSE; - i++; - if (from[i] == 'I') /* Hankaku Kana */ - hankaku = TRUE; - else - hankaku = FALSE; - } - } - else - { - if (jisx0208) - to[j++] = from[i] + 128; - else if (hankaku) - { - to[j++] = SS2; - to[j++] = from[i] + 128; - } - else - to[j++] = from[i]; - } - } - } - else if (strcmp (code, SJISSTR) == 0) - { - for (i = 0; from[i] != '\0' && j < BUFSIZ; i++) - { - p1 = from[i]; - if (p1 < 127) - to[j++] = p1; - else if ((p1 >= 161) && (p1 <= 223)) - { /* Hankaku Kana */ - to[j++] = SS2; - to[j++] = p1; - } - else - { - p2 = from[++i]; - SJIStoJIS (&p1, &p2); - to[j++] = p1 + 128; - to[j++] = p2 + 128; - } - } - } - else - { - error ("invalid code specification: \"%s\"", code); - return; + int p1, p2, i, j; + int jisx0208 = FALSE; + int hankaku = FALSE; + unsigned char *to_buf = *to; + const unsigned char *from_buf = *from; + + j = 0; + if (strcmp(code, NEWJISSTR) == 0 || strcmp(code, OLDJISSTR) == 0) { + for (i = 0; from_buf[i] != '\0' && j < BUFSIZ; i++) { + if (from_buf[i] == ESC) { + i++; + if (from_buf[i] == '$') { + jisx0208 = TRUE; + hankaku = FALSE; + i++; + } else if (from_buf[i] == '(') { + jisx0208 = FALSE; + i++; + if (from_buf[i] == 'I') + hankaku = TRUE; + else + hankaku = FALSE; + } + } else { + if (jisx0208) + to_buf[j++] = from_buf[i] + 128; + else if (hankaku) { + to_buf[j++] = SS2; + to_buf[j++] = from_buf[i] + 128; + } else + to_buf[j++] = from_buf[i]; + } + } + } else if (strcmp(code, SJISSTR) == 0) { + for (i = 0; from_buf[i] != '\0' && j < BUFSIZ; i++) { + p1 = from_buf[i]; + if (p1 < 127) + to_buf[j++] = p1; + else if ((p1 >= 161) && (p1 <= 223)) { + to_buf[j++] = SS2; + to_buf[j++] = p1; + } else { + p2 = from_buf[++i]; + SJIStoJIS(&p1, &p2); + to_buf[j++] = p1 + 128; + to_buf[j++] = p2 + 128; + } + } + } else { + gd_error("invalid code specification: \"%s\"", code); + return; } - if (j >= BUFSIZ) - { - error ("output buffer overflow at do_convert()"); - ustrcpy (to, from); - } - else - to[j] = '\0'; -#endif /* HAVE_ICONV */ + if (j >= BUFSIZ) { + gd_error("output buffer overflow at do_convert()"); + ustrcpy(*to, *from); + } else + to_buf[j] = '\0'; +#endif } -static int -do_check_and_conv (unsigned char *to, unsigned char *from) +static int do_check_and_conv(unsigned char *to, const unsigned char *from) { - static unsigned char tmp[BUFSIZ]; - int p1, p2, i, j; - int kanji = TRUE; + static unsigned char tmp[BUFSIZ]; + unsigned char *tmp_p = &tmp[0]; + int p1, p2, i, j; + int kanji = TRUE; + int copy_string = FALSE; - switch (DetectKanjiCode (from)) - { + switch (DetectKanjiCode(from)) { case NEW: - debug ("Kanji code is New JIS."); - do_convert (tmp, from, NEWJISSTR); - break; + gd_error_ex(GD_DEBUG, "Kanji code is New JIS."); + do_convert(&tmp_p, &from, NEWJISSTR); + break; case OLD: - debug ("Kanji code is Old JIS."); - do_convert (tmp, from, OLDJISSTR); - break; + gd_error_ex(GD_DEBUG, "Kanji code is Old JIS."); + do_convert(&tmp_p, &from, OLDJISSTR); + break; case ESCI: - debug ("This string includes Hankaku-Kana (jisx0201) escape sequence [ESC] + ( + I."); - do_convert (tmp, from, NEWJISSTR); - break; + gd_error_ex(GD_DEBUG, "This string includes Hankaku-Kana (jisx0201) " + "escape sequence [ESC] + ( + I."); + do_convert(&tmp_p, &from, NEWJISSTR); + break; case NEC: - debug ("Kanji code is NEC Kanji."); - error ("cannot convert NEC Kanji."); - ustrcpy (tmp, from); - kanji = FALSE; - break; + gd_error_ex(GD_DEBUG, "Kanji code is NEC Kanji."); + gd_error("cannot convert NEC Kanji."); + copy_string = TRUE; + kanji = FALSE; + break; case EUC: - debug ("Kanji code is EUC."); - ustrcpy (tmp, from); - break; + gd_error_ex(GD_DEBUG, "Kanji code is EUC."); + copy_string = TRUE; + break; case SJIS: - debug ("Kanji code is SJIS."); - do_convert (tmp, from, SJISSTR); - break; + gd_error_ex(GD_DEBUG, "Kanji code is SJIS."); + do_convert(&tmp_p, &from, SJISSTR); + break; case EUCORSJIS: - debug ("Kanji code is EUC or SJIS."); - ustrcpy (tmp, from); - kanji = FALSE; - break; + gd_error_ex(GD_DEBUG, "Kanji code is EUC or SJIS."); + copy_string = TRUE; + kanji = FALSE; + break; case ASCII: - debug ("This is ASCII string."); - ustrcpy (tmp, from); - kanji = FALSE; - break; + gd_error_ex(GD_DEBUG, "This is ASCII string."); + copy_string = TRUE; + kanji = FALSE; + break; default: - debug ("This string includes unknown code."); - ustrcpy (tmp, from); - kanji = FALSE; - break; + gd_error_ex(GD_DEBUG, "This string includes unknown code."); + copy_string = TRUE; + kanji = FALSE; + break; } - /* Hankaku Kana ---> Zenkaku Kana */ - if (kanji) - { - j = 0; - for (i = 0; tmp[i] != '\0' && j < BUFSIZ; i++) - { - if (tmp[i] == SS2) - { - p1 = tmp[++i]; - if (tmp[i + 1] == SS2) - { - p2 = tmp[i + 2]; - if (p2 == 222 || p2 == 223) - i += 2; - else - p2 = 0; - } - else - p2 = 0; - han2zen (&p1, &p2); - SJIStoJIS (&p1, &p2); - to[j++] = p1 + 128; - to[j++] = p2 + 128; - } - else - to[j++] = tmp[i]; - } - - if (j >= BUFSIZ) - { - error ("output buffer overflow at Hankaku --> Zenkaku"); - ustrcpy (to, tmp); - } - else - to[j] = '\0'; + if (copy_string) { + ustrncpy(tmp, from, BUFSIZ); + tmp[BUFSIZ - 1] = '\0'; } - else - ustrcpy (to, tmp); - return kanji; + /* Hankaku Kana ---> Zenkaku Kana */ + if (kanji) { + j = 0; + for (i = 0; tmp[i] != '\0' && j < BUFSIZ; i++) { + if (tmp[i] == SS2) { + p1 = tmp[++i]; + if (tmp[i + 1] == SS2) { + p2 = tmp[i + 2]; + if (p2 == 222 || p2 == 223) + i += 2; + else + p2 = 0; + } else + p2 = 0; + han2zen(&p1, &p2); + SJIStoJIS(&p1, &p2); + to[j++] = p1 + 128; + to[j++] = p2 + 128; + } else + to[j++] = tmp[i]; + } + + if (j >= BUFSIZ) { + gd_error("output buffer overflow at Hankaku --> Zenkaku"); + ustrcpy(to, tmp); + } else + to[j] = '\0'; + } else + ustrcpy(to, tmp); + + return kanji; } -int -any2eucjp (unsigned char *dest, unsigned char *src, unsigned int dest_max) +int any2eucjp(unsigned char *dest, const unsigned char *src, unsigned int dest_max) { - static unsigned char tmp_dest[BUFSIZ]; - int ret; + static unsigned char tmp_dest[BUFSIZ]; + int ret; - if (strlen ((const char *) src) >= BUFSIZ) - { - error ("input string too large"); - return -1; + if (strlen((const char *)src) >= BUFSIZ) { + gd_error("input string too large"); + return -1; } - if (dest_max > BUFSIZ) - { - error ("invalid maximum size of destination\nit should be less than %d.", BUFSIZ); - return -1; + if (dest_max > BUFSIZ) { + gd_error("invalid maximum size of destination\nit should be less than %d.", BUFSIZ); + return -1; } - ret = do_check_and_conv (tmp_dest, src); - if (strlen ((const char *) tmp_dest) >= dest_max) - { - error ("output buffer overflow"); - ustrcpy (dest, src); - return -1; + ret = do_check_and_conv(tmp_dest, src); + if (strlen((const char *)tmp_dest) >= dest_max) { + gd_error("output buffer overflow"); + ustrcpy(dest, src); + return -1; } - ustrcpy (dest, tmp_dest); - return ret; + ustrcpy(dest, tmp_dest); + return ret; } #if 0 diff --git a/ext/gd/libgd/gdparttopng.c b/ext/gd/libgd/gdparttopng.c index a82925b0ed47..5d5df9533d8c 100644 --- a/ext/gd/libgd/gdparttopng.c +++ b/ext/gd/libgd/gdparttopng.c @@ -1,59 +1,54 @@ -#include -#include /* For atoi */ #include "gd.h" +#include +#include /* For atoi */ /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in, *out; - int x, y, w, h; + gdImagePtr im; + FILE *in, *out; + int x, y, w, h; - if (argc != 7) - { - fprintf (stderr, "Usage: gdparttopng filename.gd filename.png x y w h\n"); - exit (1); + if (argc != 7) { + fprintf(stderr, "Usage: gdparttopng filename.gd filename.png x y w h\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - x = atoi (argv[3]); - y = atoi (argv[4]); - w = atoi (argv[5]); - h = atoi (argv[6]); + x = atoi(argv[3]); + y = atoi(argv[4]); + w = atoi(argv[5]); + h = atoi(argv[6]); - printf ("Extracting from (%d, %d), size is %dx%d\n", x, y, w, h); + printf("Extracting from (%d, %d), size is %dx%d\n", x, y, w, h); - im = gdImageCreateFromGd2Part (in, x, y, w, h); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in GD2 format!\n"); - exit (1); + im = gdImageCreateFromGd2Part(in, x, y, w, h); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in GD2 format!\n"); + exit(1); } - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } #ifdef HAVE_LIBPNG - gdImagePng (im, out); + gdImagePng(im, out); #else - fprintf(stderr, "No PNG library support.\n"); + fprintf(stderr, "No PNG library support.\n"); #endif - fclose (out); - gdImageDestroy (im); + fclose(out); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/gdtables.c b/ext/gd/libgd/gdtables.c index e34d8fc7cbe3..996fcace8ab8 100644 --- a/ext/gd/libgd/gdtables.c +++ b/ext/gd/libgd/gdtables.c @@ -5,730 +5,62 @@ #include "php_compat.h" -const int gdCosT[] = -{ - 1024, - 1023, - 1023, - 1022, - 1021, - 1020, - 1018, - 1016, - 1014, - 1011, - 1008, - 1005, - 1001, - 997, - 993, - 989, - 984, - 979, - 973, - 968, - 962, - 955, - 949, - 942, - 935, - 928, - 920, - 912, - 904, - 895, - 886, - 877, - 868, - 858, - 848, - 838, - 828, - 817, - 806, - 795, - 784, - 772, - 760, - 748, - 736, - 724, - 711, - 698, - 685, - 671, - 658, - 644, - 630, - 616, - 601, - 587, - 572, - 557, - 542, - 527, - 512, - 496, - 480, - 464, - 448, - 432, - 416, - 400, - 383, - 366, - 350, - 333, - 316, - 299, - 282, - 265, - 247, - 230, - 212, - 195, - 177, - 160, - 142, - 124, - 107, - 89, - 71, - 53, - 35, - 17, - 0, - -17, - -35, - -53, - -71, - -89, - -107, - -124, - -142, - -160, - -177, - -195, - -212, - -230, - -247, - -265, - -282, - -299, - -316, - -333, - -350, - -366, - -383, - -400, - -416, - -432, - -448, - -464, - -480, - -496, - -512, - -527, - -542, - -557, - -572, - -587, - -601, - -616, - -630, - -644, - -658, - -671, - -685, - -698, - -711, - -724, - -736, - -748, - -760, - -772, - -784, - -795, - -806, - -817, - -828, - -838, - -848, - -858, - -868, - -877, - -886, - -895, - -904, - -912, - -920, - -928, - -935, - -942, - -949, - -955, - -962, - -968, - -973, - -979, - -984, - -989, - -993, - -997, - -1001, - -1005, - -1008, - -1011, - -1014, - -1016, - -1018, - -1020, - -1021, - -1022, - -1023, - -1023, - -1024, - -1023, - -1023, - -1022, - -1021, - -1020, - -1018, - -1016, - -1014, - -1011, - -1008, - -1005, - -1001, - -997, - -993, - -989, - -984, - -979, - -973, - -968, - -962, - -955, - -949, - -942, - -935, - -928, - -920, - -912, - -904, - -895, - -886, - -877, - -868, - -858, - -848, - -838, - -828, - -817, - -806, - -795, - -784, - -772, - -760, - -748, - -736, - -724, - -711, - -698, - -685, - -671, - -658, - -644, - -630, - -616, - -601, - -587, - -572, - -557, - -542, - -527, - -512, - -496, - -480, - -464, - -448, - -432, - -416, - -400, - -383, - -366, - -350, - -333, - -316, - -299, - -282, - -265, - -247, - -230, - -212, - -195, - -177, - -160, - -142, - -124, - -107, - -89, - -71, - -53, - -35, - -17, - 0, - 17, - 35, - 53, - 71, - 89, - 107, - 124, - 142, - 160, - 177, - 195, - 212, - 230, - 247, - 265, - 282, - 299, - 316, - 333, - 350, - 366, - 383, - 400, - 416, - 432, - 448, - 464, - 480, - 496, - 512, - 527, - 542, - 557, - 572, - 587, - 601, - 616, - 630, - 644, - 658, - 671, - 685, - 698, - 711, - 724, - 736, - 748, - 760, - 772, - 784, - 795, - 806, - 817, - 828, - 838, - 848, - 858, - 868, - 877, - 886, - 895, - 904, - 912, - 920, - 928, - 935, - 942, - 949, - 955, - 962, - 968, - 973, - 979, - 984, - 989, - 993, - 997, - 1001, - 1005, - 1008, - 1011, - 1014, - 1016, - 1018, - 1020, - 1021, - 1022, - 1023, - 1023 -}; +const int gdCosT[] = { + 1024, 1023, 1023, 1022, 1021, 1020, 1018, 1016, 1014, 1011, 1008, 1005, 1001, + 997, 993, 989, 984, 979, 973, 968, 962, 955, 949, 942, 935, 928, + 920, 912, 904, 895, 886, 877, 868, 858, 848, 838, 828, 817, 806, + 795, 784, 772, 760, 748, 736, 724, 711, 698, 685, 671, 658, 644, + 630, 616, 601, 587, 572, 557, 542, 527, 512, 496, 480, 464, 448, + 432, 416, 400, 383, 366, 350, 333, 316, 299, 282, 265, 247, 230, + 212, 195, 177, 160, 142, 124, 107, 89, 71, 53, 35, 17, 0, + -17, -35, -53, -71, -89, -107, -124, -142, -160, -177, -195, -212, -230, + -247, -265, -282, -299, -316, -333, -350, -366, -383, -400, -416, -432, -448, + -464, -480, -496, -512, -527, -542, -557, -572, -587, -601, -616, -630, -644, + -658, -671, -685, -698, -711, -724, -736, -748, -760, -772, -784, -795, -806, + -817, -828, -838, -848, -858, -868, -877, -886, -895, -904, -912, -920, -928, + -935, -942, -949, -955, -962, -968, -973, -979, -984, -989, -993, -997, -1001, + -1005, -1008, -1011, -1014, -1016, -1018, -1020, -1021, -1022, -1023, -1023, -1024, -1023, + -1023, -1022, -1021, -1020, -1018, -1016, -1014, -1011, -1008, -1005, -1001, -997, -993, + -989, -984, -979, -973, -968, -962, -955, -949, -942, -935, -928, -920, -912, + -904, -895, -886, -877, -868, -858, -848, -838, -828, -817, -806, -795, -784, + -772, -760, -748, -736, -724, -711, -698, -685, -671, -658, -644, -630, -616, + -601, -587, -572, -557, -542, -527, -512, -496, -480, -464, -448, -432, -416, + -400, -383, -366, -350, -333, -316, -299, -282, -265, -247, -230, -212, -195, + -177, -160, -142, -124, -107, -89, -71, -53, -35, -17, 0, 17, 35, + 53, 71, 89, 107, 124, 142, 160, 177, 195, 212, 230, 247, 265, + 282, 299, 316, 333, 350, 366, 383, 400, 416, 432, 448, 464, 480, + 496, 512, 527, 542, 557, 572, 587, 601, 616, 630, 644, 658, 671, + 685, 698, 711, 724, 736, 748, 760, 772, 784, 795, 806, 817, 828, + 838, 848, 858, 868, 877, 886, 895, 904, 912, 920, 928, 935, 942, + 949, 955, 962, 968, 973, 979, 984, 989, 993, 997, 1001, 1005, 1008, + 1011, 1014, 1016, 1018, 1020, 1021, 1022, 1023, 1023}; -const int gdSinT[] = -{ - 0, - 17, - 35, - 53, - 71, - 89, - 107, - 124, - 142, - 160, - 177, - 195, - 212, - 230, - 247, - 265, - 282, - 299, - 316, - 333, - 350, - 366, - 383, - 400, - 416, - 432, - 448, - 464, - 480, - 496, - 512, - 527, - 542, - 557, - 572, - 587, - 601, - 616, - 630, - 644, - 658, - 671, - 685, - 698, - 711, - 724, - 736, - 748, - 760, - 772, - 784, - 795, - 806, - 817, - 828, - 838, - 848, - 858, - 868, - 877, - 886, - 895, - 904, - 912, - 920, - 928, - 935, - 942, - 949, - 955, - 962, - 968, - 973, - 979, - 984, - 989, - 993, - 997, - 1001, - 1005, - 1008, - 1011, - 1014, - 1016, - 1018, - 1020, - 1021, - 1022, - 1023, - 1023, - 1024, - 1023, - 1023, - 1022, - 1021, - 1020, - 1018, - 1016, - 1014, - 1011, - 1008, - 1005, - 1001, - 997, - 993, - 989, - 984, - 979, - 973, - 968, - 962, - 955, - 949, - 942, - 935, - 928, - 920, - 912, - 904, - 895, - 886, - 877, - 868, - 858, - 848, - 838, - 828, - 817, - 806, - 795, - 784, - 772, - 760, - 748, - 736, - 724, - 711, - 698, - 685, - 671, - 658, - 644, - 630, - 616, - 601, - 587, - 572, - 557, - 542, - 527, - 512, - 496, - 480, - 464, - 448, - 432, - 416, - 400, - 383, - 366, - 350, - 333, - 316, - 299, - 282, - 265, - 247, - 230, - 212, - 195, - 177, - 160, - 142, - 124, - 107, - 89, - 71, - 53, - 35, - 17, - 0, - -17, - -35, - -53, - -71, - -89, - -107, - -124, - -142, - -160, - -177, - -195, - -212, - -230, - -247, - -265, - -282, - -299, - -316, - -333, - -350, - -366, - -383, - -400, - -416, - -432, - -448, - -464, - -480, - -496, - -512, - -527, - -542, - -557, - -572, - -587, - -601, - -616, - -630, - -644, - -658, - -671, - -685, - -698, - -711, - -724, - -736, - -748, - -760, - -772, - -784, - -795, - -806, - -817, - -828, - -838, - -848, - -858, - -868, - -877, - -886, - -895, - -904, - -912, - -920, - -928, - -935, - -942, - -949, - -955, - -962, - -968, - -973, - -979, - -984, - -989, - -993, - -997, - -1001, - -1005, - -1008, - -1011, - -1014, - -1016, - -1018, - -1020, - -1021, - -1022, - -1023, - -1023, - -1024, - -1023, - -1023, - -1022, - -1021, - -1020, - -1018, - -1016, - -1014, - -1011, - -1008, - -1005, - -1001, - -997, - -993, - -989, - -984, - -979, - -973, - -968, - -962, - -955, - -949, - -942, - -935, - -928, - -920, - -912, - -904, - -895, - -886, - -877, - -868, - -858, - -848, - -838, - -828, - -817, - -806, - -795, - -784, - -772, - -760, - -748, - -736, - -724, - -711, - -698, - -685, - -671, - -658, - -644, - -630, - -616, - -601, - -587, - -572, - -557, - -542, - -527, - -512, - -496, - -480, - -464, - -448, - -432, - -416, - -400, - -383, - -366, - -350, - -333, - -316, - -299, - -282, - -265, - -247, - -230, - -212, - -195, - -177, - -160, - -142, - -124, - -107, - -89, - -71, - -53, - -35, - -17 -}; +const int gdSinT[] = { + 0, 17, 35, 53, 71, 89, 107, 124, 142, 160, 177, 195, 212, + 230, 247, 265, 282, 299, 316, 333, 350, 366, 383, 400, 416, 432, + 448, 464, 480, 496, 512, 527, 542, 557, 572, 587, 601, 616, 630, + 644, 658, 671, 685, 698, 711, 724, 736, 748, 760, 772, 784, 795, + 806, 817, 828, 838, 848, 858, 868, 877, 886, 895, 904, 912, 920, + 928, 935, 942, 949, 955, 962, 968, 973, 979, 984, 989, 993, 997, + 1001, 1005, 1008, 1011, 1014, 1016, 1018, 1020, 1021, 1022, 1023, 1023, 1024, + 1023, 1023, 1022, 1021, 1020, 1018, 1016, 1014, 1011, 1008, 1005, 1001, 997, + 993, 989, 984, 979, 973, 968, 962, 955, 949, 942, 935, 928, 920, + 912, 904, 895, 886, 877, 868, 858, 848, 838, 828, 817, 806, 795, + 784, 772, 760, 748, 736, 724, 711, 698, 685, 671, 658, 644, 630, + 616, 601, 587, 572, 557, 542, 527, 512, 496, 480, 464, 448, 432, + 416, 400, 383, 366, 350, 333, 316, 299, 282, 265, 247, 230, 212, + 195, 177, 160, 142, 124, 107, 89, 71, 53, 35, 17, 0, -17, + -35, -53, -71, -89, -107, -124, -142, -160, -177, -195, -212, -230, -247, + -265, -282, -299, -316, -333, -350, -366, -383, -400, -416, -432, -448, -464, + -480, -496, -512, -527, -542, -557, -572, -587, -601, -616, -630, -644, -658, + -671, -685, -698, -711, -724, -736, -748, -760, -772, -784, -795, -806, -817, + -828, -838, -848, -858, -868, -877, -886, -895, -904, -912, -920, -928, -935, + -942, -949, -955, -962, -968, -973, -979, -984, -989, -993, -997, -1001, -1005, + -1008, -1011, -1014, -1016, -1018, -1020, -1021, -1022, -1023, -1023, -1024, -1023, -1023, + -1022, -1021, -1020, -1018, -1016, -1014, -1011, -1008, -1005, -1001, -997, -993, -989, + -984, -979, -973, -968, -962, -955, -949, -942, -935, -928, -920, -912, -904, + -895, -886, -877, -868, -858, -848, -838, -828, -817, -806, -795, -784, -772, + -760, -748, -736, -724, -711, -698, -685, -671, -658, -644, -630, -616, -601, + -587, -572, -557, -542, -527, -512, -496, -480, -464, -448, -432, -416, -400, + -383, -366, -350, -333, -316, -299, -282, -265, -247, -230, -212, -195, -177, + -160, -142, -124, -107, -89, -71, -53, -35, -17}; diff --git a/ext/gd/libgd/gdtest.c b/ext/gd/libgd/gdtest.c index ba036b83d974..941e675a3f99 100644 --- a/ext/gd/libgd/gdtest.c +++ b/ext/gd/libgd/gdtest.c @@ -1,409 +1,359 @@ #include #ifdef _WIN32 #include -int -unlink (const char *filename) -{ - return _unlink (filename); -} +int unlink(const char *filename) { return _unlink(filename); } #else -#include /* for getpid(), unlink() */ +#include /* for getpid(), unlink() */ #endif #include "gd.h" -void CompareImages (char *msg, gdImagePtr im1, gdImagePtr im2); +void CompareImages(char *msg, gdImagePtr im1, gdImagePtr im2); -static int freadWrapper (void *context, char *buf, int len); -static int fwriteWrapper (void *context, const char *buffer, int len); +static int freadWrapper(void *context, char *buf, int len); +static int fwriteWrapper(void *context, const char *buffer, int len); -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im, ref, im2, im3; - FILE *in, *out; - void *iptr; - int sz; - gdIOCtxPtr ctx; - char of[256]; - int colRed, colBlu; - gdSource imgsrc; - gdSink imgsnk; - int foreground; - int i; - if (argc != 2) - { - fprintf (stderr, "Usage: gdtest filename.png\n"); - exit (1); + gdImagePtr im, ref, im2, im3; + FILE *in, *out; + void *iptr; + int sz; + gdIOCtxPtr ctx; + char of[256]; + int colRed, colBlu; + gdSource imgsrc; + gdSink imgsnk; + int foreground; + int i; + if (argc != 2) { + fprintf(stderr, "Usage: gdtest filename.png\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromPng (in); - - rewind (in); - ref = gdImageCreateFromPng (in); + im = gdImageCreateFromPng(in); - fclose (in); + rewind(in); + ref = gdImageCreateFromPng(in); - printf ("Reference File has %d Palette entries\n", ref->colorsTotal); + fclose(in); - CompareImages ("Initial Versions", ref, im); + printf("Reference File has %d Palette entries\n", ref->colorsTotal); + CompareImages("Initial Versions", ref, im); - /* */ - /* Send to PNG File then Ptr */ - /* */ - snprintf (of, sizeof(of), "%s.png", argv[1]); - out = fopen (of, "wb"); - gdImagePng (im, out); - fclose (out); + /* */ + /* Send to PNG File then Ptr */ + /* */ + snprintf(of, sizeof(of), "%s.png", argv[1]); + out = fopen(of, "wb"); + gdImagePng(im, out); + fclose(out); - in = fopen (of, "rb"); - if (!in) - { - fprintf (stderr, "PNG Output file does not exist!\n"); - exit (1); + in = fopen(of, "rb"); + if (!in) { + fprintf(stderr, "PNG Output file does not exist!\n"); + exit(1); } - im2 = gdImageCreateFromPng (in); - fclose (in); + im2 = gdImageCreateFromPng(in); + fclose(in); - CompareImages ("GD->PNG File->GD", ref, im2); + CompareImages("GD->PNG File->GD", ref, im2); - unlink (of); - gdImageDestroy (im2); + unlink(of); + gdImageDestroy(im2); - iptr = gdImagePngPtr (im, &sz); - ctx = gdNewDynamicCtx (sz, iptr); - im2 = gdImageCreateFromPngCtx (ctx); + iptr = gdImagePngPtr(im, &sz); + ctx = gdNewDynamicCtx(sz, iptr); + im2 = gdImageCreateFromPngCtx(ctx); - CompareImages ("GD->PNG ptr->GD", ref, im2); + CompareImages("GD->PNG ptr->GD", ref, im2); - gdImageDestroy (im2); - ctx->gd_free (ctx); + gdImageDestroy(im2); + ctx->gd_free(ctx); + /* */ + /* Send to GD2 File then Ptr */ + /* */ + snprintf(of, sizeof(of), "%s.gd2", argv[1]); + out = fopen(of, "wb"); + gdImageGd2(im, out, 128, 2); + fclose(out); - /* */ - /* Send to GD2 File then Ptr */ - /* */ - snprintf (of, sizeof(of), "%s.gd2", argv[1]); - out = fopen (of, "wb"); - gdImageGd2 (im, out, 128, 2); - fclose (out); - - in = fopen (of, "rb"); - if (!in) - { - fprintf (stderr, "GD2 Output file does not exist!\n"); - exit (1); + in = fopen(of, "rb"); + if (!in) { + fprintf(stderr, "GD2 Output file does not exist!\n"); + exit(1); } - im2 = gdImageCreateFromGd2 (in); - fclose (in); - - CompareImages ("GD->GD2 File->GD", ref, im2); - - unlink (of); - gdImageDestroy (im2); - - iptr = gdImageGd2Ptr (im, 128, 2, &sz); - /*printf("Got ptr %d (size %d)\n",iptr, sz); */ - ctx = gdNewDynamicCtx (sz, iptr); - /*printf("Got ctx %d\n",ctx); */ - im2 = gdImageCreateFromGd2Ctx (ctx); - /*printf("Got img2 %d\n",im2); */ - - CompareImages ("GD->GD2 ptr->GD", ref, im2); - - gdImageDestroy (im2); - ctx->gd_free (ctx); - - - /* */ - /* Send to GD File then Ptr */ - /* */ - snprintf (of, sizeof(of), "%s.gd", argv[1]); - out = fopen (of, "wb"); - gdImageGd (im, out); - fclose (out); - - in = fopen (of, "rb"); - if (!in) - { - fprintf (stderr, "GD Output file does not exist!\n"); - exit (1); + im2 = gdImageCreateFromGd2(in); + fclose(in); + + CompareImages("GD->GD2 File->GD", ref, im2); + + unlink(of); + gdImageDestroy(im2); + + iptr = gdImageGd2Ptr(im, 128, 2, &sz); + /*printf("Got ptr %d (size %d)\n",iptr, sz); */ + ctx = gdNewDynamicCtx(sz, iptr); + /*printf("Got ctx %d\n",ctx); */ + im2 = gdImageCreateFromGd2Ctx(ctx); + /*printf("Got img2 %d\n",im2); */ + + CompareImages("GD->GD2 ptr->GD", ref, im2); + + gdImageDestroy(im2); + ctx->gd_free(ctx); + + /* */ + /* Send to GD File then Ptr */ + /* */ + snprintf(of, sizeof(of), "%s.gd", argv[1]); + out = fopen(of, "wb"); + gdImageGd(im, out); + fclose(out); + + in = fopen(of, "rb"); + if (!in) { + fprintf(stderr, "GD Output file does not exist!\n"); + exit(1); } - im2 = gdImageCreateFromGd (in); - fclose (in); + im2 = gdImageCreateFromGd(in); + fclose(in); - CompareImages ("GD->GD File->GD", ref, im2); + CompareImages("GD->GD File->GD", ref, im2); - unlink (of); - gdImageDestroy (im2); + unlink(of); + gdImageDestroy(im2); - iptr = gdImageGdPtr (im, &sz); - /*printf("Got ptr %d (size %d)\n",iptr, sz); */ - ctx = gdNewDynamicCtx (sz, iptr); - /*printf("Got ctx %d\n",ctx); */ - im2 = gdImageCreateFromGdCtx (ctx); - /*printf("Got img2 %d\n",im2); */ + iptr = gdImageGdPtr(im, &sz); + /*printf("Got ptr %d (size %d)\n",iptr, sz); */ + ctx = gdNewDynamicCtx(sz, iptr); + /*printf("Got ctx %d\n",ctx); */ + im2 = gdImageCreateFromGdCtx(ctx); + /*printf("Got img2 %d\n",im2); */ - CompareImages ("GD->GD ptr->GD", ref, im2); + CompareImages("GD->GD ptr->GD", ref, im2); - gdImageDestroy (im2); - ctx->gd_free (ctx); + gdImageDestroy(im2); + ctx->gd_free(ctx); - /* + /* ** Test gdImageCreateFromPngSource' * */ - in = fopen (argv[1], "rb"); + in = fopen(argv[1], "rb"); - imgsrc.source = freadWrapper; - imgsrc.context = in; - im2 = gdImageCreateFromPngSource (&imgsrc); - fclose (in); + imgsrc.source = freadWrapper; + imgsrc.context = in; + im2 = gdImageCreateFromPngSource(&imgsrc); + fclose(in); - if (im2 == NULL) - { - printf ("GD Source: ERROR Null returned by gdImageCreateFromPngSource\n"); - } - else - { - CompareImages ("GD Source", ref, im2); - gdImageDestroy (im2); + if (im2 == NULL) { + printf("GD Source: ERROR Null returned by gdImageCreateFromPngSource\n"); + } else { + CompareImages("GD Source", ref, im2); + gdImageDestroy(im2); }; - - /* + /* ** Test gdImagePngToSink' * */ - snprintf (of, sizeof(of), "%s.snk", argv[1]); - out = fopen (of, "wb"); - imgsnk.sink = fwriteWrapper; - imgsnk.context = out; - gdImagePngToSink (im, &imgsnk); - fclose (out); - in = fopen (of, "rb"); - if (!in) - { - fprintf (stderr, "GD Sink: ERROR - GD Sink Output file does not exist!\n"); - } - else - { - im2 = gdImageCreateFromPng (in); - fclose (in); - - CompareImages ("GD Sink", ref, im2); - gdImageDestroy (im2); + snprintf(of, sizeof(of), "%s.snk", argv[1]); + out = fopen(of, "wb"); + imgsnk.sink = fwriteWrapper; + imgsnk.context = out; + gdImagePngToSink(im, &imgsnk); + fclose(out); + in = fopen(of, "rb"); + if (!in) { + fprintf(stderr, "GD Sink: ERROR - GD Sink Output file does not exist!\n"); + } else { + im2 = gdImageCreateFromPng(in); + fclose(in); + + CompareImages("GD Sink", ref, im2); + gdImageDestroy(im2); }; - unlink (of); + unlink(of); - /* */ - /* Test Extraction */ - /* */ - in = fopen ("test/gdtest_200_300_150_100.png", "rb"); - if (!in) - { - fprintf (stderr, "gdtest_200_300_150_100.png does not exist!\n"); - exit (1); + /* */ + /* Test Extraction */ + /* */ + in = fopen("test/gdtest_200_300_150_100.png", "rb"); + if (!in) { + fprintf(stderr, "gdtest_200_300_150_100.png does not exist!\n"); + exit(1); } - im2 = gdImageCreateFromPng (in); - fclose (in); - + im2 = gdImageCreateFromPng(in); + fclose(in); - in = fopen ("test/gdtest.gd2", "rb"); - if (!in) - { - fprintf (stderr, "gdtest.gd2 does not exist!\n"); - exit (1); + in = fopen("test/gdtest.gd2", "rb"); + if (!in) { + fprintf(stderr, "gdtest.gd2 does not exist!\n"); + exit(1); } - im3 = gdImageCreateFromGd2Part (in, 200, 300, 150, 100); - fclose (in); - - CompareImages ("GD2Part (gdtest_200_300_150_100.png, gdtest.gd2(part))", im2, im3); - - gdImageDestroy (im2); - gdImageDestroy (im3); - - /* */ - /* Copy Blend */ - /* */ - in = fopen ("test/gdtest.png", "rb"); - if (!in) - { - fprintf (stderr, "gdtest.png does not exist!\n"); - exit (1); + im3 = gdImageCreateFromGd2Part(in, 200, 300, 150, 100); + fclose(in); + + CompareImages("GD2Part (gdtest_200_300_150_100.png, gdtest.gd2(part))", im2, im3); + + gdImageDestroy(im2); + gdImageDestroy(im3); + + /* */ + /* Copy Blend */ + /* */ + in = fopen("test/gdtest.png", "rb"); + if (!in) { + fprintf(stderr, "gdtest.png does not exist!\n"); + exit(1); } - im2 = gdImageCreateFromPng (in); - fclose (in); + im2 = gdImageCreateFromPng(in); + fclose(in); - im3 = gdImageCreate (100, 60); - colRed = gdImageColorAllocate (im3, 255, 0, 0); - colBlu = gdImageColorAllocate (im3, 0, 0, 255); - gdImageFilledRectangle (im3, 0, 0, 49, 30, colRed); - gdImageFilledRectangle (im3, 50, 30, 99, 59, colBlu); + im3 = gdImageCreate(100, 60); + colRed = gdImageColorAllocate(im3, 255, 0, 0); + colBlu = gdImageColorAllocate(im3, 0, 0, 255); + gdImageFilledRectangle(im3, 0, 0, 49, 30, colRed); + gdImageFilledRectangle(im3, 50, 30, 99, 59, colBlu); - gdImageCopyMerge (im2, im3, 150, 200, 10, 10, 90, 50, 50); - gdImageCopyMerge (im2, im3, 180, 70, 10, 10, 90, 50, 50); + gdImageCopyMerge(im2, im3, 150, 200, 10, 10, 90, 50, 50); + gdImageCopyMerge(im2, im3, 180, 70, 10, 10, 90, 50, 50); - gdImageCopyMergeGray (im2, im3, 250, 160, 10, 10, 90, 50, 50); - gdImageCopyMergeGray (im2, im3, 80, 70, 10, 10, 90, 50, 50); + gdImageCopyMergeGray(im2, im3, 250, 160, 10, 10, 90, 50, 50); + gdImageCopyMergeGray(im2, im3, 80, 70, 10, 10, 90, 50, 50); - gdImageDestroy (im3); + gdImageDestroy(im3); - in = fopen ("test/gdtest_merge.png", "rb"); - if (!in) - { - fprintf (stderr, "gdtest_merge.png does not exist!\n"); - exit (1); + in = fopen("test/gdtest_merge.png", "rb"); + if (!in) { + fprintf(stderr, "gdtest_merge.png does not exist!\n"); + exit(1); } - im3 = gdImageCreateFromPng (in); - fclose (in); + im3 = gdImageCreateFromPng(in); + fclose(in); - printf ("[Merged Image has %d colours]\n", im2->colorsTotal); - CompareImages ("Merged (gdtest.png, gdtest_merge.png)", im2, im3); + printf("[Merged Image has %d colours]\n", im2->colorsTotal); + CompareImages("Merged (gdtest.png, gdtest_merge.png)", im2, im3); - gdImageDestroy (im2); - gdImageDestroy (im3); + gdImageDestroy(im2); + gdImageDestroy(im3); #ifdef HAVE_JPEG - out = fopen ("test/gdtest.jpg", "wb"); - if (!out) - { - fprintf (stderr, "Can't create file test/gdtest.jpg.\n"); - exit (1); + out = fopen("test/gdtest.jpg", "wb"); + if (!out) { + fprintf(stderr, "Can't create file test/gdtest.jpg.\n"); + exit(1); } - gdImageJpeg (im, out, -1); - fclose (out); - in = fopen ("test/gdtest.jpg", "rb"); - if (!in) - { - fprintf (stderr, "Can't open file test/gdtest.jpg.\n"); - exit (1); + gdImageJpeg(im, out, -1); + fclose(out); + in = fopen("test/gdtest.jpg", "rb"); + if (!in) { + fprintf(stderr, "Can't open file test/gdtest.jpg.\n"); + exit(1); } - im2 = gdImageCreateFromJpeg (in); - fclose (in); - if (!im2) - { - fprintf (stderr, "gdImageCreateFromJpeg failed.\n"); - exit (1); + im2 = gdImageCreateFromJpeg(in); + fclose(in); + if (!im2) { + fprintf(stderr, "gdImageCreateFromJpeg failed.\n"); + exit(1); } - gdImageDestroy (im2); - printf ("Created test/gdtest.jpg successfully. Compare this image\n" - "to the input image manually. Some difference must be\n" - "expected as JPEG is a lossy file format.\n"); + gdImageDestroy(im2); + printf("Created test/gdtest.jpg successfully. Compare this image\n" + "to the input image manually. Some difference must be\n" + "expected as JPEG is a lossy file format.\n"); #endif /* HAVE_JPEG */ - /* Assume the color closest to black is the foreground - color for the B&W wbmp image. */ - fprintf (stderr, "NOTE: the WBMP output image will NOT match the original unless the original\n" - "is also black and white. This is OK!\n"); - foreground = gdImageColorClosest (im, 0, 0, 0); - fprintf (stderr, "Foreground index is %d\n", foreground); - if (foreground == -1) - { - fprintf (stderr, "Source image has no colors, skipping wbmp test.\n"); - } - else - { - out = fopen ("test/gdtest.wbmp", "wb"); - if (!out) - { - fprintf (stderr, "Can't create file test/gdtest.wbmp.\n"); - exit (1); - } - gdImageWBMP (im, foreground, out); - fclose (out); - in = fopen ("test/gdtest.wbmp", "rb"); - if (!in) - { - fprintf (stderr, "Can't open file test/gdtest.wbmp.\n"); - exit (1); - } - im2 = gdImageCreateFromWBMP (in); - fprintf (stderr, "WBMP has %d colors\n", gdImageColorsTotal (im2)); - fprintf (stderr, "WBMP colors are:\n"); - for (i = 0; (i < gdImageColorsTotal (im2)); i++) - { - fprintf (stderr, "%02X%02X%02X\n", - gdImageRed (im2, i), - gdImageGreen (im2, i), - gdImageBlue (im2, i)); - } - fclose (in); - if (!im2) - { - fprintf (stderr, "gdImageCreateFromWBMP failed.\n"); - exit (1); - } - CompareImages ("WBMP test (gdtest.png, gdtest.wbmp)", ref, im2); - out = fopen ("test/gdtest_wbmp_to_png.png", "wb"); - if (!out) - { - fprintf (stderr, "Can't create file test/gdtest_wbmp_to_png.png.\n"); - exit (1); - } - gdImagePng (im2, out); - fclose (out); - gdImageDestroy (im2); + /* Assume the color closest to black is the foreground + color for the B&W wbmp image. */ + fprintf(stderr, "NOTE: the WBMP output image will NOT match the original unless the original\n" + "is also black and white. This is OK!\n"); + foreground = gdImageColorClosest(im, 0, 0, 0); + fprintf(stderr, "Foreground index is %d\n", foreground); + if (foreground == -1) { + fprintf(stderr, "Source image has no colors, skipping wbmp test.\n"); + } else { + out = fopen("test/gdtest.wbmp", "wb"); + if (!out) { + fprintf(stderr, "Can't create file test/gdtest.wbmp.\n"); + exit(1); + } + gdImageWBMP(im, foreground, out); + fclose(out); + in = fopen("test/gdtest.wbmp", "rb"); + if (!in) { + fprintf(stderr, "Can't open file test/gdtest.wbmp.\n"); + exit(1); + } + im2 = gdImageCreateFromWBMP(in); + fprintf(stderr, "WBMP has %d colors\n", gdImageColorsTotal(im2)); + fprintf(stderr, "WBMP colors are:\n"); + for (i = 0; (i < gdImageColorsTotal(im2)); i++) { + fprintf(stderr, "%02X%02X%02X\n", gdImageRed(im2, i), gdImageGreen(im2, i), + gdImageBlue(im2, i)); + } + fclose(in); + if (!im2) { + fprintf(stderr, "gdImageCreateFromWBMP failed.\n"); + exit(1); + } + CompareImages("WBMP test (gdtest.png, gdtest.wbmp)", ref, im2); + out = fopen("test/gdtest_wbmp_to_png.png", "wb"); + if (!out) { + fprintf(stderr, "Can't create file test/gdtest_wbmp_to_png.png.\n"); + exit(1); + } + gdImagePng(im2, out); + fclose(out); + gdImageDestroy(im2); } - gdImageDestroy (im); - gdImageDestroy (ref); + gdImageDestroy(im); + gdImageDestroy(ref); - return 0; + return 0; } -void -CompareImages (char *msg, gdImagePtr im1, gdImagePtr im2) +void CompareImages(char *msg, gdImagePtr im1, gdImagePtr im2) { - int cmpRes; + int cmpRes; - cmpRes = gdImageCompare (im1, im2); + cmpRes = gdImageCompare(im1, im2); - if (cmpRes & GD_CMP_IMAGE) - { - printf ("%%%s: ERROR images differ: BAD\n", msg); - } - else if (cmpRes != 0) - { - printf ("%%%s: WARNING images differ: WARNING - Probably OK\n", msg); - } - else - { - printf ("%%%s: OK\n", msg); - return; + if (cmpRes & GD_CMP_IMAGE) { + printf("%%%s: ERROR images differ: BAD\n", msg); + } else if (cmpRes != 0) { + printf("%%%s: WARNING images differ: WARNING - Probably OK\n", msg); + } else { + printf("%%%s: OK\n", msg); + return; } - if (cmpRes & (GD_CMP_SIZE_X + GD_CMP_SIZE_Y)) - { - printf ("-%s: INFO image sizes differ\n", msg); + if (cmpRes & (GD_CMP_SIZE_X + GD_CMP_SIZE_Y)) { + printf("-%s: INFO image sizes differ\n", msg); } - if (cmpRes & GD_CMP_NUM_COLORS) - { - printf ("-%s: INFO number of palette entries differ %d Vs. %d\n", msg, - im1->colorsTotal, im2->colorsTotal); + if (cmpRes & GD_CMP_NUM_COLORS) { + printf("-%s: INFO number of palette entries differ %d Vs. %d\n", msg, im1->colorsTotal, + im2->colorsTotal); } - if (cmpRes & GD_CMP_COLOR) - { - printf ("-%s: INFO actual colours of pixels differ\n", msg); + if (cmpRes & GD_CMP_COLOR) { + printf("-%s: INFO actual colours of pixels differ\n", msg); } } - -static int -freadWrapper (void *context, char *buf, int len) +static int freadWrapper(void *context, char *buf, int len) { - int got = fread (buf, 1, len, (FILE *) context); - return got; + int got = fread(buf, 1, len, (FILE *)context); + return got; } -static int -fwriteWrapper (void *context, const char *buffer, int len) +static int fwriteWrapper(void *context, const char *buffer, int len) { - return fwrite (buffer, 1, len, (FILE *) context); + return fwrite(buffer, 1, len, (FILE *)context); } diff --git a/ext/gd/libgd/gdtestft.c b/ext/gd/libgd/gdtestft.c index fcac75896cf1..7351b33adf22 100644 --- a/ext/gd/libgd/gdtestft.c +++ b/ext/gd/libgd/gdtestft.c @@ -3,106 +3,100 @@ #include #define PI 3.141592 -#define DEG2RAD(x) ((x)*PI/180.) +#define DEG2RAD(x) ((x) * PI / 180.) -#define MAX(x,y) ((x) > (y) ? (x) : (y)) -#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX4(x,y,z,w) \ - ((MAX((x),(y))) > (MAX((z),(w))) ? (MAX((x),(y))) : (MAX((z),(w)))) -#define MIN4(x,y,z,w) \ - ((MIN((x),(y))) < (MIN((z),(w))) ? (MIN((x),(y))) : (MIN((z),(w)))) +#define MAX4(x, y, z, w) ((MAX((x), (y))) > (MAX((z), (w))) ? (MAX((x), (y))) : (MAX((z), (w)))) +#define MIN4(x, y, z, w) ((MIN((x), (y))) < (MIN((z), (w))) ? (MIN((x), (y))) : (MIN((z), (w)))) -#define MAXX(x) MAX4(x[0],x[2],x[4],x[6]) -#define MINX(x) MIN4(x[0],x[2],x[4],x[6]) -#define MAXY(x) MAX4(x[1],x[3],x[5],x[7]) -#define MINY(x) MIN4(x[1],x[3],x[5],x[7]) +#define MAXX(x) MAX4(x[0], x[2], x[4], x[6]) +#define MINX(x) MIN4(x[0], x[2], x[4], x[6]) +#define MAXY(x) MAX4(x[1], x[3], x[5], x[7]) +#define MINY(x) MIN4(x[1], x[3], x[5], x[7]) -int -main (int argc, char *argv[]) +int main(int argc, char *argv[]) { #ifndef HAVE_LIBFREETYPE - fprintf (stderr, "gd was not compiled with HAVE_LIBFREETYPE defined.\n"); - fprintf (stderr, "Install the FreeType library, including the\n"); - fprintf (stderr, "header files. Then edit the gd Makefile, type\n"); - fprintf (stderr, "make clean, and type make again.\n"); - return 1; + fprintf(stderr, "gd was not compiled with HAVE_LIBFREETYPE defined.\n"); + fprintf(stderr, "Install the FreeType library, including the\n"); + fprintf(stderr, "header files. Then edit the gd Makefile, type\n"); + fprintf(stderr, "make clean, and type make again.\n"); + return 1; #else - gdImagePtr im; - int black; - int white; - int brect[8]; - int x, y; - char *err; - FILE *out; + gdImagePtr im; + int black; + int white; + int brect[8]; + int x, y; + char *err; + FILE *out; #ifdef JISX0208 - char *s = "Hello. ‚±‚ñ‚É‚¿‚Í Qyjpqg,"; /* String to draw. */ + char *s = "Hello. ‚±‚ñ‚É‚¿‚Í Qyjpqg,"; /* String to draw. */ #else - char *s = "Hello. Qyjpqg,"; /* String to draw. */ + char *s = "Hello. Qyjpqg,"; /* String to draw. */ #endif - double sz = 40.; + double sz = 40.; #if 0 double angle = 0.; #else - double angle = DEG2RAD (-90); + double angle = DEG2RAD(-90); #endif #ifdef JISX0208 - char *f = "/usr/openwin/lib/locale/ja/X11/fonts/TT/HG-MinchoL.ttf"; /* UNICODE */ - /* char *f = "/usr/local/lib/fonts/truetype/DynaFont/dfpop1.ttf"; *//* SJIS */ + char *f = "/usr/openwin/lib/locale/ja/X11/fonts/TT/HG-MinchoL.ttf"; /* UNICODE */ + /* char *f = "/usr/local/lib/fonts/truetype/DynaFont/dfpop1.ttf"; */ /* SJIS */ #else - char *f = "times"; /* TrueType font */ + char *f = "times"; /* TrueType font */ #endif - /* obtain brect so that we can size the image */ - err = gdImageStringFT ((gdImagePtr) NULL, &brect[0], 0, f, sz, angle, 0, 0, s); - if (err) - { - fprintf (stderr, "%s", err); - return 1; + /* obtain brect so that we can size the image */ + err = gdImageStringFT((gdImagePtr)NULL, &brect[0], 0, f, sz, angle, 0, 0, s); + if (err) { + fprintf(stderr, "%s", err); + return 1; } - /* create an image just big enough for the string */ - x = MAXX (brect) - MINX (brect) + 6; - y = MAXY (brect) - MINY (brect) + 6; + /* create an image just big enough for the string */ + x = MAXX(brect) - MINX(brect) + 6; + y = MAXY(brect) - MINY(brect) + 6; #if 0 im = gdImageCreate (500, 500); #else - /* gd 2.0: true color images can use freetype too */ - im = gdImageCreateTrueColor (x, y); + /* gd 2.0: true color images can use freetype too */ + im = gdImageCreateTrueColor(x, y); #endif - /* Background color. gd 2.0: fill the image with it; truecolor - images have a black background otherwise. */ - white = gdImageColorResolve (im, 255, 255, 255); - gdImageFilledRectangle (im, 0, 0, x, y, white); - black = gdImageColorResolve (im, 0, 0, 0); + /* Background color. gd 2.0: fill the image with it; truecolor + images have a black background otherwise. */ + white = gdImageColorResolve(im, 255, 255, 255); + gdImageFilledRectangle(im, 0, 0, x, y, white); + black = gdImageColorResolve(im, 0, 0, 0); - /* render the string, offset origin to center string */ - x = 0 - MINX (brect) + 3; - y = 0 - MINY (brect) + 3; + /* render the string, offset origin to center string */ + x = 0 - MINX(brect) + 3; + y = 0 - MINY(brect) + 3; - err = gdImageStringFT (im, NULL, black, f, sz, angle, x, y, s); - if (err) - { - fprintf (stderr, "%s", err); - return 1; + err = gdImageStringFT(im, NULL, black, f, sz, angle, x, y, s); + if (err) { + fprintf(stderr, "%s", err); + return 1; } - /* TBB: Write img to test/fttest.png */ - out = fopen ("test/fttest.png", "wb"); - if (!out) - { - fprintf (stderr, "Can't create test/fttest.png\n"); - exit (1); + /* TBB: Write img to test/fttest.png */ + out = fopen("test/fttest.png", "wb"); + if (!out) { + fprintf(stderr, "Can't create test/fttest.png\n"); + exit(1); } - gdImagePng (im, out); - fclose (out); - fprintf (stderr, "Test image written to test/fttest.png\n"); - /* Destroy it */ - gdImageDestroy (im); + gdImagePng(im, out); + fclose(out); + fprintf(stderr, "Test image written to test/fttest.png\n"); + /* Destroy it */ + gdImageDestroy(im); - return 0; + return 0; #endif /* HAVE_LIBFREETYPE */ } diff --git a/ext/gd/libgd/gdtopng.c b/ext/gd/libgd/gdtopng.c index 9670b101976e..efe7147dcc12 100644 --- a/ext/gd/libgd/gdtopng.c +++ b/ext/gd/libgd/gdtopng.c @@ -1,44 +1,39 @@ -#include #include "gd.h" +#include /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in, *out; - if (argc != 3) - { - fprintf (stderr, "Usage: gdtopng filename.gd filename.png\n"); - exit (1); + gdImagePtr im; + FILE *in, *out; + if (argc != 3) { + fprintf(stderr, "Usage: gdtopng filename.gd filename.png\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromGd (in); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in GD format!\n"); - exit (1); + im = gdImageCreateFromGd(in); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in GD format!\n"); + exit(1); } - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } - gdImagePng (im, out); - fclose (out); - gdImageDestroy (im); + gdImagePng(im, out); + fclose(out); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/gdxpm.c b/ext/gd/libgd/gdxpm.c index 8db7fe043826..00d6475c8e62 100644 --- a/ext/gd/libgd/gdxpm.c +++ b/ext/gd/libgd/gdxpm.c @@ -1,139 +1,244 @@ - /* - add ability to load xpm files to gd, requires the xpm - library. - Caolan.McNamara@ul.ie - http://www.csn.ul.ie/~caolan + * Add ability to load xpm files to gd, requires the xpm + * library. + * Caolan.McNamara@ul.ie + * http://www.csn.ul.ie/~caolan + */ + +/** + * File: XPM Input + * + * Read XPM images. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gd.h" +#include "gd_color_map.h" +#include "gd_errors.h" +#include "gdhelpers.h" #include #include #include -#include "gd.h" -#include "gdhelpers.h" -#ifdef HAVE_XPM +#ifndef HAVE_LIBXPM +BGD_DECLARE(gdImagePtr) gdImageCreateFromXpm(char *filename) +{ + (void)filename; + gd_error_ex(GD_ERROR, "libgd was not built with xpm support\n"); + return NULL; +} +#else #include -gdImagePtr gdImageCreateFromXpm (char *filename) +/* + Function: gdImageCreateFromXpm + + is called to load images from XPM X Window + System color bitmap format files. This function is available only + if HAVE_XPM is selected in the Makefile and the Xpm library is + linked with the application. Unlike most gd file functions, the + Xpm functions *require filenames*, not file + pointers. returns a to the new + image, or NULL if unable to load the image (most often because the + file is corrupt or does not contain an XPM bitmap format + image). You can inspect the sx and sy members of the image to + determine its size. The image must eventually be destroyed using + . + + Parameters: + + filename - The input filename (*not* FILE pointer) + + Returns: + + A pointer to the new image or NULL if an error occurred. + + Example: + (start code) + + gdImagePtr im; + FILE *in; + in = fopen("myxpm.xpm", "rb"); + im = gdImageCreateFromXpm(in); + fclose(in); + // ... Use the image ... + gdImageDestroy(im); + + (end code) + +*/ +BGD_DECLARE(gdImagePtr) gdImageCreateFromXpm(char *filename) { - XpmInfo info = {0}; - XpmImage image; - int i, j, k, number; - char buf[5]; - gdImagePtr im = 0; - int *pointer; - int red = 0, green = 0, blue = 0; - int *colors; - int ret; - - ret = XpmReadFileToXpmImage(filename, &image, &info); - if (ret != XpmSuccess) { - return 0; - } - number = image.ncolors; - for(i = 0; i < number; i++) { - if (!image.colorTable[i].c_color) { - goto done; - } - } - - if (!(im = gdImageCreate(image.width, image.height))) { - goto done; - } - - colors = (int *) safe_emalloc(number, sizeof(int), 0); - for (i = 0; i < number; i++) { - switch (strlen (image.colorTable[i].c_color)) { - case 4: - buf[1] = '\0'; - buf[0] = image.colorTable[i].c_color[1]; - red = strtol(buf, NULL, 16); - - buf[0] = image.colorTable[i].c_color[2]; - green = strtol(buf, NULL, 16); - - buf[0] = image.colorTable[i].c_color[3]; - blue = strtol(buf, NULL, 16); - break; - - case 7: - buf[2] = '\0'; - buf[0] = image.colorTable[i].c_color[1]; - buf[1] = image.colorTable[i].c_color[2]; - red = strtol(buf, NULL, 16); - - buf[0] = image.colorTable[i].c_color[3]; - buf[1] = image.colorTable[i].c_color[4]; - green = strtol(buf, NULL, 16); - - buf[0] = image.colorTable[i].c_color[5]; - buf[1] = image.colorTable[i].c_color[6]; - blue = strtol(buf, NULL, 16); - break; - - case 10: - buf[3] = '\0'; - buf[0] = image.colorTable[i].c_color[1]; - buf[1] = image.colorTable[i].c_color[2]; - buf[2] = image.colorTable[i].c_color[3]; - red = strtol(buf, NULL, 16); - red /= 64; - - buf[0] = image.colorTable[i].c_color[4]; - buf[1] = image.colorTable[i].c_color[5]; - buf[2] = image.colorTable[i].c_color[6]; - green = strtol(buf, NULL, 16); - green /= 64; - - buf[0] = image.colorTable[i].c_color[7]; - buf[1] = image.colorTable[i].c_color[8]; - buf[2] = image.colorTable[i].c_color[9]; - blue = strtol(buf, NULL, 16); - blue /= 64; - break; - - case 13: - buf[4] = '\0'; - buf[0] = image.colorTable[i].c_color[1]; - buf[1] = image.colorTable[i].c_color[2]; - buf[2] = image.colorTable[i].c_color[3]; - buf[3] = image.colorTable[i].c_color[4]; - red = strtol(buf, NULL, 16); - red /= 256; - - buf[0] = image.colorTable[i].c_color[5]; - buf[1] = image.colorTable[i].c_color[6]; - buf[2] = image.colorTable[i].c_color[7]; - buf[3] = image.colorTable[i].c_color[8]; - green = strtol(buf, NULL, 16); - green /= 256; - - buf[0] = image.colorTable[i].c_color[9]; - buf[1] = image.colorTable[i].c_color[10]; - buf[2] = image.colorTable[i].c_color[11]; - buf[3] = image.colorTable[i].c_color[12]; - blue = strtol(buf, NULL, 16); - blue /= 256; - break; - } - - - colors[i] = gdImageColorResolve(im, red, green, blue); - } - - pointer = (int *) image.data; - for (i = 0; i < image.height; i++) { - for (j = 0; j < image.width; j++) { - k = *pointer++; - gdImageSetPixel(im, j, i, colors[k]); - } - } - - gdFree(colors); - done: - XpmFreeXpmImage(&image); - XpmFreeXpmInfo(&info); - return im; + XpmInfo info = {0}; + XpmImage image; + unsigned int i, j, k, number, len; + char buf[5]; + gdImagePtr im = 0; + int *pointer; + int red = 0, green = 0, blue = 0; + int *colors; + int ret; + + ret = XpmReadFileToXpmImage(filename, &image, &info); + if (ret != XpmSuccess) { + return 0; + } + + number = image.ncolors; + if (overflow2(sizeof(int), number)) { + goto done; + } + for (i = 0; i < number; i++) { + /* + avoid NULL pointer dereference + TODO better fix need to manage monochrome/monovisual + see m_color or g4_color or g_color + */ + if (!image.colorTable[i].c_color) { + goto done; + } + } + + colors = (int *)gdMalloc(sizeof(int) * number); + if (colors == NULL) { + goto done; + } + + if (!(im = gdImageCreate(image.width, image.height))) { + gdFree(colors); + goto done; + } + + for (i = 0; i < number; i++) { + char *c_color = image.colorTable[i].c_color; + int valid_color = 1; + if (strcmp(c_color, "None") == 0) { + colors[i] = gdImageGetTransparent(im); + if (colors[i] == -1) + colors[i] = gdImageColorAllocate(im, 0, 0, 0); + if (colors[i] != -1) + gdImageColorTransparent(im, colors[i]); + continue; + } + len = strlen(c_color); + if (len < 1) { + valid_color = 0; + } + if (c_color[0] == '#') { + switch (len) { + case 4: + buf[2] = '\0'; + buf[0] = buf[1] = c_color[1]; + red = strtol(buf, NULL, 16); + + buf[0] = buf[1] = c_color[2]; + green = strtol(buf, NULL, 16); + + buf[0] = buf[1] = c_color[3]; + blue = strtol(buf, NULL, 16); + break; + + case 7: + buf[2] = '\0'; + buf[0] = c_color[1]; + buf[1] = c_color[2]; + red = strtol(buf, NULL, 16); + + buf[0] = c_color[3]; + buf[1] = c_color[4]; + green = strtol(buf, NULL, 16); + + buf[0] = c_color[5]; + buf[1] = c_color[6]; + blue = strtol(buf, NULL, 16); + break; + + case 10: + buf[3] = '\0'; + buf[0] = c_color[1]; + buf[1] = c_color[2]; + buf[2] = c_color[3]; + red = strtol(buf, NULL, 16); + red /= 64; + + buf[0] = c_color[4]; + buf[1] = c_color[5]; + buf[2] = c_color[6]; + green = strtol(buf, NULL, 16); + green /= 64; + + buf[0] = c_color[7]; + buf[1] = c_color[8]; + buf[2] = c_color[9]; + blue = strtol(buf, NULL, 16); + blue /= 64; + break; + + case 13: + buf[4] = '\0'; + buf[0] = c_color[1]; + buf[1] = c_color[2]; + buf[2] = c_color[3]; + buf[3] = c_color[4]; + red = strtol(buf, NULL, 16); + red /= 256; + + buf[0] = c_color[5]; + buf[1] = c_color[6]; + buf[2] = c_color[7]; + buf[3] = c_color[8]; + green = strtol(buf, NULL, 16); + green /= 256; + + buf[0] = c_color[9]; + buf[1] = c_color[10]; + buf[2] = c_color[11]; + buf[3] = c_color[12]; + blue = strtol(buf, NULL, 16); + blue /= 256; + break; + default: + valid_color = 0; + break; + } + } else if (!gdColorMapLookup(GD_COLOR_MAP_X11, c_color, &red, &green, &blue)) { + valid_color = 0; + } + + if (!valid_color) { + gdFree(colors); + gdImageDestroy(im); + im = NULL; + goto done; + } + + colors[i] = gdImageColorResolve(im, red, green, blue); + } + + pointer = (int *)image.data; + + for (i = 0; i < image.height; i++) { + for (j = 0; j < image.width; j++) { + k = *pointer++; + if (k >= number) { + gdFree(colors); + gdImageDestroy(im); + im = NULL; + goto done; + } + gdImageSetPixel(im, j, i, colors[k]); + } + } + + gdFree(colors); + +done: + XpmFreeXpmImage(&image); + XpmFreeXpmInfo(&info); + return im; } -#endif +#endif /* HAVE_LIBXPM */ diff --git a/ext/gd/libgd/jisx0208.h b/ext/gd/libgd/jisx0208.h index f8a8b510fa6c..b502f0f2c5e8 100644 --- a/ext/gd/libgd/jisx0208.h +++ b/ext/gd/libgd/jisx0208.h @@ -5,1201 +5,834 @@ /* JIS -> Unicode mapping table */ static unsigned short UnicodeTbl[][94] = { -{ /* category 01 */ -0x0000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, -0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8, 0xFF3E, -0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, -0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0xFF3C, -0xFF5E, 0x2225, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019, 0x201C, -0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B, -0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, -0x300F, 0x3010, 0x3011, 0xFF0B, 0xFF0D, 0x00B1, 0x00D7, 0x00F7, -0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, -0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5, 0xFF04, -0xFFE0, 0xFFE1, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20, 0x00A7, -0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7}, -{ /* category 02 */ -0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, -0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, -0x2229, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x2227, 0x2228, 0xFFE2, 0x21D2, 0x21D4, 0x2200, 0x2203, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, -0x2261, 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, -0x222B, 0x222C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, /**/ -0x00B6, 0x0000, 0x0000, 0x0000, 0x0000, 0x25EF}, -{ /* category 03 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF10, -0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, -0xFF19, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, -0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, -0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, -0xFF39, 0xFF3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, -0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, -0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, -0xFF59, 0xFF5A, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 04 */ -0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, -0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, -0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, -0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, -0x3061, 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, -0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, -0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, -0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, -0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, -0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, -0x3091, 0x3092, 0x3093, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 05 */ -0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, -0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0, -0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, -0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, -0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, -0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, -0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, -0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0x30E0, -0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, -0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, 0x30F0, -0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 06 */ -0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, -0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, -0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, -0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, -0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 07 */ -0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, -0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, -0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, -0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, -0x042F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, -0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, -0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, -0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, -0x044F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 08 */ -0x2500, 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, -0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, -0x2517, 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, -0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 09 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 10 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 11 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 12 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 13 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 14 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 15 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 16 */ -0x4E9C, 0x5516, 0x5A03, 0x963F, 0x54C0, 0x611B, 0x6328, 0x59F6, -0x9022, 0x8475, 0x831C, 0x7A50, 0x60AA, 0x63E1, 0x6E25, 0x65ED, -0x8466, 0x82A6, 0x9BF5, 0x6893, 0x5727, 0x65A1, 0x6271, 0x5B9B, -0x59D0, 0x867B, 0x98F4, 0x7D62, 0x7DBE, 0x9B8E, 0x6216, 0x7C9F, -0x88B7, 0x5B89, 0x5EB5, 0x6309, 0x6697, 0x6848, 0x95C7, 0x978D, -0x674F, 0x4EE5, 0x4F0A, 0x4F4D, 0x4F9D, 0x5049, 0x56F2, 0x5937, -0x59D4, 0x5A01, 0x5C09, 0x60DF, 0x610F, 0x6170, 0x6613, 0x6905, -0x70BA, 0x754F, 0x7570, 0x79FB, 0x7DAD, 0x7DEF, 0x80C3, 0x840E, -0x8863, 0x8B02, 0x9055, 0x907A, 0x533B, 0x4E95, 0x4EA5, 0x57DF, -0x80B2, 0x90C1, 0x78EF, 0x4E00, 0x58F1, 0x6EA2, 0x9038, 0x7A32, -0x8328, 0x828B, 0x9C2F, 0x5141, 0x5370, 0x54BD, 0x54E1, 0x56E0, -0x59FB, 0x5F15, 0x98F2, 0x6DEB, 0x80E4, 0x852D}, -{ /* category 17 */ -0x9662, 0x9670, 0x96A0, 0x97FB, 0x540B, 0x53F3, 0x5B87, 0x70CF, -0x7FBD, 0x8FC2, 0x96E8, 0x536F, 0x9D5C, 0x7ABA, 0x4E11, 0x7893, -0x81FC, 0x6E26, 0x5618, 0x5504, 0x6B1D, 0x851A, 0x9C3B, 0x59E5, -0x53A9, 0x6D66, 0x74DC, 0x958F, 0x5642, 0x4E91, 0x904B, 0x96F2, -0x834F, 0x990C, 0x53E1, 0x55B6, 0x5B30, 0x5F71, 0x6620, 0x66F3, -0x6804, 0x6C38, 0x6CF3, 0x6D29, 0x745B, 0x76C8, 0x7A4E, 0x9834, -0x82F1, 0x885B, 0x8A60, 0x92ED, 0x6DB2, 0x75AB, 0x76CA, 0x99C5, -0x60A6, 0x8B01, 0x8D8A, 0x95B2, 0x698E, 0x53AD, 0x5186, 0x5712, -0x5830, 0x5944, 0x5BB4, 0x5EF6, 0x6028, 0x63A9, 0x63F4, 0x6CBF, -0x6F14, 0x708E, 0x7114, 0x7159, 0x71D5, 0x733F, 0x7E01, 0x8276, -0x82D1, 0x8597, 0x9060, 0x925B, 0x9D1B, 0x5869, 0x65BC, 0x6C5A, -0x7525, 0x51F9, 0x592E, 0x5965, 0x5F80, 0x5FDC}, -{ /* category 18 */ -0x62BC, 0x65FA, 0x6A2A, 0x6B27, 0x6BB4, 0x738B, 0x7FC1, 0x8956, -0x9D2C, 0x9D0E, 0x9EC4, 0x5CA1, 0x6C96, 0x837B, 0x5104, 0x5C4B, -0x61B6, 0x81C6, 0x6876, 0x7261, 0x4E59, 0x4FFA, 0x5378, 0x6069, -0x6E29, 0x7A4F, 0x97F3, 0x4E0B, 0x5316, 0x4EEE, 0x4F55, 0x4F3D, -0x4FA1, 0x4F73, 0x52A0, 0x53EF, 0x5609, 0x590F, 0x5AC1, 0x5BB6, -0x5BE1, 0x79D1, 0x6687, 0x679C, 0x67B6, 0x6B4C, 0x6CB3, 0x706B, -0x73C2, 0x798D, 0x79BE, 0x7A3C, 0x7B87, 0x82B1, 0x82DB, 0x8304, -0x8377, 0x83EF, 0x83D3, 0x8766, 0x8AB2, 0x5629, 0x8CA8, 0x8FE6, -0x904E, 0x971E, 0x868A, 0x4FC4, 0x5CE8, 0x6211, 0x7259, 0x753B, -0x81E5, 0x82BD, 0x86FE, 0x8CC0, 0x96C5, 0x9913, 0x99D5, 0x4ECB, -0x4F1A, 0x89E3, 0x56DE, 0x584A, 0x58CA, 0x5EFB, 0x5FEB, 0x602A, -0x6094, 0x6062, 0x61D0, 0x6212, 0x62D0, 0x6539}, -{ /* category 19 */ -0x9B41, 0x6666, 0x68B0, 0x6D77, 0x7070, 0x754C, 0x7686, 0x7D75, -0x82A5, 0x87F9, 0x958B, 0x968E, 0x8C9D, 0x51F1, 0x52BE, 0x5916, -0x54B3, 0x5BB3, 0x5D16, 0x6168, 0x6982, 0x6DAF, 0x788D, 0x84CB, -0x8857, 0x8A72, 0x93A7, 0x9AB8, 0x6D6C, 0x99A8, 0x86D9, 0x57A3, -0x67FF, 0x86CE, 0x920E, 0x5283, 0x5687, 0x5404, 0x5ED3, 0x62E1, -0x64B9, 0x683C, 0x6838, 0x6BBB, 0x7372, 0x78BA, 0x7A6B, 0x899A, -0x89D2, 0x8D6B, 0x8F03, 0x90ED, 0x95A3, 0x9694, 0x9769, 0x5B66, -0x5CB3, 0x697D, 0x984D, 0x984E, 0x639B, 0x7B20, 0x6A2B, 0x6A7F, -0x68B6, 0x9C0D, 0x6F5F, 0x5272, 0x559D, 0x6070, 0x62EC, 0x6D3B, -0x6E07, 0x6ED1, 0x845B, 0x8910, 0x8F44, 0x4E14, 0x9C39, 0x53F6, -0x691B, 0x6A3A, 0x9784, 0x682A, 0x515C, 0x7AC3, 0x84B2, 0x91DC, -0x938C, 0x565B, 0x9D28, 0x6822, 0x8305, 0x8431}, -{ /* category 20 */ -0x7CA5, 0x5208, 0x82C5, 0x74E6, 0x4E7E, 0x4F83, 0x51A0, 0x5BD2, -0x520A, 0x52D8, 0x52E7, 0x5DFB, 0x559A, 0x582A, 0x59E6, 0x5B8C, -0x5B98, 0x5BDB, 0x5E72, 0x5E79, 0x60A3, 0x611F, 0x6163, 0x61BE, -0x63DB, 0x6562, 0x67D1, 0x6853, 0x68FA, 0x6B3E, 0x6B53, 0x6C57, -0x6F22, 0x6F97, 0x6F45, 0x74B0, 0x7518, 0x76E3, 0x770B, 0x7AFF, -0x7BA1, 0x7C21, 0x7DE9, 0x7F36, 0x7FF0, 0x809D, 0x8266, 0x839E, -0x89B3, 0x8ACC, 0x8CAB, 0x9084, 0x9451, 0x9593, 0x9591, 0x95A2, -0x9665, 0x97D3, 0x9928, 0x8218, 0x4E38, 0x542B, 0x5CB8, 0x5DCC, -0x73A9, 0x764C, 0x773C, 0x5CA9, 0x7FEB, 0x8D0B, 0x96C1, 0x9811, -0x9854, 0x9858, 0x4F01, 0x4F0E, 0x5371, 0x559C, 0x5668, 0x57FA, -0x5947, 0x5B09, 0x5BC4, 0x5C90, 0x5E0C, 0x5E7E, 0x5FCC, 0x63EE, -0x673A, 0x65D7, 0x65E2, 0x671F, 0x68CB, 0x68C4}, -{ /* category 21 */ -0x6A5F, 0x5E30, 0x6BC5, 0x6C17, 0x6C7D, 0x757F, 0x7948, 0x5B63, -0x7A00, 0x7D00, 0x5FBD, 0x898F, 0x8A18, 0x8CB4, 0x8D77, 0x8ECC, -0x8F1D, 0x98E2, 0x9A0E, 0x9B3C, 0x4E80, 0x507D, 0x5100, 0x5993, -0x5B9C, 0x622F, 0x6280, 0x64EC, 0x6B3A, 0x72A0, 0x7591, 0x7947, -0x7FA9, 0x87FB, 0x8ABC, 0x8B70, 0x63AC, 0x83CA, 0x97A0, 0x5409, -0x5403, 0x55AB, 0x6854, 0x6A58, 0x8A70, 0x7827, 0x6775, 0x9ECD, -0x5374, 0x5BA2, 0x811A, 0x8650, 0x9006, 0x4E18, 0x4E45, 0x4EC7, -0x4F11, 0x53CA, 0x5438, 0x5BAE, 0x5F13, 0x6025, 0x6551, 0x673D, -0x6C42, 0x6C72, 0x6CE3, 0x7078, 0x7403, 0x7A76, 0x7AAE, 0x7B08, -0x7D1A, 0x7CFE, 0x7D66, 0x65E7, 0x725B, 0x53BB, 0x5C45, 0x5DE8, -0x62D2, 0x62E0, 0x6319, 0x6E20, 0x865A, 0x8A31, 0x8DDD, 0x92F8, -0x6F01, 0x79A6, 0x9B5A, 0x4EA8, 0x4EAB, 0x4EAC}, -{ /* category 22 */ -0x4F9B, 0x4FA0, 0x50D1, 0x5147, 0x7AF6, 0x5171, 0x51F6, 0x5354, -0x5321, 0x537F, 0x53EB, 0x55AC, 0x5883, 0x5CE1, 0x5F37, 0x5F4A, -0x602F, 0x6050, 0x606D, 0x631F, 0x6559, 0x6A4B, 0x6CC1, 0x72C2, -0x72ED, 0x77EF, 0x80F8, 0x8105, 0x8208, 0x854E, 0x90F7, 0x93E1, -0x97FF, 0x9957, 0x9A5A, 0x4EF0, 0x51DD, 0x5C2D, 0x6681, 0x696D, -0x5C40, 0x66F2, 0x6975, 0x7389, 0x6850, 0x7C81, 0x50C5, 0x52E4, -0x5747, 0x5DFE, 0x9326, 0x65A4, 0x6B23, 0x6B3D, 0x7434, 0x7981, -0x79BD, 0x7B4B, 0x7DCA, 0x82B9, 0x83CC, 0x887F, 0x895F, 0x8B39, -0x8FD1, 0x91D1, 0x541F, 0x9280, 0x4E5D, 0x5036, 0x53E5, 0x533A, -0x72D7, 0x7396, 0x77E9, 0x82E6, 0x8EAF, 0x99C6, 0x99C8, 0x99D2, -0x5177, 0x611A, 0x865E, 0x55B0, 0x7A7A, 0x5076, 0x5BD3, 0x9047, -0x9685, 0x4E32, 0x6ADB, 0x91E7, 0x5C51, 0x5C48}, -{ /* category 23 */ -0x6398, 0x7A9F, 0x6C93, 0x9774, 0x8F61, 0x7AAA, 0x718A, 0x9688, -0x7C82, 0x6817, 0x7E70, 0x6851, 0x936C, 0x52F2, 0x541B, 0x85AB, -0x8A13, 0x7FA4, 0x8ECD, 0x90E1, 0x5366, 0x8888, 0x7941, 0x4FC2, -0x50BE, 0x5211, 0x5144, 0x5553, 0x572D, 0x73EA, 0x578B, 0x5951, -0x5F62, 0x5F84, 0x6075, 0x6176, 0x6167, 0x61A9, 0x63B2, 0x643A, -0x656C, 0x666F, 0x6842, 0x6E13, 0x7566, 0x7A3D, 0x7CFB, 0x7D4C, -0x7D99, 0x7E4B, 0x7F6B, 0x830E, 0x834A, 0x86CD, 0x8A08, 0x8A63, -0x8B66, 0x8EFD, 0x981A, 0x9D8F, 0x82B8, 0x8FCE, 0x9BE8, 0x5287, -0x621F, 0x6483, 0x6FC0, 0x9699, 0x6841, 0x5091, 0x6B20, 0x6C7A, -0x6F54, 0x7A74, 0x7D50, 0x8840, 0x8A23, 0x6708, 0x4EF6, 0x5039, -0x5026, 0x5065, 0x517C, 0x5238, 0x5263, 0x55A7, 0x570F, 0x5805, -0x5ACC, 0x5EFA, 0x61B2, 0x61F8, 0x62F3, 0x6372}, -{ /* category 24 */ -0x691C, 0x6A29, 0x727D, 0x72AC, 0x732E, 0x7814, 0x786F, 0x7D79, -0x770C, 0x80A9, 0x898B, 0x8B19, 0x8CE2, 0x8ED2, 0x9063, 0x9375, -0x967A, 0x9855, 0x9A13, 0x9E78, 0x5143, 0x539F, 0x53B3, 0x5E7B, -0x5F26, 0x6E1B, 0x6E90, 0x7384, 0x73FE, 0x7D43, 0x8237, 0x8A00, -0x8AFA, 0x9650, 0x4E4E, 0x500B, 0x53E4, 0x547C, 0x56FA, 0x59D1, -0x5B64, 0x5DF1, 0x5EAB, 0x5F27, 0x6238, 0x6545, 0x67AF, 0x6E56, -0x72D0, 0x7CCA, 0x88B4, 0x80A1, 0x80E1, 0x83F0, 0x864E, 0x8A87, -0x8DE8, 0x9237, 0x96C7, 0x9867, 0x9F13, 0x4E94, 0x4E92, 0x4F0D, -0x5348, 0x5449, 0x543E, 0x5A2F, 0x5F8C, 0x5FA1, 0x609F, 0x68A7, -0x6A8E, 0x745A, 0x7881, 0x8A9E, 0x8AA4, 0x8B77, 0x9190, 0x4E5E, -0x9BC9, 0x4EA4, 0x4F7C, 0x4FAF, 0x5019, 0x5016, 0x5149, 0x516C, -0x529F, 0x52B9, 0x52FE, 0x539A, 0x53E3, 0x5411}, -{ /* category 25 */ -0x540E, 0x5589, 0x5751, 0x57A2, 0x597D, 0x5B54, 0x5B5D, 0x5B8F, -0x5DE5, 0x5DE7, 0x5DF7, 0x5E78, 0x5E83, 0x5E9A, 0x5EB7, 0x5F18, -0x6052, 0x614C, 0x6297, 0x62D8, 0x63A7, 0x653B, 0x6602, 0x6643, -0x66F4, 0x676D, 0x6821, 0x6897, 0x69CB, 0x6C5F, 0x6D2A, 0x6D69, -0x6E2F, 0x6E9D, 0x7532, 0x7687, 0x786C, 0x7A3F, 0x7CE0, 0x7D05, -0x7D18, 0x7D5E, 0x7DB1, 0x8015, 0x8003, 0x80AF, 0x80B1, 0x8154, -0x818F, 0x822A, 0x8352, 0x884C, 0x8861, 0x8B1B, 0x8CA2, 0x8CFC, -0x90CA, 0x9175, 0x9271, 0x783F, 0x92FC, 0x95A4, 0x964D, 0x9805, -0x9999, 0x9AD8, 0x9D3B, 0x525B, 0x52AB, 0x53F7, 0x5408, 0x58D5, -0x62F7, 0x6FE0, 0x8C6A, 0x8F5F, 0x9EB9, 0x514B, 0x523B, 0x544A, -0x56FD, 0x7A40, 0x9177, 0x9D60, 0x9ED2, 0x7344, 0x6F09, 0x8170, -0x7511, 0x5FFD, 0x60DA, 0x9AA8, 0x72DB, 0x8FBC}, -{ /* category 26 */ -0x6B64, 0x9803, 0x4ECA, 0x56F0, 0x5764, 0x58BE, 0x5A5A, 0x6068, -0x61C7, 0x660F, 0x6606, 0x6839, 0x68B1, 0x6DF7, 0x75D5, 0x7D3A, -0x826E, 0x9B42, 0x4E9B, 0x4F50, 0x53C9, 0x5506, 0x5D6F, 0x5DE6, -0x5DEE, 0x67FB, 0x6C99, 0x7473, 0x7802, 0x8A50, 0x9396, 0x88DF, -0x5750, 0x5EA7, 0x632B, 0x50B5, 0x50AC, 0x518D, 0x6700, 0x54C9, -0x585E, 0x59BB, 0x5BB0, 0x5F69, 0x624D, 0x63A1, 0x683D, 0x6B73, -0x6E08, 0x707D, 0x91C7, 0x7280, 0x7815, 0x7826, 0x796D, 0x658E, -0x7D30, 0x83DC, 0x88C1, 0x8F09, 0x969B, 0x5264, 0x5728, 0x6750, -0x7F6A, 0x8CA1, 0x51B4, 0x5742, 0x962A, 0x583A, 0x698A, 0x80B4, -0x54B2, 0x5D0E, 0x57FC, 0x7895, 0x9DFA, 0x4F5C, 0x524A, 0x548B, -0x643E, 0x6628, 0x6714, 0x67F5, 0x7A84, 0x7B56, 0x7D22, 0x932F, -0x685C, 0x9BAD, 0x7B39, 0x5319, 0x518A, 0x5237}, -{ /* category 27 */ -0x5BDF, 0x62F6, 0x64AE, 0x64E6, 0x672D, 0x6BBA, 0x85A9, 0x96D1, -0x7690, 0x9BD6, 0x634C, 0x9306, 0x9BAB, 0x76BF, 0x6652, 0x4E09, -0x5098, 0x53C2, 0x5C71, 0x60E8, 0x6492, 0x6563, 0x685F, 0x71E6, -0x73CA, 0x7523, 0x7B97, 0x7E82, 0x8695, 0x8B83, 0x8CDB, 0x9178, -0x9910, 0x65AC, 0x66AB, 0x6B8B, 0x4ED5, 0x4ED4, 0x4F3A, 0x4F7F, -0x523A, 0x53F8, 0x53F2, 0x55E3, 0x56DB, 0x58EB, 0x59CB, 0x59C9, -0x59FF, 0x5B50, 0x5C4D, 0x5E02, 0x5E2B, 0x5FD7, 0x601D, 0x6307, -0x652F, 0x5B5C, 0x65AF, 0x65BD, 0x65E8, 0x679D, 0x6B62, 0x6B7B, -0x6C0F, 0x7345, 0x7949, 0x79C1, 0x7CF8, 0x7D19, 0x7D2B, 0x80A2, -0x8102, 0x81F3, 0x8996, 0x8A5E, 0x8A69, 0x8A66, 0x8A8C, 0x8AEE, -0x8CC7, 0x8CDC, 0x96CC, 0x98FC, 0x6B6F, 0x4E8B, 0x4F3C, 0x4F8D, -0x5150, 0x5B57, 0x5BFA, 0x6148, 0x6301, 0x6642}, -{ /* category 28 */ -0x6B21, 0x6ECB, 0x6CBB, 0x723E, 0x74BD, 0x75D4, 0x78C1, 0x793A, -0x800C, 0x8033, 0x81EA, 0x8494, 0x8F9E, 0x6C50, 0x9E7F, 0x5F0F, -0x8B58, 0x9D2B, 0x7AFA, 0x8EF8, 0x5B8D, 0x96EB, 0x4E03, 0x53F1, -0x57F7, 0x5931, 0x5AC9, 0x5BA4, 0x6089, 0x6E7F, 0x6F06, 0x75BE, -0x8CEA, 0x5B9F, 0x8500, 0x7BE0, 0x5072, 0x67F4, 0x829D, 0x5C61, -0x854A, 0x7E1E, 0x820E, 0x5199, 0x5C04, 0x6368, 0x8D66, 0x659C, -0x716E, 0x793E, 0x7D17, 0x8005, 0x8B1D, 0x8ECA, 0x906E, 0x86C7, -0x90AA, 0x501F, 0x52FA, 0x5C3A, 0x6753, 0x707C, 0x7235, 0x914C, -0x91C8, 0x932B, 0x82E5, 0x5BC2, 0x5F31, 0x60F9, 0x4E3B, 0x53D6, -0x5B88, 0x624B, 0x6731, 0x6B8A, 0x72E9, 0x73E0, 0x7A2E, 0x816B, -0x8DA3, 0x9152, 0x9996, 0x5112, 0x53D7, 0x546A, 0x5BFF, 0x6388, -0x6A39, 0x7DAC, 0x9700, 0x56DA, 0x53CE, 0x5468}, -{ /* category 29 */ -0x5B97, 0x5C31, 0x5DDE, 0x4FEE, 0x6101, 0x62FE, 0x6D32, 0x79C0, -0x79CB, 0x7D42, 0x7E4D, 0x7FD2, 0x81ED, 0x821F, 0x8490, 0x8846, -0x8972, 0x8B90, 0x8E74, 0x8F2F, 0x9031, 0x914B, 0x916C, 0x96C6, -0x919C, 0x4EC0, 0x4F4F, 0x5145, 0x5341, 0x5F93, 0x620E, 0x67D4, -0x6C41, 0x6E0B, 0x7363, 0x7E26, 0x91CD, 0x9283, 0x53D4, 0x5919, -0x5BBF, 0x6DD1, 0x795D, 0x7E2E, 0x7C9B, 0x587E, 0x719F, 0x51FA, -0x8853, 0x8FF0, 0x4FCA, 0x5CFB, 0x6625, 0x77AC, 0x7AE3, 0x821C, -0x99FF, 0x51C6, 0x5FAA, 0x65EC, 0x696F, 0x6B89, 0x6DF3, 0x6E96, -0x6F64, 0x76FE, 0x7D14, 0x5DE1, 0x9075, 0x9187, 0x9806, 0x51E6, -0x521D, 0x6240, 0x6691, 0x66D9, 0x6E1A, 0x5EB6, 0x7DD2, 0x7F72, -0x66F8, 0x85AF, 0x85F7, 0x8AF8, 0x52A9, 0x53D9, 0x5973, 0x5E8F, -0x5F90, 0x6055, 0x92E4, 0x9664, 0x50B7, 0x511F}, -{ /* category 30 */ -0x52DD, 0x5320, 0x5347, 0x53EC, 0x54E8, 0x5546, 0x5531, 0x5617, -0x5968, 0x59BE, 0x5A3C, 0x5BB5, 0x5C06, 0x5C0F, 0x5C11, 0x5C1A, -0x5E84, 0x5E8A, 0x5EE0, 0x5F70, 0x627F, 0x6284, 0x62DB, 0x638C, -0x6377, 0x6607, 0x660C, 0x662D, 0x6676, 0x677E, 0x68A2, 0x6A1F, -0x6A35, 0x6CBC, 0x6D88, 0x6E09, 0x6E58, 0x713C, 0x7126, 0x7167, -0x75C7, 0x7701, 0x785D, 0x7901, 0x7965, 0x79F0, 0x7AE0, 0x7B11, -0x7CA7, 0x7D39, 0x8096, 0x83D6, 0x848B, 0x8549, 0x885D, 0x88F3, -0x8A1F, 0x8A3C, 0x8A54, 0x8A73, 0x8C61, 0x8CDE, 0x91A4, 0x9266, -0x937E, 0x9418, 0x969C, 0x9798, 0x4E0A, 0x4E08, 0x4E1E, 0x4E57, -0x5197, 0x5270, 0x57CE, 0x5834, 0x58CC, 0x5B22, 0x5E38, 0x60C5, -0x64FE, 0x6761, 0x6756, 0x6D44, 0x72B6, 0x7573, 0x7A63, 0x84B8, -0x8B72, 0x91B8, 0x9320, 0x5631, 0x57F4, 0x98FE}, -{ /* category 31 */ -0x62ED, 0x690D, 0x6B96, 0x71ED, 0x7E54, 0x8077, 0x8272, 0x89E6, -0x98DF, 0x8755, 0x8FB1, 0x5C3B, 0x4F38, 0x4FE1, 0x4FB5, 0x5507, -0x5A20, 0x5BDD, 0x5BE9, 0x5FC3, 0x614E, 0x632F, 0x65B0, 0x664B, -0x68EE, 0x699B, 0x6D78, 0x6DF1, 0x7533, 0x75B9, 0x771F, 0x795E, -0x79E6, 0x7D33, 0x81E3, 0x82AF, 0x85AA, 0x89AA, 0x8A3A, 0x8EAB, -0x8F9B, 0x9032, 0x91DD, 0x9707, 0x4EBA, 0x4EC1, 0x5203, 0x5875, -0x58EC, 0x5C0B, 0x751A, 0x5C3D, 0x814E, 0x8A0A, 0x8FC5, 0x9663, -0x976D, 0x7B25, 0x8ACF, 0x9808, 0x9162, 0x56F3, 0x53A8, 0x9017, -0x5439, 0x5782, 0x5E25, 0x63A8, 0x6C34, 0x708A, 0x7761, 0x7C8B, -0x7FE0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968F, 0x745E, -0x9AC4, 0x5D07, 0x5D69, 0x6570, 0x67A2, 0x8DA8, 0x96DB, 0x636E, -0x6749, 0x6919, 0x83C5, 0x9817, 0x96C0, 0x88FE}, -{ /* category 32 */ -0x6F84, 0x647A, 0x5BF8, 0x4E16, 0x702C, 0x755D, 0x662F, 0x51C4, -0x5236, 0x52E2, 0x59D3, 0x5F81, 0x6027, 0x6210, 0x653F, 0x6574, -0x661F, 0x6674, 0x68F2, 0x6816, 0x6B63, 0x6E05, 0x7272, 0x751F, -0x76DB, 0x7CBE, 0x8056, 0x58F0, 0x88FD, 0x897F, 0x8AA0, 0x8A93, -0x8ACB, 0x901D, 0x9192, 0x9752, 0x9759, 0x6589, 0x7A0E, 0x8106, -0x96BB, 0x5E2D, 0x60DC, 0x621A, 0x65A5, 0x6614, 0x6790, 0x77F3, -0x7A4D, 0x7C4D, 0x7E3E, 0x810A, 0x8CAC, 0x8D64, 0x8DE1, 0x8E5F, -0x78A9, 0x5207, 0x62D9, 0x63A5, 0x6442, 0x6298, 0x8A2D, 0x7A83, -0x7BC0, 0x8AAC, 0x96EA, 0x7D76, 0x820C, 0x8749, 0x4ED9, 0x5148, -0x5343, 0x5360, 0x5BA3, 0x5C02, 0x5C16, 0x5DDD, 0x6226, 0x6247, -0x64B0, 0x6813, 0x6834, 0x6CC9, 0x6D45, 0x6D17, 0x67D3, 0x6F5C, -0x714E, 0x717D, 0x65CB, 0x7A7F, 0x7BAD, 0x7DDA}, -{ /* category 33 */ -0x7E4A, 0x7FA8, 0x817A, 0x821B, 0x8239, 0x85A6, 0x8A6E, 0x8CCE, -0x8DF5, 0x9078, 0x9077, 0x92AD, 0x9291, 0x9583, 0x9BAE, 0x524D, -0x5584, 0x6F38, 0x7136, 0x5168, 0x7985, 0x7E55, 0x81B3, 0x7CCE, -0x564C, 0x5851, 0x5CA8, 0x63AA, 0x66FE, 0x66FD, 0x695A, 0x72D9, -0x758F, 0x758E, 0x790E, 0x7956, 0x79DF, 0x7C97, 0x7D20, 0x7D44, -0x8607, 0x8A34, 0x963B, 0x9061, 0x9F20, 0x50E7, 0x5275, 0x53CC, -0x53E2, 0x5009, 0x55AA, 0x58EE, 0x594F, 0x723D, 0x5B8B, 0x5C64, -0x531D, 0x60E3, 0x60F3, 0x635C, 0x6383, 0x633F, 0x63BB, 0x64CD, -0x65E9, 0x66F9, 0x5DE3, 0x69CD, 0x69FD, 0x6F15, 0x71E5, 0x4E89, -0x75E9, 0x76F8, 0x7A93, 0x7CDF, 0x7DCF, 0x7D9C, 0x8061, 0x8349, -0x8358, 0x846C, 0x84BC, 0x85FB, 0x88C5, 0x8D70, 0x9001, 0x906D, -0x9397, 0x971C, 0x9A12, 0x50CF, 0x5897, 0x618E}, -{ /* category 34 */ -0x81D3, 0x8535, 0x8D08, 0x9020, 0x4FC3, 0x5074, 0x5247, 0x5373, -0x606F, 0x6349, 0x675F, 0x6E2C, 0x8DB3, 0x901F, 0x4FD7, 0x5C5E, -0x8CCA, 0x65CF, 0x7D9A, 0x5352, 0x8896, 0x5176, 0x63C3, 0x5B58, -0x5B6B, 0x5C0A, 0x640D, 0x6751, 0x905C, 0x4ED6, 0x591A, 0x592A, -0x6C70, 0x8A51, 0x553E, 0x5815, 0x59A5, 0x60F0, 0x6253, 0x67C1, -0x8235, 0x6955, 0x9640, 0x99C4, 0x9A28, 0x4F53, 0x5806, 0x5BFE, -0x8010, 0x5CB1, 0x5E2F, 0x5F85, 0x6020, 0x614B, 0x6234, 0x66FF, -0x6CF0, 0x6EDE, 0x80CE, 0x817F, 0x82D4, 0x888B, 0x8CB8, 0x9000, -0x902E, 0x968A, 0x9EDB, 0x9BDB, 0x4EE3, 0x53F0, 0x5927, 0x7B2C, -0x918D, 0x984C, 0x9DF9, 0x6EDD, 0x7027, 0x5353, 0x5544, 0x5B85, -0x6258, 0x629E, 0x62D3, 0x6CA2, 0x6FEF, 0x7422, 0x8A17, 0x9438, -0x6FC1, 0x8AFE, 0x8338, 0x51E7, 0x86F8, 0x53EA}, -{ /* category 35 */ -0x53E9, 0x4F46, 0x9054, 0x8FB0, 0x596A, 0x8131, 0x5DFD, 0x7AEA, -0x8FBF, 0x68DA, 0x8C37, 0x72F8, 0x9C48, 0x6A3D, 0x8AB0, 0x4E39, -0x5358, 0x5606, 0x5766, 0x62C5, 0x63A2, 0x65E6, 0x6B4E, 0x6DE1, -0x6E5B, 0x70AD, 0x77ED, 0x7AEF, 0x7BAA, 0x7DBB, 0x803D, 0x80C6, -0x86CB, 0x8A95, 0x935B, 0x56E3, 0x58C7, 0x5F3E, 0x65AD, 0x6696, -0x6A80, 0x6BB5, 0x7537, 0x8AC7, 0x5024, 0x77E5, 0x5730, 0x5F1B, -0x6065, 0x667A, 0x6C60, 0x75F4, 0x7A1A, 0x7F6E, 0x81F4, 0x8718, -0x9045, 0x99B3, 0x7BC9, 0x755C, 0x7AF9, 0x7B51, 0x84C4, 0x9010, -0x79E9, 0x7A92, 0x8336, 0x5AE1, 0x7740, 0x4E2D, 0x4EF2, 0x5B99, -0x5FE0, 0x62BD, 0x663C, 0x67F1, 0x6CE8, 0x866B, 0x8877, 0x8A3B, -0x914E, 0x92F3, 0x99D0, 0x6A17, 0x7026, 0x732A, 0x82E7, 0x8457, -0x8CAF, 0x4E01, 0x5146, 0x51CB, 0x558B, 0x5BF5}, -{ /* category 36 */ -0x5E16, 0x5E33, 0x5E81, 0x5F14, 0x5F35, 0x5F6B, 0x5FB4, 0x61F2, -0x6311, 0x66A2, 0x671D, 0x6F6E, 0x7252, 0x753A, 0x773A, 0x8074, -0x8139, 0x8178, 0x8776, 0x8ABF, 0x8ADC, 0x8D85, 0x8DF3, 0x929A, -0x9577, 0x9802, 0x9CE5, 0x52C5, 0x6357, 0x76F4, 0x6715, 0x6C88, -0x73CD, 0x8CC3, 0x93AE, 0x9673, 0x6D25, 0x589C, 0x690E, 0x69CC, -0x8FFD, 0x939A, 0x75DB, 0x901A, 0x585A, 0x6802, 0x63B4, 0x69FB, -0x4F43, 0x6F2C, 0x67D8, 0x8FBB, 0x8526, 0x7DB4, 0x9354, 0x693F, -0x6F70, 0x576A, 0x58F7, 0x5B2C, 0x7D2C, 0x722A, 0x540A, 0x91E3, -0x9DB4, 0x4EAD, 0x4F4E, 0x505C, 0x5075, 0x5243, 0x8C9E, 0x5448, -0x5824, 0x5B9A, 0x5E1D, 0x5E95, 0x5EAD, 0x5EF7, 0x5F1F, 0x608C, -0x62B5, 0x633A, 0x63D0, 0x68AF, 0x6C40, 0x7887, 0x798E, 0x7A0B, -0x7DE0, 0x8247, 0x8A02, 0x8AE6, 0x8E44, 0x9013}, -{ /* category 37 */ -0x90B8, 0x912D, 0x91D8, 0x9F0E, 0x6CE5, 0x6458, 0x64E2, 0x6575, -0x6EF4, 0x7684, 0x7B1B, 0x9069, 0x93D1, 0x6EBA, 0x54F2, 0x5FB9, -0x64A4, 0x8F4D, 0x8FED, 0x9244, 0x5178, 0x586B, 0x5929, 0x5C55, -0x5E97, 0x6DFB, 0x7E8F, 0x751C, 0x8CBC, 0x8EE2, 0x985B, 0x70B9, -0x4F1D, 0x6BBF, 0x6FB1, 0x7530, 0x96FB, 0x514E, 0x5410, 0x5835, -0x5857, 0x59AC, 0x5C60, 0x5F92, 0x6597, 0x675C, 0x6E21, 0x767B, -0x83DF, 0x8CED, 0x9014, 0x90FD, 0x934D, 0x7825, 0x783A, 0x52AA, -0x5EA6, 0x571F, 0x5974, 0x6012, 0x5012, 0x515A, 0x51AC, 0x51CD, -0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5B95, 0x5CF6, 0x5D8B, -0x60BC, 0x6295, 0x642D, 0x6771, 0x6843, 0x68BC, 0x68DF, 0x76D7, -0x6DD8, 0x6E6F, 0x6D9B, 0x706F, 0x71C8, 0x5F53, 0x75D8, 0x7977, -0x7B49, 0x7B54, 0x7B52, 0x7CD6, 0x7D71, 0x5230}, -{ /* category 38 */ -0x8463, 0x8569, 0x85E4, 0x8A0E, 0x8B04, 0x8C46, 0x8E0F, 0x9003, -0x900F, 0x9419, 0x9676, 0x982D, 0x9A30, 0x95D8, 0x50CD, 0x52D5, -0x540C, 0x5802, 0x5C0E, 0x61A7, 0x649E, 0x6D1E, 0x77B3, 0x7AE5, -0x80F4, 0x8404, 0x9053, 0x9285, 0x5CE0, 0x9D07, 0x533F, 0x5F97, -0x5FB3, 0x6D9C, 0x7279, 0x7763, 0x79BF, 0x7BE4, 0x6BD2, 0x72EC, -0x8AAD, 0x6803, 0x6A61, 0x51F8, 0x7A81, 0x6934, 0x5C4A, 0x9CF6, -0x82EB, 0x5BC5, 0x9149, 0x701E, 0x5678, 0x5C6F, 0x60C7, 0x6566, -0x6C8C, 0x8C5A, 0x9041, 0x9813, 0x5451, 0x66C7, 0x920D, 0x5948, -0x90A3, 0x5185, 0x4E4D, 0x51EA, 0x8599, 0x8B0E, 0x7058, 0x637A, -0x934B, 0x6962, 0x99B4, 0x7E04, 0x7577, 0x5357, 0x6960, 0x8EDF, -0x96E3, 0x6C5D, 0x4E8C, 0x5C3C, 0x5F10, 0x8FE9, 0x5302, 0x8CD1, -0x8089, 0x8679, 0x5EFF, 0x65E5, 0x4E73, 0x5165}, -{ /* category 39 */ -0x5982, 0x5C3F, 0x97EE, 0x4EFB, 0x598A, 0x5FCD, 0x8A8D, 0x6FE1, -0x79B0, 0x7962, 0x5BE7, 0x8471, 0x732B, 0x71B1, 0x5E74, 0x5FF5, -0x637B, 0x649A, 0x71C3, 0x7C98, 0x4E43, 0x5EFC, 0x4E4B, 0x57DC, -0x56A2, 0x60A9, 0x6FC3, 0x7D0D, 0x80FD, 0x8133, 0x81BF, 0x8FB2, -0x8997, 0x86A4, 0x5DF4, 0x628A, 0x64AD, 0x8987, 0x6777, 0x6CE2, -0x6D3E, 0x7436, 0x7834, 0x5A46, 0x7F75, 0x82AD, 0x99AC, 0x4FF3, -0x5EC3, 0x62DD, 0x6392, 0x6557, 0x676F, 0x76C3, 0x724C, 0x80CC, -0x80BA, 0x8F29, 0x914D, 0x500D, 0x57F9, 0x5A92, 0x6885, 0x6973, -0x7164, 0x72FD, 0x8CB7, 0x58F2, 0x8CE0, 0x966A, 0x9019, 0x877F, -0x79E4, 0x77E7, 0x8429, 0x4F2F, 0x5265, 0x535A, 0x62CD, 0x67CF, -0x6CCA, 0x767D, 0x7B94, 0x7C95, 0x8236, 0x8584, 0x8FEB, 0x66DD, -0x6F20, 0x7206, 0x7E1B, 0x83AB, 0x99C1, 0x9EA6}, -{ /* category 40 */ -0x51FD, 0x7BB1, 0x7872, 0x7BB8, 0x8087, 0x7B48, 0x6AE8, 0x5E61, -0x808C, 0x7551, 0x7560, 0x516B, 0x9262, 0x6E8C, 0x767A, 0x9197, -0x9AEA, 0x4F10, 0x7F70, 0x629C, 0x7B4F, 0x95A5, 0x9CE9, 0x567A, -0x5859, 0x86E4, 0x96BC, 0x4F34, 0x5224, 0x534A, 0x53CD, 0x53DB, -0x5E06, 0x642C, 0x6591, 0x677F, 0x6C3E, 0x6C4E, 0x7248, 0x72AF, -0x73ED, 0x7554, 0x7E41, 0x822C, 0x85E9, 0x8CA9, 0x7BC4, 0x91C6, -0x7169, 0x9812, 0x98EF, 0x633D, 0x6669, 0x756A, 0x76E4, 0x78D0, -0x8543, 0x86EE, 0x532A, 0x5351, 0x5426, 0x5983, 0x5E87, 0x5F7C, -0x60B2, 0x6249, 0x6279, 0x62AB, 0x6590, 0x6BD4, 0x6CCC, 0x75B2, -0x76AE, 0x7891, 0x79D8, 0x7DCB, 0x7F77, 0x80A5, 0x88AB, 0x8AB9, -0x8CBB, 0x907F, 0x975E, 0x98DB, 0x6A0B, 0x7C38, 0x5099, 0x5C3E, -0x5FAE, 0x6787, 0x6BD8, 0x7435, 0x7709, 0x7F8E}, -{ /* category 41 */ -0x9F3B, 0x67CA, 0x7A17, 0x5339, 0x758B, 0x9AED, 0x5F66, 0x819D, -0x83F1, 0x8098, 0x5F3C, 0x5FC5, 0x7562, 0x7B46, 0x903C, 0x6867, -0x59EB, 0x5A9B, 0x7D10, 0x767E, 0x8B2C, 0x4FF5, 0x5F6A, 0x6A19, -0x6C37, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8A55, 0x8C79, 0x5EDF, -0x63CF, 0x75C5, 0x79D2, 0x82D7, 0x9328, 0x92F2, 0x849C, 0x86ED, -0x9C2D, 0x54C1, 0x5F6C, 0x658C, 0x6D5C, 0x7015, 0x8CA7, 0x8CD3, -0x983B, 0x654F, 0x74F6, 0x4E0D, 0x4ED8, 0x57E0, 0x592B, 0x5A66, -0x5BCC, 0x51A8, 0x5E03, 0x5E9C, 0x6016, 0x6276, 0x6577, 0x65A7, -0x666E, 0x6D6E, 0x7236, 0x7B26, 0x8150, 0x819A, 0x8299, 0x8B5C, -0x8CA0, 0x8CE6, 0x8D74, 0x961C, 0x9644, 0x4FAE, 0x64AB, 0x6B66, -0x821E, 0x8461, 0x856A, 0x90E8, 0x5C01, 0x6953, 0x98A8, 0x847A, -0x8557, 0x4F0F, 0x526F, 0x5FA9, 0x5E45, 0x670D}, -{ /* category 42 */ -0x798F, 0x8179, 0x8907, 0x8986, 0x6DF5, 0x5F17, 0x6255, 0x6CB8, -0x4ECF, 0x7269, 0x9B92, 0x5206, 0x543B, 0x5674, 0x58B3, 0x61A4, -0x626E, 0x711A, 0x596E, 0x7C89, 0x7CDE, 0x7D1B, 0x96F0, 0x6587, -0x805E, 0x4E19, 0x4F75, 0x5175, 0x5840, 0x5E63, 0x5E73, 0x5F0A, -0x67C4, 0x4E26, 0x853D, 0x9589, 0x965B, 0x7C73, 0x9801, 0x50FB, -0x58C1, 0x7656, 0x78A7, 0x5225, 0x77A5, 0x8511, 0x7B86, 0x504F, -0x5909, 0x7247, 0x7BC7, 0x7DE8, 0x8FBA, 0x8FD4, 0x904D, 0x4FBF, -0x52C9, 0x5A29, 0x5F01, 0x97AD, 0x4FDD, 0x8217, 0x92EA, 0x5703, -0x6355, 0x6B69, 0x752B, 0x88DC, 0x8F14, 0x7A42, 0x52DF, 0x5893, -0x6155, 0x620A, 0x66AE, 0x6BCD, 0x7C3F, 0x83E9, 0x5023, 0x4FF8, -0x5305, 0x5446, 0x5831, 0x5949, 0x5B9D, 0x5CF0, 0x5CEF, 0x5D29, -0x5E96, 0x62B1, 0x6367, 0x653E, 0x65B9, 0x670B}, -{ /* category 43 */ -0x6CD5, 0x6CE1, 0x70F9, 0x7832, 0x7E2B, 0x80DE, 0x82B3, 0x840C, -0x84EC, 0x8702, 0x8912, 0x8A2A, 0x8C4A, 0x90A6, 0x92D2, 0x98FD, -0x9CF3, 0x9D6C, 0x4E4F, 0x4EA1, 0x508D, 0x5256, 0x574A, 0x59A8, -0x5E3D, 0x5FD8, 0x5FD9, 0x623F, 0x66B4, 0x671B, 0x67D0, 0x68D2, -0x5192, 0x7D21, 0x80AA, 0x81A8, 0x8B00, 0x8C8C, 0x8CBF, 0x927E, -0x9632, 0x5420, 0x982C, 0x5317, 0x50D5, 0x535C, 0x58A8, 0x64B2, -0x6734, 0x7267, 0x7766, 0x7A46, 0x91E6, 0x52C3, 0x6CA1, 0x6B86, -0x5800, 0x5E4C, 0x5954, 0x672C, 0x7FFB, 0x51E1, 0x76C6, 0x6469, -0x78E8, 0x9B54, 0x9EBB, 0x57CB, 0x59B9, 0x6627, 0x679A, 0x6BCE, -0x54E9, 0x69D9, 0x5E55, 0x819C, 0x6795, 0x9BAA, 0x67FE, 0x9C52, -0x685D, 0x4EA6, 0x4FE3, 0x53C8, 0x62B9, 0x672B, 0x6CAB, 0x8FC4, -0x4FAD, 0x7E6D, 0x9EBF, 0x4E07, 0x6162, 0x6E80}, -{ /* category 44 */ -0x6F2B, 0x8513, 0x5473, 0x672A, 0x9B45, 0x5DF3, 0x7B95, 0x5CAC, -0x5BC6, 0x871C, 0x6E4A, 0x84D1, 0x7A14, 0x8108, 0x5999, 0x7C8D, -0x6C11, 0x7720, 0x52D9, 0x5922, 0x7121, 0x725F, 0x77DB, 0x9727, -0x9D61, 0x690B, 0x5A7F, 0x5A18, 0x51A5, 0x540D, 0x547D, 0x660E, -0x76DF, 0x8FF7, 0x9298, 0x9CF4, 0x59EA, 0x725D, 0x6EC5, 0x514D, -0x68C9, 0x7DBF, 0x7DEC, 0x9762, 0x9EBA, 0x6478, 0x6A21, 0x8302, -0x5984, 0x5B5F, 0x6BDB, 0x731B, 0x76F2, 0x7DB2, 0x8017, 0x8499, -0x5132, 0x6728, 0x9ED9, 0x76EE, 0x6762, 0x52FF, 0x9905, 0x5C24, -0x623B, 0x7C7E, 0x8CB0, 0x554F, 0x60B6, 0x7D0B, 0x9580, 0x5301, -0x4E5F, 0x51B6, 0x591C, 0x723A, 0x8036, 0x91CE, 0x5F25, 0x77E2, -0x5384, 0x5F79, 0x7D04, 0x85AC, 0x8A33, 0x8E8D, 0x9756, 0x67F3, -0x85AE, 0x9453, 0x6109, 0x6108, 0x6CB9, 0x7652}, -{ /* category 45 */ -0x8AED, 0x8F38, 0x552F, 0x4F51, 0x512A, 0x52C7, 0x53CB, 0x5BA5, -0x5E7D, 0x60A0, 0x6182, 0x63D6, 0x6709, 0x67DA, 0x6E67, 0x6D8C, -0x7336, 0x7337, 0x7531, 0x7950, 0x88D5, 0x8A98, 0x904A, 0x9091, -0x90F5, 0x96C4, 0x878D, 0x5915, 0x4E88, 0x4F59, 0x4E0E, 0x8A89, -0x8F3F, 0x9810, 0x50AD, 0x5E7C, 0x5996, 0x5BB9, 0x5EB8, 0x63DA, -0x63FA, 0x64C1, 0x66DC, 0x694A, 0x69D8, 0x6D0B, 0x6EB6, 0x7194, -0x7528, 0x7AAF, 0x7F8A, 0x8000, 0x8449, 0x84C9, 0x8981, 0x8B21, -0x8E0A, 0x9065, 0x967D, 0x990A, 0x617E, 0x6291, 0x6B32, 0x6C83, -0x6D74, 0x7FCC, 0x7FFC, 0x6DC0, 0x7F85, 0x87BA, 0x88F8, 0x6765, -0x83B1, 0x983C, 0x96F7, 0x6D1B, 0x7D61, 0x843D, 0x916A, 0x4E71, -0x5375, 0x5D50, 0x6B04, 0x6FEB, 0x85CD, 0x862D, 0x89A7, 0x5229, -0x540F, 0x5C65, 0x674E, 0x68A8, 0x7406, 0x7483}, -{ /* category 46 */ -0x75E2, 0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x9678, 0x5F8B, 0x7387, -0x7ACB, 0x844E, 0x63A0, 0x7565, 0x5289, 0x6D41, 0x6E9C, 0x7409, -0x7559, 0x786B, 0x7C92, 0x9686, 0x7ADC, 0x9F8D, 0x4FB6, 0x616E, -0x65C5, 0x865C, 0x4E86, 0x4EAE, 0x50DA, 0x4E21, 0x51CC, 0x5BEE, -0x6599, 0x6881, 0x6DBC, 0x731F, 0x7642, 0x77AD, 0x7A1C, 0x7CE7, -0x826F, 0x8AD2, 0x907C, 0x91CF, 0x9675, 0x9818, 0x529B, 0x7DD1, -0x502B, 0x5398, 0x6797, 0x6DCB, 0x71D0, 0x7433, 0x81E8, 0x8F2A, -0x96A3, 0x9C57, 0x9E9F, 0x7460, 0x5841, 0x6D99, 0x7D2F, 0x985E, -0x4EE4, 0x4F36, 0x4F8B, 0x51B7, 0x52B1, 0x5DBA, 0x601C, 0x73B2, -0x793C, 0x82D3, 0x9234, 0x96B7, 0x96F6, 0x970A, 0x9E97, 0x9F62, -0x66A6, 0x6B74, 0x5217, 0x52A3, 0x70C8, 0x88C2, 0x5EC9, 0x604B, -0x6190, 0x6F23, 0x7149, 0x7C3E, 0x7DF4, 0x806F}, -{ /* category 47 */ -0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089, 0x8CC2, -0x8DEF, 0x9732, 0x52B4, 0x5A41, 0x5ECA, 0x5F04, 0x6717, 0x697C, -0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001, 0x807E, -0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332, 0x8AD6, -0x502D, 0x548C, 0x8A71, 0x6B6A, 0x8CC4, 0x8107, 0x60D1, 0x67A0, -0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568, 0x6900, -0x6E7E, 0x7897, 0x8155, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 48 */ -0x5F0C, 0x4E10, 0x4E15, 0x4E2A, 0x4E31, 0x4E36, 0x4E3C, 0x4E3F, -0x4E42, 0x4E56, 0x4E58, 0x4E82, 0x4E85, 0x8C6B, 0x4E8A, 0x8212, -0x5F0D, 0x4E8E, 0x4E9E, 0x4E9F, 0x4EA0, 0x4EA2, 0x4EB0, 0x4EB3, -0x4EB6, 0x4ECE, 0x4ECD, 0x4EC4, 0x4EC6, 0x4EC2, 0x4ED7, 0x4EDE, -0x4EED, 0x4EDF, 0x4EF7, 0x4F09, 0x4F5A, 0x4F30, 0x4F5B, 0x4F5D, -0x4F57, 0x4F47, 0x4F76, 0x4F88, 0x4F8F, 0x4F98, 0x4F7B, 0x4F69, -0x4F70, 0x4F91, 0x4F6F, 0x4F86, 0x4F96, 0x5118, 0x4FD4, 0x4FDF, -0x4FCE, 0x4FD8, 0x4FDB, 0x4FD1, 0x4FDA, 0x4FD0, 0x4FE4, 0x4FE5, -0x501A, 0x5028, 0x5014, 0x502A, 0x5025, 0x5005, 0x4F1C, 0x4FF6, -0x5021, 0x5029, 0x502C, 0x4FFE, 0x4FEF, 0x5011, 0x5006, 0x5043, -0x5047, 0x6703, 0x5055, 0x5050, 0x5048, 0x505A, 0x5056, 0x506C, -0x5078, 0x5080, 0x509A, 0x5085, 0x50B4, 0x50B2}, -{ /* category 49 */ -0x50C9, 0x50CA, 0x50B3, 0x50C2, 0x50D6, 0x50DE, 0x50E5, 0x50ED, -0x50E3, 0x50EE, 0x50F9, 0x50F5, 0x5109, 0x5101, 0x5102, 0x5116, -0x5115, 0x5114, 0x511A, 0x5121, 0x513A, 0x5137, 0x513C, 0x513B, -0x513F, 0x5140, 0x5152, 0x514C, 0x5154, 0x5162, 0x7AF8, 0x5169, -0x516A, 0x516E, 0x5180, 0x5182, 0x56D8, 0x518C, 0x5189, 0x518F, -0x5191, 0x5193, 0x5195, 0x5196, 0x51A4, 0x51A6, 0x51A2, 0x51A9, -0x51AA, 0x51AB, 0x51B3, 0x51B1, 0x51B2, 0x51B0, 0x51B5, 0x51BD, -0x51C5, 0x51C9, 0x51DB, 0x51E0, 0x8655, 0x51E9, 0x51ED, 0x51F0, -0x51F5, 0x51FE, 0x5204, 0x520B, 0x5214, 0x520E, 0x5227, 0x522A, -0x522E, 0x5233, 0x5239, 0x524F, 0x5244, 0x524B, 0x524C, 0x525E, -0x5254, 0x526A, 0x5274, 0x5269, 0x5273, 0x527F, 0x527D, 0x528D, -0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8FA8}, -{ /* category 50 */ -0x8FA7, 0x52AC, 0x52AD, 0x52BC, 0x52B5, 0x52C1, 0x52CD, 0x52D7, -0x52DE, 0x52E3, 0x52E6, 0x98ED, 0x52E0, 0x52F3, 0x52F5, 0x52F8, -0x52F9, 0x5306, 0x5308, 0x7538, 0x530D, 0x5310, 0x530F, 0x5315, -0x531A, 0x5323, 0x532F, 0x5331, 0x5333, 0x5338, 0x5340, 0x5346, -0x5345, 0x4E17, 0x5349, 0x534D, 0x51D6, 0x535E, 0x5369, 0x536E, -0x5918, 0x537B, 0x5377, 0x5382, 0x5396, 0x53A0, 0x53A6, 0x53A5, -0x53AE, 0x53B0, 0x53B6, 0x53C3, 0x7C12, 0x96D9, 0x53DF, 0x66FC, -0x71EE, 0x53EE, 0x53E8, 0x53ED, 0x53FA, 0x5401, 0x543D, 0x5440, -0x542C, 0x542D, 0x543C, 0x542E, 0x5436, 0x5429, 0x541D, 0x544E, -0x548F, 0x5475, 0x548E, 0x545F, 0x5471, 0x5477, 0x5470, 0x5492, -0x547B, 0x5480, 0x5476, 0x5484, 0x5490, 0x5486, 0x54C7, 0x54A2, -0x54B8, 0x54A5, 0x54AC, 0x54C4, 0x54C8, 0x54A8}, -{ /* category 51 */ -0x54AB, 0x54C2, 0x54A4, 0x54BE, 0x54BC, 0x54D8, 0x54E5, 0x54E6, -0x550F, 0x5514, 0x54FD, 0x54EE, 0x54ED, 0x54FA, 0x54E2, 0x5539, -0x5540, 0x5563, 0x554C, 0x552E, 0x555C, 0x5545, 0x5556, 0x5557, -0x5538, 0x5533, 0x555D, 0x5599, 0x5580, 0x54AF, 0x558A, 0x559F, -0x557B, 0x557E, 0x5598, 0x559E, 0x55AE, 0x557C, 0x5583, 0x55A9, -0x5587, 0x55A8, 0x55DA, 0x55C5, 0x55DF, 0x55C4, 0x55DC, 0x55E4, -0x55D4, 0x5614, 0x55F7, 0x5616, 0x55FE, 0x55FD, 0x561B, 0x55F9, -0x564E, 0x5650, 0x71DF, 0x5634, 0x5636, 0x5632, 0x5638, 0x566B, -0x5664, 0x562F, 0x566C, 0x566A, 0x5686, 0x5680, 0x568A, 0x56A0, -0x5694, 0x568F, 0x56A5, 0x56AE, 0x56B6, 0x56B4, 0x56C2, 0x56BC, -0x56C1, 0x56C3, 0x56C0, 0x56C8, 0x56CE, 0x56D1, 0x56D3, 0x56D7, -0x56EE, 0x56F9, 0x5700, 0x56FF, 0x5704, 0x5709}, -{ /* category 52 */ -0x5708, 0x570B, 0x570D, 0x5713, 0x5718, 0x5716, 0x55C7, 0x571C, -0x5726, 0x5737, 0x5738, 0x574E, 0x573B, 0x5740, 0x574F, 0x5769, -0x57C0, 0x5788, 0x5761, 0x577F, 0x5789, 0x5793, 0x57A0, 0x57B3, -0x57A4, 0x57AA, 0x57B0, 0x57C3, 0x57C6, 0x57D4, 0x57D2, 0x57D3, -0x580A, 0x57D6, 0x57E3, 0x580B, 0x5819, 0x581D, 0x5872, 0x5821, -0x5862, 0x584B, 0x5870, 0x6BC0, 0x5852, 0x583D, 0x5879, 0x5885, -0x58B9, 0x589F, 0x58AB, 0x58BA, 0x58DE, 0x58BB, 0x58B8, 0x58AE, -0x58C5, 0x58D3, 0x58D1, 0x58D7, 0x58D9, 0x58D8, 0x58E5, 0x58DC, -0x58E4, 0x58DF, 0x58EF, 0x58FA, 0x58F9, 0x58FB, 0x58FC, 0x58FD, -0x5902, 0x590A, 0x5910, 0x591B, 0x68A6, 0x5925, 0x592C, 0x592D, -0x5932, 0x5938, 0x593E, 0x7AD2, 0x5955, 0x5950, 0x594E, 0x595A, -0x5958, 0x5962, 0x5960, 0x5967, 0x596C, 0x5969}, -{ /* category 53 */ -0x5978, 0x5981, 0x599D, 0x4F5E, 0x4FAB, 0x59A3, 0x59B2, 0x59C6, -0x59E8, 0x59DC, 0x598D, 0x59D9, 0x59DA, 0x5A25, 0x5A1F, 0x5A11, -0x5A1C, 0x5A09, 0x5A1A, 0x5A40, 0x5A6C, 0x5A49, 0x5A35, 0x5A36, -0x5A62, 0x5A6A, 0x5A9A, 0x5ABC, 0x5ABE, 0x5ACB, 0x5AC2, 0x5ABD, -0x5AE3, 0x5AD7, 0x5AE6, 0x5AE9, 0x5AD6, 0x5AFA, 0x5AFB, 0x5B0C, -0x5B0B, 0x5B16, 0x5B32, 0x5AD0, 0x5B2A, 0x5B36, 0x5B3E, 0x5B43, -0x5B45, 0x5B40, 0x5B51, 0x5B55, 0x5B5A, 0x5B5B, 0x5B65, 0x5B69, -0x5B70, 0x5B73, 0x5B75, 0x5B78, 0x6588, 0x5B7A, 0x5B80, 0x5B83, -0x5BA6, 0x5BB8, 0x5BC3, 0x5BC7, 0x5BC9, 0x5BD4, 0x5BD0, 0x5BE4, -0x5BE6, 0x5BE2, 0x5BDE, 0x5BE5, 0x5BEB, 0x5BF0, 0x5BF6, 0x5BF3, -0x5C05, 0x5C07, 0x5C08, 0x5C0D, 0x5C13, 0x5C20, 0x5C22, 0x5C28, -0x5C38, 0x5C39, 0x5C41, 0x5C46, 0x5C4E, 0x5C53}, -{ /* category 54 */ -0x5C50, 0x5C4F, 0x5B71, 0x5C6C, 0x5C6E, 0x4E62, 0x5C76, 0x5C79, -0x5C8C, 0x5C91, 0x5C94, 0x599B, 0x5CAB, 0x5CBB, 0x5CB6, 0x5CBC, -0x5CB7, 0x5CC5, 0x5CBE, 0x5CC7, 0x5CD9, 0x5CE9, 0x5CFD, 0x5CFA, -0x5CED, 0x5D8C, 0x5CEA, 0x5D0B, 0x5D15, 0x5D17, 0x5D5C, 0x5D1F, -0x5D1B, 0x5D11, 0x5D14, 0x5D22, 0x5D1A, 0x5D19, 0x5D18, 0x5D4C, -0x5D52, 0x5D4E, 0x5D4B, 0x5D6C, 0x5D73, 0x5D76, 0x5D87, 0x5D84, -0x5D82, 0x5DA2, 0x5D9D, 0x5DAC, 0x5DAE, 0x5DBD, 0x5D90, 0x5DB7, -0x5DBC, 0x5DC9, 0x5DCD, 0x5DD3, 0x5DD2, 0x5DD6, 0x5DDB, 0x5DEB, -0x5DF2, 0x5DF5, 0x5E0B, 0x5E1A, 0x5E19, 0x5E11, 0x5E1B, 0x5E36, -0x5E37, 0x5E44, 0x5E43, 0x5E40, 0x5E4E, 0x5E57, 0x5E54, 0x5E5F, -0x5E62, 0x5E64, 0x5E47, 0x5E75, 0x5E76, 0x5E7A, 0x9EBC, 0x5E7F, -0x5EA0, 0x5EC1, 0x5EC2, 0x5EC8, 0x5ED0, 0x5ECF}, -{ /* category 55 */ -0x5ED6, 0x5EE3, 0x5EDD, 0x5EDA, 0x5EDB, 0x5EE2, 0x5EE1, 0x5EE8, -0x5EE9, 0x5EEC, 0x5EF1, 0x5EF3, 0x5EF0, 0x5EF4, 0x5EF8, 0x5EFE, -0x5F03, 0x5F09, 0x5F5D, 0x5F5C, 0x5F0B, 0x5F11, 0x5F16, 0x5F29, -0x5F2D, 0x5F38, 0x5F41, 0x5F48, 0x5F4C, 0x5F4E, 0x5F2F, 0x5F51, -0x5F56, 0x5F57, 0x5F59, 0x5F61, 0x5F6D, 0x5F73, 0x5F77, 0x5F83, -0x5F82, 0x5F7F, 0x5F8A, 0x5F88, 0x5F91, 0x5F87, 0x5F9E, 0x5F99, -0x5F98, 0x5FA0, 0x5FA8, 0x5FAD, 0x5FBC, 0x5FD6, 0x5FFB, 0x5FE4, -0x5FF8, 0x5FF1, 0x5FDD, 0x60B3, 0x5FFF, 0x6021, 0x6060, 0x6019, -0x6010, 0x6029, 0x600E, 0x6031, 0x601B, 0x6015, 0x602B, 0x6026, -0x600F, 0x603A, 0x605A, 0x6041, 0x606A, 0x6077, 0x605F, 0x604A, -0x6046, 0x604D, 0x6063, 0x6043, 0x6064, 0x6042, 0x606C, 0x606B, -0x6059, 0x6081, 0x608D, 0x60E7, 0x6083, 0x609A}, -{ /* category 56 */ -0x6084, 0x609B, 0x6096, 0x6097, 0x6092, 0x60A7, 0x608B, 0x60E1, -0x60B8, 0x60E0, 0x60D3, 0x60B4, 0x5FF0, 0x60BD, 0x60C6, 0x60B5, -0x60D8, 0x614D, 0x6115, 0x6106, 0x60F6, 0x60F7, 0x6100, 0x60F4, -0x60FA, 0x6103, 0x6121, 0x60FB, 0x60F1, 0x610D, 0x610E, 0x6147, -0x613E, 0x6128, 0x6127, 0x614A, 0x613F, 0x613C, 0x612C, 0x6134, -0x613D, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159, 0x615A, -0x616B, 0x6174, 0x616F, 0x6165, 0x6171, 0x615F, 0x615D, 0x6153, -0x6175, 0x6199, 0x6196, 0x6187, 0x61AC, 0x6194, 0x619A, 0x618A, -0x6191, 0x61AB, 0x61AE, 0x61CC, 0x61CA, 0x61C9, 0x61F7, 0x61C8, -0x61C3, 0x61C6, 0x61BA, 0x61CB, 0x7F79, 0x61CD, 0x61E6, 0x61E3, -0x61F6, 0x61FA, 0x61F4, 0x61FF, 0x61FD, 0x61FC, 0x61FE, 0x6200, -0x6208, 0x6209, 0x620D, 0x620C, 0x6214, 0x621B}, -{ /* category 57 */ -0x621E, 0x6221, 0x622A, 0x622E, 0x6230, 0x6232, 0x6233, 0x6241, -0x624E, 0x625E, 0x6263, 0x625B, 0x6260, 0x6268, 0x627C, 0x6282, -0x6289, 0x627E, 0x6292, 0x6293, 0x6296, 0x62D4, 0x6283, 0x6294, -0x62D7, 0x62D1, 0x62BB, 0x62CF, 0x62FF, 0x62C6, 0x64D4, 0x62C8, -0x62DC, 0x62CC, 0x62CA, 0x62C2, 0x62C7, 0x629B, 0x62C9, 0x630C, -0x62EE, 0x62F1, 0x6327, 0x6302, 0x6308, 0x62EF, 0x62F5, 0x6350, -0x633E, 0x634D, 0x641C, 0x634F, 0x6396, 0x638E, 0x6380, 0x63AB, -0x6376, 0x63A3, 0x638F, 0x6389, 0x639F, 0x63B5, 0x636B, 0x6369, -0x63BE, 0x63E9, 0x63C0, 0x63C6, 0x63E3, 0x63C9, 0x63D2, 0x63F6, -0x63C4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436, 0x651D, -0x6417, 0x6428, 0x640F, 0x6467, 0x646F, 0x6476, 0x644E, 0x652A, -0x6495, 0x6493, 0x64A5, 0x64A9, 0x6488, 0x64BC}, -{ /* category 58 */ -0x64DA, 0x64D2, 0x64C5, 0x64C7, 0x64BB, 0x64D8, 0x64C2, 0x64F1, -0x64E7, 0x8209, 0x64E0, 0x64E1, 0x62AC, 0x64E3, 0x64EF, 0x652C, -0x64F6, 0x64F4, 0x64F2, 0x64FA, 0x6500, 0x64FD, 0x6518, 0x651C, -0x6505, 0x6524, 0x6523, 0x652B, 0x6534, 0x6535, 0x6537, 0x6536, -0x6538, 0x754B, 0x6548, 0x6556, 0x6555, 0x654D, 0x6558, 0x655E, -0x655D, 0x6572, 0x6578, 0x6582, 0x6583, 0x8B8A, 0x659B, 0x659F, -0x65AB, 0x65B7, 0x65C3, 0x65C6, 0x65C1, 0x65C4, 0x65CC, 0x65D2, -0x65DB, 0x65D9, 0x65E0, 0x65E1, 0x65F1, 0x6772, 0x660A, 0x6603, -0x65FB, 0x6773, 0x6635, 0x6636, 0x6634, 0x661C, 0x664F, 0x6644, -0x6649, 0x6641, 0x665E, 0x665D, 0x6664, 0x6667, 0x6668, 0x665F, -0x6662, 0x6670, 0x6683, 0x6688, 0x668E, 0x6689, 0x6684, 0x6698, -0x669D, 0x66C1, 0x66B9, 0x66C9, 0x66BE, 0x66BC}, -{ /* category 59 */ -0x66C4, 0x66B8, 0x66D6, 0x66DA, 0x66E0, 0x663F, 0x66E6, 0x66E9, -0x66F0, 0x66F5, 0x66F7, 0x670F, 0x6716, 0x671E, 0x6726, 0x6727, -0x9738, 0x672E, 0x673F, 0x6736, 0x6741, 0x6738, 0x6737, 0x6746, -0x675E, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770, 0x67A9, -0x677C, 0x676A, 0x678C, 0x678B, 0x67A6, 0x67A1, 0x6785, 0x67B7, -0x67EF, 0x67B4, 0x67EC, 0x67B3, 0x67E9, 0x67B8, 0x67E4, 0x67DE, -0x67DD, 0x67E2, 0x67EE, 0x67B9, 0x67CE, 0x67C6, 0x67E7, 0x6A9C, -0x681E, 0x6846, 0x6829, 0x6840, 0x684D, 0x6832, 0x684E, 0x68B3, -0x682B, 0x6859, 0x6863, 0x6877, 0x687F, 0x689F, 0x688F, 0x68AD, -0x6894, 0x689D, 0x689B, 0x6883, 0x6AAE, 0x68B9, 0x6874, 0x68B5, -0x68A0, 0x68BA, 0x690F, 0x688D, 0x687E, 0x6901, 0x68CA, 0x6908, -0x68D8, 0x6922, 0x6926, 0x68E1, 0x690C, 0x68CD}, -{ /* category 60 */ -0x68D4, 0x68E7, 0x68D5, 0x6936, 0x6912, 0x6904, 0x68D7, 0x68E3, -0x6925, 0x68F9, 0x68E0, 0x68EF, 0x6928, 0x692A, 0x691A, 0x6923, -0x6921, 0x68C6, 0x6979, 0x6977, 0x695C, 0x6978, 0x696B, 0x6954, -0x697E, 0x696E, 0x6939, 0x6974, 0x693D, 0x6959, 0x6930, 0x6961, -0x695E, 0x695D, 0x6981, 0x696A, 0x69B2, 0x69AE, 0x69D0, 0x69BF, -0x69C1, 0x69D3, 0x69BE, 0x69CE, 0x5BE8, 0x69CA, 0x69DD, 0x69BB, -0x69C3, 0x69A7, 0x6A2E, 0x6991, 0x69A0, 0x699C, 0x6995, 0x69B4, -0x69DE, 0x69E8, 0x6A02, 0x6A1B, 0x69FF, 0x6B0A, 0x69F9, 0x69F2, -0x69E7, 0x6A05, 0x69B1, 0x6A1E, 0x69ED, 0x6A14, 0x69EB, 0x6A0A, -0x6A12, 0x6AC1, 0x6A23, 0x6A13, 0x6A44, 0x6A0C, 0x6A72, 0x6A36, -0x6A78, 0x6A47, 0x6A62, 0x6A59, 0x6A66, 0x6A48, 0x6A38, 0x6A22, -0x6A90, 0x6A8D, 0x6AA0, 0x6A84, 0x6AA2, 0x6AA3}, -{ /* category 61 */ -0x6A97, 0x8617, 0x6ABB, 0x6AC3, 0x6AC2, 0x6AB8, 0x6AB3, 0x6AAC, -0x6ADE, 0x6AD1, 0x6ADF, 0x6AAA, 0x6ADA, 0x6AEA, 0x6AFB, 0x6B05, -0x8616, 0x6AFA, 0x6B12, 0x6B16, 0x9B31, 0x6B1F, 0x6B38, 0x6B37, -0x76DC, 0x6B39, 0x98EE, 0x6B47, 0x6B43, 0x6B49, 0x6B50, 0x6B59, -0x6B54, 0x6B5B, 0x6B5F, 0x6B61, 0x6B78, 0x6B79, 0x6B7F, 0x6B80, -0x6B84, 0x6B83, 0x6B8D, 0x6B98, 0x6B95, 0x6B9E, 0x6BA4, 0x6BAA, -0x6BAB, 0x6BAF, 0x6BB2, 0x6BB1, 0x6BB3, 0x6BB7, 0x6BBC, 0x6BC6, -0x6BCB, 0x6BD3, 0x6BDF, 0x6BEC, 0x6BEB, 0x6BF3, 0x6BEF, 0x9EBE, -0x6C08, 0x6C13, 0x6C14, 0x6C1B, 0x6C24, 0x6C23, 0x6C5E, 0x6C55, -0x6C62, 0x6C6A, 0x6C82, 0x6C8D, 0x6C9A, 0x6C81, 0x6C9B, 0x6C7E, -0x6C68, 0x6C73, 0x6C92, 0x6C90, 0x6CC4, 0x6CF1, 0x6CD3, 0x6CBD, -0x6CD7, 0x6CC5, 0x6CDD, 0x6CAE, 0x6CB1, 0x6CBE}, -{ /* category 62 */ -0x6CBA, 0x6CDB, 0x6CEF, 0x6CD9, 0x6CEA, 0x6D1F, 0x884D, 0x6D36, -0x6D2B, 0x6D3D, 0x6D38, 0x6D19, 0x6D35, 0x6D33, 0x6D12, 0x6D0C, -0x6D63, 0x6D93, 0x6D64, 0x6D5A, 0x6D79, 0x6D59, 0x6D8E, 0x6D95, -0x6FE4, 0x6D85, 0x6DF9, 0x6E15, 0x6E0A, 0x6DB5, 0x6DC7, 0x6DE6, -0x6DB8, 0x6DC6, 0x6DEC, 0x6DDE, 0x6DCC, 0x6DE8, 0x6DD2, 0x6DC5, -0x6DFA, 0x6DD9, 0x6DE4, 0x6DD5, 0x6DEA, 0x6DEE, 0x6E2D, 0x6E6E, -0x6E2E, 0x6E19, 0x6E72, 0x6E5F, 0x6E3E, 0x6E23, 0x6E6B, 0x6E2B, -0x6E76, 0x6E4D, 0x6E1F, 0x6E43, 0x6E3A, 0x6E4E, 0x6E24, 0x6EFF, -0x6E1D, 0x6E38, 0x6E82, 0x6EAA, 0x6E98, 0x6EC9, 0x6EB7, 0x6ED3, -0x6EBD, 0x6EAF, 0x6EC4, 0x6EB2, 0x6ED4, 0x6ED5, 0x6E8F, 0x6EA5, -0x6EC2, 0x6E9F, 0x6F41, 0x6F11, 0x704C, 0x6EEC, 0x6EF8, 0x6EFE, -0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC}, -{ /* category 63 */ -0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, -0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58, 0x6F8E, -0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, -0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1, -0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001, 0x700F, -0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030, -0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, -0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD, 0x70D9, -0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, -0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184, 0x7195, -0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9, 0x71D4, -0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC}, -{ /* category 64 */ -0x71F9, 0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, -0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246, 0x724B, -0x7258, 0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, -0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, -0x72D2, 0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F, 0x7317, -0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, -0x733E, 0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, -0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE, 0x73BB, -0x73C0, 0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, -0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, -0x745C, 0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E, 0x748B, -0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1}, -{ /* category 65 */ -0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, -0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E, 0x750D, -0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, -0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567, -0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758A, -0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2, -0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, -0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF, 0x75FC, -0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, -0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, 0x7630, -0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661, 0x7662, -0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670}, -{ /* category 66 */ -0x7672, 0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, -0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4, 0x76B8, -0x76B9, 0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, -0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, -0x7729, 0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737, 0x7738, -0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, -0x7779, 0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, -0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD, 0x77D7, -0x77DA, 0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, -0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, -0x788C, 0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6, 0x78CB, -0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC}, -{ /* category 67 */ -0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, -0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A, 0x7955, -0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, -0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC, -0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20, 0x7A1F, -0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49, -0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, -0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0, 0x7AB6, -0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, -0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2, 0x7AE6, -0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33, 0x7B18, -0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50}, -{ /* category 68 */ -0x7B7A, 0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, -0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, -0x7B9F, 0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, -0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, -0x7BE9, 0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00, 0x7C07, -0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, -0x7C2A, 0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, -0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56, 0x7C65, -0x7C6C, 0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, -0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, -0x7CC0, 0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, -0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06}, -{ /* category 69 */ -0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, -0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72, 0x7D68, -0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, -0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB, -0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, -0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A, -0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, -0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37, 0x7E32, -0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, -0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D, 0x8FAE, -0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93, 0x7E94, -0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A}, -{ /* category 70 */ -0x7F45, 0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, -0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78, 0x7F82, -0x7F86, 0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, -0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, -0x8B71, 0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, -0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, -0x8018, 0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, -0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068, 0x8073, -0x8072, 0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, -0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, -0x80E5, 0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109, 0x80EF, -0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B}, -{ /* category 71 */ -0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, -0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180, 0x8182, -0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, -0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9, -0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF, 0x81E0, -0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207, -0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, -0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264, 0x8262, -0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, -0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1, 0x82E3, -0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303, 0x82FB, -0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9}, -{ /* category 72 */ -0x8335, 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, -0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A, 0x83AA, -0x839F, 0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, -0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, -0x83EB, 0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1, 0x83F7, -0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, -0x8506, 0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, -0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C, 0x846F, -0x8479, 0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, -0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, -0x8521, 0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F, 0x8515, -0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548}, -{ /* category 73 */ -0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, -0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587, 0x859C, -0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, -0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B, -0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F, 0x864D, -0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9, -0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, -0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC, 0x86DF, -0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, -0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F, 0x8737, -0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778, 0x874C, -0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759}, -{ /* category 74 */ -0x8753, 0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, -0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4, 0x87B3, -0x87C7, 0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, -0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, -0x8822, 0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B, 0x8844, -0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, -0x889E, 0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, -0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF, 0x88B1, -0x88C3, 0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, -0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, -0x8943, 0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944, 0x893B, -0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E}, -{ /* category 75 */ -0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, -0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9, 0x89A6, -0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, -0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10, -0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B, 0x8A52, -0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82, -0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, -0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7, 0x8AE4, -0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, -0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20, 0x8B33, -0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, -0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B}, -{ /* category 76 */ -0x8B5F, 0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, -0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, -0x8C48, 0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, -0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, -0x8C7C, 0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, -0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, -0x8CFA, 0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, -0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67, 0x8D6D, -0x8D71, 0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, -0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, -0x8DE3, 0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, -0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A}, -{ /* category 77 */ -0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, -0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81, 0x8E87, -0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, -0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5, -0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, -0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F, -0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, -0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C, 0x8F62, -0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, -0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4, 0x9005, -0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E, 0x9016, -0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8}, -{ /* category 78 */ -0x904F, 0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, -0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072, 0x9082, -0x907D, 0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, -0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, -0x9119, 0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163, 0x9165, -0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, -0x91AF, 0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, -0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC, 0x91F5, -0x91F6, 0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, -0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, -0x9250, 0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF, 0x92B9, -0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E}, -{ /* category 79 */ -0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, -0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD, 0x9394, -0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, -0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407, -0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441, 0x9452, -0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470, -0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, -0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599, 0x95A0, -0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, -0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6, 0x95DC, -0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F, 0x9642, -0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E}, -{ /* category 80 */ -0x965D, 0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, -0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4, 0x96B6, -0x96B8, 0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, -0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, -0x9711, 0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730, 0x9739, -0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, -0x9760, 0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, -0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F, 0x9790, -0x979C, 0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, -0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, -0x97F5, 0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837, 0x983D, -0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870}, -{ /* category 81 */ -0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, -0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912, 0x9914, -0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, -0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951, -0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD, 0x99AE, -0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE, -0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, -0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43, 0x9A3E, -0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, -0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0, 0x9ACF, -0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, -0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7}, -{ /* category 82 */ -0x9AFB, 0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, -0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, -0x9B43, 0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, -0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, -0x9BC0, 0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, -0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, -0x9C15, 0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, -0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21, 0x9C30, -0x9C47, 0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, -0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, -0x9D06, 0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44, 0x9D15, -0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48}, -{ /* category 83 */ -0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, -0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2, -0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, -0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A, -0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, -0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8, -0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, -0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF, 0x9EF4, -0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, -0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52, 0x9F54, -0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, -0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0}, -{ /* category 84 */ -0x582F, 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 85 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 86 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 87 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 88 */ -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, -{ /* category 89 */ -0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB, 0x6631, -0x68C8, 0x92F9, 0x66FB, 0x5F45, 0x4E28, 0x4EE1, 0x4EFC, 0x4F00, -0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94, 0x4FCD, -0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042, 0x5094, -0x50F4, 0x50D8, 0x514A, 0x5164, 0x519D, 0x51BE, 0x51EC, 0x5215, -0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324, 0x5372, -0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9, 0x54FF, -0x5586, 0x5759, 0x5765, 0x57AC, 0x57C8, 0x57C7, 0xFA0F, 0xFA10, -0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963, 0x59A4, -0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E, 0x5CA6, -0x5CBA, 0x5CF5, 0x5D27, 0x5D53, 0xFA11, 0x5D42, 0x5D6D, 0x5DB8, -0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7}, -{ /* category 90 */ -0x5FDE, 0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120, 0x60F2, -0x6111, 0x6137, 0x6130, 0x6198, 0x6213, 0x62A6, 0x63F5, 0x6460, -0x649D, 0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609, 0x662E, -0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673, 0x6699, -0x66A0, 0x66B2, 0x66BF, 0x66FA, 0x670E, 0xF929, 0x6766, 0x67BB, -0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968, 0xFA14, -0x6998, 0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E, 0x6AE2, -0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, 0x6C86, 0x6C6F, 0x6CDA, 0x6D04, -0x6D87, 0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2, 0x6DFC, -0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5, 0x6FF5, -0x7005, 0x7007, 0x7028, 0x7085, 0x70AB, 0x710F, 0x7104, 0x715C, -0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1}, -{ /* category 91 */ -0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6, 0x73E3, -0x73D2, 0x7407, 0x73F5, 0x7426, 0x742A, 0x7429, 0x742E, 0x7462, -0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E, 0x769B, -0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864, 0x787A, -0x7930, 0xFA18, 0xFA19, 0xFA1A, 0x7994, 0xFA1B, 0x799B, 0x7AD1, -0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C, 0x7DB7, -0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301, 0x8362, -0x837F, 0x83C7, 0x83F6, 0x8448, 0x84B4, 0x8553, 0x8559, 0x856B, -0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12, 0x8A37, -0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53, 0x8B7F, -0x8CF0, 0x8CF4, 0x8D12, 0x8D76, 0xFA23, 0x8ECF, 0xFA24, 0xFA25, -0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA}, -{ /* category 92 */ -0x91D7, 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206, 0x9210, -0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251, 0x9239, -0x9267, 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9, 0x92D0, -0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB, 0xFA28, -0x931E, 0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4, 0x93C6, -0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC, 0xFA29, -0x969D, 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F, 0x9751, -0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C, 0x999E, -0x9A4E, 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1, 0x9BBB, -0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0x0000, 0x0000, -0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, -0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02}}; + {/* category 01 */ + 0x0000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, + 0x00B4, 0xFF40, 0x00A8, 0xFF3E, 0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, + 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0xFF3C, 0xFF5E, 0x2225, 0xFF5C, 0x2026, + 0x2025, 0x2018, 0x2019, 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B, + 0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, + 0xFF0D, 0x00B1, 0x00D7, 0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, + 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5, 0xFF04, 0xFFE0, 0xFFE1, 0xFF05, 0xFF03, + 0xFF06, 0xFF0A, 0xFF20, 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7}, + {/* category 02 */ + 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, 0x3012, 0x2192, 0x2190, + 0x2191, 0x2193, 0x3013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2227, 0x2228, 0xFFE2, + 0x21D2, 0x21D4, 0x2200, 0x2203, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261, 0x2252, + 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, /**/ + 0x00B6, 0x0000, 0x0000, 0x0000, 0x0000, 0x25EF}, + {/* category 03 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, + 0xFF19, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF21, 0xFF22, 0xFF23, 0xFF24, + 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, + 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, + 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, + 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 04 */ + 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, + 0x304D, 0x304E, 0x304F, 0x3050, 0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, + 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061, 0x3062, 0x3063, 0x3064, + 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, + 0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, + 0x307D, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, + 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 05 */ + 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, + 0x30AD, 0x30AE, 0x30AF, 0x30B0, 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, + 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, 0x30C1, 0x30C2, 0x30C3, 0x30C4, + 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, + 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, + 0x30DD, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, + 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, + 0x30F5, 0x30F6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 06 */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, + 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03B1, 0x03B2, 0x03B3, 0x03B4, + 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 07 */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, + 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, + 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, + 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, + 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 08 */ + 0x2500, 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, + 0x2503, 0x250F, 0x2513, 0x251B, 0x2517, 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, + 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 09 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 10 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 11 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 12 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 13 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 14 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 15 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 16 */ + 0x4E9C, 0x5516, 0x5A03, 0x963F, 0x54C0, 0x611B, 0x6328, 0x59F6, 0x9022, 0x8475, 0x831C, 0x7A50, + 0x60AA, 0x63E1, 0x6E25, 0x65ED, 0x8466, 0x82A6, 0x9BF5, 0x6893, 0x5727, 0x65A1, 0x6271, 0x5B9B, + 0x59D0, 0x867B, 0x98F4, 0x7D62, 0x7DBE, 0x9B8E, 0x6216, 0x7C9F, 0x88B7, 0x5B89, 0x5EB5, 0x6309, + 0x6697, 0x6848, 0x95C7, 0x978D, 0x674F, 0x4EE5, 0x4F0A, 0x4F4D, 0x4F9D, 0x5049, 0x56F2, 0x5937, + 0x59D4, 0x5A01, 0x5C09, 0x60DF, 0x610F, 0x6170, 0x6613, 0x6905, 0x70BA, 0x754F, 0x7570, 0x79FB, + 0x7DAD, 0x7DEF, 0x80C3, 0x840E, 0x8863, 0x8B02, 0x9055, 0x907A, 0x533B, 0x4E95, 0x4EA5, 0x57DF, + 0x80B2, 0x90C1, 0x78EF, 0x4E00, 0x58F1, 0x6EA2, 0x9038, 0x7A32, 0x8328, 0x828B, 0x9C2F, 0x5141, + 0x5370, 0x54BD, 0x54E1, 0x56E0, 0x59FB, 0x5F15, 0x98F2, 0x6DEB, 0x80E4, 0x852D}, + {/* category 17 */ + 0x9662, 0x9670, 0x96A0, 0x97FB, 0x540B, 0x53F3, 0x5B87, 0x70CF, 0x7FBD, 0x8FC2, 0x96E8, 0x536F, + 0x9D5C, 0x7ABA, 0x4E11, 0x7893, 0x81FC, 0x6E26, 0x5618, 0x5504, 0x6B1D, 0x851A, 0x9C3B, 0x59E5, + 0x53A9, 0x6D66, 0x74DC, 0x958F, 0x5642, 0x4E91, 0x904B, 0x96F2, 0x834F, 0x990C, 0x53E1, 0x55B6, + 0x5B30, 0x5F71, 0x6620, 0x66F3, 0x6804, 0x6C38, 0x6CF3, 0x6D29, 0x745B, 0x76C8, 0x7A4E, 0x9834, + 0x82F1, 0x885B, 0x8A60, 0x92ED, 0x6DB2, 0x75AB, 0x76CA, 0x99C5, 0x60A6, 0x8B01, 0x8D8A, 0x95B2, + 0x698E, 0x53AD, 0x5186, 0x5712, 0x5830, 0x5944, 0x5BB4, 0x5EF6, 0x6028, 0x63A9, 0x63F4, 0x6CBF, + 0x6F14, 0x708E, 0x7114, 0x7159, 0x71D5, 0x733F, 0x7E01, 0x8276, 0x82D1, 0x8597, 0x9060, 0x925B, + 0x9D1B, 0x5869, 0x65BC, 0x6C5A, 0x7525, 0x51F9, 0x592E, 0x5965, 0x5F80, 0x5FDC}, + {/* category 18 */ + 0x62BC, 0x65FA, 0x6A2A, 0x6B27, 0x6BB4, 0x738B, 0x7FC1, 0x8956, 0x9D2C, 0x9D0E, 0x9EC4, 0x5CA1, + 0x6C96, 0x837B, 0x5104, 0x5C4B, 0x61B6, 0x81C6, 0x6876, 0x7261, 0x4E59, 0x4FFA, 0x5378, 0x6069, + 0x6E29, 0x7A4F, 0x97F3, 0x4E0B, 0x5316, 0x4EEE, 0x4F55, 0x4F3D, 0x4FA1, 0x4F73, 0x52A0, 0x53EF, + 0x5609, 0x590F, 0x5AC1, 0x5BB6, 0x5BE1, 0x79D1, 0x6687, 0x679C, 0x67B6, 0x6B4C, 0x6CB3, 0x706B, + 0x73C2, 0x798D, 0x79BE, 0x7A3C, 0x7B87, 0x82B1, 0x82DB, 0x8304, 0x8377, 0x83EF, 0x83D3, 0x8766, + 0x8AB2, 0x5629, 0x8CA8, 0x8FE6, 0x904E, 0x971E, 0x868A, 0x4FC4, 0x5CE8, 0x6211, 0x7259, 0x753B, + 0x81E5, 0x82BD, 0x86FE, 0x8CC0, 0x96C5, 0x9913, 0x99D5, 0x4ECB, 0x4F1A, 0x89E3, 0x56DE, 0x584A, + 0x58CA, 0x5EFB, 0x5FEB, 0x602A, 0x6094, 0x6062, 0x61D0, 0x6212, 0x62D0, 0x6539}, + {/* category 19 */ + 0x9B41, 0x6666, 0x68B0, 0x6D77, 0x7070, 0x754C, 0x7686, 0x7D75, 0x82A5, 0x87F9, 0x958B, 0x968E, + 0x8C9D, 0x51F1, 0x52BE, 0x5916, 0x54B3, 0x5BB3, 0x5D16, 0x6168, 0x6982, 0x6DAF, 0x788D, 0x84CB, + 0x8857, 0x8A72, 0x93A7, 0x9AB8, 0x6D6C, 0x99A8, 0x86D9, 0x57A3, 0x67FF, 0x86CE, 0x920E, 0x5283, + 0x5687, 0x5404, 0x5ED3, 0x62E1, 0x64B9, 0x683C, 0x6838, 0x6BBB, 0x7372, 0x78BA, 0x7A6B, 0x899A, + 0x89D2, 0x8D6B, 0x8F03, 0x90ED, 0x95A3, 0x9694, 0x9769, 0x5B66, 0x5CB3, 0x697D, 0x984D, 0x984E, + 0x639B, 0x7B20, 0x6A2B, 0x6A7F, 0x68B6, 0x9C0D, 0x6F5F, 0x5272, 0x559D, 0x6070, 0x62EC, 0x6D3B, + 0x6E07, 0x6ED1, 0x845B, 0x8910, 0x8F44, 0x4E14, 0x9C39, 0x53F6, 0x691B, 0x6A3A, 0x9784, 0x682A, + 0x515C, 0x7AC3, 0x84B2, 0x91DC, 0x938C, 0x565B, 0x9D28, 0x6822, 0x8305, 0x8431}, + {/* category 20 */ + 0x7CA5, 0x5208, 0x82C5, 0x74E6, 0x4E7E, 0x4F83, 0x51A0, 0x5BD2, 0x520A, 0x52D8, 0x52E7, 0x5DFB, + 0x559A, 0x582A, 0x59E6, 0x5B8C, 0x5B98, 0x5BDB, 0x5E72, 0x5E79, 0x60A3, 0x611F, 0x6163, 0x61BE, + 0x63DB, 0x6562, 0x67D1, 0x6853, 0x68FA, 0x6B3E, 0x6B53, 0x6C57, 0x6F22, 0x6F97, 0x6F45, 0x74B0, + 0x7518, 0x76E3, 0x770B, 0x7AFF, 0x7BA1, 0x7C21, 0x7DE9, 0x7F36, 0x7FF0, 0x809D, 0x8266, 0x839E, + 0x89B3, 0x8ACC, 0x8CAB, 0x9084, 0x9451, 0x9593, 0x9591, 0x95A2, 0x9665, 0x97D3, 0x9928, 0x8218, + 0x4E38, 0x542B, 0x5CB8, 0x5DCC, 0x73A9, 0x764C, 0x773C, 0x5CA9, 0x7FEB, 0x8D0B, 0x96C1, 0x9811, + 0x9854, 0x9858, 0x4F01, 0x4F0E, 0x5371, 0x559C, 0x5668, 0x57FA, 0x5947, 0x5B09, 0x5BC4, 0x5C90, + 0x5E0C, 0x5E7E, 0x5FCC, 0x63EE, 0x673A, 0x65D7, 0x65E2, 0x671F, 0x68CB, 0x68C4}, + {/* category 21 */ + 0x6A5F, 0x5E30, 0x6BC5, 0x6C17, 0x6C7D, 0x757F, 0x7948, 0x5B63, 0x7A00, 0x7D00, 0x5FBD, 0x898F, + 0x8A18, 0x8CB4, 0x8D77, 0x8ECC, 0x8F1D, 0x98E2, 0x9A0E, 0x9B3C, 0x4E80, 0x507D, 0x5100, 0x5993, + 0x5B9C, 0x622F, 0x6280, 0x64EC, 0x6B3A, 0x72A0, 0x7591, 0x7947, 0x7FA9, 0x87FB, 0x8ABC, 0x8B70, + 0x63AC, 0x83CA, 0x97A0, 0x5409, 0x5403, 0x55AB, 0x6854, 0x6A58, 0x8A70, 0x7827, 0x6775, 0x9ECD, + 0x5374, 0x5BA2, 0x811A, 0x8650, 0x9006, 0x4E18, 0x4E45, 0x4EC7, 0x4F11, 0x53CA, 0x5438, 0x5BAE, + 0x5F13, 0x6025, 0x6551, 0x673D, 0x6C42, 0x6C72, 0x6CE3, 0x7078, 0x7403, 0x7A76, 0x7AAE, 0x7B08, + 0x7D1A, 0x7CFE, 0x7D66, 0x65E7, 0x725B, 0x53BB, 0x5C45, 0x5DE8, 0x62D2, 0x62E0, 0x6319, 0x6E20, + 0x865A, 0x8A31, 0x8DDD, 0x92F8, 0x6F01, 0x79A6, 0x9B5A, 0x4EA8, 0x4EAB, 0x4EAC}, + {/* category 22 */ + 0x4F9B, 0x4FA0, 0x50D1, 0x5147, 0x7AF6, 0x5171, 0x51F6, 0x5354, 0x5321, 0x537F, 0x53EB, 0x55AC, + 0x5883, 0x5CE1, 0x5F37, 0x5F4A, 0x602F, 0x6050, 0x606D, 0x631F, 0x6559, 0x6A4B, 0x6CC1, 0x72C2, + 0x72ED, 0x77EF, 0x80F8, 0x8105, 0x8208, 0x854E, 0x90F7, 0x93E1, 0x97FF, 0x9957, 0x9A5A, 0x4EF0, + 0x51DD, 0x5C2D, 0x6681, 0x696D, 0x5C40, 0x66F2, 0x6975, 0x7389, 0x6850, 0x7C81, 0x50C5, 0x52E4, + 0x5747, 0x5DFE, 0x9326, 0x65A4, 0x6B23, 0x6B3D, 0x7434, 0x7981, 0x79BD, 0x7B4B, 0x7DCA, 0x82B9, + 0x83CC, 0x887F, 0x895F, 0x8B39, 0x8FD1, 0x91D1, 0x541F, 0x9280, 0x4E5D, 0x5036, 0x53E5, 0x533A, + 0x72D7, 0x7396, 0x77E9, 0x82E6, 0x8EAF, 0x99C6, 0x99C8, 0x99D2, 0x5177, 0x611A, 0x865E, 0x55B0, + 0x7A7A, 0x5076, 0x5BD3, 0x9047, 0x9685, 0x4E32, 0x6ADB, 0x91E7, 0x5C51, 0x5C48}, + {/* category 23 */ + 0x6398, 0x7A9F, 0x6C93, 0x9774, 0x8F61, 0x7AAA, 0x718A, 0x9688, 0x7C82, 0x6817, 0x7E70, 0x6851, + 0x936C, 0x52F2, 0x541B, 0x85AB, 0x8A13, 0x7FA4, 0x8ECD, 0x90E1, 0x5366, 0x8888, 0x7941, 0x4FC2, + 0x50BE, 0x5211, 0x5144, 0x5553, 0x572D, 0x73EA, 0x578B, 0x5951, 0x5F62, 0x5F84, 0x6075, 0x6176, + 0x6167, 0x61A9, 0x63B2, 0x643A, 0x656C, 0x666F, 0x6842, 0x6E13, 0x7566, 0x7A3D, 0x7CFB, 0x7D4C, + 0x7D99, 0x7E4B, 0x7F6B, 0x830E, 0x834A, 0x86CD, 0x8A08, 0x8A63, 0x8B66, 0x8EFD, 0x981A, 0x9D8F, + 0x82B8, 0x8FCE, 0x9BE8, 0x5287, 0x621F, 0x6483, 0x6FC0, 0x9699, 0x6841, 0x5091, 0x6B20, 0x6C7A, + 0x6F54, 0x7A74, 0x7D50, 0x8840, 0x8A23, 0x6708, 0x4EF6, 0x5039, 0x5026, 0x5065, 0x517C, 0x5238, + 0x5263, 0x55A7, 0x570F, 0x5805, 0x5ACC, 0x5EFA, 0x61B2, 0x61F8, 0x62F3, 0x6372}, + {/* category 24 */ + 0x691C, 0x6A29, 0x727D, 0x72AC, 0x732E, 0x7814, 0x786F, 0x7D79, 0x770C, 0x80A9, 0x898B, 0x8B19, + 0x8CE2, 0x8ED2, 0x9063, 0x9375, 0x967A, 0x9855, 0x9A13, 0x9E78, 0x5143, 0x539F, 0x53B3, 0x5E7B, + 0x5F26, 0x6E1B, 0x6E90, 0x7384, 0x73FE, 0x7D43, 0x8237, 0x8A00, 0x8AFA, 0x9650, 0x4E4E, 0x500B, + 0x53E4, 0x547C, 0x56FA, 0x59D1, 0x5B64, 0x5DF1, 0x5EAB, 0x5F27, 0x6238, 0x6545, 0x67AF, 0x6E56, + 0x72D0, 0x7CCA, 0x88B4, 0x80A1, 0x80E1, 0x83F0, 0x864E, 0x8A87, 0x8DE8, 0x9237, 0x96C7, 0x9867, + 0x9F13, 0x4E94, 0x4E92, 0x4F0D, 0x5348, 0x5449, 0x543E, 0x5A2F, 0x5F8C, 0x5FA1, 0x609F, 0x68A7, + 0x6A8E, 0x745A, 0x7881, 0x8A9E, 0x8AA4, 0x8B77, 0x9190, 0x4E5E, 0x9BC9, 0x4EA4, 0x4F7C, 0x4FAF, + 0x5019, 0x5016, 0x5149, 0x516C, 0x529F, 0x52B9, 0x52FE, 0x539A, 0x53E3, 0x5411}, + {/* category 25 */ + 0x540E, 0x5589, 0x5751, 0x57A2, 0x597D, 0x5B54, 0x5B5D, 0x5B8F, 0x5DE5, 0x5DE7, 0x5DF7, 0x5E78, + 0x5E83, 0x5E9A, 0x5EB7, 0x5F18, 0x6052, 0x614C, 0x6297, 0x62D8, 0x63A7, 0x653B, 0x6602, 0x6643, + 0x66F4, 0x676D, 0x6821, 0x6897, 0x69CB, 0x6C5F, 0x6D2A, 0x6D69, 0x6E2F, 0x6E9D, 0x7532, 0x7687, + 0x786C, 0x7A3F, 0x7CE0, 0x7D05, 0x7D18, 0x7D5E, 0x7DB1, 0x8015, 0x8003, 0x80AF, 0x80B1, 0x8154, + 0x818F, 0x822A, 0x8352, 0x884C, 0x8861, 0x8B1B, 0x8CA2, 0x8CFC, 0x90CA, 0x9175, 0x9271, 0x783F, + 0x92FC, 0x95A4, 0x964D, 0x9805, 0x9999, 0x9AD8, 0x9D3B, 0x525B, 0x52AB, 0x53F7, 0x5408, 0x58D5, + 0x62F7, 0x6FE0, 0x8C6A, 0x8F5F, 0x9EB9, 0x514B, 0x523B, 0x544A, 0x56FD, 0x7A40, 0x9177, 0x9D60, + 0x9ED2, 0x7344, 0x6F09, 0x8170, 0x7511, 0x5FFD, 0x60DA, 0x9AA8, 0x72DB, 0x8FBC}, + {/* category 26 */ + 0x6B64, 0x9803, 0x4ECA, 0x56F0, 0x5764, 0x58BE, 0x5A5A, 0x6068, 0x61C7, 0x660F, 0x6606, 0x6839, + 0x68B1, 0x6DF7, 0x75D5, 0x7D3A, 0x826E, 0x9B42, 0x4E9B, 0x4F50, 0x53C9, 0x5506, 0x5D6F, 0x5DE6, + 0x5DEE, 0x67FB, 0x6C99, 0x7473, 0x7802, 0x8A50, 0x9396, 0x88DF, 0x5750, 0x5EA7, 0x632B, 0x50B5, + 0x50AC, 0x518D, 0x6700, 0x54C9, 0x585E, 0x59BB, 0x5BB0, 0x5F69, 0x624D, 0x63A1, 0x683D, 0x6B73, + 0x6E08, 0x707D, 0x91C7, 0x7280, 0x7815, 0x7826, 0x796D, 0x658E, 0x7D30, 0x83DC, 0x88C1, 0x8F09, + 0x969B, 0x5264, 0x5728, 0x6750, 0x7F6A, 0x8CA1, 0x51B4, 0x5742, 0x962A, 0x583A, 0x698A, 0x80B4, + 0x54B2, 0x5D0E, 0x57FC, 0x7895, 0x9DFA, 0x4F5C, 0x524A, 0x548B, 0x643E, 0x6628, 0x6714, 0x67F5, + 0x7A84, 0x7B56, 0x7D22, 0x932F, 0x685C, 0x9BAD, 0x7B39, 0x5319, 0x518A, 0x5237}, + {/* category 27 */ + 0x5BDF, 0x62F6, 0x64AE, 0x64E6, 0x672D, 0x6BBA, 0x85A9, 0x96D1, 0x7690, 0x9BD6, 0x634C, 0x9306, + 0x9BAB, 0x76BF, 0x6652, 0x4E09, 0x5098, 0x53C2, 0x5C71, 0x60E8, 0x6492, 0x6563, 0x685F, 0x71E6, + 0x73CA, 0x7523, 0x7B97, 0x7E82, 0x8695, 0x8B83, 0x8CDB, 0x9178, 0x9910, 0x65AC, 0x66AB, 0x6B8B, + 0x4ED5, 0x4ED4, 0x4F3A, 0x4F7F, 0x523A, 0x53F8, 0x53F2, 0x55E3, 0x56DB, 0x58EB, 0x59CB, 0x59C9, + 0x59FF, 0x5B50, 0x5C4D, 0x5E02, 0x5E2B, 0x5FD7, 0x601D, 0x6307, 0x652F, 0x5B5C, 0x65AF, 0x65BD, + 0x65E8, 0x679D, 0x6B62, 0x6B7B, 0x6C0F, 0x7345, 0x7949, 0x79C1, 0x7CF8, 0x7D19, 0x7D2B, 0x80A2, + 0x8102, 0x81F3, 0x8996, 0x8A5E, 0x8A69, 0x8A66, 0x8A8C, 0x8AEE, 0x8CC7, 0x8CDC, 0x96CC, 0x98FC, + 0x6B6F, 0x4E8B, 0x4F3C, 0x4F8D, 0x5150, 0x5B57, 0x5BFA, 0x6148, 0x6301, 0x6642}, + {/* category 28 */ + 0x6B21, 0x6ECB, 0x6CBB, 0x723E, 0x74BD, 0x75D4, 0x78C1, 0x793A, 0x800C, 0x8033, 0x81EA, 0x8494, + 0x8F9E, 0x6C50, 0x9E7F, 0x5F0F, 0x8B58, 0x9D2B, 0x7AFA, 0x8EF8, 0x5B8D, 0x96EB, 0x4E03, 0x53F1, + 0x57F7, 0x5931, 0x5AC9, 0x5BA4, 0x6089, 0x6E7F, 0x6F06, 0x75BE, 0x8CEA, 0x5B9F, 0x8500, 0x7BE0, + 0x5072, 0x67F4, 0x829D, 0x5C61, 0x854A, 0x7E1E, 0x820E, 0x5199, 0x5C04, 0x6368, 0x8D66, 0x659C, + 0x716E, 0x793E, 0x7D17, 0x8005, 0x8B1D, 0x8ECA, 0x906E, 0x86C7, 0x90AA, 0x501F, 0x52FA, 0x5C3A, + 0x6753, 0x707C, 0x7235, 0x914C, 0x91C8, 0x932B, 0x82E5, 0x5BC2, 0x5F31, 0x60F9, 0x4E3B, 0x53D6, + 0x5B88, 0x624B, 0x6731, 0x6B8A, 0x72E9, 0x73E0, 0x7A2E, 0x816B, 0x8DA3, 0x9152, 0x9996, 0x5112, + 0x53D7, 0x546A, 0x5BFF, 0x6388, 0x6A39, 0x7DAC, 0x9700, 0x56DA, 0x53CE, 0x5468}, + {/* category 29 */ + 0x5B97, 0x5C31, 0x5DDE, 0x4FEE, 0x6101, 0x62FE, 0x6D32, 0x79C0, 0x79CB, 0x7D42, 0x7E4D, 0x7FD2, + 0x81ED, 0x821F, 0x8490, 0x8846, 0x8972, 0x8B90, 0x8E74, 0x8F2F, 0x9031, 0x914B, 0x916C, 0x96C6, + 0x919C, 0x4EC0, 0x4F4F, 0x5145, 0x5341, 0x5F93, 0x620E, 0x67D4, 0x6C41, 0x6E0B, 0x7363, 0x7E26, + 0x91CD, 0x9283, 0x53D4, 0x5919, 0x5BBF, 0x6DD1, 0x795D, 0x7E2E, 0x7C9B, 0x587E, 0x719F, 0x51FA, + 0x8853, 0x8FF0, 0x4FCA, 0x5CFB, 0x6625, 0x77AC, 0x7AE3, 0x821C, 0x99FF, 0x51C6, 0x5FAA, 0x65EC, + 0x696F, 0x6B89, 0x6DF3, 0x6E96, 0x6F64, 0x76FE, 0x7D14, 0x5DE1, 0x9075, 0x9187, 0x9806, 0x51E6, + 0x521D, 0x6240, 0x6691, 0x66D9, 0x6E1A, 0x5EB6, 0x7DD2, 0x7F72, 0x66F8, 0x85AF, 0x85F7, 0x8AF8, + 0x52A9, 0x53D9, 0x5973, 0x5E8F, 0x5F90, 0x6055, 0x92E4, 0x9664, 0x50B7, 0x511F}, + {/* category 30 */ + 0x52DD, 0x5320, 0x5347, 0x53EC, 0x54E8, 0x5546, 0x5531, 0x5617, 0x5968, 0x59BE, 0x5A3C, 0x5BB5, + 0x5C06, 0x5C0F, 0x5C11, 0x5C1A, 0x5E84, 0x5E8A, 0x5EE0, 0x5F70, 0x627F, 0x6284, 0x62DB, 0x638C, + 0x6377, 0x6607, 0x660C, 0x662D, 0x6676, 0x677E, 0x68A2, 0x6A1F, 0x6A35, 0x6CBC, 0x6D88, 0x6E09, + 0x6E58, 0x713C, 0x7126, 0x7167, 0x75C7, 0x7701, 0x785D, 0x7901, 0x7965, 0x79F0, 0x7AE0, 0x7B11, + 0x7CA7, 0x7D39, 0x8096, 0x83D6, 0x848B, 0x8549, 0x885D, 0x88F3, 0x8A1F, 0x8A3C, 0x8A54, 0x8A73, + 0x8C61, 0x8CDE, 0x91A4, 0x9266, 0x937E, 0x9418, 0x969C, 0x9798, 0x4E0A, 0x4E08, 0x4E1E, 0x4E57, + 0x5197, 0x5270, 0x57CE, 0x5834, 0x58CC, 0x5B22, 0x5E38, 0x60C5, 0x64FE, 0x6761, 0x6756, 0x6D44, + 0x72B6, 0x7573, 0x7A63, 0x84B8, 0x8B72, 0x91B8, 0x9320, 0x5631, 0x57F4, 0x98FE}, + {/* category 31 */ + 0x62ED, 0x690D, 0x6B96, 0x71ED, 0x7E54, 0x8077, 0x8272, 0x89E6, 0x98DF, 0x8755, 0x8FB1, 0x5C3B, + 0x4F38, 0x4FE1, 0x4FB5, 0x5507, 0x5A20, 0x5BDD, 0x5BE9, 0x5FC3, 0x614E, 0x632F, 0x65B0, 0x664B, + 0x68EE, 0x699B, 0x6D78, 0x6DF1, 0x7533, 0x75B9, 0x771F, 0x795E, 0x79E6, 0x7D33, 0x81E3, 0x82AF, + 0x85AA, 0x89AA, 0x8A3A, 0x8EAB, 0x8F9B, 0x9032, 0x91DD, 0x9707, 0x4EBA, 0x4EC1, 0x5203, 0x5875, + 0x58EC, 0x5C0B, 0x751A, 0x5C3D, 0x814E, 0x8A0A, 0x8FC5, 0x9663, 0x976D, 0x7B25, 0x8ACF, 0x9808, + 0x9162, 0x56F3, 0x53A8, 0x9017, 0x5439, 0x5782, 0x5E25, 0x63A8, 0x6C34, 0x708A, 0x7761, 0x7C8B, + 0x7FE0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968F, 0x745E, 0x9AC4, 0x5D07, 0x5D69, 0x6570, + 0x67A2, 0x8DA8, 0x96DB, 0x636E, 0x6749, 0x6919, 0x83C5, 0x9817, 0x96C0, 0x88FE}, + {/* category 32 */ + 0x6F84, 0x647A, 0x5BF8, 0x4E16, 0x702C, 0x755D, 0x662F, 0x51C4, 0x5236, 0x52E2, 0x59D3, 0x5F81, + 0x6027, 0x6210, 0x653F, 0x6574, 0x661F, 0x6674, 0x68F2, 0x6816, 0x6B63, 0x6E05, 0x7272, 0x751F, + 0x76DB, 0x7CBE, 0x8056, 0x58F0, 0x88FD, 0x897F, 0x8AA0, 0x8A93, 0x8ACB, 0x901D, 0x9192, 0x9752, + 0x9759, 0x6589, 0x7A0E, 0x8106, 0x96BB, 0x5E2D, 0x60DC, 0x621A, 0x65A5, 0x6614, 0x6790, 0x77F3, + 0x7A4D, 0x7C4D, 0x7E3E, 0x810A, 0x8CAC, 0x8D64, 0x8DE1, 0x8E5F, 0x78A9, 0x5207, 0x62D9, 0x63A5, + 0x6442, 0x6298, 0x8A2D, 0x7A83, 0x7BC0, 0x8AAC, 0x96EA, 0x7D76, 0x820C, 0x8749, 0x4ED9, 0x5148, + 0x5343, 0x5360, 0x5BA3, 0x5C02, 0x5C16, 0x5DDD, 0x6226, 0x6247, 0x64B0, 0x6813, 0x6834, 0x6CC9, + 0x6D45, 0x6D17, 0x67D3, 0x6F5C, 0x714E, 0x717D, 0x65CB, 0x7A7F, 0x7BAD, 0x7DDA}, + {/* category 33 */ + 0x7E4A, 0x7FA8, 0x817A, 0x821B, 0x8239, 0x85A6, 0x8A6E, 0x8CCE, 0x8DF5, 0x9078, 0x9077, 0x92AD, + 0x9291, 0x9583, 0x9BAE, 0x524D, 0x5584, 0x6F38, 0x7136, 0x5168, 0x7985, 0x7E55, 0x81B3, 0x7CCE, + 0x564C, 0x5851, 0x5CA8, 0x63AA, 0x66FE, 0x66FD, 0x695A, 0x72D9, 0x758F, 0x758E, 0x790E, 0x7956, + 0x79DF, 0x7C97, 0x7D20, 0x7D44, 0x8607, 0x8A34, 0x963B, 0x9061, 0x9F20, 0x50E7, 0x5275, 0x53CC, + 0x53E2, 0x5009, 0x55AA, 0x58EE, 0x594F, 0x723D, 0x5B8B, 0x5C64, 0x531D, 0x60E3, 0x60F3, 0x635C, + 0x6383, 0x633F, 0x63BB, 0x64CD, 0x65E9, 0x66F9, 0x5DE3, 0x69CD, 0x69FD, 0x6F15, 0x71E5, 0x4E89, + 0x75E9, 0x76F8, 0x7A93, 0x7CDF, 0x7DCF, 0x7D9C, 0x8061, 0x8349, 0x8358, 0x846C, 0x84BC, 0x85FB, + 0x88C5, 0x8D70, 0x9001, 0x906D, 0x9397, 0x971C, 0x9A12, 0x50CF, 0x5897, 0x618E}, + {/* category 34 */ + 0x81D3, 0x8535, 0x8D08, 0x9020, 0x4FC3, 0x5074, 0x5247, 0x5373, 0x606F, 0x6349, 0x675F, 0x6E2C, + 0x8DB3, 0x901F, 0x4FD7, 0x5C5E, 0x8CCA, 0x65CF, 0x7D9A, 0x5352, 0x8896, 0x5176, 0x63C3, 0x5B58, + 0x5B6B, 0x5C0A, 0x640D, 0x6751, 0x905C, 0x4ED6, 0x591A, 0x592A, 0x6C70, 0x8A51, 0x553E, 0x5815, + 0x59A5, 0x60F0, 0x6253, 0x67C1, 0x8235, 0x6955, 0x9640, 0x99C4, 0x9A28, 0x4F53, 0x5806, 0x5BFE, + 0x8010, 0x5CB1, 0x5E2F, 0x5F85, 0x6020, 0x614B, 0x6234, 0x66FF, 0x6CF0, 0x6EDE, 0x80CE, 0x817F, + 0x82D4, 0x888B, 0x8CB8, 0x9000, 0x902E, 0x968A, 0x9EDB, 0x9BDB, 0x4EE3, 0x53F0, 0x5927, 0x7B2C, + 0x918D, 0x984C, 0x9DF9, 0x6EDD, 0x7027, 0x5353, 0x5544, 0x5B85, 0x6258, 0x629E, 0x62D3, 0x6CA2, + 0x6FEF, 0x7422, 0x8A17, 0x9438, 0x6FC1, 0x8AFE, 0x8338, 0x51E7, 0x86F8, 0x53EA}, + {/* category 35 */ + 0x53E9, 0x4F46, 0x9054, 0x8FB0, 0x596A, 0x8131, 0x5DFD, 0x7AEA, 0x8FBF, 0x68DA, 0x8C37, 0x72F8, + 0x9C48, 0x6A3D, 0x8AB0, 0x4E39, 0x5358, 0x5606, 0x5766, 0x62C5, 0x63A2, 0x65E6, 0x6B4E, 0x6DE1, + 0x6E5B, 0x70AD, 0x77ED, 0x7AEF, 0x7BAA, 0x7DBB, 0x803D, 0x80C6, 0x86CB, 0x8A95, 0x935B, 0x56E3, + 0x58C7, 0x5F3E, 0x65AD, 0x6696, 0x6A80, 0x6BB5, 0x7537, 0x8AC7, 0x5024, 0x77E5, 0x5730, 0x5F1B, + 0x6065, 0x667A, 0x6C60, 0x75F4, 0x7A1A, 0x7F6E, 0x81F4, 0x8718, 0x9045, 0x99B3, 0x7BC9, 0x755C, + 0x7AF9, 0x7B51, 0x84C4, 0x9010, 0x79E9, 0x7A92, 0x8336, 0x5AE1, 0x7740, 0x4E2D, 0x4EF2, 0x5B99, + 0x5FE0, 0x62BD, 0x663C, 0x67F1, 0x6CE8, 0x866B, 0x8877, 0x8A3B, 0x914E, 0x92F3, 0x99D0, 0x6A17, + 0x7026, 0x732A, 0x82E7, 0x8457, 0x8CAF, 0x4E01, 0x5146, 0x51CB, 0x558B, 0x5BF5}, + {/* category 36 */ + 0x5E16, 0x5E33, 0x5E81, 0x5F14, 0x5F35, 0x5F6B, 0x5FB4, 0x61F2, 0x6311, 0x66A2, 0x671D, 0x6F6E, + 0x7252, 0x753A, 0x773A, 0x8074, 0x8139, 0x8178, 0x8776, 0x8ABF, 0x8ADC, 0x8D85, 0x8DF3, 0x929A, + 0x9577, 0x9802, 0x9CE5, 0x52C5, 0x6357, 0x76F4, 0x6715, 0x6C88, 0x73CD, 0x8CC3, 0x93AE, 0x9673, + 0x6D25, 0x589C, 0x690E, 0x69CC, 0x8FFD, 0x939A, 0x75DB, 0x901A, 0x585A, 0x6802, 0x63B4, 0x69FB, + 0x4F43, 0x6F2C, 0x67D8, 0x8FBB, 0x8526, 0x7DB4, 0x9354, 0x693F, 0x6F70, 0x576A, 0x58F7, 0x5B2C, + 0x7D2C, 0x722A, 0x540A, 0x91E3, 0x9DB4, 0x4EAD, 0x4F4E, 0x505C, 0x5075, 0x5243, 0x8C9E, 0x5448, + 0x5824, 0x5B9A, 0x5E1D, 0x5E95, 0x5EAD, 0x5EF7, 0x5F1F, 0x608C, 0x62B5, 0x633A, 0x63D0, 0x68AF, + 0x6C40, 0x7887, 0x798E, 0x7A0B, 0x7DE0, 0x8247, 0x8A02, 0x8AE6, 0x8E44, 0x9013}, + {/* category 37 */ + 0x90B8, 0x912D, 0x91D8, 0x9F0E, 0x6CE5, 0x6458, 0x64E2, 0x6575, 0x6EF4, 0x7684, 0x7B1B, 0x9069, + 0x93D1, 0x6EBA, 0x54F2, 0x5FB9, 0x64A4, 0x8F4D, 0x8FED, 0x9244, 0x5178, 0x586B, 0x5929, 0x5C55, + 0x5E97, 0x6DFB, 0x7E8F, 0x751C, 0x8CBC, 0x8EE2, 0x985B, 0x70B9, 0x4F1D, 0x6BBF, 0x6FB1, 0x7530, + 0x96FB, 0x514E, 0x5410, 0x5835, 0x5857, 0x59AC, 0x5C60, 0x5F92, 0x6597, 0x675C, 0x6E21, 0x767B, + 0x83DF, 0x8CED, 0x9014, 0x90FD, 0x934D, 0x7825, 0x783A, 0x52AA, 0x5EA6, 0x571F, 0x5974, 0x6012, + 0x5012, 0x515A, 0x51AC, 0x51CD, 0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5B95, 0x5CF6, 0x5D8B, + 0x60BC, 0x6295, 0x642D, 0x6771, 0x6843, 0x68BC, 0x68DF, 0x76D7, 0x6DD8, 0x6E6F, 0x6D9B, 0x706F, + 0x71C8, 0x5F53, 0x75D8, 0x7977, 0x7B49, 0x7B54, 0x7B52, 0x7CD6, 0x7D71, 0x5230}, + {/* category 38 */ + 0x8463, 0x8569, 0x85E4, 0x8A0E, 0x8B04, 0x8C46, 0x8E0F, 0x9003, 0x900F, 0x9419, 0x9676, 0x982D, + 0x9A30, 0x95D8, 0x50CD, 0x52D5, 0x540C, 0x5802, 0x5C0E, 0x61A7, 0x649E, 0x6D1E, 0x77B3, 0x7AE5, + 0x80F4, 0x8404, 0x9053, 0x9285, 0x5CE0, 0x9D07, 0x533F, 0x5F97, 0x5FB3, 0x6D9C, 0x7279, 0x7763, + 0x79BF, 0x7BE4, 0x6BD2, 0x72EC, 0x8AAD, 0x6803, 0x6A61, 0x51F8, 0x7A81, 0x6934, 0x5C4A, 0x9CF6, + 0x82EB, 0x5BC5, 0x9149, 0x701E, 0x5678, 0x5C6F, 0x60C7, 0x6566, 0x6C8C, 0x8C5A, 0x9041, 0x9813, + 0x5451, 0x66C7, 0x920D, 0x5948, 0x90A3, 0x5185, 0x4E4D, 0x51EA, 0x8599, 0x8B0E, 0x7058, 0x637A, + 0x934B, 0x6962, 0x99B4, 0x7E04, 0x7577, 0x5357, 0x6960, 0x8EDF, 0x96E3, 0x6C5D, 0x4E8C, 0x5C3C, + 0x5F10, 0x8FE9, 0x5302, 0x8CD1, 0x8089, 0x8679, 0x5EFF, 0x65E5, 0x4E73, 0x5165}, + {/* category 39 */ + 0x5982, 0x5C3F, 0x97EE, 0x4EFB, 0x598A, 0x5FCD, 0x8A8D, 0x6FE1, 0x79B0, 0x7962, 0x5BE7, 0x8471, + 0x732B, 0x71B1, 0x5E74, 0x5FF5, 0x637B, 0x649A, 0x71C3, 0x7C98, 0x4E43, 0x5EFC, 0x4E4B, 0x57DC, + 0x56A2, 0x60A9, 0x6FC3, 0x7D0D, 0x80FD, 0x8133, 0x81BF, 0x8FB2, 0x8997, 0x86A4, 0x5DF4, 0x628A, + 0x64AD, 0x8987, 0x6777, 0x6CE2, 0x6D3E, 0x7436, 0x7834, 0x5A46, 0x7F75, 0x82AD, 0x99AC, 0x4FF3, + 0x5EC3, 0x62DD, 0x6392, 0x6557, 0x676F, 0x76C3, 0x724C, 0x80CC, 0x80BA, 0x8F29, 0x914D, 0x500D, + 0x57F9, 0x5A92, 0x6885, 0x6973, 0x7164, 0x72FD, 0x8CB7, 0x58F2, 0x8CE0, 0x966A, 0x9019, 0x877F, + 0x79E4, 0x77E7, 0x8429, 0x4F2F, 0x5265, 0x535A, 0x62CD, 0x67CF, 0x6CCA, 0x767D, 0x7B94, 0x7C95, + 0x8236, 0x8584, 0x8FEB, 0x66DD, 0x6F20, 0x7206, 0x7E1B, 0x83AB, 0x99C1, 0x9EA6}, + {/* category 40 */ + 0x51FD, 0x7BB1, 0x7872, 0x7BB8, 0x8087, 0x7B48, 0x6AE8, 0x5E61, 0x808C, 0x7551, 0x7560, 0x516B, + 0x9262, 0x6E8C, 0x767A, 0x9197, 0x9AEA, 0x4F10, 0x7F70, 0x629C, 0x7B4F, 0x95A5, 0x9CE9, 0x567A, + 0x5859, 0x86E4, 0x96BC, 0x4F34, 0x5224, 0x534A, 0x53CD, 0x53DB, 0x5E06, 0x642C, 0x6591, 0x677F, + 0x6C3E, 0x6C4E, 0x7248, 0x72AF, 0x73ED, 0x7554, 0x7E41, 0x822C, 0x85E9, 0x8CA9, 0x7BC4, 0x91C6, + 0x7169, 0x9812, 0x98EF, 0x633D, 0x6669, 0x756A, 0x76E4, 0x78D0, 0x8543, 0x86EE, 0x532A, 0x5351, + 0x5426, 0x5983, 0x5E87, 0x5F7C, 0x60B2, 0x6249, 0x6279, 0x62AB, 0x6590, 0x6BD4, 0x6CCC, 0x75B2, + 0x76AE, 0x7891, 0x79D8, 0x7DCB, 0x7F77, 0x80A5, 0x88AB, 0x8AB9, 0x8CBB, 0x907F, 0x975E, 0x98DB, + 0x6A0B, 0x7C38, 0x5099, 0x5C3E, 0x5FAE, 0x6787, 0x6BD8, 0x7435, 0x7709, 0x7F8E}, + {/* category 41 */ + 0x9F3B, 0x67CA, 0x7A17, 0x5339, 0x758B, 0x9AED, 0x5F66, 0x819D, 0x83F1, 0x8098, 0x5F3C, 0x5FC5, + 0x7562, 0x7B46, 0x903C, 0x6867, 0x59EB, 0x5A9B, 0x7D10, 0x767E, 0x8B2C, 0x4FF5, 0x5F6A, 0x6A19, + 0x6C37, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8A55, 0x8C79, 0x5EDF, 0x63CF, 0x75C5, 0x79D2, 0x82D7, + 0x9328, 0x92F2, 0x849C, 0x86ED, 0x9C2D, 0x54C1, 0x5F6C, 0x658C, 0x6D5C, 0x7015, 0x8CA7, 0x8CD3, + 0x983B, 0x654F, 0x74F6, 0x4E0D, 0x4ED8, 0x57E0, 0x592B, 0x5A66, 0x5BCC, 0x51A8, 0x5E03, 0x5E9C, + 0x6016, 0x6276, 0x6577, 0x65A7, 0x666E, 0x6D6E, 0x7236, 0x7B26, 0x8150, 0x819A, 0x8299, 0x8B5C, + 0x8CA0, 0x8CE6, 0x8D74, 0x961C, 0x9644, 0x4FAE, 0x64AB, 0x6B66, 0x821E, 0x8461, 0x856A, 0x90E8, + 0x5C01, 0x6953, 0x98A8, 0x847A, 0x8557, 0x4F0F, 0x526F, 0x5FA9, 0x5E45, 0x670D}, + {/* category 42 */ + 0x798F, 0x8179, 0x8907, 0x8986, 0x6DF5, 0x5F17, 0x6255, 0x6CB8, 0x4ECF, 0x7269, 0x9B92, 0x5206, + 0x543B, 0x5674, 0x58B3, 0x61A4, 0x626E, 0x711A, 0x596E, 0x7C89, 0x7CDE, 0x7D1B, 0x96F0, 0x6587, + 0x805E, 0x4E19, 0x4F75, 0x5175, 0x5840, 0x5E63, 0x5E73, 0x5F0A, 0x67C4, 0x4E26, 0x853D, 0x9589, + 0x965B, 0x7C73, 0x9801, 0x50FB, 0x58C1, 0x7656, 0x78A7, 0x5225, 0x77A5, 0x8511, 0x7B86, 0x504F, + 0x5909, 0x7247, 0x7BC7, 0x7DE8, 0x8FBA, 0x8FD4, 0x904D, 0x4FBF, 0x52C9, 0x5A29, 0x5F01, 0x97AD, + 0x4FDD, 0x8217, 0x92EA, 0x5703, 0x6355, 0x6B69, 0x752B, 0x88DC, 0x8F14, 0x7A42, 0x52DF, 0x5893, + 0x6155, 0x620A, 0x66AE, 0x6BCD, 0x7C3F, 0x83E9, 0x5023, 0x4FF8, 0x5305, 0x5446, 0x5831, 0x5949, + 0x5B9D, 0x5CF0, 0x5CEF, 0x5D29, 0x5E96, 0x62B1, 0x6367, 0x653E, 0x65B9, 0x670B}, + {/* category 43 */ + 0x6CD5, 0x6CE1, 0x70F9, 0x7832, 0x7E2B, 0x80DE, 0x82B3, 0x840C, 0x84EC, 0x8702, 0x8912, 0x8A2A, + 0x8C4A, 0x90A6, 0x92D2, 0x98FD, 0x9CF3, 0x9D6C, 0x4E4F, 0x4EA1, 0x508D, 0x5256, 0x574A, 0x59A8, + 0x5E3D, 0x5FD8, 0x5FD9, 0x623F, 0x66B4, 0x671B, 0x67D0, 0x68D2, 0x5192, 0x7D21, 0x80AA, 0x81A8, + 0x8B00, 0x8C8C, 0x8CBF, 0x927E, 0x9632, 0x5420, 0x982C, 0x5317, 0x50D5, 0x535C, 0x58A8, 0x64B2, + 0x6734, 0x7267, 0x7766, 0x7A46, 0x91E6, 0x52C3, 0x6CA1, 0x6B86, 0x5800, 0x5E4C, 0x5954, 0x672C, + 0x7FFB, 0x51E1, 0x76C6, 0x6469, 0x78E8, 0x9B54, 0x9EBB, 0x57CB, 0x59B9, 0x6627, 0x679A, 0x6BCE, + 0x54E9, 0x69D9, 0x5E55, 0x819C, 0x6795, 0x9BAA, 0x67FE, 0x9C52, 0x685D, 0x4EA6, 0x4FE3, 0x53C8, + 0x62B9, 0x672B, 0x6CAB, 0x8FC4, 0x4FAD, 0x7E6D, 0x9EBF, 0x4E07, 0x6162, 0x6E80}, + {/* category 44 */ + 0x6F2B, 0x8513, 0x5473, 0x672A, 0x9B45, 0x5DF3, 0x7B95, 0x5CAC, 0x5BC6, 0x871C, 0x6E4A, 0x84D1, + 0x7A14, 0x8108, 0x5999, 0x7C8D, 0x6C11, 0x7720, 0x52D9, 0x5922, 0x7121, 0x725F, 0x77DB, 0x9727, + 0x9D61, 0x690B, 0x5A7F, 0x5A18, 0x51A5, 0x540D, 0x547D, 0x660E, 0x76DF, 0x8FF7, 0x9298, 0x9CF4, + 0x59EA, 0x725D, 0x6EC5, 0x514D, 0x68C9, 0x7DBF, 0x7DEC, 0x9762, 0x9EBA, 0x6478, 0x6A21, 0x8302, + 0x5984, 0x5B5F, 0x6BDB, 0x731B, 0x76F2, 0x7DB2, 0x8017, 0x8499, 0x5132, 0x6728, 0x9ED9, 0x76EE, + 0x6762, 0x52FF, 0x9905, 0x5C24, 0x623B, 0x7C7E, 0x8CB0, 0x554F, 0x60B6, 0x7D0B, 0x9580, 0x5301, + 0x4E5F, 0x51B6, 0x591C, 0x723A, 0x8036, 0x91CE, 0x5F25, 0x77E2, 0x5384, 0x5F79, 0x7D04, 0x85AC, + 0x8A33, 0x8E8D, 0x9756, 0x67F3, 0x85AE, 0x9453, 0x6109, 0x6108, 0x6CB9, 0x7652}, + {/* category 45 */ + 0x8AED, 0x8F38, 0x552F, 0x4F51, 0x512A, 0x52C7, 0x53CB, 0x5BA5, 0x5E7D, 0x60A0, 0x6182, 0x63D6, + 0x6709, 0x67DA, 0x6E67, 0x6D8C, 0x7336, 0x7337, 0x7531, 0x7950, 0x88D5, 0x8A98, 0x904A, 0x9091, + 0x90F5, 0x96C4, 0x878D, 0x5915, 0x4E88, 0x4F59, 0x4E0E, 0x8A89, 0x8F3F, 0x9810, 0x50AD, 0x5E7C, + 0x5996, 0x5BB9, 0x5EB8, 0x63DA, 0x63FA, 0x64C1, 0x66DC, 0x694A, 0x69D8, 0x6D0B, 0x6EB6, 0x7194, + 0x7528, 0x7AAF, 0x7F8A, 0x8000, 0x8449, 0x84C9, 0x8981, 0x8B21, 0x8E0A, 0x9065, 0x967D, 0x990A, + 0x617E, 0x6291, 0x6B32, 0x6C83, 0x6D74, 0x7FCC, 0x7FFC, 0x6DC0, 0x7F85, 0x87BA, 0x88F8, 0x6765, + 0x83B1, 0x983C, 0x96F7, 0x6D1B, 0x7D61, 0x843D, 0x916A, 0x4E71, 0x5375, 0x5D50, 0x6B04, 0x6FEB, + 0x85CD, 0x862D, 0x89A7, 0x5229, 0x540F, 0x5C65, 0x674E, 0x68A8, 0x7406, 0x7483}, + {/* category 46 */ + 0x75E2, 0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x9678, 0x5F8B, 0x7387, 0x7ACB, 0x844E, 0x63A0, 0x7565, + 0x5289, 0x6D41, 0x6E9C, 0x7409, 0x7559, 0x786B, 0x7C92, 0x9686, 0x7ADC, 0x9F8D, 0x4FB6, 0x616E, + 0x65C5, 0x865C, 0x4E86, 0x4EAE, 0x50DA, 0x4E21, 0x51CC, 0x5BEE, 0x6599, 0x6881, 0x6DBC, 0x731F, + 0x7642, 0x77AD, 0x7A1C, 0x7CE7, 0x826F, 0x8AD2, 0x907C, 0x91CF, 0x9675, 0x9818, 0x529B, 0x7DD1, + 0x502B, 0x5398, 0x6797, 0x6DCB, 0x71D0, 0x7433, 0x81E8, 0x8F2A, 0x96A3, 0x9C57, 0x9E9F, 0x7460, + 0x5841, 0x6D99, 0x7D2F, 0x985E, 0x4EE4, 0x4F36, 0x4F8B, 0x51B7, 0x52B1, 0x5DBA, 0x601C, 0x73B2, + 0x793C, 0x82D3, 0x9234, 0x96B7, 0x96F6, 0x970A, 0x9E97, 0x9F62, 0x66A6, 0x6B74, 0x5217, 0x52A3, + 0x70C8, 0x88C2, 0x5EC9, 0x604B, 0x6190, 0x6F23, 0x7149, 0x7C3E, 0x7DF4, 0x806F}, + {/* category 47 */ + 0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089, 0x8CC2, 0x8DEF, 0x9732, 0x52B4, 0x5A41, + 0x5ECA, 0x5F04, 0x6717, 0x697C, 0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001, 0x807E, + 0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332, 0x8AD6, 0x502D, 0x548C, 0x8A71, 0x6B6A, + 0x8CC4, 0x8107, 0x60D1, 0x67A0, 0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568, 0x6900, + 0x6E7E, 0x7897, 0x8155, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 48 */ + 0x5F0C, 0x4E10, 0x4E15, 0x4E2A, 0x4E31, 0x4E36, 0x4E3C, 0x4E3F, 0x4E42, 0x4E56, 0x4E58, 0x4E82, + 0x4E85, 0x8C6B, 0x4E8A, 0x8212, 0x5F0D, 0x4E8E, 0x4E9E, 0x4E9F, 0x4EA0, 0x4EA2, 0x4EB0, 0x4EB3, + 0x4EB6, 0x4ECE, 0x4ECD, 0x4EC4, 0x4EC6, 0x4EC2, 0x4ED7, 0x4EDE, 0x4EED, 0x4EDF, 0x4EF7, 0x4F09, + 0x4F5A, 0x4F30, 0x4F5B, 0x4F5D, 0x4F57, 0x4F47, 0x4F76, 0x4F88, 0x4F8F, 0x4F98, 0x4F7B, 0x4F69, + 0x4F70, 0x4F91, 0x4F6F, 0x4F86, 0x4F96, 0x5118, 0x4FD4, 0x4FDF, 0x4FCE, 0x4FD8, 0x4FDB, 0x4FD1, + 0x4FDA, 0x4FD0, 0x4FE4, 0x4FE5, 0x501A, 0x5028, 0x5014, 0x502A, 0x5025, 0x5005, 0x4F1C, 0x4FF6, + 0x5021, 0x5029, 0x502C, 0x4FFE, 0x4FEF, 0x5011, 0x5006, 0x5043, 0x5047, 0x6703, 0x5055, 0x5050, + 0x5048, 0x505A, 0x5056, 0x506C, 0x5078, 0x5080, 0x509A, 0x5085, 0x50B4, 0x50B2}, + {/* category 49 */ + 0x50C9, 0x50CA, 0x50B3, 0x50C2, 0x50D6, 0x50DE, 0x50E5, 0x50ED, 0x50E3, 0x50EE, 0x50F9, 0x50F5, + 0x5109, 0x5101, 0x5102, 0x5116, 0x5115, 0x5114, 0x511A, 0x5121, 0x513A, 0x5137, 0x513C, 0x513B, + 0x513F, 0x5140, 0x5152, 0x514C, 0x5154, 0x5162, 0x7AF8, 0x5169, 0x516A, 0x516E, 0x5180, 0x5182, + 0x56D8, 0x518C, 0x5189, 0x518F, 0x5191, 0x5193, 0x5195, 0x5196, 0x51A4, 0x51A6, 0x51A2, 0x51A9, + 0x51AA, 0x51AB, 0x51B3, 0x51B1, 0x51B2, 0x51B0, 0x51B5, 0x51BD, 0x51C5, 0x51C9, 0x51DB, 0x51E0, + 0x8655, 0x51E9, 0x51ED, 0x51F0, 0x51F5, 0x51FE, 0x5204, 0x520B, 0x5214, 0x520E, 0x5227, 0x522A, + 0x522E, 0x5233, 0x5239, 0x524F, 0x5244, 0x524B, 0x524C, 0x525E, 0x5254, 0x526A, 0x5274, 0x5269, + 0x5273, 0x527F, 0x527D, 0x528D, 0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8FA8}, + {/* category 50 */ + 0x8FA7, 0x52AC, 0x52AD, 0x52BC, 0x52B5, 0x52C1, 0x52CD, 0x52D7, 0x52DE, 0x52E3, 0x52E6, 0x98ED, + 0x52E0, 0x52F3, 0x52F5, 0x52F8, 0x52F9, 0x5306, 0x5308, 0x7538, 0x530D, 0x5310, 0x530F, 0x5315, + 0x531A, 0x5323, 0x532F, 0x5331, 0x5333, 0x5338, 0x5340, 0x5346, 0x5345, 0x4E17, 0x5349, 0x534D, + 0x51D6, 0x535E, 0x5369, 0x536E, 0x5918, 0x537B, 0x5377, 0x5382, 0x5396, 0x53A0, 0x53A6, 0x53A5, + 0x53AE, 0x53B0, 0x53B6, 0x53C3, 0x7C12, 0x96D9, 0x53DF, 0x66FC, 0x71EE, 0x53EE, 0x53E8, 0x53ED, + 0x53FA, 0x5401, 0x543D, 0x5440, 0x542C, 0x542D, 0x543C, 0x542E, 0x5436, 0x5429, 0x541D, 0x544E, + 0x548F, 0x5475, 0x548E, 0x545F, 0x5471, 0x5477, 0x5470, 0x5492, 0x547B, 0x5480, 0x5476, 0x5484, + 0x5490, 0x5486, 0x54C7, 0x54A2, 0x54B8, 0x54A5, 0x54AC, 0x54C4, 0x54C8, 0x54A8}, + {/* category 51 */ + 0x54AB, 0x54C2, 0x54A4, 0x54BE, 0x54BC, 0x54D8, 0x54E5, 0x54E6, 0x550F, 0x5514, 0x54FD, 0x54EE, + 0x54ED, 0x54FA, 0x54E2, 0x5539, 0x5540, 0x5563, 0x554C, 0x552E, 0x555C, 0x5545, 0x5556, 0x5557, + 0x5538, 0x5533, 0x555D, 0x5599, 0x5580, 0x54AF, 0x558A, 0x559F, 0x557B, 0x557E, 0x5598, 0x559E, + 0x55AE, 0x557C, 0x5583, 0x55A9, 0x5587, 0x55A8, 0x55DA, 0x55C5, 0x55DF, 0x55C4, 0x55DC, 0x55E4, + 0x55D4, 0x5614, 0x55F7, 0x5616, 0x55FE, 0x55FD, 0x561B, 0x55F9, 0x564E, 0x5650, 0x71DF, 0x5634, + 0x5636, 0x5632, 0x5638, 0x566B, 0x5664, 0x562F, 0x566C, 0x566A, 0x5686, 0x5680, 0x568A, 0x56A0, + 0x5694, 0x568F, 0x56A5, 0x56AE, 0x56B6, 0x56B4, 0x56C2, 0x56BC, 0x56C1, 0x56C3, 0x56C0, 0x56C8, + 0x56CE, 0x56D1, 0x56D3, 0x56D7, 0x56EE, 0x56F9, 0x5700, 0x56FF, 0x5704, 0x5709}, + {/* category 52 */ + 0x5708, 0x570B, 0x570D, 0x5713, 0x5718, 0x5716, 0x55C7, 0x571C, 0x5726, 0x5737, 0x5738, 0x574E, + 0x573B, 0x5740, 0x574F, 0x5769, 0x57C0, 0x5788, 0x5761, 0x577F, 0x5789, 0x5793, 0x57A0, 0x57B3, + 0x57A4, 0x57AA, 0x57B0, 0x57C3, 0x57C6, 0x57D4, 0x57D2, 0x57D3, 0x580A, 0x57D6, 0x57E3, 0x580B, + 0x5819, 0x581D, 0x5872, 0x5821, 0x5862, 0x584B, 0x5870, 0x6BC0, 0x5852, 0x583D, 0x5879, 0x5885, + 0x58B9, 0x589F, 0x58AB, 0x58BA, 0x58DE, 0x58BB, 0x58B8, 0x58AE, 0x58C5, 0x58D3, 0x58D1, 0x58D7, + 0x58D9, 0x58D8, 0x58E5, 0x58DC, 0x58E4, 0x58DF, 0x58EF, 0x58FA, 0x58F9, 0x58FB, 0x58FC, 0x58FD, + 0x5902, 0x590A, 0x5910, 0x591B, 0x68A6, 0x5925, 0x592C, 0x592D, 0x5932, 0x5938, 0x593E, 0x7AD2, + 0x5955, 0x5950, 0x594E, 0x595A, 0x5958, 0x5962, 0x5960, 0x5967, 0x596C, 0x5969}, + {/* category 53 */ + 0x5978, 0x5981, 0x599D, 0x4F5E, 0x4FAB, 0x59A3, 0x59B2, 0x59C6, 0x59E8, 0x59DC, 0x598D, 0x59D9, + 0x59DA, 0x5A25, 0x5A1F, 0x5A11, 0x5A1C, 0x5A09, 0x5A1A, 0x5A40, 0x5A6C, 0x5A49, 0x5A35, 0x5A36, + 0x5A62, 0x5A6A, 0x5A9A, 0x5ABC, 0x5ABE, 0x5ACB, 0x5AC2, 0x5ABD, 0x5AE3, 0x5AD7, 0x5AE6, 0x5AE9, + 0x5AD6, 0x5AFA, 0x5AFB, 0x5B0C, 0x5B0B, 0x5B16, 0x5B32, 0x5AD0, 0x5B2A, 0x5B36, 0x5B3E, 0x5B43, + 0x5B45, 0x5B40, 0x5B51, 0x5B55, 0x5B5A, 0x5B5B, 0x5B65, 0x5B69, 0x5B70, 0x5B73, 0x5B75, 0x5B78, + 0x6588, 0x5B7A, 0x5B80, 0x5B83, 0x5BA6, 0x5BB8, 0x5BC3, 0x5BC7, 0x5BC9, 0x5BD4, 0x5BD0, 0x5BE4, + 0x5BE6, 0x5BE2, 0x5BDE, 0x5BE5, 0x5BEB, 0x5BF0, 0x5BF6, 0x5BF3, 0x5C05, 0x5C07, 0x5C08, 0x5C0D, + 0x5C13, 0x5C20, 0x5C22, 0x5C28, 0x5C38, 0x5C39, 0x5C41, 0x5C46, 0x5C4E, 0x5C53}, + {/* category 54 */ + 0x5C50, 0x5C4F, 0x5B71, 0x5C6C, 0x5C6E, 0x4E62, 0x5C76, 0x5C79, 0x5C8C, 0x5C91, 0x5C94, 0x599B, + 0x5CAB, 0x5CBB, 0x5CB6, 0x5CBC, 0x5CB7, 0x5CC5, 0x5CBE, 0x5CC7, 0x5CD9, 0x5CE9, 0x5CFD, 0x5CFA, + 0x5CED, 0x5D8C, 0x5CEA, 0x5D0B, 0x5D15, 0x5D17, 0x5D5C, 0x5D1F, 0x5D1B, 0x5D11, 0x5D14, 0x5D22, + 0x5D1A, 0x5D19, 0x5D18, 0x5D4C, 0x5D52, 0x5D4E, 0x5D4B, 0x5D6C, 0x5D73, 0x5D76, 0x5D87, 0x5D84, + 0x5D82, 0x5DA2, 0x5D9D, 0x5DAC, 0x5DAE, 0x5DBD, 0x5D90, 0x5DB7, 0x5DBC, 0x5DC9, 0x5DCD, 0x5DD3, + 0x5DD2, 0x5DD6, 0x5DDB, 0x5DEB, 0x5DF2, 0x5DF5, 0x5E0B, 0x5E1A, 0x5E19, 0x5E11, 0x5E1B, 0x5E36, + 0x5E37, 0x5E44, 0x5E43, 0x5E40, 0x5E4E, 0x5E57, 0x5E54, 0x5E5F, 0x5E62, 0x5E64, 0x5E47, 0x5E75, + 0x5E76, 0x5E7A, 0x9EBC, 0x5E7F, 0x5EA0, 0x5EC1, 0x5EC2, 0x5EC8, 0x5ED0, 0x5ECF}, + {/* category 55 */ + 0x5ED6, 0x5EE3, 0x5EDD, 0x5EDA, 0x5EDB, 0x5EE2, 0x5EE1, 0x5EE8, 0x5EE9, 0x5EEC, 0x5EF1, 0x5EF3, + 0x5EF0, 0x5EF4, 0x5EF8, 0x5EFE, 0x5F03, 0x5F09, 0x5F5D, 0x5F5C, 0x5F0B, 0x5F11, 0x5F16, 0x5F29, + 0x5F2D, 0x5F38, 0x5F41, 0x5F48, 0x5F4C, 0x5F4E, 0x5F2F, 0x5F51, 0x5F56, 0x5F57, 0x5F59, 0x5F61, + 0x5F6D, 0x5F73, 0x5F77, 0x5F83, 0x5F82, 0x5F7F, 0x5F8A, 0x5F88, 0x5F91, 0x5F87, 0x5F9E, 0x5F99, + 0x5F98, 0x5FA0, 0x5FA8, 0x5FAD, 0x5FBC, 0x5FD6, 0x5FFB, 0x5FE4, 0x5FF8, 0x5FF1, 0x5FDD, 0x60B3, + 0x5FFF, 0x6021, 0x6060, 0x6019, 0x6010, 0x6029, 0x600E, 0x6031, 0x601B, 0x6015, 0x602B, 0x6026, + 0x600F, 0x603A, 0x605A, 0x6041, 0x606A, 0x6077, 0x605F, 0x604A, 0x6046, 0x604D, 0x6063, 0x6043, + 0x6064, 0x6042, 0x606C, 0x606B, 0x6059, 0x6081, 0x608D, 0x60E7, 0x6083, 0x609A}, + {/* category 56 */ + 0x6084, 0x609B, 0x6096, 0x6097, 0x6092, 0x60A7, 0x608B, 0x60E1, 0x60B8, 0x60E0, 0x60D3, 0x60B4, + 0x5FF0, 0x60BD, 0x60C6, 0x60B5, 0x60D8, 0x614D, 0x6115, 0x6106, 0x60F6, 0x60F7, 0x6100, 0x60F4, + 0x60FA, 0x6103, 0x6121, 0x60FB, 0x60F1, 0x610D, 0x610E, 0x6147, 0x613E, 0x6128, 0x6127, 0x614A, + 0x613F, 0x613C, 0x612C, 0x6134, 0x613D, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159, 0x615A, + 0x616B, 0x6174, 0x616F, 0x6165, 0x6171, 0x615F, 0x615D, 0x6153, 0x6175, 0x6199, 0x6196, 0x6187, + 0x61AC, 0x6194, 0x619A, 0x618A, 0x6191, 0x61AB, 0x61AE, 0x61CC, 0x61CA, 0x61C9, 0x61F7, 0x61C8, + 0x61C3, 0x61C6, 0x61BA, 0x61CB, 0x7F79, 0x61CD, 0x61E6, 0x61E3, 0x61F6, 0x61FA, 0x61F4, 0x61FF, + 0x61FD, 0x61FC, 0x61FE, 0x6200, 0x6208, 0x6209, 0x620D, 0x620C, 0x6214, 0x621B}, + {/* category 57 */ + 0x621E, 0x6221, 0x622A, 0x622E, 0x6230, 0x6232, 0x6233, 0x6241, 0x624E, 0x625E, 0x6263, 0x625B, + 0x6260, 0x6268, 0x627C, 0x6282, 0x6289, 0x627E, 0x6292, 0x6293, 0x6296, 0x62D4, 0x6283, 0x6294, + 0x62D7, 0x62D1, 0x62BB, 0x62CF, 0x62FF, 0x62C6, 0x64D4, 0x62C8, 0x62DC, 0x62CC, 0x62CA, 0x62C2, + 0x62C7, 0x629B, 0x62C9, 0x630C, 0x62EE, 0x62F1, 0x6327, 0x6302, 0x6308, 0x62EF, 0x62F5, 0x6350, + 0x633E, 0x634D, 0x641C, 0x634F, 0x6396, 0x638E, 0x6380, 0x63AB, 0x6376, 0x63A3, 0x638F, 0x6389, + 0x639F, 0x63B5, 0x636B, 0x6369, 0x63BE, 0x63E9, 0x63C0, 0x63C6, 0x63E3, 0x63C9, 0x63D2, 0x63F6, + 0x63C4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436, 0x651D, 0x6417, 0x6428, 0x640F, 0x6467, + 0x646F, 0x6476, 0x644E, 0x652A, 0x6495, 0x6493, 0x64A5, 0x64A9, 0x6488, 0x64BC}, + {/* category 58 */ + 0x64DA, 0x64D2, 0x64C5, 0x64C7, 0x64BB, 0x64D8, 0x64C2, 0x64F1, 0x64E7, 0x8209, 0x64E0, 0x64E1, + 0x62AC, 0x64E3, 0x64EF, 0x652C, 0x64F6, 0x64F4, 0x64F2, 0x64FA, 0x6500, 0x64FD, 0x6518, 0x651C, + 0x6505, 0x6524, 0x6523, 0x652B, 0x6534, 0x6535, 0x6537, 0x6536, 0x6538, 0x754B, 0x6548, 0x6556, + 0x6555, 0x654D, 0x6558, 0x655E, 0x655D, 0x6572, 0x6578, 0x6582, 0x6583, 0x8B8A, 0x659B, 0x659F, + 0x65AB, 0x65B7, 0x65C3, 0x65C6, 0x65C1, 0x65C4, 0x65CC, 0x65D2, 0x65DB, 0x65D9, 0x65E0, 0x65E1, + 0x65F1, 0x6772, 0x660A, 0x6603, 0x65FB, 0x6773, 0x6635, 0x6636, 0x6634, 0x661C, 0x664F, 0x6644, + 0x6649, 0x6641, 0x665E, 0x665D, 0x6664, 0x6667, 0x6668, 0x665F, 0x6662, 0x6670, 0x6683, 0x6688, + 0x668E, 0x6689, 0x6684, 0x6698, 0x669D, 0x66C1, 0x66B9, 0x66C9, 0x66BE, 0x66BC}, + {/* category 59 */ + 0x66C4, 0x66B8, 0x66D6, 0x66DA, 0x66E0, 0x663F, 0x66E6, 0x66E9, 0x66F0, 0x66F5, 0x66F7, 0x670F, + 0x6716, 0x671E, 0x6726, 0x6727, 0x9738, 0x672E, 0x673F, 0x6736, 0x6741, 0x6738, 0x6737, 0x6746, + 0x675E, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770, 0x67A9, 0x677C, 0x676A, 0x678C, 0x678B, + 0x67A6, 0x67A1, 0x6785, 0x67B7, 0x67EF, 0x67B4, 0x67EC, 0x67B3, 0x67E9, 0x67B8, 0x67E4, 0x67DE, + 0x67DD, 0x67E2, 0x67EE, 0x67B9, 0x67CE, 0x67C6, 0x67E7, 0x6A9C, 0x681E, 0x6846, 0x6829, 0x6840, + 0x684D, 0x6832, 0x684E, 0x68B3, 0x682B, 0x6859, 0x6863, 0x6877, 0x687F, 0x689F, 0x688F, 0x68AD, + 0x6894, 0x689D, 0x689B, 0x6883, 0x6AAE, 0x68B9, 0x6874, 0x68B5, 0x68A0, 0x68BA, 0x690F, 0x688D, + 0x687E, 0x6901, 0x68CA, 0x6908, 0x68D8, 0x6922, 0x6926, 0x68E1, 0x690C, 0x68CD}, + {/* category 60 */ + 0x68D4, 0x68E7, 0x68D5, 0x6936, 0x6912, 0x6904, 0x68D7, 0x68E3, 0x6925, 0x68F9, 0x68E0, 0x68EF, + 0x6928, 0x692A, 0x691A, 0x6923, 0x6921, 0x68C6, 0x6979, 0x6977, 0x695C, 0x6978, 0x696B, 0x6954, + 0x697E, 0x696E, 0x6939, 0x6974, 0x693D, 0x6959, 0x6930, 0x6961, 0x695E, 0x695D, 0x6981, 0x696A, + 0x69B2, 0x69AE, 0x69D0, 0x69BF, 0x69C1, 0x69D3, 0x69BE, 0x69CE, 0x5BE8, 0x69CA, 0x69DD, 0x69BB, + 0x69C3, 0x69A7, 0x6A2E, 0x6991, 0x69A0, 0x699C, 0x6995, 0x69B4, 0x69DE, 0x69E8, 0x6A02, 0x6A1B, + 0x69FF, 0x6B0A, 0x69F9, 0x69F2, 0x69E7, 0x6A05, 0x69B1, 0x6A1E, 0x69ED, 0x6A14, 0x69EB, 0x6A0A, + 0x6A12, 0x6AC1, 0x6A23, 0x6A13, 0x6A44, 0x6A0C, 0x6A72, 0x6A36, 0x6A78, 0x6A47, 0x6A62, 0x6A59, + 0x6A66, 0x6A48, 0x6A38, 0x6A22, 0x6A90, 0x6A8D, 0x6AA0, 0x6A84, 0x6AA2, 0x6AA3}, + {/* category 61 */ + 0x6A97, 0x8617, 0x6ABB, 0x6AC3, 0x6AC2, 0x6AB8, 0x6AB3, 0x6AAC, 0x6ADE, 0x6AD1, 0x6ADF, 0x6AAA, + 0x6ADA, 0x6AEA, 0x6AFB, 0x6B05, 0x8616, 0x6AFA, 0x6B12, 0x6B16, 0x9B31, 0x6B1F, 0x6B38, 0x6B37, + 0x76DC, 0x6B39, 0x98EE, 0x6B47, 0x6B43, 0x6B49, 0x6B50, 0x6B59, 0x6B54, 0x6B5B, 0x6B5F, 0x6B61, + 0x6B78, 0x6B79, 0x6B7F, 0x6B80, 0x6B84, 0x6B83, 0x6B8D, 0x6B98, 0x6B95, 0x6B9E, 0x6BA4, 0x6BAA, + 0x6BAB, 0x6BAF, 0x6BB2, 0x6BB1, 0x6BB3, 0x6BB7, 0x6BBC, 0x6BC6, 0x6BCB, 0x6BD3, 0x6BDF, 0x6BEC, + 0x6BEB, 0x6BF3, 0x6BEF, 0x9EBE, 0x6C08, 0x6C13, 0x6C14, 0x6C1B, 0x6C24, 0x6C23, 0x6C5E, 0x6C55, + 0x6C62, 0x6C6A, 0x6C82, 0x6C8D, 0x6C9A, 0x6C81, 0x6C9B, 0x6C7E, 0x6C68, 0x6C73, 0x6C92, 0x6C90, + 0x6CC4, 0x6CF1, 0x6CD3, 0x6CBD, 0x6CD7, 0x6CC5, 0x6CDD, 0x6CAE, 0x6CB1, 0x6CBE}, + {/* category 62 */ + 0x6CBA, 0x6CDB, 0x6CEF, 0x6CD9, 0x6CEA, 0x6D1F, 0x884D, 0x6D36, 0x6D2B, 0x6D3D, 0x6D38, 0x6D19, + 0x6D35, 0x6D33, 0x6D12, 0x6D0C, 0x6D63, 0x6D93, 0x6D64, 0x6D5A, 0x6D79, 0x6D59, 0x6D8E, 0x6D95, + 0x6FE4, 0x6D85, 0x6DF9, 0x6E15, 0x6E0A, 0x6DB5, 0x6DC7, 0x6DE6, 0x6DB8, 0x6DC6, 0x6DEC, 0x6DDE, + 0x6DCC, 0x6DE8, 0x6DD2, 0x6DC5, 0x6DFA, 0x6DD9, 0x6DE4, 0x6DD5, 0x6DEA, 0x6DEE, 0x6E2D, 0x6E6E, + 0x6E2E, 0x6E19, 0x6E72, 0x6E5F, 0x6E3E, 0x6E23, 0x6E6B, 0x6E2B, 0x6E76, 0x6E4D, 0x6E1F, 0x6E43, + 0x6E3A, 0x6E4E, 0x6E24, 0x6EFF, 0x6E1D, 0x6E38, 0x6E82, 0x6EAA, 0x6E98, 0x6EC9, 0x6EB7, 0x6ED3, + 0x6EBD, 0x6EAF, 0x6EC4, 0x6EB2, 0x6ED4, 0x6ED5, 0x6E8F, 0x6EA5, 0x6EC2, 0x6E9F, 0x6F41, 0x6F11, + 0x704C, 0x6EEC, 0x6EF8, 0x6EFE, 0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC}, + {/* category 63 */ + 0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, + 0x6F82, 0x6F7C, 0x6F58, 0x6F8E, 0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, + 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1, 0x6FEE, 0x6FDB, 0x7009, 0x700B, + 0x6FFA, 0x7011, 0x7001, 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030, + 0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, + 0x70DF, 0x70CB, 0x70DD, 0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, + 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184, 0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, + 0x71BE, 0x71D2, 0x71C9, 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC}, + {/* category 64 */ + 0x71F9, 0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, + 0x723F, 0x7240, 0x7246, 0x724B, 0x7258, 0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, + 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, 0x72D2, 0x72E2, 0x72E0, 0x72E1, + 0x72F9, 0x72F7, 0x500F, 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, + 0x733E, 0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, + 0x73C8, 0x73B3, 0x73CE, 0x73BB, 0x73C0, 0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, + 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, 0x745C, 0x7469, 0x7470, 0x7463, + 0x746A, 0x7476, 0x747E, 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1}, + {/* category 65 */ + 0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, + 0x7505, 0x750C, 0x750E, 0x750D, 0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, + 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567, 0x756B, 0x756D, 0x7578, 0x7576, + 0x7586, 0x7587, 0x7574, 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2, + 0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, + 0x75DE, 0x75FE, 0x75FF, 0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, + 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, 0x7630, 0x763B, 0x7647, 0x7648, 0x7646, + 0x765C, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670}, + {/* category 66 */ + 0x7672, 0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, + 0x769A, 0x76B0, 0x76B4, 0x76B8, 0x76B9, 0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, + 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, 0x7729, 0x7724, 0x771E, 0x7725, + 0x7726, 0x771B, 0x7737, 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, + 0x7779, 0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, + 0x77BB, 0x77C7, 0x77CD, 0x77D7, 0x77DA, 0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, + 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, 0x788C, 0x78A3, 0x78B5, 0x78AA, + 0x78AF, 0x78D1, 0x78C6, 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC}, + {/* category 67 */ + 0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, + 0x7957, 0x795F, 0x795A, 0x7955, 0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, + 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC, 0x79E1, 0x79E3, 0x7A08, 0x7A0D, + 0x7A18, 0x7A19, 0x7A20, 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49, + 0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, + 0x7AA9, 0x7AC8, 0x7AB0, 0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, + 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2, 0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, + 0x7B0A, 0x7B06, 0x7B33, 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50}, + {/* category 68 */ + 0x7B7A, 0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, + 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, 0x7B9F, 0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, + 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, 0x7BE9, 0x7C11, 0x7C14, 0x7BE6, + 0x7BE5, 0x7C60, 0x7C00, 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, + 0x7C2A, 0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, + 0x7C5F, 0x7C64, 0x7C56, 0x7C65, 0x7C6C, 0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, + 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, 0x7CC0, 0x7CC5, 0x7CC2, 0x7CD8, + 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06}, + {/* category 69 */ + 0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, + 0x7D56, 0x7D4E, 0x7D72, 0x7D68, 0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, + 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB, 0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, + 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A, + 0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, + 0x7E39, 0x7E43, 0x7E37, 0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, + 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D, 0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, + 0x7E92, 0x7E90, 0x7E93, 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A}, + {/* category 70 */ + 0x7F45, 0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, + 0x7F69, 0x7F67, 0x7F78, 0x7F82, 0x7F86, 0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, + 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, 0x8B71, 0x7FC5, 0x7FC6, 0x7FCA, + 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, + 0x8018, 0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, + 0x805F, 0x8062, 0x8068, 0x8073, 0x8072, 0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, + 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, 0x80E5, 0x80D9, 0x80DD, 0x80C4, + 0x80DA, 0x80D6, 0x8109, 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B}, + {/* category 71 */ + 0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, + 0x8188, 0x818A, 0x8180, 0x8182, 0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, + 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9, 0x81CD, 0x81D1, 0x81D9, 0x81D8, + 0x81C8, 0x81DA, 0x81DF, 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207, + 0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, + 0x825A, 0x825F, 0x8264, 0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, + 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1, 0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, + 0x82FA, 0x8393, 0x8303, 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9}, + {/* category 72 */ + 0x8335, 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, + 0x8318, 0x8385, 0x839A, 0x83AA, 0x839F, 0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, + 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, 0x83EB, 0x83CE, 0x83FD, 0x8403, + 0x83D8, 0x840B, 0x83C1, 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, + 0x8506, 0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, + 0x8469, 0x8446, 0x842C, 0x846F, 0x8479, 0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, + 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, 0x8521, 0x84FF, 0x84F4, 0x8517, + 0x8518, 0x852C, 0x851F, 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548}, + {/* category 73 */ + 0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, + 0x859B, 0x85EA, 0x8587, 0x859C, 0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, + 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B, 0x85FE, 0x85FA, 0x8606, 0x8622, + 0x861A, 0x8630, 0x863F, 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9, + 0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, + 0x86DE, 0x86E9, 0x86EC, 0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, + 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F, 0x8737, 0x873B, 0x8725, 0x8729, 0x871A, + 0x8760, 0x875F, 0x8778, 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759}, + {/* category 74 */ + 0x8753, 0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, + 0x96D6, 0x87AB, 0x87C4, 0x87B3, 0x87C7, 0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, + 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, 0x8822, 0x8821, 0x8831, 0x8836, + 0x8839, 0x8827, 0x883B, 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, + 0x889E, 0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, + 0x88A4, 0x88B0, 0x88BF, 0x88B1, 0x88C3, 0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, + 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, 0x8943, 0x891E, 0x8925, 0x892A, + 0x892B, 0x8941, 0x8944, 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E}, + {/* category 75 */ + 0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, + 0x8998, 0x89A1, 0x89A9, 0x89A6, 0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, + 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10, 0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, + 0x8A36, 0x8A41, 0x8A5B, 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82, + 0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, + 0x8AEB, 0x8AF3, 0x8AE7, 0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, + 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20, 0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, + 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B}, + {/* category 76 */ + 0x8B5F, 0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, + 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, 0x8C48, 0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, + 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, 0x8C7C, 0x8C98, 0x621D, 0x8CAD, + 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, + 0x8CFA, 0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, + 0x8D14, 0x8D16, 0x8D67, 0x8D6D, 0x8D71, 0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, + 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, 0x8DE3, 0x8DFC, 0x8E08, 0x8E09, + 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A}, + {/* category 77 */ + 0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, + 0x8E72, 0x8E7C, 0x8E81, 0x8E87, 0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, + 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5, 0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, + 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F, + 0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, + 0x8F4E, 0x8F57, 0x8F5C, 0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, + 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4, 0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, + 0x9021, 0x900D, 0x901E, 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8}, + {/* category 78 */ + 0x904F, 0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, + 0x9076, 0x96A8, 0x9072, 0x9082, 0x907D, 0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, + 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, 0x9119, 0x9132, 0x9130, 0x914A, + 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, + 0x91AF, 0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, + 0x91E1, 0x91DB, 0x91FC, 0x91F5, 0x91F6, 0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, + 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, 0x9250, 0x929C, 0x9296, 0x9293, + 0x929B, 0x925A, 0x92CF, 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E}, + {/* category 79 */ + 0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, + 0x93B0, 0x93AC, 0x93AD, 0x9394, 0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, + 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407, 0x9410, 0x9436, 0x942B, 0x9435, + 0x9421, 0x943A, 0x9441, 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470, + 0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, + 0x9596, 0x9598, 0x9599, 0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, + 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6, 0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, + 0x9628, 0x962E, 0x962F, 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E}, + {/* category 80 */ + 0x965D, 0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, + 0x96B2, 0x96B0, 0x96B4, 0x96B6, 0x96B8, 0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, + 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, 0x9711, 0x970F, 0x9716, 0x9719, + 0x9724, 0x972A, 0x9730, 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, + 0x9760, 0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, + 0x9786, 0x978B, 0x978F, 0x9790, 0x979C, 0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, + 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, 0x97F5, 0x980F, 0x980C, 0x9838, + 0x9824, 0x9821, 0x9837, 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870}, + {/* category 81 */ + 0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, + 0x9903, 0x9909, 0x9912, 0x9914, 0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, + 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951, 0x9952, 0x994C, 0x9955, 0x9997, + 0x9998, 0x99A5, 0x99AD, 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE, + 0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, + 0x9A42, 0x9A40, 0x9A43, 0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, + 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0, 0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, + 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7}, + {/* category 82 */ + 0x9AFB, 0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, + 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, 0x9B43, 0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, + 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, 0x9BC0, 0x9BCA, 0x9BB9, 0x9BC6, + 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, + 0x9C15, 0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, + 0x9C25, 0x9C24, 0x9C21, 0x9C30, 0x9C47, 0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, + 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, 0x9D06, 0x9D2A, 0x9D26, 0x9DAF, + 0x9D23, 0x9D1F, 0x9D44, 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48}, + {/* category 83 */ + 0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, + 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2, 0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, + 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A, 0x9E1B, 0x9E1E, 0x9E75, 0x9E79, + 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8, + 0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, + 0x9EE5, 0x9EE8, 0x9EEF, 0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, + 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52, 0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, + 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0}, + {/* category 84 */ + 0x582F, 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 85 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 86 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 87 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 88 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {/* category 89 */ + 0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB, 0x6631, 0x68C8, 0x92F9, 0x66FB, 0x5F45, + 0x4E28, 0x4EE1, 0x4EFC, 0x4F00, 0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94, 0x4FCD, + 0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042, 0x5094, 0x50F4, 0x50D8, 0x514A, 0x5164, + 0x519D, 0x51BE, 0x51EC, 0x5215, 0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324, 0x5372, + 0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9, 0x54FF, 0x5586, 0x5759, 0x5765, 0x57AC, + 0x57C8, 0x57C7, 0xFA0F, 0xFA10, 0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963, 0x59A4, + 0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E, 0x5CA6, 0x5CBA, 0x5CF5, 0x5D27, 0x5D53, + 0xFA11, 0x5D42, 0x5D6D, 0x5DB8, 0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7}, + {/* category 90 */ + 0x5FDE, 0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120, 0x60F2, 0x6111, 0x6137, 0x6130, 0x6198, + 0x6213, 0x62A6, 0x63F5, 0x6460, 0x649D, 0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609, 0x662E, + 0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673, 0x6699, 0x66A0, 0x66B2, 0x66BF, 0x66FA, + 0x670E, 0xF929, 0x6766, 0x67BB, 0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968, 0xFA14, + 0x6998, 0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E, 0x6AE2, 0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, + 0x6C86, 0x6C6F, 0x6CDA, 0x6D04, 0x6D87, 0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2, 0x6DFC, + 0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5, 0x6FF5, 0x7005, 0x7007, 0x7028, 0x7085, + 0x70AB, 0x710F, 0x7104, 0x715C, 0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1}, + {/* category 91 */ + 0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6, 0x73E3, 0x73D2, 0x7407, 0x73F5, 0x7426, + 0x742A, 0x7429, 0x742E, 0x7462, 0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E, 0x769B, + 0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864, 0x787A, 0x7930, 0xFA18, 0xFA19, 0xFA1A, + 0x7994, 0xFA1B, 0x799B, 0x7AD1, 0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C, 0x7DB7, + 0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301, 0x8362, 0x837F, 0x83C7, 0x83F6, 0x8448, + 0x84B4, 0x8553, 0x8559, 0x856B, 0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12, 0x8A37, + 0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53, 0x8B7F, 0x8CF0, 0x8CF4, 0x8D12, 0x8D76, + 0xFA23, 0x8ECF, 0xFA24, 0xFA25, 0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA}, + {/* category 92 */ + 0x91D7, 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206, 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, + 0x924E, 0x9259, 0x9251, 0x9239, 0x9267, 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9, 0x92D0, + 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB, 0xFA28, 0x931E, 0x92FF, 0x931D, 0x9302, + 0x9370, 0x9357, 0x93A4, 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC, 0xFA29, + 0x969D, 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F, 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, + 0xFA2B, 0x9927, 0xFA2C, 0x999E, 0x9A4E, 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1, 0x9BBB, + 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0x0000, 0x0000, 0x2170, 0x2171, 0x2172, 0x2173, + 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02}}; #endif /* JISX0208_H */ diff --git a/ext/gd/libgd/pngtogd.c b/ext/gd/libgd/pngtogd.c index b663b170b710..6c90db66dcbd 100644 --- a/ext/gd/libgd/pngtogd.c +++ b/ext/gd/libgd/pngtogd.c @@ -1,45 +1,40 @@ -#include #include "gd.h" +#include /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in, *out; - if (argc != 3) - { - fprintf (stderr, "Usage: pngtogd filename.png filename.gd\n"); - exit (1); + gdImagePtr im; + FILE *in, *out; + if (argc != 3) { + fprintf(stderr, "Usage: pngtogd filename.png filename.gd\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromPng (in); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in PNG format!\n"); - exit (1); + im = gdImageCreateFromPng(in); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in PNG format!\n"); + exit(1); } - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } - gdImageGd (im, out); - fclose (out); - gdImageDestroy (im); + gdImageGd(im, out); + fclose(out); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/pngtogd2.c b/ext/gd/libgd/pngtogd2.c index c8b1d843af13..75f52c0dcd6b 100644 --- a/ext/gd/libgd/pngtogd2.c +++ b/ext/gd/libgd/pngtogd2.c @@ -1,52 +1,47 @@ +#include "gd.h" #include #include -#include "gd.h" /* A short program which converts a .png file into a .gd file, for your convenience in creating images on the fly from a basis image that must be loaded quickly. The .gd format is not intended to be a general-purpose format. */ -int -main (int argc, char **argv) +int main(int argc, char **argv) { - gdImagePtr im; - FILE *in, *out; - int cs, fmt; + gdImagePtr im; + FILE *in, *out; + int cs, fmt; - if (argc != 5) - { - fprintf (stderr, "Usage: pngtogd2 filename.png filename.gd2 cs fmt\n"); - fprintf (stderr, " where cs is the chunk size\n"); - fprintf (stderr, " fmt is 1 for raw, 2 for compressed\n"); - exit (1); + if (argc != 5) { + fprintf(stderr, "Usage: pngtogd2 filename.png filename.gd2 cs fmt\n"); + fprintf(stderr, " where cs is the chunk size\n"); + fprintf(stderr, " fmt is 1 for raw, 2 for compressed\n"); + exit(1); } - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Input file does not exist!\n"); - exit (1); + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Input file does not exist!\n"); + exit(1); } - im = gdImageCreateFromPng (in); - fclose (in); - if (!im) - { - fprintf (stderr, "Input is not in PNG format!\n"); - exit (1); + im = gdImageCreateFromPng(in); + fclose(in); + if (!im) { + fprintf(stderr, "Input is not in PNG format!\n"); + exit(1); } - out = fopen (argv[2], "wb"); - if (!out) - { - fprintf (stderr, "Output file cannot be written to!\n"); - gdImageDestroy (im); - exit (1); + out = fopen(argv[2], "wb"); + if (!out) { + fprintf(stderr, "Output file cannot be written to!\n"); + gdImageDestroy(im); + exit(1); } - cs = atoi (argv[3]); - fmt = atoi (argv[4]); - gdImageGd2 (im, out, cs, fmt); - fclose (out); - gdImageDestroy (im); + cs = atoi(argv[3]); + fmt = atoi(argv[4]); + gdImageGd2(im, out, cs, fmt); + fclose(out); + gdImageDestroy(im); - return 0; + return 0; } diff --git a/ext/gd/libgd/testac.c b/ext/gd/libgd/testac.c index 30c54e1641fa..31300784d9d7 100644 --- a/ext/gd/libgd/testac.c +++ b/ext/gd/libgd/testac.c @@ -1,129 +1,108 @@ -#include #include "gd.h" +#include /* If palette is true, we convert from truecolor to palette at the end, to test gdImageTrueColorToPalette and see file size/ quality tradeoffs. */ -void testDrawing ( - gdImagePtr im_in, - double scale, - int blending, - int palette, - char *filename); +void testDrawing(gdImagePtr im_in, double scale, int blending, int palette, char *filename); -int -main (int argc, char *argv[]) +int main(int argc, char *argv[]) { - /* Input and output files */ - FILE *in; - FILE *out; + /* Input and output files */ + FILE *in; + FILE *out; - /* Input image */ - gdImagePtr im_in = 0; + /* Input image */ + gdImagePtr im_in = 0; - /* Colors */ - int lightBlue; + /* Colors */ + int lightBlue; - if (argc != 2) - { - fprintf (stderr, "Usage: testac filename.png\n"); - exit (1); - } - /* Load original PNG, which should contain alpha channel - information. We will use it in two ways: preserving it - literally, for use with compatible browsers, and - compositing it ourselves against a background of our - choosing (alpha blending). We'll change its size - and try creating palette versions of it. */ - in = fopen (argv[1], "rb"); - if (!in) - { - fprintf (stderr, "Can't load %s.\n", argv[1]); - exit (1); + if (argc != 2) { + fprintf(stderr, "Usage: testac filename.png\n"); + exit(1); } - else - { - im_in = gdImageCreateFromPng (in); - fclose (in); + /* Load original PNG, which should contain alpha channel + information. We will use it in two ways: preserving it + literally, for use with compatible browsers, and + compositing it ourselves against a background of our + choosing (alpha blending). We'll change its size + and try creating palette versions of it. */ + in = fopen(argv[1], "rb"); + if (!in) { + fprintf(stderr, "Can't load %s.\n", argv[1]); + exit(1); + } else { + im_in = gdImageCreateFromPng(in); + fclose(in); } - testDrawing (im_in, 1.0, 0, 0, "noblending-fullsize-truecolor.png"); - testDrawing (im_in, 1.0, 1, 0, "blending-fullsize-truecolor.png"); - testDrawing (im_in, 0.5, 0, 0, "noblending-halfsize-truecolor.png"); - testDrawing (im_in, 0.5, 1, 0, "blending-halfsize-truecolor.png"); - testDrawing (im_in, 2.0, 0, 0, "noblending-doublesize-truecolor.png"); - testDrawing (im_in, 2.0, 1, 0, "blending-doublesize-truecolor.png"); - testDrawing (im_in, 1.0, 0, 1, "noblending-fullsize-palette.png"); - testDrawing (im_in, 1.0, 1, 1, "blending-fullsize-palette.png"); - testDrawing (im_in, 0.5, 0, 1, "noblending-halfsize-palette.png"); - testDrawing (im_in, 0.5, 1, 1, "blending-halfsize-palette.png"); - testDrawing (im_in, 2.0, 0, 1, "noblending-doublesize-palette.png"); - testDrawing (im_in, 2.0, 1, 1, "blending-doublesize-palette.png"); - gdImageDestroy (im_in); - return 0; + testDrawing(im_in, 1.0, 0, 0, "noblending-fullsize-truecolor.png"); + testDrawing(im_in, 1.0, 1, 0, "blending-fullsize-truecolor.png"); + testDrawing(im_in, 0.5, 0, 0, "noblending-halfsize-truecolor.png"); + testDrawing(im_in, 0.5, 1, 0, "blending-halfsize-truecolor.png"); + testDrawing(im_in, 2.0, 0, 0, "noblending-doublesize-truecolor.png"); + testDrawing(im_in, 2.0, 1, 0, "blending-doublesize-truecolor.png"); + testDrawing(im_in, 1.0, 0, 1, "noblending-fullsize-palette.png"); + testDrawing(im_in, 1.0, 1, 1, "blending-fullsize-palette.png"); + testDrawing(im_in, 0.5, 0, 1, "noblending-halfsize-palette.png"); + testDrawing(im_in, 0.5, 1, 1, "blending-halfsize-palette.png"); + testDrawing(im_in, 2.0, 0, 1, "noblending-doublesize-palette.png"); + testDrawing(im_in, 2.0, 1, 1, "blending-doublesize-palette.png"); + gdImageDestroy(im_in); + return 0; } /* If palette is true, we convert from truecolor to palette at the end, to test gdImageTrueColorToPalette and see file size/ quality tradeoffs. */ -void -testDrawing ( - gdImagePtr im_in, - double scale, - int blending, - int palette, - char *filename) +void testDrawing(gdImagePtr im_in, double scale, int blending, int palette, char *filename) { - gdImagePtr im_out; - FILE *out; - /* Create output image. */ - im_out = gdImageCreateTrueColor ((int) (gdImageSX (im_in) * scale), - (int) (gdImageSY (im_in) * scale)); - /* - Request alpha blending. This causes future - drawing operations to perform alpha channel blending - with the background, resulting in an opaque image. - Without this call, pixels in the foreground color are - copied literally, *including* the alpha channel value, - resulting in an output image which is potentially - not opaque. This flag can be set and cleared as often - as desired. */ - gdImageAlphaBlending (im_out, blending); + gdImagePtr im_out; + FILE *out; + /* Create output image. */ + im_out = + gdImageCreateTrueColor((int)(gdImageSX(im_in) * scale), (int)(gdImageSY(im_in) * scale)); + /* + Request alpha blending. This causes future + drawing operations to perform alpha channel blending + with the background, resulting in an opaque image. + Without this call, pixels in the foreground color are + copied literally, *including* the alpha channel value, + resulting in an output image which is potentially + not opaque. This flag can be set and cleared as often + as desired. */ + gdImageAlphaBlending(im_out, blending); - /* Flood with light blue. */ - gdImageFill (im_out, (int) (gdImageSX (im_in) * scale / 2), - (int) (gdImageSY (im_in) * scale / 2), - gdTrueColor (192, 192, 255)); - /* Copy the source image. Alpha blending should result in - compositing against red. With blending turned off, the - browser or viewer will composite against its preferred - background, or, if it does not support an alpha channel, - we will see the original colors for the pixels that - ought to be transparent or semitransparent. */ - gdImageCopyResampled (im_out, im_in, - 0, 0, - 0, 0, - (int) (gdImageSX (im_in) * scale), (int) (gdImageSY (im_in) * scale), - gdImageSX (im_in), gdImageSY (im_in)); - /* Write PNG */ - out = fopen (filename, "wb"); + /* Flood with light blue. */ + gdImageFill(im_out, (int)(gdImageSX(im_in) * scale / 2), (int)(gdImageSY(im_in) * scale / 2), + gdTrueColor(192, 192, 255)); + /* Copy the source image. Alpha blending should result in + compositing against red. With blending turned off, the + browser or viewer will composite against its preferred + background, or, if it does not support an alpha channel, + we will see the original colors for the pixels that + ought to be transparent or semitransparent. */ + gdImageCopyResampled(im_out, im_in, 0, 0, 0, 0, (int)(gdImageSX(im_in) * scale), + (int)(gdImageSY(im_in) * scale), gdImageSX(im_in), gdImageSY(im_in)); + /* Write PNG */ + out = fopen(filename, "wb"); - /* If this image is the result of alpha channel blending, - it will not contain an interesting alpha channel itself. - Save a little file size by not saving the alpha channel. - Otherwise the file would typically be slightly larger. */ - gdImageSaveAlpha (im_out, !blending); + /* If this image is the result of alpha channel blending, + it will not contain an interesting alpha channel itself. + Save a little file size by not saving the alpha channel. + Otherwise the file would typically be slightly larger. */ + gdImageSaveAlpha(im_out, !blending); - /* If requested, convert from truecolor to palette. */ - if (palette) - { - /* Dithering, 256 colors. */ - gdImageTrueColorToPalette (im_out, 1, 256); + /* If requested, convert from truecolor to palette. */ + if (palette) { + /* Dithering, 256 colors. */ + gdImageTrueColorToPalette(im_out, 1, 256); } - gdImagePng (im_out, out); - fclose (out); + gdImagePng(im_out, out); + fclose(out); - gdImageDestroy (im_out); + gdImageDestroy(im_out); } diff --git a/ext/gd/libgd/wbmp.c b/ext/gd/libgd/wbmp.c index dbca0f0178c6..cc237130ee76 100644 --- a/ext/gd/libgd/wbmp.c +++ b/ext/gd/libgd/wbmp.c @@ -1,389 +1,324 @@ - /* WBMP - ** ---- - ** WBMP Level 0: B/W, Uncompressed - ** This implements the WBMP format as specified in WAPSpec 1.1 and 1.2. - ** It does not support ExtHeaders as defined in the spec. The spec states - ** that a WAP client does not need to implement ExtHeaders. - ** - ** (c) 2000 Johan Van den Brande + * ---- + * WBMP Level 0: B/W, Uncompressed + * This implements the WBMP format as specified in WAPSpec 1.1 and 1.2. + * It does not support ExtHeaders as defined in the spec. The spec states + * that a WAP client does not need to implement ExtHeaders. + * + * (c) 2000 Johan Van den Brande */ - -#include #include +#include #include #include -#include "wbmp.h" #include "gd.h" #include "gdhelpers.h" +#include "wbmp.h" #ifdef NOTDEF -#define __TEST /* Compile with main function */ -#define __DEBUG /* Extra verbose when with __TEST */ -#define __WRITE /* readwbmp and writewbmp(stdout) */ -#define __VIEW /* view the wbmp on stdout */ +#define __TEST /* Compile with main function */ +#define __DEBUG /* Extra verbose when with __TEST */ +#define __WRITE /* readwbmp and writewbmp(stdout) */ +#define __VIEW /* view the wbmp on stdout */ #endif /* getmbi - ** ------ - ** Get a multibyte integer from a generic getin function - ** 'getin' can be getc, with in = NULL - ** you can find getin as a function just above the main function - ** This way you gain a lot of flexibilty about how this package - ** reads a wbmp file. + * ------ + * Get a multibyte integer from a generic getin function + * 'getin' can be getc, with in = NULL + * you can find getin as a function just above the main function + * This way you gain a lot of flexibility about how this package + * reads a wbmp file. */ -int -getmbi (int (*getin) (void *in), void *in) +int getmbi(int (*getin)(void *in), void *in) { - unsigned int mbi = 0; - int i; - - do - { - i = getin (in); - if (i < 0) - return (-1); - mbi = (mbi << 7) | (i & 0x7f); - } - while (i & 0x80); - - return (mbi); + unsigned int mbi = 0; + int i; + + do { + i = getin(in); + if (i < 0) { + return (-1); + } + mbi = (mbi << 7) | (i & 0x7f); + } while (i & 0x80); + + return mbi; } - /* putmbi - ** ------ - ** Put a multibyte intgerer in some kind of output stream - ** I work here with a function pointer, to make it as generic - ** as possible. Look at this function as an iterator on the - ** mbi integers it spits out. - ** + * ------ + * Put a multibyte intgerer in some kind of output stream + * I work here with a function pointer, to make it as generic + * as possible. Look at this function as an iterator on the + * mbi integers it spits out. + * */ -void -putmbi (int i, void (*putout) (int c, void *out), void *out) +void putmbi(int i, void (*putout)(int c, void *out), void *out) { - int cnt, l, accu; + int cnt, l, accu; - /* Get number of septets */ - cnt = 0; - accu = 0; - while (accu != i) - accu += i & 0x7f << 7 * cnt++; - - /* Produce the multibyte output */ - for (l = cnt - 1; l > 0; l--) - putout (0x80 | (i & 0x7f << 7 * l) >> 7 * l, out); + /* Get number of septets */ + accu = cnt = 0; + while (accu != i) { + accu += i & 0x7f << 7 * cnt++; + } - putout (i & 0x7f, out); + /* Produce the multibyte output */ + for (l = cnt - 1; l > 0; l--) { + putout(0x80 | (i & 0x7f << 7 * l) >> 7 * l, out); + } + putout(i & 0x7f, out); } - - /* skipheader - ** ---------- - ** Skips the ExtHeader. Not needed for the moment - ** + * ---------- + * Skips the ExtHeader. Not needed for the moment */ -int -skipheader (int (*getin) (void *in), void *in) +int skipheader(int (*getin)(void *in), void *in) { - int i; + int i; - do - { - i = getin (in); - if (i < 0) - return (-1); - } - while (i & 0x80); + do { + i = getin(in); + if (i < 0) { + return (-1); + } + } while (i & 0x80); - return (0); + return 0; } /* create wbmp - ** ----------- - ** create an empty wbmp - ** + * ----------- + * create an empty wbmp */ -Wbmp * -createwbmp (int width, int height, int color) +Wbmp *createwbmp(int width, int height, int color) { - int i; - - Wbmp *wbmp; - if ((wbmp = (Wbmp *) gdMalloc (sizeof (Wbmp))) == NULL) - return (NULL); + int i; + Wbmp *wbmp; - if (overflow2(sizeof (int), width)) { - gdFree(wbmp); - return NULL; - } - if (overflow2(sizeof (int) * width, height)) { - gdFree(wbmp); - return NULL; - } + if ((wbmp = (Wbmp *)gdMalloc(sizeof(Wbmp))) == NULL) { + return (NULL); + } - if ((wbmp->bitmap = (int *) safe_emalloc(sizeof(int), width * height, 0)) == NULL) - { - gdFree (wbmp); - return (NULL); + if (overflow2(sizeof(int), width)) { + gdFree(wbmp); + return NULL; } - wbmp->width = width; - wbmp->height = height; + if (overflow2(sizeof(int) * width, height)) { + gdFree(wbmp); + return NULL; + } - for (i = 0; i < width * height; wbmp->bitmap[i++] = color); + if ((wbmp->bitmap = (int *)gdMalloc(sizeof(int) * width * height)) == NULL) { + gdFree(wbmp); + return NULL; + } - return (wbmp); -} + wbmp->width = width; + wbmp->height = height; + for (i = 0; i < width * height; wbmp->bitmap[i++] = color) + ; + return wbmp; +} /* readwbmp - ** ------- - ** Actually reads the WBMP format from an open file descriptor - ** It goes along by returning a pointer to a WBMP struct. - ** + * ------- + * Actually reads the WBMP format from an open file descriptor + * It goes along by returning a pointer to a WBMP struct. */ -int -readwbmp (int (*getin) (void *in), void *in, Wbmp ** return_wbmp) +int readwbmp(int (*getin)(void *in), void *in, Wbmp **return_wbmp) { - int row, col, byte, pel, pos; - Wbmp *wbmp; - - if ((wbmp = (Wbmp *) gdMalloc (sizeof (Wbmp))) == NULL) - return (-1); + int row, col, byte, pel, pos; + Wbmp *wbmp; - wbmp->type = getin (in); - if (wbmp->type != 0) - { - gdFree (wbmp); - return (-1); + if ((wbmp = (Wbmp *)gdMalloc(sizeof(Wbmp))) == NULL) { + return -1; } - if (skipheader (getin, in)) - { - gdFree (wbmp); - return (-1); + wbmp->type = getin(in); + if (wbmp->type != 0) { + gdFree(wbmp); + return -1; } + if (skipheader(getin, in)) { + gdFree(wbmp); + return -1; + } - wbmp->width = getmbi (getin, in); - if (wbmp->width == -1) - { - gdFree (wbmp); - return (-1); + wbmp->width = getmbi(getin, in); + if (wbmp->width == -1) { + gdFree(wbmp); + return -1; } - wbmp->height = getmbi (getin, in); - if (wbmp->height == -1) - { - gdFree (wbmp); - return (-1); + wbmp->height = getmbi(getin, in); + if (wbmp->height == -1) { + gdFree(wbmp); + return -1; } #ifdef __DEBUG - printf ("W: %d, H: %d\n", wbmp->width, wbmp->height); + printf("W: %d, H: %d\n", wbmp->width, wbmp->height); #endif - if (overflow2(sizeof (int), wbmp->width) || - overflow2(sizeof (int) * wbmp->width, wbmp->height)) - { - gdFree(wbmp); - return (-1); + if (overflow2(sizeof(int), wbmp->width) || overflow2(sizeof(int) * wbmp->width, wbmp->height)) { + gdFree(wbmp); + return -1; } - if ((wbmp->bitmap = (int *) safe_emalloc((size_t)wbmp->width * wbmp->height, sizeof(int), 0)) == NULL) - { - gdFree (wbmp); - return (-1); + if ((wbmp->bitmap = (int *)gdMalloc(sizeof(int) * wbmp->width * wbmp->height)) == NULL) { + gdFree(wbmp); + return -1; } #ifdef __DEBUG - printf ("DATA CONSTRUCTED\n"); + printf("DATA CONSTRUCTED\n"); #endif - pos = 0; - for (row = 0; row < wbmp->height; row++) - { - for (col = 0; col < wbmp->width;) - { - byte = getin (in); - - for (pel = 7; pel >= 0; pel--) - { - if (col++ < wbmp->width) - { - if (byte & 1 << pel) - { - wbmp->bitmap[pos] = WBMP_WHITE; - } - else - { - wbmp->bitmap[pos] = WBMP_BLACK; - } - pos++; - } - } - } + pos = 0; + for (row = 0; row < wbmp->height; row++) { + for (col = 0; col < wbmp->width;) { + byte = getin(in); + + for (pel = 7; pel >= 0; pel--) { + if (col++ < wbmp->width) { + if (byte & 1 << pel) { + wbmp->bitmap[pos] = WBMP_WHITE; + } else { + wbmp->bitmap[pos] = WBMP_BLACK; + } + pos++; + } + } + } } - *return_wbmp = wbmp; + *return_wbmp = wbmp; - return (0); + return 0; } - /* writewbmp - ** --------- - ** Write a wbmp to a file descriptor - ** - ** Why not just giving a filedescriptor to this function? - ** Well, the incentive to write this function was the complete - ** integration in gd library from www.boutell.com. They use - ** their own io functions, so the passing of a function seemed to be - ** a logic(?) decision ... - ** + * --------- + * Write a wbmp to a file descriptor + * + * Why not just giving a filedescriptor to this function? + * Well, the incentive to write this function was the complete + * integration in gd library from www.libgd.org. They use + * their own io functions, so the passing of a function seemed to be + * a logic(?) decision ... */ -int -writewbmp (Wbmp * wbmp, void (*putout) (int c, void *out), void *out) +int writewbmp(Wbmp *wbmp, void (*putout)(int c, void *out), void *out) { - int row, col; - int bitpos, octet; - - /* Generate the header */ - putout (0, out); /* WBMP Type 0: B/W, Uncompressed bitmap */ - putout (0, out); /* FixHeaderField */ - - - - /* Size of the image */ - putmbi (wbmp->width, putout, out); /* width */ - putmbi (wbmp->height, putout, out); /* height */ - - - /* Image data */ - for (row = 0; row < wbmp->height; row++) - { - bitpos = 8; - octet = 0; - for (col = 0; col < wbmp->width; col++) - { - octet |= ((wbmp->bitmap[row * wbmp->width + col] == 1) ? WBMP_WHITE : WBMP_BLACK) << --bitpos; - if (bitpos == 0) - { - bitpos = 8; - putout (octet, out); - octet = 0; - } - } - if (bitpos != 8) - putout (octet, out); - + int row, col; + int bitpos, octet; + + /* Generate the header */ + putout(0, out); /* WBMP Type 0: B/W, Uncompressed bitmap */ + putout(0, out); /* FixHeaderField */ + + /* Size of the image */ + putmbi(wbmp->width, putout, out); /* width */ + putmbi(wbmp->height, putout, out); /* height */ + + /* Image data */ + for (row = 0; row < wbmp->height; row++) { + bitpos = 8; + octet = 0; + + for (col = 0; col < wbmp->width; col++) { + octet |= ((wbmp->bitmap[row * wbmp->width + col] == 1) ? WBMP_WHITE : WBMP_BLACK) + << --bitpos; + if (bitpos == 0) { + bitpos = 8; + putout(octet, out); + octet = 0; + } + } + + if (bitpos != 8) { + putout(octet, out); + } } - return (0); + return 0; } - /* freewbmp - ** -------- - ** gdFrees up memory occupied by a WBMP structure - ** + * -------- + * gdFrees up memory occupied by a WBMP structure */ -void -freewbmp (Wbmp * wbmp) +void freewbmp(Wbmp *wbmp) { - gdFree (wbmp->bitmap); - gdFree (wbmp); + gdFree(wbmp->bitmap); + gdFree(wbmp); } - /* printwbmp - ** --------- - ** print a WBMP to stdout for visualisation - ** + * --------- + * print a WBMP to stdout for visualisation */ -void -printwbmp (Wbmp * wbmp) +void printwbmp(Wbmp *wbmp) { - int row, col; - for (row = 0; row < wbmp->height; row++) - { - for (col = 0; col < wbmp->width; col++) - { - if (wbmp->bitmap[wbmp->width * row + col] == WBMP_BLACK) - { - putchar ('#'); - } - else - { - putchar (' '); - } - } - putchar ('\n'); + int row, col; + for (row = 0; row < wbmp->height; row++) { + for (col = 0; col < wbmp->width; col++) { + if (wbmp->bitmap[wbmp->width * row + col] == WBMP_BLACK) { + putchar('#'); + } else { + putchar(' '); + } + } + putchar('\n'); } } #ifdef __TEST /* putout to file descriptor - ** ------------------------- + * ------------------------- */ -int -putout (int c, void *out) -{ - return (putc (c, (FILE *) out)); -} +int putout(int c, void *out) { return (putc(c, (FILE *)out)); } /* getin from file descriptor - ** -------------------------- + * -------------------------- */ -int -getin (void *in) -{ - return (getc ((FILE *) in)); -} - +int getin(void *in) { return (getc((FILE *)in)); } /* Main function - ** ------------- - ** + * ------------- */ -int -main (int argc, char *argv[]) +int main(int argc, char *argv[]) { - FILE *wbmp_file; - Wbmp *wbmp; - - wbmp_file = fopen (argv[1], "rb"); - if (wbmp_file) - { - readwbmp (&getin, wbmp_file, &wbmp); + FILE *wbmp_file; + Wbmp *wbmp; + wbmp_file = fopen(argv[1], "rb"); + if (wbmp_file) { + readwbmp(&getin, wbmp_file, &wbmp); #ifdef __VIEW - #ifdef __DEBUG - printf ("\nVIEWING IMAGE\n"); + printf("\nVIEWING IMAGE\n"); #endif - - printwbmp (wbmp); + printwbmp(wbmp); #endif - #ifdef __WRITE - #ifdef __DEBUG - printf ("\nDUMPING WBMP to STDOUT\n"); + printf("\nDUMPING WBMP to STDOUT\n"); #endif - - writewbmp (wbmp, &putout, stdout); + writewbmp(wbmp, &putout, stdout); #endif - - freewbmp (wbmp); - fclose (wbmp_file); + freewbmp(wbmp); + fclose(wbmp_file); } } #endif diff --git a/ext/gd/libgd/wbmp.h b/ext/gd/libgd/wbmp.h index 89b64c7d1c0d..496a7e2be7b6 100644 --- a/ext/gd/libgd/wbmp.h +++ b/ext/gd/libgd/wbmp.h @@ -1,16 +1,16 @@ /* WBMP -** ---- -** WBMP Level 0: B/W, Uncompressed -** This implements the WBMP format as specified in WAPSpec 1.1 and 1.2. -** It does not support ExtHeaders as defined in the spec. The spec states -** that a WAP client does not need to implement ExtHeaders. -** -** (c) 2000 Johan Van den Brande -** -** Header file + * ---- + * WBMP Level 0: B/W, Uncompressed + * This implements the WBMP format as specified in WAPSpec 1.1 and 1.2. + * It does not support ExtHeaders as defined in the spec. The spec states + * that a WAP client does not need to implement ExtHeaders. + * + * (c) 2000 Johan Van den Brande + * + * Header file */ #ifndef __WBMP_H -#define __WBMP_H 1 +#define __WBMP_H 1 #ifdef HAVE_CONFIG_H #include "config.h" @@ -19,34 +19,30 @@ #include "php_compat.h" /* WBMP struct -** ----------- -** A Wireless bitmap structure -** + * ----------- + * A Wireless bitmap structure */ -typedef struct Wbmp_ -{ - int type; /* type of the wbmp */ - int width; /* width of the image */ - int height; /* height of the image */ - int *bitmap; /* pointer to data: 0 = WHITE , 1 = BLACK */ +typedef struct Wbmp_ { + int type; /* type of the wbmp */ + int width; /* width of the image */ + int height; /* height of the image */ + int *bitmap; /* pointer to data: 0 = WHITE , 1 = BLACK */ } Wbmp; -#define WBMP_WHITE 1 -#define WBMP_BLACK 0 - +#define WBMP_WHITE 1 +#define WBMP_BLACK 0 /* Proto's -** ------- -** + * ------- */ -void putmbi( int i, void (*putout)(int c, void *out), void *out); -int getmbi ( int (*getin)(void *in), void *in ); -int skipheader( int (*getin)(void *in), void *in ); -Wbmp *createwbmp( int width, int height, int color ); -int readwbmp( int (*getin)(void *in), void *in, Wbmp **wbmp ); -int writewbmp( Wbmp *wbmp, void (*putout)( int c, void *out), void *out); -void freewbmp( Wbmp *wbmp ); -void printwbmp( Wbmp *wbmp ); +void putmbi(int i, void (*putout)(int c, void *out), void *out); +int getmbi(int (*getin)(void *in), void *in); +int skipheader(int (*getin)(void *in), void *in); +Wbmp *createwbmp(int width, int height, int color); +int readwbmp(int (*getin)(void *in), void *in, Wbmp **wbmp); +int writewbmp(Wbmp *wbmp, void (*putout)(int c, void *out), void *out); +void freewbmp(Wbmp *wbmp); +void printwbmp(Wbmp *wbmp); #endif diff --git a/ext/gd/libgd/webpng.c b/ext/gd/libgd/webpng.c index c01dd82f23d5..e10d6d9ed9be 100644 --- a/ext/gd/libgd/webpng.c +++ b/ext/gd/libgd/webpng.c @@ -3,287 +3,217 @@ /* Bring in standard I/O and string manipulation functions */ #include -#include /* for atoi() */ +#include /* for atoi() */ #include #ifdef _WIN32 #include -int -getpid () -{ - return _getpid (); -} +int getpid() { return _getpid(); } #else -#include /* for getpid(), unlink() */ +#include /* for getpid(), unlink() */ #endif -int -main (int argc, char **argv) +int main(int argc, char **argv) { - FILE *in; - FILE *out; - char outFn[20]; - int useStdinStdout = 0; + FILE *in; + FILE *out; + char outFn[20]; + int useStdinStdout = 0; - /* Declare our image pointer */ - gdImagePtr im = 0; - int i; - /* We'll clear 'no' once we know the user has made a - reasonable request. */ - int no = 1; - /* We'll set 'write' once we know the user's request - requires that the image be written back to disk. */ - int write = 0; - /* C programs always get at least one argument; we want at - least one more (the image), more in practice. */ - if (argc < 2 || !strcmp (argv[1], "--help")) - { - no = 1; - goto usage; + /* Declare our image pointer */ + gdImagePtr im = 0; + int i; + /* We'll clear 'no' once we know the user has made a + reasonable request. */ + int no = 1; + /* We'll set 'write' once we know the user's request + requires that the image be written back to disk. */ + int write = 0; + /* C programs always get at least one argument; we want at + least one more (the image), more in practice. */ + if (argc < 2 || !strcmp(argv[1], "--help")) { + no = 1; + goto usage; } - /* The last argument should be the image. Open the file. */ - if (strcmp ("-", argv[argc - 1]) == 0) - { /* - is synonymous with STDIN */ - useStdinStdout = 1; - in = stdin; + /* The last argument should be the image. Open the file. */ + if (strcmp("-", argv[argc - 1]) == 0) { /* - is synonymous with STDIN */ + useStdinStdout = 1; + in = stdin; + } else { + in = fopen(argv[argc - 1], "rb"); } - else - { - in = fopen (argv[argc - 1], "rb"); + if (!in) { + fprintf(stderr, "Error: can't open file %s.\n", argv[argc - 1]); + exit(1); } - if (!in) - { - fprintf (stderr, - "Error: can't open file %s.\n", argv[argc - 1]); - exit (1); + /* Now load the image. */ + im = gdImageCreateFromPng(in); + fclose(in); + /* If the load failed, it must not be a PNG file. */ + if (!im) { + fprintf(stderr, "Error: %s is not a valid PNG file.\n", argv[argc - 1]); + exit(1); } - /* Now load the image. */ - im = gdImageCreateFromPng (in); - fclose (in); - /* If the load failed, it must not be a PNG file. */ - if (!im) - { - fprintf (stderr, - "Error: %s is not a valid PNG file.\n", argv[argc - 1]); - exit (1); - } - /* Consider each argument in turn. */ - for (i = 1; (i < (argc - 1)); i++) - { - /* -i turns on and off interlacing. */ - if (!strcmp (argv[i], "--help")) - { - /* Every program should use this for help! :) */ - no = 1; - goto usage; - } - else if (!strcmp (argv[i], "-i")) - { - if (i == (argc - 2)) - { - fprintf (stderr, - "Error: -i specified without y or n.\n"); - no = 1; - goto usage; - } - if (!strcmp (argv[i + 1], "y")) - { - /* Set interlace. */ - gdImageInterlace (im, 1); - } - else if (!strcmp (argv[i + 1], "n")) - { - /* Clear interlace. */ - gdImageInterlace (im, 0); - } - else - { - fprintf (stderr, - "Error: -i specified without y or n.\n"); - no = 1; - goto usage; - } - i++; - no = 0; - write = 1; - } - else if (!strcmp (argv[i], "-t")) - { - /* Set transparent index (or none). */ - int index; - if (i == (argc - 2)) - { - fprintf (stderr, - "Error: -t specified without a color table index.\n"); - no = 1; - goto usage; - } - if (!strcmp (argv[i + 1], "none")) - { - /* -1 means not transparent. */ - gdImageColorTransparent (im, -1); - } - else - { - /* OK, get an integer and set the index. */ - index = atoi (argv[i + 1]); - gdImageColorTransparent (im, index); - } - i++; - write = 1; - no = 0; - } - else if (!strcmp (argv[i], "-l")) - { - /* List the colors in the color table. */ - int j; - if (!im->trueColor) - { - /* Tabs used below. */ - printf ("Index Red Green Blue Alpha\n"); - for (j = 0; (j < gdImageColorsTotal (im)); j++) - { - /* Use access macros to learn colors. */ - printf ("%d %d %d %d %d\n", - j, - gdImageRed (im, j), - gdImageGreen (im, j), - gdImageBlue (im, j), - gdImageAlpha (im, j)); - } - } - else - { - printf ("Truecolor image, no palette entries to list.\n"); - } - no = 0; - } - else if (!strcmp (argv[i], "-d")) - { - /* Output dimensions, etc. */ - int t; - printf ("Width: %d Height: %d Colors: %d\n", - gdImageSX (im), gdImageSY (im), - gdImageColorsTotal (im)); - t = gdImageGetTransparent (im); - if (t != (-1)) - { - printf ("First 100%% transparent index: %d\n", t); - } - else - { - /* -1 means the image is not transparent. */ - printf ("First 100%% transparent index: none\n"); - } - if (gdImageGetInterlaced (im)) - { - printf ("Interlaced: yes\n"); - } - else - { - printf ("Interlaced: no\n"); - } - no = 0; - } - else if (!strcmp(argv[i], "-a")) - { - int maxx, maxy, x, y, alpha, pix, nalpha = 0; - - maxx = gdImageSX(im); - maxy = gdImageSY(im); + /* Consider each argument in turn. */ + for (i = 1; (i < (argc - 1)); i++) { + /* -i turns on and off interlacing. */ + if (!strcmp(argv[i], "--help")) { + /* Every program should use this for help! :) */ + no = 1; + goto usage; + } else if (!strcmp(argv[i], "-i")) { + if (i == (argc - 2)) { + fprintf(stderr, "Error: -i specified without y or n.\n"); + no = 1; + goto usage; + } + if (!strcmp(argv[i + 1], "y")) { + /* Set interlace. */ + gdImageInterlace(im, 1); + } else if (!strcmp(argv[i + 1], "n")) { + /* Clear interlace. */ + gdImageInterlace(im, 0); + } else { + fprintf(stderr, "Error: -i specified without y or n.\n"); + no = 1; + goto usage; + } + i++; + no = 0; + write = 1; + } else if (!strcmp(argv[i], "-t")) { + /* Set transparent index (or none). */ + int index; + if (i == (argc - 2)) { + fprintf(stderr, "Error: -t specified without a color table index.\n"); + no = 1; + goto usage; + } + if (!strcmp(argv[i + 1], "none")) { + /* -1 means not transparent. */ + gdImageColorTransparent(im, -1); + } else { + /* OK, get an integer and set the index. */ + index = atoi(argv[i + 1]); + gdImageColorTransparent(im, index); + } + i++; + write = 1; + no = 0; + } else if (!strcmp(argv[i], "-l")) { + /* List the colors in the color table. */ + int j; + if (!im->trueColor) { + /* Tabs used below. */ + printf("Index Red Green Blue Alpha\n"); + for (j = 0; (j < gdImageColorsTotal(im)); j++) { + /* Use access macros to learn colors. */ + printf("%d %d %d %d %d\n", j, gdImageRed(im, j), + gdImageGreen(im, j), gdImageBlue(im, j), gdImageAlpha(im, j)); + } + } else { + printf("Truecolor image, no palette entries to list.\n"); + } + no = 0; + } else if (!strcmp(argv[i], "-d")) { + /* Output dimensions, etc. */ + int t; + printf("Width: %d Height: %d Colors: %d\n", gdImageSX(im), gdImageSY(im), + gdImageColorsTotal(im)); + t = gdImageGetTransparent(im); + if (t != (-1)) { + printf("First 100%% transparent index: %d\n", t); + } else { + /* -1 means the image is not transparent. */ + printf("First 100%% transparent index: none\n"); + } + if (gdImageGetInterlaced(im)) { + printf("Interlaced: yes\n"); + } else { + printf("Interlaced: no\n"); + } + no = 0; + } else if (!strcmp(argv[i], "-a")) { + int maxx, maxy, x, y, alpha, pix, nalpha = 0; - printf("alpha channel information:\n"); + maxx = gdImageSX(im); + maxy = gdImageSY(im); - if (im->trueColor) { - for (y = 0; y < maxy; y++) { - for (x = 0; x < maxx; x++) { - pix = gdImageGetPixel(im, x, y); - alpha = gdTrueColorGetAlpha(pix); + printf("alpha channel information:\n"); - if (alpha > gdAlphaOpaque) { - /* Use access macros to learn colors. */ - printf ("%d %d %d %d\n", - gdTrueColorGetRed(pix), - gdTrueColorGetGreen(pix), - gdTrueColorGetBlue(pix), - alpha); - nalpha++; - } + if (im->trueColor) { + for (y = 0; y < maxy; y++) { + for (x = 0; x < maxx; x++) { + pix = gdImageGetPixel(im, x, y); + alpha = gdTrueColorGetAlpha(pix); - } - } - } - else - printf("NOT a true color image\n"); - no = 0; - printf("%d alpha channels\n", nalpha); + if (alpha > gdAlphaOpaque) { + /* Use access macros to learn colors. */ + printf("%d %d %d %d\n", gdTrueColorGetRed(pix), + gdTrueColorGetGreen(pix), gdTrueColorGetBlue(pix), alpha); + nalpha++; + } + } + } + } else + printf("NOT a true color image\n"); + no = 0; + printf("%d alpha channels\n", nalpha); - } - else - { - fprintf (stderr, "Unknown argument: %s\n", argv[i]); - break; - } + } else { + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + break; + } } usage: - if (no) - { - /* If the command failed, output an explanation. */ - fprintf (stderr, - "Usage: webpng [-i y|n ] [-l] [-t index|none ] [-d] pngname.png\n" + if (no) { + /* If the command failed, output an explanation. */ + fprintf( + stderr, + "Usage: webpng [-i y|n ] [-l] [-t index|none ] [-d] pngname.png\n" - " -i [y|n] Turns on/off interlace\n" - " -l Prints the table of color indexes\n" - " -t [index] Set the transparent color to the specified index (0-255 or \"none\")\n" - " -d Reports the dimensions and other characteristics of the image.\n" - " -a Prints all alpha channels that are not 100%% opaque.\n" - "\n" - "If you specify '-' as the input file, stdin/stdout will be used input/output.\n" - ); + " -i [y|n] Turns on/off interlace\n" + " -l Prints the table of color indexes\n" + " -t [index] Set the transparent color to the specified index (0-255 or \"none\")\n" + " -d Reports the dimensions and other characteristics of the image.\n" + " -a Prints all alpha channels that are not 100%% opaque.\n" + "\n" + "If you specify '-' as the input file, stdin/stdout will be used input/output.\n"); } - if (write) - { - if (useStdinStdout) - { - out = stdout; - } - else - { - /* Open a temporary file. */ + if (write) { + if (useStdinStdout) { + out = stdout; + } else { + /* Open a temporary file. */ - /* "temp.tmp" is not good temporary filename. */ - snprintf (outFn, sizeof(outFn), "webpng.tmp%d", getpid ()); - out = fopen (outFn, "wb"); + /* "temp.tmp" is not good temporary filename. */ + snprintf(outFn, sizeof(outFn), "webpng.tmp%d", getpid()); + out = fopen(outFn, "wb"); - if (!out) - { - fprintf (stderr, - "Unable to write to %s -- exiting\n", outFn); - exit (1); - } - } + if (!out) { + fprintf(stderr, "Unable to write to %s -- exiting\n", outFn); + exit(1); + } + } - /* Write the new PNG. */ - gdImagePng (im, out); + /* Write the new PNG. */ + gdImagePng(im, out); - if (!useStdinStdout) - { - fclose (out); - /* Erase the old PNG. */ - unlink (argv[argc - 1]); - /* Rename the new to the old. */ - if (rename (outFn, argv[argc - 1]) != 0) - { - perror ("rename"); - exit (1); - } - } + if (!useStdinStdout) { + fclose(out); + /* Erase the old PNG. */ + unlink(argv[argc - 1]); + /* Rename the new to the old. */ + if (rename(outFn, argv[argc - 1]) != 0) { + perror("rename"); + exit(1); + } + } } - /* Delete the image from memory. */ - if (im) - { - gdImageDestroy (im); + /* Delete the image from memory. */ + if (im) { + gdImageDestroy(im); } - /* All's well that ends well. */ - return 0; + /* All's well that ends well. */ + return 0; } diff --git a/ext/gd/tests/bug43073.phpt b/ext/gd/tests/bug43073.phpt index 80c6480fea57..9867eb98a268 100644 --- a/ext/gd/tests/bug43073.phpt +++ b/ext/gd/tests/bug43073.phpt @@ -13,21 +13,21 @@ gd = $exp[$i][$j] - 1 && $bbox[$j] <= $exp[$i][$j] + 1) { echo '.'; } else { - echo "(expected $exp[$i][$j], got $bbox[$j])"; + echo "(expected {$exp[$i][$j]}, got {$bbox[$j]})"; } } echo "\n"; diff --git a/ext/gd/tests/bug48732-mb.phpt b/ext/gd/tests/bug48732-mb.phpt index 361c1ac4d88f..e57a913de007 100644 --- a/ext/gd/tests/bug48732-mb.phpt +++ b/ext/gd/tests/bug48732-mb.phpt @@ -22,6 +22,7 @@ imagepng($g, "$cwd/bug48732ç§ã¯ã‚¬ãƒ©ã‚¹ã‚’食ã¹ã‚‰ã‚Œã¾ã™.png"); echo 'Left Bottom: (' . $bbox[0] . ', ' . $bbox[1] . ')'; ?> --CLEAN-- - + --EXPECT-- -Left Bottom: (0, 46) +Left Bottom: (0, 40) diff --git a/ext/gd/tests/bug48732.phpt b/ext/gd/tests/bug48732.phpt index ed73954de333..7a3775776d64 100644 --- a/ext/gd/tests/bug48732.phpt +++ b/ext/gd/tests/bug48732.phpt @@ -22,6 +22,7 @@ imagepng($g, "$cwd/bug48732.png"); echo 'Left Bottom: (' . $bbox[0] . ', ' . $bbox[1] . ')'; ?> --CLEAN-- - + --EXPECT-- -Left Bottom: (0, 46) +Left Bottom: (0, 40) diff --git a/ext/gd/tests/bug48801-mb.phpt b/ext/gd/tests/bug48801-mb.phpt index 247161518b19..f07361694a2e 100644 --- a/ext/gd/tests/bug48801-mb.phpt +++ b/ext/gd/tests/bug48801-mb.phpt @@ -18,7 +18,7 @@ echo '(' . $bbox[4] . ', ' . $bbox[5] . ")\n"; echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n"; ?> --EXPECTREGEX-- -\(4, 15\) -\(16[0-1], 15\) -\(16[0-1], -4[7-8]\) -\(4, -4[7-8]\) +\(4, 1[4-5]\) +\(16[0-1], 1[4-5]\) +\(16[0-1], -4[6-8]\) +\(4, -4[6-8]\) diff --git a/ext/gd/tests/bug48801.phpt b/ext/gd/tests/bug48801.phpt index 361bf59a65b9..c9ca3c16c1fa 100644 --- a/ext/gd/tests/bug48801.phpt +++ b/ext/gd/tests/bug48801.phpt @@ -17,7 +17,7 @@ echo '(' . $bbox[4] . ', ' . $bbox[5] . ")\n"; echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n"; ?> --EXPECTREGEX-- -\(4, 15\) -\(16[0-1], 15\) -\(16[0-1], -4[7-8]\) -\(4, -4[7-8]\) +\(4, 14\) +\(16[0-1], 14\) +\(16[0-1], -4[6-8]\) +\(4, -4[5-8]\) diff --git a/ext/gd/tests/bug53504.phpt b/ext/gd/tests/bug53504.phpt index 5bd81f35aa0c..fb96ac3c8f2a 100644 --- a/ext/gd/tests/bug53504.phpt +++ b/ext/gd/tests/bug53504.phpt @@ -22,35 +22,35 @@ $blue = imagecolorallocate($g, 0, 0, 255); $tests = [ // Kerning examples (unfortunately not available in "Tuffy" test font): - ['fontSize' => 50, 'angle' => 0, 'x' => 20, 'y' => 70, 'text' => 'AV Teg', 'exp' => [2,15, 208,15, 208,-48, 2,-48]], - ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 350, 'text' => 'AV Teg', 'exp' => [15,-1, 15,-208, -48,-208, -48,-2]], - ['fontSize' => 50, 'angle' => 40, 'x' => 130, 'y' => 280, 'text' => 'AV Teg', 'exp' => [11,11, 169,-122, 129,-171, -30,-39]], + ['fontSize' => 50, 'angle' => 0, 'x' => 20, 'y' => 70, 'text' => 'AV Teg', 'exp' => [2,14, 209,14, 209,-47, 2,-47]], + ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 350, 'text' => 'AV Teg', 'exp' => [14,-2, 14,-209, -47,-209, -47,-2]], + ['fontSize' => 50, 'angle' => 40, 'x' => 130, 'y' => 280, 'text' => 'AV Teg', 'exp' => [11,9, 169,-123, 129,-170, -28,-37]], // Shift-Test: - ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 110, 'text' => 'H-Shift', 'exp' => [8,2, 386,2, 386,-97, 8,-97]], + ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 110, 'text' => 'H-Shift', 'exp' => [8,1, 393,1, 393,-97, 8,-97]], // Small/single chars: - ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 220, 'text' => '-', 'exp' => [7,-37, 51,-37, 51,-46, 7,-46]], - ['fontSize' => 100, 'angle' => 0, 'x' => 430, 'y' => 220, 'text' => ',', 'exp' => [7,15, 21,15, 21,-13, 7,-13]], - ['fontSize' => 100, 'angle' => 0, 'x' => 510, 'y' => 220, 'text' => '.', 'exp' => [7,1, 21,1, 21,-13, 7,-13]], - ['fontSize' => 100, 'angle' => 0, 'x' => 590, 'y' => 220, 'text' => '|', 'exp' => [8,0, 17,0, 17,-95, 8,-95]], - ['fontSize' => 100, 'angle' => 0, 'x' => 670, 'y' => 220, 'text' => 'g', 'exp' => [5,29, 60,29, 60,-72, 5,-72]], + ['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 220, 'text' => '-', 'exp' => [7,-36, 56,-36, 56,-46, 7,-46]], + ['fontSize' => 100, 'angle' => 0, 'x' => 430, 'y' => 220, 'text' => ',', 'exp' => [6,14, 25,14, 25,-13, 6,-13]], + ['fontSize' => 100, 'angle' => 0, 'x' => 510, 'y' => 220, 'text' => '.', 'exp' => [6,0, 26,0, 26,-13, 6,-13]], + ['fontSize' => 100, 'angle' => 0, 'x' => 590, 'y' => 220, 'text' => '|', 'exp' => [8,0, 23,0, 23,-95, 8,-95]], + ['fontSize' => 100, 'angle' => 0, 'x' => 670, 'y' => 220, 'text' => 'g', 'exp' => [4,29, 66,29, 66,-71, 4,-71]], // Multi-Line + rotation: - ['fontSize' => 30, 'angle' => 0, 'x' => 20, 'y' => 400, 'text' => "Multi\nLine\nTest", 'exp' => [2,107, 80,107, 80,-29, 2,-29]], - ['fontSize' => 30, 'angle' => 40, 'x' => 150, 'y' => 420, 'text' => "Multi\nLine\nTest", 'exp' => [70,81, 131,31, 43,-74, -18,-24]], - ['fontSize' => 30, 'angle' => 90, 'x' => 250, 'y' => 340, 'text' => "Multi\nLine\nTest", 'exp' => [107,-1, 107,-80, -29,-80, -29,-2]], + ['fontSize' => 30, 'angle' => 0, 'x' => 20, 'y' => 400, 'text' => "Multi\nLine\nTest", 'exp' => [1,84, 81,84, 81,-28, 1,-28]], + ['fontSize' => 30, 'angle' => 40, 'x' => 150, 'y' => 420, 'text' => "Multi\nLine\nTest", 'exp' => [55,63, 116,12, 43,-74, -17,-22]], + ['fontSize' => 30, 'angle' => 90, 'x' => 250, 'y' => 340, 'text' => "Multi\nLine\nTest", 'exp' => [84,-1, 84,-81, -28,-81, -28,-1]], // Some edge case glyphs: - ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 750, 'text' => "iiiiiiiiiiii", 'exp' => [0,-4, 0,-165, -47,-165, -47,-4]], - ['fontSize' => 50, 'angle' => 90, 'x' => 150, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [-19,-2, -18,-167, -29,-167, -29,-2]], - ['fontSize' => 50, 'angle' => 50, 'x' => 210, 'y' => 750, 'text' => "iiiiiiiiiiii", 'exp' => [3,-3, 107,-127, 70,-157, -34,-33]], - ['fontSize' => 50, 'angle' => 50, 'x' => 300, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [-13,-13, 93,-141, 85,-147, -21,-20]], - ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 650, 'text' => "iiiiiiiiiiii", 'exp' => [4,0, 165,0, 165,-47, 4,-47]], - ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [2,-19, 167,-19, 167,-29, 2,-29]], + ['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 750, 'text' => "iiiiiiiiiiii", 'exp' => [0,-4, 0,-165, -46,-165, -46,-4]], + ['fontSize' => 50, 'angle' => 90, 'x' => 150, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [-18,-1, -18,-168, -27,-168, -27,-1]], + ['fontSize' => 50, 'angle' => 50, 'x' => 210, 'y' => 750, 'text' => "iiiiiiiiiiii", 'exp' => [2,-3, 108,-129, 73,-159, -32,-33]], + ['fontSize' => 50, 'angle' => 50, 'x' => 300, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [-12,-13, 94,-140, 86,-146, -20,-19]], + ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 650, 'text' => "iiiiiiiiiiii", 'exp' => [4,0, 165,0, 165,-46, 4,-46]], + ['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 750, 'text' => "~~~~~~~", 'exp' => [1,-18, 168,-18, 168,-27, 1,-27]], // "Big" test: - ['fontSize' => 200, 'angle' => 0, 'x' => 400, 'y' => 500, 'text' => "Big", 'exp' => [16,59, 329,59, 329,-190, 16,-190]], + ['fontSize' => 200, 'angle' => 0, 'x' => 400, 'y' => 500, 'text' => "Big", 'exp' => [15,58, 342,58, 342,-189, 15,-189]], ]; foreach ($tests as $testnum => $test) { @@ -71,8 +71,8 @@ foreach ($tests as $testnum => $test) { // check if both bboxes match when adding x/y offset: for ($i = 0; $i < count($bbox); $i += 2) { - if ($bbox[$i] + $test['x'] !== $bboxDrawn[$i]) echo "imageftbbox and imagefttext differ!\n"; - if ($bbox[$i + 1] + $test['y'] !== $bboxDrawn[$i + 1]) echo "imageftbbox and imagefttext differ!\n"; + if (abs($bbox[$i] + $test['x'] - $bboxDrawn[$i]) > 1) echo "imageftbbox and imagefttext differ!\n"; + if (abs($bbox[$i + 1] + $test['y'] - $bboxDrawn[$i + 1]) > 1) echo "imageftbbox and imagefttext differ!\n"; } // draw bounding box: diff --git a/ext/gd/tests/bug65148.phpt b/ext/gd/tests/bug65148.phpt index 0d71655aad10..77f4a384f186 100644 --- a/ext/gd/tests/bug65148.phpt +++ b/ext/gd/tests/bug65148.phpt @@ -46,128 +46,128 @@ Array ( [IMG_BELL] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BESSEL] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BICUBIC] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BICUBIC_FIXED] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BILINEAR_FIXED] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BLACKMAN] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BOX] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_BSPLINE] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_CATMULLROM] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_GAUSSIAN] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_GENERALIZED_CUBIC] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_HERMITE] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_HAMMING] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_HANNING] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_MITCHELL] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_POWER] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_QUADRATIC] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_SINC] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_NEAREST_NEIGHBOUR] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_WEIGHTED4] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) [IMG_TRIANGLE] => Array ( - [x] => 40 - [y] => 23 + [x] => 42 + [y] => 24 ) ) diff --git a/ext/gd/tests/bug73272.png b/ext/gd/tests/bug73272.png index 514e012a92983a86dd87692666e16ead7d3b8cd3..6de8ac4715bd092d88b96275500a155b36243803 100644 GIT binary patch literal 749 zcmeAS@N?(olHy`uVBq!ia0vp^T|nH)!3HEh3okeYq&N#aB8wRq_>O=u<5X=vX$A(S z`JOJ0Ar*7p-o5CzI6$QB;@OmY|EJDeJy~JpM|tiq8zNsH+j30oX|cSHrrzsa>sal7 ze16IMK(58fMZ<;8L{RAIO?&q2-T(ijoa8C9mek0s*z5X%J+~h(E?6~f`Cs-88#n)d ze)}H#rw#Ivp`m~OeqoDwYTn56dwu2ejqg_db&>Qw5!oO6wI|`0-xTe#N5V{!QSxWv zHY#st-#vHh9F256^XkWyyY!vkI_)%?w02!1&)RA8{9QNI{#$oCV0Nd`VZK*3p;5k5 zBFnDuT$Eeusxd#VDDI`0cXvRVar72<0WQIXb5iQve!a_+Kh<+wUG}b>$U4O-lS?D( zT8cL5oqqJz`Q;|%wDR8pX>xDpD($P)Fn(P!X~S91gOW2fT&#Sj+-^7eo&WaD?diK} zH8#Kc)>Q~JSmym)rIlGir+U(QzRT)5y)0W+=BDc;nbx`V_D9dq#R0qC&bYaSGxf)p zke3#|t7f%6vbBAl`CaJWw*%i^67x<)K6cCc-(UUfE>*p}`TO0X+S}*r%kKv>OUjuB{@A;% m{%e8GLIFXbFX>54tiN<>pX4uw@5_Pdm%-E3&t;ucLK6U%wlDqw literal 741 zcmeAS@N?(olHy`uVBq!ia0vp^?LgeY!3HGHZv1hdfq`j_r;B4q#hkZy@8-=85NN&V zeD!;MYL#pEgz~vZ*yc-K+g1ETo@G9JQ|g{IuXpiPJh;AGyrHtQ*+a(Vk%AEWM2n6i zjw*agg#xq_DtT=&Ew!@Z_mA(pfBk*Qy5mye;`Ohsrlm^WelPeoKmWV!z2|<@cK->w zT$fz`-Ej8oHLLdD^6#7PveDA++0^H&R=j;;Q5;?{-Q$*TpEjS`ZugCu;>RVf=j^fe z=?^Q6`5Axoo7G(9Ou^&o<^jhw%yRvjYO9oWPhWcXB=YA;!_%CrB&R*Rd&{R!vShZ* z(#JDWv)+_)E%}p|s8jj~YFFCDzdJTR0 zf=gz~0L}S(*YfwLTGyKvX;)vK$$e>&CUZCTki_B&8RJ=oH|A@}x-a*4NDr~bS)F`$yl*UtTRT+l+ll{}P9|of&uL zZs|+@yq|l&tiJBfdY|vi=hAP*8T4J>YWwU#DZP?`3g068dvYhFRx>{YC1M6oS3j3^ HP6 --EXPECT-- -size: 247*247 -size: 237*237 -size: 229*229 -size: 175*175 +size: 255*255 +size: 253*253 +size: 253*253 +size: 225*225 diff --git a/ext/gd/tests/createfromwbmp2_extern.phpt b/ext/gd/tests/createfromwbmp2_extern.phpt index 5cd178cb59e5..68895f9a3570 100644 --- a/ext/gd/tests/createfromwbmp2_extern.phpt +++ b/ext/gd/tests/createfromwbmp2_extern.phpt @@ -2,10 +2,6 @@ imagecreatefromwbmp with invalid wbmp --EXTENSIONS-- gd ---SKIPIF-- - --FILE-- lGnihhWKkhMv8xTCWi*_-vK)% zJx3xUsw%=?;z(Yq6GTKTM3?1dwcOs!l<29OZNBVVZ{uT+KX91rGRt8`Iq{w4xgV%RjE2Jb9zEG^NnC$1x>>bH9@C z(dVqu!qFtAOjRtT2Sn7i?KHaGlRaDBxJyp%KHT%Wf9Gy>U%ydhUI%RwYg3r@wqaTl z%i(vM_iRT_@J)30ee1lDxOcA#9VOq^MrV~A>qh^|nx(*o|&xgnGJacHj}v907)KH5_TDP}EJqdsB`?(<*8R?l{XRSc~D z>DDW74lRcb}#Kkvz=hoUT48*1!uP^iU`S(?9;nq}V=cff9CHCnmQ>COh zss6-PF6uy%EL{~51_eIa2-?7W^;F&9>bisecEzLjcT1O>HNN*38aC$T3gs42T)2P# zyiMMT#MUR3gC_R&M*=E+TW?p*xM+6&_@?JBvCDmKGd4NO^|=PeT;FKsF>>j*F^P$Z zetv#?U0huL&dl6$ae1lEC9k7%T@@ zX+9qZkBw&f?bw7Dao4pM!<%YUMU>g8%T2QmlaU+_l&7JgvGeeFs+?4DTV&|px*$Wy z2Q3~|H8pN7t_$qcN61J74Kk+nGPZ0So*pTqJa|n*LrzbRK8)#n zLPo~HT>Ylszv;HOw+#&qKWArGY|fRneE&{<{P^*u)zveiqL1}6k~11-atU1O z>TU9It2L}~eW+oyHNv^oopVO?)29y~(#_4x{G2315K3p~NW4g+#TllsfSw-1?Ck8{ zy}i{IIY+1h7S_F9L`DuSdmIR;)Y8)WTv{5c#9nbT&vE(hk=>D@q1bm(dEWlJ37FswjmnHJ{y`l!r=p5e;7J5jV&D=kH)oJx^!tj?s%`kt*@_7ck*PliJ-c+ zcFn)>7H)2C*|c`ybLXy@nVs^z@Sv&b>L=r}(Awd_t*tkqp;XDq$%ligMb4fTl9W`o zvEfguu4cTy#=1K_IhoPgU?8;S*Q~n36I&CbN^!(ois=$tI14hU+LWkn)B!0}Z%$oc zs45vtyQ9Vy{!E4AGDk$6dA8c$Q?#2Ubi&b;_L3nk?$I(U>wQmKee9Iiquc7+Catvh z5+}5B9Xoc6k%@^ds)3uI-@?^3>dhO*#l=NJNIrZZOVX$>FDD)y9c8CJ(A?6Jnw~y_ z1iz#l=#F z{KuJnwuIW-+y5?mygl!!Xlcob{N(%ZzlA=}xvbBhztz_tKw(OGZ_#DxHa5kG6}ZkZ z?c29cJXUqKzfA4MjgwYM)cv+T4P|S-L3A>&%4_F(2K!@Uv*MIYa`n5qyD#$5nu*2` zzDU(dXqyn1l$6WtJVhNSE+bQGE^P1YtiVN6VVY&<HN^FldkV27KA+%)ryA3c*c7tO=86#TBtH~hd<*#Sl2X!&>zdy|}n zIG=B9HLEuB{a?Qhl{yFY7rOzq-j|lX&B_vbqsr;;?;ji%=D5A#TxBXmK|!&!yln5~ z72G*Bx-!#^fT3f}Td)!<)mZtCFh90~Zmp8d*|D?O$6Ph@sB zEG!7!zki>PkB@~msK9$$a(H-{hL(27Ns|7=iNvq*zUEgbcCTgZ$Oi9}Xnb$%-}Kya zXZe=idBxH3%-y?pAIY)4j){qmjaA@?xOU@4Q_yjNx~8UTYtb_@GHQ43aGPe)h1FiX zcke$=PEG`{t*0lrdxnEL@W-!TRgI05%1O_^e^*XUNfC-x85td2m~7|${{6dBl7_Xl zHKA}jr>e*Q+0Xnr`|{<>C$+;OqN2j*&xa@{B~%Q4TkY;&R4W@toU|XT-Mh={6RDc> zZ{wvZJ9XUm`O^`X5t=~xJ>g31>IxhWgG=mtEM>|9S*Zgb#nI;p{abaD;%7V_qF83f zoT@c>wD2-V!A~o_CyHgAc%U3NvzE>o#~FqkBY}GJ=YRg_cXW2PTIGr}QuWw~0sBYB z#dyp7E&>nVnq>0X+J?kdL6s&m4rRZc{cSx^IEdYGM?L%rR zv5AjQTuqH4KR-WiA+*7wq984vq{712xw&T@938oNcq(RGhV8_ESmmZ?W@>8c1hfne z__SJ;iFvFybKlx(K1oO{F#)3=t*zzQzNx7x>}AaQy89kqqLPx5;NakQ>Rh+)-5V|U z+pn9U{WK_uo}NCww3I1Oz5+oPv+Wxh7n{UNdV6~*4<3wfw@S^- z93QLCb!XrC^k1Y1NUy{>Th|=0MPyAWVvXl zr6P6t9&4wKPEJ16;_)vx8JM1?BuZ?pTVI!83s?H~tlk`rvtm$CPfxFE*nVh1TrxKF z0?P%-*c~~_EZtzmMApM(EKv>EW_h+FT0F;EZ=+4HnxG;b4wABKYHroLrhBe8bB$W6 za+dLMne3i5eyzd*=tn0;-$KO5{rAMG)>c>V)6aN`mWIVCCLRI6w6`B~cX!8!B8TyX z;f)(ep1S(_^10PSEgodUp2r%gRdcHm5fOe$?9_s|Y{AX&8(tCbil<=3dmgK%T#Q#o zFGo?Fj#WKM?qAo`vTO8|u0ODx*~ullr@eZ=+FO8#y<{xqp+ki7Fv_&Fwhr#=Gr}4h z8X9^E90Qh@oze5@>F7+XtjG~IPtS8<%rdm(hw*Q6ZtgKO_q8=~H21Y_Z*3i&0>^Qx zw6rvQgZ-svX1egf%aHiQ&C1D19z-5RkBU}L)k1pWk6^S4c#Wu- z7+NPQ4LJeJ+9u3xZNtF4P-Eb4;Y#uf3j2bBg3K)~f#Ha4xwYu2sD`e-zM4Th1bBYk zOY8detel*S;*80trO3!*!0*wdq_w&JakcFo+p7Zo%8ly{k4dFf*yoem&`S|u>=~ZS zMI*-%K@4u7&P4;V!4^)YoTQOI8mWAG(C*dJ-Cz^JCrB=v8A?TQ#+?^IKjxz&o;>^>hf{B`KWW} z=KsqH?ti7>eNCs<)RMEMqvM%!5^!x1{Sj;`DJiM3sR>9LqMBlM=T2Zphc?!D?%X*i zC#QjBkAlL&gpv{hqM(3&Y0(Dx4-J`)jg8qA7~U3Pco`LS=kDE8j8r*gWon=&o12?h z8wH=?FB+u-;f}8MJKu&!F z13i6xxs)~nsQ2_RVqLGAER0IMF5lNY>idyrr!9}q)a#V8`$9zAyw11zMPUM!^7M?U zth`uQR75}(rvEi4w0RycUxF6U(0JH-+Z-7O@Iyh{Snqs7Ub{L~o6p`Mi zPMreDHp%PyAD5p#K`T`HmB6Cc8sr~Vial41-Xojb$3+_?6aKE`$a!#c+1JVHT*!ww zWvLQ9b0v-lA{75X*)dL}LbwtMfapiLrI0Yq5=Tu;bzqCBeBw3qhzst~+fU`$uxqJW z%FjA(3DRhAPwHf@4cor+T-SfGnDbrv@%M(V{6aSBKtiNi#xJsk0~aKuq~ufD!ee3@ zK+P<2=p&V@TU(D*S62hz&_TGkxd#^)p94aBZN^7NND2%?t4#+728jI1FQ}?gfwiHR zCus3VNK4n)t(b3DGAxnk*-YAUP{O_1Z82to^=0y2}#L{;f48~%{c;R z;G#*$%nS<;KSD{m_v+QFJ!L*|uU|8eQ&7+|FeD&NoFtn@M%Z2F`fdRp(8BtBcJEtQ zSUjmVU0PZa6cKp@@d-s4CbRRZuuxKko4AYrj-)7G?+mqd};J zVwy<&WYKc4e{Lm4Xl9To*7xqQhAVy6<--^8+G#>`l+!aa2V{d=#w$&;239vw@)b6D zgo+l9uCmGVp}LsZ`mSx_cYpu!hp*b&G}E{K{KyTHDyLMbrz%G+Eo)CbnB$ zOKZocAOwIXR0|YQN=ga<0lQjMR0K9UEqWq6JUr8|YR%7P5R#dan}(5i!byL?V(k&L zW-85mAQO9}@&S)$DzsFG-JZ3W;GXh*TYQRRjx|kNY{g>h%R!+Hc6MX=VlAup%C3-J zWRG-E*Q)ZN4I1RN+n7swVQgIV7@HP0J_b>o93Sr+7FVnzq@?VR zYpER^WCiER@3lc~0fP=Z^nrVU9ph3zFkTOj*U){ySi7R$rIi&2H@7D(h->M8*;jvhVwtE0nIgdsXQIvxvflY(fA&&!M2-oB6Z6*y1Pf^4I6VCApg zys5It8MG4z0=+BE3fDCo*5NVr;!bMZYGqzH9)DP~sOZa=F9Qp1C^@WI=*$^EUA|;c zc!a>g!2z9es5YaP{ZUNuY(s%*F08&Mch(RFtt5@I5g|W~& zFgR#5Bs{3-!Gj0o_Hhl{22|c8tm*Y@HeTMKj~{tBI5?07uT(hD8$P6`V;8ePS2Z<{ z0ucx_yrZM0qGHd2+Z)h54o=S2+aeI^f)Wx?ndP@dgi(iRyP#4bAqPL*D5`00rb6lv z-U7ejiA+dM4a8e~(f<$qm2r2Yb1SEzL7?Pk*$;vXw0_tB;dbX@mI@7czIs|ah!gR< z*4vLru5)-a_H@uo=yQB*s0%FPQ5@5LS#J(}lxJ%l7lbE*)O#kJ(sqXiow&cTXT_nw z&~D##p7xaHZ9U2hT&m``$I?te6if#NYjSf1+Dp7bHN1_kSIe87f};ec_AxE3tE)>k zLt9f{|NF*Jm9MgFLqyu5&hQ8o|^Z*Ns-lG@r@!05-1AAkS(BbU~0 zn%4~)RBM&{JR+j9riK(^8IXn@u`e*hOAM}Vre-x zKc<%P6HuKaf%2$2tGjpo_h&$Q`S>sg%FCgdoj7q~WMl-8PWTL)Jk)2`pFas7KZ5pF zgTy1v-^9g%e4&*C09YZ>d3nuXx8Mc93&8OSsiIAIzxeN?L;3F?m90Hzt4aBP+itI) zFN`-;V3Vb#C29f0Pfu5OU}@>a&W^XDq9XFYC*R%=C#Ja2x&+Fs#63hJhgEfo{5dwD|bjHZ~lQ%0h5NY;6S$3=F2Hr{{5_kdTlp z3_uj){zFW_wAIz%cTA@>)gqXahvU#n|qs2u+nzC%svFULBPnUj-qnVlMV36XKQ$~<)M zqZYU?B}Oe16Wq#41P_Mg@P)K?>Y!?{0c@?Ey}hh(^p+fDs@78on(66as0rj(R8$mN zBuFLd3B(vG2;HEJOgmt8S!bxsSC9%~GwuQDfEf^QZ)(Zj#A!ROn8U9jcC&fJudddPrev=RFR;-n49FL7MkIUVU zRb7=Jgkt`)eg;73038wd4MYL_-)I$b(nuwCf&yX(;sC2e7!IPNt8+b4O*ysvCsfi; z_IBU>S3+y-_oiXhZKhr|wu;nVOrEq_hRLTa9+*Sb~_dge$#@jZK0q z9v(J>Ek=*!!EbbvQiW~Y5+}n6!wh5u@A1K<;Mtelg)4!N+9;E@Isw!S3~pEwA8jHW zbDd11cy%s2amHeg<)=svbU$8RUT_Myd|mM9AZtLc(TV&!N@0;Uj1G-9L;(-6^h~1? zHW?XaP-O@#ssO*+BHv*{pcBHrI!Z?JAuY}9)-8W69)+t{4}#bbR8&rvuCDGkYtcmQ zG(7`@L_`e<1jA+2A*-mUVRZEL^71ktKR+975X5P**E)5Ec2Kn`jMO7R)eoT^@86Go z`ji}zg%b)a$p+m4c?0+I_uq%KkBp3LY-xdG#>K^Da_7#0n5M_5Wv1T%s5LKX+4#dD zih$Ew3+=q=o}OF7*vfzUQA3OCb^LrIw?vfJa%sdPx;%3dSz=Xp#ZJ~v^`keTW>{Fg z|2fvgZl>?B9~~_T48P+Rshor&B_6&iCgc}x-!4oqm8H8?;CPAcNR??8i)(}YQ(G&& z2Q4>?-DfFlVm!BngM&-@dIGfg6pOwrxl`@`o9`3nb4Sn)ruNO7zQ_bHPlO$s?73(>E~dN*#2{>i%*@OH)qoD2pKh2rJ44G~y?*@|XvS}A zQSfm5$v5Q3Q(&{dd`XKYiKg`>FAuGZlSCH6I9Bx%m{~+411ys>0HDhBO zfKNHSPq6i4V^2a`0lm}H(@g~FFlc!q?Y&iS=Xe~27omZE&oJ;36lVKk2Mk7;6gJq_o%3-2+ja13Ym>ogHi^tSy@@RZLJ9*=imv~)Yjes)+p_|phK&hgmwgv6N>K2m7r(0@WU}w=C5DB zpje_~Vuou%PQdogE-AUHrgkWEY)mk}*ZBA5u50*Mcc=(4X>IL#l;lY$MkFSb2J!sn zwp^N#2*a_TZ-Mv_IxwCYLP-6}zhPS;GU0?1W-xqw?67pmNIrL2!v+PLha3kaK?lM% ze)HxHIvQ{oUzi9-!&!Lq_N{4_E(9aGjcHC-esM8-vR4^U?{$#Law}}rJ>=d$QVP&V@Nb`17PL9e~!=rXfSr-!A%yG zbE|tzxVbKre4h90)+^O&yyH?7P9HMj>b#H^&x$*)U%xKpvGinP!y`K~?N(=RfR*MK77oFzhFAG4HufrnI7HR^ zgoKB9LEvOQjA*n`C;$S#0Hng_8Jq1bQc+VwOCw~Vn;RW8?!}9H3HN>cNFGsN6~U>N z@BWWtLFrLLz@c4}jDLq{S#~-`O+OUBDd;@(L4{wIof=cIia&oCV003Qp_o{GL&HT( zA(lP-)8>bU9%g3?udJ+q$7_MqTe*U+) zRZUENW@q_9rhy$D@N??TQM*!1VRn|#VVdgdEF%g)Xb@Su01j1o{r4lI(PW# zgPqu92b-I9nBLDvTD{IF+dS#>QA0>wr}oz`8aO>L<-qaK=;XkezkI18j9T*Z2jCgQ z0|uKWsju4Fl7dSb)liK)z>{I22!h?+y~~Sag!Kkz25CLIw)P5lASSp2O&uH}rL7)! zsHPmne&HvmaA-E`taT2nfI@Bhz$rbnpeMf|&REsqf9nuSa%ndet$8Bq zx&Gr|fY|Er*wfUk5+Ovy<} z{^r8#1$*Qb&8UGNQ&Ypx8E9UwU%$@B5D^tNGCqDrSXd6$IwsK=r4aBczn9Cxvg~^1 zPTq6g-RyYZLroUi+uoRaVE%^PBloQW#{%m`J|Ps>Qnk!)-Ll8HzTAWb<|lYLNCLbW z&=7$tpaMZ7zC-1*I#&P*Wke7?oJP*4_~CIcT+e|Bm^Zy zz=b4@12^v{XTU}z>*0-R*eBie{X4arc7`^^MT)+vy6@lF7(&SJ@q*I(%MqsS3?a`W zBlme#tnx%Ee}in7D`Ww)S7Lt%#FKMQ(%_ad)`Aol6ux0&mGr!4d3ny9r6OKA9y*mNzwXft+Y$j34yD_5FXkK-ssAW3+q-dChR zD%Y7|4hxG|^Vf!F&VFkg58jLnWF#IOkNIRV=F?wQm!D$%XrYVr4`q#I%YTu|tkLpi z*%dMyq5GM=QbMxCn59RI=|jHI9wPt}-~j~=r*m>@3Yyo=IL~*}i{OC*770EFL3mgvT%%eHEGjJPs%zrxw;WAs?^)xuD=OoF|M) zz@o8nMwvSKr_M7S!Y68pGh%4I*=#^M=sB6Y`>v2-HN#*lDxP)8<2(!z0!Kkd#!QsJ zQBcF`TQcXcy&5zV$^%12SYYrkP8}v2a48}r z4#raGE*l#gZY$H#fFDn> zL`MuAeUFpmwm%(|%k;GML*H+%VPNwz!2VzgJZxd43PAiT z=ILNGqw1lRpvXZC_8A+%1zg?S7aZKeNbXP4s+xR7qK!BXUxHmMHcUj!l zmY3`UEp_z^f^=K*Um{{Q{TeGajK-5h2q#P&1?zTybvOL|%cYnYo}A3_`0-9AAV{~Nms>INrUOGoF7h=@Eky?}J@rLv9oRq4XL-tUg6V50OnAenoakH=U)<7JMUJ%9TXfMwbH{JuAqnw^{fmmW>UaAsjEKMZ zEFJNA8GBXj`Y{j_(8Fu$>fgYEFqXolV0-WWoCj&k$AH-(!w4@3&yJw`(I z_E|riChf$@0xIDl;b;aytPpJT08}s7*=Haeni2s#N{qm%V19x|X=rOJ3evGNQsEH8 zqp=%BO^;1oRx;>M^et}98;gp3{@qn?jBw%{1-QyQw1RINj)zQu)nlfE_z+SLRIA+N z8Qe>pMqp`FTK}6E_GoAY9wZtDX0);i#U)w3Bh^8+J*ReWWaLSI-ahW^?4_kuH8jDE z4vsk!CU5bX; zSuy^g`%zOFhpcz!j7mqC_v~XklG-U1)9Pn#c>V(2_qX9Bmf4~7+)iV9&+KJs0V1af&vK)NHNthg@e_dXPZ+{ zP>-VqV6+(g#<$*v)ny7I`SYg+#xs`8_m`TBLy7u`mE9e;)3cwiuei*Xd0j@^!7v3g z6er1(Ane6%3l9?#*lwD7!`=ajL#MLCp&8Tyq%4}Fu*|tD;QNPWEqCLWRp}XbX3tB@ ztW7d83>lvY5h?agy9|W~<^Yk8G|AGX1XANb9JmH1i!iZ0Jx@d`E4bg`P91OQzid`{ z&duMl0|TS{qN4ROW@&aYv8c&OD-8CY0P$1W7Pi(M2`AqA=PY455#F)*^8=5YgR3ir zWi!jo42;sz77M;cEf(S`QcTB_t&=slaSDwfe{n!dkS<^ReD#-tb#1@bnc003gco0` zrr6O+s$h$BaCY_wBNl#0e?*-plI>Dv?SMmebB{sd%+JH;JeE`obm^e#yMZscb`}%o!Z@- Zt^YpgEY`fAqRs8Jiy+u~Cv$9vn-dmZ;DvC0*GUG}i`;ug@jL6K4%m2Cm z&xcDn=bm%k_j$hSx$$~Bs-#4WL&FX4BrjzOQUgP*Vo!DD1GQi9C<==>&w0C>Y0r?azsaJMSWVE8eACVZ_COS7XF#lLZZ0JfpZxQ09|d~W?(n-$cR%)A(KM#> z8ai&e%*x*}y(nV4(&RqO+Oo97qfYtQ+1XKq@wZpA|DT_?i6W4MR9w5WPv&nREJD$T zGhUbID4zWbVypA{Uvy->%RPUq3xgC8d^|i0!o$*v@)S)@V_X)&hvPU%OZ0{@vzQw* z*6u=QWc~D(2t5g3c^L+yoY?GxuH^dr<}1+Z$tz(_N)GtrSsFjPD1o>3l75t zgC9N!!Nv3Pn7O#Q#}++=CG-823dg@EMOjlBH%s6l zQR!HGcehG~c{62jaPYH7494c3xMk(#SFc_Tk0z#DT1OT44h?mGe{Y~~W|n}ftQ>b3 zSTkjX{P-7L-0r;Q9n&h!>gDllWRro7ZMdwA7keQBm-wWc$(;hh7II=e=Qr(&gVdEd zjY{X%E#XOUad&r6QzNJ}x^;GT_I})1!hV20%h;Hzs5hSuNhQ6pM*b_{uXy#lxDS&} zjwp`qFJDF+MZA`~lfQ2sQY$}~^83S%fJfY#ZZs$#A9ZW=9(?&;08+=JB#m^r;eE9vhPf~L7BV}b=ERXG(=JV6xP#r|jZYO>~ zKYt?yLiT)Dadma|9<{Qlh{(w1;iyf`hvMQ$#^e}vb#=a*H?JWCOlzYA+xCgDJkA0_ z(uVnaPfyQJ`vs{Ykdt)|e?LyU6?3Z3wD{gC)ydb8r#LCML?^s{{Tc_EDtqw)-i9kZ zKJ=uEjUX&OK0fH;bnRvW2c4kB0F!&$^EA%A*RiZiU*f3DYC1Rrry}pEUzRLnS(;#S@z=zrLk25a2*{^`x>5Cnb@<(!=8F0Q8#EG)FsE8+lV{B|p!um6gMw(K`{4g}t=#rlS6=Pq| zWu|;p+N1W90pTPyU2a4OEKU8SOU9#|r$>KYvlAQyemWm?4^c!6^!G1!k;`i7QwRJV z6!ZR;c7qxx{PJSwa_G$)PV9xyi?z^EyF2>Ds9nL(Q(~-zMaHMn1tbAuO@ERLLd zT2lx*&{>2Lmh^G;r!2DANca+V7_xJ8A?dV7lE{BMWjD)f$ASPDS)g>RW7Y>qP zYiVg&@M^YdoJNh0M~{r?@gzjd%~{}JV`a|aVq=kzkjRyeb?zJ&x3)6TCgQTD;^*h* zd$i7em~>g+*idMiAg*a;y0mXt>a06l?r?UnU3DhKtF2Bg8dGna9xNze?W-NAsNmb4 zZcIfq*$*;atgFg$Almo)Zke|SNnm{_DvGe}PI_%rzJf#|04)?16(>bMAc9XPU?)Bgx z+0LuA=&~*PT%XKqJ2sd%d0bl?$Wg2`>Ob3VO@%##cMHk|!NN3H2B;zoQCRM8+bCQ;dphZgX4uX9_wS4QtWzi`D2z-^C8VWMIyRUc93K~d{75@EIEXoGcULHd zMJgsMi&i{eCnGz1`DcwS0S>mf?*=s)vpAx9A~7Y4HsjBH>Jw>J6G^G(=zOd_nzJhV za>@nOoeo`h1#Db-tAGFrSY>`%;{Luq9s+EGd-qP}p6*;lWb;4hcAfrs6~PvI?m1L& zHy3eIDvOT4efu^5cuxvTai`U*u0_3D&44GFC6#6k3AtkKTnL%7KXrMxZSk-?h70bJ z;9vtHWM*bUR~`6EOM5IJusi^{g0>na2*Si>eW={$K7>DeQ^$24D#l(~vcKLLiG#dp zP_pYFd&YpZdo=C7Hr4Q$Fs$R`tKf_5Y*hqc>+6Zlw-(h7zP|rGGPPGy!jZi^_r_j; z4L$ixEJ%UaY1tI8crxpYqr%$^%SwfS=UVrXe9uFqTOh5jo(3IRzPbr*y#KzmfCC}3 zi@ai9V}pyed)#q(7oJN~p2Eb`RK0v$qi%9wxuGG-VwhV{?U}=HNsoQ;UCEbq_JbTk zX-wGy4>e4)ZmLse^O-7S_bba&hzI}k!4TbVpF7vCg%c7IYH4Z~f3vf(v*S!mNFWFk z%}t&^n>+sA*6P1!6ZpwOBkc>|=^X)q6zF|eRxLF(D|>q`s9Grb`|U2Gxy>$lj#ASYF(HDjFCV_-bNP-_S5>YRU}oMk`m8lv^iHE-+BCJDDkVxhK_D zB}7JHz<$O`#OACKYqk7nct|I=OP-9JO67WSaq;BHh@3oy|Ky|Wy~YkSKi2NW+0NR- zC$q|k^DedvWR9={hJD4vgrR@!>vCYtz87BI0tE*}OebL8YkB!^0tcBd^`xu_U0N8@ zcYYoOE1r@<2Fi2PzRuIbLyu-Fl|@Pgf!TgbiQ><9(FbGBSoQAnGWHG*23e1ej*dpR z{KkI!)OK_fLf6ARuPr7JDNvm3258fHukQA})g-LTNu(28g~o#d>$pVkjM>!aQ7g+_ zoGe2D);?_Y)2CLR+VXRos*g`7@|bDz%##h38MGHRt}yA0io)4A40M}q$pp~>%Jv&~ zAtwSTL6t2yqn2-M%kTd>0p)0?qeBKI1WXn%_}_$(q~uFLvFS#407-7de}etB0~ip6 zJA^W6J6+9nx|8#wuy7c(s!`bNgbaX~7Lj^1nFxW&&OUBt8g=Z})9DMN+Pt&PpiYY7~G z{G?LYWK%$Jnp7Gw-wiYj{-mj)!Jfbo6B`Tqk_aH~=*SNcsms3ZHQ?3kEQV1q z^g?#oO)OI%KNP4J6KyQvp0CU=0rP_gHCp^}j6s z2Tj&pc-RX7`^C=P@QBbkrA0h0uC8y5${%TIk)$$9C?N9gKKwLNERn<~)B6jD}Gd zv0d`HgWa$!P-)-48*We5#UlJyGL5|^goh~*jrr+5&vW~a=K>QECWeMzFl+^cv)#I5 zB(V+3t`qtQd%>YFSS7Mkt{C->Op=~t|8T;xl%2r*gB6yT>y(l{_31(+c^UIDxROWg z6tCC`&d>i2g8+A`F)?(`N=y5kZ5fgi!R|3x`^db}jYV1imu>3Z?r-VbRP?W%WD4PW zUORK20F?b&^VxzAu7+Q&Lc=>4P~cPnf{V>~Tmdb)J=+SPt!!mS)4k%QpT-$taZlle zR?xqS2x(iq!=m1iUv{f2EAWRvX;#>#hb+Bs)YD&NWGKl~ym;AZJ^jl)D<99c|eH?gCw^ zs^YVd;Xf3h17U24TJ5;l>>x-wsJXlMg$f5*cyUl_-r{o;Yq$6LbuAs85|v_WFjycB zf@KQJUahRGyst*L_-QeNyihKpm=%IWhSKfN;-^;Drx^nLgvX%cH&X%?3i_j@qy&%}V=lmhh~#MT z?$F?bKlteKnp1t_Uiw!XkPIt%65i{3{x8zgyTEwB)>(LdrGZ0XXP(S>;;Xh#Uzc+d zVT4v6FE<{uuS08#t@e~*9Y9i3jd9d19qC8MHbwG>a~_`}(nfjP>eI+m}Wo{=%4 z(<+Q7ft^rUUyd?@9cd+G1uDLlPqBK!7Jf0Sq?ODki*&F_B09v2lz;oA__mUMR1uG5 z!fkPJf_xqPWR<@Sw^g+Dso{c(X`H^yjLEA^wsA#quU}u|N!Wi&4_cc-`6`_IPz^K* z=7Ml#mEum2)dP&AF;i0>`Lii(=fc!gD&>zXRq_90CExOsdiwO~dz9mgtgI4V>s!WE zhPZhENJei}@yS{3R8;VwhQM3E;?KH?wYIc?rL3#1HN3hW!{nw%9E|}V*rV%09OrM| zywNKkudS|5g-*V9?V8Ku$HhDepoZ_r%3N=%w(Ka7JzpdzOj;lPyEe>?D3A_F>$G|X zAdW_(%N*x_{(QH=z0+|iqw2K!qw)bLz|%o@6B83PD69t$`l0I~8VC=^VkE%>&9w`; z22=>>l%P$mzJOge4Z5+?zw!A}gr=*ItTfwzkrH8?domxarLFyzodBahK}{2e?R+|B z#g%_G>0Eq@VT|4fZ7J{1)h?T+&m!SOOT!+(GnL4fE$ z5r9AbfWro0T{p_B?L9qjLu*z&Y&A?cSyOKE#EY2-^3cLIZ9D6_y%M-m~ zl*i%5I7TD`V2tU?4$861St*T=IX3`G2Cd~kAqWE^4?3E3xPQ%gYYOwfZGctdGHq*x zp#?I9?ws8_p}Id^LWOlCA6@+*acEq1UnpYv^5=DteVO}Pf`Rj09=xSi3vRjQ$15tLVaq~ z_wP|dLpphqzC_yE+CsU5m>Y9;cBWuph=;pf1dsyFD(>}*03MSB9>IWHx2Ua+d8f(z zLrD6rZdea)_)hyCo}$4$g0QpWol?MoPOF8*uGpId*tRw{hKmkEc@pQH{bV4)gTIrrNGcVh)Y8AbF|x))Fq>NP-lr?s7#xFiW2V`go0ypRz8MVzgE~<7z}8F|Qdz8mOzPSra>wj=W|4l>L%&YJrA3=m`j z*_w|;skpQjGzC8aclx6bC;md|lt6(lM$^5>&DFG0O4|3A2D%1y1g}8g7{Ngp1}WTX z-LMRJl_s}YNef@}VH95||EDYIX(rw!oI`_yE7R^B+E#B$t)U%pkj9q5_bYofG&LDm zS#=;x{r4|0^8?%6zzn3C!8gH>J!0$y;FM-aG7b+}D|>l)dB0=7Zf`FD%GKA`=T!!N zTEg5T0+U!7S6gT!&bQn{`@#7`>;a+C2*_kdk=m-Nlv+Mv5G~aZMuk5=nsyk<_ySqq z!hPe1%2sdEG{F$@BvibA|9aM~bliCbbQ;7v;5ZH0A@8d?ijhA3m2O^ZM~L?ustC9oGH2vEb2xS`d zRrIMbO3JL#2zPMH&j7UPt5;#Lh)D?vHZCrFaI}Q)h9STfIKV?i=@ts9vJL)+Eg7)2 z2}MGXg`-d?kPS(3aaLd)O9x&myE?9CYk26#Cw!KB$Z|(mcoAalA3tJdX3Pm9!WI{; zf32*%h9tqLoCaiwqobo7n8WTQhDeZ4*Kgdwg*%1j?TI3!vT88HOFXn2#Oa14-rvO4sLGzgrZlFDS@wC-`R;ZD*p=LhnkroflR`4 zxf|mNJUu-zmb0=_8Kem$RWDz@e3qVmC0{2N(hSw6xapMBGvF_;<1RBYu8lk28M^%+5-N*;~|=;Fztd>YiLx^r^db>?1y*depd2kGG?F5Zl;VaCQg+8OwX? zmUZ1EI|265_AGJwH=M+;5m)fZFcM0qlnSCVM<=Ini)vMOcR?tu5flmw9PReDTSjJP zr&}ZFr9ZQ+bO}XB=Y}Pi&|qqG)~yHPm=UN^U^h?}nUX0VpCykPz`wxrF@qR`W{D+d zQ-QaHY(@jr{%43)%*@O%44VEeYN%5_W8%lsw-TLrn(zKX$?osJ zqry>nWG4hZ2J&HOb^s-2VS5tLQ$U==L_|=hpuQ3l6P@ZNA$Bj-Egbs!)5MpM8|sTS zH5LlM$;nAeSGSZWAt@zgbs$F=vL}${a0Vz-!Y~Mr4gNacyGPlR%v2gObI-s48e1I! zII;kd4n$QTZcWY2wZPOOpfnr+xj?f=cCy3qJ#lda;KEWEgkzxHaFBqWv@|prX$xgr z+b>}7^p}^ncTQTGg0en64b9up`;nc=6qeU8jLM-~lJ9Ep@@f_x7p{<%*?ZqIvrPD;t}lsTFq-#&Izpk#6+y zye_Pc5Pg&1mYEqJEeK{>TG~-aeW1cYN5NPM69DtjlSIv4T;jYq!9!Vq!eUcZLZaqV z{Y;@As?7op4-TyS{plmv0reix%!9FESEt0o$45<16RW3<%+FVECvR_UeH|Hj#%Np5 z$eeszS~_Wadt2};%<5obx3T)Ix|_ZMD)fFF8%qxoi4^Omr#WeDZSA18%}9f{m&Bm8qy(l{4^5G@th7Gzi zfg!i~=nXolxFIim6hp^P;wNNsTyk98!V8CY0L=jcgr%f%L82d=24TqehI$do@z=}$ z9vi;=7v zAU3!^sG+8YMaMw6ug7`la?n!mJB8i~Y_WN`Y)*S65fy9U*#>^{p*s z=mL0tNVI^$FtHb)2y|Wj$B&=a{$7L8N@ZnbTvAe0*CGbCB0AYY&;I-O@7aqNah;v= zy|qaY3;Fo@eYUT=#>IsP2NMtyhC|Z-`0=fx;$mxS|IDB!U?mSZcb&G!GXGQG zVI{RS>|ZzDH(qXZTZTIp$V2MM_M7P~E}`0fLuzG69WXWu0RtdT9M5?K0=E$JS%7BS z9x~?!Gfc~YEqepwkz}#CUf|OAHqC?f*M(}YK_UU-1U$SfI2;fs%g_XX^rqm$Z)j#= zOe^}|p_aP3KJJD7;w31c8$ujS9lM{2%haHGo?vV&^!hSr+xIZ+fKj%}mmVsW<;K2F z%R~1D)6tQM*rE?YGslw-&w^zR8a~F&H~WNUjJ;>2-3Q2e~08em9Y1iC;qg*kk94P#M` zcIL_JCP@%Hf`S+v7hxm?u=N0if*D&CqZWWNK!8L}Z>jhyUg6!?*j4ft{(-K06_g*$ zUFYk`Hb1FJXL+DuKJz^Ahdh^@{Ug*nl#r-!`nPZ2V7Ld>uBD-21AKZz-G-e)&$vBV zlZWqoYBt-J^R9(v%()es=#?s7y@c1q2%t<2Bb70Cf*u6=M>+7TFQtBhmpX zAWOJ;^Cm2@H7Gq8Vq+qD5PR6y=VKwvps3aQwzhoTfzWEHRb~f_AIelOtelA0^IZiA z03V1g1#nCW2?-Q5G|?Ly&U_Ya^dNv>7;OTf3@o5kjmZa(MFot%+uPfFA>qvh_U&1A zb}WoJioy6m&~GI6+A`#H1uz9{H0CrgY>OV^LZKHyn3x})oQH=;4z{F3D<`79UIgSW ziTCP&JPa#0Ha0$-B-}zI;>wm)e&wIPNnu%2ciWq8xK%v&_Y(0O9b+GZL5a{!!BpDB zXiQM}$zrEh3mAv-#wQj)z$){D)^majp)ahQ{41kSrN5Y((Q9v*g7Ek#B zLSf_?L&^jY3jT~SSq0LHx0>0b%X@H}pyAU2Zu$85-t?~&K~_X6HX|3uvFKw#LD!yF zs92WDs_bOOxa2CDxBWYO&^{6Z_|478{*`-TqL7y+jfyl*R~Fa+qmBT(K!=o%C%`CH zoSE#unAx?+b-(2vZS5v>+c~oO=V{(zqQg|-^=dK3W>0h=UR^ElV&T(miH3g6oc#?? z0th_kRu?rx;!)j`pK>h*YmSo8k$$t5mF XOpUP)>2o#sEEhsuSx2c#!7Ac^)=i)y diff --git a/ext/gd/tests/imagepng_filters.phpt b/ext/gd/tests/imagepng_filters.phpt new file mode 100644 index 000000000000..ecb5f51f61d4 --- /dev/null +++ b/ext/gd/tests/imagepng_filters.phpt @@ -0,0 +1,59 @@ +--TEST-- +imagepng() supports all PNG filter constants with bundled GD +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/main/php_compat.h b/main/php_compat.h index 68bc4e7550f8..852b4fbb1ff8 100644 --- a/main/php_compat.h +++ b/main/php_compat.h @@ -266,6 +266,8 @@ #define gdImageCreateFromWBMPCtx php_gd_gdImageCreateFromWBMPCtx #define gdImageCreateFromWBMPPtr php_gd_gdImageCreateFromWBMPPtr #define gdImageCreateFromXbm php_gd_gdImageCreateFromXbm +#define gdImageWebp php_gd_gdImageWebp +#define gdImageHeif php_gd_gdImageHeif #define gdImageCreatePaletteFromTrueColor php_gd_gdImageCreatePaletteFromTrueColor #define gdImageCreateTrueColor php_gd_gdImageCreateTrueColor #define gdImageDashedLine php_gd_gdImageDashedLine @@ -307,6 +309,7 @@ #define gdImagePngPtr php_gd_gdImagePngPtr #define gdImagePngPtrEx php_gd_gdImagePngPtrEx #define gdImagePngToSink php_gd_gdImagePngToSink +#define gdPngGetVersionString php_gd_gdPngGetVersionString #define gdImagePolygon php_gd_gdImagePolygon #define gdImageRectangle php_gd_gdImageRectangle #define gdImageRotate php_gd_gdImageRotate @@ -375,7 +378,10 @@ #define gdCacheGet php_gd_gdCacheGet #define gdFontCacheSetup php_gd_gdFontCacheSetup #define gdFontCacheShutdown php_gd_gdFontCacheShutdown +#define gdFontCacheMutexSetup php_gd_gdFontCacheMutexSetup +#define gdFontCacheMutexShutdown php_gd_gdFontCacheMutexShutdown #define gdFreeFontCache php_gd_gdFreeFontCache +#define gdFTUseFontConfig php_gd_gdFTUseFontConfig #endif /* HAVE_GD_BUNDLED */ /* Define to specify how much context to retain around the current parse From 9674d849ebf1c4841d278fc09023f9c2474cf758 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 16 Jul 2026 17:04:15 +0100 Subject: [PATCH 208/211] main: convert win32_docref functions to a new function using new stream error API (#22748) * main: move php_win32_docref{1|2}_from_error() functions and make static Those are only used by the plain wrapper, so no need to expose them * main: convert win32_docref functions to a new fonction using new stream error API --- .../tests/phar_buildfromdirectory2-win.phpt | 6 ++--- ext/phar/tests/phar_gobyebye-win32.phpt | 2 -- ext/standard/tests/dir/bug80960.phpt | 6 ----- .../tests/dir/opendir_variation6-win32.phpt | 8 ------- .../tests/file/rename_variation-win32.phpt | 2 +- .../tests/file/rename_variation11-win32.phpt | 6 ++--- .../tests/file/rename_variation12-win32.phpt | 6 ++--- .../tests/file/rename_variation13-win32.phpt | 24 +++++++++---------- .../tests/file/rename_variation3-win32.phpt | 2 +- .../tests/file/rename_variation8-win32.phpt | 4 ++-- .../tests/general_functions/bug44295-win.phpt | 4 ++-- main/main.c | 21 ---------------- main/php.h | 4 ---- main/streams/plain_wrapper.c | 22 +++++++++++++---- 14 files changed, 45 insertions(+), 72 deletions(-) diff --git a/ext/phar/tests/phar_buildfromdirectory2-win.phpt b/ext/phar/tests/phar_buildfromdirectory2-win.phpt index 19a5ae8bfa2e..e2d51b2c4664 100644 --- a/ext/phar/tests/phar_buildfromdirectory2-win.phpt +++ b/ext/phar/tests/phar_buildfromdirectory2-win.phpt @@ -19,6 +19,6 @@ try { echo $e->getMessage() . "\n"; } ?> ---EXPECTF-- -%s(24) "UnexpectedValueException" -RecursiveDirectoryIterator::__construct(1): %s (code: 2) +--EXPECT-- +string(24) "UnexpectedValueException" +RecursiveDirectoryIterator::__construct(1): Failed to open directory: No such file or directory diff --git a/ext/phar/tests/phar_gobyebye-win32.phpt b/ext/phar/tests/phar_gobyebye-win32.phpt index 7baa0c618fe6..8100e6cf3000 100644 --- a/ext/phar/tests/phar_gobyebye-win32.phpt +++ b/ext/phar/tests/phar_gobyebye-win32.phpt @@ -44,6 +44,4 @@ bool(false) bool(false) bool(false) -Warning: opendir(foo/hi): %s (code: 3) in phar://%sphar_gobyebye-win32.phar.php/foo/hi on line %d - Warning: opendir(foo/hi): Failed to open directory: No such file or directory in phar://%sphar_gobyebye-win32.phar.php/foo/hi on line %d diff --git a/ext/standard/tests/dir/bug80960.phpt b/ext/standard/tests/dir/bug80960.phpt index 43d01465599b..33fcaf9fe287 100644 --- a/ext/standard/tests/dir/bug80960.phpt +++ b/ext/standard/tests/dir/bug80960.phpt @@ -11,14 +11,8 @@ opendir("notexist?"); opendir(str_pad("longname", PHP_MAXPATHLEN - strlen(getcwd()), "_")); ?> --EXPECTF-- -Warning: opendir(notexist*): %s (code: 123) in %s on line %d - Warning: opendir(notexist*): Failed to open directory: No such file or directory in %s on line %d -Warning: opendir(notexist?): %s (code: 123) in %s on line %d - Warning: opendir(notexist?): Failed to open directory: No such file or directory in %s on line %d -Warning: opendir(longname%r_+%r): %s (code: 111) in %s on line %d - Warning: opendir(longname%r_+%r): Failed to open directory: Filename too long in %s on line %d diff --git a/ext/standard/tests/dir/opendir_variation6-win32.phpt b/ext/standard/tests/dir/opendir_variation6-win32.phpt index 40a0e9197828..e2cd97f4e05e 100644 --- a/ext/standard/tests/dir/opendir_variation6-win32.phpt +++ b/ext/standard/tests/dir/opendir_variation6-win32.phpt @@ -46,23 +46,15 @@ rmdir($dir_path); -- Wildcard = '*' -- Warning: opendir(%s/opendir_var*): %s in %s on line %d - -Warning: opendir(%s/opendir_var*): Failed to open directory: %s in %s on line %d bool(false) Warning: opendir(%s/*): %s in %s on line %d - -Warning: opendir(%s/*): Failed to open directory: %s in %s on line %d bool(false) -- Wildcard = '?' -- Warning: opendir(%s/opendir_variation6/sub_dir?): %s in %s on line %d - -Warning: opendir(%s/opendir_variation6/sub_dir?): Failed to open directory: %s in %s on line %d bool(false) -Warning: opendir(%s/opendir_variation6/sub?dir1): %s in %s on line %d - Warning: opendir(%s/opendir_variation6/sub?dir1): Failed to open directory: %s in %s on line %d bool(false) diff --git a/ext/standard/tests/file/rename_variation-win32.phpt b/ext/standard/tests/file/rename_variation-win32.phpt index a348438bed55..158a8421ec79 100644 --- a/ext/standard/tests/file/rename_variation-win32.phpt +++ b/ext/standard/tests/file/rename_variation-win32.phpt @@ -61,7 +61,7 @@ bool(false) bool(true) -- Iteration 2 -- -Warning: rename(%s/rename_variation-win32/rename_variation.tmp/,%s/rename_variation2.tmp): %rThe filename, directory name, or volume label syntax is incorrect|The directory name is invalid%r (code: %r123|267%r) in %s on line %d +Warning: rename(): %rThe filename, directory name, or volume label syntax is incorrect|The directory name is invalid%r (code: %r123|267%r) in %s on line %d bool(false) bool(false) bool(false) diff --git a/ext/standard/tests/file/rename_variation11-win32.phpt b/ext/standard/tests/file/rename_variation11-win32.phpt index e4c55fb6ce5c..8b937b3db4e6 100644 --- a/ext/standard/tests/file/rename_variation11-win32.phpt +++ b/ext/standard/tests/file/rename_variation11-win32.phpt @@ -86,12 +86,12 @@ bool(true) -- Iteration 5 -- -Warning: rename(%s\renameVar11\renameVar11Sub\..\\\renameVar11Sub\\..\\..\renameVar11Sub\renameMe.tmp,%s\renameVar11\renameVar11Sub\..\\\renameVar11Sub\\..\\..\renameVar11Sub\IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) -- Iteration 6 -- -Warning: rename(%s\renameVar11\renameVar11Sub\BADDIR\renameMe.tmp,%s\renameVar11\renameVar11Sub\BADDIR\IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) -- Iteration 7 -- @@ -112,7 +112,7 @@ bool(true) -- Iteration 11 -- -Warning: rename(BADDIR\renameMe.tmp,BADDIR\IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) -- Iteration 12 -- diff --git a/ext/standard/tests/file/rename_variation12-win32.phpt b/ext/standard/tests/file/rename_variation12-win32.phpt index 527a8865c331..97aa58f6173c 100644 --- a/ext/standard/tests/file/rename_variation12-win32.phpt +++ b/ext/standard/tests/file/rename_variation12-win32.phpt @@ -83,12 +83,12 @@ bool(true) -- Iteration 5 -- -Warning: rename(%s/renameVar12/renameVar12Sub/..///renameVar12Sub//..//../renameVar12Sub/renameMe.tmp,%s/renameVar12/renameVar12Sub/..///renameVar12Sub//..//../renameVar12Sub/IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) -- Iteration 6 -- -Warning: rename(%s/renameVar12/renameVar12Sub/BADDIR/renameMe.tmp,%s/renameVar12/renameVar12Sub/BADDIR/IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) -- Iteration 7 -- @@ -109,7 +109,7 @@ bool(true) -- Iteration 11 -- -Warning: rename(BADDIR/renameMe.tmp,BADDIR/IwasRenamed.tmp): The system cannot find the path specified (code: 3) in %s on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %s on line %d bool(false) *** Done *** diff --git a/ext/standard/tests/file/rename_variation13-win32.phpt b/ext/standard/tests/file/rename_variation13-win32.phpt index a2cf9a528d1b..cfccdd87fc79 100644 --- a/ext/standard/tests/file/rename_variation13-win32.phpt +++ b/ext/standard/tests/file/rename_variation13-win32.phpt @@ -65,45 +65,45 @@ rmdir($file_path); -- 0 testing '-1' integer -- bool(true) -Warning: rename(-1,%safile.tmp): The system cannot find the file specified (code: 2) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the file specified (code: 2) in %srename_variation13-win32.php on line %d bool(false) -- 1 testing '1' boolean -- bool(true) -Warning: rename(1,%safile.tmp): The system cannot find the file specified (code: 2) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the file specified (code: 2) in %srename_variation13-win32.php on line %d bool(false) -- 2 testing '' boolean -- -Warning: rename(%safile.tmp,): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d +Warning: rename(): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d bool(false) -Warning: rename(,%safile.tmp): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d +Warning: rename(): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d bool(false) -- 3 testing '' string -- -Warning: rename(%safile.tmp,): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d +Warning: rename(): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d bool(false) -Warning: rename(,%safile.tmp): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d +Warning: rename(): %r(Invalid argument|(The parameter is incorrect|The system cannot find the path specified) \(code: \d+\))%r in %srename_variation13-win32.php on line %d bool(false) -- 4 testing ' ' string -- -Warning: rename(%s): The filename, directory name, or volume label syntax is incorrect (code: 123) in %srename_variation13-win32.php on line %d +Warning: rename(): The filename, directory name, or volume label syntax is incorrect (code: 123) in %srename_variation13-win32.php on line %d bool(false) -Warning: rename(%s): The filename, directory name, or volume label syntax is incorrect (code: 123) in %srename_variation13-win32.php on line %d +Warning: rename(): The filename, directory name, or volume label syntax is incorrect (code: 123) in %srename_variation13-win32.php on line %d bool(false) -- 5 testing '/no/such/file/dir' string -- -Warning: rename(%safile.tmp,/no/such/file/dir): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d bool(false) -Warning: rename(/no/such/file/dir,%safile.tmp): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d bool(false) -- 6 testing 'php/php' string -- -Warning: rename(%safile.tmp,php/php): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d bool(false) -Warning: rename(php/php,%safile.tmp): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d +Warning: rename(): The system cannot find the path specified (code: 3) in %srename_variation13-win32.php on line %d bool(false) diff --git a/ext/standard/tests/file/rename_variation3-win32.phpt b/ext/standard/tests/file/rename_variation3-win32.phpt index 8f347e5506f9..847027118633 100644 --- a/ext/standard/tests/file/rename_variation3-win32.phpt +++ b/ext/standard/tests/file/rename_variation3-win32.phpt @@ -64,7 +64,7 @@ bool(true) -- Renaming existing file to existing directory name -- -Warning: rename(%s/rename_variation3.tmp,%s/rename_variation3_dir): Access is denied (code: 5) in %s on line %d +Warning: rename(): Access is denied (code: 5) in %s on line %d bool(false) bool(true) bool(true) diff --git a/ext/standard/tests/file/rename_variation8-win32.phpt b/ext/standard/tests/file/rename_variation8-win32.phpt index c880cc60eeb9..aa8c70ae51a7 100644 --- a/ext/standard/tests/file/rename_variation8-win32.phpt +++ b/ext/standard/tests/file/rename_variation8-win32.phpt @@ -46,7 +46,7 @@ rmdir(__DIR__."/rename_basic_dir1"); --EXPECTF-- *** Testing rename() on non-existing file *** -Warning: rename(%s/non_existent_file.tmp,%s/rename_variation8_new.tmp): The system cannot find the file specified (code: 2) in %s on line %d +Warning: rename(): The system cannot find the file specified (code: 2) in %s on line %d bool(false) bool(false) bool(false) @@ -58,7 +58,7 @@ bool(true) *** Testing rename() on non-existing directory *** -Warning: rename(%s/non_existent_dir,%s/rename_basic_dir2): The system cannot find the file specified (code: 2) in %s on line %d +Warning: rename(): The system cannot find the file specified (code: 2) in %s on line %d bool(false) bool(false) bool(false) diff --git a/ext/standard/tests/general_functions/bug44295-win.phpt b/ext/standard/tests/general_functions/bug44295-win.phpt index e5eb4c26a941..fd014cf9e9ac 100644 --- a/ext/standard/tests/general_functions/bug44295-win.phpt +++ b/ext/standard/tests/general_functions/bug44295-win.phpt @@ -23,7 +23,7 @@ try { ?> ==DONE== ---EXPECTF-- +--EXPECT-- before -in catch: DirectoryIterator::__construct(c:\not\exists\here): %s (code: 3) +in catch: DirectoryIterator::__construct(c:\not\exists\here): Failed to open directory: No such file or directory ==DONE== diff --git a/main/main.c b/main/main.c index 9e77c99a45df..aba833b43b90 100644 --- a/main/main.c +++ b/main/main.c @@ -1287,27 +1287,6 @@ PHPAPI ZEND_COLD void php_error_docref2(const char *docref, const char *param1, } /* }}} */ -#ifdef PHP_WIN32 -PHPAPI ZEND_COLD void php_win32_docref1_from_error(DWORD error, const char *param1) { - char *buf = php_win32_error_to_msg(error); - size_t buf_len; - - buf_len = strlen(buf); - if (buf_len >= 2) { - buf[buf_len - 1] = '\0'; - buf[buf_len - 2] = '\0'; - } - php_error_docref1(NULL, param1, E_WARNING, "%s (code: %lu)", buf, error); - php_win32_error_msg_free(buf); -} - -PHPAPI ZEND_COLD void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2) { - char *buf = php_win32_error_to_msg(error); - php_error_docref2(NULL, param1, param2, E_WARNING, "%s (code: %lu)", buf, error); - php_win32_error_msg_free(buf); -} -#endif - /* {{{ php_html_puts */ PHPAPI void php_html_puts(const char *str, size_t size) { diff --git a/main/php.h b/main/php.h index 77976a68c018..c7516bca3124 100644 --- a/main/php.h +++ b/main/php.h @@ -307,10 +307,6 @@ PHPAPI ZEND_COLD void php_error_docref1(const char *docref, const char *param1, PHP_ATTRIBUTE_FORMAT(printf, 4, 5); PHPAPI ZEND_COLD void php_error_docref2(const char *docref, const char *param1, const char *param2, int type, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 5, 6); -#ifdef PHP_WIN32 -PHPAPI ZEND_COLD void php_win32_docref1_from_error(DWORD error, const char *param1); -PHPAPI ZEND_COLD void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2); -#endif END_EXTERN_C() #define zenderror phperror diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 9a98873b5d33..a95ca0eab768 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -161,6 +161,20 @@ typedef struct { } php_stdio_stream_data; #define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd +#ifdef PHP_WIN32 +static ZEND_COLD void php_win32_stream_wrapper_warn_error( + php_stream_wrapper *wrapper, + php_stream_context *context, + int options, + zend_enum_StreamErrorCode code, + DWORD error +) { + char *buf = php_win32_error_to_msg(error); + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, code, "%s (code: %lu)", buf, error); + php_win32_error_msg_free(buf); +} +#endif + static int do_fstat(php_stdio_stream_data *d, int force) { if (!d->cached_fstat || (force && !d->no_forced_fstat)) { @@ -1130,7 +1144,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, const #ifdef PHP_WIN32 if (!dir) { - php_win32_docref1_from_error(GetLastError(), path); + php_win32_stream_wrapper_warn_error(wrapper, context, options, PHP_STREAM_EC(OpenFailed), GetLastError()); } if (dir && dir->finished) { @@ -1336,11 +1350,11 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url_from, strlen(url_from))) { - php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to); + php_win32_stream_wrapper_warn_error(wrapper, context, options, PHP_STREAM_EC(InvalidPath), ERROR_INVALID_NAME); return 0; } if (!php_win32_check_trailing_space(url_to, strlen(url_to))) { - php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to); + php_win32_stream_wrapper_warn_error(wrapper, context, options, PHP_STREAM_EC(InvalidPath), ERROR_INVALID_NAME); return 0; } #endif @@ -1421,7 +1435,7 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #endif #ifdef PHP_WIN32 - php_win32_docref2_from_error(GetLastError(), url_from, url_to); + php_win32_stream_wrapper_warn_error(wrapper, context, options, PHP_STREAM_EC(RenameFailed), GetLastError()); #else php_stream_wrapper_warn(wrapper, context, options, RenameFailed, From 0549c8e05457148ec88eff9d481b37235142d50c Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 16 Jul 2026 08:09:34 -0400 Subject: [PATCH 209/211] ext/filter: fix use-after-free in parse_str() with filter.default php_sapi_filter() re-exported PARSE_STRING results through Z_STRLEN/Z_STRVAL without checking the type php_zval_filter() left behind. A validation failure frees the string and stores IS_FALSE, so the macros read the freed zend_string; a successful FILTER_VALIDATE_INT stores IS_LONG, so they dereference the integer as a pointer. Convert with zval_get_tmp_string() before writing back. Closes GH-22765 --- ext/filter/filter.c | 9 ++++++--- .../tests/filter_default_parse_str.phpt | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 ext/filter/tests/filter_default_parse_str.phpt diff --git a/ext/filter/filter.c b/ext/filter/filter.c index c9d0f256b590..c65f9ac7994c 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -349,15 +349,18 @@ static unsigned int php_sapi_filter(int arg, const char *var, char **val, size_t } if (retval) { + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(&new_var, &tmp_str); if (new_val_len) { - *new_val_len = Z_STRLEN(new_var); + *new_val_len = ZSTR_LEN(str); } efree(*val); - if (Z_STRLEN(new_var)) { - *val = estrndup(Z_STRVAL(new_var), Z_STRLEN(new_var)); + if (ZSTR_LEN(str)) { + *val = estrndup(ZSTR_VAL(str), ZSTR_LEN(str)); } else { *val = estrdup(""); } + zend_tmp_string_release(tmp_str); zval_ptr_dtor(&new_var); } diff --git a/ext/filter/tests/filter_default_parse_str.phpt b/ext/filter/tests/filter_default_parse_str.phpt new file mode 100644 index 000000000000..71563060db36 --- /dev/null +++ b/ext/filter/tests/filter_default_parse_str.phpt @@ -0,0 +1,19 @@ +--TEST-- +filter.default with parse_str must not crash on non-string filter results +--EXTENSIONS-- +filter +--INI-- +filter.default=int +--FILE-- + +--EXPECTF-- +Deprecated: The filter.default ini setting is deprecated in %s on line %d +array(2) { + ["a"]=> + string(1) "1" + ["b"]=> + string(0) "" +} From e60d756f2ccf5754b4acb656c39d35b74f423b8c Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com> Date: Thu, 16 Jul 2026 18:43:40 +0200 Subject: [PATCH 210/211] zend_API: Add `_ex` variants of `zend_argument_error*` (#21829) These variants take the function as parameter instead of infering it from the call stack. This is useful in ZEND_TYPE_ASSERT, and will be used in PFA impl. --- UPGRADING.INTERNALS | 3 +++ Zend/zend_API.c | 46 +++++++++++++++++++++++++++++++++++------- Zend/zend_API.h | 5 ++++- Zend/zend_vm_def.h | 5 +++-- Zend/zend_vm_execute.h | 10 +++++---- ext/openssl/openssl.c | 2 +- 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 462e567f3f36..cca78d1d3b44 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -129,6 +129,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES zend_reflection_property_set_raw_value() to expose the functionality of ReflectionProperty::setRawValueWithoutLazyInitialization() and ReflectionProperty::setRawValue() to C extensions. + . zend_argument_error_variadic() now takes a new 'function' parameters. + . Added zend_argument_error_ex(), zend_argument_type_error_ex(), + zend_argument_value_error_ex(). ======================== 2. Build system changes diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e334b18fe1a4..72f4727eeddf 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -374,7 +374,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void) ); } -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va) /* {{{ */ +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic( + zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, va_list va) { zend_string *func_name; const char *arg_name; @@ -383,8 +384,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_en return; } - func_name = get_active_function_or_method_name(); - arg_name = get_active_function_arg_name(arg_num); + func_name = get_function_or_method_name(function); + arg_name = get_function_arg_name(function, arg_num); zend_vspprintf(&message, 0, format, va); zend_throw_error(error_ce, "%s(): Argument #%d%s%s%s %s", @@ -394,34 +395,65 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_en efree(message); zend_string_release(func_name); } -/* }}} */ + +ZEND_API ZEND_COLD void zend_argument_error_ex(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, ...) +{ + va_list va; + + va_start(va, format); + zend_argument_error_variadic(error_ce, function, arg_num, format, va); + va_end(va); +} ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...) /* {{{ */ { va_list va; + const zend_function *function = zend_active_function(); va_start(va, format); - zend_argument_error_variadic(error_ce, arg_num, format, va); + zend_argument_error_variadic(error_ce, function, arg_num, format, va); va_end(va); } /* }}} */ +ZEND_API ZEND_COLD void zend_argument_type_error_ex( + const zend_function *function, uint32_t arg_num, const char *format, ...) +{ + va_list va; + + va_start(va, format); + zend_argument_error_variadic(zend_ce_type_error, function, arg_num, format, va); + va_end(va); +} + ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...) /* {{{ */ { va_list va; + const zend_function *function = zend_active_function(); va_start(va, format); - zend_argument_error_variadic(zend_ce_type_error, arg_num, format, va); + zend_argument_error_variadic(zend_ce_type_error, function, arg_num, format, va); va_end(va); } /* }}} */ +ZEND_API ZEND_COLD void zend_argument_value_error_ex( + const zend_function *function, uint32_t arg_num, const char *format, ...) +{ + va_list va; + + va_start(va, format); + zend_argument_error_variadic(zend_ce_value_error, function, arg_num, format, va); + va_end(va); +} + ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format, ...) /* {{{ */ { va_list va; + const zend_function *function = zend_active_function(); va_start(va, format); - zend_argument_error_variadic(zend_ce_value_error, arg_num, format, va); + zend_argument_error_variadic(zend_ce_value_error, function, arg_num, format, va); va_end(va); } /* }}} */ diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 593be26788d7..bbfe64257744 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1579,10 +1579,13 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_nu ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_or_null_error(uint32_t num, char *error); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void); -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, va_list va); ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...); +ZEND_API ZEND_COLD void zend_argument_error_ex(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...); +ZEND_API ZEND_COLD void zend_argument_type_error_ex(const zend_function *function, uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format, ...); +ZEND_API ZEND_COLD void zend_argument_value_error_ex(const zend_function *function, uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num); ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_class_entry *old_ce); ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, const zend_class_entry *old_ce); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index ec9a97eeb254..61f89435843e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8921,9 +8921,10 @@ ZEND_VM_HOT_HANDLER(211, ZEND_TYPE_ASSERT, CONST, ANY, NUM) zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 34b2455ce439..bcf4df0c8e74 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6170,9 +6170,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_T zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } @@ -58859,9 +58860,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_TYPE_A zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index e11ad5c3428c..fa91f232bcdf 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -640,7 +640,7 @@ static void php_openssl_check_path_error(uint32_t arg_num, int type, const char va_start(va, format); if (type == E_ERROR) { - zend_argument_error_variadic(zend_ce_value_error, arg_num, format, va); + zend_argument_error_variadic(zend_ce_value_error, zend_active_function(), arg_num, format, va); } else { arg_name = get_active_function_arg_name(arg_num); php_verror(NULL, arg_name, type, format, va); From 228a294512353b1b11144606a994c787f3b6f991 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 5 Aug 2025 10:14:06 +0200 Subject: [PATCH 211/211] Partial function application RFC: https://wiki.php.net/rfc/partial_function_application_v2 Co-authored-by: Joe Watkins --- NEWS | 3 + UPGRADING | 2 + UPGRADING.INTERNALS | 4 + Zend/Optimizer/compact_literals.c | 5 + Zend/Optimizer/optimize_func_calls.c | 6 +- Zend/Optimizer/zend_call_graph.c | 2 + Zend/Optimizer/zend_inference.c | 1 + .../first_class_callable_non_unary_error.phpt | 10 - ...rst_class_callable_non_variadic_error.phpt | 10 - Zend/tests/partial_application/assert.phpt | 45 + .../partial_application/attributes_001.phpt | 88 ++ .../partial_application/attributes_002.phpt | 19 + .../partial_application/attributes_003.phpt | 16 + Zend/tests/partial_application/clone.phpt | 50 + .../compile_errors_001.phpt | 8 + .../compile_errors_002.phpt | 8 + .../compile_errors_003.phpt | 8 + .../compile_errors_004.phpt | 8 + .../compile_errors_005.phpt | 8 + .../compile_errors_006.phpt | 8 + .../tests/partial_application/deprecated.phpt | 14 + .../tests/partial_application/errors_001.phpt | 62 + .../tests/partial_application/errors_002.phpt | 15 + .../tests/partial_application/errors_003.phpt | 74 ++ .../tests/partial_application/errors_004.phpt | 16 + .../tests/partial_application/errors_005.phpt | 20 + .../tests/partial_application/errors_006.phpt | 45 + .../tests/partial_application/export_001.phpt | 14 + .../partial_application/extra_named.phpt | 49 + .../partial_application/function_name.phpt | 40 + Zend/tests/partial_application/fuzz_001.phpt | 21 + Zend/tests/partial_application/fuzz_003.phpt | 20 + Zend/tests/partial_application/fuzz_004.phpt | 19 + Zend/tests/partial_application/fuzz_005.phpt | 16 + Zend/tests/partial_application/fuzz_006.phpt | 16 + Zend/tests/partial_application/fuzz_007.phpt | 19 + Zend/tests/partial_application/hook.phpt | 25 + .../instance_polymorphism.phpt | 43 + Zend/tests/partial_application/invokable.phpt | 51 + Zend/tests/partial_application/jit_001.phpt | 9 + Zend/tests/partial_application/magic_001.phpt | 81 ++ Zend/tests/partial_application/magic_002.phpt | 72 + Zend/tests/partial_application/magic_003.phpt | 13 + Zend/tests/partial_application/magic_004.phpt | 14 + Zend/tests/partial_application/magic_005.phpt | 29 + Zend/tests/partial_application/magic_006.phpt | 20 + .../named_placeholders.phpt | 121 ++ .../partial_application/never_cache_001.phpt | 30 + .../non_dynamic_call_funcs.phpt | 46 + Zend/tests/partial_application/observers.phpt | 34 + .../optional_placeholder.phpt | 77 ++ .../partial_application/param_reorder.phpt | 202 +++ .../pipe_optimization_001.phpt | 47 + .../pipe_optimization_002.phpt | 51 + .../pipe_optimization_003.phpt | 51 + .../pipe_optimization_004.phpt | 88 ++ .../pipe_optimization_005.phpt | 51 + .../pipe_optimization_006.phpt | 55 + .../pipe_optimization_007.phpt | 88 ++ .../pipe_optimization_008.phpt | 99 ++ .../pipe_optimization_009.phpt | 102 ++ .../pipe_optimization_010.phpt | 58 + .../pipe_optimization_011.phpt | 98 ++ .../pipe_optimization_012.phpt | 52 + .../pipe_optimization_013.phpt | 87 ++ .../pipe_optimization_014.phpt | 68 + .../pipe_optimization_015.phpt | 55 + Zend/tests/partial_application/preloading.inc | 13 + .../tests/partial_application/preloading.phpt | 19 + .../partial_application/rebinding_001.phpt | 59 + .../partial_application/rebinding_002.phpt | 45 + .../partial_application/rebinding_003.phpt | 69 + .../recorded_warnings.phpt | 14 + .../partial_application/references_001.phpt | 24 + .../partial_application/references_002.phpt | 24 + .../partial_application/references_003.phpt | 20 + .../partial_application/references_004.phpt | 42 + .../partial_application/references_005.phpt | 26 + .../partial_application/reflection_001.phpt | 48 + .../partial_application/reflection_002.phpt | 57 + .../partial_application/reflection_003.phpt | 43 + .../partial_application/reflection_004.phpt | 16 + .../partial_application/reflection_005.phpt | 16 + .../relative_return_types.phpt | 148 +++ .../partial_application/return_type.phpt | 20 + .../partial_application/rfc_examples.inc | 76 ++ .../rfc_examples_const_expr.phpt | 25 + .../rfc_examples_debug.phpt | 25 + .../rfc_examples_errors.phpt | 34 + .../rfc_examples_eval_order.phpt | 30 + .../rfc_examples_extra_args.phpt | 54 + .../rfc_examples_incompatible_functions.phpt | 14 + .../rfc_examples_magic_methods.phpt | 47 + .../rfc_examples_overview.phpt | 44 + .../rfc_examples_scoping.phpt | 113 ++ .../rfc_examples_semantics.phpt | 30 + .../rfc_examples_semantics_examples.phpt | 217 +++ .../static_method_001.phpt | 18 + .../partial_application/static_pfa_001.phpt | 45 + .../partial_application/static_pfa_002.phpt | 35 + .../partial_application/static_pfa_003.phpt | 30 + .../partial_application/static_pfa_004.phpt | 44 + .../partial_application/static_pfa_005.phpt | 34 + .../static_polymorphism.phpt | 43 + .../partial_application/static_vars_001.phpt | 21 + .../partial_application/static_vars_002.phpt | 22 + .../partial_application/static_vars_003.phpt | 25 + .../superfluous_args_are_forwarded.phpt | 44 + Zend/tests/partial_application/this.phpt | 20 + .../variation_call_001.phpt | 31 + .../variation_closure_001.phpt | 40 + .../variation_closure_002.phpt | 28 + .../variation_closure_003.phpt | 46 + .../variation_debug_001.phpt | 40 + .../variation_debug_002.phpt | 49 + .../partial_application/variation_ex_001.phpt | 14 + .../partial_application/variation_gc_001.phpt | 19 + .../partial_application/variation_gc_002.phpt | 11 + .../partial_application/variation_gc_003.phpt | 15 + .../variation_invoke_001.phpt | 21 + .../variation_nocall_001.phpt | 12 + .../variation_nocall_002.phpt | 30 + .../variation_parent_001.phpt | 76 ++ .../variation_scope_001.phpt | 18 + .../variation_strict_001.phpt | 20 + .../variation_variadics_001.phpt | 31 + .../variation_variadics_002.phpt | 25 + .../variation_variadics_004.phpt | 45 + .../variation_variadics_006.phpt | 17 + .../variation_variadics_007.phpt | 14 + .../variation_variadics_008.phpt | 16 + Zend/zend_ast.c | 10 + Zend/zend_ast.h | 3 + Zend/zend_closures.c | 85 +- Zend/zend_closures.h | 1 + Zend/zend_compile.c | 327 ++++- Zend/zend_compile.h | 4 +- Zend/zend_execute.c | 16 +- Zend/zend_execute.h | 2 + Zend/zend_execute_API.c | 4 + Zend/zend_globals.h | 1 + Zend/zend_language_scanner.l | 83 +- Zend/zend_partial.c | 1163 +++++++++++++++++ Zend/zend_partial.h | 35 + Zend/zend_string.h | 5 + Zend/zend_types.h | 2 + Zend/zend_vm_def.h | 63 + Zend/zend_vm_execute.h | 487 +++++-- Zend/zend_vm_execute.skl | 1 + Zend/zend_vm_handlers.h | 940 ++++++------- Zend/zend_vm_opcodes.c | 8 +- Zend/zend_vm_opcodes.h | 4 +- configure.ac | 1 + ext/opcache/ZendAccelerator.c | 199 +++ ext/opcache/ZendAccelerator.h | 10 + ext/opcache/jit/zend_jit.c | 3 + ext/opcache/jit/zend_jit_ir.c | 2 +- ext/opcache/jit/zend_jit_vm_helpers.c | 3 +- .../array_map_foreach_optimization_006.phpt | 84 ++ .../array_map_foreach_optimization_007.phpt | 109 ++ win32/build/config.w32 | 2 +- 161 files changed, 8057 insertions(+), 697 deletions(-) delete mode 100644 Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt delete mode 100644 Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt create mode 100644 Zend/tests/partial_application/assert.phpt create mode 100644 Zend/tests/partial_application/attributes_001.phpt create mode 100644 Zend/tests/partial_application/attributes_002.phpt create mode 100644 Zend/tests/partial_application/attributes_003.phpt create mode 100644 Zend/tests/partial_application/clone.phpt create mode 100644 Zend/tests/partial_application/compile_errors_001.phpt create mode 100644 Zend/tests/partial_application/compile_errors_002.phpt create mode 100644 Zend/tests/partial_application/compile_errors_003.phpt create mode 100644 Zend/tests/partial_application/compile_errors_004.phpt create mode 100644 Zend/tests/partial_application/compile_errors_005.phpt create mode 100644 Zend/tests/partial_application/compile_errors_006.phpt create mode 100644 Zend/tests/partial_application/deprecated.phpt create mode 100644 Zend/tests/partial_application/errors_001.phpt create mode 100644 Zend/tests/partial_application/errors_002.phpt create mode 100644 Zend/tests/partial_application/errors_003.phpt create mode 100644 Zend/tests/partial_application/errors_004.phpt create mode 100644 Zend/tests/partial_application/errors_005.phpt create mode 100644 Zend/tests/partial_application/errors_006.phpt create mode 100644 Zend/tests/partial_application/export_001.phpt create mode 100644 Zend/tests/partial_application/extra_named.phpt create mode 100644 Zend/tests/partial_application/function_name.phpt create mode 100644 Zend/tests/partial_application/fuzz_001.phpt create mode 100644 Zend/tests/partial_application/fuzz_003.phpt create mode 100644 Zend/tests/partial_application/fuzz_004.phpt create mode 100644 Zend/tests/partial_application/fuzz_005.phpt create mode 100644 Zend/tests/partial_application/fuzz_006.phpt create mode 100644 Zend/tests/partial_application/fuzz_007.phpt create mode 100644 Zend/tests/partial_application/hook.phpt create mode 100644 Zend/tests/partial_application/instance_polymorphism.phpt create mode 100644 Zend/tests/partial_application/invokable.phpt create mode 100644 Zend/tests/partial_application/jit_001.phpt create mode 100644 Zend/tests/partial_application/magic_001.phpt create mode 100644 Zend/tests/partial_application/magic_002.phpt create mode 100644 Zend/tests/partial_application/magic_003.phpt create mode 100644 Zend/tests/partial_application/magic_004.phpt create mode 100644 Zend/tests/partial_application/magic_005.phpt create mode 100644 Zend/tests/partial_application/magic_006.phpt create mode 100644 Zend/tests/partial_application/named_placeholders.phpt create mode 100644 Zend/tests/partial_application/never_cache_001.phpt create mode 100644 Zend/tests/partial_application/non_dynamic_call_funcs.phpt create mode 100644 Zend/tests/partial_application/observers.phpt create mode 100644 Zend/tests/partial_application/optional_placeholder.phpt create mode 100644 Zend/tests/partial_application/param_reorder.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_001.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_002.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_003.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_004.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_005.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_006.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_007.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_008.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_009.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_010.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_011.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_012.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_013.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_014.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_015.phpt create mode 100644 Zend/tests/partial_application/preloading.inc create mode 100644 Zend/tests/partial_application/preloading.phpt create mode 100644 Zend/tests/partial_application/rebinding_001.phpt create mode 100644 Zend/tests/partial_application/rebinding_002.phpt create mode 100644 Zend/tests/partial_application/rebinding_003.phpt create mode 100644 Zend/tests/partial_application/recorded_warnings.phpt create mode 100644 Zend/tests/partial_application/references_001.phpt create mode 100644 Zend/tests/partial_application/references_002.phpt create mode 100644 Zend/tests/partial_application/references_003.phpt create mode 100644 Zend/tests/partial_application/references_004.phpt create mode 100644 Zend/tests/partial_application/references_005.phpt create mode 100644 Zend/tests/partial_application/reflection_001.phpt create mode 100644 Zend/tests/partial_application/reflection_002.phpt create mode 100644 Zend/tests/partial_application/reflection_003.phpt create mode 100644 Zend/tests/partial_application/reflection_004.phpt create mode 100644 Zend/tests/partial_application/reflection_005.phpt create mode 100644 Zend/tests/partial_application/relative_return_types.phpt create mode 100644 Zend/tests/partial_application/return_type.phpt create mode 100644 Zend/tests/partial_application/rfc_examples.inc create mode 100644 Zend/tests/partial_application/rfc_examples_const_expr.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_debug.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_errors.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_eval_order.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_extra_args.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_magic_methods.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_overview.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_scoping.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_semantics.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_semantics_examples.phpt create mode 100644 Zend/tests/partial_application/static_method_001.phpt create mode 100644 Zend/tests/partial_application/static_pfa_001.phpt create mode 100644 Zend/tests/partial_application/static_pfa_002.phpt create mode 100644 Zend/tests/partial_application/static_pfa_003.phpt create mode 100644 Zend/tests/partial_application/static_pfa_004.phpt create mode 100644 Zend/tests/partial_application/static_pfa_005.phpt create mode 100644 Zend/tests/partial_application/static_polymorphism.phpt create mode 100644 Zend/tests/partial_application/static_vars_001.phpt create mode 100644 Zend/tests/partial_application/static_vars_002.phpt create mode 100644 Zend/tests/partial_application/static_vars_003.phpt create mode 100644 Zend/tests/partial_application/superfluous_args_are_forwarded.phpt create mode 100644 Zend/tests/partial_application/this.phpt create mode 100644 Zend/tests/partial_application/variation_call_001.phpt create mode 100644 Zend/tests/partial_application/variation_closure_001.phpt create mode 100644 Zend/tests/partial_application/variation_closure_002.phpt create mode 100644 Zend/tests/partial_application/variation_closure_003.phpt create mode 100644 Zend/tests/partial_application/variation_debug_001.phpt create mode 100644 Zend/tests/partial_application/variation_debug_002.phpt create mode 100644 Zend/tests/partial_application/variation_ex_001.phpt create mode 100644 Zend/tests/partial_application/variation_gc_001.phpt create mode 100644 Zend/tests/partial_application/variation_gc_002.phpt create mode 100644 Zend/tests/partial_application/variation_gc_003.phpt create mode 100644 Zend/tests/partial_application/variation_invoke_001.phpt create mode 100644 Zend/tests/partial_application/variation_nocall_001.phpt create mode 100644 Zend/tests/partial_application/variation_nocall_002.phpt create mode 100644 Zend/tests/partial_application/variation_parent_001.phpt create mode 100644 Zend/tests/partial_application/variation_scope_001.phpt create mode 100644 Zend/tests/partial_application/variation_strict_001.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_001.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_002.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_004.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_006.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_007.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_008.phpt create mode 100644 Zend/zend_partial.c create mode 100644 Zend/zend_partial.h create mode 100644 ext/standard/tests/array/array_map_foreach_optimization_006.phpt create mode 100644 ext/standard/tests/array/array_map_foreach_optimization_007.phpt diff --git a/NEWS b/NEWS index cb10b3be0b6d..918624b868b2 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0alpha3 +- Core: + . Implemented partial function application RFC. (Arnaud) + - GMP: . Fixed GMP power and shift operators to reject GMP right operands outside the unsigned long range instead of silently truncating them. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 26e50b215f77..145f2239b56b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -235,6 +235,8 @@ PHP 8.6 UPGRADE NOTES RFC: https://wiki.php.net/rfc/debugable-enums . #[\Override] can now be applied to class constants, including enum cases. RFC: https://wiki.php.net/rfc/override_constants + . Implemented partial function application + RFC: https://wiki.php.net/rfc/partial_function_application_v2 - Curl: . curl_getinfo() return array now includes a new size_delivered key, which diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index cca78d1d3b44..fd6d009aaffc 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -132,6 +132,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES . zend_argument_error_variadic() now takes a new 'function' parameters. . Added zend_argument_error_ex(), zend_argument_type_error_ex(), zend_argument_value_error_ex(). + . Added zend_ast_dup(). + . Added zend_compile_ast(). + . Added zend_check_type_ex(). + . Added zend_create_partial_closure(). ======================== 2. Build system changes diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index cf74dd8fc147..a4ecb19c85ef 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -733,6 +733,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_SEND_VAR_NO_REF_EX: case ZEND_SEND_REF: case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_PLACEHOLDER: case ZEND_CHECK_FUNC_ARG: if (opline->op2_type == IS_CONST) { opline->result.num = cache_size; @@ -745,6 +746,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += sizeof(void *); } break; + case ZEND_CALLABLE_CONVERT_PARTIAL: + opline->op1.num = cache_size; + cache_size += 2 * sizeof(void *); + break; } opline++; } diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 69c371207ddc..05cdce4fc4cf 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -191,6 +191,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: call--; if (call_stack[call].func && call_stack[call].opline) { zend_op *fcall = call_stack[call].opline; @@ -223,13 +224,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) * At this point we also know whether or not the result of * the DO opcode is used, allowing to optimize calls to * ZEND_ACC_NODISCARD functions. */ - if (opline->opcode != ZEND_CALLABLE_CONVERT) { + if (opline->opcode != ZEND_CALLABLE_CONVERT && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) { opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline)); } if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level) && call_stack[call].try_inline - && opline->opcode != ZEND_CALLABLE_CONVERT) { + && opline->opcode != ZEND_CALLABLE_CONVERT + && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) { zend_try_inline_call(op_array, fcall, opline, call_stack[call].func); } } diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c index bb80e21a2465..b67f57cf041d 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -124,6 +124,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: func_info->flags |= ZEND_FUNC_HAS_CALLS; if (call_info) { call_info->caller_call_opline = opline; @@ -140,6 +141,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_NO_REF_EX: case ZEND_SEND_USER: + case ZEND_SEND_PLACEHOLDER: if (call_info) { if (opline->op2_type == IS_CONST) { call_info->named_args = true; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 05d33d3d75fb..2e6cc70ec254 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3903,6 +3903,7 @@ static zend_always_inline zend_result _zend_update_type_info( } break; case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: UPDATE_SSA_TYPE(MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN, ssa_op->result_def); UPDATE_SSA_OBJ_TYPE(zend_ce_closure, /* is_instanceof */ false, ssa_op->result_def); break; diff --git a/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt deleted file mode 100644 index 74e36a9ad0df..000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: more than one argument ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt deleted file mode 100644 index efbd13b7593b..000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: non-variadic placeholder ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/partial_application/assert.phpt b/Zend/tests/partial_application/assert.phpt new file mode 100644 index 000000000000..166cc5022eb2 --- /dev/null +++ b/Zend/tests/partial_application/assert.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA of assert() behaves like a dynamic call to assert() +--FILE-- +getMessage(), "\n"; +} + +try { + echo "# Dynamic call:\n"; + (function ($f) { $f(false); })('assert'); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +try { + echo "# PFA call:\n"; + $f = assert(?); + $f(false); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +try { + echo "# Upper-case assert():\n"; + $f = ASSERT(?); + $f(false); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +?> +--EXPECT-- +# Static call: +AssertionError: assert(false) +# Dynamic call: +AssertionError: (no message) +# PFA call: +AssertionError: (no message) +# Upper-case assert(): +AssertionError: (no message) diff --git a/Zend/tests/partial_application/attributes_001.phpt b/Zend/tests/partial_application/attributes_001.phpt new file mode 100644 index 000000000000..827ad41321ec --- /dev/null +++ b/Zend/tests/partial_application/attributes_001.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA inherits NoDiscard and SensitiveParameter attributes +--FILE-- +getAttributes()); + + foreach ($r->getParameters() as $i => $p) { + echo "Parameter $i:\n"; + var_dump($p->getAttributes()); + } +} + +echo "# Orig attributes:\n"; + +dump_attributes('f'); + +$f = f(1, ?, ?, ...); + +echo "# PFA attributes:\n"; + +dump_attributes($f); + +?> +--EXPECTF-- +# Orig attributes: +array(2) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(9) "NoDiscard" + } + [1]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(4) "Test" + } +} +Parameter 0: +array(0) { +} +Parameter 1: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(18) "SensitiveParameter" + } +} +Parameter 2: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(4) "Test" + } +} +# PFA attributes: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(9) "NoDiscard" + } +} +Parameter 0: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(18) "SensitiveParameter" + } +} +Parameter 1: +array(0) { +} +Parameter 2: +array(0) { +} diff --git a/Zend/tests/partial_application/attributes_002.phpt b/Zend/tests/partial_application/attributes_002.phpt new file mode 100644 index 000000000000..be1f1612f938 --- /dev/null +++ b/Zend/tests/partial_application/attributes_002.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA preserves #[SensitiveParameter] +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): f(1, Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#1 %s(%d): {closure:pfa:%s:7}(Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/attributes_003.phpt b/Zend/tests/partial_application/attributes_003.phpt new file mode 100644 index 000000000000..d113d02eb8f7 --- /dev/null +++ b/Zend/tests/partial_application/attributes_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA preserves #[NoDiscard] +--FILE-- + +--EXPECTF-- +Warning: The return value of function {closure:%s}() should either be used or intentionally ignored by casting it as (void) in %s on line 7 diff --git a/Zend/tests/partial_application/clone.phpt b/Zend/tests/partial_application/clone.phpt new file mode 100644 index 000000000000..f778d776b717 --- /dev/null +++ b/Zend/tests/partial_application/clone.phpt @@ -0,0 +1,50 @@ +--TEST-- +clone() can be partially applied +--FILE-- + 7])); + +$clone = clone(?, ['a' => 8]); +var_dump($clone(new C(9, 10))); + +?> +--EXPECTF-- +object(C)#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(2) +} +object(C)#%d (2) { + ["a"]=> + int(3) + ["b"]=> + int(4) +} +object(C)#%d (2) { + ["a"]=> + int(7) + ["b"]=> + int(6) +} +object(C)#%d (2) { + ["a"]=> + int(8) + ["b"]=> + int(10) +} diff --git a/Zend/tests/partial_application/compile_errors_001.phpt b/Zend/tests/partial_application/compile_errors_001.phpt new file mode 100644 index 000000000000..f08495a1f1e5 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_001.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: multiple variadic placeholders +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder may only appear once in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_002.phpt b/Zend/tests/partial_application/compile_errors_002.phpt new file mode 100644 index 000000000000..b6a2073a8363 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_002.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_003.phpt b/Zend/tests/partial_application/compile_errors_003.phpt new file mode 100644 index 000000000000..26ff8435111b --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_003.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: placeholders can not appear after named args +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_004.phpt b/Zend/tests/partial_application/compile_errors_004.phpt new file mode 100644 index 000000000000..ac7ec163c5da --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_004.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after named args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_005.phpt b/Zend/tests/partial_application/compile_errors_005.phpt new file mode 100644 index 000000000000..30e4aa12b488 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_005.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after positional args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_006.phpt b/Zend/tests/partial_application/compile_errors_006.phpt new file mode 100644 index 000000000000..8549c671e906 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: can not use unpacking in PFA, including with variadic placeholders +--FILE-- + "bar"], ...); +?> +--EXPECTF-- +Fatal error: Cannot combine partial application and unpacking in %s on line %d diff --git a/Zend/tests/partial_application/deprecated.phpt b/Zend/tests/partial_application/deprecated.phpt new file mode 100644 index 000000000000..5a6280347cfc --- /dev/null +++ b/Zend/tests/partial_application/deprecated.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFAs may emit deprecation warnings +--FILE-- + +--EXPECTF-- +Deprecated: Function f() is deprecated in %s on line %d diff --git a/Zend/tests/partial_application/errors_001.phpt b/Zend/tests/partial_application/errors_001.phpt new file mode 100644 index 000000000000..2d5348cb28af --- /dev/null +++ b/Zend/tests/partial_application/errors_001.phpt @@ -0,0 +1,62 @@ +--TEST-- +PFA errors: PFA instantiation follows the usual argument count validation +--FILE-- +getMessage()); +} + +try { + foo(?, ?, ?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + $c = new C(); + $c->f(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + property_exists(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + usleep(?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + foo(?, ?, ?, ?, ...); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +/* It is allowed to specify less than the number of required params, when there + * is a variadic placeholder */ +foo(?, ...); + +?> +--EXPECT-- +ArgumentCountError: Partial application of foo() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given +ArgumentCountError: Partial application of C::f() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of property_exists() expects exactly 2 arguments, 1 given +ArgumentCountError: Partial application of usleep() expects at most 1 arguments, 2 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given diff --git a/Zend/tests/partial_application/errors_002.phpt b/Zend/tests/partial_application/errors_002.phpt new file mode 100644 index 000000000000..0132d8873e2a --- /dev/null +++ b/Zend/tests/partial_application/errors_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA errors: named parameter that resolve to the position of a placeholder is an error +--FILE-- +getMessage()); +} +?> +--EXPECT-- +Error: Named parameter $a overwrites previous placeholder diff --git a/Zend/tests/partial_application/errors_003.phpt b/Zend/tests/partial_application/errors_003.phpt new file mode 100644 index 000000000000..85ecf398fa1b --- /dev/null +++ b/Zend/tests/partial_application/errors_003.phpt @@ -0,0 +1,74 @@ +--TEST-- +PFA errors: PFA call follows the usual argument count validation +--FILE-- +getMessage()); +} + +$foo = foo(?, ?); + +try { + $foo(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar = bar(?, ?, ...); + +try { + $bar(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +class Foo { + public function bar($a, ...$b) {} +} + +$foo = new Foo; + +$bar = $foo->bar(?); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$repeat = str_repeat('a', ...); + +try { + $repeat(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$usleep = usleep(?); + +try { + $usleep(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$usleep(1, 2); + +?> +--EXPECTF-- +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 3 expected +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected diff --git a/Zend/tests/partial_application/errors_004.phpt b/Zend/tests/partial_application/errors_004.phpt new file mode 100644 index 000000000000..e5e1432753b2 --- /dev/null +++ b/Zend/tests/partial_application/errors_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA errors: not specifying a required param is an error +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: f(): Argument #2 ($b) not passed in %s:6 +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/errors_005.phpt b/Zend/tests/partial_application/errors_005.phpt new file mode 100644 index 000000000000..23fc2a75dff3 --- /dev/null +++ b/Zend/tests/partial_application/errors_005.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA errors: Can not fetch default parameter value for Closure::__invoke() +--FILE-- +__invoke(0, 0, ?); +echo "No error\n"; + +try { + $g = $f->__invoke(0, 0, ...); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +No error +ArgumentCountError: Closure::__invoke(): Argument #3 ($c) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/errors_006.phpt b/Zend/tests/partial_application/errors_006.phpt new file mode 100644 index 000000000000..9534bb40ee54 --- /dev/null +++ b/Zend/tests/partial_application/errors_006.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA errors: Some internal function parameters have UNKNOWN default value +--FILE-- + array_keys($array, ???, true) + $f = array_keys(?, strict: true); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + // fn (array $array, mixed $filter_value = ???) => array_keys($array, $filter_value, true) + $f = array_keys(?, strict: true, ...); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +array(1) { + [0]=> + int(1) +} +array(0) { +} +array(1) { + [0]=> + int(1) +} +array(0) { +} +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/export_001.phpt b/Zend/tests/partial_application/export_001.phpt new file mode 100644 index 000000000000..b48146bcc65a --- /dev/null +++ b/Zend/tests/partial_application/export_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA AST export +--INI-- +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +AssertionError: assert(0 && foo(?) && foo(new stdClass(), bar: 1, ...)) diff --git a/Zend/tests/partial_application/extra_named.phpt b/Zend/tests/partial_application/extra_named.phpt new file mode 100644 index 000000000000..4dd80cfa0127 --- /dev/null +++ b/Zend/tests/partial_application/extra_named.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA extra named parameters are forwarded to the actual function +--FILE-- + +--EXPECT-- +array(3) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" +} diff --git a/Zend/tests/partial_application/function_name.phpt b/Zend/tests/partial_application/function_name.phpt new file mode 100644 index 000000000000..68d2ccf84871 --- /dev/null +++ b/Zend/tests/partial_application/function_name.phpt @@ -0,0 +1,40 @@ +--TEST-- +Partial application function name +--FILE-- +getName()); + } +} + +function f() { + echo "# From a function:\n"; + var_dump((new ReflectionFunction(g(?)))->getName()); + + echo "# Declared on same line:\n"; + [$a, $b] = [g(?), g(?)]; + var_dump((new ReflectionFunction($a))->getName(), (new ReflectionFunction($b))->getName()); +} + +f(); +C::m(); + +echo "# From global scope:\n"; +var_dump((new ReflectionFunction(g(?)))->getName()); + +?> +--EXPECTF-- +# From a function: +string(20) "{closure:pfa:f():14}" +# Declared on same line: +string(20) "{closure:pfa:f():17}" +string(20) "{closure:pfa:f():17}" +# From a method: +string(22) "{closure:pfa:C::m():8}" +# From global scope: +string(%d) "{closure:pfa:%sfunction_name.php:25}" diff --git a/Zend/tests/partial_application/fuzz_001.phpt b/Zend/tests/partial_application/fuzz_001.phpt new file mode 100644 index 000000000000..790162ba6a36 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Closure application fuzz 001 +--FILE-- + +--EXPECTF-- +Closure [ function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/fuzz_003.phpt b/Zend/tests/partial_application/fuzz_003.phpt new file mode 100644 index 000000000000..6e9d583fda99 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +Closure application fuzz 003 +--FILE-- +method(1, ...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/fuzz_004.phpt b/Zend/tests/partial_application/fuzz_004.phpt new file mode 100644 index 000000000000..ea005304a3af --- /dev/null +++ b/Zend/tests/partial_application/fuzz_004.phpt @@ -0,0 +1,19 @@ +--TEST-- +Closure application fuzz 004 +--FILE-- +__invoke(UNDEFINED); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/fuzz_005.phpt b/Zend/tests/partial_application/fuzz_005.phpt new file mode 100644 index 000000000000..ea04862d8399 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 005 +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/partial_application/fuzz_006.phpt b/Zend/tests/partial_application/fuzz_006.phpt new file mode 100644 index 000000000000..26ec6e3e4dd1 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 006 +--FILE-- + +--EXPECT-- +int(1) diff --git a/Zend/tests/partial_application/fuzz_007.phpt b/Zend/tests/partial_application/fuzz_007.phpt new file mode 100644 index 000000000000..123ce29d8b18 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_007.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA fuzz 007 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/hook.phpt b/Zend/tests/partial_application/hook.phpt new file mode 100644 index 000000000000..6402c5d01e74 --- /dev/null +++ b/Zend/tests/partial_application/hook.phpt @@ -0,0 +1,25 @@ +--TEST-- +Parent property hook call can not be partially applied +--FILE-- +a = 1; + +?> +--EXPECTF-- +Fatal error: Cannot create Closure for parent property hook call in %s on line %d diff --git a/Zend/tests/partial_application/instance_polymorphism.phpt b/Zend/tests/partial_application/instance_polymorphism.phpt new file mode 100644 index 000000000000..59fb5795d60f --- /dev/null +++ b/Zend/tests/partial_application/instance_polymorphism.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA: instance polymorphism +--FILE-- +m(?); + } +} + +class C extends P { + public function m(string|array $b): void { + echo __METHOD__, PHP_EOL; + var_dump($b); + } +} + +for ($i = 0; $i < 2; $i++) { + (new P())->get()(a: 'a'); + (new C())->get()(b: []); +} + +?> +--EXPECT-- +P::m +string(1) "a" +C::m +array(0) { +} +P::m +string(1) "a" +C::m +array(0) { +} diff --git a/Zend/tests/partial_application/invokable.phpt b/Zend/tests/partial_application/invokable.phpt new file mode 100644 index 000000000000..c21030e7733a --- /dev/null +++ b/Zend/tests/partial_application/invokable.phpt @@ -0,0 +1,51 @@ +--TEST-- +__invoke() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 11 - 11 + + - Parameters [2] { + Parameter #0 [ int $a ] + Parameter #1 [ object $b ] + } + - Return [ C ] +} + +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 15 - 15 + + - Bound Variables [1] { + Variable #0 [ $b ] + } + + - Parameters [1] { + Parameter #0 [ int $a ] + } + - Return [ C ] +} + +int(1) +object(stdClass)#%d (0) { +} diff --git a/Zend/tests/partial_application/jit_001.phpt b/Zend/tests/partial_application/jit_001.phpt new file mode 100644 index 000000000000..84aefa05ab28 --- /dev/null +++ b/Zend/tests/partial_application/jit_001.phpt @@ -0,0 +1,9 @@ +--TEST-- +PFA JIT 001 +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt new file mode 100644 index 000000000000..bdcc10675785 --- /dev/null +++ b/Zend/tests/partial_application/magic_001.phpt @@ -0,0 +1,81 @@ +--TEST-- +__call() can be partially applied +--FILE-- +method(?); + +echo (string) new ReflectionFunction($bar); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar(1, 2); +$bar(1); + +$bar = $foo->method(?, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(10); +$bar(10, 20); + +$bar = $foo->method(new Foo, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(100); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s 12 - 12 + + - Parameters [1] { + Parameter #0 [ mixed $arguments0 ] + } +} +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +Foo::method +int(1) +Foo::method +int(1) +Closure [ public method {closure:%s:%d} ] { + @@ %s 25 - 25 + + - Parameters [2] { + Parameter #0 [ mixed $arguments0 ] + Parameter #1 [ mixed ...$arguments ] + } +} +Foo::method +int(10) +Foo::method +int(10) +int(20) +Closure [ public method {closure:%s:%d} ] { + @@ %s 32 - 32 + + - Bound Variables [1] { + Variable #0 [ $arguments2 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$arguments ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_002.phpt b/Zend/tests/partial_application/magic_002.phpt new file mode 100644 index 000000000000..1d5efaea7c63 --- /dev/null +++ b/Zend/tests/partial_application/magic_002.phpt @@ -0,0 +1,72 @@ +--TEST-- +__callStatic() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ static public method {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ mixed $arguments0 ] + } +} +Foo::method +int(1) +Foo::method +int(1) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 17 - 17 + + - Parameters [2] { + Parameter #0 [ mixed $arguments0 ] + Parameter #1 [ mixed ...$arguments ] + } +} +Foo::method +int(10) +Foo::method +int(10) +int(20) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 24 - 24 + + - Bound Variables [1] { + Variable #0 [ $arguments2 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$arguments ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_003.phpt b/Zend/tests/partial_application/magic_003.phpt new file mode 100644 index 000000000000..242403ea38fc --- /dev/null +++ b/Zend/tests/partial_application/magic_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA magic trampoline release (result unused) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_004.phpt b/Zend/tests/partial_application/magic_004.phpt new file mode 100644 index 000000000000..8ef93a91ce83 --- /dev/null +++ b/Zend/tests/partial_application/magic_004.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA magic trampoline release (result used) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt new file mode 100644 index 000000000000..e70ed90b1767 --- /dev/null +++ b/Zend/tests/partial_application/magic_005.phpt @@ -0,0 +1,29 @@ +--TEST-- +PFA magic null ptr deref in arginfo +--FILE-- +method(?); +var_dump($bar); +?> +--EXPECTF-- +object(Closure)#%d (%d) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%smagic_005.php" + ["line"]=> + int(7) + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$arguments0"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/magic_006.phpt b/Zend/tests/partial_application/magic_006.phpt new file mode 100644 index 000000000000..29564d8b615a --- /dev/null +++ b/Zend/tests/partial_application/magic_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA magic varargs +--FILE-- +method(1,...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/named_placeholders.phpt b/Zend/tests/partial_application/named_placeholders.phpt new file mode 100644 index 000000000000..19ce8cc03860 --- /dev/null +++ b/Zend/tests/partial_application/named_placeholders.phpt @@ -0,0 +1,121 @@ +--TEST-- +PFA supports named placeholders +--FILE-- +getMessage(), "\n"; +} + +echo "# Case 4\n"; + +function bar($a = 1, $b = 2, ...$c) { + var_dump($a, $b, $c); +} + +$d = bar(b: ?, ...); + +echo (string) new ReflectionFunction($d); + +$d(new B, new A, new C); + +echo "# Case 5\n"; + +try { + $d = bar(?, a: ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Case 6\n"; + +try { + $d = bar(c: ?, ...); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# Case 1 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 13 - 13 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +# Case 2 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 21 - 21 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +# Case 3 +ArgumentCountError: {closure:pfa:%s:29}(): Argument #1 ($a) not passed +# Case 4 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 42 - 42 + + - Parameters [3] { + Parameter #0 [ $b ] + Parameter #1 [ $a = 1 ] + Parameter #2 [ ...$c ] + } +} +object(A)#%d (0) { +} +object(B)#%d (0) { +} +array(1) { + [0]=> + object(C)#%d (0) { + } +} +# Case 5 +Error: Named parameter $a overwrites previous placeholder +# Case 6 +Error: Cannot use named placeholder for unknown or variadic parameter $c diff --git a/Zend/tests/partial_application/never_cache_001.phpt b/Zend/tests/partial_application/never_cache_001.phpt new file mode 100644 index 000000000000..3c9e8a173e51 --- /dev/null +++ b/Zend/tests/partial_application/never_cache_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Inline cache can not be used for ZEND_ACC_NEVER_CACHE functions +--FILE-- +__invoke(?)); +} + +?> +--EXPECT-- +string(1) "A" +string(1) "B" +string(1) "A" +string(1) "B" diff --git a/Zend/tests/partial_application/non_dynamic_call_funcs.phpt b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt new file mode 100644 index 000000000000..db25ef3dcc93 --- /dev/null +++ b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt @@ -0,0 +1,46 @@ +--TEST-- +Functions that can not be called dynamically, can not be partially applied +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECT-- +Error: Cannot call func_get_arg() dynamically +Error: Cannot call compact() dynamically +Error: Cannot call extract() dynamically +Error: Cannot call func_get_args() dynamically +Error: Cannot call func_num_args() dynamically +Error: Cannot call get_defined_vars() dynamically diff --git a/Zend/tests/partial_application/observers.phpt b/Zend/tests/partial_application/observers.phpt new file mode 100644 index 000000000000..8717b38ca326 --- /dev/null +++ b/Zend/tests/partial_application/observers.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA support observers +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_output=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + <{closure:%s}> + + + + +int(1) +int(2) + + + + diff --git a/Zend/tests/partial_application/optional_placeholder.phpt b/Zend/tests/partial_application/optional_placeholder.phpt new file mode 100644 index 000000000000..d6230241d906 --- /dev/null +++ b/Zend/tests/partial_application/optional_placeholder.phpt @@ -0,0 +1,77 @@ +--TEST-- +PFA optional placeholder: ? always generates required params; ... inherits optionality +--FILE-- +getParameters()[0]->isOptional()); + +$f = foo(?, ?); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional()); + +$f = foo(?, ?, ?); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +echo "# ... inherits optionality from the original function\n"; +$f = foo(?, ...); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +$f = foo(?, ?, ...); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +echo "# Calling works: ? params are required, unspecified optional params use their defaults\n"; +$f = foo(?, ?); +var_dump($f(10, 'hello')); + +echo "# Named ? placeholders are also required\n"; + +function bar(int $x = 0, int $y = 0) { + return [$x, $y]; +} + +$f = bar(y: ?); +var_dump((new ReflectionFunction($f))->getParameters()[0]->isOptional()); +var_dump($f(42)); + +?> +--EXPECT-- +# ? makes targeted params required regardless of original optionality +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +# ... inherits optionality from the original function +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +# Calling works: ? params are required, unspecified optional params use their defaults +array(3) { + [0]=> + int(10) + [1]=> + string(5) "hello" + [2]=> + float(3.14) +} +# Named ? placeholders are also required +bool(false) +array(2) { + [0]=> + int(0) + [1]=> + int(42) +} diff --git a/Zend/tests/partial_application/param_reorder.phpt b/Zend/tests/partial_application/param_reorder.phpt new file mode 100644 index 000000000000..3ade1beb0af7 --- /dev/null +++ b/Zend/tests/partial_application/param_reorder.phpt @@ -0,0 +1,202 @@ +--TEST-- +Named parameters define the order of parameters in a PFA +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +# All named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 13 - 13 + + - Parameters [4] { + Parameter #0 [ $d ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + Parameter #3 [ $a ] + } +} + +array(4) { + [0]=> + int(4) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + int(1) +} +# Some named: Positional first, then named in specified order +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 19 - 19 + + - Parameters [4] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $d ] + Parameter #3 [ $c ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(4) + [3]=> + int(3) +} +# Some named, one unspecified +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 25 - 25 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + NULL +} +# Some named, some implicit added by '...' +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 31 - 31 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ $d = NULL ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(4) +} +# Some named, some implicit added by '...' on variadic function +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 37 - 37 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ ...$d ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } +} +# Some prebound, some named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 43 - 43 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $d ] + } + + - Parameters [2] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + } +} + +array(4) { + [0]=> + int(-1) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(-2) +} +# Some named, some required missing +ArgumentCountError: f(): Argument #1 ($a) not passed diff --git a/Zend/tests/partial_application/pipe_optimization_001.phpt b/Zend/tests/partial_application/pipe_optimization_001.phpt new file mode 100644 index 000000000000..9610f37b76ac --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA optimization: PFA with single placeholder arg can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a) { + var_dump($a); + } +} + +1 |> foo(?); + +?> +--EXPECTF-- +$_main: + ; (lines=9, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_001.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +foo: + ; (lines=5, args=1, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_001.php:4-6 +0000 CV0($a) = RECV 1 +0001 INIT_FCALL 1 %d string("var_dump") +0002 SEND_VAR CV0($a) 1 +0003 DO_ICALL +0004 RETURN null +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_002.phpt b/Zend/tests/partial_application/pipe_optimization_002.phpt new file mode 100644 index 000000000000..729e70d30e6b --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ?); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_002.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_002.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_003.phpt b/Zend/tests/partial_application/pipe_optimization_003.phpt new file mode 100644 index 000000000000..da112f8f3cea --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_003.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (placeholder first) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(?, 1); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_003.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(2) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_003.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(2) +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt new file mode 100644 index 000000000000..addee498d810 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(?, ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=22, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_004.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_PLACEHOLDER 1 +0007 SEND_PLACEHOLDER 2 +0008 T1 = CALLABLE_CONVERT_PARTIAL %d +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 T1 = FETCH_CLASS_NAME CV0($e) +0015 ECHO T1 +0016 ECHO string(": ") +0017 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0018 T1 = DO_FCALL +0019 ECHO T1 +0020 ECHO string("\n") +0021 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_004.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 T2 = DO_UCALL +0006 RETURN T2 +ArgumentCountError: Too few arguments to function {closure:pfa:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_005.phpt b/Zend/tests/partial_application/pipe_optimization_005.phpt new file mode 100644 index 000000000000..3ccfec836609 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_005.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (variadic) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_005.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_005.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_006.phpt b/Zend/tests/partial_application/pipe_optimization_006.phpt new file mode 100644 index 000000000000..6e06477427a3 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_006.phpt @@ -0,0 +1,55 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +2 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_006.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_006.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt new file mode 100644 index 000000000000..09c3c765d237 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(a: ?, b: ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=22, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_007.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_PLACEHOLDER string("a") +0007 SEND_PLACEHOLDER string("b") +0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...) +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 T1 = FETCH_CLASS_NAME CV0($e) +0015 ECHO T1 +0016 ECHO string(": ") +0017 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0018 T1 = DO_FCALL +0019 ECHO T1 +0020 ECHO string("\n") +0021 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_007.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 T2 = DO_UCALL +0006 RETURN T2 +ArgumentCountError: Too few arguments to function {closure:pfa:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt new file mode 100644 index 000000000000..070074632c77 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -0,0 +1,99 @@ +--TEST-- +PFA pipe optimization: PFA with both a variadic placeholder and named arg can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(a: 1, ...); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=18, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_008.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_VAL_EX int(1) string("a") +0007 T1 = CALLABLE_CONVERT_PARTIAL %d +0008 INIT_DYNAMIC_CALL 1 T1 +0009 SEND_VAL_EX int(2) 1 +0010 DO_FCALL +0011 RETURN int(1) +0012 CV0($e) = CATCH string("Throwable") +0013 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0014 T1 = DO_FCALL +0015 ECHO T1 +0016 ECHO string("\n") +0017 RETURN int(1) +EXCEPTION TABLE: + 0005, 0012, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_008.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($a) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:%s:%d}: + ; (lines=18, args=1, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($b) = RECV 1 +0001 BIND_STATIC CV1($a) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("foo") +0006 SEND_VAR CV1($a) 1 +0007 SEND_VAR CV0($b) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("foo") +0011 SEND_VAR CV1($a) 1 +0012 SEND_VAR CV0($b) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_009.phpt b/Zend/tests/partial_application/pipe_optimization_009.phpt new file mode 100644 index 000000000000..118482a3860b --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_009.phpt @@ -0,0 +1,102 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + echo __FUNCTION__, "\n"; + return 2; + } +} + +lhs() |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:1-24 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 INIT_FCALL_BY_NAME 0 string("lhs") +0009 T0 = DO_FCALL_BY_NAME +0010 INIT_FCALL_BY_NAME 3 string("foo") +0011 INIT_FCALL_BY_NAME 0 string("arg1") +0012 V1 = DO_FCALL_BY_NAME +0013 SEND_VAR_NO_REF_EX V1 1 +0014 SEND_VAL_EX T0 2 +0015 INIT_FCALL_BY_NAME 0 string("arg2") +0016 V0 = DO_FCALL_BY_NAME +0017 SEND_VAR_NO_REF_EX V0 3 +0018 DO_FCALL_BY_NAME +0019 RETURN int(1) +LIVE RANGES: + 0: 0010 - 0014 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:11-14 +0000 ECHO string("arg1\n") +0001 RETURN int(1) + +arg2: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:15-18 +0000 ECHO string("arg2\n") +0001 RETURN int(2) +lhs +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_010.phpt b/Zend/tests/partial_application/pipe_optimization_010.phpt new file mode 100644 index 000000000000..f77c2a97732e --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_010.phpt @@ -0,0 +1,58 @@ +--TEST-- +PFA pipe optimization: References +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo(&$a, $b) { + var_dump($a, $b); + $a = 2; + } +} + +1 |> foo($a, ?); +var_dump($a); + +?> +--EXPECTF-- +$_main: + ; (lines=13, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_010.php:1-14 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAR_EX CV0($a) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 INIT_FCALL 1 %d string("var_dump") +0010 SEND_VAR CV0($a) 1 +0011 DO_ICALL +0012 RETURN int(1) + +foo: + ; (lines=8, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_010.php:4-7 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 ASSIGN CV0($a) int(2) +0007 RETURN null +NULL +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_011.phpt b/Zend/tests/partial_application/pipe_optimization_011.phpt new file mode 100644 index 000000000000..cd1c986d99e7 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_011.phpt @@ -0,0 +1,98 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function arg1() { + global $a; + $a = 2; + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + global $a; + $a = 3; + echo __FUNCTION__, "\n"; + return 2; + } +} + +$a = 0; +$a |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=19, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:1-25 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0007 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("arg1") %d +0006 DECLARE_FUNCTION string("arg2") %d +0007 ASSIGN CV0($a) int(0) +0008 T1 = QM_ASSIGN CV0($a) +0009 INIT_FCALL_BY_NAME 3 string("foo") +0010 INIT_FCALL_BY_NAME 0 string("arg1") +0011 V2 = DO_FCALL_BY_NAME +0012 SEND_VAR_NO_REF_EX V2 1 +0013 SEND_VAL_EX T1 2 +0014 INIT_FCALL_BY_NAME 0 string("arg2") +0015 V1 = DO_FCALL_BY_NAME +0016 SEND_VAR_NO_REF_EX V1 3 +0017 DO_FCALL_BY_NAME +0018 RETURN int(1) +LIVE RANGES: + 1: 0009 - 0013 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +arg1: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:7-12 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(2) +0002 ECHO string("arg1\n") +0003 RETURN int(1) + +arg2: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:13-18 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(3) +0002 ECHO string("arg2\n") +0003 RETURN int(2) +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_012.phpt b/Zend/tests/partial_application/pipe_optimization_012.phpt new file mode 100644 index 000000000000..da172874adce --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_012.phpt @@ -0,0 +1,52 @@ +--TEST-- +PFA optimization: PFA with named args and placeholders can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +1 |> foo(?, b: 2); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("b") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_013.phpt b/Zend/tests/partial_application/pipe_optimization_013.phpt new file mode 100644 index 000000000000..7d1a48b2f2ed --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_013.phpt @@ -0,0 +1,87 @@ +--TEST-- +PFA optimization: PFA with named args and a variadic placeholder can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +1 |> foo(b: 2, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=12, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_VAL_EX int(2) string("b") +0007 T0 = CALLABLE_CONVERT_PARTIAL 3 +0008 INIT_DYNAMIC_CALL 1 T0 +0009 SEND_VAL_EX int(1) 1 +0010 DO_FCALL +0011 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-9 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($b) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:pfa:%s:9}: + ; (lines=18, args=1, vars=2, tmps=%d) + ; (after optimizer) + ; %s:9-9 +0000 CV0($a) = RECV 1 +0001 BIND_STATIC CV1($b) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("foo") +0006 SEND_VAR CV0($a) 1 +0007 SEND_VAR CV1($b) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("foo") +0011 SEND_VAR CV0($a) 1 +0012 SEND_VAR CV1($b) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_014.phpt b/Zend/tests/partial_application/pipe_optimization_014.phpt new file mode 100644 index 000000000000..6e66d7e0e99d --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_014.phpt @@ -0,0 +1,68 @@ +--TEST-- +PFA pipe optimization: PFA with unknown named parameter can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +try { + 2 |> foo(1, unknown: ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("unknown") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) +0011 CV0($e) = CATCH string("Error") +0012 T1 = FETCH_CLASS_NAME CV0($e) +0013 ECHO T1 +0014 ECHO string(": ") +0015 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0016 T1 = DO_FCALL +0017 ECHO T1 +0018 ECHO string("\n") +0019 RETURN int(1) +EXCEPTION TABLE: + 0005, 0011, -, - + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +Error: Unknown named parameter $unknown diff --git a/Zend/tests/partial_application/pipe_optimization_015.phpt b/Zend/tests/partial_application/pipe_optimization_015.phpt new file mode 100644 index 000000000000..ce293c7a300c --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_015.phpt @@ -0,0 +1,55 @@ +--TEST-- +PFA pipe optimization: PFA with skipped optional parameter can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +3 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(3) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(3) diff --git a/Zend/tests/partial_application/preloading.inc b/Zend/tests/partial_application/preloading.inc new file mode 100644 index 000000000000..885ed0c5b0f1 --- /dev/null +++ b/Zend/tests/partial_application/preloading.inc @@ -0,0 +1,13 @@ + diff --git a/Zend/tests/partial_application/preloading.phpt b/Zend/tests/partial_application/preloading.phpt new file mode 100644 index 000000000000..23ad6edf2c4c --- /dev/null +++ b/Zend/tests/partial_application/preloading.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.preload={PWD}/preloading.inc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(2) +int(2) diff --git a/Zend/tests/partial_application/rebinding_001.phpt b/Zend/tests/partial_application/rebinding_001.phpt new file mode 100644 index 000000000000..d1913957a8c9 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_001.phpt @@ -0,0 +1,59 @@ +--TEST-- +PFA can only be rebound to an instanceof $this +--FILE-- +f(?); +$g = $c->g(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$f->bindTo(new C)(1); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$f->bindTo(new SubClass)(1); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on instance method:\n"; +try { + $f->bindTo(null)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static method:\n"; +$g->bindTo(null)(1); + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Cannot unbind $this on instance method: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Can unbind $this on static method: +string(1) "C" diff --git a/Zend/tests/partial_application/rebinding_002.phpt b/Zend/tests/partial_application/rebinding_002.phpt new file mode 100644 index 000000000000..cfead54bbd81 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_002.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA scope cannot be rebound +--FILE-- +f(?); +$g = g(?); + +echo "# Can be rebound to the same scope:\n"; +$f->bindTo($c, C::class)(1); + +echo "# Method cannot be rebound to a different scope:\n"; +try { + $f->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Function cannot be rebound to a different scope:\n"; +try { + $g->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +# Can be rebound to the same scope: +string(1) "C" +# Method cannot be rebound to a different scope: + +Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Function cannot be rebound to a different scope: + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable diff --git a/Zend/tests/partial_application/rebinding_003.phpt b/Zend/tests/partial_application/rebinding_003.phpt new file mode 100644 index 000000000000..9417bbc2d320 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_003.phpt @@ -0,0 +1,69 @@ +--TEST-- +Rebinding PFA of Closure rebinds inner Closure +--FILE-- +getF()(?); +$g = $c->getG()(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$d = new C(); +$f->bindTo($d)($d); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$d = new SubClass(); +$f->bindTo($d)($d); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)($c); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on non-static closure:\n"; +try { + $f->bindTo(null)($c); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static closure:\n"; +$g->bindTo(null)($c); + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +bool(true) +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +bool(true) +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Cannot unbind $this on non-static closure: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Can unbind $this on static closure: +string(1) "C" +object(C)#%d (0) { +} diff --git a/Zend/tests/partial_application/recorded_warnings.phpt b/Zend/tests/partial_application/recorded_warnings.phpt new file mode 100644 index 000000000000..f25be826f525 --- /dev/null +++ b/Zend/tests/partial_application/recorded_warnings.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA compilation warnings are recorded and replayed +--FILE-- + +--EXPECTF-- +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 3 + +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 5 diff --git a/Zend/tests/partial_application/references_001.phpt b/Zend/tests/partial_application/references_001.phpt new file mode 100644 index 000000000000..080b3085acc0 --- /dev/null +++ b/Zend/tests/partial_application/references_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA receives by val if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &string(49) "unchanged because foo() doesn't take by reference" +} +string(49) "unchanged because foo() doesn't take by reference" diff --git a/Zend/tests/partial_application/references_002.phpt b/Zend/tests/partial_application/references_002.phpt new file mode 100644 index 000000000000..42a90648c1ae --- /dev/null +++ b/Zend/tests/partial_application/references_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &int(2) +} +int(2) diff --git a/Zend/tests/partial_application/references_003.phpt b/Zend/tests/partial_application/references_003.phpt new file mode 100644 index 000000000000..be116b06c79f --- /dev/null +++ b/Zend/tests/partial_application/references_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +{closure:%s}(): Argument #1 ($b) could not be passed by reference diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt new file mode 100644 index 000000000000..e5163c710da2 --- /dev/null +++ b/Zend/tests/partial_application/references_004.phpt @@ -0,0 +1,42 @@ +--TEST-- +PFA receives variadic param by ref if the actual function does +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreferences_004.php 13 - 13 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $args2 ] + } + + - Parameters [2] { + Parameter #0 [ &$args0 ] + Parameter #1 [ &...$args ] + } +} + +int(-2) +int(-3) +int(-4) diff --git a/Zend/tests/partial_application/references_005.phpt b/Zend/tests/partial_application/references_005.phpt new file mode 100644 index 000000000000..e8c7c27a07be --- /dev/null +++ b/Zend/tests/partial_application/references_005.phpt @@ -0,0 +1,26 @@ +--TEST-- +PFA inherits return by ref +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(1) +} +array(1) { + [0]=> + int(1) +} diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt new file mode 100644 index 000000000000..ac079515d0a0 --- /dev/null +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +PFA reflection: optional parameters +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 6 - 6 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 10 - 10 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 14 - 14 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] + } +} diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt new file mode 100644 index 000000000000..da91a7af50cf --- /dev/null +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -0,0 +1,57 @@ +--TEST-- +PFA reflection: variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ ...$b ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 14 - 14 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + Parameter #2 [ $b1 ] + } +} diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt new file mode 100644 index 000000000000..90506d38a778 --- /dev/null +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA reflection: internal with variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 2 - 2 + + - Parameters [1] { + Parameter #0 [ string $format ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 6 - 6 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed ...$values ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 10 - 10 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed $values0 ] + } + - Return [ string ] +} diff --git a/Zend/tests/partial_application/reflection_004.phpt b/Zend/tests/partial_application/reflection_004.phpt new file mode 100644 index 000000000000..7226383f5afa --- /dev/null +++ b/Zend/tests/partial_application/reflection_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isAnonymous() is true for partials +--FILE-- +isAnonymous()); + +var_dump((new ReflectionFunction(function () {}))->isAnonymous()); + +var_dump((new ReflectionFunction(sprintf(?)))->isAnonymous()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/reflection_005.phpt b/Zend/tests/partial_application/reflection_005.phpt new file mode 100644 index 000000000000..be86270c004c --- /dev/null +++ b/Zend/tests/partial_application/reflection_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isClosure() is true for partials +--FILE-- +isClosure()); + +var_dump((new ReflectionFunction(function () {}))->isClosure()); + +var_dump((new ReflectionFunction(sprintf(?)))->isClosure()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/relative_return_types.phpt b/Zend/tests/partial_application/relative_return_types.phpt new file mode 100644 index 000000000000..5729f7edfd02 --- /dev/null +++ b/Zend/tests/partial_application/relative_return_types.phpt @@ -0,0 +1,148 @@ +--TEST-- +PFA supports relative return types +--FILE-- + 0) { + trait T { + public function getSelf(object $o): self { + return $o; + } + public function getStatic(object $o): static { + return $o; + } + } +} + +class C { + use T; +} + +class D extends C { +} + +$c = new C; + +echo "# C::getSelf():\n"; + +$self = $c->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($c)); +var_dump($self(new D)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# C::getStatic():\n"; + +$static = $c->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($c)); +var_dump($static(new D)); +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$d = new D; + +echo "# D::getSelf():\n"; + +$self = $d->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($d)); +var_dump($self(new C)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# D::getStatic():\n"; + +$static = $d->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($d)); +try { + var_dump($static(new C)); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# C::getSelf(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 25 - 25 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getSelf(): Return value must be of type C, stdClass returned +# C::getStatic(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 39 - 39 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getStatic(): Return value must be of type C, stdClass returned +# D::getSelf(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 55 - 55 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(D)#%d (0) { +} +object(C)#%d (0) { +} +TypeError: C::getSelf(): Return value must be of type C, stdClass returned +# D::getStatic(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 69 - 69 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(D)#%d (0) { +} +TypeError: C::getStatic(): Return value must be of type D, C returned +C::getStatic(): Return value must be of type D, stdClass returned diff --git a/Zend/tests/partial_application/return_type.phpt b/Zend/tests/partial_application/return_type.phpt new file mode 100644 index 000000000000..ae3738e2c660 --- /dev/null +++ b/Zend/tests/partial_application/return_type.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA inherits return type +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [0] { + } + - Return [ array ] +} diff --git a/Zend/tests/partial_application/rfc_examples.inc b/Zend/tests/partial_application/rfc_examples.inc new file mode 100644 index 000000000000..53babc07888b --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples.inc @@ -0,0 +1,76 @@ + [$pfa, $closure]) { + echo "# ", $test, ": "; + $pfaReflector = new ReflectionFunction($pfa); + $closureReflector = new ReflectionFunction($closure); + + try { + if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) { + throw new Exception(sprintf( + "Arity does not match: expected %d, got %d", + count($closureReflector->getParameters()), + count($pfaReflector->getParameters()), + )); + } + + $it = new MultipleIterator(); + $it->attachIterator(new ArrayIterator($pfaReflector->getParameters())); + $it->attachIterator(new ArrayIterator($closureReflector->getParameters())); + foreach ($it as $i => [$pfaParam, $closureParam]) { + [$i] = $i; + if ($pfaParam->getName() !== $closureParam->getName()) { + throw new Exception(sprintf("Name of param %d does not match: %s vs %s", + $i, + $pfaParam->getName(), + $closureParam->getName(), + )); + } + if ((string)$pfaParam->getType() !== (string)$closureParam->getType()) { + throw new Exception(sprintf("Type of param %d does not match: %s vs %s", + $i, + $pfaParam->getType(), + $closureParam->getType(), + )); + } + if ($pfaParam->isOptional() !== $closureParam->isOptional()) { + throw new Exception(sprintf("Optionalness of param %d does not match: %d vs %d", + $i, + $pfaParam->isOptional(), + $closureParam->isOptional(), + )); + } + } + + $args = []; + foreach ($pfaReflector->getParameters() as $i => $p) { + $args[] = match ((string) $p->getType()) { + 'int' => 100 + $i, + 'float' => 100.5 + $i, + '?float' => 100.5 + $i, + 'string' => (string) (100 + $i), + 'Point' => new Point, + 'mixed' => "mixed($i)", + '' => "mixed($i)", + }; + } + + if ($pfaReflector->getClosureThis() !== $closureReflector->getClosureThis()) { + throw new Exception("\$this differs"); + } + + if ($pfa(...$args) !== $closure(...$args)) { + throw new Exception("PFA is not equivalent to closure"); + } + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + echo $pfaReflector; + echo $closureReflector; + return; + } + + echo "Ok\n"; + } +} diff --git a/Zend/tests/partial_application/rfc_examples_const_expr.phpt b/Zend/tests/partial_application/rfc_examples_const_expr.phpt new file mode 100644 index 000000000000..e2fc57440256 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_const_expr.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Constant expressions" section +--XFAIL-- +PFA in constant expressions not implemented yet +--FILE-- + +==DONE== +--EXPECTF-- +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_debug.phpt b/Zend/tests/partial_application/rfc_examples_debug.phpt new file mode 100644 index 000000000000..9c5501090d4e --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_debug.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Debug output" section +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): g() +#1 %s(%d): f(1, Object(SensitiveParameterValue), 3) +#2 %s(%d): {closure:pfa:%s}(1, Object(SensitiveParameterValue)) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/rfc_examples_errors.phpt b/Zend/tests/partial_application/rfc_examples_errors.phpt new file mode 100644 index 000000000000..21818d7446e3 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_errors.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA RFC examples: "Error examples" section +--FILE-- +getMessage(), "\n"; +} + +try { + stuff(?, ?, ?, ?, ?, ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + stuff(?, ?, 3.5, $point, i: 5); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: Partial application of stuff() expects at least 4 arguments, 1 given +ArgumentCountError: Partial application of stuff() expects at most 5 arguments, 6 given +Error: Named parameter $i overwrites previous placeholder diff --git a/Zend/tests/partial_application/rfc_examples_eval_order.phpt b/Zend/tests/partial_application/rfc_examples_eval_order.phpt new file mode 100644 index 000000000000..2fd6c9422e92 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_eval_order.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Evaluation order" section +--FILE-- + speak($who, getArg()); +print "Arnaud\n"; +$arrow('Larry'); + +$partial = speak(?, getArg()); +print "Arnaud\n"; +$partial('Larry'); + +?> +--EXPECT-- +Arnaud +getArg +Larry: hi +getArg +Arnaud +Larry: hi diff --git a/Zend/tests/partial_application/rfc_examples_extra_args.phpt b/Zend/tests/partial_application/rfc_examples_extra_args.phpt new file mode 100644 index 000000000000..f6a24df8cc71 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_extra_args.phpt @@ -0,0 +1,54 @@ +--TEST-- +PFA RFC examples: "Variadics, func_get_args(), and extraneous arguments" section +--FILE-- + [ + foo(1, ?), + static function (int $j) { return foo(1, $j); }, + ], + 'If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function' => [ + foo(1, ?, ...), + static function (int $j) { return foo(1, $j, ...array_slice(func_get_args(), 1)); }, + ], + 'If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected†by the variadic parameter as normal' => [ + foo2(1, ?, ...), + static function (int $j, ...$extra) { return foo2(1, $j, ...$extra); }, + ], +]; + +check_equivalence($tests); + +echo "# The extra parameter here will be passed to the closure object, which will simply ignore it:\n"; +var_dump(foo(1, ?)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al:\n"; +var_dump(foo(1, ?, ...)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the \$extra array:\n"; +var_dump(foo2(1, ?, ...)(4, 'ignore me')); + +?> +==DONE== +--EXPECT-- +# If a PFA call has no ... placeholder, then any extraneous arguments to the resulting closure will be ignored. That is consistent with how manually writing the equivalent closure would behave, and is the same regardless of whether the underlying function is variadic: Ok +# If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function: Ok +# If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected†by the variadic parameter as normal: Ok +# The extra parameter here will be passed to the closure object, which will simply ignore it: +int(2) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al: +int(3) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the $extra array: +int(3) +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt new file mode 100644 index 000000000000..dd99cb229ae8 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA RFC examples: "Incompatible functions" section +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Cannot call func_get_arg() dynamically diff --git a/Zend/tests/partial_application/rfc_examples_magic_methods.phpt b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt new file mode 100644 index 000000000000..7162a790c57c --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA RFC examples: "Magic methods" section +--FILE-- + [ + $f->method(?, ?), + (function ($f) { + return fn(mixed $arguments0, mixed $arguments1) => $f->method($arguments0, $arguments1); + })($f)->bindTo($f), + ], +]); + +try { + $f->method(?, ?)(a: 1, b: 2); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +# Test 1: Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Ok +Error: Unknown named parameter $a diff --git a/Zend/tests/partial_application/rfc_examples_overview.phpt b/Zend/tests/partial_application/rfc_examples_overview.phpt new file mode 100644 index 000000000000..b6fc8c0586c2 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_overview.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFA RFC examples: "Overview" section +--FILE-- + [ + foo(1, ?, 3, 4), + static fn(int $b): int => foo(1, $b, 3, 4), + ], + 'Test 2' => [ + foo(1, ?, 3, ?), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], + 'Test 3' => [ + foo(1, ...), + static fn(int $b, int $c, int $d): int => foo(1, $b, $c, $d), + ], + 'Test 4' => [ + foo(1, 2, ...), + static fn(int $c, int $d): int => foo(1, 2, $c, $d), + ], + 'Test 5' => [ + foo(1, ?, 3, ...), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok +# Test 3: Ok +# Test 4: Ok +# Test 5: Ok diff --git a/Zend/tests/partial_application/rfc_examples_scoping.phpt b/Zend/tests/partial_application/rfc_examples_scoping.phpt new file mode 100644 index 000000000000..232d225dca6a --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_scoping.phpt @@ -0,0 +1,113 @@ +--TEST-- +PFA RFC examples: "Scoping" section +--FILE-- + [ + foo(?, ?), + static fn(int $i, int $j): string => foo($i, $j), + ], + 'Static closure 2' => [ + Foo::bar(?, ?), + static fn(int $i, int $j): string => Foo::bar($i, $j), + ], + 'Static closure 3' => [ + foo(?, ?)(1, ?), + static fn(int $j): string => foo(1, $j), + ], + 'Static closure 4' => [ + foo(...)(?), + static fn(int $i): string => foo($i, 0), + ], +]; + +check_equivalence($tests); + +$c = new C(); +$f = $c->f(?); + +echo "# Cannot unbind \$this:\n"; +var_dump($f->bindTo(null, C::class)); // Warning: Cannot unbind $this of method, this will be an error in PHP 9 (returns null) + +echo "# Cannot rebind scope:\n"; +var_dump($f->bindTo($c, CSubClass::class)); // Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 (returns null) + +echo "# Can rebind \$this with subclass:\n"; +var_dump($f->bindTo(new CSubClass, C::class)); // Allowed + +echo "# Cannot rebind \$this with unrelated class:\n"; +$f = $f->bindTo(new Unrelated, C::class); // Warning: Cannot bind method C::{closure:/path/to/test.php:11}() to object of class Unrelated, this will be an error in PHP 9 (returns null) + +echo "# self resolution:\n"; +$c = new CSubClass(); +var_dump($c->f(?)(1)); // string(1) "C" +var_dump($c->g(?)(1)); // string(9) "CSubClass" +var_dump($c->h(1)(1)); // string(1) "C" + +?> +--EXPECTF-- +# Static closure 1: Ok +# Static closure 2: Ok +# Static closure 3: Ok +# Static closure 4: Ok +# Cannot unbind $this: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +NULL +# Cannot rebind scope: + +Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 in %s on line %d +NULL +# Can rebind $this with subclass: +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:pfa:%s}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(53) + ["this"]=> + object(CSubClass)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} +# Cannot rebind $this with unrelated class: + +Warning: Cannot bind method C::{closure:pfa:%s}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +# self resolution: +string(1) "C" +string(9) "CSubClass" +string(1) "C" diff --git a/Zend/tests/partial_application/rfc_examples_semantics.phpt b/Zend/tests/partial_application/rfc_examples_semantics.phpt new file mode 100644 index 000000000000..a3ea25c30d6d --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics" section +--FILE-- + [ + foo(?, ?, ?, ?), + static fn(int $a, int $b, string $c0, string $c1) => foo($a, $b, $c0, $c1), + ], + 'Test 2' => [ + stuff(1, ?, p: ?, f: 3.14, ...), + static fn(string $s, Point $p, int $m = 0) => stuff(1, $s, 3.14, $p, $m), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok diff --git a/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt new file mode 100644 index 000000000000..d8c4a83ca074 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt @@ -0,0 +1,217 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics: Examples" section +--FILE-- + [ + stuff(?, ?, ...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'The degenerate "first class callables" case. (Supported since 8.1)' => [ + stuff(...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (1)' => [ + stuff(1, 'hi', ?, ?, ?), + static fn(float $f3, Point $p4, int $m5): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (2)' => [ + stuff(1, 'hi', ...), + static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, but not just from the left (1)' => [ + stuff(1, ?, 3.5, ?, ?), + static fn(string $s2, Point $p4, int $m5): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide some values, but not just from the left (2)' => [ + stuff(1, ?, 3.5, ...), + static fn(string $s2, Point $p4, int $m5 = 0): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide just the last value' => [ + stuff(?, ?, ?, ?, 5), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4, 5), + ], + 'Not accounting for an optional argument means it will always get its default value' => [ + stuff(?, ?, ?, ?), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4), + ], + 'Named arguments can be pulled "out of order", and still work (1)' => [ + stuff(?, ?, f3: 3.5, p4: $point), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + 'Named arguments can be pulled "out of order", and still work (2)' => [ + stuff(?, ?, p4: $point, f3: 3.5), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + + 'But named placeholders adopt the order listed' => [ + stuff(s2: ?, i1: ?, p4: ?, f3: 3.5), + static fn(string $s2, int $i1, Point $p4): array => stuff($i1, $s2, 3.5, $p4), + ], + 'The ... "everything else" placeholder respects named arguments' => [ + stuff(?, ?, f3: 3.5, p4: $point, ...), + static fn(int $i1, string $s2, int $m5 = 0): array => stuff($i1, $s2, 3.5, $point, $m5), + ], + 'Prefill all parameters, making a "delayed call" or "thunk"' => [ + stuff(1, 'hi', 3.4, $point, 5, ...), + static fn(): array => stuff(1, 'hi', 3.4, $point, 5), + ], + + // Variadics + + 'FCC equivalent. The signature is unchanged' => [ + things(...), + static fn(int $i1, ?float $f3 = null, Point ...$points): array => things(...[$i1, $f3, ...$points]), + ], + 'Provide some values, but allow the variadic to remain variadic' => [ + things(1, 3.14, ...), + static fn(Point ...$points): array => things(1, 3.14, ...$points), + ], + 'In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case' => [ + things(?, ?, ?, ?), + static fn(int $i1, ?float $f3, Point $points0, Point $points1): array => things($i1, $f3, $points0, $points1), + ], + + // Esoteric examples + + 'Esoteric 1' => [ + four(...), + static fn(int $a, int $b, int $c, int $d): string => four($a, $b, $c, $d), + ], + 'Esoteric 2' => [ + four(1, 2, ...), + static fn(int $c, int $d): string => four(1, 2, $c, $d), + ], + 'Esoteric 3' => [ + four(1, 2, 3, ?), + static fn(int $d): string => four(1, 2, 3, $d), + ], + 'Esoteric 4' => [ + four(1, ?, ?, 4), + static fn(int $b, int $c): string => four(1, $b, $c, 4), + ], + 'Esoteric 5' => [ + four(1, 2, 3, 4, ...), + static fn(): string => four(1, 2, 3, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 6' => [ + four(d: 4, a: 1, ...), + static fn(int $b, int $c): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 7' => [ + four(c: ?, d: 4, b: ?, a: 1), + static fn(int $c, int $b): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + + // Other callable styles + + 'This is allowed. Note the method is static, thus the partial closure is static' => [ + E::make(1, ?), + static fn(int $y): E => E::make(1, $y), + ], + 'Note the method is non-static, so the partial closure is non-static' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + return [ + $e->foo(?, ?, 3), + (function ($e) { + return fn(int $a, int $b): array => $e->foo($a, $b, 3); + })($e)->bindTo($e), + ]; + })(), + '$c can then be further refined' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + $c = $e->foo(?, ?, 3); + return [ + $c(1, ?), + (function ($e) { + return fn(int $b): array => $e->foo(1, $b, 3); + })($e)->bindTo($e), + ]; + })(), + 'RunMe' => (function () { + $r = new RunMe(); + return [ + $r(?, 3), + (function ($r) { + return fn(int $a): string => $r($a, 3); + })($r)->bindTo($r), + ]; + })(), +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Manually specify the first two values, and pull the rest "as is": Ok +# The degenerate "first class callables" case. (Supported since 8.1): Ok +# Provide some values, require the rest to be provided later (1): Ok +# Provide some values, require the rest to be provided later (2): Ok +# Provide some values, but not just from the left (1): Ok +# Provide some values, but not just from the left (2): Ok +# Provide just the last value: Ok +# Not accounting for an optional argument means it will always get its default value: Ok +# Named arguments can be pulled "out of order", and still work (1): Ok +# Named arguments can be pulled "out of order", and still work (2): Ok +# But named placeholders adopt the order listed: Ok +# The ... "everything else" placeholder respects named arguments: Ok +# Prefill all parameters, making a "delayed call" or "thunk": Ok +# FCC equivalent. The signature is unchanged: Ok +# Provide some values, but allow the variadic to remain variadic: Ok +# In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case: Ok +# Esoteric 1: Ok +# Esoteric 2: Ok +# Esoteric 3: Ok +# Esoteric 4: Ok +# Esoteric 5: Ok +# Esoteric 6: Ok +# Esoteric 7: Ok +# This is allowed. Note the method is static, thus the partial closure is static: Ok +# Note the method is non-static, so the partial closure is non-static: Ok +# $c can then be further refined: Ok +# RunMe: Ok diff --git a/Zend/tests/partial_application/static_method_001.phpt b/Zend/tests/partial_application/static_method_001.phpt new file mode 100644 index 000000000000..ce4151441c33 --- /dev/null +++ b/Zend/tests/partial_application/static_method_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA supports static methods +--FILE-- + +--EXPECTF-- +Foo::method diff --git a/Zend/tests/partial_application/static_pfa_001.phpt b/Zend/tests/partial_application/static_pfa_001.phpt new file mode 100644 index 000000000000..bc0f65b1ffd8 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_001.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA of static method is static +--FILE-- +f(?); +echo new ReflectionFunction($f), "\n"; +$f(1); + +// Warns +var_dump($f->bindTo(new C)); + +?> +--EXPECTF-- +Closure [ static public method {closure:pfa:%s:9} ] { + @@ %s 9 - 9 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) +Closure [ static public method {closure:pfa:%s:13} ] { + @@ %s 13 - 13 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_pfa_002.phpt b/Zend/tests/partial_application/static_pfa_002.phpt new file mode 100644 index 000000000000..5222109958b8 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_002.phpt @@ -0,0 +1,35 @@ +--TEST-- +PFA of instance method is not static +--FILE-- +f(?); +echo new ReflectionFunction($f), "\n"; +$f($c); + +$c2 = new C(); +$f->bindTo($c2)($c2); + +?> +--EXPECTF-- +Closure [ public method {closure:pfa:%s:10} ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +object(C)#%d (0) { +} +bool(true) +object(C)#%d (0) { +} +bool(true) diff --git a/Zend/tests/partial_application/static_pfa_003.phpt b/Zend/tests/partial_application/static_pfa_003.phpt new file mode 100644 index 000000000000..e7b3c187e187 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA of standalone function is static +--FILE-- +bindTo(new class {})); + +?> +--EXPECTF-- +Closure [ static function {closure:pfa:%s:7} ] { + @@ %s 7 - 7 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_pfa_004.phpt b/Zend/tests/partial_application/static_pfa_004.phpt new file mode 100644 index 000000000000..093430232d43 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_004.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFA of non-static closure is not static +--FILE-- +getMessage(), "\n"; + } +}; + +echo "# Original PFA\n"; + +$f = $c(?); +echo new ReflectionFunction($f), "\n"; +$f($c); + +echo "# Re-bound PFA\n"; + +$c2 = new class {}; +$f->bindTo($c2)($c2); + +?> +--EXPECTF-- +# Original PFA +Closure [ function {closure:pfa:%s:13} ] { + @@ %s 13 - 13 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +Error: Using $this when not in object context +# Re-bound PFA +object(class@anonymous)#3 (0) { +} +bool(true) diff --git a/Zend/tests/partial_application/static_pfa_005.phpt b/Zend/tests/partial_application/static_pfa_005.phpt new file mode 100644 index 000000000000..2783485b2f49 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_005.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA of static closure is static +--FILE-- +bindTo(new class {})); + +?> +--EXPECTF-- +Closure [ static function {closure:pfa:%s:7} ] { + @@ %s 7 - 7 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_polymorphism.phpt b/Zend/tests/partial_application/static_polymorphism.phpt new file mode 100644 index 000000000000..5f121c65c0e8 --- /dev/null +++ b/Zend/tests/partial_application/static_polymorphism.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA: static polymorphism +--FILE-- + +--EXPECT-- +P::m +string(1) "a" +C::m +array(0) { +} +P::m +string(1) "a" +C::m +array(0) { +} diff --git a/Zend/tests/partial_application/static_vars_001.phpt b/Zend/tests/partial_application/static_vars_001.phpt new file mode 100644 index 000000000000..610cddf37b54 --- /dev/null +++ b/Zend/tests/partial_application/static_vars_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +PFA static variables are shared (001) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/static_vars_002.phpt b/Zend/tests/partial_application/static_vars_002.phpt new file mode 100644 index 000000000000..4e6cc82b720d --- /dev/null +++ b/Zend/tests/partial_application/static_vars_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +PFA static variables are shared (002) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/static_vars_003.phpt b/Zend/tests/partial_application/static_vars_003.phpt new file mode 100644 index 000000000000..04328bf717b8 --- /dev/null +++ b/Zend/tests/partial_application/static_vars_003.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA static variables are shared (003) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt new file mode 100644 index 000000000000..506092655217 --- /dev/null +++ b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFAs forwards superfluous args iff a variadic placeholder is specified +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(1) { + [0]=> + int(1) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/partial_application/this.phpt b/Zend/tests/partial_application/this.phpt new file mode 100644 index 000000000000..bda6900bae39 --- /dev/null +++ b/Zend/tests/partial_application/this.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA $this +--FILE-- +method(new stdClass, ...); + +$baz = $bar(new stdClass, ...); + +var_dump($foo === $baz()); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/partial_application/variation_call_001.phpt b/Zend/tests/partial_application/variation_call_001.phpt new file mode 100644 index 000000000000..b9c1c19ec9c6 --- /dev/null +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: call +--FILE-- +method(?, new Param); + +$closure(1); + +$closure->call(/* newThis: */ new Foo(), 10); +?> +--EXPECT-- +Bar: 1, Param +Foo: 10, Param diff --git a/Zend/tests/partial_application/variation_closure_001.phpt b/Zend/tests/partial_application/variation_closure_001.phpt new file mode 100644 index 000000000000..1d31f22c7f8a --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +PFA variation: Closure +--FILE-- + +--EXPECTF-- +Closure [ function {closure:%s:%d} ] { + @@ %s 5 - 5 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +Closure [ function {closure:pfa:%s:%d} ] { + @@ %s 10 - 10 + + - Bound Variables [2] { + Variable #0 [ $fn2 ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $fn ] + } +} diff --git a/Zend/tests/partial_application/variation_closure_002.phpt b/Zend/tests/partial_application/variation_closure_002.phpt new file mode 100644 index 000000000000..41abe0aaab0e --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -0,0 +1,28 @@ +--TEST-- +PFA variation: Closure::__invoke() +--FILE-- +__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +$function(10); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_002.php 6 - 6 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +int(10) diff --git a/Zend/tests/partial_application/variation_closure_003.phpt b/Zend/tests/partial_application/variation_closure_003.phpt new file mode 100644 index 000000000000..da567e179853 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -0,0 +1,46 @@ +--TEST-- +PFA variation: Closure::__invoke() with $this +--FILE-- +bar(); + +$function = $closure->__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +var_dump($function(10)); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_003.php 14 - 14 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +array(2) { + [0]=> + object(Foo)#1 (0) { + } + [1]=> + array(2) { + [0]=> + int(1) + [1]=> + int(10) + } +} diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt new file mode 100644 index 000000000000..04b63f3c4010 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +PFA variation: var_dump(), user function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_001.php" + ["line"]=> + int(6) + ["static"]=> + array(4) { + ["b"]=> + object(stdClass)#%d (0) { + } + ["c"]=> + int(20) + ["c2"]=> + object(stdClass)#%d (0) { + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + int(4) + } + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt new file mode 100644 index 000000000000..a7c4c2d76e49 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA variation: var_dump(), internal function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_002.php" + ["line"]=> + int(2) + ["static"]=> + array(3) { + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["arrays2"]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + object(stdClass)#%d (0) { + } + } + } + ["parameter"]=> + array(2) { + ["$callback"]=> + string(10) "" + ["$arrays"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_ex_001.phpt b/Zend/tests/partial_application/variation_ex_001.phpt new file mode 100644 index 000000000000..0f36e90f616d --- /dev/null +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: UAF in cleanup unfinished calls +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +ArgumentCountError: Partial application of {closure:%s:%d}() expects at most 0 arguments, 1 given diff --git a/Zend/tests/partial_application/variation_gc_001.phpt b/Zend/tests/partial_application/variation_gc_001.phpt new file mode 100644 index 000000000000..2db49cc74363 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA variation: GC (001) +--FILE-- +method = self::__construct(new stdClass, ...); + } +} + +$foo = new Foo(new stdClass); +$foo->bar = $foo; + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_002.phpt b/Zend/tests/partial_application/variation_gc_002.phpt new file mode 100644 index 000000000000..31c721e8e681 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_002.phpt @@ -0,0 +1,11 @@ +--TEST-- +PFA variation: GC (002) +--FILE-- +prop = var_dump($obj, ?); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_003.phpt b/Zend/tests/partial_application/variation_gc_003.phpt new file mode 100644 index 000000000000..23cdaa1c666f --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA variation: GC (003) +--FILE-- +prop = test(?, x: $obj); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_invoke_001.phpt b/Zend/tests/partial_application/variation_invoke_001.phpt new file mode 100644 index 000000000000..9285c08da19b --- /dev/null +++ b/Zend/tests/partial_application/variation_invoke_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +PFA variation: __invoke() +--FILE-- +__invoke(32)); + +try { + $foo->nothing(); +} catch (Error $ex) { + echo $ex::class, ": ", $ex->getMessage(), "\n"; +} +?> +--EXPECT-- +int(42) +Error: Call to undefined method Closure::nothing() diff --git a/Zend/tests/partial_application/variation_nocall_001.phpt b/Zend/tests/partial_application/variation_nocall_001.phpt new file mode 100644 index 000000000000..3fbb3ec8d8c3 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +PFA variation: no call args leak +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_nocall_002.phpt b/Zend/tests/partial_application/variation_nocall_002.phpt new file mode 100644 index 000000000000..cd4823f1bd05 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA variation: no call, order of destruction +--FILE-- +id, "\n"; + } +} +$foo = new Foo; +$f = $foo->method(?); +$g = $f(?); + +$map = new WeakMap(); +$map[$f] = new Dtor(1); +$map[$g] = new Dtor(2); + +unset($f); +unset($g); + +echo "OK"; +?> +--EXPECT-- +Dtor::__destruct 2 +Dtor::__destruct 1 +OK diff --git a/Zend/tests/partial_application/variation_parent_001.phpt b/Zend/tests/partial_application/variation_parent_001.phpt new file mode 100644 index 000000000000..98095f45009a --- /dev/null +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -0,0 +1,76 @@ +--TEST-- +PFA variation: parent +--FILE-- +method(10, ...); +$baz = $bar(20, ...); + +var_dump($baz, $c = $baz(), $c() === $foo); +?> +--EXPECTF-- +object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_parent_001.php" + ["line"]=> + int(12) + ["static"]=> + array(2) { + ["fn"]=> + object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s:%d}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(11) + ["static"]=> + array(1) { + ["a"]=> + int(10) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(2) { + ["$b"]=> + string(10) "" + ["$c"]=> + string(10) "" + } + } + ["b"]=> + int(20) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$c"]=> + string(10) "" + } +} +object(Closure)#%d (4) { + ["name"]=> + string(25) "{closure:Foo::method():4}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(4) + ["this"]=> + object(Foo)#%d (0) { + } +} +bool(true) diff --git a/Zend/tests/partial_application/variation_scope_001.phpt b/Zend/tests/partial_application/variation_scope_001.phpt new file mode 100644 index 000000000000..0dcea0921c43 --- /dev/null +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA variation: called scope +--FILE-- +method(new stdClass, ...); + +$bar(); +?> +--EXPECT-- +Foo::method diff --git a/Zend/tests/partial_application/variation_strict_001.phpt b/Zend/tests/partial_application/variation_strict_001.phpt new file mode 100644 index 000000000000..835f6e138055 --- /dev/null +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA variation: strict_types declared +--FILE-- +getMessage()); +} +?> +--EXPECTF-- +TypeError: {closure:%s:%d}(): Argument #1 ($int) must be of type int, string given, called in %s on line %d diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt new file mode 100644 index 000000000000..850f0eda149d --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: variadics, user function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $b2 ] + } + + - Parameters [1] { + Parameter #0 [ ...$b ] + } +} +int(10) +int(100) +int(1000) +int(10000) diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt new file mode 100644 index 000000000000..21d8169fc42c --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA variation: variadics, internal function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %svariation_variadics_002.php 2 - 2 + + - Bound Variables [2] { + Variable #0 [ $format ] + Variable #1 [ $values2 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$values ] + } + - Return [ string ] +} +100 1000 10000 diff --git a/Zend/tests/partial_application/variation_variadics_004.phpt b/Zend/tests/partial_application/variation_variadics_004.phpt new file mode 100644 index 000000000000..228404dc8272 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_004.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA variation: variadics and optional args +--FILE-- + +--EXPECT-- +# Bound year, pass day: +2006-01-02 +# Bound month, pass day: +2005-12-02 +# Bound month, bound year, pass day: +2016-12-02 +# Bound month, no args: +2005-12-01 +# Bound month, bound year, no args: +2016-12-01 diff --git a/Zend/tests/partial_application/variation_variadics_006.phpt b/Zend/tests/partial_application/variation_variadics_006.phpt new file mode 100644 index 000000000000..cda62a2f3bfb --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_006.phpt @@ -0,0 +1,17 @@ +--TEST-- +PFA variation: named may overwrite variadic placeholder +--FILE-- + +--EXPECTF-- +array(1) { + [0]=> + string(1) "a" +} diff --git a/Zend/tests/partial_application/variation_variadics_007.phpt b/Zend/tests/partial_application/variation_variadics_007.phpt new file mode 100644 index 000000000000..9624e05b449b --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_007.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: extra through variadic +--FILE-- + $a + $b)); +?> +--EXPECT-- +int(3) diff --git a/Zend/tests/partial_application/variation_variadics_008.phpt b/Zend/tests/partial_application/variation_variadics_008.phpt new file mode 100644 index 000000000000..4190bb759e68 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA variation: variadics wrong signature checked +--FILE-- +getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +Partial application of {closure:%s:%d}() expects at most 1 arguments, 2 given diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 3aa04de860a1..adcb62a51d1f 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1459,6 +1459,16 @@ ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast) return ref; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast) +{ + ZEND_ASSERT(ast != NULL); + + zend_ast *buf = zend_ast_alloc(zend_ast_tree_size(ast)); + zend_ast_tree_copy(ast, buf); + + return buf; +} + ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) { tail_call: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 86a68c1cbaf9..26f54102a143 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -350,7 +350,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *ast, zend_class_entry *scope, bool *short_circuited_ptr, zend_ast_evaluate_ctx *ctx); ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix); +/* Copies 'ast' to the heap, returns a refcounted AST reference */ ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast); +/* Duplicates 'ast' on the arena */ +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 840d2dbe32e1..91f690c73ed0 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -27,6 +27,11 @@ #include "zend_globals.h" #include "zend_closures_arginfo.h" +/* Closure is a PFA */ +#define ZEND_PARTIAL OBJ_EXTRA_FLAG_PRIV_1 +/* Closure is a PFA of a Closure. Rebinding the PFA requires rebinding the inner Closure. */ +#define ZEND_PARTIAL_OF_CLOSURE OBJ_EXTRA_FLAG_PRIV_2 + typedef struct _zend_closure { zend_object std; zend_function func; @@ -40,6 +45,17 @@ ZEND_API zend_class_entry *zend_ce_closure; static zend_object_handlers closure_handlers; static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags); + +static inline uint32_t zend_closure_flags(const zend_closure *closure) +{ + return closure->std.extra_flags & (ZEND_PARTIAL|ZEND_PARTIAL_OF_CLOSURE); +} + +static inline bool zend_closure_is_fake(const zend_closure *closure) +{ + return closure->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE; +} ZEND_METHOD(Closure, __invoke) /* {{{ */ { @@ -77,7 +93,9 @@ static bool zend_valid_closure_binding( zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */ { zend_function *func = &closure->func; - bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + // TODO: rename variable + bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0 + || (closure->std.extra_flags & ZEND_PARTIAL); if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9"); @@ -161,7 +179,9 @@ ZEND_METHOD(Closure, call) if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; - zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); + zend_create_closure_ex(&new_closure, &closure->func, newclass, + closure->called_scope, newthis, + zend_closure_is_fake(closure), zend_closure_flags(closure)); closure = (zend_closure *) Z_OBJ(new_closure); fci_cache.function_handler = &closure->func; @@ -177,6 +197,7 @@ ZEND_METHOD(Closure, call) memset(&fake_closure->std, 0, sizeof(fake_closure->std)); fake_closure->std.gc.refcount = 1; fake_closure->std.gc.u.type_info = GC_NULL; + fake_closure->std.extra_flags = zend_closure_flags(closure); ZVAL_UNDEF(&fake_closure->this_ptr); fake_closure->called_scope = NULL; my_function = &fake_closure->func; @@ -223,7 +244,7 @@ ZEND_METHOD(Closure, call) } /* }}} */ -static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) +static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) { zend_class_entry *ce, *called_scope; zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); @@ -235,14 +256,15 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z ce = closure->func.common.scope; } else if ((ce = zend_lookup_class(scope_str)) == NULL) { zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str)); - RETURN_NULL(); + RETVAL_NULL(); + return FAILURE; } } else { ce = NULL; } if (!zend_valid_closure_binding(closure, newthis, ce)) { - return; + return FAILURE; } if (newthis) { @@ -251,7 +273,34 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z called_scope = ce; } - zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); + zend_create_closure_ex(return_value, &closure->func, ce, called_scope, newthis, + zend_closure_is_fake(closure), zend_closure_flags(closure)); + + if (zend_closure_flags(closure) & ZEND_PARTIAL_OF_CLOSURE) { + /* Re-bind the inner closure */ + + zend_closure *new_closure = (zend_closure*)Z_OBJ_P(return_value); + HashTable *static_variables = ZEND_MAP_PTR_GET(new_closure->func.op_array.static_variables_ptr); + ZEND_ASSERT(static_variables->nNumOfElements > 0); + zval *inner = &static_variables->arData[0].val; + ZEND_ASSERT(Z_TYPE_P(inner) == IS_OBJECT && Z_OBJCE_P(inner) == zend_ce_closure); + + zval new_inner; + if (do_closure_bind(&new_inner, inner, newthis, scope_obj, scope_str) != SUCCESS) { + /* Should not happen, as we have already validated arguments and the + * inner closure should have the same constraints. */ + ZEND_UNREACHABLE(); + zval_ptr_dtor(return_value); + ZVAL_NULL(return_value); + return FAILURE; + } + + zend_object *garbage = Z_OBJ_P(inner); + ZVAL_COPY_VALUE(inner, &new_inner); + zend_object_release(garbage); + } + + return SUCCESS; } /* {{{ Create a closure from another one and bind to another object and scope */ @@ -588,8 +637,9 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */ zend_closure *closure = (zend_closure *)zobject; zval result; - zend_create_closure(&result, &closure->func, - closure->func.common.scope, closure->called_scope, &closure->this_ptr); + zend_create_closure_ex(&result, &closure->func, + closure->func.common.scope, closure->called_scope, &closure->this_ptr, + zend_closure_is_fake(closure), zend_closure_flags(closure)); return Z_OBJ(result); } /* }}} */ @@ -757,7 +807,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ } /* }}} */ -static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */ +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags) /* {{{ */ { zend_closure *closure; void *ptr; @@ -765,6 +815,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en object_init_ex(res, zend_ce_closure); closure = (zend_closure *)Z_OBJ_P(res); + closure->std.extra_flags = flags; if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) { /* use dummy scope if we're binding an object without specifying a scope */ @@ -862,14 +913,16 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) { zend_create_closure_ex(res, func, scope, called_scope, this_ptr, - /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0); + /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0, + /* flags */ 0); } ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; - zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true); + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, + /* is_fake */ true, /* flags */ 0); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; @@ -879,6 +932,16 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas } /* }}} */ +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure) +{ + uint32_t flags = ZEND_PARTIAL; + if (partial_of_closure) { + flags |= ZEND_PARTIAL_OF_CLOSURE; + } + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, + /* is_fake */ false, flags); +} + void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */ zval instance; zend_internal_function trampoline; diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 2ff4934f2a3a..305d82e5015a 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -36,6 +36,7 @@ extern ZEND_API zend_class_entry *zend_ce_closure; ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ac5a9d71a6ea..f44784239533 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -28,6 +28,8 @@ #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_types.h" +#include "zend_portability.h" +#include "zend_string.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" @@ -102,6 +104,8 @@ static void zend_compile_expr(znode *result, zend_ast *ast); static void zend_compile_stmt(zend_ast *ast); static void zend_compile_assign(znode *result, zend_ast *ast, bool stmt, uint32_t type); +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg); + #ifdef ZEND_CHECK_STACK_LIMIT zend_never_inline static void zend_stack_limit_error(void) { @@ -3771,12 +3775,16 @@ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg return (uint32_t) -1; } -static uint32_t zend_compile_args( - zend_ast *ast, const zend_function *fbc, bool *may_have_extra_named_args) /* {{{ */ +static uint32_t zend_compile_args_ex( + zend_ast *ast, const zend_function *fbc, + bool *may_have_extra_named_args, + bool is_call_partial, bool *uses_variadic_placeholder_p, + zval *named_positions) /* {{{ */ { const zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; bool uses_arg_unpack = false; + bool uses_variadic_placeholder = false; uint32_t arg_count = 0; /* number of arguments not including unpacks */ /* Whether named arguments are used syntactically, to enforce language level limitations. @@ -3802,6 +3810,11 @@ static uint32_t zend_compile_args( "Cannot use argument unpacking after named arguments"); } + if (is_call_partial) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + /* Unpack may contain named arguments. */ may_have_undef = true; if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) { @@ -3842,18 +3855,75 @@ static uint32_t zend_compile_args( may_have_undef = true; *may_have_extra_named_args = true; } + + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } } else { if (uses_arg_unpack) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after argument unpacking"); } - if (uses_named_args) { + bool is_variadic_placeholder = arg->kind == ZEND_AST_PLACEHOLDER_ARG + && arg->attr == ZEND_PLACEHOLDER_VARIADIC; + + if (uses_named_args && !is_variadic_placeholder) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after named argument"); } - arg_count++; + if (uses_variadic_placeholder) { + if (is_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder may only appear once"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } + } + + if (!is_variadic_placeholder) { + arg_count++; + } + } + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (uses_arg_unpack) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + + if (arg->attr == ZEND_PLACEHOLDER_VARIADIC) { + uses_variadic_placeholder = true; + /* Do not emit ZEND_SEND_PLACEHOLDER: We represent the variadic + * placeholder with a flag on the ZEND_CONVERT_CALLABLE_PARTIAL + * op instead. */ + continue; + } + + if (arg_name) { + if (Z_ISUNDEF_P(named_positions)) { + array_init(named_positions); + } + zval tmp; + ZVAL_LONG(&tmp, zend_hash_num_elements(Z_ARRVAL_P(named_positions))); + zend_hash_add(Z_ARRVAL_P(named_positions), arg_name, &tmp); + } + + opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); + if (arg_name) { + opline->op2_type = IS_CONST; + zend_string_addref(arg_name); + opline->op2.constant = zend_add_literal_string(&arg_name); + opline->result.num = zend_alloc_cache_slots(2); + } else if (arg->attr != ZEND_PLACEHOLDER_VARIADIC) { + opline->op2.opline_num = arg_num; + opline->result.var = EX_NUM_TO_VAR(arg_num - 1); + } + + continue; } /* Treat passing of $GLOBALS the same as passing a call. @@ -3968,14 +4038,23 @@ static uint32_t zend_compile_args( } } - if (may_have_undef) { - zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + if (!is_call_partial) { + if (may_have_undef) { + zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + } + } else { + *uses_variadic_placeholder_p = uses_variadic_placeholder; } return arg_count; } /* }}} */ +static uint32_t zend_compile_args(zend_ast *ast, const zend_function *fbc, bool *may_have_extra_named_args) +{ + return zend_compile_args_ex(ast, fbc, may_have_extra_named_args, false, NULL, NULL); +} + ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *fbc, bool result_used) /* {{{ */ { uint32_t no_discard = result_used ? 0 : ZEND_ACC_NODISCARD; @@ -4009,6 +4088,38 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f } /* }}} */ +static void zend_compile_call_partial(znode *result, uint32_t arg_count, + bool may_have_extra_named_args, bool uses_variadic_placeholder, + zval *named_positions, uint32_t opnum_init, const zend_function *fbc) { + + zend_op *init_opline = &CG(active_op_array)->opcodes[opnum_init]; + + init_opline->extended_value = arg_count; + + ZEND_ASSERT(init_opline->opcode != ZEND_NEW); + + if (init_opline->opcode == ZEND_INIT_FCALL) { + init_opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); + } + + zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, + NULL, NULL); + + opline->op1.num = zend_alloc_cache_slots(2); + + if (may_have_extra_named_args) { + opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; + } + if (uses_variadic_placeholder) { + opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER; + } + + if (!Z_ISUNDEF_P(named_positions)) { + opline->op2.constant = zend_add_literal(named_positions); + opline->op2_type = IS_CONST; + } +} + static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno, uint32_t type) /* {{{ */ { zend_op *opline; @@ -4024,23 +4135,43 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); } - zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders"); - } + zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (opcode == ZEND_INIT_FCALL) { - opline->op1.num = zend_vm_calc_used_stack(0, fbc); - } + /* FCCs are a special case of PFAs with a single variadic placeholder */ + if (fcc_args->children == 1 && fcc_args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC) { - zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); - if (opcode == ZEND_INIT_FCALL - || opcode == ZEND_INIT_FCALL_BY_NAME - || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { - callable_convert_op->extended_value = zend_alloc_cache_slot(); - } else { - callable_convert_op->extended_value = (uint32_t)-1; + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(0, fbc); + } + + zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); + if (opcode == ZEND_INIT_FCALL + || opcode == ZEND_INIT_FCALL_BY_NAME + || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { + callable_convert_op->extended_value = zend_alloc_cache_slot(); + } else { + callable_convert_op->extended_value = (uint32_t)-1; + } + + return true; } + + args_ast = ((zend_ast_fcc*)args_ast)->args; + + bool may_have_extra_named_args; + bool uses_variadic_placeholder; + + zval named_positions; + ZVAL_UNDEF(&named_positions); + + uint32_t arg_count = zend_compile_args_ex(args_ast, fbc, + &may_have_extra_named_args, true, &uses_variadic_placeholder, + &named_positions); + + zend_compile_call_partial(result, arg_count, + may_have_extra_named_args, uses_variadic_placeholder, + &named_positions, opnum_init, fbc); + return true; } @@ -5126,42 +5257,21 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg } zend_ast *callback = args->child[0]; - - /* Bail out if the callback is not a FCC/PFA. */ - zend_ast *args_ast; - switch (callback->kind) { - case ZEND_AST_CALL: - case ZEND_AST_STATIC_CALL: - args_ast = zend_ast_call_get_args(callback); - if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { - return FAILURE; - } - - break; - default: - return FAILURE; - } - - /* Bail out if the callback is assert() due to the AST stringification logic - * breaking for the generated call. - */ - if (callback->kind == ZEND_AST_CALL - && callback->child[0]->kind == ZEND_AST_ZVAL - && Z_TYPE_P(zend_ast_get_zval(callback->child[0])) == IS_STRING - && zend_string_equals_literal_ci(zend_ast_get_str(callback->child[0]), "assert")) { - return FAILURE; - } - - zend_ast_list *callback_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (callback_args->children != 1 || callback_args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - /* Full PFA is not yet implemented, will fail in zend_compile_call_common(). */ + if (callback->kind != ZEND_AST_CALL && callback->kind != ZEND_AST_STATIC_CALL) { return FAILURE; } znode value; value.op_type = IS_TMP_VAR; value.u.op.var = get_temporary_variable(); - zend_ast *call_args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&value)); + + zend_ast *call_args = zend_partial_apply(callback, + zend_ast_create_znode(&value)); + if (!call_args) { + CG(active_op_array)->T--; + /* The callback is not a FCC/PFA, or is not optimizable */ + return FAILURE; + } zend_op *opline; @@ -6769,6 +6879,76 @@ static bool zend_is_pipe_optimizable_callable_name(zend_ast *ast) return true; } +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) +{ + if (callable_ast->kind != ZEND_AST_CALL + && callable_ast->kind != ZEND_AST_STATIC_CALL + && callable_ast->kind != ZEND_AST_METHOD_CALL) { + return NULL; + } + + zend_ast *args_ast = zend_ast_call_get_args(callable_ast); + if (!args_ast || args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { + return NULL; + } + + if (callable_ast->kind == ZEND_AST_CALL && + !zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { + return NULL; + } + + zend_ast_list *arg_list = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + + zend_ast *first_placeholder = NULL; + bool uses_named_args = false; + + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg->kind == ZEND_AST_NAMED_ARG) { + uses_named_args = true; + arg = arg->child[1]; + } + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (first_placeholder == NULL) { + first_placeholder = arg; + } else { + /* A PFA with multiple placeholders is unexpected in this + * context, and will usually error due to a missing argument, + * so we don't optimize those. */ + return NULL; + } + if (arg->attr == ZEND_PLACEHOLDER_VARIADIC && uses_named_args) { + /* A PFA with both a variadic placeholder and named args can not + * be optimized because this would result in a positional arg + * after a named arg: f(name: $v, ...) -> f(name: $v, pipe_arg). + * Arg placeholders ('?') are safe since they are not allowed + * after named args. */ + return NULL; + } + } + } + + ZEND_ASSERT(first_placeholder); + + zend_ast *new_arg_list = zend_ast_create_list(0, arg_list->kind); + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg == first_placeholder) { + new_arg_list = zend_ast_list_add(new_arg_list, pipe_arg); + } else if (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1] == first_placeholder) { + zend_ast *name = arg->child[0]; + new_arg_list = zend_ast_list_add(new_arg_list, + zend_ast_create(ZEND_AST_NAMED_ARG, name, pipe_arg)); + } else { + new_arg_list = zend_ast_list_add(new_arg_list, arg); + } + } + + return new_arg_list; +} + static void zend_compile_pipe(znode *result, zend_ast *ast, uint32_t type) { zend_ast *operand_ast = ast->child[0]; @@ -6793,29 +6973,35 @@ static void zend_compile_pipe(znode *result, zend_ast *ast, uint32_t type) } /* Turn the operand into a function parameter list. */ - zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)); + zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result); zend_ast *fcall_ast; znode callable_result; + zend_ast *pfa_arg_list_ast = NULL; - /* Turn $foo |> bar(...) into bar($foo). */ - if (callable_ast->kind == ZEND_AST_CALL - && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT - && zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { - fcall_ast = zend_ast_create(ZEND_AST_CALL, - callable_ast->child[0], arg_list_ast); - /* Turn $foo |> bar::baz(...) into bar::baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_STATIC_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); - /* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_METHOD_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); + /* Turn $foo |> PFA into plain function call if possible */ + if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) { + switch (callable_ast->kind) { + case ZEND_AST_CALL: + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast->child[0], pfa_arg_list_ast); + break; + case ZEND_AST_STATIC_CALL: + fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + case ZEND_AST_METHOD_CALL: + fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + default: + ZEND_UNREACHABLE(); + } /* Turn $foo |> $expr into ($expr)($foo) */ } else { + zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_compile_expr(&callable_result, callable_ast); callable_ast = zend_ast_create_znode(&callable_result); fcall_ast = zend_ast_create(ZEND_AST_CALL, @@ -11834,6 +12020,13 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr) if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) { zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); } + + zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args); + if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { + // TODO: PFAs + zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); + } + ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr); switch ((*ast_ptr)->kind) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 2351882a560d..69d7aeb2f373 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -958,6 +958,7 @@ struct _zend_arena; ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type); ZEND_API zend_op_array *compile_string(zend_string *source_string, const char *filename, zend_compile_position position); ZEND_API zend_op_array *compile_filename(int type, zend_string *filename); +ZEND_API zend_op_array *zend_compile_ast(zend_ast *ast, int type, zend_string *filename); ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, struct _zend_arena **ast_arena, zend_string *filename); ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...); @@ -1119,7 +1120,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_THROW_IS_EXPR 1u -#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1 +#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0) +#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1) /* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1b28ce25fe37..c51a55bac4c9 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1232,6 +1232,13 @@ static zend_always_inline bool zend_check_type( return zend_check_type_slow(type, arg, ref, is_return_type, is_internal); } +/* We can not expose zend_check_type() directly because it's inline and uses static functions */ +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal) +{ + return zend_check_type(type, arg, is_return_type, is_internal); +} + ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type) { @@ -4676,6 +4683,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4732,6 +4740,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4812,6 +4821,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4869,6 +4879,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -5578,9 +5589,10 @@ zval * ZEND_FASTCALL zend_handle_named_arg( } } else { arg = ZEND_CALL_VAR_NUM(call, arg_offset); + if (UNEXPECTED(!Z_ISUNDEF_P(arg))) { - zend_throw_error(NULL, "Named parameter $%s overwrites previous argument", - ZSTR_VAL(arg_name)); + zend_throw_error(NULL, "Named parameter $%s overwrites previous %s", + ZSTR_VAL(arg_name), Z_TYPE_P(arg) == _IS_PLACEHOLDER ? "placeholder" : "argument"); return NULL; } } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ba48b19bcfe1..48f7e7a72530 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -109,6 +109,8 @@ ZEND_API zend_never_inline ZEND_COLD void zend_verify_never_error( ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type); +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index cb48e6234371..88b0a485750a 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -38,6 +38,7 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_frameless_function.h" +#include "zend_partial.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -201,6 +202,7 @@ void init_executor(void) /* {{{ */ zend_weakrefs_init(); zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0); EG(active) = 1; } @@ -418,6 +420,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1); zend_hash_clean(&EG(callable_convert_cache)); + zend_hash_clean(&EG(partial_function_application_cache)); #if ZEND_DEBUG if (!CG(unclean_shutdown)) { @@ -514,6 +517,7 @@ void shutdown_executor(void) /* {{{ */ } zend_hash_destroy(&EG(callable_convert_cache)); + zend_hash_destroy(&EG(partial_function_application_cache)); } #if ZEND_DEBUG diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index f78567cfaa74..83360a2c96d2 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -325,6 +325,7 @@ struct _zend_executor_globals { zend_strtod_state strtod_state; HashTable callable_convert_cache; + HashTable partial_function_application_cache; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 1d7e41a2a7d3..bf6bbe7f9d90 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -594,6 +594,43 @@ ZEND_API zend_result open_file_for_scanning(zend_file_handle *file_handle) return SUCCESS; } +static zend_op_array *zend_compile_ast_internal(int type) +{ + ZEND_ASSERT(CG(in_compilation)); + ZEND_ASSERT(CG(ast)); + + uint32_t last_lineno = CG(zend_lineno); + zend_file_context original_file_context; + zend_oparray_context original_oparray_context; + zend_op_array *original_active_op_array = CG(active_op_array); + + zend_op_array *op_array = emalloc(sizeof(*op_array)); + init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); + CG(active_op_array) = op_array; + + /* Use heap to not waste arena memory */ + op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + + if (zend_ast_process) { + zend_ast_process(CG(ast)); + } + + zend_file_context_begin(&original_file_context); + zend_oparray_context_begin(&original_oparray_context, op_array); + zend_compile_top_stmt(CG(ast)); + CG(zend_lineno) = last_lineno; + zend_emit_final_return(type == ZEND_USER_FUNCTION); + op_array->line_start = 1; + op_array->line_end = last_lineno; + pass_two(op_array); + zend_oparray_context_end(&original_oparray_context); + zend_file_context_end(&original_file_context); + + CG(active_op_array) = original_active_op_array; + + return op_array; +} + static zend_op_array *zend_compile(zend_function_type type) { zend_op_array *op_array = NULL; @@ -604,34 +641,7 @@ static zend_op_array *zend_compile(zend_function_type type) CG(ast_arena) = zend_arena_create(1024 * 32); if (!zendparse()) { - uint32_t last_lineno = CG(zend_lineno); - zend_file_context original_file_context; - zend_oparray_context original_oparray_context; - zend_op_array *original_active_op_array = CG(active_op_array); - - op_array = emalloc(sizeof(zend_op_array)); - init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); - CG(active_op_array) = op_array; - - /* Use heap to not waste arena memory */ - op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; - - if (zend_ast_process) { - zend_ast_process(CG(ast)); - } - - zend_file_context_begin(&original_file_context); - zend_oparray_context_begin(&original_oparray_context, op_array); - zend_compile_top_stmt(CG(ast)); - CG(zend_lineno) = last_lineno; - zend_emit_final_return(type == ZEND_USER_FUNCTION); - op_array->line_start = 1; - op_array->line_end = last_lineno; - pass_two(op_array); - zend_oparray_context_end(&original_oparray_context); - zend_file_context_end(&original_file_context); - - CG(active_op_array) = original_active_op_array; + op_array = zend_compile_ast_internal(type); } zend_ast_destroy(CG(ast)); @@ -674,6 +684,23 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type) return op_array; } +ZEND_API zend_op_array *zend_compile_ast( + zend_ast *ast, int type, zend_string *filename) +{ + zend_string *original_compiled_filename = CG(compiled_filename); + bool original_in_compilation = CG(in_compilation); + CG(in_compilation) = true; + CG(ast) = ast; + + zend_set_compiled_filename(filename); + zend_op_array *op_array = zend_compile_ast_internal(type); + + CG(in_compilation) = original_in_compilation; + zend_restore_compiled_filename(original_compiled_filename); + + return op_array; +} + ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, zend_arena **ast_arena, zend_string *filename) { zval code_zv; diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c new file mode 100644 index 000000000000..4095daa1f1a3 --- /dev/null +++ b/Zend/zend_partial.c @@ -0,0 +1,1163 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/** + * Partial Function Application: + * + * A partial application is compiled to the usual sequence of function call + * opcodes (INIT_FCALL, SEND_VAR, etc), but the sequence ends with a + * CALLABLE_CONVERT_PARTIAL opcode instead of DO_FCALL, similarly to + * first class callables. Placeholders are compiled to SEND_PLACEHOLDER opcodes: + * + * $f = f($a, ?) + * + * 0001 INIT_FCALL f + * 0002 SEND_VAR CV($a) + * 0003 SEND_PLACEHOLDER + * 0004 CV($f) = CALLABLE_CONVERT_PARTIAL + * + * SEND_PLACEHOLDER sets the argument slot type to _IS_PLACEHOLDER. + * + * CALLABLE_CONVERT_PARTIAL uses the information available on the stack to + * create a Closure and return it, consuming the stack frame in the process + * like an internal function call. + * + * We create the Closure by generating the relevant AST and compiling it to an + * op_array. The op_array is cached in the Opcache SHM and inline caches. + * + * This file implements the Closure generation logic + * (see zend_partial_create(), zp_compile()). + */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_arena.h" +#include "zend_ast.h" +#include "zend_compile.h" +#include "zend_closures.h" +#include "zend_attributes.h" +#include "zend_exceptions.h" +#include "ext/opcache/ZendAccelerator.h" + +static zend_always_inline bool Z_IS_PLACEHOLDER_P(const zval *p) { + return Z_TYPE_P(p) == _IS_PLACEHOLDER; +} + +static zend_always_inline bool zp_is_static_closure(const zend_function *function) { + return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)); +} + +static zend_always_inline bool zp_is_non_static_closure(const zend_function *function) { + return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == ZEND_ACC_CLOSURE); +} + +static zend_never_inline ZEND_COLD void zp_args_underflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + const char *limit = function->common.num_args <= function->common.required_num_args ? + "exactly" : "at least"; + + zend_argument_count_error( + "Partial application of %s() expects %s %d arguments, %d given", + ZSTR_VAL(symbol), limit, expected, args); + + zend_string_release(symbol); +} + +static zend_never_inline ZEND_COLD void zp_args_overflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + + zend_argument_count_error( + "Partial application of %s() expects at most %d arguments, %d given", + ZSTR_VAL(symbol), expected, args); + + zend_string_release(symbol); +} + +static zend_result zp_args_check(const zend_function *function, + uint32_t argc, const zval *argv, + const zend_array *extra_named_args, + bool uses_variadic_placeholder) { + + if (extra_named_args) { + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(extra_named_args, zend_string *key, zval *arg) { + if (UNEXPECTED(Z_IS_PLACEHOLDER_P(arg))) { + zend_throw_error(NULL, + "Cannot use named placeholder for unknown or variadic parameter $%s", + ZSTR_VAL(key)); + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + } + + if (argc < function->common.required_num_args) { + if (uses_variadic_placeholder) { + /* Missing args will be turned into placeholders */ + return SUCCESS; + } + + zp_args_underflow(function, argc, function->common.required_num_args); + return FAILURE; + } else if (argc > function->common.num_args + && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zp_args_overflow(function, argc, function->common.num_args); + return FAILURE; + } + + return SUCCESS; +} + +typedef struct zp_names { + zend_string *variadic_param; + zend_string *extra_named_params; + zend_string *inner_closure; + zend_string *params[1]; /* argc elements */ +} zp_names; + +static bool zp_name_exists(const zp_names *names, uint32_t argc, const zend_string *name) +{ + for (uint32_t i = 0; i < argc; i++) { + if (names->params[i] && zend_string_equals(names->params[i], name)) { + return true; + } + } + if (names->variadic_param && zend_string_equals(names->variadic_param, name)) { + return true; + } + if (names->extra_named_params && zend_string_equals(names->extra_named_params, name)) { + return true; + } + if (names->inner_closure && zend_string_equals(names->inner_closure, name)) { + return true; + } + + return false; +} + +static void zp_names_dtor(zp_names *names, uint32_t argc) +{ + for (uint32_t i = 0; i < argc; i++) { + if (names->params[i]) { + zend_string_release(names->params[i]); + } + } + if (names->variadic_param) { + zend_string_release(names->variadic_param); + } + if (names->extra_named_params) { + zend_string_release(names->extra_named_params); + } + if (names->inner_closure) { + zend_string_release(names->inner_closure); + } +} + +static zend_string *zp_get_func_param_name(const zend_function *function, uint32_t arg_offset) +{ + return zend_string_copy(function->common.arg_info[arg_offset].name); +} + +/* Assign a name for every variable that will be used in the generated closure, + * including params and used vars. */ +static zp_names *zp_assign_names(uint32_t argc, zval *argv, + zend_function *function, bool variadic_partial, + zend_array *extra_named_params) +{ + zp_names *names = zend_arena_calloc(&CG(ast_arena), + 1, zend_safe_address_guarded(argc, sizeof(*names->params), offsetof(zp_names, params))); + + /* Assign names for params. We never rename those. */ + for (uint32_t offset = 0, num_args = MIN(argc, function->common.num_args); + offset < num_args; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + names->params[offset] = zp_get_func_param_name(function, offset); + } + } + + /* Assign name for the variadic param. Never renamed. */ + if (variadic_partial && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + names->variadic_param = zp_get_func_param_name(function, function->common.num_args); + } + + /* Assign names for placeholders that bind to the variadic param: + * + * function f($a, ...$args) {} + * f(?, ?, ...); // The second placeholder binds into the variadic param. + * + * By default these are named $origNameN with N the offset from the + * variadic param. In case of clash we increment N until a free name is + * found. */ + for (uint32_t offset = function->common.num_args; offset < argc; offset++) { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + if (!Z_IS_PLACEHOLDER_P(&argv[offset])) { + continue; + } + zend_string *orig_name = zp_get_func_param_name(function, function->common.num_args); + zend_string *new_name; + for (uint32_t n = 0;; n++) { + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); + if (!zp_name_exists(names, argc, new_name)) { + break; + } + zend_string_release(new_name); + } + names->params[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign names for pre-bound params (lexical vars). + * There may be clashes, we ensure to generate unique names. */ + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) { + continue; + } + uint32_t n = 2; + zend_string *orig_name = zp_get_func_param_name(function, MIN(offset, function->common.num_args)); + zend_string *new_name = zend_string_copy(orig_name); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); + n++; + } + names->params[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign name for $extra_named_params */ + if (extra_named_params) { + uint32_t n = 2; + zend_string *new_name = ZSTR_INIT_LITERAL("extra_named_params", 0); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + new_name = zend_strpprintf(0, "%s%" PRIu32, "extra_named_params", n); + n++; + } + names->extra_named_params = new_name; + } + + /* Assign name for $fn */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + uint32_t n = 2; + zend_string *new_name = ZSTR_INIT_LITERAL("fn", 0); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + new_name = zend_strpprintf(0, "%s%" PRIu32, "fn", n); + n++; + } + names->inner_closure = new_name; + } + + return names; +} + +ZEND_ATTRIBUTE_CONST static inline bool zp_is_power_of_two(uint32_t x) +{ + return (x > 0) && !(x & (x - 1)); +} + +ZEND_ATTRIBUTE_CONST static bool zp_is_simple_type(uint32_t type_mask) +{ + ZEND_ASSERT(!(type_mask & ~_ZEND_TYPE_MAY_BE_MASK)); + + return zp_is_power_of_two(type_mask) + || type_mask == MAY_BE_BOOL + || type_mask == MAY_BE_ANY; +} + +static zend_ast *zp_simple_type_to_ast(uint32_t type) +{ + zend_string *name; + + switch (type) { + case MAY_BE_NULL: + name = ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE); + break; + case MAY_BE_TRUE: + name = ZSTR_KNOWN(ZEND_STR_TRUE); + break; + case MAY_BE_FALSE: + name = ZSTR_KNOWN(ZEND_STR_FALSE); + break; + case MAY_BE_LONG: + name = ZSTR_KNOWN(ZEND_STR_INT); + break; + case MAY_BE_DOUBLE: + name = ZSTR_KNOWN(ZEND_STR_FLOAT); + break; + case MAY_BE_STRING: + name = ZSTR_KNOWN(ZEND_STR_STRING); + break; + case MAY_BE_BOOL: + name = ZSTR_KNOWN(ZEND_STR_BOOL); + break; + case MAY_BE_VOID: + name = ZSTR_KNOWN(ZEND_STR_VOID); + break; + case MAY_BE_NEVER: + name = ZSTR_KNOWN(ZEND_STR_NEVER); + break; + case MAY_BE_OBJECT: + name = ZSTR_KNOWN(ZEND_STR_OBJECT); + break; + case MAY_BE_ANY: + name = ZSTR_KNOWN(ZEND_STR_MIXED); + break; + case MAY_BE_CALLABLE: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); + case MAY_BE_ARRAY: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); + case MAY_BE_STATIC: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC); + default: + ZEND_UNREACHABLE(); + } + + zend_ast *ast = zend_ast_create_zval_from_str(name); + ast->attr = ZEND_NAME_NOT_FQ; + + return ast; +} + +static zend_ast *zp_type_name_to_ast(zend_string *name) +{ + zend_ast *ast = zend_ast_create_zval_from_str(name); + + if (zend_get_class_fetch_type(name) != ZEND_FETCH_CLASS_DEFAULT) { + ast->attr = ZEND_NAME_NOT_FQ; + } else { + ast->attr = ZEND_NAME_FQ; + } + + return ast; +} + +static zend_ast *zp_type_to_ast(const zend_type type) +{ + if (!ZEND_TYPE_IS_SET(type)) { + return NULL; + } + + if (ZEND_TYPE_IS_UNION(type) + || (ZEND_TYPE_IS_COMPLEX(type) && ZEND_TYPE_PURE_MASK(type)) + || (ZEND_TYPE_PURE_MASK(type) && !zp_is_simple_type(ZEND_TYPE_PURE_MASK(type)))) { + /* This is a union type */ + + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_UNION); + + /* Add complex types if any */ + if (ZEND_TYPE_HAS_LIST(type)) { + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), const zend_type *type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(type)) { + zend_ast *name_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + type_ast = zend_ast_list_add(type_ast, name_ast); + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + } + + /* Add simple types if any */ + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + + /* IS_TRUE|IS_FALSE is represented as a single bool node */ + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(MAY_BE_BOOL)); + type_mask &= ~MAY_BE_BOOL; + } + for (uint32_t may_be_type = 1; may_be_type < _ZEND_TYPE_MAY_BE_MASK; may_be_type <<= 1) { + if (type_mask & may_be_type) { + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(may_be_type)); + } + } + + return type_ast; + } + + if (ZEND_TYPE_IS_INTERSECTION(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_INTERSECTION); + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), const zend_type *type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + return type_ast; + } + + if (ZEND_TYPE_HAS_NAME(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + zend_ast *type_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + return type_ast; + } + + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + ZEND_ASSERT(zp_is_simple_type(type_mask)); + + return zp_simple_type_to_ast(type_mask); +} + +static zend_result zp_get_param_default_value(zval *result, zend_function *function, uint32_t arg_offset) +{ + ZEND_ASSERT(arg_offset < function->common.num_args); + + if (function->type == ZEND_USER_FUNCTION) { + zend_op *opline = &function->op_array.opcodes[arg_offset]; + if (EXPECTED(opline->opcode == ZEND_RECV_INIT)) { + ZVAL_COPY(result, RT_CONSTANT(opline, opline->op2)); + return SUCCESS; + } + ZEND_ASSERT(opline->opcode == ZEND_RECV); + } else { + if (function->common.fn_flags & ZEND_ACC_USER_ARG_INFO) { + goto error; + } + + const zend_arg_info *arg_info = &function->internal_function.arg_info[arg_offset]; + + if (zend_get_default_from_internal_arg_info(result, arg_info) == SUCCESS) { + return SUCCESS; + } + } + +error: + zend_argument_error_ex(zend_ce_argument_count_error, function, arg_offset + 1, + "must be passed explicitly, because the default value is not known"); + + return FAILURE; +} + +static bool zp_arg_must_be_sent_by_ref(const zend_function *function, uint32_t arg_num) +{ + if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) { + if (QUICK_ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + } else if (ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + return false; +} + +static zend_ast *zp_attribute_to_ast(zend_attribute *attribute) +{ + zend_ast *args_ast; + if (attribute->argc) { + args_ast = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); + for (uint32_t i = 0; i < attribute->argc; i++) { + zend_ast *arg_ast = zend_ast_create_zval(&attribute->args[i].value); + if (attribute->args[i].name) { + arg_ast = zend_ast_create(ZEND_AST_NAMED_ARG, + zend_ast_create_zval_from_str( + zend_string_copy(attribute->args[i].name)), + arg_ast); + } + args_ast = zend_ast_list_add(args_ast, arg_ast); + } + } else { + args_ast = NULL; + } + return zend_ast_create(ZEND_AST_ATTRIBUTE, + zend_ast_create_zval_from_str(zend_string_copy(attribute->name)), + args_ast); +} + +static zend_ast *zp_param_attributes_to_ast(zend_function *function, + uint32_t offset) +{ + zend_ast *attributes_ast = NULL; + if (!function->common.attributes) { + return NULL; + } + + /* Inherit the SensitiveParameter attribute */ + zend_attribute *attr = zend_get_parameter_attribute_str( + function->common.attributes, + "sensitiveparameter", strlen("sensitiveparameter"), offset); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + + return attributes_ast; +} + +static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline) +{ + /* We attempt to generate a name that hints at where the PFA was created, + * similarly to Closures (GH-13550). + * We do not attempt to make the name unique. */ + + zend_string *filename = declaring_op_array->filename; + uint32_t start_lineno = declaring_opline->lineno; + + zend_string *class = zend_empty_string; + zend_string *separator = zend_empty_string; + zend_string *function = filename; + const char *parens = ""; + + if (declaring_op_array->function_name) { + function = declaring_op_array->function_name; + if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) { + /* If the parent function is a closure, don't redundantly + * add the classname and parentheses. */ + } else { + parens = "()"; + + if (declaring_op_array->scope && declaring_op_array->scope->name) { + class = declaring_op_array->scope->name; + separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM); + } + } + } + + zend_string *name = zend_strpprintf_unchecked( + 0, + "{closure:pfa:%S%S%S%s:%" PRIu32 "}", + class, + separator, + function, + parens, + start_lineno + ); + + return name; +} + +/* Generate the AST for calling the actual function */ +static zend_ast *zp_compile_forwarding_call( + zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + zp_names *var_names, bool uses_variadic_placeholder, uint32_t num_args, + zend_class_entry *called_scope, zend_type return_type, + bool forward_superfluous_args, + zend_ast *stmts_ast) +{ + bool is_assert = zend_string_equals(function->common.function_name, + ZSTR_KNOWN(ZEND_STR_ASSERT)); + + zend_ast *args_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *call_ast = NULL; + + if (is_assert) { + /* We are going to call assert() dynamically (via call_user_func), + * otherwise assert() would print the generated AST on failure, which is + * irrelevant. */ + args_ast = zend_ast_list_add(args_ast, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ASSERT))); + } + + /* Generate positional arguments */ + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_ISUNDEF(argv[offset])) { + /* Argument was not passed. Pass its default value. */ + if (offset < function->common.required_num_args) { + /* Required param was not passed. This can happen due to named + * args. Using the same exception CE and message as + * zend_handle_undef_args(). */ + zend_argument_error_ex(zend_ce_argument_count_error, function, offset + 1, "not passed"); + goto error; + } + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + ZEND_ASSERT(EG(exception)); + goto error; + } + zend_ast *default_value_ast; + if (Z_TYPE(default_value) == IS_CONSTANT_AST) { + /* Must dup AST because we are going to destroy it */ + default_value_ast = zend_ast_dup(Z_ASTVAL(default_value)); + } else { + default_value_ast = zend_ast_create_zval(&default_value); + } + args_ast = zend_ast_list_add(args_ast, default_value_ast); + } else { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset])))); + } + } + /* Use unpacking to pass extra named params */ + if (extra_named_params) { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->extra_named_params))))); + } + if (uses_variadic_placeholder) { + if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + /* Pass variadic params */ + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->variadic_param))))); + } else if (forward_superfluous_args) { + /* When a '...' placeholder is used, and the underlying function is + * not variadic, superfluous arguments are forwarded. + * Add a ...array_slice(func_get_args(), n) argument, which should + * be compiled as ZEND_AST_UNPACK + ZEND_FUNC_GET_ARGS. */ + + zend_ast *func_get_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_GET_ARGS))); + func_get_args_name_ast->attr = ZEND_NAME_FQ; + + zend_ast *array_slice_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_ARRAY_SLICE))); + array_slice_name_ast->attr = ZEND_NAME_FQ; + + args_ast = zend_ast_list_add(args_ast, + zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_CALL, + array_slice_name_ast, + zend_ast_create_list(2, ZEND_AST_ARG_LIST, + zend_ast_create(ZEND_AST_CALL, + func_get_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_args))))); + } + } + + if (is_assert) { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CALL_USER_FUNC)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } else if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *fn_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->inner_closure))); + call_ast = zend_ast_create(ZEND_AST_CALL, fn_ast, args_ast); + } else if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + zend_ast *this_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_THIS))); + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_METHOD_CALL, this_ast, + method_name_ast, args_ast); + } else if (called_scope) { + zend_ast *class_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_STATIC)); + class_name_ast->attr = ZEND_NAME_NOT_FQ; + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_STATIC_CALL, class_name_ast, + method_name_ast, args_ast); + } else { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(zend_string_copy(function->common.function_name)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } + + /* Void functions can not 'return $expr' */ + if (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_VOID) { + stmts_ast = zend_ast_list_add(stmts_ast, call_ast); + } else { + zend_ast *return_ast = zend_ast_create(ZEND_AST_RETURN, call_ast); + stmts_ast = zend_ast_list_add(stmts_ast, return_ast); + } + + return stmts_ast; + +error: + zend_ast_destroy(args_ast); + zend_ast_destroy(call_ast); + return NULL; +} + +static uint32_t zp_compute_num_required(const zend_function *function, + uint32_t orig_offset, uint32_t new_offset, uint32_t num_required) { + if (orig_offset < function->common.num_args) { + if (orig_offset < function->common.required_num_args) { + num_required = MAX(num_required, new_offset + 1); + } + } else { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + /* Placeholders that run into the variadic portion become + * required and make all params before them required */ + ZEND_ASSERT(orig_offset >= num_required); + num_required = new_offset + 1; + } + + return num_required; +} + +/* Compile PFA to an op_array */ +static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + zend_op_array *op_array = NULL; + + if (UNEXPECTED(function->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)) { + const char *format = "Cannot call %S() dynamically"; + zend_throw_error(NULL, format, function->common.function_name); + return NULL; + } + + if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) { + ZEND_ASSERT(EG(exception)); + return NULL; + } + + zend_class_entry *called_scope; + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + /* CG(ast_arena) is usually NULL, so we can't just make a snapshot */ + zend_arena *orig_ast_arena = CG(ast_arena); + CG(ast_arena) = zend_arena_create(1024 * 4); + + uint32_t orig_lineno = CG(zend_lineno); + CG(zend_lineno) = zend_get_executed_lineno(); + + uint32_t new_argc = argc; + + if (uses_variadic_placeholder) { + /* A variadic placeholder generates an implicit positional placeholder + * for any unspecified arg, so make room for those. */ + new_argc = MAX(new_argc, function->common.num_args); + } + + zval *tmp = zend_arena_alloc(&CG(ast_arena), zend_safe_address_guarded(new_argc, sizeof(*tmp), 0)); + memcpy(tmp, argv, zend_safe_address_guarded(argc, sizeof(*tmp), 0)); + argv = tmp; + + /* Compute param positions and number of required args, add implicit + * placeholders. + * + * Parameters are placed in the following order: + * - Positional placeholders + * - Then named placeholders in their syntax order + * - Then implicit placeholders added by '...' + */ + uint32_t num_params = 0; + uint32_t num_required = 0; + uint32_t *arg_to_param_offset_map = zend_arena_alloc(&CG(ast_arena), + zend_safe_address_guarded(new_argc, sizeof(*arg_to_param_offset_map), 0)); + { + uint32_t num_positional = 0; + + /* First, we handle explicit placeholders */ + for (uint32_t arg_offset = 0; arg_offset < argc; arg_offset++) { + if (!Z_IS_PLACEHOLDER_P(&argv[arg_offset])) { + continue; + } + + num_params++; + + zend_arg_info *arg_info = &function->common.arg_info[MIN(arg_offset, function->common.num_args)]; + zval *named_pos = named_positions ? zend_hash_find(named_positions, arg_info->name) : NULL; + uint32_t param_offset; + if (named_pos) { + /* Placeholder is sent as named arg. 'num_positional' is fixed + * at this point. */ + param_offset = num_positional + Z_LVAL_P(named_pos); + } else { + /* Placeholder is sent as positional */ + param_offset = num_positional++; + } + + arg_to_param_offset_map[arg_offset] = param_offset; + } + + num_required = num_params; + + /* Handle implicit placeholders added by '...' */ + if (uses_variadic_placeholder) { + for (uint32_t arg_offset = 0; arg_offset < new_argc; arg_offset++) { + if (arg_offset < argc && !Z_ISUNDEF(argv[arg_offset])) { + continue; + } + + /* Unspecified parameters become placeholders */ + Z_TYPE_INFO(argv[arg_offset]) = _IS_PLACEHOLDER; + + num_params++; + + uint32_t param_offset = num_params - 1; + + arg_to_param_offset_map[arg_offset] = param_offset; + + num_required = zp_compute_num_required(function, + arg_offset, param_offset, num_required); + } + } + } + + argc = new_argc; + + /* Assign variable names */ + + zp_names *var_names = zp_assign_names(argc, argv, function, + uses_variadic_placeholder, extra_named_params); + + /* Generate AST */ + + zend_ast *lexical_vars_ast = zend_ast_create_list(0, ZEND_AST_CLOSURE_USES); + zend_ast *params_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *return_type_ast = NULL; + zend_ast *stmts_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *attributes_ast = NULL; + + /* Generate AST for params and lexical vars */ + { + /* The inner Closure, if any, is assumed to be the first lexical var by + * do_closure_bind(). */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->inner_closure)); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + zend_ast **params = zend_arena_calloc(&CG(ast_arena), num_params, sizeof(*params)); + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + zend_arg_info *arg_info = &function->common.arg_info[MIN(offset, function->common.num_args)]; + + int param_flags = 0; + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + param_flags |= ZEND_PARAM_REF; + } + + uint32_t param_offset = arg_to_param_offset_map[offset]; + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *default_value_ast = NULL; + if (param_offset >= num_required) { + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + for (uint32_t i = 0; i < num_params; i++) { + zend_ast_destroy(params[i]); + } + goto error; + } + default_value_ast = zend_ast_create_zval(&default_value); + } + + ZEND_ASSERT(offset < function->common.num_args || (function->common.fn_flags & ZEND_ACC_VARIADIC)); + + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, MIN(offset, function->common.num_args)); + params[param_offset] = zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(var_names->params[offset])), + default_value_ast, attributes_ast, NULL, NULL); + + } else if (!Z_ISUNDEF(argv[offset])) { + // TODO: If the pre-bound parameter is a literal, it can be a + // literal in the function body instead of a lexical var. + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->params[offset])); + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + lexical_var_ast->attr = ZEND_BIND_REF; + } + lexical_vars_ast = zend_ast_list_add( + lexical_vars_ast, lexical_var_ast); + } + } + + for (uint32_t i = 0; i < num_params; i++) { + params_ast = zend_ast_list_add(params_ast, params[i]); + } + } + + if (extra_named_params) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->extra_named_params)); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + /* If we have a variadic placeholder and the underlying function is + * variadic, add a variadic param. */ + if (uses_variadic_placeholder + && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_arg_info *arg_info = &function->common.arg_info[function->common.num_args]; + int param_flags = ZEND_PARAM_VARIADIC; + if (zp_arg_must_be_sent_by_ref(function, function->common.num_args+1)) { + param_flags |= ZEND_PARAM_REF; + } + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, function->common.num_args); + params_ast = zend_ast_list_add(params_ast, zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(var_names->variadic_param)), + NULL, attributes_ast, NULL, NULL)); + } + + zend_type return_type = {0}; + if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + return_type = (function->common.arg_info-1)->type; + return_type_ast = zp_type_to_ast(return_type); + } + + /** + * Generate function body. + * + * If we may need to forward superflous arguments, do that conditionally, as + * it's faster: + * + * if (func_num_args() <= n) { + * // normal call + * } else { + * // call with superflous arg forwarding + * } + * + * The func_num_args() call should be compiled to a single FUNC_NUM_ARGS op. + */ + + if (uses_variadic_placeholder && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_ast *no_forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + + no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, no_forwarding_ast); + + if (!no_forwarding_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + + forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, true, forwarding_ast); + + if (!forwarding_ast) { + ZEND_ASSERT(EG(exception)); + zend_ast_destroy(no_forwarding_ast); + goto error; + } + + zend_ast *func_num_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_NUM_ARGS))); + func_num_args_name_ast->attr = ZEND_NAME_FQ; + + stmts_ast = zend_ast_list_add(stmts_ast, + zend_ast_create_list(2, ZEND_AST_IF, + zend_ast_create(ZEND_AST_IF_ELEM, + zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL, + zend_ast_create(ZEND_AST_CALL, func_num_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_params)), + no_forwarding_ast), + zend_ast_create(ZEND_AST_IF_ELEM, + NULL, + forwarding_ast))); + } else { + stmts_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, stmts_ast); + + if (!stmts_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + } + + /* Inherit the NoDiscard attribute */ + if (function->common.attributes) { + zend_attribute *attr = zend_get_attribute_str( + function->common.attributes, "nodiscard", strlen("nodiscard")); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + } + + int closure_flags = function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE; + zend_ast *closure_ast = zend_ast_create_decl(ZEND_AST_CLOSURE, + closure_flags, CG(zend_lineno), NULL, + NULL, params_ast, lexical_vars_ast, stmts_ast, + return_type_ast, attributes_ast); + + if (Z_TYPE_P(this_ptr) != IS_OBJECT && !zp_is_non_static_closure(function)) { + ((zend_ast_decl*)closure_ast)->flags |= ZEND_ACC_STATIC; + } + +#if ZEND_DEBUG + { + const char *tmp = getenv("DUMP_PFA_AST"); + if (tmp && ZEND_ATOL(tmp)) { + zend_string *str = zend_ast_export("", closure_ast, ""); + fprintf(stderr, "PFA AST: %s\n", ZSTR_VAL(str)); + zend_string_release(str); + } + } +#endif + + zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline); + + op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array, + declaring_opline, function, pfa_name); + + zend_ast_destroy(closure_ast); + +clean: + zp_names_dtor(var_names, argc); + zend_arena_destroy(CG(ast_arena)); + CG(ast_arena) = orig_ast_arena; + CG(zend_lineno) = orig_lineno; + + return op_array; + +error: + zend_ast_destroy(lexical_vars_ast); + zend_ast_destroy(params_ast); + zend_ast_destroy(return_type_ast); + zend_ast_destroy(stmts_ast); + zend_ast_destroy(attributes_ast); + goto clean; +} + +/* Get the op_array of a PFA from caches or compile it */ +static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION + ? cache_slot[0] == function + : cache_slot[0] == function->op_array.opcodes)) { + ZEND_ASSERT(!(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)); + return cache_slot[1]; + } + + const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array, + declaring_opline, function); + + if (UNEXPECTED(!op_array)) { + op_array = zp_compile(this_ptr, function, argc, argv, + extra_named_params, named_positions, declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + } + + if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) { + cache_slot[0] = function->type == ZEND_INTERNAL_FUNCTION + ? (void*)function + : (void*)function->op_array.opcodes; + cache_slot[1] = (zend_op_array*)op_array; + } + + return op_array; +} + +/* Bind pre-bound arguments as lexical vars */ +static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv, + zend_array *extra_named_params) { + + zend_arg_info *arg_infos = function->common.arg_info; + uint32_t bind_offset = 0; + + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zval var; + ZVAL_OBJ(&var, ZEND_CLOSURE_OBJECT(function)); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + bind_offset += sizeof(Bucket); + } + + for (uint32_t offset = 0; offset < argc; offset++) { + zval *var = &argv[offset]; + if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) { + continue; + } + zend_arg_info *arg_info; + if (offset < function->common.num_args) { + arg_info = &arg_infos[offset]; + } else if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + arg_info = &arg_infos[function->common.num_args]; + } else { + arg_info = NULL; + } + if (arg_info && ZEND_TYPE_IS_SET(arg_info->type) + && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var, 0, 0))) { + zend_verify_arg_error(function, arg_info, offset+1, var); + zval_ptr_dtor(result); + ZVAL_NULL(result); + return; + } + ZEND_ASSERT(zp_arg_must_be_sent_by_ref(function, offset+1) ? Z_ISREF_P(var) : !Z_ISREF_P(var)); + zend_closure_bind_var_ex(result, bind_offset, var); + bind_offset += sizeof(Bucket); + } + + if (extra_named_params) { + zval var; + ZVAL_ARR(&var, extra_named_params); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + } +} + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv, + extra_named_params, named_positions, + declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + + if (UNEXPECTED(!op_array)) { + ZEND_ASSERT(EG(exception)); + ZVAL_NULL(result); + return; + } + + zend_class_entry *called_scope; + zval object; + + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + if (Z_TYPE_P(this_ptr) == IS_OBJECT && !zp_is_static_closure(function)) { + ZVAL_COPY_VALUE(&object, this_ptr); + } else { + ZVAL_UNDEF(&object); + } + + zend_create_partial_closure(result, (zend_function*)op_array, + function->common.scope, called_scope, &object, + (function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); + + zp_bind(result, function, argc, argv, extra_named_params); +} + +void zend_partial_op_array_dtor(zval *pDest) +{ + destroy_op_array(Z_PTR_P(pDest)); +} diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h new file mode 100644 index 000000000000..3c47305ff543 --- /dev/null +++ b/Zend/zend_partial.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ +#ifndef ZEND_PARTIAL_H +#define ZEND_PARTIAL_H + +#include "zend_compile.h" + +BEGIN_EXTERN_C() + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder); + +void zend_partial_op_array_dtor(zval *pDest); + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_string.h b/Zend/zend_string.h index fdfff2bbc894..cb0502891efa 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -707,6 +707,11 @@ default: ZEND_UNREACHABLE(); _(ZEND_STR_AUTOGLOBAL_ENV, "_ENV") \ _(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \ _(ZEND_STR_COUNT, "count") \ + _(ZEND_STR_FUNC_NUM_ARGS, "func_num_args") \ + _(ZEND_STR_FUNC_GET_ARGS, "func_get_args") \ + _(ZEND_STR_ASSERT, "assert") \ + _(ZEND_STR_CALL_USER_FUNC, "call_user_func") \ + _(ZEND_STR_ARRAY_SLICE, "array_slice") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 56774e4e9d8f..b30f53d7f917 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -943,6 +943,8 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 #define IS_OBJ_LAZY_UNINITIALIZED (1U<<31) /* Virtual proxy or uninitialized Ghost */ #define IS_OBJ_LAZY_PROXY (1U<<30) /* Virtual proxy (may be initialized) */ +#define OBJ_EXTRA_FLAG_PRIV_1 (1U<<29) /* Reserved for private use by the object itself */ +#define OBJ_EXTRA_FLAG_PRIV_2 (1U<<28) /* Reserved for private use by the object itself */ #define OBJ_EXTRA_FLAGS(obj) ((obj)->extra_flags) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 61f89435843e..0e5efa6f826c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5726,6 +5726,30 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(213, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED|NUM) +{ + zval *arg; + + if (OP2_TYPE == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED) { USE_OPLINE @@ -9842,6 +9866,45 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NUM) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = GET_OP2_ZVAL_PTR(); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + ZEND_VM_HANDLER(208, ZEND_JMP_FRAMELESS, CONST, JMP_ADDR, NUM|CACHE_SLOT) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bcf4df0c8e74..6b675837d3a7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -306,6 +306,7 @@ static uint8_t zend_user_opcodes[256] = {0, }; #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" #define SPEC_START_MASK 0x0000ffff #define SPEC_EXTRA_MASK 0xfffc0000 @@ -4272,6 +4273,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_R ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4416,6 +4456,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_RECV_VARIADIC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -34472,6 +34551,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CHECK_FUNC_AR ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -37099,6 +37202,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_C ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56962,6 +57089,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_I ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57106,6 +57272,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_VARIADIC_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -86960,6 +87165,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_FUNC_ARG_SPE ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -89587,6 +89816,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -109275,6 +109528,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -110679,6 +110942,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_INIT_SPEC_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP): VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP) ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -110694,6 +110962,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_VARIADIC_SPEC_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED): VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED) ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -113917,6 +114190,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_FUNC_ARG_SPEC_UNUSED_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST): VM_TRACE(ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST) ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -114097,6 +114375,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_NEW_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_NEW_SPEC_UNUSED_UNUSED) ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118213,6 +118496,16 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_NULL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -121691,6 +121984,16 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_TAILCALL_HANDLER, ZEND_RECV_NOTYPE_SPEC_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, @@ -122659,7 +122962,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3474, + 3484, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -122693,7 +122996,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3474, + 3484, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -122826,50 +123129,50 @@ void zend_vm_init(void) 2556, 2557, 2558, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, + 2559 | SPEC_RULE_OP2, + 2564 | SPEC_RULE_OP2, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, }; #if 0 #elif (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) @@ -123062,7 +123365,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2567 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2577 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123070,7 +123373,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2592 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2602 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123078,7 +123381,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2627 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123089,17 +123392,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2642 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2652 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2667 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2677 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2702 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -123110,17 +123413,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2717 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2727 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2742 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2752 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2777 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -123131,16 +123434,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2877 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3092 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3102 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3098 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3108 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -123151,16 +123454,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2952 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3027 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3095 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3105 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3103 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3113 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -123171,12 +123474,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2877 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -123187,12 +123490,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2952 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3027 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -123200,12 +123503,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3108 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3118 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3183 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3193 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -123213,79 +123516,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3258 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3268 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3333 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3343 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3420 | SPEC_RULE_OP1; + spec = 3430 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3425 | SPEC_RULE_OP1; + spec = 3435 | SPEC_RULE_OP1; } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3430 | SPEC_RULE_OP1; + spec = 3440 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3408 | SPEC_RULE_RETVAL; + spec = 3418 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3410 | SPEC_RULE_RETVAL; + spec = 3420 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3412 | SPEC_RULE_RETVAL; + spec = 3422 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3414 | SPEC_RULE_RETVAL; + spec = 3424 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3416; + spec = 3426; } else if (op1_info == MAY_BE_LONG) { - spec = 3417; + spec = 3427; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3418; + spec = 3428; } else if (op1_info == MAY_BE_LONG) { - spec = 3419; + spec = 3429; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2566; + spec = 2576; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2559; + spec = 2569; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2560; + spec = 2570; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3470; + spec = 3480; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3465 | SPEC_RULE_OP1; + spec = 3475 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3472 | SPEC_RULE_RETVAL; + spec = 3482 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -123293,22 +123596,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3435 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3445 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3471; + spec = 3481; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3460 | SPEC_RULE_OP1; + spec = 3470 | SPEC_RULE_OP1; } break; case ZEND_COUNT: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 2561 | SPEC_RULE_OP1; + spec = 2571 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index beabe36688df..4e8f28270bae 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -1,4 +1,5 @@ #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" {%DEFINES%} diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 6f1595195450..7ffe2c220a02 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1087,507 +1087,511 @@ _(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ _(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \ - _(2559, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2560, ZEND_RECV_NOTYPE_SPEC) \ - _(2562, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ - _(2565, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2566, ZEND_JMP_FORWARD_SPEC) \ - _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2576, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2577, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2578, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2579, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2581, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) \ + _(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \ + _(2564, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \ + _(2567, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ + _(2569, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2570, ZEND_RECV_NOTYPE_SPEC) \ + _(2572, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ + _(2575, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2576, ZEND_JMP_FORWARD_SPEC) \ + _(2582, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2583, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2584, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2586, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2587, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2588, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2589, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2601, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2602, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2603, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2604, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2606, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2597, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2599, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2601, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2607, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2608, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2609, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2611, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2612, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2613, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2614, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2626, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2627, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2628, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2629, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2631, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2624, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2626, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2632, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2633, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2634, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2636, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2637, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2638, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2639, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2651, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2652, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2649, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2651, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2657, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2658, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2659, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2661, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2664, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2669, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2676, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2677, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2678, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2679, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2681, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2676, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2678, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2679, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2681, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2682, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2683, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2684, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2686, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2687, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2688, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2689, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2694, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2696, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2701, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2702, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2703, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2704, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2706, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2699, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2701, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2703, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2704, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2706, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2707, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2708, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2709, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2711, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2712, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2713, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2714, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2726, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2727, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2728, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2729, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2731, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2724, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2726, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2732, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2733, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2734, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2736, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2737, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2738, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2739, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2751, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2752, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2753, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2754, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2756, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2747, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2749, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2751, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2757, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2758, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2759, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2761, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2762, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2763, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2764, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2776, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2777, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2778, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2779, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2781, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2774, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2776, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2782, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2783, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2784, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2786, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2787, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2788, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2789, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3092, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3093, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3094, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3095, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3096, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3097, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3098, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3102, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3103, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3107, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3111, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3112, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3113, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3114, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3115, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3120, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3124, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3125, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3408, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3409, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3410, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3411, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3412, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3413, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3414, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3415, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3416, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3417, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3418, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3419, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3420, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3421, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3422, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3424, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3425, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3426, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3427, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3429, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3430, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3431, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3432, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3434, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3437, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3439, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3440, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3442, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3444, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3445, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3462, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3464, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3467, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3469, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3470, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3471, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3472, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3473, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3473+1, ZEND_NULL) + _(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2799, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2801, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2862, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2863, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2867, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2868, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2869, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2870, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2937, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2938, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2942, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2943, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2944, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2945, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3012, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3013, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3017, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3018, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3019, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3020, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3087, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3088, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3092, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3093, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3094, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3095, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3102, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3103, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3104, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3105, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3106, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3107, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3108, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3112, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3113, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3117, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3123, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3124, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3125, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3126, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3130, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3131, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3132, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3133, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3134, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3161, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3178, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3179, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3183, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3184, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3185, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3186, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3190, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3191, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3236, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3253, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3254, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3258, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3259, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3260, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3261, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3265, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3266, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3311, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3328, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3333, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3334, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3335, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3336, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3340, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3386, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3403, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3404, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3408, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3409, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3410, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3411, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3415, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3416, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3418, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3419, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3420, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3421, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3422, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3423, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3424, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3425, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3426, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3427, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3428, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3429, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3430, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ + _(3431, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3432, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3434, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3435, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ + _(3436, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3437, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3439, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3440, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ + _(3441, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3442, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3444, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3450, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3451, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3452, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3454, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ + _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3472, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3474, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3477, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3479, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3480, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3481, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3482, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3483, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3483+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 0ece3e6f0c66..9846e36037b9 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[212] = { +static const char *zend_vm_opcodes_names[214] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,11 @@ static const char *zend_vm_opcodes_names[212] = { "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", "ZEND_TYPE_ASSERT", + "ZEND_CALLABLE_CONVERT_PARTIAL", + "ZEND_SEND_PLACEHOLDER", }; -static uint32_t zend_vm_opcodes_flags[212] = { +static uint32_t zend_vm_opcodes_flags[214] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -449,6 +451,8 @@ static uint32_t zend_vm_opcodes_flags[212] = { 0x01001103, 0x00000303, 0x01000003, + 0x010003a0, + 0x00001301, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 92b46e6628f3..61147518d36c 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -331,7 +331,9 @@ END_EXTERN_C() #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 #define ZEND_TYPE_ASSERT 211 +#define ZEND_CALLABLE_CONVERT_PARTIAL 212 +#define ZEND_SEND_PLACEHOLDER 213 -#define ZEND_VM_LAST_OPCODE 211 +#define ZEND_VM_LAST_OPCODE 213 #endif diff --git a/configure.ac b/configure.ac index 9014869fb94e..b50339063fc4 100644 --- a/configure.ac +++ b/configure.ac @@ -1789,6 +1789,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_observer.c zend_opcode.c zend_operators.c + zend_partial.c zend_property_hooks.c zend_ptr_stack.c zend_signal.c diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index ac6d6787a6d6..2c467dc1fc8a 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -24,7 +24,9 @@ #include "zend_compile.h" #include "ZendAccelerator.h" #include "zend_modules.h" +#include "zend_operators.h" #include "zend_persist.h" +#include "zend_portability.h" #include "zend_shared_alloc.h" #include "zend_accelerator_module.h" #include "zend_accelerator_blacklist.h" @@ -48,6 +50,7 @@ #include "zend_system_id.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/basic_functions.h" +#include "zend_vm_opcodes.h" #ifdef ZEND_WIN32 # include "ext/standard/md5.h" @@ -2005,6 +2008,202 @@ static bool check_persistent_script_access(const zend_persistent_script *persist } } +static const char hexchars[] = "0123456789abcdef"; + +static char *zend_accel_uintptr_hex(char *dest, uintptr_t n) +{ + do { + *dest++ = hexchars[n & 0xf]; + n >>= 4; + } while (n); + + return dest; +} + +/* Prevents collisions with real scripts, as we don't cache paths prefixed with + * a scheme, except file:// and phar://. */ +#define PFA_KEY_PREFIX "pfa://" + +static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, + const zend_function *called_function) +{ + const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2); + zend_string *key = zend_string_alloc(max_key_len, 0); + + char *dest = ZSTR_VAL(key); + dest = zend_mempcpy(dest, PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX)); + dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline); + *dest++ = ':'; + + const void *ptr; + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION) { + /* Can not use 'called_function' as part of the key, as it's an inner + * pointer to a Closure, which may be freed. Use its opcodes instead. + * zend_accel_compile_pfa() ensures to extend the lifetime of opcodes + * in this case. */ + ptr = called_function->op_array.opcodes; + } else { + ptr = called_function; + } + dest = zend_accel_uintptr_hex(dest, (uintptr_t)ptr); + + *dest = '\0'; + ZSTR_LEN(key) = dest - ZSTR_VAL(key); + + return key; +} + +const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, const zend_function *called_function) +{ + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + zend_op_array *op_array = NULL; + + /* A PFA is SHM-cacheable if the declaring_op_array and called_function are + * cached. */ + if (ZCG(accelerator_enabled) + && !file_cache_only + && !declaring_op_array->refcount + && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) { + zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key); + if (persistent_script) { + op_array = persistent_script->script.main_op_array.dynamic_func_defs[0]; + if (persistent_script->num_warnings) { + zend_emit_recorded_errors_ex(persistent_script->num_warnings, + persistent_script->warnings); + } + } + } else { + op_array = zend_hash_find_ptr(&EG(partial_function_application_cache), key); + } + + zend_string_release(key); + + return op_array; +} + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name) +{ + zend_begin_record_errors(); + zend_op_array *op_array; + + uint32_t orig_compiler_options = CG(compiler_options); + + zend_try { + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER; +#ifdef ZEND_WIN32 + /* On Windows, don't compile with internal classes. Shm may be attached from different + * processes with internal classes living in different addresses. */ + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; +#endif + + op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename); + + CG(compiler_options) = orig_compiler_options; + } zend_catch { + CG(compiler_options) = orig_compiler_options; + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + zend_bailout(); + } zend_end_try(); + + ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); + + zend_string_release(op_array->dynamic_func_defs[0]->function_name); + op_array->dynamic_func_defs[0]->function_name = pfa_func_name; + + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + + /* Cache op_array only if the declaring op_array and the called function + * are cached */ + if (!ZCG(accelerator_enabled) + || file_cache_only + || declaring_op_array->refcount + || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount) + || (ZCSG(restart_in_progress) && accel_restart_is_active()) + || (!ZCG(counted) && accel_activate_add() == FAILURE)) { + zend_op_array *script_op_array = op_array; + zend_op_array *op_array = script_op_array->dynamic_func_defs[0]; + GC_ADDREF(op_array->function_name); + (*op_array->refcount)++; + destroy_op_array(script_op_array); + efree(script_op_array); + + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION + && called_function->op_array.refcount) { + /* Extend the lifetime of the called opcodes if + * the called function is a closure. + * See comment in zend_accel_pfa_key(). */ + zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(*copy)); + memcpy(copy, called_function, sizeof(*copy)); + zend_string_addref(copy->function_name); + (*copy->refcount)++; + /* Reference the copy in op_array->dynamic_func_defs so that it's + * destroyed when op_array is destroyed. */ + ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs); + op_array->dynamic_func_defs = safe_emalloc(1, sizeof(*op_array->dynamic_func_defs), 0); + op_array->dynamic_func_defs[0] = copy; + op_array->num_dynamic_func_defs = 1; + } + + zend_hash_add_new_ptr(&EG(partial_function_application_cache), key, op_array); + zend_string_release(key); + + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + + return op_array; + } + + zend_persistent_script *new_persistent_script = create_persistent_script(); + new_persistent_script->script.main_op_array = *op_array; + efree_size(op_array, sizeof(*op_array)); + new_persistent_script->script.filename = key; + + if (ZCG(accel_directives).record_warnings) { + new_persistent_script->num_warnings = EG(errors).size; + new_persistent_script->warnings = EG(errors).errors; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + bool from_shared_memory; + /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ + bool orig_gc_state = gc_enable(false); + char *orig_file_cache = ZCG(accel_directives).file_cache; + /* Disable file_cache temporarily, as we can't guarantee consistency. */ + ZCG(accel_directives).file_cache = NULL; + new_persistent_script = cache_script_in_shared_memory(new_persistent_script, NULL, &from_shared_memory); + ZCG(accel_directives).file_cache = orig_file_cache; + gc_enable(orig_gc_state); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + /* We may have switched to an existing persistent script that was persisted in + * the meantime. Make sure to use its warnings if available. */ + if (ZCG(accel_directives).record_warnings) { + EG(record_errors) = false; + zend_emit_recorded_errors_ex(new_persistent_script->num_warnings, new_persistent_script->warnings); + } else { + zend_emit_recorded_errors(); + } + zend_free_recorded_errors(); + + return new_persistent_script->script.main_op_array.dynamic_func_defs[0]; +} + /* zend_compile() replacement */ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) { diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 91642e288d31..ef7eea433c09 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -334,6 +334,16 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); +const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function); + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name); + END_EXTERN_C() /* memory write protection */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 6f550f0cf36c..cb2791fb45ac 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -301,6 +301,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return 0; case ZEND_SEND_VAL: case ZEND_SEND_VAR: @@ -386,6 +387,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: end = opline; if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) { /* INIT_FCALL and DO_FCALL in different BasicBlocks */ @@ -865,6 +867,7 @@ static bool zend_jit_dec_call_level(uint8_t opcode) case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return true; default: return false; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index b0b04456706b..f3dbcc84c124 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10112,7 +10112,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen } bool may_have_extra_named_params = - opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS && + (opline->extended_value & ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS) && (!func || func->common.fn_flags & ZEND_ACC_VARIADIC); if (!jit->reuse_ip) { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 85a81c1573bc..b01c3aaac62e 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1057,7 +1057,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, TRACE_RECORD(ZEND_JIT_TRACE_DO_ICALL, 0, func); } } else if (opline->opcode == ZEND_INCLUDE_OR_EVAL - || opline->opcode == ZEND_CALLABLE_CONVERT) { + || opline->opcode == ZEND_CALLABLE_CONVERT + || opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { /* TODO: Support tracing JIT for ZEND_CALLABLE_CONVERT. */ stop = ZEND_JIT_TRACE_STOP_INTERPRETER; break; diff --git a/ext/standard/tests/array/array_map_foreach_optimization_006.phpt b/ext/standard/tests/array/array_map_foreach_optimization_006.phpt new file mode 100644 index 000000000000..e21e3e0a9a8b --- /dev/null +++ b/ext/standard/tests/array/array_map_foreach_optimization_006.phpt @@ -0,0 +1,84 @@ +--TEST-- +array_map(): foreach optimization - PFA +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 INIT_FCALL 2 %d string("range") +0001 SEND_VAL int(1) 1 +0002 SEND_VAL int(10) 2 +0003 T2 = DO_ICALL +0004 ASSIGN CV0($array) T2 +0005 TYPE_ASSERT 131079 string("array_map") CV0($array) +0006 T2 = INIT_ARRAY 0 (packed) NEXT +0007 V3 = FE_RESET_R CV0($array) 0015 +0008 T5 = FE_FETCH_R V3 T4 0015 +0009 INIT_FCALL 2 %d string("plusn") +0010 SEND_VAL T4 1 +0011 SEND_VAL int(2) 2 +0012 T4 = DO_UCALL +0013 T2 = ADD_ARRAY_ELEMENT T4 T5 +0014 JMP 0008 +0015 FE_FREE V3 +0016 ASSIGN CV1($foo) T2 +0017 INIT_FCALL 1 %d string("var_dump") +0018 SEND_VAR CV1($foo) 1 +0019 DO_ICALL +0020 RETURN int(1) +LIVE RANGES: + 2: 0007 - 0016 (tmp/var) + 3: 0008 - 0015 (loop) + 4: 0009 - 0010 (tmp/var) + 5: 0009 - 0013 (tmp/var) + +plusn: + ; (lines=4, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s +0000 CV0($x) = RECV 1 +0001 CV1($n) = RECV 2 +0002 T2 = ADD CV0($x) CV1($n) +0003 RETURN T2 +array(10) { + [0]=> + int(3) + [1]=> + int(4) + [2]=> + int(5) + [3]=> + int(6) + [4]=> + int(7) + [5]=> + int(8) + [6]=> + int(9) + [7]=> + int(10) + [8]=> + int(11) + [9]=> + int(12) +} diff --git a/ext/standard/tests/array/array_map_foreach_optimization_007.phpt b/ext/standard/tests/array/array_map_foreach_optimization_007.phpt new file mode 100644 index 000000000000..67654b9e7640 --- /dev/null +++ b/ext/standard/tests/array/array_map_foreach_optimization_007.phpt @@ -0,0 +1,109 @@ +--TEST-- +array_map(): foreach optimization - unoptimizable PFA +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 INIT_FCALL 2 %d string("range") +0001 SEND_VAL int(1) 1 +0002 SEND_VAL int(10) 2 +0003 T2 = DO_ICALL +0004 ASSIGN CV0($array) T2 +0005 INIT_FCALL 2 %d string("array_map") +0006 INIT_FCALL 0 %d string("plusn") +0007 SEND_VAL int(2) string("n") +0008 T2 = CALLABLE_CONVERT_PARTIAL 2 +0009 SEND_VAL T2 1 +0010 SEND_VAR CV0($array) 2 +0011 T2 = DO_ICALL +0012 ASSIGN CV1($foo) T2 +0013 INIT_FCALL 1 %d string("var_dump") +0014 SEND_VAR CV1($foo) 1 +0015 DO_ICALL +0016 RETURN int(1) + +plusn: + ; (lines=4, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s +0000 CV0($x) = RECV 1 +0001 CV1($n) = RECV 2 +0002 T2 = ADD CV0($x) CV1($n) +0003 RETURN T2 + +$_main: + ; (lines=4, args=0, vars=1, tmps=1) + ; (after optimizer) + ; %s:1-9 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($n) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:pfa:%s:9}: + ; (lines=18, args=1, vars=2, tmps=2) + ; (after optimizer) + ; %s:9-9 +0000 CV0($x) = RECV 1 +0001 BIND_STATIC CV1($n) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("plusn") +0006 SEND_VAR CV0($x) 1 +0007 SEND_VAR CV1($n) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("plusn") +0011 SEND_VAR CV0($x) 1 +0012 SEND_VAR CV1($n) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +array(10) { + [0]=> + int(3) + [1]=> + int(4) + [2]=> + int(5) + [3]=> + int(6) + [4]=> + int(7) + [5]=> + int(8) + [6]=> + int(9) + [7]=> + int(10) + [8]=> + int(11) + [9]=> + int(12) +} diff --git a/win32/build/config.w32 b/win32/build/config.w32 index a0f26033306f..8ec6b31a11a0 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c zend_autoload.c"); + zend_lazy_objects.c zend_autoload.c zend_partial.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({