You need to sign in or sign up before continuing.
提交 5d385a52 编写于 作者: A andrew

Merge

......@@ -1022,3 +1022,4 @@ c63c2923e1f99c1f350bd24b42daf885023f18b7 jdk8u242-b08
c63c2923e1f99c1f350bd24b42daf885023f18b7 jdk8u242-ga
44c4cee50aeb94c4629e642681ff099ad9dcac12 jdk8u252-b00
4dd113d7811ea6651c1c96f9c641b8bec8e31669 jdk8u252-b01
8d39522b0f7573e69260eb3f7af360b72b27dfc3 jdk8u252-b02
#
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
......@@ -407,13 +407,17 @@ COPY_FILES += $(POLICY_DST)
##########################################################################################
# CACERTS_FILE is optionally set in configure to override the default cacerts
# which is otherwise generated in GenerateData.gmk
CACERTS_SRC := $(CACERTS_FILE)
CACERTS_DST := $(JDK_OUTPUTDIR)/lib/security/cacerts
$(CACERTS_DST): $(CACERTS_SRC)
$(call install-file)
COPY_FILES += $(CACERTS_DST)
ifneq ($(CACERTS_FILE), )
COPY_FILES += $(CACERTS_DST)
endif
##########################################################################################
......
#
# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
......@@ -52,6 +52,20 @@ GENDATA += $(GENDATA_HTML32DTD)
##########################################################################################
GENDATA_CACERTS_SRC := $(JDK_TOPDIR)/make/data/cacerts/
GENDATA_CACERTS := $(JDK_OUTPUTDIR)/lib/security/cacerts
$(GENDATA_CACERTS): $(BUILD_TOOLS) $(wildcard $(GENDATA_CACERTS_SRC)/*)
$(ECHO) $(LOG_INFO) "Generating cacerts"
$(MKDIR) -p $(@D)
$(TOOL_GENERATECACERTS) $(GENDATA_CACERTS_SRC) $@
ifeq ($(CACERTS_FILE), )
GENDATA += $(GENDATA_CACERTS)
endif
################################################################################
GENDATA_UNINAME := $(JDK_OUTPUTDIR)/classes/java/lang/uniName.dat
$(GENDATA_UNINAME): $(JDK_TOPDIR)/make/data/unicodedata/UnicodeData.txt $(BUILD_TOOLS)
......
#
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
......@@ -96,6 +96,9 @@ TOOL_HASHER = $(JAVA_SMALL) -cp $(JDK_OUTPUTDIR)/btclasses \
TOOL_TZDB = $(JAVA_SMALL) -cp $(JDK_OUTPUTDIR)/btclasses \
build.tools.tzdb.TzdbZoneRulesCompiler
TOOL_GENERATECACERTS = $(JAVA_SMALL) -cp $(JDK_OUTPUTDIR)/btclasses \
build.tools.generatecacerts.GenerateCacerts
# TODO: There are references to the jdwpgen.jar in jdk/make/netbeans/jdwpgen/build.xml
# and nbproject/project.properties in the same dir. Needs to be looked at.
......
Each file in this directory (except for this README) contains a CA certificate in PEM format. It can be generated with
keytool -J-Duser.timezone=GMT -printcert -file ca.cert | sed -n '1,4p;8,10p'
keytool -printcert -file ca.cert -rfc
Please note the textual part before the "-----BEGIN CERTIFICATE-----" line is just a suggestion and not arbitrary.
After any change in this directory, please remember to update the content of `test/jdk/sun/security/lib/cacerts/VerifyCACerts.java` as well.
All changes to this directory need to be approved by the Security group.
Owner: CN=Actalis Authentication Root CA, O=Actalis S.p.A./03358520967, L=Milan, C=IT
Issuer: CN=Actalis Authentication Root CA, O=Actalis S.p.A./03358520967, L=Milan, C=IT
Serial number: 570a119742c4e3cc
Valid from: Thu Sep 22 11:22:02 GMT 2011 until: Sun Sep 22 11:22:02 GMT 2030
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----
Owner: CN=AddTrust Class 1 CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE
Issuer: CN=AddTrust Class 1 CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE
Serial number: 1
Valid from: Tue May 30 10:38:31 GMT 2000 until: Sat May 30 10:38:31 GMT 2020
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
-----END CERTIFICATE-----
Owner: CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE
Issuer: CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE
Serial number: 1
Valid from: Tue May 30 10:48:38 GMT 2000 until: Sat May 30 10:48:38 GMT 2020
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
Owner: CN=AddTrust Qualified CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE
Issuer: CN=AddTrust Qualified CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE
Serial number: 1
Valid from: Tue May 30 10:44:50 GMT 2000 until: Sat May 30 10:44:50 GMT 2020
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
xqE=
-----END CERTIFICATE-----
Owner: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
Serial number: 7777062726a9b17c
Valid from: Fri Jan 29 14:06:06 GMT 2010 until: Tue Dec 31 14:06:06 GMT 2030
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----
Owner: CN=AffirmTrust Networking, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Networking, O=AffirmTrust, C=US
Serial number: 7c4f04391cd4992d
Valid from: Fri Jan 29 14:08:24 GMT 2010 until: Tue Dec 31 14:08:24 GMT 2030
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----
Owner: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Serial number: 6d8c1446b1a60aee
Valid from: Fri Jan 29 14:10:36 GMT 2010 until: Mon Dec 31 14:10:36 GMT 2040
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
KeC2uAloGRwYQw==
-----END CERTIFICATE-----
Owner: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
Serial number: 7497258ac73f7a54
Valid from: Fri Jan 29 14:20:24 GMT 2010 until: Mon Dec 31 14:20:24 GMT 2040
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
-----END CERTIFICATE-----
Owner: CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE
Issuer: CN=Baltimore CyberTrust Root, OU=CyberTrust, O=Baltimore, C=IE
Serial number: 20000b9
Valid from: Fri May 12 18:46:00 GMT 2000 until: Mon May 12 23:59:00 GMT 2025
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----
Owner: CN=Buypass Class 2 Root CA, O=Buypass AS-983163327, C=NO
Issuer: CN=Buypass Class 2 Root CA, O=Buypass AS-983163327, C=NO
Serial number: 2
Valid from: Tue Oct 26 08:38:03 GMT 2010 until: Fri Oct 26 08:38:03 GMT 2040
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
-----END CERTIFICATE-----
Owner: CN=Buypass Class 3 Root CA, O=Buypass AS-983163327, C=NO
Issuer: CN=Buypass Class 3 Root CA, O=Buypass AS-983163327, C=NO
Serial number: 2
Valid from: Tue Oct 26 08:28:58 GMT 2010 until: Fri Oct 26 08:28:58 GMT 2040
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
-----END CERTIFICATE-----
Owner: CN=Chambers of Commerce Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU
Issuer: CN=Chambers of Commerce Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU
Serial number: a3da427ea4b1aeda
Valid from: Fri Aug 01 12:29:50 GMT 2008 until: Sat Jul 31 12:29:50 GMT 2038
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz
IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz
MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj
dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw
EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp
MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9
28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq
VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q
DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR
5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL
ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a
Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl
UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s
+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5
Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx
hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV
HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1
+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN
YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t
L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy
ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt
IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV
HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w
DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW
PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF
5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1
glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH
FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2
pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD
xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG
tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq
jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De
fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ
d0jQ
-----END CERTIFICATE-----
Owner: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU
Issuer: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU
Serial number: 0
Valid from: Tue Sep 30 16:13:43 GMT 2003 until: Wed Sep 30 16:13:44 GMT 2037
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
tGWaIZDgqtCYvDi1czyL+Nw=
-----END CERTIFICATE-----
Owner: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU
Issuer: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU
Serial number: c9cdd3e9d57d23ce
Valid from: Fri Aug 01 12:31:40 GMT 2008 until: Sat Jul 31 12:31:40 GMT 2038
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx
MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy
cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG
A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl
BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed
KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7
G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2
zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4
ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG
HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2
Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V
yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e
beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r
6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog
zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW
BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr
ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp
ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk
cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt
YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC
CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow
KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI
hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ
UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz
X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x
fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz
a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd
Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd
SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O
AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso
M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge
v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
-----END CERTIFICATE-----
Owner: CN=Certum CA, O=Unizeto Sp. z o.o., C=PL
Issuer: CN=Certum CA, O=Unizeto Sp. z o.o., C=PL
Serial number: 10020
Valid from: Tue Jun 11 10:46:39 GMT 2002 until: Fri Jun 11 10:46:39 GMT 2027
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
6GAqm4VKQPNriiTsBhYscw==
-----END CERTIFICATE-----
Owner: CN=Certum Trusted Network CA, OU=Certum Certification Authority, O=Unizeto Technologies S.A., C=PL
Issuer: CN=Certum Trusted Network CA, OU=Certum Certification Authority, O=Unizeto Technologies S.A., C=PL
Serial number: 444c0
Valid from: Wed Oct 22 12:07:37 GMT 2008 until: Mon Dec 31 12:07:37 GMT 2029
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----
Owner: OU=ePKI Root Certification Authority, O="Chunghwa Telecom Co., Ltd.", C=TW
Issuer: OU=ePKI Root Certification Authority, O="Chunghwa Telecom Co., Ltd.", C=TW
Serial number: 15c8bd65475cafb897005ee406d2bc9d
Valid from: Mon Dec 20 02:31:27 GMT 2004 until: Wed Dec 20 02:31:27 GMT 2034
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw
IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL
SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH
SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh
ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X
DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1
TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ
fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA
sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU
WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS
nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH
dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip
NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC
AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF
MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB
uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl
PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP
JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/
gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2
j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6
5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB
o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS
/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z
Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE
W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D
hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----
Owner: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB
Issuer: CN=AAA Certificate Services, O=Comodo CA Limited, L=Salford, ST=Greater Manchester, C=GB
Serial number: 1
Valid from: Thu Jan 01 00:00:00 GMT 2004 until: Sun Dec 31 23:59:59 GMT 2028
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----
Owner: CN=COMODO ECC Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
Issuer: CN=COMODO ECC Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
Serial number: 1f47afaa62007050544c019e9b63992a
Valid from: Thu Mar 06 00:00:00 GMT 2008 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
Owner: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
Issuer: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
Serial number: 4caaf9cadb636fe01ff74ed85b03869d
Valid from: Tue Jan 19 00:00:00 GMT 2010 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
NVOFBkpdn627G190
-----END CERTIFICATE-----
Owner: CN=DigiCert Assured ID Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Assured ID Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: b931c3ad63967ea6723bfc3af9af44b
Valid from: Thu Aug 01 12:00:00 GMT 2013 until: Fri Jan 15 12:00:00 GMT 2038
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
IhNzbM8m9Yop5w==
-----END CERTIFICATE-----
Owner: CN=DigiCert Assured ID Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Assured ID Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: ba15afa1ddfa0b54944afcd24a06cec
Valid from: Thu Aug 01 12:00:00 GMT 2013 until: Fri Jan 15 12:00:00 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
6pZjamVFkpUBtA==
-----END CERTIFICATE-----
Owner: CN=DigiCert Assured ID Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Assured ID Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: ce7e0e517d846fe8fe560fc1bf03039
Valid from: Fri Nov 10 00:00:00 GMT 2006 until: Mon Nov 10 00:00:00 GMT 2031
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----
Owner: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 83be056904246b1a1756ac95991c74a
Valid from: Fri Nov 10 00:00:00 GMT 2006 until: Mon Nov 10 00:00:00 GMT 2031
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
Owner: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 33af1e6a711a9a0bb2864b11d09fae5
Valid from: Thu Aug 01 12:00:00 GMT 2013 until: Fri Jan 15 12:00:00 GMT 2038
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----
Owner: CN=DigiCert Global Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Global Root G3, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 55556bcf25ea43535c3a40fd5ab4572
Valid from: Thu Aug 01 12:00:00 GMT 2013 until: Fri Jan 15 12:00:00 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
sycX
-----END CERTIFICATE-----
Owner: CN=DigiCert High Assurance EV Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert High Assurance EV Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 2ac5c266a0b409b8f0b79f2ae462577
Valid from: Fri Nov 10 00:00:00 GMT 2006 until: Mon Nov 10 00:00:00 GMT 2031
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
Owner: CN=DigiCert Trusted Root G4, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Trusted Root G4, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 59b1b579e8e2132e23907bda777755c
Valid from: Thu Aug 01 12:00:00 GMT 2013 until: Fri Jan 15 12:00:00 GMT 2038
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----
Owner: CN=D-TRUST Root Class 3 CA 2 2009, O=D-Trust GmbH, C=DE
Issuer: CN=D-TRUST Root Class 3 CA 2 2009, O=D-Trust GmbH, C=DE
Serial number: 983f3
Valid from: Thu Nov 05 08:35:58 GMT 2009 until: Mon Nov 05 08:35:58 GMT 2029
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
Johw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----
Owner: CN=D-TRUST Root Class 3 CA 2 EV 2009, O=D-Trust GmbH, C=DE
Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009, O=D-Trust GmbH, C=DE
Serial number: 983f4
Valid from: Thu Nov 05 08:50:46 GMT 2009 until: Mon Nov 05 08:50:46 GMT 2029
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
-----END CERTIFICATE-----
Owner: CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), O=Entrust.net
Issuer: CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), O=Entrust.net
Serial number: 3863def8
Valid from: Fri Dec 24 17:50:51 GMT 1999 until: Tue Jul 24 14:15:12 GMT 2029
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3
MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub
j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo
U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b
u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
fF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----
Owner: CN=Entrust Root Certification Authority, OU="(c) 2006 Entrust, Inc.", OU=www.entrust.net/CPS is incorporated by reference, O="Entrust, Inc.", C=US
Issuer: CN=Entrust Root Certification Authority, OU="(c) 2006 Entrust, Inc.", OU=www.entrust.net/CPS is incorporated by reference, O="Entrust, Inc.", C=US
Serial number: 456b5054
Valid from: Mon Nov 27 20:23:42 GMT 2006 until: Fri Nov 27 20:53:42 GMT 2026
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
Owner: CN=Entrust Root Certification Authority - EC1, OU="(c) 2012 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US
Issuer: CN=Entrust Root Certification Authority - EC1, OU="(c) 2012 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US
Serial number: a68b79290000000050d091f9
Valid from: Tue Dec 18 15:25:36 GMT 2012 until: Fri Dec 18 15:55:36 GMT 2037
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----
Owner: CN=Entrust Root Certification Authority - G2, OU="(c) 2009 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US
Issuer: CN=Entrust Root Certification Authority - G2, OU="(c) 2009 Entrust, Inc. - for authorized use only", OU=See www.entrust.net/legal-terms, O="Entrust, Inc.", C=US
Serial number: 4a538c28
Valid from: Tue Jul 07 17:25:54 GMT 2009 until: Sat Dec 07 17:55:54 GMT 2030
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy
NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T
RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN
cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW
wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1
U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0
jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN
BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/
jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v
1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R
nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH
VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
-----END CERTIFICATE-----
Owner: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US
Issuer: CN=GeoTrust Global CA, O=GeoTrust Inc., C=US
Serial number: 23456
Valid from: Tue May 21 04:00:00 GMT 2002 until: Sat May 21 04:00:00 GMT 2022
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----
Owner: CN=GeoTrust Primary Certification Authority, O=GeoTrust Inc., C=US
Issuer: CN=GeoTrust Primary Certification Authority, O=GeoTrust Inc., C=US
Serial number: 18acb56afd69b6153a636cafdafac4a1
Valid from: Mon Nov 27 00:00:00 GMT 2006 until: Wed Jul 16 23:59:59 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
-----END CERTIFICATE-----
Owner: CN=GeoTrust Primary Certification Authority - G2, OU=(c) 2007 GeoTrust Inc. - For authorized use only, O=GeoTrust Inc., C=US
Issuer: CN=GeoTrust Primary Certification Authority - G2, OU=(c) 2007 GeoTrust Inc. - For authorized use only, O=GeoTrust Inc., C=US
Serial number: 3cb2f4480a00e2feeb243b5e603ec36b
Valid from: Mon Nov 05 00:00:00 GMT 2007 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
rD6ogRLQy7rQkgu2npaqBA+K
-----END CERTIFICATE-----
Owner: CN=GeoTrust Primary Certification Authority - G3, OU=(c) 2008 GeoTrust Inc. - For authorized use only, O=GeoTrust Inc., C=US
Issuer: CN=GeoTrust Primary Certification Authority - G3, OU=(c) 2008 GeoTrust Inc. - For authorized use only, O=GeoTrust Inc., C=US
Serial number: 15ac6e9419b2794b41f627a9c3180f1f
Valid from: Wed Apr 02 00:00:00 GMT 2008 until: Tue Dec 01 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
spki4cErx5z481+oghLrGREt
-----END CERTIFICATE-----
Owner: CN=GeoTrust Universal CA, O=GeoTrust Inc., C=US
Issuer: CN=GeoTrust Universal CA, O=GeoTrust Inc., C=US
Serial number: 1
Valid from: Thu Mar 04 05:00:00 GMT 2004 until: Sun Mar 04 05:00:00 GMT 2029
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
-----END CERTIFICATE-----
Owner: CN=GlobalSign Root CA, OU=Root CA, O=GlobalSign nv-sa, C=BE
Issuer: CN=GlobalSign Root CA, OU=Root CA, O=GlobalSign nv-sa, C=BE
Serial number: 40000000001154b5ac394
Valid from: Tue Sep 01 12:00:00 GMT 1998 until: Fri Jan 28 12:00:00 GMT 2028
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
Owner: CN=GlobalSign, O=GlobalSign, OU=GlobalSign ECC Root CA - R4
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign ECC Root CA - R4
Serial number: 2a38a41c960a04de42b228a50be8349802
Valid from: Tue Nov 13 00:00:00 GMT 2012 until: Tue Jan 19 03:14:07 GMT 2038
Signature algorithm name: SHA256withECDSA
Subject Public Key Algorithm: 256-bit EC (secp256r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
ewv4n4Q=
-----END CERTIFICATE-----
Owner: CN=GlobalSign, O=GlobalSign, OU=GlobalSign ECC Root CA - R5
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign ECC Root CA - R5
Serial number: 605949e0262ebb55f90a778a71f94ad86c
Valid from: Tue Nov 13 00:00:00 GMT 2012 until: Tue Jan 19 03:14:07 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
xwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----
Owner: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R2
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R2
Serial number: 400000000010f8626e60d
Valid from: Fri Dec 15 08:00:00 GMT 2006 until: Wed Dec 15 08:00:00 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----
Owner: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R3
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R3
Serial number: 4000000000121585308a2
Valid from: Wed Mar 18 10:00:00 GMT 2009 until: Sun Mar 18 10:00:00 GMT 2029
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----
Owner: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
Serial number: 45e6bb038333c3856548e6ff4551
Valid from: Wed Dec 10 00:00:00 GMT 2014 until: Sun Dec 10 00:00:00 GMT 2034
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET
MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI
xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k
ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD
aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw
LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw
1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX
k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2
SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h
bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n
WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY
rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce
MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu
bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt
Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61
55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj
vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf
cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz
oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp
nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs
pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v
JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4
5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
-----END CERTIFICATE-----
Owner: OU=Go Daddy Class 2 Certification Authority, O="The Go Daddy Group, Inc.", C=US
Issuer: OU=Go Daddy Class 2 Certification Authority, O="The Go Daddy Group, Inc.", C=US
Serial number: 0
Valid from: Tue Jun 29 17:06:20 GMT 2004 until: Thu Jun 29 17:06:20 GMT 2034
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----
Owner: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
Serial number: 0
Valid from: Tue Sep 01 00:00:00 GMT 2009 until: Thu Dec 31 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
4uJEvlz36hz1
-----END CERTIFICATE-----
Owner: CN=IdenTrust Commercial Root CA 1, O=IdenTrust, C=US
Issuer: CN=IdenTrust Commercial Root CA 1, O=IdenTrust, C=US
Serial number: a0142800000014523c844b500000002
Valid from: Thu Jan 16 18:12:23 GMT 2014 until: Mon Jan 16 18:12:23 GMT 2034
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw
JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT
3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU
+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp
S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1
bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi
T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL
vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK
Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK
dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT
c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv
l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N
iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD
ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt
LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93
nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3
+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK
W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT
AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq
l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG
4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ
mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H
-----END CERTIFICATE-----
Owner: CN=DST Root CA X3, O=Digital Signature Trust Co.
Issuer: CN=DST Root CA X3, O=Digital Signature Trust Co.
Serial number: 44afb080d6a327ba893039862ef8406b
Valid from: Sat Sep 30 21:12:19 GMT 2000 until: Thu Sep 30 14:01:15 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
Owner: CN=IdenTrust Public Sector Root CA 1, O=IdenTrust, C=US
Issuer: CN=IdenTrust Public Sector Root CA 1, O=IdenTrust, C=US
Serial number: a0142800000014523cf467c00000002
Valid from: Thu Jan 16 17:53:32 GMT 2014 until: Mon Jan 16 17:53:32 GMT 2034
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu
VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN
MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0
MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7
ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy
RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS
bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF
/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R
3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw
EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy
9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V
GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ
2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV
WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD
W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN
AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV
DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9
TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G
lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW
mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df
WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5
+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ
tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA
GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv
8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c
-----END CERTIFICATE-----
Owner: CN=KEYNECTIS ROOT CA, OU=ROOT, O=KEYNECTIS, C=FR
Issuer: CN=KEYNECTIS ROOT CA, OU=ROOT, O=KEYNECTIS, C=FR
Serial number: 1121bc276c5547af584eefd4ced629b2a285
Valid from: Tue May 26 00:00:00 GMT 2009 until: Tue May 26 00:00:00 GMT 2020
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIID5TCCAs2gAwIBAgISESG8J2xVR69YTu/UztYpsqKFMA0GCSqGSIb3DQEBCwUA
MEwxCzAJBgNVBAYTAkZSMRIwEAYDVQQKEwlLRVlORUNUSVMxDTALBgNVBAsTBFJP
T1QxGjAYBgNVBAMTEUtFWU5FQ1RJUyBST09UIENBMB4XDTA5MDUyNjAwMDAwMFoX
DTIwMDUyNjAwMDAwMFowTDELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUtFWU5FQ1RJ
UzENMAsGA1UECxMEUk9PVDEaMBgGA1UEAxMRS0VZTkVDVElTIFJPT1QgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/bMXhaGtJhuVaTUhPaSI+t7b
YDZAF2nCFGP7uNnCdBU3LpzQIM1pjYQyooVMFLSb8iWzVCqDPy2+D/M7ZNH/oFDv
d087TuE/C2SFmrpYftLDYtNkJaLUspc8d11jKjOS/M2CDZtUlYf1teuMzVvRyjAv
yYhGtc0NEbQYj+7RoT5dFegoz9/DkJtszNEMRXezOuuKkB3pr2RqiXupPUN0+uRn
IqH73E3E9WLJyiW0yYBgM6nde6ACv5YlCl7JXyl7tBeBi22BGdDZg1wFj0FpGmlD
gJ+or+DpjJGLJyuiJmDND/KkowKDjhiBwheKQxX5bfMdEKRanERhIyF62PvRAgMB
AAGjgcAwgb0wEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwVwYD
VR0fBFAwTjBMoEqgSIZGaHR0cDovL3RydXN0Y2VudGVyLWNybC5jZXJ0aWZpY2F0
Mi5jb20vS2V5bmVjdGlzL0tFWU5FQ1RJU19ST09UX0NBLmNybDAdBgNVHQ4EFgQU
77cjl9CokX+mz6YhwDSfzHdB4dAwHwYDVR0jBBgwFoAU77cjl9CokX+mz6YhwDSf
zHdB4dAwDQYJKoZIhvcNAQELBQADggEBABoxaZlCwuVAhaKfksNj1I8hOagZIf56
/MNNQPMr6EusW0xZk8bcfguvfF+VhWu9x2+6wb74xjpnS5PGBWk+JC3wG5HGPj/s
QhiTbAMkim75IGcrfG2rNMkqIjMN132P7tI2ZELINZpuGWHLjWfwaKfQJAXmwxe6
Ra58Q7WAeANNIHMF/EMQnTVpQnWUJYIrpjuQGN7Bqa/zLZW/lafPGJfhWeKirxoW
YQ33E3FTkzf9PK8AHWyLFK9Gloy2UnzMLU7N4elLCu6a/nqY5ym6G9ocutxrzQQO
JkCp63M8/lCoESdVvduOS+9PGO0V/72GmGbumiVxNGxQ8bJRy2adTSk=
-----END CERTIFICATE-----
Owner: CN=ISRG Root X1, O=Internet Security Research Group, C=US
Issuer: CN=ISRG Root X1, O=Internet Security Research Group, C=US
Serial number: 8210cfb0d240e3594463e0bb63828b00
Valid from: Thu Jun 04 11:04:38 GMT 2015 until: Mon Jun 04 11:04:38 GMT 2035
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
Owner: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU
Issuer: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU
Serial number: bb8
Valid from: Thu Mar 17 09:51:37 GMT 2011 until: Wed Mar 17 09:51:37 GMT 2021
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDZDCCAkygAwIBAgICC7gwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCTFUx
FjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0IEdsb2Jh
bCBSb290MB4XDTExMDMxNzA5NTEzN1oXDTIxMDMxNzA5NTEzN1owRDELMAkGA1UE
BhMCTFUxFjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0
IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn+n
QPAiygz267Hxyw6VV0B1r6A/Ps7sqjJX5hmxZ0OYWmt8s7j6eJyqpoSyYBuAQc5j
zR8XCJmk9e8+EsdMsFeaXHhAePxFjdqRZ9w6Ubltc+a3OY52OrQfBfVpVfmTz3iI
Sr6qm9d7R1tGBEyCFqY19vx039a0r9jitScRdFmiwmYsaArhmIiIPIoFdRTjuK7z
CISbasE/MRivJ6VLm6T9eTHemD0OYcqHmMH4ijCc+j4z1aXEAwfh95Z0GAAnOCfR
K6qq4UFFi2/xJcLcopeVx0IUM115hCNq52XAV6DYXaljAeew5Ivo+MVjuOVsdJA9
x3f8K7p56aTGEnin/wIDAQABo2AwXjAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAfBgNVHSMEGDAWgBQXFYWJCS8kh28/HRvk8pZ5g0gTzjAdBgNVHQ4EFgQU
FxWFiQkvJIdvPx0b5PKWeYNIE84wDQYJKoZIhvcNAQELBQADggEBAFrwHNDUUM9B
fua4nX3DcNBeNv9ujnov3kgR1TQuPLdFwlQlp+HBHjeDtpSutkVIA+qVvuucarQ3
XB8u02uCgUNbCj8RVWOs+nwIAjegPDkEM/6XMshS5dklTbDG7mgfcKpzzlcD3H0K
DTPy0lrfCmw7zBFRlxqkIaKFNQLXgCLShLL4wKpov9XrqsMLq6F8K/f1O4fhVFfs
BSTveUJO84ton+Ruy4KZycwq3FPCH3CDqyEPVrRI/98HIrOM+R2mBN8tAza53W/+
MYhm/2xtRDSvCHc+JtJy9LtHVpM8mGPhM7uZI5K1g3noHZ9nrWLWidb2/CfeMifL
hNp3hSGhEiE=
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root Certification Authority, OU=Root Certification Authority, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root Certification Authority, OU=Root Certification Authority, O=QuoVadis Limited, C=BM
Serial number: 3ab6508b
Valid from: Mon Mar 19 18:33:33 GMT 2001 until: Wed Mar 17 18:33:33 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
SnQ2+Q==
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root CA 1 G3, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root CA 1 G3, O=QuoVadis Limited, C=BM
Serial number: 78585f2ead2c194be3370735341328b596d46593
Valid from: Thu Jan 12 17:27:44 GMT 2012 until: Sun Jan 12 17:27:44 GMT 2042
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00
MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV
wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe
rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341
68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh
4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp
UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o
abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc
3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G
KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt
hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO
Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt
zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD
ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2
cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN
qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5
YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv
b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2
8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k
NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj
ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp
q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt
nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root CA 2, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root CA 2, O=QuoVadis Limited, C=BM
Serial number: 509
Valid from: Fri Nov 24 18:27:00 GMT 2006 until: Mon Nov 24 18:23:33 GMT 2031
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root CA 2 G3, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root CA 2 G3, O=QuoVadis Limited, C=BM
Serial number: 445734245b81899b35f2ceb82b3b5ba726f07528
Valid from: Thu Jan 12 18:59:32 GMT 2012 until: Sun Jan 12 18:59:32 GMT 2042
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root CA 3, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root CA 3, O=QuoVadis Limited, C=BM
Serial number: 5c6
Valid from: Fri Nov 24 19:11:23 GMT 2006 until: Mon Nov 24 19:06:44 GMT 2031
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----
Owner: CN=QuoVadis Root CA 3 G3, O=QuoVadis Limited, C=BM
Issuer: CN=QuoVadis Root CA 3 G3, O=QuoVadis Limited, C=BM
Serial number: 2ef59b0228a7db7affd5a3a9eebd03a0cf126a1d
Valid from: Thu Jan 12 20:26:32 GMT 2012 until: Sun Jan 12 20:26:32 GMT 2042
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR
/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu
FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR
U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c
ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR
FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k
A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw
eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl
sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp
VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q
A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+
ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD
ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI
FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv
oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg
u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP
0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf
3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl
8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+
DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN
PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/
ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
-----END CERTIFICATE-----
Owner: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP
Issuer: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP
Serial number: 0
Valid from: Tue Sep 30 04:20:49 GMT 2003 until: Sat Sep 30 04:20:49 GMT 2023
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
-----END CERTIFICATE-----
Owner: OU=Security Communication RootCA2, O="SECOM Trust Systems CO.,LTD.", C=JP
Issuer: OU=Security Communication RootCA2, O="SECOM Trust Systems CO.,LTD.", C=JP
Serial number: 0
Valid from: Fri May 29 05:00:39 GMT 2009 until: Tue May 29 05:00:39 GMT 2029
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----
Owner: CN=SecureTrust CA, O=SecureTrust Corporation, C=US
Issuer: CN=SecureTrust CA, O=SecureTrust Corporation, C=US
Serial number: cf08e5c0816a5ad427ff0eb271859d0
Valid from: Tue Nov 07 19:31:18 GMT 2006 until: Mon Dec 31 19:40:55 GMT 2029
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----
Owner: CN=Sonera Class2 CA, O=Sonera, C=FI
Issuer: CN=Sonera Class2 CA, O=Sonera, C=FI
Serial number: 1d
Valid from: Fri Apr 06 07:29:40 GMT 2001 until: Tue Apr 06 07:29:40 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
-----END CERTIFICATE-----
Owner: OU=Starfield Class 2 Certification Authority, O="Starfield Technologies, Inc.", C=US
Issuer: OU=Starfield Class 2 Certification Authority, O="Starfield Technologies, Inc.", C=US
Serial number: 0
Valid from: Tue Jun 29 17:39:16 GMT 2004 until: Thu Jun 29 17:39:16 GMT 2034
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----
Owner: CN=Starfield Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: CN=Starfield Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Serial number: 0
Valid from: Tue Sep 01 00:00:00 GMT 2009 until: Thu Dec 31 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----
Owner: CN=Starfield Services Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Issuer: CN=Starfield Services Root Certificate Authority - G2, O="Starfield Technologies, Inc.", L=Scottsdale, ST=Arizona, C=US
Serial number: 0
Valid from: Tue Sep 01 00:00:00 GMT 2009 until: Thu Dec 31 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
sSi6
-----END CERTIFICATE-----
Owner: CN=SwissSign Gold CA - G2, O=SwissSign AG, C=CH
Issuer: CN=SwissSign Gold CA - G2, O=SwissSign AG, C=CH
Serial number: bb401c43f55e4fb0
Valid from: Wed Oct 25 08:30:35 GMT 2006 until: Sat Oct 25 08:30:35 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----
Owner: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
Issuer: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
Serial number: 4eb200670c035d4f
Valid from: Wed Oct 25 08:36:00 GMT 2006 until: Sat Oct 25 08:36:00 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu
IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw
WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD
ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y
IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn
IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+
6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob
jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw
izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl
+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY
zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP
pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF
KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW
ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB
AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0
ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA
A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0
uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+
FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7
jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/
u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D
YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1
puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa
icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG
DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x
kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z
Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
-----END CERTIFICATE-----
Owner: CN=SwissSign Silver CA - G2, O=SwissSign AG, C=CH
Issuer: CN=SwissSign Silver CA - G2, O=SwissSign AG, C=CH
Serial number: 4f1bd42f54bb2f4b
Valid from: Wed Oct 25 08:32:46 GMT 2006 until: Sat Oct 25 08:32:46 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----
Owner: CN=TeliaSonera Root CA v1, O=TeliaSonera
Issuer: CN=TeliaSonera Root CA v1, O=TeliaSonera
Serial number: 95be16a0f72e46f17b398272fa8bcd96
Valid from: Thu Oct 18 12:00:50 GMT 2007 until: Mon Oct 18 12:00:50 GMT 2032
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw
NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv
b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD
VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F
VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1
7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X
Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+
/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs
81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm
dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe
Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu
sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4
pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs
slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ
arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD
VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG
9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl
dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj
TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed
Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7
Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI
OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7
vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW
t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn
HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
Owner: EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA
Issuer: EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA
Serial number: 36122296c5e338a520a1d25f4cd70954
Valid from: Thu Aug 01 00:00:00 GMT 1996 until: Fri Jan 01 23:59:59 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDNjCCAp+gAwIBAgIQNhIilsXjOKUgodJfTNcJVDANBgkqhkiG9w0BAQUFADCB
zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIxMDEwMTIzNTk1OVow
gc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcT
CUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNV
BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRo
YXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1z
ZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560
ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j
+ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBlkKyID1bZ5jA01CbH0FDxkt5r1DmI
CSLGpmODA/eZd9iy5Ri4XWPz1HP7bJyZePFLeH0ZJMMrAoT4vCLZiiLXoPxx7JGH
IPG47LHlVYCsPVLIOQ7C8MAFT9aCdYy9X9LcdpoFEsmvcsPcJX6kTY4XpeCHf+Ga
WuFg3GQjPEIuTQ==
-----END CERTIFICATE-----
Owner: CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
Issuer: CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
Serial number: 344ed55720d5edec49f42fce37db2b6d
Valid from: Fri Nov 17 00:00:00 GMT 2006 until: Wed Jul 16 23:59:59 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----
Owner: CN=thawte Primary Root CA - G2, OU="(c) 2007 thawte, Inc. - For authorized use only", O="thawte, Inc.", C=US
Issuer: CN=thawte Primary Root CA - G2, OU="(c) 2007 thawte, Inc. - For authorized use only", O="thawte, Inc.", C=US
Serial number: 35fc265cd9844fc93d263d579baed756
Valid from: Mon Nov 05 00:00:00 GMT 2007 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
-----END CERTIFICATE-----
Owner: CN=thawte Primary Root CA - G3, OU="(c) 2008 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
Issuer: CN=thawte Primary Root CA - G3, OU="(c) 2008 thawte, Inc. - For authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US
Serial number: 600197b746a7eab4b49ad64b2ff790fb
Valid from: Wed Apr 02 00:00:00 GMT 2008 until: Tue Dec 01 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
MdRAGmI0Nj81Aa6sY6A=
-----END CERTIFICATE-----
Owner: CN=T-TeleSec GlobalRoot Class 2, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Issuer: CN=T-TeleSec GlobalRoot Class 2, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Serial number: 1
Valid from: Wed Oct 01 10:40:14 GMT 2008 until: Sat Oct 01 23:59:59 GMT 2033
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd
AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC
FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi
1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq
jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ
wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/
WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy
NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC
uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw
IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6
g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP
BSeOE6Fuwg==
-----END CERTIFICATE-----
Owner: CN=T-TeleSec GlobalRoot Class 3, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Issuer: CN=T-TeleSec GlobalRoot Class 3, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Serial number: 1
Valid from: Wed Oct 01 10:29:56 GMT 2008 until: Sat Oct 01 23:59:59 GMT 2033
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
TpPDpFQUWw==
-----END CERTIFICATE-----
Owner: CN=USERTrust ECC Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
Issuer: CN=USERTrust ECC Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
Serial number: 5c8b99c55a94c5d27156decd8980cc26
Valid from: Mon Feb 01 00:00:00 GMT 2010 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----
Owner: CN=USERTrust RSA Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
Issuer: CN=USERTrust RSA Certification Authority, O=The USERTRUST Network, L=Jersey City, ST=New Jersey, C=US
Serial number: 1fd6d30fca3ca51a81bbc640e35032d
Valid from: Mon Feb 01 00:00:00 GMT 2010 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----
Owner: CN=UTN-USERFirst-Object, OU=http://www.usertrust.com, O=The USERTRUST Network, L=Salt Lake City, ST=UT, C=US
Issuer: CN=UTN-USERFirst-Object, OU=http://www.usertrust.com, O=The USERTRUST Network, L=Salt Lake City, ST=UT, C=US
Serial number: 44be0c8b500024b411d3362de0b35f1b
Valid from: Fri Jul 09 18:31:20 GMT 1999 until: Tue Jul 09 18:40:36 GMT 2019
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB
lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt
T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc
BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3
dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP
HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO
KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo
5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+
pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb
kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC
AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov
L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV
HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN
AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw
NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB
mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU
4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5
81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR
Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g=
-----END CERTIFICATE-----
Owner: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 2 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US
Issuer: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 2 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US
Serial number: b92f60cc889fa17a4609b85b706c8aaf
Valid from: Mon May 18 00:00:00 GMT 1998 until: Tue Aug 01 23:59:59 GMT 2028
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 1
-----BEGIN CERTIFICATE-----
MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns
YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe
Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj
IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx
KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM
HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw
DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC
AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji
nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn
jBJ7xUS0rg==
-----END CERTIFICATE-----
Owner: OU=Class 3 Public Primary Certification Authority, O="VeriSign, Inc.", C=US
Issuer: OU=Class 3 Public Primary Certification Authority, O="VeriSign, Inc.", C=US
Serial number: 3c9131cb1ff6d01b0e9ab8d044bf12be
Valid from: Mon Jan 29 00:00:00 GMT 1996 until: Wed Aug 02 23:59:59 GMT 2028
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 1
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
-----END CERTIFICATE-----
Owner: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 3 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US
Issuer: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 3 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US
Serial number: 7dd9fe07cfa81eb7107967fba78934c6
Valid from: Mon May 18 00:00:00 GMT 1998 until: Tue Aug 01 23:59:59 GMT 2028
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 1
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
oJ2daZH9
-----END CERTIFICATE-----
Owner: CN=VeriSign Class 3 Public Primary Certification Authority - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Serial number: 9b7e0649a33e62b9d5ee90487129ef57
Valid from: Fri Oct 01 00:00:00 GMT 1999 until: Wed Jul 16 23:59:59 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
-----END CERTIFICATE-----
Owner: CN=VeriSign Class 3 Public Primary Certification Authority - G4, OU="(c) 2007 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4, OU="(c) 2007 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Serial number: 2f80fe238c0e220f486712289187acb3
Valid from: Mon Nov 05 00:00:00 GMT 2007 until: Mon Jan 18 23:59:59 GMT 2038
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----
Owner: CN=VeriSign Class 3 Public Primary Certification Authority - G5, OU="(c) 2006 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5, OU="(c) 2006 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Serial number: 18dad19e267de8bb4a2158cdcc6b3b4a
Valid from: Wed Nov 08 00:00:00 GMT 2006 until: Wed Jul 16 23:59:59 GMT 2036
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----
Owner: CN=Thawte Timestamping CA, OU=Thawte Certification, O=Thawte, L=Durbanville, ST=Western Cape, C=ZA
Issuer: CN=Thawte Timestamping CA, OU=Thawte Certification, O=Thawte, L=Durbanville, ST=Western Cape, C=ZA
Serial number: 67c8e1e8e3be1cbdfc913b8ea6238749
Valid from: Wed Jan 01 00:00:00 GMT 1997 until: Fri Jan 01 23:59:59 GMT 2021
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 1024-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIICsDCCAhmgAwIBAgIQZ8jh6OO+HL38kTuOpiOHSTANBgkqhkiG9w0BAQUFADCB
izELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxML
RHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENl
cnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcN
OTcwMTAxMDAwMDAwWhcNMjEwMTAxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT
BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNV
BAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNV
BAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
MIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u
6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522
FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzAR
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAS+mqF4EF+3kKMZ/F
QfRWVKvpwuWXjhj+kckMPiZkyaFMJ2SnvQGTVXFuF0853BvcSTUQOSP/ypvIz2Y/
3Ewa1IEGQlIf4SaxFhe65nByMUToTo1b5NP50OOPJWQx5yr4GIg2GlLFDUE1G2m3
JvUXzMEZXkt8XOKDgJH6L/uatxY=
-----END CERTIFICATE-----
Owner: CN=VeriSign Universal Root Certification Authority, OU="(c) 2008 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Issuer: CN=VeriSign Universal Root Certification Authority, OU="(c) 2008 VeriSign, Inc. - For authorized use only", OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
Serial number: 401ac46421b31321030ebbe4121ac51d
Valid from: Wed Apr 02 00:00:00 GMT 2008 until: Tue Dec 01 23:59:59 GMT 2037
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
7M2CYfE45k+XmCpajQ==
-----END CERTIFICATE-----
Owner: CN=XRamp Global Certification Authority, O=XRamp Security Services Inc, OU=www.xrampsecurity.com, C=US
Issuer: CN=XRamp Global Certification Authority, O=XRamp Security Services Inc, OU=www.xrampsecurity.com, C=US
Serial number: 50946cec18ead59c4dd597ef758fa0ad
Valid from: Mon Nov 01 17:14:04 GMT 2004 until: Mon Jan 01 05:37:19 GMT 2035
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
O+7ETPTsJ3xCwnR8gooJybQDJbw=
-----END CERTIFICATE-----
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package build.tools.generatecacerts;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
/**
* Generate cacerts
* args[0]: Full path string to the directory that contains CA certs
* args[1]: Full path string to the generated cacerts
*/
public class GenerateCacerts {
public static void main(String[] args) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X509");
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get(args[0]))) {
for (Path p : ds) {
String fName = p.getFileName().toString();
if (!fName.equals("README")) {
String alias = fName + " [jdk]";
try (InputStream fis = Files.newInputStream(p)) {
ks.setCertificateEntry(alias, cf.generateCertificate(fis));
}
}
}
}
try (FileOutputStream fos = new FileOutputStream(args[1])) {
ks.store(fos, "changeit".toCharArray());
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logInfo;
public final class ArrayCache implements MarlinConst {
static final int BUCKETS = 4;
static final int MIN_ARRAY_SIZE = 4096;
static final int MAX_ARRAY_SIZE;
static final int MASK_CLR_1 = ~1;
// threshold to grow arrays only by (3/2) instead of 2
static final int THRESHOLD_ARRAY_SIZE;
static final int[] ARRAY_SIZES = new int[BUCKETS];
// dirty byte array sizes
static final int MIN_DIRTY_BYTE_ARRAY_SIZE = 32 * 2048; // 32px x 2048px
static final int MAX_DIRTY_BYTE_ARRAY_SIZE;
static final int[] DIRTY_BYTE_ARRAY_SIZES = new int[BUCKETS];
// large array thresholds:
static final long THRESHOLD_LARGE_ARRAY_SIZE;
static final long THRESHOLD_HUGE_ARRAY_SIZE;
// stats
private static int resizeInt = 0;
private static int resizeDirtyInt = 0;
private static int resizeDirtyFloat = 0;
private static int resizeDirtyByte = 0;
private static int oversize = 0;
static {
// initialize buckets for int/float arrays
int arraySize = MIN_ARRAY_SIZE;
for (int i = 0; i < BUCKETS; i++, arraySize <<= 2) {
ARRAY_SIZES[i] = arraySize;
if (doTrace) {
logInfo("arraySize[" + i + "]: " + arraySize);
}
}
MAX_ARRAY_SIZE = arraySize >> 2;
/* initialize buckets for dirty byte arrays
(large AA chunk = 32 x 2048 pixels) */
arraySize = MIN_DIRTY_BYTE_ARRAY_SIZE;
for (int i = 0; i < BUCKETS; i++, arraySize <<= 1) {
DIRTY_BYTE_ARRAY_SIZES[i] = arraySize;
if (doTrace) {
logInfo("dirty arraySize[" + i + "]: " + arraySize);
}
}
MAX_DIRTY_BYTE_ARRAY_SIZE = arraySize >> 1;
// threshold to grow arrays only by (3/2) instead of 2
THRESHOLD_ARRAY_SIZE = Math.max(2 * 1024 * 1024, MAX_ARRAY_SIZE); // 2M
THRESHOLD_LARGE_ARRAY_SIZE = 8L * THRESHOLD_ARRAY_SIZE; // 16M
THRESHOLD_HUGE_ARRAY_SIZE = 8L * THRESHOLD_LARGE_ARRAY_SIZE; // 128M
if (doStats || doMonitors) {
logInfo("ArrayCache.BUCKETS = " + BUCKETS);
logInfo("ArrayCache.MIN_ARRAY_SIZE = " + MIN_ARRAY_SIZE);
logInfo("ArrayCache.MAX_ARRAY_SIZE = " + MAX_ARRAY_SIZE);
logInfo("ArrayCache.ARRAY_SIZES = "
+ Arrays.toString(ARRAY_SIZES));
logInfo("ArrayCache.MIN_DIRTY_BYTE_ARRAY_SIZE = "
+ MIN_DIRTY_BYTE_ARRAY_SIZE);
logInfo("ArrayCache.MAX_DIRTY_BYTE_ARRAY_SIZE = "
+ MAX_DIRTY_BYTE_ARRAY_SIZE);
logInfo("ArrayCache.ARRAY_SIZES = "
+ Arrays.toString(DIRTY_BYTE_ARRAY_SIZES));
logInfo("ArrayCache.THRESHOLD_ARRAY_SIZE = "
+ THRESHOLD_ARRAY_SIZE);
logInfo("ArrayCache.THRESHOLD_LARGE_ARRAY_SIZE = "
+ THRESHOLD_LARGE_ARRAY_SIZE);
logInfo("ArrayCache.THRESHOLD_HUGE_ARRAY_SIZE = "
+ THRESHOLD_HUGE_ARRAY_SIZE);
}
}
private ArrayCache() {
// Utility class
}
static synchronized void incResizeInt() {
resizeInt++;
}
static synchronized void incResizeDirtyInt() {
resizeDirtyInt++;
}
static synchronized void incResizeDirtyFloat() {
resizeDirtyFloat++;
}
static synchronized void incResizeDirtyByte() {
resizeDirtyByte++;
}
static synchronized void incOversize() {
oversize++;
}
static void dumpStats() {
if (resizeInt != 0 || resizeDirtyInt != 0 || resizeDirtyFloat != 0
|| resizeDirtyByte != 0 || oversize != 0) {
logInfo("ArrayCache: int resize: " + resizeInt
+ " - dirty int resize: " + resizeDirtyInt
+ " - dirty float resize: " + resizeDirtyFloat
+ " - dirty byte resize: " + resizeDirtyByte
+ " - oversize: " + oversize);
}
}
// small methods used a lot (to be inlined / optimized by hotspot)
static int getBucket(final int length) {
for (int i = 0; i < ARRAY_SIZES.length; i++) {
if (length <= ARRAY_SIZES[i]) {
return i;
}
}
return -1;
}
static int getBucketDirtyBytes(final int length) {
for (int i = 0; i < DIRTY_BYTE_ARRAY_SIZES.length; i++) {
if (length <= DIRTY_BYTE_ARRAY_SIZES[i]) {
return i;
}
}
return -1;
}
/**
* Return the new array size (~ x2)
* @param curSize current used size
* @param needSize needed size
* @return new array size
*/
public static int getNewSize(final int curSize, final int needSize) {
final int initial = (curSize & MASK_CLR_1);
int size;
if (initial > THRESHOLD_ARRAY_SIZE) {
size = initial + (initial >> 1); // x(3/2)
} else {
size = (initial) << 1; // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
// align to 4096:
size = ((needSize >> 12) + 1) << 12;
}
return size;
}
/**
* Return the new array size (~ x2)
* @param curSize current used size
* @param needSize needed size
* @return new array size
*/
public static long getNewLargeSize(final long curSize, final long needSize) {
long size;
if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) {
size = curSize + (curSize >> 2L); // x(5/4)
} else if (curSize > THRESHOLD_LARGE_ARRAY_SIZE) {
size = curSize + (curSize >> 1L); // x(3/2)
} else {
size = curSize << 1L; // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
// align to 4096:
size = ((needSize >> 12) + 1) << 12;
}
if (size >= Integer.MAX_VALUE) {
if (curSize >= Integer.MAX_VALUE) {
// hard overflow failure - we can't even accommodate
// new items without overflowing
throw new ArrayIndexOutOfBoundsException(
"array exceeds maximum capacity !");
}
// resize to maximum capacity:
size = Integer.MAX_VALUE;
}
return size;
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class ByteArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<byte[]> byteArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("ByteArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + byteArrays.size());
}
}
ByteArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.byteArrays = new ArrayDeque<byte[]>(6);
}
byte[] getArray() {
if (doStats) {
getOp++;
}
// use cache:
final byte[] array = byteArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new byte[arraySize];
}
void putDirtyArray(final byte[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, BYTE_0);
}
// fill cache:
byteArrays.addLast(array);
}
void putArray(final byte[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, BYTE_0);
// fill cache:
byteArrays.addLast(array);
}
static void fill(final byte[] array, final int fromIndex,
final int toIndex, final byte value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final byte[] array, final int fromIndex,
final int toIndex, final byte value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.awt.geom.PathConsumer2D;
final class CollinearSimplifier implements PathConsumer2D {
enum SimplifierState {
Empty, PreviousPoint, PreviousLine
};
// slope precision threshold
static final float EPS = 1e-4f; // aaime proposed 1e-3f
PathConsumer2D delegate;
SimplifierState state;
float px1, py1, px2, py2;
float pslope;
CollinearSimplifier() {
}
public CollinearSimplifier init(PathConsumer2D delegate) {
this.delegate = delegate;
this.state = SimplifierState.Empty;
return this; // fluent API
}
@Override
public void pathDone() {
emitStashedLine();
state = SimplifierState.Empty;
delegate.pathDone();
}
@Override
public void closePath() {
emitStashedLine();
state = SimplifierState.Empty;
delegate.closePath();
}
@Override
public long getNativeConsumer() {
return 0;
}
@Override
public void quadTo(float x1, float y1, float x2, float y2) {
emitStashedLine();
delegate.quadTo(x1, y1, x2, y2);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x2;
py1 = y2;
}
@Override
public void curveTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
emitStashedLine();
delegate.curveTo(x1, y1, x2, y2, x3, y3);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x3;
py1 = y3;
}
@Override
public void moveTo(float x, float y) {
emitStashedLine();
delegate.moveTo(x, y);
state = SimplifierState.PreviousPoint;
px1 = x;
py1 = y;
}
@Override
public void lineTo(final float x, final float y) {
switch (state) {
case Empty:
delegate.lineTo(x, y);
state = SimplifierState.PreviousPoint;
px1 = x;
py1 = y;
return;
case PreviousPoint:
state = SimplifierState.PreviousLine;
px2 = x;
py2 = y;
pslope = getSlope(px1, py1, x, y);
return;
case PreviousLine:
final float slope = getSlope(px2, py2, x, y);
// test for collinearity
if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) {
// merge segments
px2 = x;
py2 = y;
return;
}
// emit previous segment
delegate.lineTo(px2, py2);
px1 = px2;
py1 = py2;
px2 = x;
py2 = y;
pslope = slope;
return;
default:
}
}
private void emitStashedLine() {
if (state == SimplifierState.PreviousLine) {
delegate.lineTo(px2, py2);
}
}
private static float getSlope(float x1, float y1, float x2, float y2) {
float dy = y2 - y1;
if (dy == 0f) {
return (x2 > x1) ? Float.POSITIVE_INFINITY
: Float.NEGATIVE_INFINITY;
}
return (x2 - x1) / dy;
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Iterator;
final class Curve {
float ax, ay, bx, by, cx, cy, dx, dy;
float dax, day, dbx, dby;
// shared iterator instance
private final BreakPtrIterator iterator = new BreakPtrIterator();
Curve() {
}
void set(float[] points, int type) {
switch(type) {
case 8:
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
return;
case 6:
set(points[0], points[1],
points[2], points[3],
points[4], points[5]);
return;
default:
throw new InternalError("Curves can only be cubic or quadratic");
}
}
void set(float x1, float y1,
float x2, float y2,
float x3, float y3,
float x4, float y4)
{
ax = 3f * (x2 - x3) + x4 - x1;
ay = 3f * (y2 - y3) + y4 - y1;
bx = 3f * (x1 - 2f * x2 + x3);
by = 3f * (y1 - 2f * y2 + y3);
cx = 3f * (x2 - x1);
cy = 3f * (y2 - y1);
dx = x1;
dy = y1;
dax = 3f * ax; day = 3f * ay;
dbx = 2f * bx; dby = 2f * by;
}
void set(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
ax = 0f; ay = 0f;
bx = x1 - 2f * x2 + x3;
by = y1 - 2f * y2 + y3;
cx = 2f * (x2 - x1);
cy = 2f * (y2 - y1);
dx = x1;
dy = y1;
dax = 0f; day = 0f;
dbx = 2f * bx; dby = 2f * by;
}
float xat(float t) {
return t * (t * (t * ax + bx) + cx) + dx;
}
float yat(float t) {
return t * (t * (t * ay + by) + cy) + dy;
}
float dxat(float t) {
return t * (t * dax + dbx) + cx;
}
float dyat(float t) {
return t * (t * day + dby) + cy;
}
int dxRoots(float[] roots, int off) {
return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
}
int dyRoots(float[] roots, int off) {
return Helpers.quadraticRoots(day, dby, cy, roots, off);
}
int infPoints(float[] pts, int off) {
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
// Fortunately, this turns out to be quadratic, so there are at
// most 2 inflection points.
final float a = dax * dby - dbx * day;
final float b = 2f * (cy * dax - day * cx);
final float c = cy * dbx - cx * dby;
return Helpers.quadraticRoots(a, b, c, pts, off);
}
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(float[] pts, int off) {
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
// because the roots of a polynomial are not changed after multiplication
// by a constant, and this way we save a few multiplications).
final float a = 2f * (dax*dax + day*day);
final float b = 3f * (dax*dbx + day*dby);
final float c = 2f * (dax*cx + day*cy) + dbx*dbx + dby*dby;
final float d = dbx*cx + dby*cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
// a variant of the false position algorithm to find the roots. False
// position requires that 2 initial values x0,x1 be given, and that the
// function must have opposite signs at those values. To find such
// values, we need the local extrema of the ROC function, for which we
// need the roots of its derivative; however, it's harder to find the
// roots of the derivative in this case than it is to find the roots
// of the original function. So, we find all points where this curve's
// first and second derivative are perpendicular, and we pretend these
// are our local extrema. There are at most 3 of these, so we will check
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
int numPerpdfddf = perpendiculardfddf(roots, off);
float t0 = 0, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1f; // always check interval end points
numPerpdfddf++;
for (int i = off; i < off + numPerpdfddf; i++) {
float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
if (ft0 == 0f) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0f) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private static float eliminateInf(float x) {
return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
// This only works for the ROCsq-x functions. It might be nice to have
// the function as an argument, but that would be awkward in java6.
// TODO: It is something to consider for java8 (or whenever lambda
// expressions make it into the language), depending on how closures
// and turn out. Same goes for the newton's method
// algorithm in Helpers.java
private float falsePositionROCsqMinusX(float x0, float x1,
final float x, final float err)
{
final int iterLimit = 100;
int side = 0;
float t = x1, ft = eliminateInf(ROCsq(t) - x);
float s = x0, fs = eliminateInf(ROCsq(s) - x);
float r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = ROCsq(r) - x;
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
fs /= (1 << (-side));
side--;
} else {
side = -1;
}
} else if (fr * fs > 0) {
fs = fr; s = r;
if (side > 0) {
ft /= (1 << side);
side++;
} else {
side = 1;
}
} else {
break;
}
}
return r;
}
private static boolean sameSign(float x, float y) {
// another way is to test if x*y > 0. This is bad for small x, y.
return (x < 0f && y < 0f) || (x > 0f && y > 0f);
}
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private float ROCsq(final float t) {
// dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
final float dx = t * (t * dax + dbx) + cx;
final float dy = t * (t * day + dby) + cy;
final float ddx = 2f * dax * t + dbx;
final float ddy = 2f * day * t + dby;
final float dx2dy2 = dx*dx + dy*dy;
final float ddx2ddy2 = ddx*ddx + ddy*ddy;
final float ddxdxddydy = ddx*dx + ddy*dy;
return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
}
// curve to be broken should be in pts
// this will change the contents of pts but not Ts
// TODO: There's no reason for Ts to be an array. All we need is a sequence
// of t values at which to subdivide. An array statisfies this condition,
// but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
// Doing this will also make dashing easier, since we could easily make
// LengthIterator an Iterator<Float> and feed it to this function to simplify
// the loop in Dasher.somethingTo.
BreakPtrIterator breakPtsAtTs(final float[] pts, final int type,
final float[] Ts, final int numTs)
{
assert pts.length >= 2*type && numTs <= Ts.length;
// initialize shared iterator:
iterator.init(pts, type, Ts, numTs);
return iterator;
}
static final class BreakPtrIterator {
private int nextCurveIdx;
private int curCurveOff;
private float prevT;
private float[] pts;
private int type;
private float[] ts;
private int numTs;
void init(final float[] pts, final int type,
final float[] ts, final int numTs) {
this.pts = pts;
this.type = type;
this.ts = ts;
this.numTs = numTs;
nextCurveIdx = 0;
curCurveOff = 0;
prevT = 0f;
}
public boolean hasNext() {
return nextCurveIdx <= numTs;
}
public int next() {
int ret;
if (nextCurveIdx < numTs) {
float curT = ts[nextCurveIdx];
float splitT = (curT - prevT) / (1f - prevT);
Helpers.subdivideAt(splitT,
pts, curCurveOff,
pts, 0,
pts, type, type);
prevT = curT;
ret = 0;
curCurveOff = type;
} else {
ret = curCurveOff;
}
nextCurveIdx++;
return ret;
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
/**
* The <code>Dasher</code> class takes a series of linear commands
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
* <code>end</code>) and breaks them into smaller segments according to a
* dash pattern array and a starting dash phase.
*
* <p> Issues: in J2Se, a zero length dash segment as drawn as a very
* short dash, whereas Pisces does not draw anything. The PostScript
* semantics are unclear.
*
*/
final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
static final int recLimit = 4;
static final float ERR = 0.01f;
static final float minTincrement = 1f / (1 << recLimit);
private PathConsumer2D out;
private float[] dash;
private int dashLen;
private float startPhase;
private boolean startDashOn;
private int startIdx;
private boolean starting;
private boolean needsMoveTo;
private int idx;
private boolean dashOn;
private float phase;
private float sx, sy;
private float x0, y0;
// temporary storage for the current curve
private final float[] curCurvepts;
// per-thread renderer context
final RendererContext rdrCtx;
// dashes array (dirty)
final float[] dashes_initial = new float[INITIAL_ARRAY];
// flag to recycle dash array copy
boolean recycleDashes;
// per-thread initial arrays (large enough to satisfy most usages
// +1 to avoid recycling in Helpers.widenArray()
private final float[] firstSegmentsBuffer_initial = new float[INITIAL_ARRAY + 1];
/**
* Constructs a <code>Dasher</code>.
* @param rdrCtx per-thread renderer context
*/
Dasher(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
firstSegmentsBuffer = firstSegmentsBuffer_initial;
// we need curCurvepts to be able to contain 2 curves because when
// dashing curves, we need to subdivide it
curCurvepts = new float[8 * 2];
}
/**
* Initialize the <code>Dasher</code>.
*
* @param out an output <code>PathConsumer2D</code>.
* @param dash an array of <code>float</code>s containing the dash pattern
* @param dashLen length of the given dash array
* @param phase a <code>float</code> containing the dash phase
* @param recycleDashes true to indicate to recycle the given dash array
* @return this instance
*/
Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
float phase, boolean recycleDashes)
{
if (phase < 0f) {
throw new IllegalArgumentException("phase < 0 !");
}
this.out = out;
// Normalize so 0 <= phase < dash[0]
int idx = 0;
dashOn = true;
float d;
while (phase >= (d = dash[idx])) {
phase -= d;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
this.dash = dash;
this.dashLen = dashLen;
this.startPhase = this.phase = phase;
this.startDashOn = dashOn;
this.startIdx = idx;
this.starting = true;
needsMoveTo = false;
firstSegidx = 0;
this.recycleDashes = recycleDashes;
return this; // fluent API
}
/**
* Disposes this dasher:
* clean up before reusing this instance
*/
void dispose() {
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(curCurvepts, 0f);
Arrays.fill(firstSegmentsBuffer, 0f);
}
// Return arrays:
if (recycleDashes && dash != dashes_initial) {
rdrCtx.putDirtyFloatArray(dash);
dash = null;
}
if (firstSegmentsBuffer != firstSegmentsBuffer_initial) {
rdrCtx.putDirtyFloatArray(firstSegmentsBuffer);
firstSegmentsBuffer = firstSegmentsBuffer_initial;
}
}
@Override
public void moveTo(float x0, float y0) {
if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
needsMoveTo = true;
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
this.sx = this.x0 = x0;
this.sy = this.y0 = y0;
this.starting = true;
}
private void emitSeg(float[] buf, int off, int type) {
switch (type) {
case 8:
out.curveTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3],
buf[off+4], buf[off+5]);
return;
case 6:
out.quadTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3]);
return;
case 4:
out.lineTo(buf[off], buf[off+1]);
return;
default:
}
}
private void emitFirstSegments() {
final float[] fSegBuf = firstSegmentsBuffer;
for (int i = 0; i < firstSegidx; ) {
int type = (int)fSegBuf[i];
emitSeg(fSegBuf, i + 1, type);
i += (type - 1);
}
firstSegidx = 0;
}
// We don't emit the first dash right away. If we did, caps would be
// drawn on it, but we need joins to be drawn if there's a closePath()
// So, we store the path elements that make up the first dash in the
// buffer below.
private float[] firstSegmentsBuffer; // dynamic array
private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
// fullCurve is true iff the curve in pts has not been split.
private void goTo(float[] pts, int off, final int type) {
float x = pts[off + type - 4];
float y = pts[off + type - 3];
if (dashOn) {
if (starting) {
int len = type - 2 + 1;
int segIdx = firstSegidx;
float[] buf = firstSegmentsBuffer;
if (segIdx + len > buf.length) {
if (doStats) {
RendererContext.stats.stat_array_dasher_firstSegmentsBuffer
.add(segIdx + len);
}
firstSegmentsBuffer = buf
= rdrCtx.widenDirtyFloatArray(buf, segIdx, segIdx + len);
}
buf[segIdx++] = type;
len--;
// small arraycopy (2, 4 or 6) but with offset:
System.arraycopy(pts, off, buf, segIdx, len);
segIdx += len;
firstSegidx = segIdx;
} else {
if (needsMoveTo) {
out.moveTo(x0, y0);
needsMoveTo = false;
}
emitSeg(pts, off, type);
}
} else {
starting = false;
needsMoveTo = true;
}
this.x0 = x;
this.y0 = y;
}
@Override
public void lineTo(float x1, float y1) {
float dx = x1 - x0;
float dy = y1 - y0;
float len = dx*dx + dy*dy;
if (len == 0f) {
return;
}
len = (float) Math.sqrt(len);
// The scaling factors needed to get the dx and dy of the
// transformed dash segments.
final float cx = dx / len;
final float cy = dy / len;
final float[] _curCurvepts = curCurvepts;
final float[] _dash = dash;
float leftInThisDashSegment;
float dashdx, dashdy, p;
while (true) {
leftInThisDashSegment = _dash[idx] - phase;
if (len <= leftInThisDashSegment) {
_curCurvepts[0] = x1;
_curCurvepts[1] = y1;
goTo(_curCurvepts, 0, 4);
// Advance phase within current dash segment
phase += len;
// TODO: compare float values using epsilon:
if (len == leftInThisDashSegment) {
phase = 0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
return;
}
dashdx = _dash[idx] * cx;
dashdy = _dash[idx] * cy;
if (phase == 0f) {
_curCurvepts[0] = x0 + dashdx;
_curCurvepts[1] = y0 + dashdy;
} else {
p = leftInThisDashSegment / _dash[idx];
_curCurvepts[0] = x0 + p * dashdx;
_curCurvepts[1] = y0 + p * dashdy;
}
goTo(_curCurvepts, 0, 4);
len -= leftInThisDashSegment;
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0f;
}
}
// shared instance in Dasher
private final LengthIterator li = new LengthIterator();
// preconditions: curCurvepts must be an array of length at least 2 * type,
// that contains the curve we want to dash in the first type elements
private void somethingTo(int type) {
if (pointCurve(curCurvepts, type)) {
return;
}
li.initializeIterationOnCurve(curCurvepts, type);
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
float lastSplitT = 0f;
float t;
float leftInThisDashSegment = dash[idx] - phase;
while ((t = li.next(leftInThisDashSegment)) < 1f) {
if (t != 0f) {
Helpers.subdivideAt((t - lastSplitT) / (1f - lastSplitT),
curCurvepts, curCurveoff,
curCurvepts, 0,
curCurvepts, type, type);
lastSplitT = t;
goTo(curCurvepts, 2, type);
curCurveoff = type;
}
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0f;
leftInThisDashSegment = dash[idx];
}
goTo(curCurvepts, curCurveoff+2, type);
phase += li.lastSegLen();
if (phase >= dash[idx]) {
phase = 0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
// reset LengthIterator:
li.reset();
}
private static boolean pointCurve(float[] curve, int type) {
for (int i = 2; i < type; i++) {
if (curve[i] != curve[i-2]) {
return false;
}
}
return true;
}
// Objects of this class are used to iterate through curves. They return
// t values where the left side of the curve has a specified length.
// It does this by subdividing the input curve until a certain error
// condition has been met. A recursive subdivision procedure would
// return as many as 1<<limit curves, but this is an iterator and we
// don't need all the curves all at once, so what we carry out a
// lazy inorder traversal of the recursion tree (meaning we only move
// through the tree when we need the next subdivided curve). This saves
// us a lot of memory because at any one time we only need to store
// limit+1 curves - one for each level of the tree + 1.
// NOTE: the way we do things here is not enough to traverse a general
// tree; however, the trees we are interested in have the property that
// every non leaf node has exactly 2 children
static final class LengthIterator {
private enum Side {LEFT, RIGHT};
// Holds the curves at various levels of the recursion. The root
// (i.e. the original curve) is at recCurveStack[0] (but then it
// gets subdivided, the left half is put at 1, so most of the time
// only the right half of the original curve is at 0)
private final float[][] recCurveStack; // dirty
// sides[i] indicates whether the node at level i+1 in the path from
// the root to the current leaf is a left or right child of its parent.
private final Side[] sides; // dirty
private int curveType;
// lastT and nextT delimit the current leaf.
private float nextT;
private float lenAtNextT;
private float lastT;
private float lenAtLastT;
private float lenAtLastSplit;
private float lastSegLen;
// the current level in the recursion tree. 0 is the root. limit
// is the deepest possible leaf.
private int recLevel;
private boolean done;
// the lengths of the lines of the control polygon. Only its first
// curveType/2 - 1 elements are valid. This is an optimization. See
// next(float) for more detail.
private final float[] curLeafCtrlPolyLengths = new float[3];
LengthIterator() {
this.recCurveStack = new float[recLimit + 1][8];
this.sides = new Side[recLimit];
// if any methods are called without first initializing this object
// on a curve, we want it to fail ASAP.
this.nextT = Float.MAX_VALUE;
this.lenAtNextT = Float.MAX_VALUE;
this.lenAtLastSplit = Float.MIN_VALUE;
this.recLevel = Integer.MIN_VALUE;
this.lastSegLen = Float.MAX_VALUE;
this.done = true;
}
/**
* Reset this LengthIterator.
*/
void reset() {
// keep data dirty
// as it appears not useful to reset data:
if (doCleanDirty) {
final int recLimit = recCurveStack.length - 1;
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0f);
}
Arrays.fill(sides, Side.LEFT);
Arrays.fill(curLeafCtrlPolyLengths, 0f);
Arrays.fill(nextRoots, 0f);
Arrays.fill(flatLeafCoefCache, 0f);
flatLeafCoefCache[2] = -1f;
}
}
void initializeIterationOnCurve(float[] pts, int type) {
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
this.curveType = type;
this.recLevel = 0;
this.lastT = 0f;
this.lenAtLastT = 0f;
this.nextT = 0f;
this.lenAtNextT = 0f;
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0f;
if (recLevel > 0) {
this.sides[0] = Side.LEFT;
this.done = false;
} else {
// the root of the tree is a leaf so we're done.
this.sides[0] = Side.RIGHT;
this.done = true;
}
this.lastSegLen = 0f;
}
// 0 == false, 1 == true, -1 == invalid cached value.
private int cachedHaveLowAcceleration = -1;
private boolean haveLowAcceleration(float err) {
if (cachedHaveLowAcceleration == -1) {
final float len1 = curLeafCtrlPolyLengths[0];
final float len2 = curLeafCtrlPolyLengths[1];
// the test below is equivalent to !within(len1/len2, 1, err).
// It is using a multiplication instead of a division, so it
// should be a bit faster.
if (!Helpers.within(len1, len2, err*len2)) {
cachedHaveLowAcceleration = 0;
return false;
}
if (curveType == 8) {
final float len3 = curLeafCtrlPolyLengths[2];
// if len1 is close to 2 and 2 is close to 3, that probably
// means 1 is close to 3 so the second part of this test might
// not be needed, but it doesn't hurt to include it.
final float errLen3 = err * len3;
if (!(Helpers.within(len2, len3, errLen3) &&
Helpers.within(len1, len3, errLen3))) {
cachedHaveLowAcceleration = 0;
return false;
}
}
cachedHaveLowAcceleration = 1;
return true;
}
return (cachedHaveLowAcceleration == 1);
}
// we want to avoid allocations/gc so we keep this array so we
// can put roots in it,
private final float[] nextRoots = new float[4];
// caches the coefficients of the current leaf in its flattened
// form (see inside next() for what that means). The cache is
// invalid when it's third element is negative, since in any
// valid flattened curve, this would be >= 0.
private final float[] flatLeafCoefCache = new float[]{0f, 0f, -1f, 0f};
// returns the t value where the remaining curve should be split in
// order for the left subdivided curve to have length len. If len
// is >= than the length of the uniterated curve, it returns 1.
float next(final float len) {
final float targetLength = lenAtLastSplit + len;
while (lenAtNextT < targetLength) {
if (done) {
lastSegLen = lenAtNextT - lenAtLastSplit;
return 1f;
}
goToNextLeaf();
}
lenAtLastSplit = targetLength;
final float leaflen = lenAtNextT - lenAtLastT;
float t = (targetLength - lenAtLastT) / leaflen;
// cubicRootsInAB is a fairly expensive call, so we just don't do it
// if the acceleration in this section of the curve is small enough.
if (!haveLowAcceleration(0.05f)) {
// We flatten the current leaf along the x axis, so that we're
// left with a, b, c which define a 1D Bezier curve. We then
// solve this to get the parameter of the original leaf that
// gives us the desired length.
final float[] _flatLeafCoefCache = flatLeafCoefCache;
if (_flatLeafCoefCache[2] < 0) {
float x = 0f + curLeafCtrlPolyLengths[0],
y = x + curLeafCtrlPolyLengths[1];
if (curveType == 8) {
float z = y + curLeafCtrlPolyLengths[2];
_flatLeafCoefCache[0] = 3f * (x - y) + z;
_flatLeafCoefCache[1] = 3f * (y - 2f * x);
_flatLeafCoefCache[2] = 3f * x;
_flatLeafCoefCache[3] = -z;
} else if (curveType == 6) {
_flatLeafCoefCache[0] = 0f;
_flatLeafCoefCache[1] = y - 2f * x;
_flatLeafCoefCache[2] = 2f * x;
_flatLeafCoefCache[3] = -y;
}
}
float a = _flatLeafCoefCache[0];
float b = _flatLeafCoefCache[1];
float c = _flatLeafCoefCache[2];
float d = t * _flatLeafCoefCache[3];
// we use cubicRootsInAB here, because we want only roots in 0, 1,
// and our quadratic root finder doesn't filter, so it's just a
// matter of convenience.
int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
if (n == 1 && !Float.isNaN(nextRoots[0])) {
t = nextRoots[0];
}
}
// t is relative to the current leaf, so we must make it a valid parameter
// of the original curve.
t = t * (nextT - lastT) + lastT;
if (t >= 1f) {
t = 1f;
done = true;
}
// even if done = true, if we're here, that means targetLength
// is equal to, or very, very close to the total length of the
// curve, so lastSegLen won't be too high. In cases where len
// overshoots the curve, this method will exit in the while
// loop, and lastSegLen will still be set to the right value.
lastSegLen = len;
return t;
}
float lastSegLen() {
return lastSegLen;
}
// go to the next leaf (in an inorder traversal) in the recursion tree
// preconditions: must be on a leaf, and that leaf must not be the root.
private void goToNextLeaf() {
// We must go to the first ancestor node that has an unvisited
// right child.
int _recLevel = recLevel;
final Side[] _sides = sides;
_recLevel--;
while(_sides[_recLevel] == Side.RIGHT) {
if (_recLevel == 0) {
recLevel = 0;
done = true;
return;
}
_recLevel--;
}
_sides[_recLevel] = Side.RIGHT;
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(recCurveStack[_recLevel], 0,
recCurveStack[_recLevel+1], 0, 8);
_recLevel++;
recLevel = _recLevel;
goLeft();
}
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
float len = onLeaf();
if (len >= 0f) {
lastT = nextT;
lenAtLastT = lenAtNextT;
nextT += (1 << (recLimit - recLevel)) * minTincrement;
lenAtNextT += len;
// invalidate caches
flatLeafCoefCache[2] = -1f;
cachedHaveLowAcceleration = -1;
} else {
Helpers.subdivide(recCurveStack[recLevel], 0,
recCurveStack[recLevel+1], 0,
recCurveStack[recLevel], 0, curveType);
sides[recLevel] = Side.LEFT;
recLevel++;
goLeft();
}
}
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
// the length of the leaf if we are on a leaf.
private float onLeaf() {
float[] curve = recCurveStack[recLevel];
float polyLen = 0f;
float x0 = curve[0], y0 = curve[1];
for (int i = 2; i < curveType; i += 2) {
final float x1 = curve[i], y1 = curve[i+1];
final float len = Helpers.linelen(x0, y0, x1, y1);
polyLen += len;
curLeafCtrlPolyLengths[i/2 - 1] = len;
x0 = x1;
y0 = y1;
}
final float lineLen = Helpers.linelen(curve[0], curve[1],
curve[curveType-2],
curve[curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == recLimit) {
return (polyLen + lineLen) / 2f;
}
return -1f;
}
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
final float[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
_curCurvepts[2] = x1; _curCurvepts[3] = y1;
_curCurvepts[4] = x2; _curCurvepts[5] = y2;
_curCurvepts[6] = x3; _curCurvepts[7] = y3;
somethingTo(8);
}
@Override
public void quadTo(float x1, float y1, float x2, float y2) {
final float[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
_curCurvepts[2] = x1; _curCurvepts[3] = y1;
_curCurvepts[4] = x2; _curCurvepts[5] = y2;
somethingTo(6);
}
@Override
public void closePath() {
lineTo(sx, sy);
if (firstSegidx > 0) {
if (!dashOn || needsMoveTo) {
out.moveTo(sx, sy);
}
emitFirstSegments();
}
moveTo(sx, sy);
}
@Override
public void pathDone() {
if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
out.pathDone();
// Dispose this instance:
dispose();
}
@Override
public long getNativeConsumer() {
throw new InternalError("Dasher does not use a native consumer");
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class FloatArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<float[]> floatArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("FloatArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + floatArrays.size());
}
}
FloatArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.floatArrays = new ArrayDeque<float[]>(6);
}
float[] getArray() {
if (doStats) {
getOp++;
}
// use cache
final float[] array = floatArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new float[arraySize];
}
void putDirtyArray(final float[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, 0f);
}
// fill cache:
floatArrays.addLast(array);
}
void putArray(final float[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, 0f);
// fill cache:
floatArrays.addLast(array);
}
static void fill(final float[] array, final int fromIndex,
final int toIndex, final float value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final float[] array, final int fromIndex,
final int toIndex, final float value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.misc.DoubleConsts;
import sun.misc.FloatConsts;
/**
* Faster Math ceil / floor routines derived from StrictMath
*/
public final class FloatMath implements MarlinConst {
// overflow / NaN handling enabled:
static final boolean CHECK_OVERFLOW = true;
static final boolean CHECK_NAN = true;
private FloatMath() {
// utility class
}
// faster inlined min/max functions in the branch prediction is high
static float max(final float a, final float b) {
// no NaN handling
return (a >= b) ? a : b;
}
static int max(final int a, final int b) {
return (a >= b) ? a : b;
}
static int min(final int a, final int b) {
return (a <= b) ? a : b;
}
/**
* Returns the smallest (closest to negative infinity) {@code float} value
* that is greater than or equal to the argument and is equal to a
* mathematical integer. Special cases:
* <ul><li>If the argument value is already equal to a mathematical integer,
* then the result is the same as the argument. <li>If the argument is NaN
* or an infinity or positive zero or negative zero, then the result is the
* same as the argument. <li>If the argument value is less than zero but
* greater than -1.0, then the result is negative zero.</ul> Note that the
* value of {@code StrictMath.ceil(x)} is exactly the value of
* {@code -StrictMath.floor(-x)}.
*
* @param a a value.
* @return the smallest (closest to negative infinity) floating-point value
* that is greater than or equal to the argument and is equal to a
* mathematical integer.
*/
public static float ceil_f(final float a) {
// Derived from StrictMath.ceil(double):
// Inline call to Math.getExponent(a) to
// compute only once Float.floatToRawIntBits(a)
final int doppel = Float.floatToRawIntBits(a);
final int exponent = ((doppel & FloatConsts.EXP_BIT_MASK)
>> (FloatConsts.SIGNIFICAND_WIDTH - 1))
- FloatConsts.EXP_BIAS;
if (exponent < 0) {
/*
* Absolute value of argument is less than 1.
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -0f : 1f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
* Infinity, NaN, or a value so large it must be integral.
*/
return a;
}
// Else the argument is either an integral value already XOR it
// has to be rounded to one.
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
}
// 0 handled above as an integer
// sign: 1 for negative, 0 for positive numbers
// add : 0 for negative and 1 for positive numbers
return Float.intBitsToFloat(intpart) + ((~intpart) >>> 31);
}
/**
* Returns the largest (closest to positive infinity) {@code float} value
* that is less than or equal to the argument and is equal to a mathematical
* integer. Special cases:
* <ul><li>If the argument value is already equal to a mathematical integer,
* then the result is the same as the argument. <li>If the argument is NaN
* or an infinity or positive zero or negative zero, then the result is the
* same as the argument.</ul>
*
* @param a a value.
* @return the largest (closest to positive infinity) floating-point value
* that less than or equal to the argument and is equal to a mathematical
* integer.
*/
public static float floor_f(final float a) {
// Derived from StrictMath.floor(double):
// Inline call to Math.getExponent(a) to
// compute only once Float.floatToRawIntBits(a)
final int doppel = Float.floatToRawIntBits(a);
final int exponent = ((doppel & FloatConsts.EXP_BIT_MASK)
>> (FloatConsts.SIGNIFICAND_WIDTH - 1))
- FloatConsts.EXP_BIAS;
if (exponent < 0) {
/*
* Absolute value of argument is less than 1.
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -1f : 0f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
* Infinity, NaN, or a value so large it must be integral.
*/
return a;
}
// Else the argument is either an integral value already XOR it
// has to be rounded to one.
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
}
// 0 handled above as an integer
// sign: 1 for negative, 0 for positive numbers
// add : -1 for negative and 0 for positive numbers
return Float.intBitsToFloat(intpart) + (intpart >> 31);
}
/**
* Faster alternative to ceil(float) optimized for the integer domain
* and supporting NaN and +/-Infinity.
*
* @param a a value.
* @return the largest (closest to positive infinity) integer value
* that less than or equal to the argument and is equal to a mathematical
* integer.
*/
public static int ceil_int(final float a) {
final int intpart = (int) a;
if (a <= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MAX_VALUE)
|| CHECK_NAN && Float.isNaN(a)) {
return intpart;
}
return intpart + 1;
}
/**
* Faster alternative to floor(float) optimized for the integer domain
* and supporting NaN and +/-Infinity.
*
* @param a a value.
* @return the largest (closest to positive infinity) floating-point value
* that less than or equal to the argument and is equal to a mathematical
* integer.
*/
public static int floor_int(final float a) {
final int intpart = (int) a;
if (a >= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MIN_VALUE)
|| CHECK_NAN && Float.isNaN(a)) {
return intpart;
}
return intpart - 1;
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.sqrt;
import static java.lang.Math.cbrt;
import static java.lang.Math.acos;
final class Helpers implements MarlinConst {
private Helpers() {
throw new Error("This is a non instantiable class");
}
static boolean within(final float x, final float y, final float err) {
final float d = y - x;
return (d <= err && d >= -err);
}
static boolean within(final double x, final double y, final double err) {
final double d = y - x;
return (d <= err && d >= -err);
}
static int quadraticRoots(final float a, final float b,
final float c, float[] zeroes, final int off)
{
int ret = off;
float t;
if (a != 0f) {
final float dis = b*b - 4*a*c;
if (dis > 0f) {
final float sqrtDis = (float)Math.sqrt(dis);
// depending on the sign of b we use a slightly different
// algorithm than the traditional one to find one of the roots
// so we can avoid adding numbers of different signs (which
// might result in loss of precision).
if (b >= 0f) {
zeroes[ret++] = (2f * c) / (-b - sqrtDis);
zeroes[ret++] = (-b - sqrtDis) / (2f * a);
} else {
zeroes[ret++] = (-b + sqrtDis) / (2f * a);
zeroes[ret++] = (2f * c) / (-b + sqrtDis);
}
} else if (dis == 0f) {
t = (-b) / (2f * a);
zeroes[ret++] = t;
}
} else {
if (b != 0f) {
t = (-c) / b;
zeroes[ret++] = t;
}
}
return ret - off;
}
// find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
static int cubicRootsInAB(float d, float a, float b, float c,
float[] pts, final int off,
final float A, final float B)
{
if (d == 0f) {
int num = quadraticRoots(a, b, c, pts, off);
return filterOutNotInAB(pts, off, num, A, B) - off;
}
// From Graphics Gems:
// http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// (also from awt.geom.CubicCurve2D. But here we don't need as
// much accuracy and we don't want to create arrays so we use
// our own customized version).
// normal form: x^3 + ax^2 + bx + c = 0
a /= d;
b /= d;
c /= d;
// substitute x = y - A/3 to eliminate quadratic term:
// x^3 +Px + Q = 0
//
// Since we actually need P/3 and Q/2 for all of the
// calculations that follow, we will calculate
// p = P/3
// q = Q/2
// instead and use those values for simplicity of the code.
double sq_A = a * a;
double p = (1.0/3.0) * ((-1.0/3.0) * sq_A + b);
double q = (1.0/2.0) * ((2.0/27.0) * a * sq_A - (1.0/3.0) * a * b + c);
// use Cardano's formula
double cb_p = p * p * p;
double D = q * q + cb_p;
int num;
if (D < 0.0) {
// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
final double phi = (1.0/3.0) * acos(-q / sqrt(-cb_p));
final double t = 2.0 * sqrt(-p);
pts[ off+0 ] = (float)( t * cos(phi));
pts[ off+1 ] = (float)(-t * cos(phi + (PI / 3.0)));
pts[ off+2 ] = (float)(-t * cos(phi - (PI / 3.0)));
num = 3;
} else {
final double sqrt_D = sqrt(D);
final double u = cbrt(sqrt_D - q);
final double v = - cbrt(sqrt_D + q);
pts[ off ] = (float)(u + v);
num = 1;
if (within(D, 0.0, 1e-8)) {
pts[off+1] = -(pts[off] / 2f);
num = 2;
}
}
final float sub = (1f/3f) * a;
for (int i = 0; i < num; ++i) {
pts[ off+i ] -= sub;
}
return filterOutNotInAB(pts, off, num, A, B) - off;
}
static float evalCubic(final float a, final float b,
final float c, final float d,
final float t)
{
return t * (t * (t * a + b) + c) + d;
}
static float evalQuad(final float a, final float b,
final float c, final float t)
{
return t * (t * a + b) + c;
}
// returns the index 1 past the last valid element remaining after filtering
static int filterOutNotInAB(float[] nums, final int off, final int len,
final float a, final float b)
{
int ret = off;
for (int i = off, end = off + len; i < end; i++) {
if (nums[i] >= a && nums[i] < b) {
nums[ret++] = nums[i];
}
}
return ret;
}
static float polyLineLength(float[] poly, final int off, final int nCoords) {
assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
float acc = 0;
for (int i = off + 2; i < off + nCoords; i += 2) {
acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
}
return acc;
}
static float linelen(float x1, float y1, float x2, float y2) {
final float dx = x2 - x1;
final float dy = y2 - y1;
return (float)Math.sqrt(dx*dx + dy*dy);
}
static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
float[] right, int rightoff, int type)
{
switch(type) {
case 6:
Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
return;
case 8:
Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
return;
default:
throw new InternalError("Unsupported curve type");
}
}
static void isort(float[] a, int off, int len) {
for (int i = off + 1, end = off + len; i < end; i++) {
float ai = a[i];
int j = i - 1;
for (; j >= off && a[j] > ai; j--) {
a[j+1] = a[j];
}
a[j+1] = ai;
}
}
// Most of these are copied from classes in java.awt.geom because we need
// float versions of these functions, and Line2D, CubicCurve2D,
// QuadCurve2D don't provide them.
/**
* Subdivides the cubic curve specified by the coordinates
* stored in the <code>src</code> array at indices <code>srcoff</code>
* through (<code>srcoff</code>&nbsp;+&nbsp;7) and stores the
* resulting two subdivided curves into the two result arrays at the
* corresponding indices.
* Either or both of the <code>left</code> and <code>right</code>
* arrays may be <code>null</code> or a reference to the same array
* as the <code>src</code> array.
* Note that the last point in the first subdivided curve is the
* same as the first point in the second subdivided curve. Thus,
* it is possible to pass the same array for <code>left</code>
* and <code>right</code> and to use offsets, such as <code>rightoff</code>
* equals (<code>leftoff</code> + 6), in order
* to avoid allocating extra storage for this common point.
* @param src the array holding the coordinates for the source curve
* @param srcoff the offset into the array of the beginning of the
* the 6 source coordinates
* @param left the array for storing the coordinates for the first
* half of the subdivided curve
* @param leftoff the offset into the array of the beginning of the
* the 6 left coordinates
* @param right the array for storing the coordinates for the second
* half of the subdivided curve
* @param rightoff the offset into the array of the beginning of the
* the 6 right coordinates
* @since 1.7
*/
static void subdivideCubic(float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx1 = src[srcoff + 2];
float ctrly1 = src[srcoff + 3];
float ctrlx2 = src[srcoff + 4];
float ctrly2 = src[srcoff + 5];
float x2 = src[srcoff + 6];
float y2 = src[srcoff + 7];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
}
x1 = (x1 + ctrlx1) / 2f;
y1 = (y1 + ctrly1) / 2f;
x2 = (x2 + ctrlx2) / 2f;
y2 = (y2 + ctrly2) / 2f;
float centerx = (ctrlx1 + ctrlx2) / 2f;
float centery = (ctrly1 + ctrly2) / 2f;
ctrlx1 = (x1 + centerx) / 2f;
ctrly1 = (y1 + centery) / 2f;
ctrlx2 = (x2 + centerx) / 2f;
ctrly2 = (y2 + centery) / 2f;
centerx = (ctrlx1 + ctrlx2) / 2f;
centery = (ctrly1 + ctrly2) / 2f;
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx1;
left[leftoff + 5] = ctrly1;
left[leftoff + 6] = centerx;
left[leftoff + 7] = centery;
}
if (right != null) {
right[rightoff + 0] = centerx;
right[rightoff + 1] = centery;
right[rightoff + 2] = ctrlx2;
right[rightoff + 3] = ctrly2;
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
}
static void subdivideCubicAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx1 = src[srcoff + 2];
float ctrly1 = src[srcoff + 3];
float ctrlx2 = src[srcoff + 4];
float ctrly2 = src[srcoff + 5];
float x2 = src[srcoff + 6];
float y2 = src[srcoff + 7];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
}
x1 = x1 + t * (ctrlx1 - x1);
y1 = y1 + t * (ctrly1 - y1);
x2 = ctrlx2 + t * (x2 - ctrlx2);
y2 = ctrly2 + t * (y2 - ctrly2);
float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
float centery = ctrly1 + t * (ctrly2 - ctrly1);
ctrlx1 = x1 + t * (centerx - x1);
ctrly1 = y1 + t * (centery - y1);
ctrlx2 = centerx + t * (x2 - centerx);
ctrly2 = centery + t * (y2 - centery);
centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
centery = ctrly1 + t * (ctrly2 - ctrly1);
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx1;
left[leftoff + 5] = ctrly1;
left[leftoff + 6] = centerx;
left[leftoff + 7] = centery;
}
if (right != null) {
right[rightoff + 0] = centerx;
right[rightoff + 1] = centery;
right[rightoff + 2] = ctrlx2;
right[rightoff + 3] = ctrly2;
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
}
static void subdivideQuad(float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx = src[srcoff + 2];
float ctrly = src[srcoff + 3];
float x2 = src[srcoff + 4];
float y2 = src[srcoff + 5];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
x1 = (x1 + ctrlx) / 2f;
y1 = (y1 + ctrly) / 2f;
x2 = (x2 + ctrlx) / 2f;
y2 = (y2 + ctrly) / 2f;
ctrlx = (x1 + x2) / 2f;
ctrly = (y1 + y2) / 2f;
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx;
left[leftoff + 5] = ctrly;
}
if (right != null) {
right[rightoff + 0] = ctrlx;
right[rightoff + 1] = ctrly;
right[rightoff + 2] = x2;
right[rightoff + 3] = y2;
}
}
static void subdivideQuadAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx = src[srcoff + 2];
float ctrly = src[srcoff + 3];
float x2 = src[srcoff + 4];
float y2 = src[srcoff + 5];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
x1 = x1 + t * (ctrlx - x1);
y1 = y1 + t * (ctrly - y1);
x2 = ctrlx + t * (x2 - ctrlx);
y2 = ctrly + t * (y2 - ctrly);
ctrlx = x1 + t * (x2 - x1);
ctrly = y1 + t * (y2 - y1);
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx;
left[leftoff + 5] = ctrly;
}
if (right != null) {
right[rightoff + 0] = ctrlx;
right[rightoff + 1] = ctrly;
right[rightoff + 2] = x2;
right[rightoff + 3] = y2;
}
}
static void subdivideAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff, int size)
{
switch(size) {
case 8:
subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
case 6:
subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class IntArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<int[]> intArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("IntArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + intArrays.size());
}
}
IntArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.intArrays = new ArrayDeque<int[]>(6);
}
int[] getArray() {
if (doStats) {
getOp++;
}
// use cache:
final int[] array = intArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new int[arraySize];
}
void putDirtyArray(final int[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, 0);
}
// fill cache:
intArrays.addLast(array);
}
void putArray(final int[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, 0);
// fill cache:
intArrays.addLast(array);
}
static void fill(final int[] array, final int fromIndex,
final int toIndex, final int value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final int[] array, final int fromIndex,
final int toIndex, final int value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.misc.Unsafe;
/**
* An object used to cache pre-rendered complex paths.
*
* @see Renderer
*/
public final class MarlinCache implements MarlinConst {
static final boolean FORCE_RLE = MarlinProperties.isForceRLE();
static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE();
// minimum width to try using RLE encoding:
static final int RLE_MIN_WIDTH
= Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth());
// maximum width for RLE encoding:
// values are stored as int [x|alpha] where alpha is 8 bits
static final int RLE_MAX_WIDTH = 1 << (24 - 1);
// 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes
// x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression
static final long INITIAL_CHUNK_ARRAY = TILE_SIZE * INITIAL_PIXEL_DIM; // 64K
// The alpha map used by this object (taken out of our map cache) to convert
// pixel coverage counts gotten from MarlinCache (which are in the range
// [0, maxalpha]) into alpha values, which are in [0,256).
static final byte[] ALPHA_MAP;
static final OffHeapArray ALPHA_MAP_UNSAFE;
static {
final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA);
ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K
ALPHA_MAP =_ALPHA_MAP;
final Unsafe _unsafe = OffHeapArray.unsafe;
final long addr = ALPHA_MAP_UNSAFE.address;
for (int i = 0; i < _ALPHA_MAP.length; i++) {
_unsafe.putByte(addr + i, _ALPHA_MAP[i]);
}
}
int bboxX0, bboxY0, bboxX1, bboxY1;
// 1D dirty arrays
// row index in rowAAChunk[]
final long[] rowAAChunkIndex = new long[TILE_SIZE];
// first pixel (inclusive) for each row
final int[] rowAAx0 = new int[TILE_SIZE];
// last pixel (exclusive) for each row
final int[] rowAAx1 = new int[TILE_SIZE];
// encoding mode (0=raw, 1=RLE encoding) for each row
final int[] rowAAEnc = new int[TILE_SIZE];
// coded length (RLE encoding) for each row
final long[] rowAALen = new long[TILE_SIZE];
// last position in RLE decoding for each row (getAlpha):
final long[] rowAAPos = new long[TILE_SIZE];
// dirty off-heap array containing pixel coverages for (32) rows (packed)
// if encoding=raw, it contains alpha coverage values (val) as integer
// if encoding=RLE, it contains tuples (val, last x-coordinate exclusive)
// use rowAAx0/rowAAx1 to get row indices within this chunk
final OffHeapArray rowAAChunk;
// current position in rowAAChunk array
long rowAAChunkPos;
// touchedTile[i] is the sum of all the alphas in the tile with
// x=j*TILE_SIZE+bboxX0.
int[] touchedTile;
// per-thread renderer context
final RendererContext rdrCtx;
// large cached touchedTile (dirty)
final int[] touchedTile_initial = new int[INITIAL_ARRAY]; // 1 tile line
int tileMin, tileMax;
boolean useRLE = false;
MarlinCache(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
rowAAChunk = new OffHeapArray(rdrCtx, INITIAL_CHUNK_ARRAY);
touchedTile = touchedTile_initial;
// tile used marks:
tileMin = Integer.MAX_VALUE;
tileMax = Integer.MIN_VALUE;
}
void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY)
{
// assert maxy >= miny && maxx >= minx;
bboxX0 = minx;
bboxY0 = miny;
bboxX1 = maxx;
bboxY1 = maxy;
final int width = (maxx - minx);
if (FORCE_NO_RLE) {
useRLE = false;
} else if (FORCE_RLE) {
useRLE = true;
} else {
// heuristics: use both bbox area and complexity
// ie number of primitives:
// fast check min and max width (maxx < 23bits):
if (width <= RLE_MIN_WIDTH || width >= RLE_MAX_WIDTH) {
useRLE = false;
} else {
// perimeter approach: how fit the total length into given height:
// if stroking: meanCrossings /= 2 => divide edgeSumDeltaY by 2
final int heightSubPixel
= (((maxy - miny) << SUBPIXEL_LG_POSITIONS_Y) << rdrCtx.stroking);
// check meanDist > block size:
// check width / (meanCrossings - 1) >= RLE_THRESHOLD
// fast case: (meanCrossingPerPixel <= 2) means 1 span only
useRLE = (edgeSumDeltaY <= (heightSubPixel << 1))
// note: already checked (meanCrossingPerPixel <= 2)
// rewritten to avoid division:
|| (width * heightSubPixel) >
((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG);
// ((edgeSumDeltaY - heightSubPixel) * RLE_THRESHOLD);
// ((edgeSumDeltaY - heightSubPixel) << BLOCK_TH_LG);
if (doTrace && !useRLE) {
final float meanCrossings
= ((float) edgeSumDeltaY) / heightSubPixel;
final float meanDist = width / (meanCrossings - 1);
System.out.println("High complexity: "
+ " for bbox[width = " + width
+ " height = " + (maxy - miny)
+ "] edgeSumDeltaY = " + edgeSumDeltaY
+ " heightSubPixel = " + heightSubPixel
+ " meanCrossings = "+ meanCrossings
+ " meanDist = " + meanDist
+ " width = " + (width * heightSubPixel)
+ " <= criteria: " + ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG)
);
}
}
}
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG;
if (nxTiles > INITIAL_ARRAY) {
if (doStats) {
RendererContext.stats.stat_array_marlincache_touchedTile
.add(nxTiles);
}
touchedTile = rdrCtx.getIntArray(nxTiles);
}
}
/**
* Disposes this cache:
* clean up before reusing this instance
*/
void dispose() {
// Reset touchedTile if needed:
resetTileLine(0);
// Return arrays:
if (touchedTile != touchedTile_initial) {
rdrCtx.putIntArray(touchedTile, 0, 0); // already zero filled
touchedTile = touchedTile_initial;
}
// At last: resize back off-heap rowAA to initial size
if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) {
// note: may throw OOME:
rowAAChunk.resize(INITIAL_CHUNK_ARRAY);
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
rowAAChunk.fill(BYTE_0);
}
}
void resetTileLine(final int pminY) {
// update bboxY0 to process a complete tile line [0 - 32]
bboxY0 = pminY;
// reset current pos
if (doStats) {
RendererContext.stats.stat_cache_rowAAChunk.add(rowAAChunkPos);
}
rowAAChunkPos = 0L;
// Reset touchedTile:
if (tileMin != Integer.MAX_VALUE) {
if (doStats) {
RendererContext.stats.stat_cache_tiles.add(tileMax - tileMin);
}
// clean only dirty touchedTile:
if (tileMax == 1) {
touchedTile[0] = 0;
} else {
IntArrayCache.fill(touchedTile, tileMin, tileMax, 0);
}
// reset tile used marks:
tileMin = Integer.MAX_VALUE;
tileMax = Integer.MIN_VALUE;
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
rowAAChunk.fill(BYTE_0);
}
}
void clearAARow(final int y) {
// process tile line [0 - 32]
final int row = y - bboxY0;
// update pixel range:
rowAAx0[row] = 0; // first pixel inclusive
rowAAx1[row] = 0; // last pixel exclusive
rowAAEnc[row] = 0; // raw encoding
// note: leave rowAAChunkIndex[row] undefined
// and rowAALen[row] & rowAAPos[row] (RLE)
}
/**
* Copy the given alpha data into the rowAA cache
* @param alphaRow alpha data to copy from
* @param y y pixel coordinate
* @param px0 first pixel inclusive x0
* @param px1 last pixel exclusive x1
*/
void copyAARowNoRLE(final int[] alphaRow, final int y,
final int px0, final int px1)
{
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.start();
}
// skip useless pixels above boundary
final int px_bbox1 = FloatMath.min(px1, bboxX1);
if (doLogBounds) {
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
+ " (" + px1 + ") [ for y=" + y);
}
final int row = y - bboxY0;
// update pixel range:
rowAAx0[row] = px0; // first pixel inclusive
rowAAx1[row] = px_bbox1; // last pixel exclusive
rowAAEnc[row] = 0; // raw encoding
// get current position (bytes):
final long pos = rowAAChunkPos;
// update row index to current position:
rowAAChunkIndex[row] = pos;
// determine need array size (may overflow):
final long needSize = pos + (px_bbox1 - px0);
// update next position (bytes):
rowAAChunkPos = needSize;
// update row data:
final OffHeapArray _rowAAChunk = rowAAChunk;
// ensure rowAAChunk capacity:
if (_rowAAChunk.length < needSize) {
expandRowAAChunk(needSize);
}
if (doStats) {
RendererContext.stats.stat_cache_rowAA.add(px_bbox1 - px0);
}
// rowAA contains only alpha values for range[x0; x1[
final int[] _touchedTile = touchedTile;
final int _TILE_SIZE_LG = TILE_SIZE_LG;
final int from = px0 - bboxX0; // first pixel inclusive
final int to = px_bbox1 - bboxX0; // last pixel exclusive
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_BYTE = 1L;
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
long addr_off = _rowAAChunk.address + pos;
// compute alpha sum into rowAA:
for (int x = from, val = 0; x < to; x++) {
// alphaRow is in [0; MAX_COVERAGE]
val += alphaRow[x]; // [from; to[
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
System.out.println("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
System.out.println("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
// store alpha sum (as byte):
if (val == 0) {
_unsafe.putByte(addr_off, (byte)0); // [0..255]
} else {
_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255]
// update touchedTile
_touchedTile[x >> _TILE_SIZE_LG] += val;
}
addr_off += SIZE_BYTE;
}
// update tile used marks:
int tx = from >> _TILE_SIZE_LG; // inclusive
if (tx < tileMin) {
tileMin = tx;
}
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
if (tx > tileMax) {
tileMax = tx;
}
if (doLogBounds) {
MarlinUtils.logInfo("clear = [" + from + " ... " + to + "[");
}
// Clear alpha row for reuse:
IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0);
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.stop();
}
}
void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,
final int y, final int px0, final int px1)
{
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.start();
}
// Copy rowAA data into the piscesCache if one is present
final int _bboxX0 = bboxX0;
// process tile line [0 - 32]
final int row = y - bboxY0;
final int from = px0 - _bboxX0; // first pixel inclusive
// skip useless pixels above boundary
final int px_bbox1 = FloatMath.min(px1, bboxX1);
final int to = px_bbox1 - _bboxX0; // last pixel exclusive
if (doLogBounds) {
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
+ " (" + px1 + ") [ for y=" + y);
}
// get current position:
final long initialPos = startRLERow(row, px0, px_bbox1);
// determine need array size:
// pessimistic: max needed size = deltaX x 4 (1 int)
final int maxLen = (to - from);
final long needSize = initialPos + (maxLen << 2);
// update row data:
OffHeapArray _rowAAChunk = rowAAChunk;
// ensure rowAAChunk capacity:
if (_rowAAChunk.length < needSize) {
expandRowAAChunk(needSize);
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_INT = 4L;
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
long addr_off = _rowAAChunk.address + initialPos;
final int[] _touchedTile = touchedTile;
final int _TILE_SIZE_LG = TILE_SIZE_LG;
final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
// traverse flagged blocks:
final int blkW = (from >> _BLK_SIZE_LG);
final int blkE = (to >> _BLK_SIZE_LG) + 1;
// Perform run-length encoding and store results in the piscesCache
int val = 0;
int cx0 = from;
int runLen;
final int _MAX_VALUE = Integer.MAX_VALUE;
int last_t0 = _MAX_VALUE;
int skip = 0;
for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
if (blkFlags[t] != 0) {
blkFlags[t] = 0;
if (last_t0 == _MAX_VALUE) {
last_t0 = t;
}
continue;
}
if (last_t0 != _MAX_VALUE) {
// emit blocks:
blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
last_t0 = _MAX_VALUE;
// (last block pixel+1) inclusive => +1
blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to);
for (cx = blk_x0; cx < blk_x1; cx++) {
if ((delta = alphaRow[cx]) != 0) {
alphaRow[cx] = 0;
// not first rle entry:
if (cx != cx0) {
runLen = cx - cx0;
// store alpha coverage (ensure within bounds):
// as [absX|val] where:
// absX is the absolute x-coordinate:
// note: last pixel exclusive (>= 0)
// note: it should check X is smaller than 23bits (overflow)!
// special case to encode entries into a single int:
if (val == 0) {
_unsafe.putInt(addr_off,
((_bboxX0 + cx) << 8)
);
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + cx) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
);
if (runLen == 1) {
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
} else {
touchTile(cx0, val, cx, runLen, _touchedTile);
}
}
addr_off += SIZE_INT;
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding_runLen
.add(runLen);
}
cx0 = cx;
}
// alpha value = running sum of coverage delta:
val += delta;
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
System.out.println("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
System.out.println("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
}
}
} else if (doStats) {
skip++;
}
}
// Process remaining RLE run:
runLen = to - cx0;
// store alpha coverage (ensure within bounds):
// as (int)[absX|val] where:
// absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7
// note: last pixel exclusive (>= 0)
// note: it should check X is smaller than 23bits (overflow)!
// special case to encode entries into a single int:
if (val == 0) {
_unsafe.putInt(addr_off,
((_bboxX0 + to) << 8)
);
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + to) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
);
if (runLen == 1) {
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
} else {
touchTile(cx0, val, to, runLen, _touchedTile);
}
}
addr_off += SIZE_INT;
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding_runLen
.add(runLen);
}
long len = (addr_off - _rowAAChunk.address);
// update coded length as bytes:
rowAALen[row] = (len - initialPos);
// update current position:
rowAAChunkPos = len;
if (doStats) {
RendererContext.stats.stat_cache_rowAA.add(rowAALen[row]);
RendererContext.stats.hist_tile_generator_encoding_ratio.add(
(100 * skip) / (blkE - blkW)
);
}
// update tile used marks:
int tx = from >> _TILE_SIZE_LG; // inclusive
if (tx < tileMin) {
tileMin = tx;
}
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
if (tx > tileMax) {
tileMax = tx;
}
// Clear alpha row for reuse:
if (px1 > bboxX1) {
alphaRow[to ] = 0;
alphaRow[to + 1] = 0;
}
if (doChecks) {
IntArrayCache.check(blkFlags, 0, blkFlags.length, 0);
IntArrayCache.check(alphaRow, 0, alphaRow.length, 0);
}
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.stop();
}
}
long startRLERow(final int row, final int x0, final int x1) {
// rows are supposed to be added by increasing y.
rowAAx0[row] = x0; // first pixel inclusive
rowAAx1[row] = x1; // last pixel exclusive
rowAAEnc[row] = 1; // RLE encoding
rowAAPos[row] = 0L; // position = 0
// update row index to current position:
return (rowAAChunkIndex[row] = rowAAChunkPos);
}
private void expandRowAAChunk(final long needSize) {
if (doStats) {
RendererContext.stats.stat_array_marlincache_rowAAChunk
.add(needSize);
}
// note: throw IOOB if neededSize > 2Gb:
final long newSize = ArrayCache.getNewLargeSize(rowAAChunk.length, needSize);
rowAAChunk.resize(newSize);
}
private void touchTile(final int x0, final int val, final int x1,
final int runLen,
final int[] _touchedTile)
{
// the x and y of the current row, minus bboxX0, bboxY0
// process tile line [0 - 32]
final int _TILE_SIZE_LG = TILE_SIZE_LG;
// update touchedTile
int tx = (x0 >> _TILE_SIZE_LG);
// handle trivial case: same tile (x0, x0+runLen)
if (tx == (x1 >> _TILE_SIZE_LG)) {
// same tile:
_touchedTile[tx] += val * runLen;
return;
}
final int tx1 = (x1 - 1) >> _TILE_SIZE_LG;
if (tx <= tx1) {
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
_touchedTile[tx++] += val * (nextTileXCoord - x0);
}
if (tx < tx1) {
// don't go all the way to tx1 - we need to handle the last
// tile as a special case (just like we did with the first
final int tileVal = (val << _TILE_SIZE_LG);
for (; tx < tx1; tx++) {
_touchedTile[tx] += tileVal;
}
}
// they will be equal unless x0 >> TILE_SIZE_LG == tx1
if (tx == tx1) {
final int txXCoord = tx << _TILE_SIZE_LG;
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1;
_touchedTile[tx] += val * (lastXCoord - txXCoord);
}
}
int alphaSumInTile(final int x) {
return touchedTile[(x - bboxX0) >> TILE_SIZE_LG];
}
@Override
public String toString() {
return "bbox = ["
+ bboxX0 + ", " + bboxY0 + " => "
+ bboxX1 + ", " + bboxY1 + "]\n";
}
private static byte[] buildAlphaMap(final int maxalpha) {
// double size !
final byte[] alMap = new byte[maxalpha << 1];
final int halfmaxalpha = maxalpha >> 2;
for (int i = 0; i <= maxalpha; i++) {
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
// System.out.println("alphaMap[" + i + "] = "
// + Byte.toUnsignedInt(alMap[i]));
}
return alMap;
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
/**
* Marlin constant holder using System properties
*/
interface MarlinConst {
// enable Logs (logger or stdout)
static final boolean enableLogs = false;
// enable Logger
static final boolean useLogger = enableLogs && MarlinProperties.isUseLogger();
// log new RendererContext
static final boolean logCreateContext = enableLogs
&& MarlinProperties.isLogCreateContext();
// log misc.Unsafe alloc/realloc/free
static final boolean logUnsafeMalloc = enableLogs
&& MarlinProperties.isLogUnsafeMalloc();
// do statistics
static final boolean doStats = enableLogs && MarlinProperties.isDoStats();
// do monitors
// disabled to reduce byte-code size a bit...
static final boolean doMonitors = enableLogs && false; // MarlinProperties.isDoMonitors();
// do checks
static final boolean doChecks = false; // MarlinProperties.isDoChecks();
// do AA range checks: disable when algorithm / code is stable
static final boolean DO_AA_RANGE_CHECK = false;
// enable logs
static final boolean doLogWidenArray = enableLogs && false;
// enable oversize logs
static final boolean doLogOverSize = enableLogs && false;
// enable traces
static final boolean doTrace = enableLogs && false;
// do flush monitors
static final boolean doFlushMonitors = true;
// use one polling thread to dump statistics/monitors
static final boolean useDumpThread = false;
// thread dump interval (ms)
static final long statDump = 5000L;
// do clean dirty array
static final boolean doCleanDirty = false;
// flag to use line simplifier
static final boolean useSimplifier = MarlinProperties.isUseSimplifier();
// flag to enable logs related bounds checks
static final boolean doLogBounds = enableLogs && false;
// Initial Array sizing (initial context capacity) ~ 512K
// 2048 pixel (width x height) for initial capacity
static final int INITIAL_PIXEL_DIM
= MarlinProperties.getInitialImageSize();
// typical array sizes: only odd numbers allowed below
static final int INITIAL_ARRAY = 256;
static final int INITIAL_SMALL_ARRAY = 1024;
static final int INITIAL_MEDIUM_ARRAY = 4096;
static final int INITIAL_LARGE_ARRAY = 8192;
static final int INITIAL_ARRAY_16K = 16384;
static final int INITIAL_ARRAY_32K = 32768;
// alpha row dimension
static final int INITIAL_AA_ARRAY = INITIAL_PIXEL_DIM;
// initial edges (24 bytes) = 24K [ints] = 96K
static final int INITIAL_EDGES_CAPACITY = 4096 * 24; // 6 ints per edges
// zero value as byte
static final byte BYTE_0 = (byte) 0;
// subpixels expressed as log2
public static final int SUBPIXEL_LG_POSITIONS_X
= MarlinProperties.getSubPixel_Log2_X();
public static final int SUBPIXEL_LG_POSITIONS_Y
= MarlinProperties.getSubPixel_Log2_Y();
// number of subpixels
public static final int SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
public static final int SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
public static final float NORM_SUBPIXELS
= (float)Math.sqrt(( SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_X
+ SUBPIXEL_POSITIONS_Y * SUBPIXEL_POSITIONS_Y)/2.0);
public static final int MAX_AA_ALPHA
= SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y;
public static final int TILE_SIZE_LG = MarlinProperties.getTileSize_Log2();
public static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32 by default
public static final int BLOCK_SIZE_LG = MarlinProperties.getBlockSize_Log2();
public static final int BLOCK_SIZE = 1 << BLOCK_SIZE_LG;
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.security.AccessController;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.security.action.GetPropertyAction;
public final class MarlinProperties {
private MarlinProperties() {
// no-op
}
// marlin system properties
public static boolean isUseThreadLocal() {
return getBoolean("sun.java2d.renderer.useThreadLocal", "true");
}
/**
* Return the initial pixel size used to define initial arrays
* (tile AA chunk, alpha line, buckets)
*
* @return 64 < initial pixel size < 32768 (2048 by default)
*/
public static int getInitialImageSize() {
return getInteger("sun.java2d.renderer.pixelsize", 2048, 64, 32 * 1024);
}
/**
* Return the log(2) corresponding to subpixel on x-axis (
*
* @return 1 (2 subpixels) < initial pixel size < 4 (256 subpixels)
* (3 by default ie 8 subpixels)
*/
public static int getSubPixel_Log2_X() {
return getInteger("sun.java2d.renderer.subPixel_log2_X", 3, 1, 8);
}
/**
* Return the log(2) corresponding to subpixel on y-axis (
*
* @return 1 (2 subpixels) < initial pixel size < 8 (256 subpixels)
* (3 by default ie 8 subpixels)
*/
public static int getSubPixel_Log2_Y() {
return getInteger("sun.java2d.renderer.subPixel_log2_Y", 3, 1, 8);
}
/**
* Return the log(2) corresponding to the square tile size in pixels
*
* @return 3 (8x8 pixels) < tile size < 8 (256x256 pixels)
* (5 by default ie 32x32 pixels)
*/
public static int getTileSize_Log2() {
return getInteger("sun.java2d.renderer.tileSize_log2", 5, 3, 8);
}
/**
* Return the log(2) corresponding to the block size in pixels
*
* @return 3 (8 pixels) < block size < 8 (256 pixels)
* (5 by default ie 32 pixels)
*/
public static int getBlockSize_Log2() {
return getInteger("sun.java2d.renderer.blockSize_log2", 5, 3, 8);
}
// RLE / blockFlags settings
public static boolean isForceRLE() {
return getBoolean("sun.java2d.renderer.forceRLE", "false");
}
public static boolean isForceNoRLE() {
return getBoolean("sun.java2d.renderer.forceNoRLE", "false");
}
public static boolean isUseTileFlags() {
return getBoolean("sun.java2d.renderer.useTileFlags", "true");
}
public static boolean isUseTileFlagsWithHeuristics() {
return isUseTileFlags()
&& getBoolean("sun.java2d.renderer.useTileFlags.useHeuristics", "true");
}
public static int getRLEMinWidth() {
return getInteger("sun.java2d.renderer.rleMinWidth", 64, 0, Integer.MAX_VALUE);
}
// optimisation parameters
public static boolean isUseSimplifier() {
return getBoolean("sun.java2d.renderer.useSimplifier", "false");
}
// debugging parameters
public static boolean isDoStats() {
return getBoolean("sun.java2d.renderer.doStats", "false");
}
public static boolean isDoMonitors() {
return getBoolean("sun.java2d.renderer.doMonitors", "false");
}
public static boolean isDoChecks() {
return getBoolean("sun.java2d.renderer.doChecks", "false");
}
// logging parameters
public static boolean isUseLogger() {
return getBoolean("sun.java2d.renderer.useLogger", "false");
}
public static boolean isLogCreateContext() {
return getBoolean("sun.java2d.renderer.logCreateContext", "false");
}
public static boolean isLogUnsafeMalloc() {
return getBoolean("sun.java2d.renderer.logUnsafeMalloc", "false");
}
// system property utilities
static boolean getBoolean(final String key, final String def) {
return Boolean.valueOf(AccessController.doPrivileged(
new GetPropertyAction(key, def)));
}
static int getInteger(final String key, final int def,
final int min, final int max)
{
final String property = AccessController.doPrivileged(
new GetPropertyAction(key));
int value = def;
if (property != null) {
try {
value = Integer.decode(property);
} catch (NumberFormatException e) {
logInfo("Invalid integer value for " + key + " = " + property);
}
}
// check for invalid values
if ((value < min) || (value > max)) {
logInfo("Invalid value for " + key + " = " + value
+ "; expected value in range[" + min + ", " + max + "] !");
value = def;
}
return value;
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.awt.BasicStroke;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.lang.ref.Reference;
import java.security.AccessController;
import java.util.concurrent.ConcurrentLinkedQueue;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.pipe.AATileGenerator;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderingEngine;
import sun.security.action.GetPropertyAction;
/**
* Marlin RendererEngine implementation (derived from Pisces)
*/
public class MarlinRenderingEngine extends RenderingEngine
implements MarlinConst
{
private static enum NormMode {ON_WITH_AA, ON_NO_AA, OFF}
private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
/**
* Public constructor
*/
public MarlinRenderingEngine() {
super();
logSettings(MarlinRenderingEngine.class.getName());
}
/**
* Create a widened path as specified by the parameters.
* <p>
* The specified {@code src} {@link Shape} is widened according
* to the specified attribute parameters as per the
* {@link BasicStroke} specification.
*
* @param src the source path to be widened
* @param width the width of the widened path as per {@code BasicStroke}
* @param caps the end cap decorations as per {@code BasicStroke}
* @param join the segment join decorations as per {@code BasicStroke}
* @param miterlimit the miter limit as per {@code BasicStroke}
* @param dashes the dash length array as per {@code BasicStroke}
* @param dashphase the initial dash phase as per {@code BasicStroke}
* @return the widened path stored in a new {@code Shape} object
* @since 1.7
*/
@Override
public Shape createStrokedShape(Shape src,
float width,
int caps,
int join,
float miterlimit,
float dashes[],
float dashphase)
{
final RendererContext rdrCtx = getRendererContext();
try {
// initialize a large copyable Path2D to avoid a lot of array growing:
final Path2D.Float p2d =
(rdrCtx.p2d == null) ?
(rdrCtx.p2d = new Path2D.Float(Path2D.WIND_NON_ZERO,
INITIAL_MEDIUM_ARRAY))
: rdrCtx.p2d;
// reset
p2d.reset();
strokeTo(rdrCtx,
src,
null,
width,
NormMode.OFF,
caps,
join,
miterlimit,
dashes,
dashphase,
rdrCtx.transformerPC2D.wrapPath2d(p2d)
);
// Use Path2D copy constructor (trim)
return new Path2D.Float(p2d);
} finally {
// recycle the RendererContext instance
returnRendererContext(rdrCtx);
}
}
/**
* Sends the geometry for a widened path as specified by the parameters
* to the specified consumer.
* <p>
* The specified {@code src} {@link Shape} is widened according
* to the parameters specified by the {@link BasicStroke} object.
* Adjustments are made to the path as appropriate for the
* {@link VALUE_STROKE_NORMALIZE} hint if the {@code normalize}
* boolean parameter is true.
* Adjustments are made to the path as appropriate for the
* {@link VALUE_ANTIALIAS_ON} hint if the {@code antialias}
* boolean parameter is true.
* <p>
* The geometry of the widened path is forwarded to the indicated
* {@link PathConsumer2D} object as it is calculated.
*
* @param src the source path to be widened
* @param bs the {@code BasicSroke} object specifying the
* decorations to be applied to the widened path
* @param normalize indicates whether stroke normalization should
* be applied
* @param antialias indicates whether or not adjustments appropriate
* to antialiased rendering should be applied
* @param consumer the {@code PathConsumer2D} instance to forward
* the widened geometry to
* @since 1.7
*/
@Override
public void strokeTo(Shape src,
AffineTransform at,
BasicStroke bs,
boolean thin,
boolean normalize,
boolean antialias,
final PathConsumer2D consumer)
{
final NormMode norm = (normalize) ?
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
: NormMode.OFF;
final RendererContext rdrCtx = getRendererContext();
try {
strokeTo(rdrCtx, src, at, bs, thin, norm, antialias, consumer);
} finally {
// recycle the RendererContext instance
returnRendererContext(rdrCtx);
}
}
final void strokeTo(final RendererContext rdrCtx,
Shape src,
AffineTransform at,
BasicStroke bs,
boolean thin,
NormMode normalize,
boolean antialias,
PathConsumer2D pc2d)
{
float lw;
if (thin) {
if (antialias) {
lw = userSpaceLineWidth(at, MIN_PEN_SIZE);
} else {
lw = userSpaceLineWidth(at, 1.0f);
}
} else {
lw = bs.getLineWidth();
}
strokeTo(rdrCtx,
src,
at,
lw,
normalize,
bs.getEndCap(),
bs.getLineJoin(),
bs.getMiterLimit(),
bs.getDashArray(),
bs.getDashPhase(),
pc2d);
}
private final float userSpaceLineWidth(AffineTransform at, float lw) {
float widthScale;
if (at == null) {
widthScale = 1.0f;
} else if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM |
AffineTransform.TYPE_GENERAL_SCALE)) != 0) {
widthScale = (float)Math.sqrt(at.getDeterminant());
} else {
// First calculate the "maximum scale" of this transform.
double A = at.getScaleX(); // m00
double C = at.getShearX(); // m01
double B = at.getShearY(); // m10
double D = at.getScaleY(); // m11
/*
* Given a 2 x 2 affine matrix [ A B ] such that
* [ C D ]
* v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
* find the maximum magnitude (norm) of the vector v'
* with the constraint (x^2 + y^2 = 1).
* The equation to maximize is
* |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
* or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
* Since sqrt is monotonic we can maximize |v'|^2
* instead and plug in the substitution y = sqrt(1 - x^2).
* Trigonometric equalities can then be used to get
* rid of most of the sqrt terms.
*/
double EA = A*A + B*B; // x^2 coefficient
double EB = 2.0*(A*C + B*D); // xy coefficient
double EC = C*C + D*D; // y^2 coefficient
/*
* There is a lot of calculus omitted here.
*
* Conceptually, in the interests of understanding the
* terms that the calculus produced we can consider
* that EA and EC end up providing the lengths along
* the major axes and the hypot term ends up being an
* adjustment for the additional length along the off-axis
* angle of rotated or sheared ellipses as well as an
* adjustment for the fact that the equation below
* averages the two major axis lengths. (Notice that
* the hypot term contains a part which resolves to the
* difference of these two axis lengths in the absence
* of rotation.)
*
* In the calculus, the ratio of the EB and (EA-EC) terms
* ends up being the tangent of 2*theta where theta is
* the angle that the long axis of the ellipse makes
* with the horizontal axis. Thus, this equation is
* calculating the length of the hypotenuse of a triangle
* along that axis.
*/
double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
// sqrt omitted, compare to squared limits below.
double widthsquared = ((EA + EC + hypot)/2.0);
widthScale = (float)Math.sqrt(widthsquared);
}
return (lw / widthScale);
}
final void strokeTo(final RendererContext rdrCtx,
Shape src,
AffineTransform at,
float width,
NormMode normalize,
int caps,
int join,
float miterlimit,
float dashes[],
float dashphase,
PathConsumer2D pc2d)
{
// We use strokerat and outat so that in Stroker and Dasher we can work only
// with the pre-transformation coordinates. This will repeat a lot of
// computations done in the path iterator, but the alternative is to
// work with transformed paths and compute untransformed coordinates
// as needed. This would be faster but I do not think the complexity
// of working with both untransformed and transformed coordinates in
// the same code is worth it.
// However, if a path's width is constant after a transformation,
// we can skip all this untransforming.
// If normalization is off we save some transformations by not
// transforming the input to pisces. Instead, we apply the
// transformation after the path processing has been done.
// We can't do this if normalization is on, because it isn't a good
// idea to normalize before the transformation is applied.
AffineTransform strokerat = null;
AffineTransform outat = null;
PathIterator pi;
int dashLen = -1;
boolean recycleDashes = false;
if (at != null && !at.isIdentity()) {
final double a = at.getScaleX();
final double b = at.getShearX();
final double c = at.getShearY();
final double d = at.getScaleY();
final double det = a * d - c * b;
if (Math.abs(det) <= (2f * Float.MIN_VALUE)) {
// this rendering engine takes one dimensional curves and turns
// them into 2D shapes by giving them width.
// However, if everything is to be passed through a singular
// transformation, these 2D shapes will be squashed down to 1D
// again so, nothing can be drawn.
// Every path needs an initial moveTo and a pathDone. If these
// are not there this causes a SIGSEGV in libawt.so (at the time
// of writing of this comment (September 16, 2010)). Actually,
// I am not sure if the moveTo is necessary to avoid the SIGSEGV
// but the pathDone is definitely needed.
pc2d.moveTo(0f, 0f);
pc2d.pathDone();
return;
}
// If the transform is a constant multiple of an orthogonal transformation
// then every length is just multiplied by a constant, so we just
// need to transform input paths to stroker and tell stroker
// the scaled width. This condition is satisfied if
// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
// leave a bit of room for error.
if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
final float scale = (float) Math.sqrt(a*a + c*c);
if (dashes != null) {
recycleDashes = true;
dashLen = dashes.length;
final float[] newDashes;
if (dashLen <= INITIAL_ARRAY) {
newDashes = rdrCtx.dasher.dashes_initial;
} else {
if (doStats) {
RendererContext.stats.stat_array_dasher_dasher
.add(dashLen);
}
newDashes = rdrCtx.getDirtyFloatArray(dashLen);
}
System.arraycopy(dashes, 0, newDashes, 0, dashLen);
dashes = newDashes;
for (int i = 0; i < dashLen; i++) {
dashes[i] = scale * dashes[i];
}
dashphase = scale * dashphase;
}
width = scale * width;
pi = getNormalizingPathIterator(rdrCtx, normalize,
src.getPathIterator(at));
// by now strokerat == null && outat == null. Input paths to
// stroker (and maybe dasher) will have the full transform at
// applied to them and nothing will happen to the output paths.
} else {
if (normalize != NormMode.OFF) {
strokerat = at;
pi = getNormalizingPathIterator(rdrCtx, normalize,
src.getPathIterator(at));
// by now strokerat == at && outat == null. Input paths to
// stroker (and maybe dasher) will have the full transform at
// applied to them, then they will be normalized, and then
// the inverse of *only the non translation part of at* will
// be applied to the normalized paths. This won't cause problems
// in stroker, because, suppose at = T*A, where T is just the
// translation part of at, and A is the rest. T*A has already
// been applied to Stroker/Dasher's input. Then Ainv will be
// applied. Ainv*T*A is not equal to T, but it is a translation,
// which means that none of stroker's assumptions about its
// input will be violated. After all this, A will be applied
// to stroker's output.
} else {
outat = at;
pi = src.getPathIterator(null);
// outat == at && strokerat == null. This is because if no
// normalization is done, we can just apply all our
// transformations to stroker's output.
}
}
} else {
// either at is null or it's the identity. In either case
// we don't transform the path.
pi = getNormalizingPathIterator(rdrCtx, normalize,
src.getPathIterator(null));
}
if (useSimplifier) {
// Use simplifier after stroker before Renderer
// to remove collinear segments (notably due to cap square)
pc2d = rdrCtx.simplifier.init(pc2d);
}
// by now, at least one of outat and strokerat will be null. Unless at is not
// a constant multiple of an orthogonal transformation, they will both be
// null. In other cases, outat == at if normalization is off, and if
// normalization is on, strokerat == at.
final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
pc2d = transformerPC2D.transformConsumer(pc2d, outat);
pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
if (dashes != null) {
if (!recycleDashes) {
dashLen = dashes.length;
}
pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
recycleDashes);
}
pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
pathTo(rdrCtx, pi, pc2d);
/*
* Pipeline seems to be:
* shape.getPathIterator
* -> NormalizingPathIterator
* -> inverseDeltaTransformConsumer
* -> Dasher
* -> Stroker
* -> deltaTransformConsumer OR transformConsumer
*
* -> CollinearSimplifier to remove redundant segments
*
* -> pc2d = Renderer (bounding box)
*/
}
private static boolean nearZero(final double num) {
return Math.abs(num) < 2.0 * Math.ulp(num);
}
PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
final NormMode mode,
final PathIterator src)
{
switch (mode) {
case ON_WITH_AA:
// NormalizingPathIterator NearestPixelCenter:
return rdrCtx.nPCPathIterator.init(src);
case ON_NO_AA:
// NearestPixel NormalizingPathIterator:
return rdrCtx.nPQPathIterator.init(src);
case OFF:
// return original path iterator if normalization is disabled:
return src;
default:
throw new InternalError("Unrecognized normalization mode");
}
}
abstract static class NormalizingPathIterator implements PathIterator {
private PathIterator src;
// the adjustment applied to the current position.
private float curx_adjust, cury_adjust;
// the adjustment applied to the last moveTo position.
private float movx_adjust, movy_adjust;
private final float[] tmp;
NormalizingPathIterator(final float[] tmp) {
this.tmp = tmp;
}
final NormalizingPathIterator init(final PathIterator src) {
this.src = src;
return this; // fluent API
}
/**
* Disposes this path iterator:
* clean up before reusing this instance
*/
final void dispose() {
// free source PathIterator:
this.src = null;
}
@Override
public final int currentSegment(final float[] coords) {
if (doMonitors) {
RendererContext.stats.mon_npi_currentSegment.start();
}
int lastCoord;
final int type = src.currentSegment(coords);
switch(type) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
lastCoord = 0;
break;
case PathIterator.SEG_QUADTO:
lastCoord = 2;
break;
case PathIterator.SEG_CUBICTO:
lastCoord = 4;
break;
case PathIterator.SEG_CLOSE:
// we don't want to deal with this case later. We just exit now
curx_adjust = movx_adjust;
cury_adjust = movy_adjust;
if (doMonitors) {
RendererContext.stats.mon_npi_currentSegment.stop();
}
return type;
default:
throw new InternalError("Unrecognized curve type");
}
// TODO: handle NaN, Inf and overflow
// normalize endpoint
float coord, x_adjust, y_adjust;
coord = coords[lastCoord];
x_adjust = normCoord(coord); // new coord
coords[lastCoord] = x_adjust;
x_adjust -= coord;
coord = coords[lastCoord + 1];
y_adjust = normCoord(coord); // new coord
coords[lastCoord + 1] = y_adjust;
y_adjust -= coord;
// now that the end points are done, normalize the control points
switch(type) {
case PathIterator.SEG_MOVETO:
movx_adjust = x_adjust;
movy_adjust = y_adjust;
break;
case PathIterator.SEG_LINETO:
break;
case PathIterator.SEG_QUADTO:
coords[0] += (curx_adjust + x_adjust) / 2f;
coords[1] += (cury_adjust + y_adjust) / 2f;
break;
case PathIterator.SEG_CUBICTO:
coords[0] += curx_adjust;
coords[1] += cury_adjust;
coords[2] += x_adjust;
coords[3] += y_adjust;
break;
case PathIterator.SEG_CLOSE:
// handled earlier
default:
}
curx_adjust = x_adjust;
cury_adjust = y_adjust;
if (doMonitors) {
RendererContext.stats.mon_npi_currentSegment.stop();
}
return type;
}
abstract float normCoord(final float coord);
@Override
public final int currentSegment(final double[] coords) {
final float[] _tmp = tmp; // dirty
int type = this.currentSegment(_tmp);
for (int i = 0; i < 6; i++) {
coords[i] = _tmp[i];
}
return type;
}
@Override
public final int getWindingRule() {
return src.getWindingRule();
}
@Override
public final boolean isDone() {
if (src.isDone()) {
// Dispose this instance:
dispose();
return true;
}
return false;
}
@Override
public final void next() {
src.next();
}
static final class NearestPixelCenter
extends NormalizingPathIterator
{
NearestPixelCenter(final float[] tmp) {
super(tmp);
}
@Override
float normCoord(final float coord) {
// round to nearest pixel center
return FloatMath.floor_f(coord) + 0.5f;
}
}
static final class NearestPixelQuarter
extends NormalizingPathIterator
{
NearestPixelQuarter(final float[] tmp) {
super(tmp);
}
@Override
float normCoord(final float coord) {
// round to nearest (0.25, 0.25) pixel quarter
return FloatMath.floor_f(coord + 0.25f) + 0.25f;
}
}
}
private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
final PathConsumer2D pc2d)
{
// mark context as DIRTY:
rdrCtx.dirty = true;
final float[] coords = rdrCtx.float6;
pathToLoop(coords, pi, pc2d);
// mark context as CLEAN:
rdrCtx.dirty = false;
}
private static void pathToLoop(final float[] coords, final PathIterator pi,
final PathConsumer2D pc2d)
{
for (; !pi.isDone(); pi.next()) {
switch (pi.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
pc2d.moveTo(coords[0], coords[1]);
continue;
case PathIterator.SEG_LINETO:
pc2d.lineTo(coords[0], coords[1]);
continue;
case PathIterator.SEG_QUADTO:
pc2d.quadTo(coords[0], coords[1],
coords[2], coords[3]);
continue;
case PathIterator.SEG_CUBICTO:
pc2d.curveTo(coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
continue;
case PathIterator.SEG_CLOSE:
pc2d.closePath();
continue;
default:
}
}
pc2d.pathDone();
}
/**
* Construct an antialiased tile generator for the given shape with
* the given rendering attributes and store the bounds of the tile
* iteration in the bbox parameter.
* The {@code at} parameter specifies a transform that should affect
* both the shape and the {@code BasicStroke} attributes.
* The {@code clip} parameter specifies the current clip in effect
* in device coordinates and can be used to prune the data for the
* operation, but the renderer is not required to perform any
* clipping.
* If the {@code BasicStroke} parameter is null then the shape
* should be filled as is, otherwise the attributes of the
* {@code BasicStroke} should be used to specify a draw operation.
* The {@code thin} parameter indicates whether or not the
* transformed {@code BasicStroke} represents coordinates smaller
* than the minimum resolution of the antialiasing rasterizer as
* specified by the {@code getMinimumAAPenWidth()} method.
* <p>
* Upon returning, this method will fill the {@code bbox} parameter
* with 4 values indicating the bounds of the iteration of the
* tile generator.
* The iteration order of the tiles will be as specified by the
* pseudo-code:
* <pre>
* for (y = bbox[1]; y < bbox[3]; y += tileheight) {
* for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
* }
* }
* </pre>
* If there is no output to be rendered, this method may return
* null.
*
* @param s the shape to be rendered (fill or draw)
* @param at the transform to be applied to the shape and the
* stroke attributes
* @param clip the current clip in effect in device coordinates
* @param bs if non-null, a {@code BasicStroke} whose attributes
* should be applied to this operation
* @param thin true if the transformed stroke attributes are smaller
* than the minimum dropout pen width
* @param normalize true if the {@code VALUE_STROKE_NORMALIZE}
* {@code RenderingHint} is in effect
* @param bbox returns the bounds of the iteration
* @return the {@code AATileGenerator} instance to be consulted
* for tile coverages, or null if there is no output to render
* @since 1.7
*/
@Override
public AATileGenerator getAATileGenerator(Shape s,
AffineTransform at,
Region clip,
BasicStroke bs,
boolean thin,
boolean normalize,
int bbox[])
{
MarlinTileGenerator ptg = null;
Renderer r = null;
final RendererContext rdrCtx = getRendererContext();
try {
// Test if at is identity:
final AffineTransform _at = (at != null && !at.isIdentity()) ? at
: null;
final NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
if (bs == null) {
// fill shape:
final PathIterator pi = getNormalizingPathIterator(rdrCtx, norm,
s.getPathIterator(_at));
r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
clip.getWidth(), clip.getHeight(),
pi.getWindingRule());
// TODO: subdivide quad/cubic curves into monotonic curves ?
pathTo(rdrCtx, pi, r);
} else {
// draw shape with given stroke:
r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
clip.getWidth(), clip.getHeight(),
PathIterator.WIND_NON_ZERO);
strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
}
if (r.endRendering()) {
ptg = rdrCtx.ptg.init();
ptg.getBbox(bbox);
// note: do not returnRendererContext(rdrCtx)
// as it will be called later by MarlinTileGenerator.dispose()
r = null;
}
} finally {
if (r != null) {
// dispose renderer:
r.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
}
}
// Return null to cancel AA tile generation (nothing to render)
return ptg;
}
@Override
public final AATileGenerator getAATileGenerator(double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2,
Region clip,
int bbox[])
{
// REMIND: Deal with large coordinates!
double ldx1, ldy1, ldx2, ldy2;
boolean innerpgram = (lw1 > 0.0 && lw2 > 0.0);
if (innerpgram) {
ldx1 = dx1 * lw1;
ldy1 = dy1 * lw1;
ldx2 = dx2 * lw2;
ldy2 = dy2 * lw2;
x -= (ldx1 + ldx2) / 2.0;
y -= (ldy1 + ldy2) / 2.0;
dx1 += ldx1;
dy1 += ldy1;
dx2 += ldx2;
dy2 += ldy2;
if (lw1 > 1.0 && lw2 > 1.0) {
// Inner parallelogram was entirely consumed by stroke...
innerpgram = false;
}
} else {
ldx1 = ldy1 = ldx2 = ldy2 = 0.0;
}
MarlinTileGenerator ptg = null;
Renderer r = null;
final RendererContext rdrCtx = getRendererContext();
try {
r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
clip.getWidth(), clip.getHeight(),
Renderer.WIND_EVEN_ODD);
r.moveTo((float) x, (float) y);
r.lineTo((float) (x+dx1), (float) (y+dy1));
r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
r.lineTo((float) (x+dx2), (float) (y+dy2));
r.closePath();
if (innerpgram) {
x += ldx1 + ldx2;
y += ldy1 + ldy2;
dx1 -= 2.0 * ldx1;
dy1 -= 2.0 * ldy1;
dx2 -= 2.0 * ldx2;
dy2 -= 2.0 * ldy2;
r.moveTo((float) x, (float) y);
r.lineTo((float) (x+dx1), (float) (y+dy1));
r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
r.lineTo((float) (x+dx2), (float) (y+dy2));
r.closePath();
}
r.pathDone();
if (r.endRendering()) {
ptg = rdrCtx.ptg.init();
ptg.getBbox(bbox);
// note: do not returnRendererContext(rdrCtx)
// as it will be called later by MarlinTileGenerator.dispose()
r = null;
}
} finally {
if (r != null) {
// dispose renderer:
r.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
}
}
// Return null to cancel AA tile generation (nothing to render)
return ptg;
}
/**
* Returns the minimum pen width that the antialiasing rasterizer
* can represent without dropouts occuring.
* @since 1.7
*/
@Override
public float getMinimumAAPenSize() {
return MIN_PEN_SIZE;
}
static {
if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO ||
PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD ||
BasicStroke.JOIN_MITER != Stroker.JOIN_MITER ||
BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND ||
BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL ||
BasicStroke.CAP_BUTT != Stroker.CAP_BUTT ||
BasicStroke.CAP_ROUND != Stroker.CAP_ROUND ||
BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE)
{
throw new InternalError("mismatched renderer constants");
}
}
// --- RendererContext handling ---
// use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext
private static final boolean useThreadLocal;
// hard reference
static final int REF_HARD = 0;
// soft reference
static final int REF_SOFT = 1;
// weak reference
static final int REF_WEAK = 2;
// reference type stored in either TL or CLQ
static final int REF_TYPE;
// Per-thread RendererContext
private static final ThreadLocal<Object> rdrCtxThreadLocal;
// RendererContext queue when ThreadLocal is disabled
private static final ConcurrentLinkedQueue<Object> rdrCtxQueue;
// Static initializer to use TL or CLQ mode
static {
// CLQ mode by default:
useThreadLocal = MarlinProperties.isUseThreadLocal();
rdrCtxThreadLocal = (useThreadLocal) ? new ThreadLocal<Object>()
: null;
rdrCtxQueue = (!useThreadLocal) ? new ConcurrentLinkedQueue<Object>()
: null;
// Soft reference by default:
String refType = AccessController.doPrivileged(
new GetPropertyAction("sun.java2d.renderer.useRef",
"soft"));
switch (refType) {
default:
case "soft":
REF_TYPE = REF_SOFT;
break;
case "weak":
REF_TYPE = REF_WEAK;
break;
case "hard":
REF_TYPE = REF_HARD;
break;
}
}
private static boolean settingsLogged = !enableLogs;
private static void logSettings(final String reClass) {
// log information at startup
if (settingsLogged) {
return;
}
settingsLogged = true;
String refType;
switch (REF_TYPE) {
default:
case REF_HARD:
refType = "hard";
break;
case REF_SOFT:
refType = "soft";
break;
case REF_WEAK:
refType = "weak";
break;
}
logInfo("=========================================================="
+ "=====================");
logInfo("Marlin software rasterizer = ENABLED");
logInfo("Version = ["
+ Version.getVersion() + "]");
logInfo("sun.java2d.renderer = "
+ reClass);
logInfo("sun.java2d.renderer.useThreadLocal = "
+ useThreadLocal);
logInfo("sun.java2d.renderer.useRef = "
+ refType);
logInfo("sun.java2d.renderer.pixelsize = "
+ MarlinConst.INITIAL_PIXEL_DIM);
logInfo("sun.java2d.renderer.subPixel_log2_X = "
+ MarlinConst.SUBPIXEL_LG_POSITIONS_X);
logInfo("sun.java2d.renderer.subPixel_log2_Y = "
+ MarlinConst.SUBPIXEL_LG_POSITIONS_Y);
logInfo("sun.java2d.renderer.tileSize_log2 = "
+ MarlinConst.TILE_SIZE_LG);
logInfo("sun.java2d.renderer.blockSize_log2 = "
+ MarlinConst.BLOCK_SIZE_LG);
logInfo("sun.java2d.renderer.blockSize_log2 = "
+ MarlinConst.BLOCK_SIZE_LG);
// RLE / blockFlags settings
logInfo("sun.java2d.renderer.forceRLE = "
+ MarlinProperties.isForceRLE());
logInfo("sun.java2d.renderer.forceNoRLE = "
+ MarlinProperties.isForceNoRLE());
logInfo("sun.java2d.renderer.useTileFlags = "
+ MarlinProperties.isUseTileFlags());
logInfo("sun.java2d.renderer.useTileFlags.useHeuristics = "
+ MarlinProperties.isUseTileFlagsWithHeuristics());
logInfo("sun.java2d.renderer.rleMinWidth = "
+ MarlinCache.RLE_MIN_WIDTH);
// optimisation parameters
logInfo("sun.java2d.renderer.useSimplifier = "
+ MarlinConst.useSimplifier);
// debugging parameters
logInfo("sun.java2d.renderer.doStats = "
+ MarlinConst.doStats);
logInfo("sun.java2d.renderer.doMonitors = "
+ MarlinConst.doMonitors);
logInfo("sun.java2d.renderer.doChecks = "
+ MarlinConst.doChecks);
// logging parameters
logInfo("sun.java2d.renderer.useLogger = "
+ MarlinConst.useLogger);
logInfo("sun.java2d.renderer.logCreateContext = "
+ MarlinConst.logCreateContext);
logInfo("sun.java2d.renderer.logUnsafeMalloc = "
+ MarlinConst.logUnsafeMalloc);
// quality settings
logInfo("Renderer settings:");
logInfo("CUB_COUNT_LG = " + Renderer.CUB_COUNT_LG);
logInfo("CUB_DEC_BND = " + Renderer.CUB_DEC_BND);
logInfo("CUB_INC_BND = " + Renderer.CUB_INC_BND);
logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);
logInfo("=========================================================="
+ "=====================");
}
/**
* Get the RendererContext instance dedicated to the current thread
* @return RendererContext instance
*/
@SuppressWarnings({"unchecked"})
static RendererContext getRendererContext() {
RendererContext rdrCtx = null;
final Object ref = (useThreadLocal) ? rdrCtxThreadLocal.get()
: rdrCtxQueue.poll();
if (ref != null) {
// resolve reference:
rdrCtx = (REF_TYPE == REF_HARD) ? ((RendererContext) ref)
: ((Reference<RendererContext>) ref).get();
}
// create a new RendererContext if none is available
if (rdrCtx == null) {
rdrCtx = RendererContext.createContext();
if (useThreadLocal) {
// update thread local reference:
rdrCtxThreadLocal.set(rdrCtx.reference);
}
}
if (doMonitors) {
RendererContext.stats.mon_pre_getAATileGenerator.start();
}
return rdrCtx;
}
/**
* Reset and return the given RendererContext instance for reuse
* @param rdrCtx RendererContext instance
*/
static void returnRendererContext(final RendererContext rdrCtx) {
rdrCtx.dispose();
if (doMonitors) {
RendererContext.stats.mon_pre_getAATileGenerator.stop();
}
if (!useThreadLocal) {
rdrCtxQueue.offer(rdrCtx.reference);
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.java2d.pipe.AATileGenerator;
import sun.misc.Unsafe;
final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
private static final int MAX_TILE_ALPHA_SUM = TILE_SIZE * TILE_SIZE
* MAX_AA_ALPHA;
private final Renderer rdr;
private final MarlinCache cache;
private int x, y;
MarlinTileGenerator(Renderer r) {
this.rdr = r;
this.cache = r.cache;
}
MarlinTileGenerator init() {
this.x = cache.bboxX0;
this.y = cache.bboxY0;
return this; // fluent API
}
/**
* Disposes this tile generator:
* clean up before reusing this instance
*/
@Override
public void dispose() {
if (doMonitors) {
// called from AAShapePipe.renderTiles() (render tiles end):
RendererContext.stats.mon_pipe_renderTiles.stop();
}
// dispose cache:
cache.dispose();
// dispose renderer:
rdr.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdr.rdrCtx);
}
void getBbox(int bbox[]) {
bbox[0] = cache.bboxX0;
bbox[1] = cache.bboxY0;
bbox[2] = cache.bboxX1;
bbox[3] = cache.bboxY1;
}
/**
* Gets the width of the tiles that the generator batches output into.
* @return the width of the standard alpha tile
*/
@Override
public int getTileWidth() {
if (doMonitors) {
// called from AAShapePipe.renderTiles() (render tiles start):
RendererContext.stats.mon_pipe_renderTiles.start();
}
return TILE_SIZE;
}
/**
* Gets the height of the tiles that the generator batches output into.
* @return the height of the standard alpha tile
*/
@Override
public int getTileHeight() {
return TILE_SIZE;
}
/**
* Gets the typical alpha value that will characterize the current
* tile.
* The answer may be 0x00 to indicate that the current tile has
* no coverage in any of its pixels, or it may be 0xff to indicate
* that the current tile is completely covered by the path, or any
* other value to indicate non-trivial coverage cases.
* @return 0x00 for no coverage, 0xff for total coverage, or any other
* value for partial coverage of the tile
*/
@Override
public int getTypicalAlpha() {
int al = cache.alphaSumInTile(x);
// Note: if we have a filled rectangle that doesn't end on a tile
// border, we could still return 0xff, even though al!=maxTileAlphaSum
// This is because if we return 0xff, our users will fill a rectangle
// starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
// and height min(TILE_SIZE,bboxY1-y), which is what should happen.
// However, to support this, we would have to use 2 Math.min's
// and 2 multiplications per tile, instead of just 2 multiplications
// to compute maxTileAlphaSum. The savings offered would probably
// not be worth it, considering how rare this case is.
// Note: I have not tested this, so in the future if it is determined
// that it is worth it, it should be implemented. Perhaps this method's
// interface should be changed to take arguments the width and height
// of the current tile. This would eliminate the 2 Math.min calls that
// would be needed here, since our caller needs to compute these 2
// values anyway.
final int alpha = (al == 0x00 ? 0x00
: (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80));
if (doStats) {
RendererContext.stats.hist_tile_generator_alpha.add(alpha);
}
return alpha;
}
/**
* Skips the current tile and moves on to the next tile.
* Either this method, or the getAlpha() method should be called
* once per tile, but not both.
*/
@Override
public void nextTile() {
if ((x += TILE_SIZE) >= cache.bboxX1) {
x = cache.bboxX0;
y += TILE_SIZE;
if (y < cache.bboxY1) {
// compute for the tile line
// [ y; max(y + TILE_SIZE, bboxY1) ]
this.rdr.endRendering(y);
}
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
@Override
public void getAlpha(final byte tile[], final int offset,
final int rowstride)
{
if (cache.useRLE) {
getAlphaRLE(tile, offset, rowstride);
} else {
getAlphaNoRLE(tile, offset, rowstride);
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
private void getAlphaNoRLE(final byte tile[], final int offset,
final int rowstride)
{
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.start();
}
// local vars for performance:
final MarlinCache _cache = this.cache;
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
final int[] rowAAx0 = _cache.rowAAx0;
final int[] rowAAx1 = _cache.rowAAx1;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1);
// note: process tile line [0 - 32[
final int y0 = 0;
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y;
if (doLogBounds) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
+ "[ [" + y0 + " ... " + y1 + "[");
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE = 1L;
final long addr_rowAA = _cache.rowAAChunk.address;
long addr;
final int skipRowPixels = (rowstride - (x1 - x0));
int aax0, aax1, end;
int idx = offset;
for (int cy = y0, cx; cy < y1; cy++) {
// empty line (default)
cx = x0;
aax1 = rowAAx1[cy]; // exclusive
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
aax0 = rowAAx0[cy]; // inclusive
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
}
// now: cx >= x0 but cx < aax0 (x1 < aax0)
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
// cx inside tile[x0; x1[ :
tile[idx++] = _unsafe.getByte(addr); // [0..255]
addr += SIZE;
}
}
}
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
if (doTrace) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
nextTile();
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.stop();
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
private void getAlphaRLE(final byte tile[], final int offset,
final int rowstride)
{
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.start();
}
// Decode run-length encoded alpha mask data
// The data for row j begins at cache.rowOffsetsRLE[j]
// and is encoded as a set of 2-byte pairs (val, runLen)
// terminated by a (0, 0) pair.
// local vars for performance:
final MarlinCache _cache = this.cache;
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
final int[] rowAAx0 = _cache.rowAAx0;
final int[] rowAAx1 = _cache.rowAAx1;
final int[] rowAAEnc = _cache.rowAAEnc;
final long[] rowAALen = _cache.rowAALen;
final long[] rowAAPos = _cache.rowAAPos;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1);
// note: process tile line [0 - 32[
final int y0 = 0;
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y;
if (doLogBounds) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
+ "[ [" + y0 + " ... " + y1 + "[");
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_BYTE = 1L;
final long SIZE_INT = 4L;
final long addr_rowAA = _cache.rowAAChunk.address;
long addr, addr_row, last_addr, addr_end;
final int skipRowPixels = (rowstride - (x1 - x0));
int cx, cy, cx1;
int rx0, rx1, runLen, end;
int packed;
byte val;
int idx = offset;
for (cy = y0; cy < y1; cy++) {
// empty line (default)
cx = x0;
if (rowAAEnc[cy] == 0) {
// Raw encoding:
final int aax1 = rowAAx1[cy]; // exclusive
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
final int aax0 = rowAAx0[cy]; // inclusive
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
}
// now: cx >= x0 but cx < aax0 (x1 < aax0)
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
tile[idx++] = _unsafe.getByte(addr); // [0..255]
addr += SIZE_BYTE;
}
}
}
} else {
// RLE encoding:
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (rowAAx1[cy] > x0) { // last pixel exclusive
cx = rowAAx0[cy]; // inclusive
if (cx > x1) {
cx = x1;
}
// fill line start until first AA pixel rowAA exclusive:
for (int i = x0; i < cx; i++) {
tile[idx++] = 0;
}
// get row address:
addr_row = addr_rowAA + rowAAChunkIndex[cy];
// get row end address:
addr_end = addr_row + rowAALen[cy]; // coded length
// reuse previous iteration position:
addr = addr_row + rowAAPos[cy];
last_addr = 0L;
while ((cx < x1) && (addr < addr_end)) {
// keep current position:
last_addr = addr;
// packed value:
packed = _unsafe.getInt(addr);
// last exclusive pixel x-coordinate:
cx1 = (packed >> 8);
// as bytes:
addr += SIZE_INT;
rx0 = cx;
if (rx0 < x0) {
rx0 = x0;
}
rx1 = cx = cx1;
if (rx1 > x1) {
rx1 = x1;
cx = x1; // fix last x
}
// adjust runLen:
runLen = rx1 - rx0;
// ensure rx1 > rx0:
if (runLen > 0) {
val = (byte)(packed & 0xFF); // [0..255]
do {
tile[idx++] = val;
} while (--runLen > 0);
}
}
// Update last position in RLE entries:
if (last_addr != 0L) {
// Fix x0:
rowAAx0[cy] = cx; // inclusive
// Fix position:
rowAAPos[cy] = (last_addr - addr_row);
}
}
}
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
if (doTrace) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
nextTile();
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.stop();
}
}
static String hex(int v, int d) {
String s = Integer.toHexString(v);
while (s.length() < d) {
s = "0" + s;
}
return s.substring(0, d);
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;
public final class MarlinUtils {
// TODO: use sun.util.logging.PlatformLogger once in JDK9
private static final java.util.logging.Logger log;
static {
if (MarlinConst.useLogger) {
log = java.util.logging.Logger.getLogger("sun.java2d.marlin");
} else {
log = null;
}
}
private MarlinUtils() {
// no-op
}
public static void logInfo(final String msg) {
if (MarlinConst.useLogger) {
log.info(msg);
} else if (MarlinConst.enableLogs) {
System.out.print("INFO: ");
System.out.println(msg);
}
}
public static void logException(final String msg, final Throwable th) {
if (MarlinConst.useLogger) {
// log.warning(msg, th);
log.log(java.util.logging.Level.WARNING, msg, th);
} else if (MarlinConst.enableLogs) {
System.out.print("WARNING: ");
System.out.println(msg);
th.printStackTrace(System.err);
}
}
// Returns the caller's class and method's name; best effort
// if cannot infer, return the logger's name.
static String getCallerInfo(String className) {
String sourceClassName = null;
String sourceMethodName = null;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
boolean lookingForClassName = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame = access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
if (lookingForClassName) {
// Skip all frames until we have found the first frame having the class name.
if (cname.equals(className)) {
lookingForClassName = false;
}
} else {
if (!cname.equals(className)) {
// We've found the relevant frame.
sourceClassName = cname;
sourceMethodName = frame.getMethodName();
break;
}
}
}
if (sourceClassName != null) {
return sourceClassName + " " + sourceMethodName;
} else {
return "unknown";
}
}
}
/*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
/**
* MergeSort adapted from (OpenJDK 8) java.util.Array.legacyMergeSort(Object[])
* to swap two arrays at the same time (x & y)
* and use external auxiliary storage for temporary arrays
*/
final class MergeSort {
// insertion sort threshold
public static final int INSERTION_SORT_THRESHOLD = 14;
/**
* Modified merge sort:
* Input arrays are in both auxX/auxY (sorted: 0 to insertionSortIndex)
* and x/y (unsorted: insertionSortIndex to toIndex)
* Outputs are stored in x/y arrays
*/
static void mergeSortNoCopy(final int[] x, final int[] y,
final int[] auxX, final int[] auxY,
final int toIndex,
final int insertionSortIndex)
{
if ((toIndex > x.length) || (toIndex > y.length)
|| (toIndex > auxX.length) || (toIndex > auxY.length)) {
// explicit check to avoid bound checks within hot loops (below):
throw new ArrayIndexOutOfBoundsException("bad arguments: toIndex="
+ toIndex);
}
// sort second part only using merge / insertion sort
// in auxiliary storage (auxX/auxY)
mergeSort(x, y, x, auxX, y, auxY, insertionSortIndex, toIndex);
// final pass to merge both
// Merge sorted parts (auxX/auxY) into x/y arrays
if ((insertionSortIndex == 0)
|| (auxX[insertionSortIndex - 1] <= auxX[insertionSortIndex])) {
// System.out.println("mergeSortNoCopy: ordered");
// 34 occurences
// no initial left part or both sublists (auxX, auxY) are sorted:
// copy back data into (x, y):
System.arraycopy(auxX, 0, x, 0, toIndex);
System.arraycopy(auxY, 0, y, 0, toIndex);
return;
}
for (int i = 0, p = 0, q = insertionSortIndex; i < toIndex; i++) {
if ((q >= toIndex) || ((p < insertionSortIndex)
&& (auxX[p] <= auxX[q]))) {
x[i] = auxX[p];
y[i] = auxY[p];
p++;
} else {
x[i] = auxX[q];
y[i] = auxY[q];
q++;
}
}
}
/**
* Src is the source array that starts at index 0
* Dest is the (possibly larger) array destination with a possible offset
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
*/
private static void mergeSort(final int[] refX, final int[] refY,
final int[] srcX, final int[] dstX,
final int[] srcY, final int[] dstY,
final int low, final int high)
{
final int length = high - low;
/*
* Tuning parameter: list size at or below which insertion sort
* will be used in preference to mergesort.
*/
if (length <= INSERTION_SORT_THRESHOLD) {
// Insertion sort on smallest arrays
dstX[low] = refX[low];
dstY[low] = refY[low];
for (int i = low + 1, j = low, x, y; i < high; j = i++) {
x = refX[i];
y = refY[i];
while (dstX[j] > x) {
// swap element
dstX[j + 1] = dstX[j];
dstY[j + 1] = dstY[j];
if (j-- == low) {
break;
}
}
dstX[j + 1] = x;
dstY[j + 1] = y;
}
return;
}
// Recursively sort halves of dest into src
// note: use signed shift (not >>>) for performance
// as indices are small enough to exceed Integer.MAX_VALUE
final int mid = (low + high) >> 1;
mergeSort(refX, refY, dstX, srcX, dstY, srcY, low, mid);
mergeSort(refX, refY, dstX, srcX, dstY, srcY, mid, high);
// If arrays are inverted ie all(A) > all(B) do swap A and B to dst
if (srcX[high - 1] <= srcX[low]) {
// System.out.println("mergeSort: inverse ordered");
// 1561 occurences
final int left = mid - low;
final int right = high - mid;
final int off = (left != right) ? 1 : 0;
// swap parts:
System.arraycopy(srcX, low, dstX, mid + off, left);
System.arraycopy(srcX, mid, dstX, low, right);
System.arraycopy(srcY, low, dstY, mid + off, left);
System.arraycopy(srcY, mid, dstY, low, right);
return;
}
// If arrays are already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (srcX[mid - 1] <= srcX[mid]) {
// System.out.println("mergeSort: ordered");
// 14 occurences
System.arraycopy(srcX, low, dstX, low, length);
System.arraycopy(srcY, low, dstY, low, length);
return;
}
// Merge sorted halves (now in src) into dest
for (int i = low, p = low, q = mid; i < high; i++) {
if ((q >= high) || ((p < mid) && (srcX[p] <= srcX[q]))) {
dstX[i] = srcX[p];
dstY[i] = srcY[p];
p++;
} else {
dstX[i] = srcX[q];
dstY[i] = srcY[q];
q++;
}
}
}
private MergeSort() {
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Vector;
import static sun.java2d.marlin.MarlinConst.logUnsafeMalloc;
import sun.misc.ThreadGroupUtils;
import sun.misc.Unsafe;
/**
*
* @author bourgesl
*/
final class OffHeapArray {
// unsafe reference
static final Unsafe unsafe;
// size of int / float
static final int SIZE_INT;
// RendererContext reference queue
private static final ReferenceQueue<Object> rdrQueue
= new ReferenceQueue<Object>();
// reference list
private static final Vector<OffHeapReference> refList
= new Vector<OffHeapReference>(32);
static {
unsafe = Unsafe.getUnsafe();
SIZE_INT = Unsafe.ARRAY_INT_INDEX_SCALE;
// Mimics Java2D Disposer:
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
* Make its parent the top-level thread group.
*/
final ThreadGroup rootTG
= ThreadGroupUtils.getRootThreadGroup();
final Thread t = new Thread(rootTG, new OffHeapDisposer(),
"MarlinRenderer Disposer");
t.setContextClassLoader(null);
t.setDaemon(true);
t.setPriority(Thread.MAX_PRIORITY);
t.start();
return null;
}
);
}
/* members */
long address;
long length;
int used;
OffHeapArray(final Object parent, final long len) {
// note: may throw OOME:
this.address = unsafe.allocateMemory(len);
this.length = len;
this.used = 0;
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapArray.allocateMemory = "
+ len + " to addr = " + this.address);
}
// Create the phantom reference to ensure freeing off-heap memory:
refList.add(new OffHeapReference(parent, this));
}
/*
* As realloc may change the address, updating address is MANDATORY
* @param len new array length
* @throws OutOfMemoryError if the allocation is refused by the system
*/
void resize(final long len) {
// note: may throw OOME:
this.address = unsafe.reallocateMemory(address, len);
this.length = len;
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapArray.reallocateMemory = "
+ len + " to addr = " + this.address);
}
}
void free() {
unsafe.freeMemory(this.address);
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapEdgeArray.free = "
+ this.length
+ " at addr = " + this.address);
}
}
void fill(final byte val) {
unsafe.setMemory(this.address, this.length, val);
}
static final class OffHeapReference extends PhantomReference<Object> {
private final OffHeapArray array;
OffHeapReference(final Object parent, final OffHeapArray edges) {
super(parent, rdrQueue);
this.array = edges;
}
void dispose() {
// free off-heap blocks
this.array.free();
}
}
static final class OffHeapDisposer implements Runnable {
@Override
public void run() {
final Thread currentThread = Thread.currentThread();
OffHeapReference ref;
// check interrupted:
for (; !currentThread.isInterrupted();) {
try {
ref = (OffHeapReference)rdrQueue.remove();
ref.dispose();
refList.remove(ref);
} catch (InterruptedException ie) {
MarlinUtils.logException("OffHeapDisposer interrupted:",
ie);
}
}
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
import static sun.java2d.marlin.OffHeapArray.SIZE_INT;
import sun.misc.Unsafe;
final class Renderer implements PathConsumer2D, MarlinConst {
static final boolean DISABLE_RENDER = false;
static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags();
static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics();
private static final int ALL_BUT_LSB = 0xfffffffe;
private static final int ERR_STEP_MAX = 0x7fffffff; // = 2^31 - 1
private static final double POWER_2_TO_32 = 0x1.0p32;
// use float to make tosubpix methods faster (no int to float conversion)
public static final float f_SUBPIXEL_POSITIONS_X
= (float) SUBPIXEL_POSITIONS_X;
public static final float f_SUBPIXEL_POSITIONS_Y
= (float) SUBPIXEL_POSITIONS_Y;
public static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
public static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
// number of subpixels corresponding to a tile line
private static final int SUBPIXEL_TILE
= TILE_SIZE << SUBPIXEL_LG_POSITIONS_Y;
// 2048 (pixelSize) pixels (height) x 8 subpixels = 64K
static final int INITIAL_BUCKET_ARRAY
= INITIAL_PIXEL_DIM * SUBPIXEL_POSITIONS_Y;
public static final int WIND_EVEN_ODD = 0;
public static final int WIND_NON_ZERO = 1;
// common to all types of input path segments.
// OFFSET as bytes
// only integer values:
public static final long OFF_CURX_OR = 0;
public static final long OFF_ERROR = OFF_CURX_OR + SIZE_INT;
public static final long OFF_BUMP_X = OFF_ERROR + SIZE_INT;
public static final long OFF_BUMP_ERR = OFF_BUMP_X + SIZE_INT;
public static final long OFF_NEXT = OFF_BUMP_ERR + SIZE_INT;
public static final long OFF_YMAX = OFF_NEXT + SIZE_INT;
// size of one edge in bytes
public static final int SIZEOF_EDGE_BYTES = (int)(OFF_YMAX + SIZE_INT);
// curve break into lines
// cubic error in subpixels to decrement step
private static final float CUB_DEC_ERR_SUBPIX
= 2.5f * (NORM_SUBPIXELS / 8f); // 2.5 subpixel for typical 8x8 subpixels
// cubic error in subpixels to increment step
private static final float CUB_INC_ERR_SUBPIX
= 1f * (NORM_SUBPIXELS / 8f); // 1 subpixel for typical 8x8 subpixels
// cubic bind length to decrement step = 8 * error in subpixels
// pisces: 20 / 8
// openjfx pisces: 8 / 3.2
// multiply by 8 = error scale factor:
public static final float CUB_DEC_BND
= 8f * CUB_DEC_ERR_SUBPIX; // 20f means 2.5 subpixel error
// cubic bind length to increment step = 8 * error in subpixels
public static final float CUB_INC_BND
= 8f * CUB_INC_ERR_SUBPIX; // 8f means 1 subpixel error
// cubic countlg
public static final int CUB_COUNT_LG = 2;
// cubic count = 2^countlg
private static final int CUB_COUNT = 1 << CUB_COUNT_LG;
// cubic count^2 = 4^countlg
private static final int CUB_COUNT_2 = 1 << (2 * CUB_COUNT_LG);
// cubic count^3 = 8^countlg
private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);
// cubic dt = 1 / count
private static final float CUB_INV_COUNT = 1f / CUB_COUNT;
// cubic dt^2 = 1 / count^2 = 1 / 4^countlg
private static final float CUB_INV_COUNT_2 = 1f / CUB_COUNT_2;
// cubic dt^3 = 1 / count^3 = 1 / 8^countlg
private static final float CUB_INV_COUNT_3 = 1f / CUB_COUNT_3;
// quad break into lines
// quadratic error in subpixels
private static final float QUAD_DEC_ERR_SUBPIX
= 1f * (NORM_SUBPIXELS / 8f); // 1 subpixel for typical 8x8 subpixels
// quadratic bind length to decrement step = 8 * error in subpixels
// pisces and openjfx pisces: 32
public static final float QUAD_DEC_BND
= 8f * QUAD_DEC_ERR_SUBPIX; // 8f means 1 subpixel error
//////////////////////////////////////////////////////////////////////////////
// SCAN LINE
//////////////////////////////////////////////////////////////////////////////
// crossings ie subpixel edge x coordinates
private int[] crossings;
// auxiliary storage for crossings (merge sort)
private int[] aux_crossings;
// indices into the segment pointer lists. They indicate the "active"
// sublist in the segment lists (the portion of the list that contains
// all the segments that cross the next scan line).
private int edgeCount;
private int[] edgePtrs;
// auxiliary storage for edge pointers (merge sort)
private int[] aux_edgePtrs;
// max used for both edgePtrs and crossings (stats only)
private int activeEdgeMaxUsed;
// per-thread initial arrays (large enough to satisfy most usages) (1024)
private final int[] crossings_initial = new int[INITIAL_SMALL_ARRAY]; // 4K
// +1 to avoid recycling in Helpers.widenArray()
private final int[] edgePtrs_initial = new int[INITIAL_SMALL_ARRAY + 1]; // 4K
// merge sort initial arrays (large enough to satisfy most usages) (1024)
private final int[] aux_crossings_initial = new int[INITIAL_SMALL_ARRAY]; // 4K
// +1 to avoid recycling in Helpers.widenArray()
private final int[] aux_edgePtrs_initial = new int[INITIAL_SMALL_ARRAY + 1]; // 4K
//////////////////////////////////////////////////////////////////////////////
// EDGE LIST
//////////////////////////////////////////////////////////////////////////////
private float edgeMinY = Float.POSITIVE_INFINITY;
private float edgeMaxY = Float.NEGATIVE_INFINITY;
private float edgeMinX = Float.POSITIVE_INFINITY;
private float edgeMaxX = Float.NEGATIVE_INFINITY;
// edges [floats|ints] stored in off-heap memory
private final OffHeapArray edges;
private int[] edgeBuckets;
private int[] edgeBucketCounts; // 2*newedges + (1 if pruning needed)
// used range for edgeBuckets / edgeBucketCounts
private int buckets_minY;
private int buckets_maxY;
// sum of each edge delta Y (subpixels)
private int edgeSumDeltaY;
// +1 to avoid recycling in Helpers.widenArray()
private final int[] edgeBuckets_initial
= new int[INITIAL_BUCKET_ARRAY + 1]; // 64K
private final int[] edgeBucketCounts_initial
= new int[INITIAL_BUCKET_ARRAY + 1]; // 64K
// Flattens using adaptive forward differencing. This only carries out
// one iteration of the AFD loop. All it does is update AFD variables (i.e.
// X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
private void quadBreakIntoLinesAndAdd(float x0, float y0,
final Curve c,
final float x2, final float y2)
{
int count = 1; // dt = 1 / count
// maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
float maxDD = FloatMath.max(Math.abs(c.dbx), Math.abs(c.dby));
final float _DEC_BND = QUAD_DEC_BND;
while (maxDD >= _DEC_BND) {
// divide step by half:
maxDD /= 4f; // error divided by 2^2 = 4
count <<= 1;
if (doStats) {
RendererContext.stats.stat_rdr_quadBreak_dec.add(count);
}
}
int nL = 0; // line count
if (count > 1) {
final float icount = 1f / count; // dt
final float icount2 = icount * icount; // dt^2
final float ddx = c.dbx * icount2;
final float ddy = c.dby * icount2;
float dx = c.bx * icount2 + c.cx * icount;
float dy = c.by * icount2 + c.cy * icount;
float x1, y1;
while (--count > 0) {
x1 = x0 + dx;
dx += ddx;
y1 = y0 + dy;
dy += ddy;
addLine(x0, y0, x1, y1);
if (doStats) { nL++; }
x0 = x1;
y0 = y1;
}
}
addLine(x0, y0, x2, y2);
if (doStats) {
RendererContext.stats.stat_rdr_quadBreak.add(nL + 1);
}
}
// x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
// using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
// numerical errors, and our callers already have the exact values.
// Another alternative would be to pass all the control points, and call
// c.set here, but then too many numbers are passed around.
private void curveBreakIntoLinesAndAdd(float x0, float y0,
final Curve c,
final float x3, final float y3)
{
int count = CUB_COUNT;
final float icount = CUB_INV_COUNT; // dt
final float icount2 = CUB_INV_COUNT_2; // dt^2
final float icount3 = CUB_INV_COUNT_3; // dt^3
// the dx and dy refer to forward differencing variables, not the last
// coefficients of the "points" polynomial
float dddx, dddy, ddx, ddy, dx, dy;
dddx = 2f * c.dax * icount3;
dddy = 2f * c.day * icount3;
ddx = dddx + c.dbx * icount2;
ddy = dddy + c.dby * icount2;
dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;
// we use x0, y0 to walk the line
float x1 = x0, y1 = y0;
int nL = 0; // line count
final float _DEC_BND = CUB_DEC_BND;
final float _INC_BND = CUB_INC_BND;
while (count > 0) {
// divide step by half:
while (Math.abs(ddx) >= _DEC_BND || Math.abs(ddy) >= _DEC_BND) {
dddx /= 8f;
dddy /= 8f;
ddx = ddx/4f - dddx;
ddy = ddy/4f - dddy;
dx = (dx - ddx) / 2f;
dy = (dy - ddy) / 2f;
count <<= 1;
if (doStats) {
RendererContext.stats.stat_rdr_curveBreak_dec.add(count);
}
}
// double step:
// TODO: why use first derivative dX|Y instead of second ddX|Y ?
// both scale changes should use speed or acceleration to have the same metric.
// can only do this on even "count" values, because we must divide count by 2
while (count % 2 == 0
&& Math.abs(dx) <= _INC_BND && Math.abs(dy) <= _INC_BND)
{
dx = 2f * dx + ddx;
dy = 2f * dy + ddy;
ddx = 4f * (ddx + dddx);
ddy = 4f * (ddy + dddy);
dddx *= 8f;
dddy *= 8f;
count >>= 1;
if (doStats) {
RendererContext.stats.stat_rdr_curveBreak_inc.add(count);
}
}
if (--count > 0) {
x1 += dx;
dx += ddx;
ddx += dddx;
y1 += dy;
dy += ddy;
ddy += dddy;
} else {
x1 = x3;
y1 = y3;
}
addLine(x0, y0, x1, y1);
if (doStats) { nL++; }
x0 = x1;
y0 = y1;
}
if (doStats) {
RendererContext.stats.stat_rdr_curveBreak.add(nL);
}
}
private void addLine(float x1, float y1, float x2, float y2) {
if (doMonitors) {
RendererContext.stats.mon_rdr_addLine.start();
}
if (doStats) {
RendererContext.stats.stat_rdr_addLine.add(1);
}
int or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
if (y2 < y1) {
or = 0;
float tmp = y2;
y2 = y1;
y1 = tmp;
tmp = x2;
x2 = x1;
x1 = tmp;
}
// convert subpixel coordinates (float) into pixel positions (int)
// The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)
// Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply
// ceil(y1) or ceil(y2)
// upper integer (inclusive)
final int firstCrossing = FloatMath.max(FloatMath.ceil_int(y1), boundsMinY);
// note: use boundsMaxY (last Y exclusive) to compute correct coverage
// upper integer (exclusive)
final int lastCrossing = FloatMath.min(FloatMath.ceil_int(y2), boundsMaxY);
/* skip horizontal lines in pixel space and clip edges
out of y range [boundsMinY; boundsMaxY] */
if (firstCrossing >= lastCrossing) {
if (doMonitors) {
RendererContext.stats.mon_rdr_addLine.stop();
}
if (doStats) {
RendererContext.stats.stat_rdr_addLine_skip.add(1);
}
return;
}
// edge min/max X/Y are in subpixel space (inclusive)
if (y1 < edgeMinY) {
edgeMinY = y1;
}
if (y2 > edgeMaxY) {
edgeMaxY = y2;
}
// Use double-precision for improved accuracy:
final double x1d = x1;
final double y1d = y1;
final double slope = (x2 - x1d) / (y2 - y1d);
if (slope >= 0.0) { // <==> x1 < x2
if (x1 < edgeMinX) {
edgeMinX = x1;
}
if (x2 > edgeMaxX) {
edgeMaxX = x2;
}
} else {
if (x2 < edgeMinX) {
edgeMinX = x2;
}
if (x1 > edgeMaxX) {
edgeMaxX = x1;
}
}
// local variables for performance:
final int _SIZEOF_EDGE_BYTES = SIZEOF_EDGE_BYTES;
final OffHeapArray _edges = edges;
// get free pointer (ie length in bytes)
final int edgePtr = _edges.used;
// use substraction to avoid integer overflow:
if (_edges.length - edgePtr < _SIZEOF_EDGE_BYTES) {
// suppose _edges.length > _SIZEOF_EDGE_BYTES
// so doubling size is enough to add needed bytes
// note: throw IOOB if neededSize > 2Gb:
final long edgeNewSize = ArrayCache.getNewLargeSize(_edges.length,
edgePtr + _SIZEOF_EDGE_BYTES);
if (doStats) {
RendererContext.stats.stat_rdr_edges_resizes.add(edgeNewSize);
}
_edges.resize(edgeNewSize);
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_INT = 4L;
long addr = _edges.address + edgePtr;
// The x value must be bumped up to its position at the next HPC we will evaluate.
// "firstcrossing" is the (sub)pixel number where the next crossing occurs
// thus, the actual coordinate of the next HPC is "firstcrossing + 0.5"
// so the Y distance we cover is "firstcrossing + 0.5 - trueY".
// Note that since y1 (and y2) are already biased by -0.5 in tosubpixy(), we have
// y1 = trueY - 0.5
// trueY = y1 + 0.5
// firstcrossing + 0.5 - trueY = firstcrossing + 0.5 - (y1 + 0.5)
// = firstcrossing - y1
// The x coordinate at that HPC is then:
// x1_intercept = x1 + (firstcrossing - y1) * slope
// The next VPC is then given by:
// VPC index = ceil(x1_intercept - 0.5), or alternately
// VPC index = floor(x1_intercept - 0.5 + 1 - epsilon)
// epsilon is hard to pin down in floating point, but easy in fixed point, so if
// we convert to fixed point then these operations get easier:
// long x1_fixed = x1_intercept * 2^32; (fixed point 32.32 format)
// curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1)
// = fixed_floor(x1_fixed + 2^31 - 1)
// = fixed_floor(x1_fixed + 0x7fffffff)
// and error = fixed_fract(x1_fixed + 0x7fffffff)
final double x1_intercept = x1d + (firstCrossing - y1d) * slope;
// inlined scalb(x1_intercept, 32):
final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))
+ 0x7fffffffL;
// curx:
// last bit corresponds to the orientation
_unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or);
addr += SIZE_INT;
_unsafe.putInt(addr, ((int) x1_fixed_biased) >>> 1);
addr += SIZE_INT;
// inlined scalb(slope, 32):
final long slope_fixed = (long) (POWER_2_TO_32 * slope);
// last bit set to 0 to keep orientation:
_unsafe.putInt(addr, (((int) (slope_fixed >> 31L)) & ALL_BUT_LSB));
addr += SIZE_INT;
_unsafe.putInt(addr, ((int) slope_fixed) >>> 1);
addr += SIZE_INT;
final int[] _edgeBuckets = edgeBuckets;
final int[] _edgeBucketCounts = edgeBucketCounts;
final int _boundsMinY = boundsMinY;
// each bucket is a linked list. this method adds ptr to the
// start of the "bucket"th linked list.
final int bucketIdx = firstCrossing - _boundsMinY;
// pointer from bucket
_unsafe.putInt(addr, _edgeBuckets[bucketIdx]);
addr += SIZE_INT;
// y max (inclusive)
_unsafe.putInt(addr, lastCrossing);
// Update buckets:
// directly the edge struct "pointer"
_edgeBuckets[bucketIdx] = edgePtr;
_edgeBucketCounts[bucketIdx] += 2; // 1 << 1
// last bit means edge end
_edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1;
// update sum of delta Y (subpixels):
edgeSumDeltaY += (lastCrossing - firstCrossing);
// update free pointer (ie length in bytes)
_edges.used += _SIZEOF_EDGE_BYTES;
if (doMonitors) {
RendererContext.stats.mon_rdr_addLine.stop();
}
}
// END EDGE LIST
//////////////////////////////////////////////////////////////////////////////
// Cache to store RLE-encoded coverage mask of the current primitive
final MarlinCache cache;
// Bounds of the drawing region, at subpixel precision.
private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
// Current winding rule
private int windingRule;
// Current drawing position, i.e., final point of last segment
private float x0, y0;
// Position of most recent 'moveTo' command
private float pix_sx0, pix_sy0;
// per-thread renderer context
final RendererContext rdrCtx;
// dirty curve
private final Curve curve;
Renderer(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
this.edges = new OffHeapArray(rdrCtx, INITIAL_EDGES_CAPACITY); // 96K
this.curve = rdrCtx.curve;
edgeBuckets = edgeBuckets_initial;
edgeBucketCounts = edgeBucketCounts_initial;
alphaLine = alphaLine_initial;
this.cache = rdrCtx.cache;
// ScanLine:
crossings = crossings_initial;
aux_crossings = aux_crossings_initial;
edgePtrs = edgePtrs_initial;
aux_edgePtrs = aux_edgePtrs_initial;
edgeCount = 0;
activeEdgeMaxUsed = 0;
}
Renderer init(final int pix_boundsX, final int pix_boundsY,
final int pix_boundsWidth, final int pix_boundsHeight,
final int windingRule) {
this.windingRule = windingRule;
// bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
this.boundsMinX = pix_boundsX << SUBPIXEL_LG_POSITIONS_X;
this.boundsMaxX =
(pix_boundsX + pix_boundsWidth) << SUBPIXEL_LG_POSITIONS_X;
this.boundsMinY = pix_boundsY << SUBPIXEL_LG_POSITIONS_Y;
this.boundsMaxY =
(pix_boundsY + pix_boundsHeight) << SUBPIXEL_LG_POSITIONS_Y;
if (doLogBounds) {
MarlinUtils.logInfo("boundsXY = [" + boundsMinX + " ... "
+ boundsMaxX + "[ [" + boundsMinY + " ... "
+ boundsMaxY + "[");
}
// see addLine: ceil(boundsMaxY) => boundsMaxY + 1
// +1 for edgeBucketCounts
final int edgeBucketsLength = (boundsMaxY - boundsMinY) + 1;
if (edgeBucketsLength > INITIAL_BUCKET_ARRAY) {
if (doStats) {
RendererContext.stats.stat_array_renderer_edgeBuckets
.add(edgeBucketsLength);
RendererContext.stats.stat_array_renderer_edgeBucketCounts
.add(edgeBucketsLength);
}
edgeBuckets = rdrCtx.getIntArray(edgeBucketsLength);
edgeBucketCounts = rdrCtx.getIntArray(edgeBucketsLength);
}
edgeMinY = Float.POSITIVE_INFINITY;
edgeMaxY = Float.NEGATIVE_INFINITY;
edgeMinX = Float.POSITIVE_INFINITY;
edgeMaxX = Float.NEGATIVE_INFINITY;
// reset used mark:
edgeCount = 0;
activeEdgeMaxUsed = 0;
edges.used = 0;
edgeSumDeltaY = 0;
return this; // fluent API
}
/**
* Disposes this renderer and recycle it clean up before reusing this instance
*/
void dispose() {
if (doStats) {
RendererContext.stats.stat_rdr_activeEdges.add(activeEdgeMaxUsed);
RendererContext.stats.stat_rdr_edges.add(edges.used);
RendererContext.stats.stat_rdr_edges_count
.add(edges.used / SIZEOF_EDGE_BYTES);
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(crossings, 0);
Arrays.fill(aux_crossings, 0);
Arrays.fill(edgePtrs, 0);
Arrays.fill(aux_edgePtrs, 0);
}
// Return arrays:
if (crossings != crossings_initial) {
rdrCtx.putDirtyIntArray(crossings);
crossings = crossings_initial;
if (aux_crossings != aux_crossings_initial) {
rdrCtx.putDirtyIntArray(aux_crossings);
aux_crossings = aux_crossings_initial;
}
}
if (edgePtrs != edgePtrs_initial) {
rdrCtx.putDirtyIntArray(edgePtrs);
edgePtrs = edgePtrs_initial;
if (aux_edgePtrs != aux_edgePtrs_initial) {
rdrCtx.putDirtyIntArray(aux_edgePtrs);
aux_edgePtrs = aux_edgePtrs_initial;
}
}
if (alphaLine != alphaLine_initial) {
rdrCtx.putIntArray(alphaLine, 0, 0); // already zero filled
alphaLine = alphaLine_initial;
}
if (blkFlags != blkFlags_initial) {
rdrCtx.putIntArray(blkFlags, 0, 0); // already zero filled
blkFlags = blkFlags_initial;
}
if (edgeMinY != Float.POSITIVE_INFINITY) {
// clear used part
if (edgeBuckets == edgeBuckets_initial) {
// fill only used part
IntArrayCache.fill(edgeBuckets, buckets_minY,
buckets_maxY, 0);
IntArrayCache.fill(edgeBucketCounts, buckets_minY,
buckets_maxY + 1, 0);
} else {
// clear only used part
rdrCtx.putIntArray(edgeBuckets, buckets_minY,
buckets_maxY);
edgeBuckets = edgeBuckets_initial;
rdrCtx.putIntArray(edgeBucketCounts, buckets_minY,
buckets_maxY + 1);
edgeBucketCounts = edgeBucketCounts_initial;
}
} else if (edgeBuckets != edgeBuckets_initial) {
// unused arrays
rdrCtx.putIntArray(edgeBuckets, 0, 0);
edgeBuckets = edgeBuckets_initial;
rdrCtx.putIntArray(edgeBucketCounts, 0, 0);
edgeBucketCounts = edgeBucketCounts_initial;
}
// At last: resize back off-heap edges to initial size
if (edges.length != INITIAL_EDGES_CAPACITY) {
// note: may throw OOME:
edges.resize(INITIAL_EDGES_CAPACITY);
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
edges.fill(BYTE_0);
}
if (doMonitors) {
RendererContext.stats.mon_rdr_endRendering.stop();
}
}
private static float tosubpixx(final float pix_x) {
return f_SUBPIXEL_POSITIONS_X * pix_x;
}
private static float tosubpixy(final float pix_y) {
// shift y by -0.5 for fast ceil(y - 0.5):
return f_SUBPIXEL_POSITIONS_Y * pix_y - 0.5f;
}
@Override
public void moveTo(float pix_x0, float pix_y0) {
closePath();
this.pix_sx0 = pix_x0;
this.pix_sy0 = pix_y0;
this.y0 = tosubpixy(pix_y0);
this.x0 = tosubpixx(pix_x0);
}
@Override
public void lineTo(float pix_x1, float pix_y1) {
float x1 = tosubpixx(pix_x1);
float y1 = tosubpixy(pix_y1);
addLine(x0, y0, x1, y1);
x0 = x1;
y0 = y1;
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
final float xe = tosubpixx(x3);
final float ye = tosubpixy(y3);
curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
tosubpixx(x2), tosubpixy(y2), xe, ye);
curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
}
@Override
public void quadTo(float x1, float y1, float x2, float y2) {
final float xe = tosubpixx(x2);
final float ye = tosubpixy(y2);
curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
}
@Override
public void closePath() {
// lineTo expects its input in pixel coordinates.
lineTo(pix_sx0, pix_sy0);
}
@Override
public void pathDone() {
closePath();
}
@Override
public long getNativeConsumer() {
throw new InternalError("Renderer does not use a native consumer.");
}
// clean alpha array (zero filled)
private int[] alphaLine;
// 2048 (pixelsize) pixel large
private final int[] alphaLine_initial = new int[INITIAL_AA_ARRAY]; // 8K
private void _endRendering(final int ymin, final int ymax) {
if (DISABLE_RENDER) {
return;
}
// Get X bounds as true pixel boundaries to compute correct pixel coverage:
final int bboxx0 = bbox_spminX;
final int bboxx1 = bbox_spmaxX;
final boolean windingRuleEvenOdd = (windingRule == WIND_EVEN_ODD);
// Useful when processing tile line by tile line
final int[] _alpha = alphaLine;
// local vars (performance):
final MarlinCache _cache = cache;
final OffHeapArray _edges = edges;
final int[] _edgeBuckets = edgeBuckets;
final int[] _edgeBucketCounts = edgeBucketCounts;
int[] _crossings = this.crossings;
int[] _edgePtrs = this.edgePtrs;
// merge sort auxiliary storage:
int[] _aux_crossings = this.aux_crossings;
int[] _aux_edgePtrs = this.aux_edgePtrs;
// copy constants:
final long _OFF_ERROR = OFF_ERROR;
final long _OFF_BUMP_X = OFF_BUMP_X;
final long _OFF_BUMP_ERR = OFF_BUMP_ERR;
final long _OFF_NEXT = OFF_NEXT;
final long _OFF_YMAX = OFF_YMAX;
final int _ALL_BUT_LSB = ALL_BUT_LSB;
final int _ERR_STEP_MAX = ERR_STEP_MAX;
// unsafe I/O:
final Unsafe _unsafe = OffHeapArray.unsafe;
final long addr0 = _edges.address;
long addr;
final int _SUBPIXEL_LG_POSITIONS_X = SUBPIXEL_LG_POSITIONS_X;
final int _SUBPIXEL_LG_POSITIONS_Y = SUBPIXEL_LG_POSITIONS_Y;
final int _SUBPIXEL_MASK_X = SUBPIXEL_MASK_X;
final int _SUBPIXEL_MASK_Y = SUBPIXEL_MASK_Y;
final int _SUBPIXEL_POSITIONS_X = SUBPIXEL_POSITIONS_X;
final int _MIN_VALUE = Integer.MIN_VALUE;
final int _MAX_VALUE = Integer.MAX_VALUE;
// Now we iterate through the scanlines. We must tell emitRow the coord
// of the first non-transparent pixel, so we must keep accumulators for
// the first and last pixels of the section of the current pixel row
// that we will emit.
// We also need to accumulate pix_bbox, but the iterator does it
// for us. We will just get the values from it once this loop is done
int minX = _MAX_VALUE;
int maxX = _MIN_VALUE;
int y = ymin;
int bucket = y - boundsMinY;
int numCrossings = this.edgeCount;
int edgePtrsLen = _edgePtrs.length;
int crossingsLen = _crossings.length;
int _arrayMaxUsed = activeEdgeMaxUsed;
int ptrLen = 0, newCount, ptrEnd;
int bucketcount, i, j, ecur;
int cross, lastCross;
int x0, x1, tmp, sum, prev, curx, curxo, crorientation, err;
int pix_x, pix_xmaxm1, pix_xmax;
int low, high, mid, prevNumCrossings;
boolean useBinarySearch;
final int[] _blkFlags = blkFlags;
final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
final int _BLK_SIZE = BLOCK_SIZE;
final boolean _enableBlkFlagsHeuristics = ENABLE_BLOCK_FLAGS_HEURISTICS && this.enableBlkFlags;
// Use block flags if large pixel span and few crossings:
// ie mean(distance between crossings) is high
boolean useBlkFlags = this.prevUseBlkFlags;
final int stroking = rdrCtx.stroking;
int lastY = -1; // last emited row
// Iteration on scanlines
for (; y < ymax; y++, bucket++) {
// --- from former ScanLineIterator.next()
bucketcount = _edgeBucketCounts[bucket];
// marker on previously sorted edges:
prevNumCrossings = numCrossings;
// bucketCount indicates new edge / edge end:
if (bucketcount != 0) {
if (doStats) {
RendererContext.stats.stat_rdr_activeEdges_updates
.add(numCrossings);
}
// last bit set to 1 means that edges ends
if ((bucketcount & 0x1) != 0) {
// eviction in active edge list
// cache edges[] address + offset
addr = addr0 + _OFF_YMAX;
for (i = 0, newCount = 0; i < numCrossings; i++) {
// get the pointer to the edge
ecur = _edgePtrs[i];
// random access so use unsafe:
if (_unsafe.getInt(addr + ecur) > y) {
_edgePtrs[newCount++] = ecur;
}
}
// update marker on sorted edges minus removed edges:
prevNumCrossings = numCrossings = newCount;
}
ptrLen = bucketcount >> 1; // number of new edge
if (ptrLen != 0) {
if (doStats) {
RendererContext.stats.stat_rdr_activeEdges_adds
.add(ptrLen);
if (ptrLen > 10) {
RendererContext.stats.stat_rdr_activeEdges_adds_high
.add(ptrLen);
}
}
ptrEnd = numCrossings + ptrLen;
if (edgePtrsLen < ptrEnd) {
if (doStats) {
RendererContext.stats.stat_array_renderer_edgePtrs
.add(ptrEnd);
}
this.edgePtrs = _edgePtrs
= rdrCtx.widenDirtyIntArray(_edgePtrs, numCrossings,
ptrEnd);
edgePtrsLen = _edgePtrs.length;
// Get larger auxiliary storage:
if (_aux_edgePtrs != aux_edgePtrs_initial) {
rdrCtx.putDirtyIntArray(_aux_edgePtrs);
}
// use ArrayCache.getNewSize() to use the same growing
// factor than widenDirtyIntArray():
if (doStats) {
RendererContext.stats.stat_array_renderer_aux_edgePtrs
.add(ptrEnd);
}
this.aux_edgePtrs = _aux_edgePtrs
= rdrCtx.getDirtyIntArray(
ArrayCache.getNewSize(numCrossings, ptrEnd)
);
}
// cache edges[] address + offset
addr = addr0 + _OFF_NEXT;
// add new edges to active edge list:
for (ecur = _edgeBuckets[bucket];
numCrossings < ptrEnd; numCrossings++)
{
// store the pointer to the edge
_edgePtrs[numCrossings] = ecur;
// random access so use unsafe:
ecur = _unsafe.getInt(addr + ecur);
}
if (crossingsLen < numCrossings) {
// Get larger array:
if (_crossings != crossings_initial) {
rdrCtx.putDirtyIntArray(_crossings);
}
if (doStats) {
RendererContext.stats.stat_array_renderer_crossings
.add(numCrossings);
}
this.crossings = _crossings
= rdrCtx.getDirtyIntArray(numCrossings);
// Get larger auxiliary storage:
if (_aux_crossings != aux_crossings_initial) {
rdrCtx.putDirtyIntArray(_aux_crossings);
}
if (doStats) {
RendererContext.stats.stat_array_renderer_aux_crossings
.add(numCrossings);
}
this.aux_crossings = _aux_crossings
= rdrCtx.getDirtyIntArray(numCrossings);
crossingsLen = _crossings.length;
}
if (doStats) {
// update max used mark
if (numCrossings > _arrayMaxUsed) {
_arrayMaxUsed = numCrossings;
}
}
} // ptrLen != 0
} // bucketCount != 0
if (numCrossings != 0) {
/*
* thresholds to switch to optimized merge sort
* for newly added edges + final merge pass.
*/
if ((ptrLen < 10) || (numCrossings < 40)) {
if (doStats) {
RendererContext.stats.hist_rdr_crossings
.add(numCrossings);
RendererContext.stats.hist_rdr_crossings_adds
.add(ptrLen);
}
/*
* threshold to use binary insertion sort instead of
* straight insertion sort (to reduce minimize comparisons).
*/
useBinarySearch = (numCrossings >= 20);
// if small enough:
lastCross = _MIN_VALUE;
for (i = 0; i < numCrossings; i++) {
// get the pointer to the edge
ecur = _edgePtrs[i];
/* convert subpixel coordinates (float) into pixel
positions (int) for coming scanline */
/* note: it is faster to always update edges even
if it is removed from AEL for coming or last scanline */
// random access so use unsafe:
addr = addr0 + ecur; // ecur + OFF_F_CURX
// get current crossing:
curx = _unsafe.getInt(addr);
// update crossing with orientation at last bit:
cross = curx;
// Increment x using DDA (fixed point):
curx += _unsafe.getInt(addr + _OFF_BUMP_X);
// Increment error:
err = _unsafe.getInt(addr + _OFF_ERROR)
+ _unsafe.getInt(addr + _OFF_BUMP_ERR);
// Manual carry handling:
// keep sign and carry bit only and ignore last bit (preserve orientation):
_unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));
_unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
if (doStats) {
RendererContext.stats.stat_rdr_crossings_updates
.add(numCrossings);
}
// insertion sort of crossings:
if (cross < lastCross) {
if (doStats) {
RendererContext.stats.stat_rdr_crossings_sorts
.add(i);
}
/* use binary search for newly added edges
in crossings if arrays are large enough */
if (useBinarySearch && (i >= prevNumCrossings)) {
if (doStats) {
RendererContext.stats.
stat_rdr_crossings_bsearch.add(i);
}
low = 0;
high = i - 1;
do {
// note: use signed shift (not >>>) for performance
// as indices are small enough to exceed Integer.MAX_VALUE
mid = (low + high) >> 1;
if (_crossings[mid] < cross) {
low = mid + 1;
} else {
high = mid - 1;
}
} while (low <= high);
for (j = i - 1; j >= low; j--) {
_crossings[j + 1] = _crossings[j];
_edgePtrs [j + 1] = _edgePtrs[j];
}
_crossings[low] = cross;
_edgePtrs [low] = ecur;
} else {
j = i - 1;
_crossings[i] = _crossings[j];
_edgePtrs[i] = _edgePtrs[j];
while ((--j >= 0) && (_crossings[j] > cross)) {
_crossings[j + 1] = _crossings[j];
_edgePtrs [j + 1] = _edgePtrs[j];
}
_crossings[j + 1] = cross;
_edgePtrs [j + 1] = ecur;
}
} else {
_crossings[i] = lastCross = cross;
}
}
} else {
if (doStats) {
RendererContext.stats.stat_rdr_crossings_msorts
.add(numCrossings);
RendererContext.stats.hist_rdr_crossings_ratio
.add((1000 * ptrLen) / numCrossings);
RendererContext.stats.hist_rdr_crossings_msorts
.add(numCrossings);
RendererContext.stats.hist_rdr_crossings_msorts_adds
.add(ptrLen);
}
// Copy sorted data in auxiliary arrays
// and perform insertion sort on almost sorted data
// (ie i < prevNumCrossings):
lastCross = _MIN_VALUE;
for (i = 0; i < numCrossings; i++) {
// get the pointer to the edge
ecur = _edgePtrs[i];
/* convert subpixel coordinates (float) into pixel
positions (int) for coming scanline */
/* note: it is faster to always update edges even
if it is removed from AEL for coming or last scanline */
// random access so use unsafe:
addr = addr0 + ecur; // ecur + OFF_F_CURX
// get current crossing:
curx = _unsafe.getInt(addr);
// update crossing with orientation at last bit:
cross = curx;
// Increment x using DDA (fixed point):
curx += _unsafe.getInt(addr + _OFF_BUMP_X);
// Increment error:
err = _unsafe.getInt(addr + _OFF_ERROR)
+ _unsafe.getInt(addr + _OFF_BUMP_ERR);
// Manual carry handling:
// keep sign and carry bit only and ignore last bit (preserve orientation):
_unsafe.putInt(addr, curx - ((err >> 30) & _ALL_BUT_LSB));
_unsafe.putInt(addr + _OFF_ERROR, (err & _ERR_STEP_MAX));
if (doStats) {
RendererContext.stats.stat_rdr_crossings_updates
.add(numCrossings);
}
if (i >= prevNumCrossings) {
// simply store crossing as edgePtrs is in-place:
// will be copied and sorted efficiently by mergesort later:
_crossings[i] = cross;
} else if (cross < lastCross) {
if (doStats) {
RendererContext.stats.stat_rdr_crossings_sorts
.add(i);
}
// (straight) insertion sort of crossings:
j = i - 1;
_aux_crossings[i] = _aux_crossings[j];
_aux_edgePtrs[i] = _aux_edgePtrs[j];
while ((--j >= 0) && (_aux_crossings[j] > cross)) {
_aux_crossings[j + 1] = _aux_crossings[j];
_aux_edgePtrs [j + 1] = _aux_edgePtrs[j];
}
_aux_crossings[j + 1] = cross;
_aux_edgePtrs [j + 1] = ecur;
} else {
// auxiliary storage:
_aux_crossings[i] = lastCross = cross;
_aux_edgePtrs [i] = ecur;
}
}
// use Mergesort using auxiliary arrays (sort only right part)
MergeSort.mergeSortNoCopy(_crossings, _edgePtrs,
_aux_crossings, _aux_edgePtrs,
numCrossings, prevNumCrossings);
}
// reset ptrLen
ptrLen = 0;
// --- from former ScanLineIterator.next()
/* note: bboxx0 and bboxx1 must be pixel boundaries
to have correct coverage computation */
// right shift on crossings to get the x-coordinate:
curxo = _crossings[0];
x0 = curxo >> 1;
if (x0 < minX) {
minX = x0; // subpixel coordinate
}
x1 = _crossings[numCrossings - 1] >> 1;
if (x1 > maxX) {
maxX = x1; // subpixel coordinate
}
// compute pixel coverages
prev = curx = x0;
// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
// last bit contains orientation (0 or 1)
crorientation = ((curxo & 0x1) << 1) - 1;
if (windingRuleEvenOdd) {
sum = crorientation;
// Even Odd winding rule: take care of mask ie sum(orientations)
for (i = 1; i < numCrossings; i++) {
curxo = _crossings[i];
curx = curxo >> 1;
// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
// last bit contains orientation (0 or 1)
crorientation = ((curxo & 0x1) << 1) - 1;
if ((sum & 0x1) != 0) {
// TODO: perform line clipping on left-right sides
// to avoid such bound checks:
x0 = (prev > bboxx0) ? prev : bboxx0;
x1 = (curx < bboxx1) ? curx : bboxx1;
if (x0 < x1) {
x0 -= bboxx0; // turn x0, x1 from coords to indices
x1 -= bboxx0; // in the alpha array.
pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;
pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
if (pix_x == pix_xmaxm1) {
// Start and end in same pixel
tmp = (x1 - x0); // number of subpixels
_alpha[pix_x ] += tmp;
_alpha[pix_x + 1] -= tmp;
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
}
} else {
tmp = (x0 & _SUBPIXEL_MASK_X);
_alpha[pix_x ]
+= (_SUBPIXEL_POSITIONS_X - tmp);
_alpha[pix_x + 1]
+= tmp;
pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
tmp = (x1 & _SUBPIXEL_MASK_X);
_alpha[pix_xmax ]
-= (_SUBPIXEL_POSITIONS_X - tmp);
_alpha[pix_xmax + 1]
-= tmp;
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
}
}
}
}
sum += crorientation;
prev = curx;
}
} else {
// Non-zero winding rule: optimize that case (default)
// and avoid processing intermediate crossings
for (i = 1, sum = 0;; i++) {
sum += crorientation;
if (sum != 0) {
// prev = min(curx)
if (prev > curx) {
prev = curx;
}
} else {
// TODO: perform line clipping on left-right sides
// to avoid such bound checks:
x0 = (prev > bboxx0) ? prev : bboxx0;
x1 = (curx < bboxx1) ? curx : bboxx1;
if (x0 < x1) {
x0 -= bboxx0; // turn x0, x1 from coords to indices
x1 -= bboxx0; // in the alpha array.
pix_x = x0 >> _SUBPIXEL_LG_POSITIONS_X;
pix_xmaxm1 = (x1 - 1) >> _SUBPIXEL_LG_POSITIONS_X;
if (pix_x == pix_xmaxm1) {
// Start and end in same pixel
tmp = (x1 - x0); // number of subpixels
_alpha[pix_x ] += tmp;
_alpha[pix_x + 1] -= tmp;
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
}
} else {
tmp = (x0 & _SUBPIXEL_MASK_X);
_alpha[pix_x ]
+= (_SUBPIXEL_POSITIONS_X - tmp);
_alpha[pix_x + 1]
+= tmp;
pix_xmax = x1 >> _SUBPIXEL_LG_POSITIONS_X;
tmp = (x1 & _SUBPIXEL_MASK_X);
_alpha[pix_xmax ]
-= (_SUBPIXEL_POSITIONS_X - tmp);
_alpha[pix_xmax + 1]
-= tmp;
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
}
}
}
prev = _MAX_VALUE;
}
if (i == numCrossings) {
break;
}
curxo = _crossings[i];
curx = curxo >> 1;
// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
// last bit contains orientation (0 or 1)
crorientation = ((curxo & 0x1) << 1) - 1;
}
}
} // numCrossings > 0
// even if this last row had no crossings, alpha will be zeroed
// from the last emitRow call. But this doesn't matter because
// maxX < minX, so no row will be emitted to the MarlinCache.
if ((y & _SUBPIXEL_MASK_Y) == _SUBPIXEL_MASK_Y) {
lastY = y >> _SUBPIXEL_LG_POSITIONS_Y;
// convert subpixel to pixel coordinate within boundaries:
minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
if (maxX >= minX) {
// note: alpha array will be zeroed by copyAARow()
// +2 because alpha [pix_minX; pix_maxX+1]
// fix range [x0; x1[
copyAARow(_alpha, lastY, minX, maxX + 2, useBlkFlags);
// speculative for next pixel row (scanline coherence):
if (_enableBlkFlagsHeuristics) {
// Use block flags if large pixel span and few crossings:
// ie mean(distance between crossings) is larger than
// 1 block size;
// fast check width:
maxX -= minX;
// if stroking: numCrossings /= 2
// => shift numCrossings by 1
// condition = (width / (numCrossings - 1)) > blockSize
useBlkFlags = (maxX > _BLK_SIZE) && (maxX >
(((numCrossings >> stroking) - 1) << _BLK_SIZE_LG));
if (doStats) {
tmp = FloatMath.max(1,
((numCrossings >> stroking) - 1));
RendererContext.stats.hist_tile_generator_encoding_dist
.add(maxX / tmp);
}
}
} else {
_cache.clearAARow(lastY);
}
minX = _MAX_VALUE;
maxX = _MIN_VALUE;
}
} // scan line iterator
// Emit final row
y--;
y >>= _SUBPIXEL_LG_POSITIONS_Y;
// convert subpixel to pixel coordinate within boundaries:
minX = FloatMath.max(minX, bboxx0) >> _SUBPIXEL_LG_POSITIONS_X;
maxX = FloatMath.min(maxX, bboxx1) >> _SUBPIXEL_LG_POSITIONS_X;
if (maxX >= minX) {
// note: alpha array will be zeroed by copyAARow()
// +2 because alpha [pix_minX; pix_maxX+1]
// fix range [x0; x1[
copyAARow(_alpha, y, minX, maxX + 2, useBlkFlags);
} else if (y != lastY) {
_cache.clearAARow(y);
}
// update member:
edgeCount = numCrossings;
prevUseBlkFlags = useBlkFlags;
if (doStats) {
// update max used mark
activeEdgeMaxUsed = _arrayMaxUsed;
}
}
boolean endRendering() {
if (doMonitors) {
RendererContext.stats.mon_rdr_endRendering.start();
}
if (edgeMinY == Float.POSITIVE_INFINITY) {
return false; // undefined edges bounds
}
final int _boundsMinY = boundsMinY;
final int _boundsMaxY = boundsMaxY;
// bounds as inclusive intervals
final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5f), boundsMinX);
final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5f), boundsMaxX - 1);
// y1 (and y2) are already biased by -0.5 in tosubpixy():
final int spminY = FloatMath.max(FloatMath.ceil_int(edgeMinY), _boundsMinY);
int maxY = FloatMath.ceil_int(edgeMaxY);
final int spmaxY;
if (maxY <= _boundsMaxY - 1) {
spmaxY = maxY;
} else {
spmaxY = _boundsMaxY - 1;
maxY = _boundsMaxY;
}
buckets_minY = spminY - _boundsMinY;
buckets_maxY = maxY - _boundsMinY;
if (doLogBounds) {
MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX
+ "][" + edgeMinY + " ... " + edgeMaxY + "]");
MarlinUtils.logInfo("spXY = [" + spminX + " ... " + spmaxX
+ "][" + spminY + " ... " + spmaxY + "]");
}
// test clipping for shapes out of bounds
if ((spminX > spmaxX) || (spminY > spmaxY)) {
return false;
}
// half open intervals
// inclusive:
final int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
// exclusive:
final int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
// inclusive:
final int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
// exclusive:
final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
// store BBox to answer ptg.getBBox():
this.cache.init(pminX, pminY, pmaxX, pmaxY, edgeSumDeltaY);
// Heuristics for using block flags:
if (ENABLE_BLOCK_FLAGS) {
enableBlkFlags = this.cache.useRLE;
prevUseBlkFlags = enableBlkFlags && !ENABLE_BLOCK_FLAGS_HEURISTICS;
if (enableBlkFlags) {
// ensure blockFlags array is large enough:
// note: +2 to ensure enough space left at end
final int nxTiles = ((pmaxX - pminX) >> TILE_SIZE_LG) + 2;
if (nxTiles > INITIAL_ARRAY) {
blkFlags = rdrCtx.getIntArray(nxTiles);
}
}
}
// memorize the rendering bounding box:
/* note: bbox_spminX and bbox_spmaxX must be pixel boundaries
to have correct coverage computation */
// inclusive:
bbox_spminX = pminX << SUBPIXEL_LG_POSITIONS_X;
// exclusive:
bbox_spmaxX = pmaxX << SUBPIXEL_LG_POSITIONS_X;
// inclusive:
bbox_spminY = spminY;
// exclusive:
bbox_spmaxY = FloatMath.min(spmaxY + 1, pmaxY << SUBPIXEL_LG_POSITIONS_Y);
if (doLogBounds) {
MarlinUtils.logInfo("pXY = [" + pminX + " ... " + pmaxX
+ "[ [" + pminY + " ... " + pmaxY + "[");
MarlinUtils.logInfo("bbox_spXY = [" + bbox_spminX + " ... "
+ bbox_spmaxX + "[ [" + bbox_spminY + " ... "
+ bbox_spmaxY + "[");
}
// Prepare alpha line:
// add 2 to better deal with the last pixel in a pixel row.
final int width = (pmaxX - pminX) + 2;
// Useful when processing tile line by tile line
if (width > INITIAL_AA_ARRAY) {
if (doStats) {
RendererContext.stats.stat_array_renderer_alphaline
.add(width);
}
alphaLine = rdrCtx.getIntArray(width);
}
// process first tile line:
endRendering(pminY);
return true;
}
private int bbox_spminX, bbox_spmaxX, bbox_spminY, bbox_spmaxY;
void endRendering(final int pminY) {
if (doMonitors) {
RendererContext.stats.mon_rdr_endRendering_Y.start();
}
final int spminY = pminY << SUBPIXEL_LG_POSITIONS_Y;
final int fixed_spminY = FloatMath.max(bbox_spminY, spminY);
// avoid rendering for last call to nextTile()
if (fixed_spminY < bbox_spmaxY) {
// process a complete tile line ie scanlines for 32 rows
final int spmaxY = FloatMath.min(bbox_spmaxY, spminY + SUBPIXEL_TILE);
// process tile line [0 - 32]
cache.resetTileLine(pminY);
// Process only one tile line:
_endRendering(fixed_spminY, spmaxY);
}
if (doMonitors) {
RendererContext.stats.mon_rdr_endRendering_Y.stop();
}
}
private boolean enableBlkFlags = false;
private boolean prevUseBlkFlags = false;
private final int[] blkFlags_initial = new int[INITIAL_ARRAY]; // 1 tile line
/* block flags (0|1) */
private int[] blkFlags = blkFlags_initial;
void copyAARow(final int[] alphaRow,
final int pix_y, final int pix_from, final int pix_to,
final boolean useBlockFlags)
{
if (useBlockFlags) {
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding.add(1);
}
cache.copyAARowRLE_WithBlockFlags(blkFlags, alphaRow, pix_y, pix_from, pix_to);
} else {
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding.add(0);
}
cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to);
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.awt.geom.Path2D;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import static sun.java2d.marlin.ArrayCache.*;
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
import static sun.java2d.marlin.MarlinUtils.getCallerInfo;
import static sun.java2d.marlin.MarlinUtils.logInfo;
/**
* This class is a renderer context dedicated to a single thread
*/
final class RendererContext implements MarlinConst {
private static final String className = RendererContext.class.getName();
// RendererContext creation counter
private static final AtomicInteger contextCount = new AtomicInteger(1);
// RendererContext statistics
static final RendererStats stats = (doStats || doMonitors)
? RendererStats.getInstance(): null;
private static final boolean USE_CACHE_HARD_REF = doStats
|| (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
/**
* Create a new renderer context
*
* @return new RendererContext instance
*/
static RendererContext createContext() {
final RendererContext newCtx = new RendererContext("ctx"
+ Integer.toString(contextCount.getAndIncrement()));
if (RendererContext.stats != null) {
RendererContext.stats.allContexts.add(newCtx);
}
return newCtx;
}
// context name (debugging purposes)
final String name;
/*
* Reference to this instance (hard, soft or weak).
* @see MarlinRenderingEngine#REF_TYPE
*/
final Object reference;
// dirty flag indicating an exception occured during pipeline in pathTo()
boolean dirty = false;
// dynamic array caches kept using weak reference (low memory footprint)
WeakReference<ArrayCachesHolder> refArrayCaches = null;
// hard reference to array caches (for statistics)
ArrayCachesHolder hardRefArrayCaches = null;
// shared data
final float[] float6 = new float[6];
// shared curve (dirty) (Renderer / Stroker)
final Curve curve = new Curve();
// MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
final NormalizingPathIterator nPCPathIterator;
// MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
final NormalizingPathIterator nPQPathIterator;
// MarlinRenderingEngine.TransformingPathConsumer2D
final TransformingPathConsumer2D transformerPC2D;
// recycled Path2D instance
Path2D.Float p2d = null;
final Renderer renderer;
final Stroker stroker;
// Simplifies out collinear lines
final CollinearSimplifier simplifier = new CollinearSimplifier();
final Dasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
/**
* Constructor
*
* @param name
*/
RendererContext(final String name) {
if (logCreateContext) {
MarlinUtils.logInfo("new RendererContext = " + name);
}
this.name = name;
// NormalizingPathIterator instances:
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6);
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new TransformingPathConsumer2D();
// Renderer:
cache = new MarlinCache(this);
renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache
ptg = new MarlinTileGenerator(renderer);
stroker = new Stroker(this);
dasher = new Dasher(this);
// Create the reference to this instance (hard, soft or weak):
switch (MarlinRenderingEngine.REF_TYPE) {
default:
case MarlinRenderingEngine.REF_HARD:
reference = this;
break;
case MarlinRenderingEngine.REF_SOFT:
reference = new SoftReference<RendererContext>(this);
break;
case MarlinRenderingEngine.REF_WEAK:
reference = new WeakReference<RendererContext>(this);
break;
}
}
/**
* Disposes this renderer context:
* clean up before reusing this context
*/
void dispose() {
stroking = 0;
// reset hard reference to array caches if needed:
if (!USE_CACHE_HARD_REF) {
hardRefArrayCaches = null;
}
// if context is maked as DIRTY:
if (dirty) {
// may happen if an exception if thrown in the pipeline processing:
// force cleanup of all possible pipelined blocks (except Renderer):
// NormalizingPathIterator instances:
this.nPCPathIterator.dispose();
this.nPQPathIterator.dispose();
// Dasher:
this.dasher.dispose();
// Stroker:
this.stroker.dispose();
// mark context as CLEAN:
dirty = false;
}
}
// Array caches
ArrayCachesHolder getArrayCachesHolder() {
// Use hard reference first (cached resolved weak reference):
ArrayCachesHolder holder = hardRefArrayCaches;
if (holder == null) {
// resolve reference:
holder = (refArrayCaches != null)
? refArrayCaches.get()
: null;
// create a new ArrayCachesHolder if none is available
if (holder == null) {
if (logCreateContext) {
MarlinUtils.logInfo("new ArrayCachesHolder for "
+ "RendererContext = " + name);
}
holder = new ArrayCachesHolder();
if (USE_CACHE_HARD_REF) {
// update hard reference:
hardRefArrayCaches = holder;
}
// update weak reference:
refArrayCaches = new WeakReference<ArrayCachesHolder>(holder);
}
}
return holder;
}
// dirty byte array cache
ByteArrayCache getDirtyByteArrayCache(final int length) {
final int bucket = ArrayCache.getBucketDirtyBytes(length);
return getArrayCachesHolder().dirtyByteArrayCaches[bucket];
}
byte[] getDirtyByteArray(final int length) {
if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) {
return getDirtyByteArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyByteArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new byte[length];
}
void putDirtyByteArray(final byte[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) {
getDirtyByteArrayCache(length).putDirtyArray(array, length);
}
}
byte[] widenDirtyByteArray(final byte[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyByte();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyByteArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
// int array cache
IntArrayCache getIntArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().intArrayCaches[bucket];
}
int[] getIntArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getIntArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t"
+ getCallerInfo(className));
}
return new int[length];
}
// unused
int[] widenIntArray(final int[] in, final int usedSize,
final int needSize, final int clearTo)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeInt();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final int[] res = getIntArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo)
if (doLogWidenArray) {
logInfo("widenIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putIntArray(final int[] array, final int fromIndex,
final int toIndex)
{
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getIntArrayCache(length).putArray(array, length, fromIndex, toIndex);
}
}
// dirty int array cache
IntArrayCache getDirtyIntArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().dirtyIntArrayCaches[bucket];
}
int[] getDirtyIntArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getDirtyIntArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyIntArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new int[length];
}
int[] widenDirtyIntArray(final int[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyInt();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyIntArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putDirtyIntArray(final int[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getDirtyIntArrayCache(length).putDirtyArray(array, length);
}
}
// dirty float array cache
FloatArrayCache getDirtyFloatArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().dirtyFloatArrayCaches[bucket];
}
float[] getDirtyFloatArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getDirtyFloatArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyFloatArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new float[length];
}
float[] widenDirtyFloatArray(final float[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyFloat();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyFloatArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putDirtyFloatArray(final float[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getDirtyFloatArrayCache(length).putDirtyArray(array, length);
}
}
/* class holding all array cache instances */
static final class ArrayCachesHolder {
// zero-filled int array cache:
final IntArrayCache[] intArrayCaches;
// dirty array caches:
final IntArrayCache[] dirtyIntArrayCaches;
final FloatArrayCache[] dirtyFloatArrayCaches;
final ByteArrayCache[] dirtyByteArrayCaches;
ArrayCachesHolder() {
intArrayCaches = new IntArrayCache[BUCKETS];
dirtyIntArrayCaches = new IntArrayCache[BUCKETS];
dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS];
dirtyByteArrayCaches = new ByteArrayCache[BUCKETS];
for (int i = 0; i < BUCKETS; i++) {
intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
// dirty array caches:
dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]);
dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]);
}
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.java2d.marlin.stats.Histogram;
import sun.java2d.marlin.stats.Monitor;
import sun.java2d.marlin.stats.StatLong;
/**
* This class gathers global rendering statistics for debugging purposes only
*/
public final class RendererStats implements MarlinConst {
// singleton
private static volatile RendererStats singleton = null;
static RendererStats getInstance() {
if (singleton == null) {
singleton = new RendererStats();
}
return singleton;
}
public static void dumpStats() {
if (singleton != null) {
singleton.dump();
}
}
/* RendererContext collection as hard references
(only used for debugging purposes) */
final ConcurrentLinkedQueue<RendererContext> allContexts
= new ConcurrentLinkedQueue<RendererContext>();
// stats
final StatLong stat_cache_rowAA
= new StatLong("cache.rowAA");
final StatLong stat_cache_rowAAChunk
= new StatLong("cache.rowAAChunk");
final StatLong stat_cache_tiles
= new StatLong("cache.tiles");
final StatLong stat_rdr_poly_stack_curves
= new StatLong("renderer.poly.stack.curves");
final StatLong stat_rdr_poly_stack_types
= new StatLong("renderer.poly.stack.types");
final StatLong stat_rdr_addLine
= new StatLong("renderer.addLine");
final StatLong stat_rdr_addLine_skip
= new StatLong("renderer.addLine.skip");
final StatLong stat_rdr_curveBreak
= new StatLong("renderer.curveBreakIntoLinesAndAdd");
final StatLong stat_rdr_curveBreak_dec
= new StatLong("renderer.curveBreakIntoLinesAndAdd.dec");
final StatLong stat_rdr_curveBreak_inc
= new StatLong("renderer.curveBreakIntoLinesAndAdd.inc");
final StatLong stat_rdr_quadBreak
= new StatLong("renderer.quadBreakIntoLinesAndAdd");
final StatLong stat_rdr_quadBreak_dec
= new StatLong("renderer.quadBreakIntoLinesAndAdd.dec");
final StatLong stat_rdr_edges
= new StatLong("renderer.edges");
final StatLong stat_rdr_edges_count
= new StatLong("renderer.edges.count");
final StatLong stat_rdr_edges_resizes
= new StatLong("renderer.edges.resize");
final StatLong stat_rdr_activeEdges
= new StatLong("renderer.activeEdges");
final StatLong stat_rdr_activeEdges_updates
= new StatLong("renderer.activeEdges.updates");
final StatLong stat_rdr_activeEdges_adds
= new StatLong("renderer.activeEdges.adds");
final StatLong stat_rdr_activeEdges_adds_high
= new StatLong("renderer.activeEdges.adds_high");
final StatLong stat_rdr_crossings_updates
= new StatLong("renderer.crossings.updates");
final StatLong stat_rdr_crossings_sorts
= new StatLong("renderer.crossings.sorts");
final StatLong stat_rdr_crossings_bsearch
= new StatLong("renderer.crossings.bsearch");
final StatLong stat_rdr_crossings_msorts
= new StatLong("renderer.crossings.msorts");
// growable arrays
final StatLong stat_array_dasher_dasher
= new StatLong("array.dasher.dasher.d_float");
final StatLong stat_array_dasher_firstSegmentsBuffer
= new StatLong("array.dasher.firstSegmentsBuffer.d_float");
final StatLong stat_array_stroker_polystack_curves
= new StatLong("array.stroker.polystack.curves.d_float");
final StatLong stat_array_stroker_polystack_curveTypes
= new StatLong("array.stroker.polystack.curveTypes.d_byte");
final StatLong stat_array_marlincache_rowAAChunk
= new StatLong("array.marlincache.rowAAChunk.d_byte");
final StatLong stat_array_marlincache_touchedTile
= new StatLong("array.marlincache.touchedTile.int");
final StatLong stat_array_renderer_alphaline
= new StatLong("array.renderer.alphaline.int");
final StatLong stat_array_renderer_crossings
= new StatLong("array.renderer.crossings.int");
final StatLong stat_array_renderer_aux_crossings
= new StatLong("array.renderer.aux_crossings.int");
final StatLong stat_array_renderer_edgeBuckets
= new StatLong("array.renderer.edgeBuckets.int");
final StatLong stat_array_renderer_edgeBucketCounts
= new StatLong("array.renderer.edgeBucketCounts.int");
final StatLong stat_array_renderer_edgePtrs
= new StatLong("array.renderer.edgePtrs.int");
final StatLong stat_array_renderer_aux_edgePtrs
= new StatLong("array.renderer.aux_edgePtrs.int");
// histograms
final Histogram hist_rdr_crossings
= new Histogram("renderer.crossings");
final Histogram hist_rdr_crossings_ratio
= new Histogram("renderer.crossings.ratio");
final Histogram hist_rdr_crossings_adds
= new Histogram("renderer.crossings.adds");
final Histogram hist_rdr_crossings_msorts
= new Histogram("renderer.crossings.msorts");
final Histogram hist_rdr_crossings_msorts_adds
= new Histogram("renderer.crossings.msorts.adds");
final Histogram hist_tile_generator_alpha
= new Histogram("tile_generator.alpha");
final Histogram hist_tile_generator_encoding
= new Histogram("tile_generator.encoding");
final Histogram hist_tile_generator_encoding_dist
= new Histogram("tile_generator.encoding.dist");
final Histogram hist_tile_generator_encoding_ratio
= new Histogram("tile_generator.encoding.ratio");
final Histogram hist_tile_generator_encoding_runLen
= new Histogram("tile_generator.encoding.runLen");
// all stats
final StatLong[] statistics = new StatLong[]{
stat_cache_rowAA,
stat_cache_rowAAChunk,
stat_cache_tiles,
stat_rdr_poly_stack_types,
stat_rdr_poly_stack_curves,
stat_rdr_addLine,
stat_rdr_addLine_skip,
stat_rdr_curveBreak,
stat_rdr_curveBreak_dec,
stat_rdr_curveBreak_inc,
stat_rdr_quadBreak,
stat_rdr_quadBreak_dec,
stat_rdr_edges,
stat_rdr_edges_count,
stat_rdr_edges_resizes,
stat_rdr_activeEdges,
stat_rdr_activeEdges_updates,
stat_rdr_activeEdges_adds,
stat_rdr_activeEdges_adds_high,
stat_rdr_crossings_updates,
stat_rdr_crossings_sorts,
stat_rdr_crossings_bsearch,
stat_rdr_crossings_msorts,
hist_rdr_crossings,
hist_rdr_crossings_ratio,
hist_rdr_crossings_adds,
hist_rdr_crossings_msorts,
hist_rdr_crossings_msorts_adds,
hist_tile_generator_alpha,
hist_tile_generator_encoding,
hist_tile_generator_encoding_dist,
hist_tile_generator_encoding_ratio,
hist_tile_generator_encoding_runLen,
stat_array_dasher_dasher,
stat_array_dasher_firstSegmentsBuffer,
stat_array_stroker_polystack_curves,
stat_array_stroker_polystack_curveTypes,
stat_array_marlincache_rowAAChunk,
stat_array_marlincache_touchedTile,
stat_array_renderer_alphaline,
stat_array_renderer_crossings,
stat_array_renderer_aux_crossings,
stat_array_renderer_edgeBuckets,
stat_array_renderer_edgeBucketCounts,
stat_array_renderer_edgePtrs,
stat_array_renderer_aux_edgePtrs
};
// monitors
final Monitor mon_pre_getAATileGenerator
= new Monitor("MarlinRenderingEngine.getAATileGenerator()");
final Monitor mon_npi_currentSegment
= new Monitor("NormalizingPathIterator.currentSegment()");
final Monitor mon_rdr_addLine
= new Monitor("Renderer.addLine()");
final Monitor mon_rdr_endRendering
= new Monitor("Renderer.endRendering()");
final Monitor mon_rdr_endRendering_Y
= new Monitor("Renderer._endRendering(Y)");
final Monitor mon_rdr_copyAARow
= new Monitor("Renderer.copyAARow()");
final Monitor mon_pipe_renderTiles
= new Monitor("AAShapePipe.renderTiles()");
final Monitor mon_ptg_getAlpha
= new Monitor("MarlinTileGenerator.getAlpha()");
final Monitor mon_debug
= new Monitor("DEBUG()");
// all monitors
final Monitor[] monitors = new Monitor[]{
mon_pre_getAATileGenerator,
mon_npi_currentSegment,
mon_rdr_addLine,
mon_rdr_endRendering,
mon_rdr_endRendering_Y,
mon_rdr_copyAARow,
mon_pipe_renderTiles,
mon_ptg_getAlpha,
mon_debug
};
private RendererStats() {
super();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
dump();
}
});
if (useDumpThread) {
final Timer statTimer = new Timer("RendererStats");
statTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
dump();
}
}, statDump, statDump);
}
}
void dump() {
if (doStats) {
ArrayCache.dumpStats();
}
final RendererContext[] all = allContexts.toArray(
new RendererContext[allContexts.size()]);
for (RendererContext rdrCtx : all) {
logInfo("RendererContext: " + rdrCtx.name);
if (doMonitors) {
for (Monitor monitor : monitors) {
if (monitor.count != 0) {
logInfo(monitor.toString());
}
}
// As getAATileGenerator percents:
final long total = mon_pre_getAATileGenerator.sum;
if (total != 0L) {
for (Monitor monitor : monitors) {
logInfo(monitor.name + " : "
+ ((100d * monitor.sum) / total) + " %");
}
}
if (doFlushMonitors) {
for (Monitor m : monitors) {
m.reset();
}
}
}
if (doStats) {
for (StatLong stat : statistics) {
if (stat.count != 0) {
logInfo(stat.toString());
stat.reset();
}
}
// IntArrayCaches stats:
final RendererContext.ArrayCachesHolder holder
= rdrCtx.getArrayCachesHolder();
logInfo("Array caches for thread: " + rdrCtx.name);
for (IntArrayCache cache : holder.intArrayCaches) {
cache.dumpStats();
}
logInfo("Dirty Array caches for thread: " + rdrCtx.name);
for (IntArrayCache cache : holder.dirtyIntArrayCaches) {
cache.dumpStats();
}
for (FloatArrayCache cache : holder.dirtyFloatArrayCaches) {
cache.dumpStats();
}
for (ByteArrayCache cache : holder.dirtyByteArrayCaches) {
cache.dumpStats();
}
}
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import java.util.Arrays;
import static java.lang.Math.ulp;
import static java.lang.Math.sqrt;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.marlin.Curve.BreakPtrIterator;
// TODO: some of the arithmetic here is too verbose and prone to hard to
// debug typos. We should consider making a small Point/Vector class that
// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
final class Stroker implements PathConsumer2D, MarlinConst {
private static final int MOVE_TO = 0;
private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
private static final int CLOSE = 2;
/**
* Constant value for join style.
*/
public static final int JOIN_MITER = 0;
/**
* Constant value for join style.
*/
public static final int JOIN_ROUND = 1;
/**
* Constant value for join style.
*/
public static final int JOIN_BEVEL = 2;
/**
* Constant value for end cap style.
*/
public static final int CAP_BUTT = 0;
/**
* Constant value for end cap style.
*/
public static final int CAP_ROUND = 1;
/**
* Constant value for end cap style.
*/
public static final int CAP_SQUARE = 2;
// pisces used to use fixed point arithmetic with 16 decimal digits. I
// didn't want to change the values of the constant below when I converted
// it to floating point, so that's why the divisions by 2^16 are there.
private static final float ROUND_JOIN_THRESHOLD = 1000/65536f;
private static final float C = 0.5522847498307933f;
private static final int MAX_N_CURVES = 11;
private PathConsumer2D out;
private int capStyle;
private int joinStyle;
private float lineWidth2;
private final float[] offset0 = new float[2];
private final float[] offset1 = new float[2];
private final float[] offset2 = new float[2];
private final float[] miter = new float[2];
private float miterLimitSq;
private int prev;
// The starting point of the path, and the slope there.
private float sx0, sy0, sdx, sdy;
// the current point and the slope there.
private float cx0, cy0, cdx, cdy; // c stands for current
// vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the
// first and last points on the left parallel path. Since this path is
// parallel, it's slope at any point is parallel to the slope of the
// original path (thought they may have different directions), so these
// could be computed from sdx,sdy and cdx,cdy (and vice versa), but that
// would be error prone and hard to read, so we keep these anyway.
private float smx, smy, cmx, cmy;
private final PolyStack reverse;
// This is where the curve to be processed is put. We give it
// enough room to store 2 curves: one for the current subdivision, the
// other for the rest of the curve.
private final float[] middle = new float[2 * 8];
private final float[] lp = new float[8];
private final float[] rp = new float[8];
private final float[] subdivTs = new float[MAX_N_CURVES - 1];
// per-thread renderer context
final RendererContext rdrCtx;
// dirty curve
final Curve curve;
/**
* Constructs a <code>Stroker</code>.
* @param rdrCtx per-thread renderer context
*/
Stroker(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
this.reverse = new PolyStack(rdrCtx);
this.curve = rdrCtx.curve;
}
/**
* Inits the <code>Stroker</code>.
*
* @param pc2d an output <code>PathConsumer2D</code>.
* @param lineWidth the desired line width in pixels
* @param capStyle the desired end cap style, one of
* <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
* <code>CAP_SQUARE</code>.
* @param joinStyle the desired line join style, one of
* <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
* <code>JOIN_BEVEL</code>.
* @param miterLimit the desired miter limit
* @return this instance
*/
Stroker init(PathConsumer2D pc2d,
float lineWidth,
int capStyle,
int joinStyle,
float miterLimit)
{
this.out = pc2d;
this.lineWidth2 = lineWidth / 2f;
this.capStyle = capStyle;
this.joinStyle = joinStyle;
float limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;
this.prev = CLOSE;
rdrCtx.stroking = 1;
return this; // fluent API
}
/**
* Disposes this stroker:
* clean up before reusing this instance
*/
void dispose() {
reverse.dispose();
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0f);
Arrays.fill(offset1, 0f);
Arrays.fill(offset2, 0f);
Arrays.fill(miter, 0f);
Arrays.fill(middle, 0f);
Arrays.fill(lp, 0f);
Arrays.fill(rp, 0f);
Arrays.fill(subdivTs, 0f);
}
}
private static void computeOffset(final float lx, final float ly,
final float w, final float[] m)
{
float len = lx*lx + ly*ly;
if (len == 0f) {
m[0] = 0f;
m[1] = 0f;
} else {
len = (float) sqrt(len);
m[0] = (ly * w) / len;
m[1] = -(lx * w) / len;
}
}
// Returns true if the vectors (dx1, dy1) and (dx2, dy2) are
// clockwise (if dx1,dy1 needs to be rotated clockwise to close
// the smallest angle between it and dx2,dy2).
// This is equivalent to detecting whether a point q is on the right side
// of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and
// q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a
// clockwise order.
// NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left.
private static boolean isCW(final float dx1, final float dy1,
final float dx2, final float dy2)
{
return dx1 * dy2 <= dy1 * dx2;
}
private void drawRoundJoin(float x, float y,
float omx, float omy, float mx, float my,
boolean rev,
float threshold)
{
if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) {
return;
}
float domx = omx - mx;
float domy = omy - my;
float len = domx*domx + domy*domy;
if (len < threshold) {
return;
}
if (rev) {
omx = -omx;
omy = -omy;
mx = -mx;
my = -my;
}
drawRoundJoin(x, y, omx, omy, mx, my, rev);
}
private void drawRoundJoin(float cx, float cy,
float omx, float omy,
float mx, float my,
boolean rev)
{
// The sign of the dot product of mx,my and omx,omy is equal to the
// the sign of the cosine of ext
// (ext is the angle between omx,omy and mx,my).
double cosext = omx * mx + omy * my;
// If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
// need 1 curve to approximate the circle section that joins omx,omy
// and mx,my.
final int numCurves = cosext >= 0 ? 1 : 2;
switch (numCurves) {
case 1:
drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev);
break;
case 2:
// we need to split the arc into 2 arcs spanning the same angle.
// The point we want will be one of the 2 intersections of the
// perpendicular bisector of the chord (omx,omy)->(mx,my) and the
// circle. We could find this by scaling the vector
// (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies
// on the circle), but that can have numerical problems when the angle
// between omx,omy and mx,my is close to 180 degrees. So we compute a
// normal of (omx,omy)-(mx,my). This will be the direction of the
// perpendicular bisector. To get one of the intersections, we just scale
// this vector that its length is lineWidth2 (this works because the
// perpendicular bisector goes through the origin). This scaling doesn't
// have numerical problems because we know that lineWidth2 divided by
// this normal's length is at least 0.5 and at most sqrt(2)/2 (because
// we know the angle of the arc is > 90 degrees).
float nx = my - omy, ny = omx - mx;
float nlen = (float) sqrt(nx*nx + ny*ny);
float scale = lineWidth2/nlen;
float mmx = nx * scale, mmy = ny * scale;
// if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've
// computed the wrong intersection so we get the other one.
// The test above is equivalent to if (rev).
if (rev) {
mmx = -mmx;
mmy = -mmy;
}
drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev);
drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev);
break;
default:
}
}
// the input arc defined by omx,omy and mx,my must span <= 90 degrees.
private void drawBezApproxForArc(final float cx, final float cy,
final float omx, final float omy,
final float mx, final float my,
boolean rev)
{
float cosext2 = (omx * mx + omy * my) / (2f * lineWidth2 * lineWidth2);
// cv is the length of P1-P0 and P2-P3 divided by the radius of the arc
// (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that
// define the bezier curve we're computing.
// It is computed using the constraints that P1-P0 and P3-P2 are parallel
// to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
float cv = (float) ((4.0 / 3.0) * sqrt(0.5-cosext2) /
(1.0 + sqrt(cosext2+0.5)));
// if clockwise, we need to negate cv.
if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
cv = -cv;
}
final float x1 = cx + omx;
final float y1 = cy + omy;
final float x2 = x1 - cv * omy;
final float y2 = y1 + cv * omx;
final float x4 = cx + mx;
final float y4 = cy + my;
final float x3 = x4 + cv * my;
final float y3 = y4 - cv * mx;
emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev);
}
private void drawRoundCap(float cx, float cy, float mx, float my) {
// the first and second arguments of the following two calls
// are really will be ignored by emitCurveTo (because of the false),
// but we put them in anyway, as opposed to just giving it 4 zeroes,
// because it's just 4 additions and it's not good to rely on this
// sort of assumption (right now it's true, but that may change).
emitCurveTo(cx+mx-C*my, cy+my+C*mx,
cx-my+C*mx, cy+mx+C*my,
cx-my, cy+mx);
emitCurveTo(cx-my-C*mx, cy+mx-C*my,
cx-mx-C*my, cy-my+C*mx,
cx-mx, cy-my);
}
// Put the intersection point of the lines (x0, y0) -> (x1, y1)
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1].
// If the lines are parallel, it will put a non finite number in m.
private static void computeIntersection(final float x0, final float y0,
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
final float[] m, int off)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
float x10p = x1p - x0p;
float y10p = y1p - y0p;
float den = x10*y10p - x10p*y10;
float t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
m[off++] = x0 + t*x10;
m[off] = y0 + t*y10;
}
private void drawMiter(final float pdx, final float pdy,
final float x0, final float y0,
final float dx, final float dy,
float omx, float omy, float mx, float my,
boolean rev)
{
if ((mx == omx && my == omy) ||
(pdx == 0f && pdy == 0f) ||
(dx == 0f && dy == 0f))
{
return;
}
if (rev) {
omx = -omx;
omy = -omy;
mx = -mx;
my = -my;
}
computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
(dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
miter, 0);
final float miterX = miter[0];
final float miterY = miter[1];
float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
// If the lines are parallel, lenSq will be either NaN or +inf
// (actually, I'm not sure if the latter is possible. The important
// thing is that -inf is not possible, because lenSq is a square).
// For both of those values, the comparison below will fail and
// no miter will be drawn, which is correct.
if (lenSq < miterLimitSq) {
emitLineTo(miterX, miterY, rev);
}
}
@Override
public void moveTo(float x0, float y0) {
if (prev == DRAWING_OP_TO) {
finish();
}
this.sx0 = this.cx0 = x0;
this.sy0 = this.cy0 = y0;
this.cdx = this.sdx = 1;
this.cdy = this.sdy = 0;
this.prev = MOVE_TO;
}
@Override
public void lineTo(float x1, float y1) {
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0f && dy == 0f) {
dx = 1f;
}
computeOffset(dx, dy, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
emitLineTo(cx0 + mx, cy0 + my);
emitLineTo( x1 + mx, y1 + my);
emitLineToRev(cx0 - mx, cy0 - my);
emitLineToRev( x1 - mx, y1 - my);
this.cmx = mx;
this.cmy = my;
this.cdx = dx;
this.cdy = dy;
this.cx0 = x1;
this.cy0 = y1;
this.prev = DRAWING_OP_TO;
}
@Override
public void closePath() {
if (prev != DRAWING_OP_TO) {
if (prev == CLOSE) {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
this.cmx = this.smx = 0;
this.cmy = this.smy = -lineWidth2;
this.cdx = this.sdx = 1;
this.cdy = this.sdy = 0;
finish();
return;
}
if (cx0 != sx0 || cy0 != sy0) {
lineTo(sx0, sy0);
}
drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
emitLineTo(sx0 + smx, sy0 + smy);
emitMoveTo(sx0 - smx, sy0 - smy);
emitReverse();
this.prev = CLOSE;
emitClose();
}
private void emitReverse() {
reverse.popAll(out);
}
@Override
public void pathDone() {
if (prev == DRAWING_OP_TO) {
finish();
}
out.pathDone();
// this shouldn't matter since this object won't be used
// after the call to this method.
this.prev = CLOSE;
// Dispose this instance:
dispose();
}
private void finish() {
if (capStyle == CAP_ROUND) {
drawRoundCap(cx0, cy0, cmx, cmy);
} else if (capStyle == CAP_SQUARE) {
emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
}
emitReverse();
if (capStyle == CAP_ROUND) {
drawRoundCap(sx0, sy0, -smx, -smy);
} else if (capStyle == CAP_SQUARE) {
emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
}
emitClose();
}
private void emitMoveTo(final float x0, final float y0) {
out.moveTo(x0, y0);
}
private void emitLineTo(final float x1, final float y1) {
out.lineTo(x1, y1);
}
private void emitLineToRev(final float x1, final float y1) {
reverse.pushLine(x1, y1);
}
private void emitLineTo(final float x1, final float y1,
final boolean rev)
{
if (rev) {
emitLineToRev(x1, y1);
} else {
emitLineTo(x1, y1);
}
}
private void emitQuadTo(final float x1, final float y1,
final float x2, final float y2)
{
out.quadTo(x1, y1, x2, y2);
}
private void emitQuadToRev(final float x0, final float y0,
final float x1, final float y1)
{
reverse.pushQuad(x0, y0, x1, y1);
}
private void emitCurveTo(final float x1, final float y1,
final float x2, final float y2,
final float x3, final float y3)
{
out.curveTo(x1, y1, x2, y2, x3, y3);
}
private void emitCurveToRev(final float x0, final float y0,
final float x1, final float y1,
final float x2, final float y2)
{
reverse.pushCubic(x0, y0, x1, y1, x2, y2);
}
private void emitCurveTo(final float x0, final float y0,
final float x1, final float y1,
final float x2, final float y2,
final float x3, final float y3, final boolean rev)
{
if (rev) {
reverse.pushCubic(x0, y0, x1, y1, x2, y2);
} else {
out.curveTo(x1, y1, x2, y2, x3, y3);
}
}
private void emitClose() {
out.closePath();
}
private void drawJoin(float pdx, float pdy,
float x0, float y0,
float dx, float dy,
float omx, float omy,
float mx, float my)
{
if (prev != DRAWING_OP_TO) {
emitMoveTo(x0 + mx, y0 + my);
this.sdx = dx;
this.sdy = dy;
this.smx = mx;
this.smy = my;
} else {
boolean cw = isCW(pdx, pdy, dx, dy);
if (joinStyle == JOIN_MITER) {
drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
} else if (joinStyle == JOIN_ROUND) {
drawRoundJoin(x0, y0,
omx, omy,
mx, my, cw,
ROUND_JOIN_THRESHOLD);
}
emitLineTo(x0, y0, !cw);
}
prev = DRAWING_OP_TO;
}
private static boolean within(final float x1, final float y1,
final float x2, final float y2,
final float ERR)
{
assert ERR > 0 : "";
// compare taxicab distance. ERR will always be small, so using
// true distance won't give much benefit
return (Helpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs
Helpers.within(y1, y2, ERR)); // this is just as good.
}
private void getLineOffsets(float x1, float y1,
float x2, float y2,
float[] left, float[] right) {
computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
left[0] = x1 + mx;
left[1] = y1 + my;
left[2] = x2 + mx;
left[3] = y2 + my;
right[0] = x1 - mx;
right[1] = y1 - my;
right[2] = x2 - mx;
right[3] = y2 - my;
}
private int computeOffsetCubic(float[] pts, final int off,
float[] leftOff, float[] rightOff)
{
// if p1=p2 or p3=p4 it means that the derivative at the endpoint
// vanishes, which creates problems with computeOffset. Usually
// this happens when this stroker object is trying to winden
// a curve with a cusp. What happens is that curveTo splits
// the input curve at the cusp, and passes it to this function.
// because of inaccuracies in the splitting, we consider points
// equal if they're very close to each other.
final float x1 = pts[off + 0], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float x4 = pts[off + 6], y4 = pts[off + 7];
float dx4 = x4 - x3;
float dy4 = y4 - y3;
float dx1 = x2 - x1;
float dy1 = y2 - y1;
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore if p1 == p2
final boolean p1eqp2 = within(x1,y1,x2,y2, 6f * ulp(y2));
final boolean p3eqp4 = within(x3,y3,x4,y4, 6f * ulp(y4));
if (p1eqp2 && p3eqp4) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
} else if (p1eqp2) {
dx1 = x3 - x1;
dy1 = y3 - y1;
} else if (p3eqp4) {
dx4 = x4 - x2;
dy4 = y4 - y2;
}
// if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
float dotsq = (dx1 * dx4 + dy1 * dy4);
dotsq *= dotsq;
float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
if (Helpers.within(dotsq, l1sq * l4sq, 4f * ulp(dotsq))) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
}
// What we're trying to do in this function is to approximate an ideal
// offset curve (call it I) of the input curve B using a bezier curve Bp.
// The constraints I use to get the equations are:
//
// 1. The computed curve Bp should go through I(0) and I(1). These are
// x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find
// 4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p).
//
// 2. Bp should have slope equal in absolute value to I at the endpoints. So,
// (by the way, the operator || in the comments below means "aligned with".
// It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that
// vectors I'(0) and Bp'(0) are aligned, which is the same as saying
// that the tangent lines of I and Bp at 0 are parallel. Mathematically
// this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some
// nonzero constant.)
// I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and
// I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1).
// We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same
// is true for any bezier curve; therefore, we get the equations
// (1) p2p = c1 * (p2-p1) + p1p
// (2) p3p = c2 * (p4-p3) + p4p
// We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number
// of unknowns from 4 to 2 (i.e. just c1 and c2).
// To eliminate these 2 unknowns we use the following constraint:
//
// 3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note
// that I(0.5) is *the only* reason for computing dxm,dym. This gives us
// (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to
// (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3
// We can substitute (1) and (2) from above into (4) and we get:
// (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p
// which is equivalent to
// (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p)
//
// The right side of this is a 2D vector, and we know I(0.5), which gives us
// Bp(0.5), which gives us the value of the right side.
// The left side is just a matrix vector multiplication in disguise. It is
//
// [x2-x1, x4-x3][c1]
// [y2-y1, y4-y3][c2]
// which, is equal to
// [dx1, dx4][c1]
// [dy1, dy4][c2]
// At this point we are left with a simple linear system and we solve it by
// getting the inverse of the matrix above. Then we use [c1,c2] to compute
// p2p and p3p.
float x = (x1 + 3f * (x2 + x3) + x4) / 8f;
float y = (y1 + 3f * (y2 + y3) + y4) / 8f;
// (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
// c*B'(0.5) for some constant c.
float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
// this computes the offsets at t=0, 0.5, 1, using the property that
// for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
// the (dx/dt, dy/dt) vectors at the endpoints.
computeOffset(dx1, dy1, lineWidth2, offset0);
computeOffset(dxm, dym, lineWidth2, offset1);
computeOffset(dx4, dy4, lineWidth2, offset2);
float x1p = x1 + offset0[0]; // start
float y1p = y1 + offset0[1]; // point
float xi = x + offset1[0]; // interpolation
float yi = y + offset1[1]; // point
float x4p = x4 + offset2[0]; // end
float y4p = y4 + offset2[1]; // point
float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
float two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
float two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
float x2p, y2p, x3p, y3p;
x2p = x1p + c1*dx1;
y2p = y1p + c1*dy1;
x3p = x4p + c2*dx4;
y3p = y4p + c2*dy4;
leftOff[0] = x1p; leftOff[1] = y1p;
leftOff[2] = x2p; leftOff[3] = y2p;
leftOff[4] = x3p; leftOff[5] = y3p;
leftOff[6] = x4p; leftOff[7] = y4p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
xi = xi - 2f * offset1[0]; yi = yi - 2f * offset1[1];
x4p = x4 - offset2[0]; y4p = y4 - offset2[1];
two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
x2p = x1p + c1*dx1;
y2p = y1p + c1*dy1;
x3p = x4p + c2*dx4;
y3p = y4p + c2*dy4;
rightOff[0] = x1p; rightOff[1] = y1p;
rightOff[2] = x2p; rightOff[3] = y2p;
rightOff[4] = x3p; rightOff[5] = y3p;
rightOff[6] = x4p; rightOff[7] = y4p;
return 8;
}
// return the kind of curve in the right and left arrays.
private int computeOffsetQuad(float[] pts, final int off,
float[] leftOff, float[] rightOff)
{
final float x1 = pts[off + 0], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float dx3 = x3 - x2;
final float dy3 = y3 - y2;
final float dx1 = x2 - x1;
final float dy1 = y2 - y1;
// this computes the offsets at t = 0, 1
computeOffset(dx1, dy1, lineWidth2, offset0);
computeOffset(dx3, dy3, lineWidth2, offset1);
leftOff[0] = x1 + offset0[0]; leftOff[1] = y1 + offset0[1];
leftOff[4] = x3 + offset1[0]; leftOff[5] = y3 + offset1[1];
rightOff[0] = x1 - offset0[0]; rightOff[1] = y1 - offset0[1];
rightOff[4] = x3 - offset1[0]; rightOff[5] = y3 - offset1[1];
float x1p = leftOff[0]; // start
float y1p = leftOff[1]; // point
float x3p = leftOff[4]; // end
float y3p = leftOff[5]; // point
// Corner cases:
// 1. If the two control vectors are parallel, we'll end up with NaN's
// in leftOff (and rightOff in the body of the if below), so we'll
// do getLineOffsets, which is right.
// 2. If the first or second two points are equal, then (dx1,dy1)==(0,0)
// or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1)
// or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that
// computeIntersection will put NaN's in leftOff and right off, and
// we will do getLineOffsets, which is right.
computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
float cx = leftOff[2];
float cy = leftOff[3];
if (!(isFinite(cx) && isFinite(cy))) {
// maybe the right path is not degenerate.
x1p = rightOff[0];
y1p = rightOff[1];
x3p = rightOff[4];
y3p = rightOff[5];
computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
cx = rightOff[2];
cy = rightOff[3];
if (!(isFinite(cx) && isFinite(cy))) {
// both are degenerate. This curve is a line.
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
}
// {left,right}Off[0,1,4,5] are already set to the correct values.
leftOff[2] = 2f * x2 - cx;
leftOff[3] = 2f * y2 - cy;
return 6;
}
// rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2))
// == 2*(x2, y2) - (left_x2, left_y2)
rightOff[2] = 2f * x2 - cx;
rightOff[3] = 2f * y2 - cy;
return 6;
}
private static boolean isFinite(float x) {
return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
}
// If this class is compiled with ecj, then Hotspot crashes when OSR
// compiling this function. See bugs 7004570 and 6675699
// TODO: until those are fixed, we should work around that by
// manually inlining this into curveTo and quadTo.
/******************************* WORKAROUND **********************************
private void somethingTo(final int type) {
// need these so we can update the state at the end of this method
final float xf = middle[type-2], yf = middle[type-1];
float dxs = middle[2] - middle[0];
float dys = middle[3] - middle[1];
float dxf = middle[type - 2] - middle[type - 4];
float dyf = middle[type - 1] - middle[type - 3];
switch(type) {
case 6:
if ((dxs == 0f && dys == 0f) ||
(dxf == 0f && dyf == 0f)) {
dxs = dxf = middle[4] - middle[0];
dys = dyf = middle[5] - middle[1];
}
break;
case 8:
boolean p1eqp2 = (dxs == 0f && dys == 0f);
boolean p3eqp4 = (dxf == 0f && dyf == 0f);
if (p1eqp2) {
dxs = middle[4] - middle[0];
dys = middle[5] - middle[1];
if (dxs == 0f && dys == 0f) {
dxs = middle[6] - middle[0];
dys = middle[7] - middle[1];
}
}
if (p3eqp4) {
dxf = middle[6] - middle[2];
dyf = middle[7] - middle[3];
if (dxf == 0f && dyf == 0f) {
dxf = middle[6] - middle[0];
dyf = middle[7] - middle[1];
}
}
}
if (dxs == 0f && dys == 0f) {
// this happens iff the "curve" is just a point
lineTo(middle[0], middle[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
float len = (float) sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float) sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
int nSplits = findSubdivPoints(curve, middle, subdivTs, type, lineWidth2);
int kind = 0;
BreakPtrIterator it = curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
switch (type) {
case 8:
kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
break;
case 6:
kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
break;
}
emitLineTo(lp[0], lp[1]);
switch(kind) {
case 8:
emitCurveTo(lp[2], lp[3], lp[4], lp[5], lp[6], lp[7]);
emitCurveToRev(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5]);
break;
case 6:
emitQuadTo(lp[2], lp[3], lp[4], lp[5]);
emitQuadToRev(rp[0], rp[1], rp[2], rp[3]);
break;
case 4:
emitLineTo(lp[2], lp[3]);
emitLineTo(rp[0], rp[1], true);
break;
}
emitLineTo(rp[kind - 2], rp[kind - 1], true);
}
this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
}
****************************** END WORKAROUND *******************************/
// finds values of t where the curve in pts should be subdivided in order
// to get good offset curves a distance of w away from the middle curve.
// Stores the points in ts, and returns how many of them there were.
private static int findSubdivPoints(final Curve c, float[] pts, float[] ts,
final int type, final float w)
{
final float x12 = pts[2] - pts[0];
final float y12 = pts[3] - pts[1];
// if the curve is already parallel to either axis we gain nothing
// from rotating it.
if (y12 != 0f && x12 != 0f) {
// we rotate it so that the first vector in the control polygon is
// parallel to the x-axis. This will ensure that rotated quarter
// circles won't be subdivided.
final float hypot = (float) sqrt(x12 * x12 + y12 * y12);
final float cos = x12 / hypot;
final float sin = y12 / hypot;
final float x1 = cos * pts[0] + sin * pts[1];
final float y1 = cos * pts[1] - sin * pts[0];
final float x2 = cos * pts[2] + sin * pts[3];
final float y2 = cos * pts[3] - sin * pts[2];
final float x3 = cos * pts[4] + sin * pts[5];
final float y3 = cos * pts[5] - sin * pts[4];
switch(type) {
case 8:
final float x4 = cos * pts[6] + sin * pts[7];
final float y4 = cos * pts[7] - sin * pts[6];
c.set(x1, y1, x2, y2, x3, y3, x4, y4);
break;
case 6:
c.set(x1, y1, x2, y2, x3, y3);
break;
default:
}
} else {
c.set(pts, type);
}
int ret = 0;
// we subdivide at values of t such that the remaining rotated
// curves are monotonic in x and y.
ret += c.dxRoots(ts, ret);
ret += c.dyRoots(ts, ret);
// subdivide at inflection points.
if (type == 8) {
// quadratic curves can't have inflection points
ret += c.infPoints(ts, ret);
}
// now we must subdivide at points where one of the offset curves will have
// a cusp. This happens at ts where the radius of curvature is equal to w.
ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
Helpers.isort(ts, 0, ret);
return ret;
}
@Override public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
final float[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
mid[2] = x1; mid[3] = y1;
mid[4] = x2; mid[5] = y2;
mid[6] = x3; mid[7] = y3;
// inlined version of somethingTo(8);
// See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = mid[6], yf = mid[7];
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[6] - mid[4];
float dyf = mid[7] - mid[5];
boolean p1eqp2 = (dxs == 0f && dys == 0f);
boolean p3eqp4 = (dxf == 0f && dyf == 0f);
if (p1eqp2) {
dxs = mid[4] - mid[0];
dys = mid[5] - mid[1];
if (dxs == 0f && dys == 0f) {
dxs = mid[6] - mid[0];
dys = mid[7] - mid[1];
}
}
if (p3eqp4) {
dxf = mid[6] - mid[2];
dyf = mid[7] - mid[3];
if (dxf == 0f && dyf == 0f) {
dxf = mid[6] - mid[0];
dyf = mid[7] - mid[1];
}
}
if (dxs == 0f && dys == 0f) {
// this happens if the "curve" is just a point
lineTo(mid[0], mid[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
float len = (float) sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float) sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
final float[] l = lp;
final float[] r = rp;
int kind = 0;
BreakPtrIterator it = curve.breakPtsAtTs(mid, 8, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
kind = computeOffsetCubic(mid, curCurveOff, l, r);
emitLineTo(l[0], l[1]);
switch(kind) {
case 8:
emitCurveTo(l[2], l[3], l[4], l[5], l[6], l[7]);
emitCurveToRev(r[0], r[1], r[2], r[3], r[4], r[5]);
break;
case 4:
emitLineTo(l[2], l[3]);
emitLineToRev(r[0], r[1]);
break;
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
}
@Override public void quadTo(float x1, float y1, float x2, float y2) {
final float[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
mid[2] = x1; mid[3] = y1;
mid[4] = x2; mid[5] = y2;
// inlined version of somethingTo(8);
// See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = mid[4], yf = mid[5];
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[4] - mid[2];
float dyf = mid[5] - mid[3];
if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
dxs = dxf = mid[4] - mid[0];
dys = dyf = mid[5] - mid[1];
}
if (dxs == 0f && dys == 0f) {
// this happens if the "curve" is just a point
lineTo(mid[0], mid[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
float len = (float) sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float) sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
final float[] l = lp;
final float[] r = rp;
int kind = 0;
BreakPtrIterator it = curve.breakPtsAtTs(mid, 6, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
kind = computeOffsetQuad(mid, curCurveOff, l, r);
emitLineTo(l[0], l[1]);
switch(kind) {
case 6:
emitQuadTo(l[2], l[3], l[4], l[5]);
emitQuadToRev(r[0], r[1], r[2], r[3]);
break;
case 4:
emitLineTo(l[2], l[3]);
emitLineToRev(r[0], r[1]);
break;
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
}
@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
}
// a stack of polynomial curves where each curve shares endpoints with
// adjacent ones.
static final class PolyStack {
private static final byte TYPE_LINETO = (byte) 0;
private static final byte TYPE_QUADTO = (byte) 1;
private static final byte TYPE_CUBICTO = (byte) 2;
float[] curves;
int end;
byte[] curveTypes;
int numCurves;
// per-thread renderer context
final RendererContext rdrCtx;
// per-thread initial arrays (large enough to satisfy most usages: 8192)
// +1 to avoid recycling in Helpers.widenArray()
private final float[] curves_initial = new float[INITIAL_LARGE_ARRAY + 1]; // 32K
private final byte[] curveTypes_initial = new byte[INITIAL_LARGE_ARRAY + 1]; // 8K
// used marks (stats only)
int curveTypesUseMark;
int curvesUseMark;
/**
* Constructor
* @param rdrCtx per-thread renderer context
*/
PolyStack(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
curves = curves_initial;
curveTypes = curveTypes_initial;
end = 0;
numCurves = 0;
if (doStats) {
curveTypesUseMark = 0;
curvesUseMark = 0;
}
}
/**
* Disposes this PolyStack:
* clean up before reusing this instance
*/
void dispose() {
end = 0;
numCurves = 0;
if (doStats) {
RendererContext.stats.stat_rdr_poly_stack_types
.add(curveTypesUseMark);
RendererContext.stats.stat_rdr_poly_stack_curves
.add(curvesUseMark);
// reset marks
curveTypesUseMark = 0;
curvesUseMark = 0;
}
// Return arrays:
// curves and curveTypes are kept dirty
if (curves != curves_initial) {
rdrCtx.putDirtyFloatArray(curves);
curves = curves_initial;
}
if (curveTypes != curveTypes_initial) {
rdrCtx.putDirtyByteArray(curveTypes);
curveTypes = curveTypes_initial;
}
}
private void ensureSpace(final int n) {
if (end + n > curves.length) {
if (doStats) {
RendererContext.stats.stat_array_stroker_polystack_curves
.add(end + n);
}
curves = rdrCtx.widenDirtyFloatArray(curves, end, end + n);
}
if (numCurves + 1 > curveTypes.length) {
if (doStats) {
RendererContext.stats.stat_array_stroker_polystack_curveTypes
.add(numCurves + 1);
}
curveTypes = rdrCtx.widenDirtyByteArray(curveTypes,
numCurves,
numCurves + 1);
}
}
void pushCubic(float x0, float y0,
float x1, float y1,
float x2, float y2)
{
ensureSpace(6);
curveTypes[numCurves++] = TYPE_CUBICTO;
// we reverse the coordinate order to make popping easier
final float[] _curves = curves;
int e = end;
_curves[e++] = x2; _curves[e++] = y2;
_curves[e++] = x1; _curves[e++] = y1;
_curves[e++] = x0; _curves[e++] = y0;
end = e;
}
void pushQuad(float x0, float y0,
float x1, float y1)
{
ensureSpace(4);
curveTypes[numCurves++] = TYPE_QUADTO;
final float[] _curves = curves;
int e = end;
_curves[e++] = x1; _curves[e++] = y1;
_curves[e++] = x0; _curves[e++] = y0;
end = e;
}
void pushLine(float x, float y) {
ensureSpace(2);
curveTypes[numCurves++] = TYPE_LINETO;
curves[end++] = x; curves[end++] = y;
}
void popAll(PathConsumer2D io) {
if (doStats) {
// update used marks:
if (numCurves > curveTypesUseMark) {
curveTypesUseMark = numCurves;
}
if (end > curvesUseMark) {
curvesUseMark = end;
}
}
final byte[] _curveTypes = curveTypes;
final float[] _curves = curves;
int nc = numCurves;
int e = end;
while (nc != 0) {
switch(_curveTypes[--nc]) {
case TYPE_LINETO:
e -= 2;
io.lineTo(_curves[e], _curves[e+1]);
continue;
case TYPE_QUADTO:
e -= 4;
io.quadTo(_curves[e+0], _curves[e+1],
_curves[e+2], _curves[e+3]);
continue;
case TYPE_CUBICTO:
e -= 6;
io.curveTo(_curves[e+0], _curves[e+1],
_curves[e+2], _curves[e+3],
_curves[e+4], _curves[e+5]);
continue;
default:
}
}
numCurves = 0;
end = 0;
}
@Override
public String toString() {
String ret = "";
int nc = numCurves;
int e = end;
int len;
while (nc != 0) {
switch(curveTypes[--nc]) {
case TYPE_LINETO:
len = 2;
ret += "line: ";
break;
case TYPE_QUADTO:
len = 4;
ret += "quad: ";
break;
case TYPE_CUBICTO:
len = 6;
ret += "cubic: ";
break;
default:
len = 0;
}
e -= len;
ret += Arrays.toString(Arrays.copyOfRange(curves, e, e+len))
+ "\n";
}
return ret;
}
}
}
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import sun.awt.geom.PathConsumer2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
final class TransformingPathConsumer2D {
TransformingPathConsumer2D() {
// used by RendererContext
}
// recycled PathConsumer2D instance from transformConsumer()
private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper();
PathConsumer2D wrapPath2d(Path2D.Float p2d)
{
return wp_Path2DWrapper.init(p2d);
}
// recycled PathConsumer2D instances from transformConsumer()
private final TranslateFilter tx_TranslateFilter = new TranslateFilter();
private final DeltaScaleFilter tx_DeltaScaleFilter = new DeltaScaleFilter();
private final ScaleFilter tx_ScaleFilter = new ScaleFilter();
private final DeltaTransformFilter tx_DeltaTransformFilter = new DeltaTransformFilter();
private final TransformFilter tx_TransformFilter = new TransformFilter();
PathConsumer2D transformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float mxt = (float) at.getTranslateX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
float myt = (float) at.getTranslateY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
if (mxt == 0f && myt == 0f) {
return out;
} else {
return tx_TranslateFilter.init(out, mxt, myt);
}
} else {
if (mxt == 0f && myt == 0f) {
return tx_DeltaScaleFilter.init(out, mxx, myy);
} else {
return tx_ScaleFilter.init(out, mxx, myy, mxt, myt);
}
}
} else if (mxt == 0f && myt == 0f) {
return tx_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
} else {
return tx_TransformFilter.init(out, mxx, mxy, mxt, myx, myy, myt);
}
}
// recycled PathConsumer2D instances from deltaTransformConsumer()
private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
return out;
} else {
return dt_DeltaScaleFilter.init(out, mxx, myy);
}
} else {
return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
}
}
// recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
return out;
} else {
return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
}
} else {
float det = mxx * myy - mxy * myx;
return iv_DeltaTransformFilter.init(out,
myy / det,
-mxy / det,
-myx / det,
mxx / det);
}
}
static final class TranslateFilter implements PathConsumer2D {
private PathConsumer2D out;
private float tx, ty;
TranslateFilter() {}
TranslateFilter init(PathConsumer2D out,
float tx, float ty)
{
this.out = out;
this.tx = tx;
this.ty = ty;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 + tx, y0 + ty);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 + tx, y1 + ty);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 + tx, y1 + ty,
x2 + tx, y2 + ty);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 + tx, y1 + ty,
x2 + tx, y2 + ty,
x3 + tx, y3 + ty);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class ScaleFilter implements PathConsumer2D {
private PathConsumer2D out;
private float sx, sy, tx, ty;
ScaleFilter() {}
ScaleFilter init(PathConsumer2D out,
float sx, float sy,
float tx, float ty)
{
this.out = out;
this.sx = sx;
this.sy = sy;
this.tx = tx;
this.ty = ty;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * sx + tx, y0 * sy + ty);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * sx + tx, y1 * sy + ty);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * sx + tx, y1 * sy + ty,
x2 * sx + tx, y2 * sy + ty);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * sx + tx, y1 * sy + ty,
x2 * sx + tx, y2 * sy + ty,
x3 * sx + tx, y3 * sy + ty);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class TransformFilter implements PathConsumer2D {
private PathConsumer2D out;
private float mxx, mxy, mxt, myx, myy, myt;
TransformFilter() {}
TransformFilter init(PathConsumer2D out,
float mxx, float mxy, float mxt,
float myx, float myy, float myt)
{
this.out = out;
this.mxx = mxx;
this.mxy = mxy;
this.mxt = mxt;
this.myx = myx;
this.myy = myy;
this.myt = myt;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * mxx + y0 * mxy + mxt,
x0 * myx + y0 * myy + myt);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt,
x2 * mxx + y2 * mxy + mxt,
x2 * myx + y2 * myy + myt);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt,
x2 * mxx + y2 * mxy + mxt,
x2 * myx + y2 * myy + myt,
x3 * mxx + y3 * mxy + mxt,
x3 * myx + y3 * myy + myt);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class DeltaScaleFilter implements PathConsumer2D {
private PathConsumer2D out;
private float sx, sy;
DeltaScaleFilter() {}
DeltaScaleFilter init(PathConsumer2D out,
float mxx, float myy)
{
this.out = out;
sx = mxx;
sy = myy;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * sx, y0 * sy);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * sx, y1 * sy);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * sx, y1 * sy,
x2 * sx, y2 * sy);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * sx, y1 * sy,
x2 * sx, y2 * sy,
x3 * sx, y3 * sy);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class DeltaTransformFilter implements PathConsumer2D {
private PathConsumer2D out;
private float mxx, mxy, myx, myy;
DeltaTransformFilter() {}
DeltaTransformFilter init(PathConsumer2D out,
float mxx, float mxy,
float myx, float myy)
{
this.out = out;
this.mxx = mxx;
this.mxy = mxy;
this.myx = myx;
this.myy = myy;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * mxx + y0 * mxy,
x0 * myx + y0 * myy);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy,
x2 * mxx + y2 * mxy,
x2 * myx + y2 * myy);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy,
x2 * mxx + y2 * mxy,
x2 * myx + y2 * myy,
x3 * mxx + y3 * mxy,
x3 * myx + y3 * myy);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class Path2DWrapper implements PathConsumer2D {
private Path2D.Float p2d;
Path2DWrapper() {}
Path2DWrapper init(Path2D.Float p2d) {
this.p2d = p2d;
return this;
}
@Override
public void moveTo(float x0, float y0) {
p2d.moveTo(x0, y0);
}
@Override
public void lineTo(float x1, float y1) {
p2d.lineTo(x1, y1);
}
@Override
public void closePath() {
p2d.closePath();
}
@Override
public void pathDone() {}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
p2d.curveTo(x1, y1, x2, y2, x3, y3);
}
@Override
public void quadTo(float x1, float y1, float x2, float y2) {
p2d.quadTo(x1, y1, x2, y2);
}
@Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
public final class Version {
private static final String version = "marlin-0.7.2-Unsafe-OpenJDK";
public static String getVersion() {
return version;
}
private Version() {
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin.stats;
import java.util.Arrays;
/**
* Generic histogram based on long statistics
*/
public final class Histogram extends StatLong {
static final int BUCKET = 2;
static final int MAX = 20;
static final int LAST = MAX - 1;
static final int[] STEPS = new int[MAX];
static {
STEPS[0] = 0;
STEPS[1] = 1;
for (int i = 2; i < MAX; i++) {
STEPS[i] = STEPS[i - 1] * BUCKET;
}
// System.out.println("Histogram.STEPS = " + Arrays.toString(STEPS));
}
static int bucket(int val) {
for (int i = 1; i < MAX; i++) {
if (val < STEPS[i]) {
return i - 1;
}
}
return LAST;
}
private final StatLong[] stats = new StatLong[MAX];
public Histogram(final String name) {
super(name);
for (int i = 0; i < MAX; i++) {
stats[i] = new StatLong(String.format("%5s .. %5s", STEPS[i],
((i + 1 < MAX) ? STEPS[i + 1] : "~")));
}
}
@Override
public void reset() {
super.reset();
for (int i = 0; i < MAX; i++) {
stats[i].reset();
}
}
@Override
public void add(int val) {
super.add(val);
stats[bucket(val)].add(val);
}
@Override
public void add(long val) {
add((int) val);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(2048);
super.toString(sb).append(" { ");
for (int i = 0; i < MAX; i++) {
if (stats[i].count != 0l) {
sb.append("\n ").append(stats[i].toString());
}
}
return sb.append(" }").toString();
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin.stats;
/**
* Generic monitor ie gathers time statistics as nanos.
*/
public final class Monitor extends StatLong {
private static final long INVALID = -1L;
private long start = INVALID;
public Monitor(final String name) {
super(name);
}
public void start() {
start = System.nanoTime();
}
public void stop() {
final long elapsed = System.nanoTime() - start;
if (start != INVALID && elapsed > 0l) {
add(elapsed);
}
start = INVALID;
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin.stats;
/**
* Statistics as long values
*/
public class StatLong {
public final String name;
public long count = 0l;
public long sum = 0l;
public long min = Integer.MAX_VALUE;
public long max = Integer.MIN_VALUE;
public StatLong(final String name) {
this.name = name;
}
public void reset() {
count = 0l;
sum = 0l;
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
}
public void add(final int val) {
count++;
sum += val;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
}
public void add(final long val) {
count++;
sum += val;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(128);
toString(sb);
return sb.toString();
}
public final StringBuilder toString(final StringBuilder sb) {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(sum).append(" avg: ");
sb.append(trimTo3Digits(((double) sum) / count));
sb.append(" [").append(min).append(" | ").append(max).append("]");
return sb;
}
/**
* Adjust the given double value to keep only 3 decimal digits
*
* @param value value to adjust
* @return double value with only 3 decimal digits
*/
public static double trimTo3Digits(final double value) {
return ((long) (1e3d * value)) / 1e3d;
}
}
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -22,14 +22,12 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.pipe;
import java.awt.BasicStroke;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import sun.awt.SunHints;
import sun.java2d.SunGraphics2D;
......@@ -45,6 +43,15 @@ public class AAShapePipe
{
static RenderingEngine renderengine = RenderingEngine.getInstance();
// Per-thread TileState (~1K very small so do not use any Weak Reference)
private static final ThreadLocal<TileState> tileStateThreadLocal =
new ThreadLocal<TileState>() {
@Override
protected TileState initialValue() {
return new TileState();
}
};
CompositePipe outpipe;
public AAShapePipe(CompositePipe pipe) {
......@@ -68,20 +75,6 @@ public class AAShapePipe
renderPath(sg, s, null);
}
private static Rectangle2D computeBBox(double ux1, double uy1,
double ux2, double uy2)
{
if ((ux2 -= ux1) < 0) {
ux1 += ux2;
ux2 = -ux2;
}
if ((uy2 -= uy1) < 0) {
uy1 += uy2;
uy2 = -uy2;
}
return new Rectangle2D.Double(ux1, uy1, ux2, uy2);
}
public void fillParallelogram(SunGraphics2D sg,
double ux1, double uy1,
double ux2, double uy2,
......@@ -90,7 +83,9 @@ public class AAShapePipe
double dx2, double dy2)
{
Region clip = sg.getCompClip();
int abox[] = new int[4];
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
clip, abox);
......@@ -99,7 +94,7 @@ public class AAShapePipe
return;
}
renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
}
public void drawParallelogram(SunGraphics2D sg,
......@@ -111,7 +106,9 @@ public class AAShapePipe
double lw1, double lw2)
{
Region clip = sg.getCompClip();
int abox[] = new int[4];
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1, lw2,
clip, abox);
......@@ -122,23 +119,7 @@ public class AAShapePipe
// Note that bbox is of the original shape, not the wide path.
// This is appropriate for handing to Paint methods...
renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
}
private static byte[] theTile;
private synchronized static byte[] getAlphaTile(int len) {
byte[] t = theTile;
if (t == null || t.length < len) {
t = new byte[len];
} else {
theTile = null;
}
return t;
}
private synchronized static void dropAlphaTile(byte[] t) {
theTile = t;
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
}
public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
......@@ -147,7 +128,9 @@ public class AAShapePipe
boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
Region clip = sg.getCompClip();
int abox[] = new int[4];
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(s, sg.transform, clip,
bs, thin, adjust, abox);
......@@ -156,31 +139,30 @@ public class AAShapePipe
return;
}
renderTiles(sg, s, aatg, abox);
renderTiles(sg, s, aatg, abox, ts);
}
public void renderTiles(SunGraphics2D sg, Shape s,
AATileGenerator aatg, int abox[])
AATileGenerator aatg, int abox[], TileState ts)
{
Object context = null;
byte alpha[] = null;
try {
context = outpipe.startSequence(sg, s,
new Rectangle(abox[0], abox[1],
abox[2] - abox[0],
abox[3] - abox[1]),
ts.computeDevBox(abox),
abox);
int tw = aatg.getTileWidth();
int th = aatg.getTileHeight();
alpha = getAlphaTile(tw * th);
final int tw = aatg.getTileWidth();
final int th = aatg.getTileHeight();
// get tile from thread local storage:
final byte[] alpha = ts.getAlphaTile(tw * th);
byte[] atile;
for (int y = abox[1]; y < abox[3]; y += th) {
int h = Math.min(th, abox[3] - y);
for (int x = abox[0]; x < abox[2]; x += tw) {
int w = Math.min(tw, abox[2] - x);
int h = Math.min(th, abox[3] - y);
int a = aatg.getTypicalAlpha();
if (a == 0x00 ||
......@@ -207,9 +189,56 @@ public class AAShapePipe
if (context != null) {
outpipe.endSequence(context);
}
if (alpha != null) {
dropAlphaTile(alpha);
}
}
// Tile state used by AAShapePipe
static final class TileState {
// cached tile (32 x 32 tile by default)
private byte[] theTile = new byte[32 * 32];
// dirty aabox array
final int[] abox = new int[4];
// dirty bbox rectangle
private final Rectangle dev = new Rectangle();
// dirty bbox rectangle2D.Double
private final Rectangle2D.Double bbox2D = new Rectangle2D.Double();
byte[] getAlphaTile(int len) {
byte[] t = theTile;
if (t.length < len) {
// create a larger tile and may free current theTile (too small)
theTile = t = new byte[len];
}
return t;
}
Rectangle computeDevBox(final int[] abox) {
final Rectangle box = this.dev;
box.x = abox[0];
box.y = abox[1];
box.width = abox[2] - abox[0];
box.height = abox[3] - abox[1];
return box;
}
Rectangle2D computeBBox(double ux1, double uy1,
double ux2, double uy2)
{
if ((ux2 -= ux1) < 0.0) {
ux1 += ux2;
ux2 = -ux2;
}
if ((uy2 -= uy1) < 0.0) {
uy1 += uy2;
uy2 = -uy2;
}
final Rectangle2D.Double box = this.bbox2D;
box.x = ux1;
box.y = uy1;
box.width = ux2;
box.height = uy2;
return box;
}
}
}
# Pisces Rendering Engine module
sun.java2d.pisces.PiscesRenderingEngine
# Marlin Rendering Engine module
sun.java2d.marlin.MarlinRenderingEngine
/*
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
......@@ -289,6 +289,14 @@ public class ZipFileSystem extends FileSystem {
def.end();
}
beginWrite(); // lock and sync
try {
// Clear the map so that its keys & values can be garbage collected
inodes = null;
} finally {
endWrite();
}
IOException ioe = null;
synchronized (tmppaths) {
for (Path p: tmppaths) {
......
......@@ -3,3 +3,6 @@ sun.java2d.jules.JulesRenderingEngine
# Pisces Rendering Engine module
sun.java2d.pisces.PiscesRenderingEngine
# Marlin Rendering Engine module
sun.java2d.marlin.MarlinRenderingEngine
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import sun.java2d.marlin.FloatMath;
/*
* @test
* @summary Check for correct implementation of FloatMath.ceil/floor
* @run main CeilAndFloorTests
*/
public class CeilAndFloorTests {
public static String toHexString(float f) {
if (!Float.isNaN(f))
return Float.toHexString(f);
else
return "NaN(0x" + Integer.toHexString(Float.floatToRawIntBits(f)) + ")";
}
public static int test(String testName, float input,
float result, float expected) {
if (Float.compare(expected, result) != 0) {
System.err.println("Failure for " + testName + ":\n" +
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
"\tgot " + result + "\t(" + toHexString(result) + ").");
return 1;
}
else
return 0;
}
public static int test_skip_0(String testName, float input,
float result, float expected)
{
// floor_int does not distinguish +0f and -0f
// but it is not critical for Marlin
if (Float.compare(expected, result) != 0 && (expected != 0f))
{
System.err.println("Failure for " + testName + ":\n" +
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
"\tgot " + result + "\t(" + toHexString(result) + ").");
return 1;
}
else
return 0;
}
private static int testCeilCase(float input, float expected) {
int failures = 0;
// float result:
failures += test("FloatMath.ceil_f", input, FloatMath.ceil_f(input), expected);
// int result:
failures += test("FloatMath.ceil_int", input, FloatMath.ceil_int(input), (int)expected);
failures += test("FloatMath.ceil_f (int)", input, (int)FloatMath.ceil_f(input), (int)expected);
return failures;
}
private static int testFloorCase(float input, float expected) {
int failures = 0;
// float result:
failures += test ("FloatMath.floor_f", input, FloatMath.floor_f(input), expected);
// ignore difference between +0f and -0f:
failures += test_skip_0("FloatMath.floor_int", input, FloatMath.floor_int(input), (int)expected);
failures += test_skip_0("FloatMath.floor_f (int)", input, (int)FloatMath.floor_f(input), (int)expected);
return failures;
}
private static int nearIntegerTests() {
int failures = 0;
float [] fixedPoints = {
-0.0f,
0.0f,
-1.0f,
1.0f,
-0x1.0p52f,
0x1.0p52f,
-Float.MAX_VALUE,
Float.MAX_VALUE,
Float.NEGATIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.NaN,
};
for(float fixedPoint : fixedPoints) {
failures += testCeilCase(fixedPoint, fixedPoint);
failures += testFloorCase(fixedPoint, fixedPoint);
}
for(int i = Float.MIN_EXPONENT; i <= Float.MAX_EXPONENT; i++) {
float powerOfTwo = Math.scalb(1.0f, i);
float neighborDown = Math.nextDown(powerOfTwo);
float neighborUp = Math.nextUp(powerOfTwo);
if (i < 0) {
failures += testCeilCase( powerOfTwo, 1.0f);
failures += testCeilCase(-powerOfTwo, -0.0f);
failures += testFloorCase( powerOfTwo, 0.0f);
failures += testFloorCase(-powerOfTwo, -1.0f);
failures += testCeilCase( neighborDown, 1.0f);
failures += testCeilCase(-neighborDown, -0.0f);
failures += testFloorCase( neighborUp, 0.0f);
failures += testFloorCase(-neighborUp, -1.0f);
} else {
failures += testCeilCase(powerOfTwo, powerOfTwo);
failures += testFloorCase(powerOfTwo, powerOfTwo);
if (neighborDown==Math.rint(neighborDown)) {
failures += testCeilCase( neighborDown, neighborDown);
failures += testCeilCase(-neighborDown, -neighborDown);
failures += testFloorCase( neighborDown, neighborDown);
failures += testFloorCase(-neighborDown,-neighborDown);
} else {
failures += testCeilCase( neighborDown, powerOfTwo);
failures += testFloorCase(-neighborDown, -powerOfTwo);
}
if (neighborUp==Math.rint(neighborUp)) {
failures += testCeilCase(neighborUp, neighborUp);
failures += testCeilCase(-neighborUp, -neighborUp);
failures += testFloorCase(neighborUp, neighborUp);
failures += testFloorCase(-neighborUp, -neighborUp);
} else {
failures += testFloorCase(neighborUp, powerOfTwo);
failures += testCeilCase(-neighborUp, -powerOfTwo);
}
}
}
for(int i = -(0x10000); i <= 0x10000; i++) {
float f = (float) i;
float neighborDown = Math.nextDown(f);
float neighborUp = Math.nextUp(f);
failures += testCeilCase( f, f);
failures += testCeilCase(-f, -f);
failures += testFloorCase( f, f);
failures += testFloorCase(-f, -f);
if (Math.abs(f) > 1.0) {
failures += testCeilCase( neighborDown, f);
failures += testCeilCase(-neighborDown, -f+1);
failures += testFloorCase( neighborUp, f);
failures += testFloorCase(-neighborUp, -f-1);
}
}
return failures;
}
public static int roundingTests() {
int failures = 0;
float [][] testCases = {
{ Float.MIN_VALUE, 1.0f},
{-Float.MIN_VALUE, -0.0f},
{ Math.nextDown(Float.MIN_NORMAL), 1.0f},
{-Math.nextDown(Float.MIN_NORMAL), -0.0f},
{ Float.MIN_NORMAL, 1.0f},
{-Float.MIN_NORMAL, -0.0f},
{ 0.1f, 1.0f},
{-0.1f, -0.0f},
{ 0.5f, 1.0f},
{-0.5f, -0.0f},
{ 1.5f, 2.0f},
{-1.5f, -1.0f},
{ 2.5f, 3.0f},
{-2.5f, -2.0f},
{ 12.3456789f, 13.0f},
{-12.3456789f, -12.0f},
{ Math.nextDown(1.0f), 1.0f},
{ Math.nextDown(-1.0f), -1.0f},
{ Math.nextUp(1.0f), 2.0f},
{ Math.nextUp(-1.0f), -0.0f},
{ 0x1.0p22f, 0x1.0p22f},
{-0x1.0p22f, -0x1.0p22f},
{ Math.nextDown(0x1.0p22f), 0x1.0p22f},
{-Math.nextUp(0x1.0p22f), -0x1.0p22f},
{ Math.nextUp(0x1.0p22f), 0x1.0p22f+1f},
{-Math.nextDown(0x1.0p22f), -0x1.0p22f+1f},
{ Math.nextDown(0x1.0p23f), 0x1.0p23f},
{-Math.nextUp(0x1.0p23f), -0x1.0p23f-1f},
{ Math.nextUp(0x1.0p23f), 0x1.0p23f+1f},
{-Math.nextDown(0x1.0p23f), -0x1.0p23f+1f},
};
for(float[] testCase : testCases) {
failures += testCeilCase(testCase[0], testCase[1]);
failures += testFloorCase(-testCase[0], -testCase[1]);
}
return failures;
}
public static void main(String... args) {
int failures = 0;
System.out.println("nearIntegerTests");
failures += nearIntegerTests();
System.out.println("roundingTests");
failures += roundingTests();
if (failures > 0) {
System.err.println("Testing {FloatMath}.ceil/floor incurred "
+ failures + " failures.");
throw new RuntimeException();
}
}
}
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import static java.awt.geom.Path2D.WIND_NON_ZERO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import sun.java2d.pipe.RenderingEngine;
/**
* Simple crash rendering test using huge GeneralPaths with marlin renderer
*
* run it with large heap (2g):
* java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest
*
* @author bourgesl
*/
public class CrashTest {
static final boolean SAVE_IMAGE = false;
static boolean USE_ROUND_CAPS_AND_JOINS = true;
public static void main(String[] args) {
// try insane image sizes:
// subpixel coords may overflow:
// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6);
// larger than 23 bits: (RLE)
testHugeImage(8388608 + 1, 10);
test(0.1f, false, 0);
test(0.1f, true, 7f);
// Exceed 2Gb OffHeap buffer for edges:
try {
USE_ROUND_CAPS_AND_JOINS = true;
test(0.1f, true, 0.1f);
System.out.println("Exception MISSING.");
}
catch (Throwable th) {
if (th instanceof ArrayIndexOutOfBoundsException) {
System.out.println("ArrayIndexOutOfBoundsException expected.");
} else {
System.out.println("Exception occured:");
th.printStackTrace();
}
}
}
private static void test(final float lineStroke,
final boolean useDashes,
final float dashMinLen)
throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "test: "
+ "lineStroke=" + lineStroke
+ ", useDashes=" + useDashes
+", dashMinLen=" + dashMinLen
);
final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
System.out.println("Testing renderer = " + renderer);
final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen);
// TODO: test Dasher.firstSegmentsBuffer resizing ?
// array.dasher.firstSegmentsBuffer.d_float[2] sum: 6 avg: 3.0 [3 | 3]
/*
// Marlin growable arrays:
= new StatLong("array.dasher.firstSegmentsBuffer.d_float");
= new StatLong("array.stroker.polystack.curves.d_float");
= new StatLong("array.stroker.polystack.curveTypes.d_byte");
= new StatLong("array.marlincache.rowAAChunk.d_byte");
= new StatLong("array.marlincache.touchedTile.int");
= new StatLong("array.renderer.alphaline.int");
= new StatLong("array.renderer.crossings.int");
= new StatLong("array.renderer.aux_crossings.int");
= new StatLong("array.renderer.edgeBuckets.int");
= new StatLong("array.renderer.edgeBucketCounts.int");
= new StatLong("array.renderer.edgePtrs.int");
= new StatLong("array.renderer.aux_edgePtrs.int");
*/
// size > 8192 (exceed both tile and buckets arrays)
final int size = 9000;
System.out.println("image size = " + size);
final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = (Graphics2D) image.getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setClip(0, 0, size, size);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, size, size);
g2d.setStroke(stroke);
g2d.setColor(Color.BLACK);
final long start = System.nanoTime();
paint(g2d, size - 10f);
final long time = System.nanoTime() - start;
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
if (SAVE_IMAGE) {
try {
final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
} catch (IOException ex) {
System.out.println("Writing file failure:");
ex.printStackTrace();
}
}
} finally {
g2d.dispose();
}
}
private static void testHugeImage(final int width, final int height)
throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "testHugeImage: "
+ "width=" + width
+ ", height=" + height
);
final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
System.out.println("Testing renderer = " + renderer);
final BasicStroke stroke = createStroke(2.5f, false, 0);
// size > 24bits (exceed both tile and buckets arrays)
System.out.println("image size = " + width + " x "+height);
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
final Graphics2D g2d = (Graphics2D) image.getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, width, height);
g2d.setStroke(stroke);
g2d.setColor(Color.BLACK);
final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32);
path.moveTo(0, 0);
path.lineTo(width, 0);
path.lineTo(width, height);
path.lineTo(0, height);
path.lineTo(0, 0);
final long start = System.nanoTime();
g2d.draw(path);
final long time = System.nanoTime() - start;
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
if (SAVE_IMAGE) {
try {
final File file = new File("CrashTest-" + renderer +
"-huge-" + width + "x" +height + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
} catch (IOException ex) {
System.out.println("Writing file failure:");
ex.printStackTrace();
}
}
} finally {
g2d.dispose();
}
}
private static void paint(final Graphics2D g2d, final float size) {
final double halfSize = size / 2.0;
final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32 * 1024);
// show cross:
path.moveTo(0, 0);
path.lineTo(size, size);
path.moveTo(size, 0);
path.lineTo(0, size);
path.moveTo(0, 0);
path.lineTo(size, 0);
path.moveTo(0, 0);
path.lineTo(0, size);
path.moveTo(0, 0);
double r = size;
final int ratio = 100;
int repeats = 1;
int n = 0;
while (r > 1.0) {
repeats *= ratio;
if (repeats > 10000) {
repeats = 10000;
}
for (int i = 0; i < repeats; i++) {
path.lineTo(halfSize - 0.5 * r + i * r / repeats,
halfSize - 0.5 * r);
n++;
path.lineTo(halfSize - 0.5 * r + i * r / repeats + 0.1,
halfSize + 0.5 * r);
n++;
}
r -= halfSize;
}
System.out.println("draw : " + n + " lines.");
g2d.draw(path);
}
private static BasicStroke createStroke(final float width,
final boolean useDashes,
final float dashMinLen) {
final float[] dashes;
if (useDashes) {
// huge dash array (exceed Dasher.INITIAL_ARRAY)
dashes = new float[512];
float cur = dashMinLen;
float step = 0.01f;
for (int i = 0; i < dashes.length; i += 2) {
dashes[i] = cur;
dashes[i + 1] = cur;
cur += step;
}
} else {
dashes = null;
}
if (USE_ROUND_CAPS_AND_JOINS) {
// Use both round Caps & Joins:
return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 100.0f, dashes, 0.0f);
}
return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 100.0f, dashes, 0.0f);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册